Home » pdfbox-1.1.0-src » org.apache.fontbox.cff » [javadoc | source]

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   package org.apache.fontbox.cff;
   18   
   19   import java.io.IOException;
   20   import java.util.ArrayList;
   21   import java.util.Arrays;
   22   import java.util.Collections;
   23   import java.util.List;
   24   
   25   import org.apache.fontbox.cff.charset.CFFCharset;
   26   import org.apache.fontbox.cff.charset.CFFExpertCharset;
   27   import org.apache.fontbox.cff.charset.CFFExpertSubsetCharset;
   28   import org.apache.fontbox.cff.charset.CFFISOAdobeCharset;
   29   import org.apache.fontbox.cff.encoding.CFFEncoding;
   30   import org.apache.fontbox.cff.encoding.CFFExpertEncoding;
   31   import org.apache.fontbox.cff.encoding.CFFStandardEncoding;
   32   
   33   /**
   34    * This class represents a parser for a CFF font. 
   35    * @author Villu Ruusmann
   36    * @version $Revision: 1.0 $
   37    */
   38   public class CFFParser
   39   {
   40   
   41       private CFFDataInput input = null;
   42       private Header header = null;
   43       private IndexData nameIndex = null;
   44       private IndexData topDictIndex = null;
   45       private IndexData stringIndex = null;
   46       private IndexData globalSubrIndex = null;
   47   
   48       /**
   49        * Parsing CFF Font using a byte array as input.
   50        * @param bytes the given byte array
   51        * @return the parsed CFF fonts
   52        * @throws IOException If there is an error reading from the stream
   53        */
   54       public List<CFFFont> parse(byte[] bytes) throws IOException
   55       {
   56           input = new CFFDataInput(bytes);
   57           header = readHeader(input);
   58           nameIndex = readIndexData(input);
   59           topDictIndex = readIndexData(input);
   60           stringIndex = readIndexData(input);
   61           globalSubrIndex = readIndexData(input);
   62   
   63           List<CFFFont> fonts = new ArrayList<CFFFont>();
   64           for (int i = 0; i < nameIndex.count; i++)
   65           {
   66               CFFFont font = parseFont(i);
   67               fonts.add(font);
   68           }
   69           return fonts;
   70       }
   71   
   72       private static Header readHeader(CFFDataInput input) throws IOException
   73       {
   74           Header header = new Header();
   75           header.major = input.readCard8();
   76           header.minor = input.readCard8();
   77           header.hdrSize = input.readCard8();
   78           header.offSize = input.readOffSize();
   79           return header;
   80       }
   81   
   82       private static IndexData readIndexData(CFFDataInput input)
   83               throws IOException
   84       {
   85           IndexData index = new IndexData();
   86           index.count = input.readCard16();
   87           if (index.count == 0)
   88           {
   89               return index;
   90           }
   91           index.offSize = input.readOffSize();
   92           index.offset = new int[index.count + 1];
   93           for (int i = 0; i < index.offset.length; i++)
   94           {
   95               index.offset[i] = input.readOffset(index.offSize);
   96           }
   97           index.data = new int[index.offset[index.offset.length - 1]
   98                   - index.offset[0]];
   99           for (int i = 0; i < index.data.length; i++)
  100           {
  101               index.data[i] = input.readCard8();
  102           }
  103           return index;
  104       }
  105   
  106       private static DictData readDictData(CFFDataInput input) throws IOException
  107       {
  108           DictData dict = new DictData();
  109           dict.entries = new ArrayList<DictData.Entry>();
  110           while (input.hasRemaining())
  111           {
  112               DictData.Entry entry = readEntry(input);
  113               dict.entries.add(entry);
  114           }
  115           return dict;
  116       }
  117   
  118       private static DictData.Entry readEntry(CFFDataInput input)
  119               throws IOException
  120       {
  121           DictData.Entry entry = new DictData.Entry();
  122           while (true)
  123           {
  124               int b0 = input.readUnsignedByte();
  125   
  126               if (b0 >= 0 && b0 <= 21)
  127               {
  128                   entry.operator = readOperator(input, b0);
  129                   break;
  130               } 
  131               else if (b0 == 28 || b0 == 29)
  132               {
  133                   entry.operands.add(readIntegerNumber(input, b0));
  134               } 
  135               else if (b0 == 30)
  136               {
  137                   entry.operands.add(readRealNumber(input, b0));
  138               } 
  139               else if (b0 >= 32 && b0 <= 254)
  140               {
  141                   entry.operands.add(readIntegerNumber(input, b0));
  142               } 
  143               else
  144               {
  145                   throw new IllegalArgumentException();
  146               }
  147           }
  148           return entry;
  149       }
  150   
  151       private static CFFOperator readOperator(CFFDataInput input, int b0)
  152               throws IOException
  153       {
  154           CFFOperator.Key key = readOperatorKey(input, b0);
  155           return CFFOperator.getOperator(key);
  156       }
  157   
  158       private static CFFOperator.Key readOperatorKey(CFFDataInput input, int b0)
  159               throws IOException
  160       {
  161           if (b0 == 12)
  162           {
  163               int b1 = input.readUnsignedByte();
  164               return new CFFOperator.Key(b0, b1);
  165           }
  166           return new CFFOperator.Key(b0);
  167       }
  168   
  169       private static Integer readIntegerNumber(CFFDataInput input, int b0)
  170               throws IOException
  171       {
  172           if (b0 == 28)
  173           {
  174               int b1 = input.readUnsignedByte();
  175               int b2 = input.readUnsignedByte();
  176               return Integer.valueOf((short) (b1 << 8 | b2));
  177           } 
  178           else if (b0 == 29)
  179           {
  180               int b1 = input.readUnsignedByte();
  181               int b2 = input.readUnsignedByte();
  182               int b3 = input.readUnsignedByte();
  183               int b4 = input.readUnsignedByte();
  184               return Integer.valueOf(b1 << 24 | b2 << 16 | b3 << 8 | b4);
  185           } 
  186           else if (b0 >= 32 && b0 <= 246)
  187           {
  188               return Integer.valueOf(b0 - 139);
  189           } 
  190           else if (b0 >= 247 && b0 <= 250)
  191           {
  192               int b1 = input.readUnsignedByte();
  193               return Integer.valueOf((b0 - 247) * 256 + b1 + 108);
  194           } 
  195           else if (b0 >= 251 && b0 <= 254)
  196           {
  197               int b1 = input.readUnsignedByte();
  198               return Integer.valueOf(-(b0 - 251) * 256 - b1 - 108);
  199           } 
  200           else
  201           {
  202               throw new IllegalArgumentException();
  203           }
  204       }
  205   
  206       private static Double readRealNumber(CFFDataInput input, int b0)
  207               throws IOException
  208       {
  209           StringBuffer sb = new StringBuffer();
  210           boolean done = false;
  211           while (!done)
  212           {
  213               int b = input.readUnsignedByte();
  214               int[] nibbles = { b / 16, b % 16 };
  215               for (int nibble : nibbles)
  216               {
  217                   switch (nibble)
  218                   {
  219                   case 0x0:
  220                   case 0x1:
  221                   case 0x2:
  222                   case 0x3:
  223                   case 0x4:
  224                   case 0x5:
  225                   case 0x6:
  226                   case 0x7:
  227                   case 0x8:
  228                   case 0x9:
  229                       sb.append(nibble);
  230                       break;
  231                   case 0xa:
  232                       sb.append(".");
  233                       break;
  234                   case 0xb:
  235                       sb.append("E");
  236                       break;
  237                   case 0xc:
  238                       sb.append("E-");
  239                       break;
  240                   case 0xd:
  241                       break;
  242                   case 0xe:
  243                       sb.append("-");
  244                       break;
  245                   case 0xf:
  246                       done = true;
  247                       break;
  248                   default:
  249                       throw new IllegalArgumentException();
  250                   }
  251               }
  252           }
  253           return Double.valueOf(sb.toString());
  254       }
  255   
  256       private CFFFont parseFont(int index) throws IOException
  257       {
  258           CFFFont font = new CFFFont();
  259           DataInput nameInput = new DataInput(nameIndex.getBytes(index));
  260           String name = nameInput.getString();
  261           font.setName(name);
  262           CFFDataInput topDictInput = new CFFDataInput(topDictIndex
  263                   .getBytes(index));
  264           DictData topDict = readDictData(topDictInput);
  265           DictData.Entry syntheticBaseEntry = topDict.getEntry("SyntheticBase");
  266           if (syntheticBaseEntry != null)
  267           {
  268               throw new IOException("Synthetic Fonts are not supported");
  269           }
  270           DictData.Entry rosEntry = topDict.getEntry("ROS");
  271           if (rosEntry != null)
  272           {
  273               throw new IOException("CID-keyed Fonts are not supported");
  274           }
  275           font.addValueToTopDict("version", getString(topDict,"version"));
  276           font.addValueToTopDict("Notice", getString(topDict,"Notice"));
  277           font.addValueToTopDict("Copyright", getString(topDict,"Copyright"));
  278           font.addValueToTopDict("FullName", getString(topDict,"FullName"));
  279           font.addValueToTopDict("FamilyName", getString(topDict,"FamilyName"));
  280           font.addValueToTopDict("Weight", getString(topDict,"Weight"));
  281           font.addValueToTopDict("isFixedPitch", getBoolean(topDict, "isFixedPitch", false));
  282           font.addValueToTopDict("ItalicAngle", getNumber(topDict, "ItalicAngle", 0));
  283           font.addValueToTopDict("UnderlinePosition", getNumber(topDict, "UnderlinePosition", -100));
  284           font.addValueToTopDict("UnderlineThickness", getNumber(topDict, "UnderlineThickness", 50));
  285           font.addValueToTopDict("PaintType", getNumber(topDict, "PaintType", 0));
  286           font.addValueToTopDict("CharstringType", getNumber(topDict, "CharstringType", 2));
  287           font.addValueToTopDict("FontMatrix", getArray(topDict, "FontMatrix", Arrays
  288                   .<Number> asList(Double.valueOf(0.001), Double.valueOf(0),
  289                           Double.valueOf(0), Double.valueOf(0.001), Double
  290                                   .valueOf(0), Double.valueOf(0))));
  291           font.addValueToTopDict("UniqueID", getNumber(topDict, "UniqueID", null));
  292           font.addValueToTopDict("FontBBox", getArray(topDict, "FontBBox", Arrays
  293                   .<Number> asList(Integer.valueOf(0), Integer.valueOf(0),
  294                           Integer.valueOf(0), Integer.valueOf(0))));
  295           font.addValueToTopDict("StrokeWidth", getNumber(topDict, "StrokeWidth", 0));
  296           font.addValueToTopDict("XUID", getArray(topDict, "XUID", null));
  297           DictData.Entry charStringsEntry = topDict.getEntry("CharStrings");
  298           int charStringsOffset = charStringsEntry.getNumber(0).intValue();
  299           input.setPosition(charStringsOffset);
  300           IndexData charStringsIndex = readIndexData(input);
  301           DictData.Entry charsetEntry = topDict.getEntry("charset");
  302           CFFCharset charset;
  303           int charsetId = charsetEntry != null ? charsetEntry.getNumber(0)
  304                   .intValue() : 0;
  305           if (charsetId == 0)
  306           {
  307               charset = CFFISOAdobeCharset.getInstance();
  308           } 
  309           else if (charsetId == 1)
  310           {
  311               charset = CFFExpertCharset.getInstance();
  312           } 
  313           else if (charsetId == 2)
  314           {
  315               charset = CFFExpertSubsetCharset.getInstance();
  316           } 
  317           else
  318           {
  319               input.setPosition(charsetId);
  320               charset = readCharset(input, charStringsIndex.count);
  321           }
  322           font.setCharset(charset);
  323           font.getCharStringsDict().put(".notdef", charStringsIndex.getBytes(0));
  324           int[] gids = new int[charStringsIndex.count];
  325           List<CFFCharset.Entry> glyphEntries = charset.getEntries();
  326           for (int i = 1; i < charStringsIndex.count; i++)
  327           {
  328               CFFCharset.Entry glyphEntry = glyphEntries.get(i - 1);
  329               gids[i - 1] = glyphEntry.getSID();
  330               font.getCharStringsDict().put(glyphEntry.getName(), charStringsIndex.getBytes(i));
  331           }
  332           DictData.Entry encodingEntry = topDict.getEntry("Encoding");
  333           CFFEncoding encoding;
  334           int encodingId = encodingEntry != null ? encodingEntry.getNumber(0)
  335                   .intValue() : 0;
  336           if (encodingId == 0)
  337           {
  338               encoding = CFFStandardEncoding.getInstance();
  339           } 
  340           else if (encodingId == 1)
  341           {
  342               encoding = CFFExpertEncoding.getInstance();
  343           } 
  344           else
  345           {
  346               input.setPosition(encodingId);
  347               encoding = readEncoding(input, gids);
  348           }
  349           font.setEncoding(encoding);
  350           DictData.Entry privateEntry = topDict.getEntry("Private");
  351           int privateOffset = privateEntry.getNumber(1).intValue();
  352           input.setPosition(privateOffset);
  353           int privateSize = privateEntry.getNumber(0).intValue();
  354           CFFDataInput privateDictData = new CFFDataInput(input.readBytes(privateSize));
  355           DictData privateDict = readDictData(privateDictData);
  356           font.addValueToPrivateDict("BlueValues", getDelta(privateDict, "BlueValues", null));
  357           font.addValueToPrivateDict("OtherBlues", getDelta(privateDict, "OtherBlues", null));
  358           font.addValueToPrivateDict("FamilyBlues", getDelta(privateDict, "FamilyBlues", null));
  359           font.addValueToPrivateDict("FamilyOtherBlues", getDelta(privateDict, "FamilyOtherBlues", null));
  360           font.addValueToPrivateDict("BlueScale", getNumber(privateDict, "BlueScale", Double.valueOf(0.039625)));
  361           font.addValueToPrivateDict("BlueShift", getNumber(privateDict, "BlueShift", Integer.valueOf(7)));
  362           font.addValueToPrivateDict("BlueFuzz", getNumber(privateDict, "BlueFuzz", Integer.valueOf(1)));
  363           font.addValueToPrivateDict("StdHW", getNumber(privateDict, "StdHW", null));
  364           font.addValueToPrivateDict("StdVW", getNumber(privateDict, "StdVW", null));
  365           font.addValueToPrivateDict("StemSnapH", getDelta(privateDict, "StemSnapH", null));
  366           font.addValueToPrivateDict("StemSnapV", getDelta(privateDict, "StemSnapV", null));
  367           font.addValueToPrivateDict("ForceBold", getBoolean(privateDict, "ForceBold", false));
  368           font.addValueToPrivateDict("LanguageGroup", getNumber(privateDict, "LanguageGroup", Integer.valueOf(0)));
  369           font.addValueToPrivateDict("ExpansionFactor", getNumber(privateDict, "ExpansionFactor", Double.valueOf(0.06)));
  370           font.addValueToPrivateDict("initialRandomSeed", getNumber(privateDict, "initialRandomSeed", Integer.valueOf(0)));
  371           font.addValueToPrivateDict("defaultWidthX", getNumber(privateDict, "defaultWidthX", Integer.valueOf(0)));
  372           font.addValueToPrivateDict("nominalWidthX", getNumber(privateDict, "nominalWidthX", Integer.valueOf(0)));
  373           return font;
  374       }
  375   
  376       private String readString(int index) throws IOException
  377       {
  378           if (index >= 0 && index <= 390)
  379           {
  380               return CFFStandardString.getName(index);
  381           }
  382           DataInput dataInput = new DataInput(stringIndex.getBytes(index - 391));
  383           return dataInput.getString();
  384       }
  385   
  386       private String getString(DictData dict, String name) throws IOException
  387       {
  388           DictData.Entry entry = dict.getEntry(name);
  389           return (entry != null ? readString(entry.getNumber(0).intValue()) : null);
  390       }
  391   
  392       private Boolean getBoolean(DictData dict, String name, boolean defaultValue) throws IOException
  393       {
  394           DictData.Entry entry = dict.getEntry(name);
  395           return entry != null ? entry.getBoolean(0) : defaultValue;
  396       }
  397   
  398       private Number getNumber(DictData dict, String name, Number defaultValue) throws IOException
  399       {
  400           DictData.Entry entry = dict.getEntry(name);
  401           return entry != null ? entry.getNumber(0) : defaultValue;
  402       }
  403   
  404       // TODO Where is the difference to getDelta??
  405       private List<Number> getArray(DictData dict, String name, List<Number> defaultValue) throws IOException
  406       {
  407           DictData.Entry entry = dict.getEntry(name);
  408           return entry != null ? entry.getArray() : defaultValue;
  409       }
  410   
  411       // TODO Where is the difference to getArray??
  412       private List<Number> getDelta(DictData dict, String name, List<Number> defaultValue) throws IOException
  413       {
  414           DictData.Entry entry = dict.getEntry(name);
  415           return entry != null ? entry.getArray() : defaultValue;
  416       }
  417   
  418       private CFFEncoding readEncoding(CFFDataInput dataInput, int[] gids)
  419               throws IOException
  420       {
  421           int format = dataInput.readCard8();
  422           int baseFormat = format & 0x7f;
  423   
  424           if (baseFormat == 0)
  425           {
  426               return readFormat0Encoding(dataInput, format, gids);
  427           } 
  428           else if (baseFormat == 1)
  429           {
  430               return readFormat1Encoding(dataInput, format, gids);
  431           } 
  432           else
  433           {
  434               throw new IllegalArgumentException();
  435           }
  436       }
  437   
  438       private Format0Encoding readFormat0Encoding(CFFDataInput dataInput, int format,
  439               int[] gids) throws IOException
  440       {
  441           Format0Encoding encoding = new Format0Encoding();
  442           encoding.format = format;
  443           encoding.nCodes = dataInput.readCard8();
  444           encoding.code = new int[encoding.nCodes];
  445           for (int i = 0; i < encoding.code.length; i++)
  446           {
  447               encoding.code[i] = dataInput.readCard8();
  448               encoding.register(encoding.code[i], gids[i]);
  449           }
  450           if ((format & 0x80) != 0)
  451           {
  452               readSupplement(dataInput, encoding);
  453           }
  454           return encoding;
  455       }
  456   
  457       private Format1Encoding readFormat1Encoding(CFFDataInput dataInput, int format,
  458               int[] gids) throws IOException
  459       {
  460           Format1Encoding encoding = new Format1Encoding();
  461           encoding.format = format;
  462           encoding.nRanges = dataInput.readCard8();
  463           int count = 0;
  464           encoding.range = new Format1Encoding.Range1[encoding.nRanges];
  465           for (int i = 0; i < encoding.range.length; i++)
  466           {
  467               Format1Encoding.Range1 range = new Format1Encoding.Range1();
  468               range.first = dataInput.readCard8();
  469               range.nLeft = dataInput.readCard8();
  470               encoding.range[i] = range;
  471               for (int j = 0; j < 1 + range.nLeft; j++)
  472               {
  473                   encoding.register(range.first + j, gids[count + j]);
  474               }
  475               count += 1 + range.nLeft;
  476           }
  477           if ((format & 0x80) != 0)
  478           {
  479               readSupplement(dataInput, encoding);
  480           }
  481           return encoding;
  482       }
  483   
  484       private void readSupplement(CFFDataInput dataInput, EmbeddedEncoding encoding)
  485               throws IOException
  486       {
  487           encoding.nSups = dataInput.readCard8();
  488           encoding.supplement = new EmbeddedEncoding.Supplement[encoding.nSups];
  489           for (int i = 0; i < encoding.supplement.length; i++)
  490           {
  491               EmbeddedEncoding.Supplement supplement = new EmbeddedEncoding.Supplement();
  492               supplement.code = dataInput.readCard8();
  493               supplement.glyph = dataInput.readSID();
  494               encoding.supplement[i] = supplement;
  495           }
  496       }
  497   
  498       private CFFCharset readCharset(CFFDataInput dataInput, int nGlyphs)
  499               throws IOException
  500       {
  501           int format = dataInput.readCard8();
  502           if (format == 0)
  503           {
  504               return readFormat0Charset(dataInput, format, nGlyphs);
  505           } 
  506           else if (format == 1)
  507           {
  508               return readFormat1Charset(dataInput, format, nGlyphs);
  509           } 
  510           else
  511           {
  512               throw new IllegalArgumentException();
  513           }
  514       }
  515   
  516       private Format0Charset readFormat0Charset(CFFDataInput dataInput, int format,
  517               int nGlyphs) throws IOException
  518       {
  519           Format0Charset charset = new Format0Charset();
  520           charset.format = format;
  521           charset.glyph = new int[nGlyphs - 1];
  522           for (int i = 0; i < charset.glyph.length; i++)
  523           {
  524               charset.glyph[i] = dataInput.readSID();
  525               charset.register(charset.glyph[i], readString(charset.glyph[i]));
  526           }
  527           return charset;
  528       }
  529   
  530       private Format1Charset readFormat1Charset(CFFDataInput dataInput, int format,
  531               int nGlyphs) throws IOException
  532       {
  533           Format1Charset charset = new Format1Charset();
  534           charset.format = format;
  535           charset.range = new Format1Charset.Range1[0];
  536           for (int i = 0; i < nGlyphs - 1;)
  537           {
  538               Format1Charset.Range1[] newRange = new Format1Charset.Range1[charset.range.length + 1];
  539               System.arraycopy(charset.range, 0, newRange, 0,
  540                       charset.range.length);
  541               charset.range = newRange;
  542               Format1Charset.Range1 range = new Format1Charset.Range1();
  543               range.first = dataInput.readSID();
  544               range.nLeft = dataInput.readCard8();
  545               charset.range[charset.range.length - 1] = range;
  546               for (int j = 0; j < 1 + range.nLeft; j++)
  547               {
  548                   charset.register(range.first + j, readString(range.first + j));
  549               }
  550               i += 1 + range.nLeft;
  551           }
  552           return charset;
  553       }
  554   
  555       /**
  556        * Inner class holding the header of a CFF font. 
  557        */
  558       private static class Header
  559       {
  560           private int major;
  561           private int minor;
  562           private int hdrSize;
  563           private int offSize;
  564   
  565           @Override
  566           public String toString()
  567           {
  568               return getClass().getName() + "[major=" + major + ", minor="
  569                       + minor + ", hdrSize=" + hdrSize + ", offSize=" + offSize
  570                       + "]";
  571           }
  572       }
  573   
  574       /**
  575        * Inner class holding the IndexData of a CFF font. 
  576        */
  577       private static class IndexData
  578       {
  579           private int count;
  580           private int offSize;
  581           private int[] offset;
  582           private int[] data;
  583   
  584           public byte[] getBytes(int index)
  585           {
  586               int length = offset[index + 1] - offset[index];
  587               byte[] bytes = new byte[length];
  588               for (int i = 0; i < length; i++)
  589               {
  590                   bytes[i] = (byte) data[offset[index] - 1 + i];
  591               }
  592               return bytes;
  593           }
  594   
  595           @Override
  596           public String toString()
  597           {
  598               return getClass().getName() + "[count=" + count + ", offSize="
  599                       + offSize + ", offset=" + Arrays.toString(offset)
  600                       + ", data=" + Arrays.toString(data) + "]";
  601           }
  602       }
  603   
  604       /**
  605        * Inner class holding the DictData of a CFF font. 
  606        */
  607       private static class DictData
  608       {
  609   
  610           private List<Entry> entries = null;
  611   
  612           public Entry getEntry(CFFOperator.Key key)
  613           {
  614               return getEntry(CFFOperator.getOperator(key));
  615           }
  616   
  617           public Entry getEntry(String name)
  618           {
  619               return getEntry(CFFOperator.getOperator(name));
  620           }
  621   
  622           private Entry getEntry(CFFOperator operator)
  623           {
  624               for (Entry entry : entries)
  625               {
  626                   if (entry.operator.equals(operator))
  627                   {
  628                       return entry;
  629                   }
  630               }
  631               return null;
  632           }
  633   
  634           /**
  635            * {@inheritDoc} 
  636            */
  637           public String toString()
  638           {
  639               return getClass().getName() + "[entries=" + entries + "]";
  640           }
  641   
  642           /**
  643            * Inner class holding an operand of a CFF font. 
  644            */
  645           private static class Entry
  646           {
  647               private List<Number> operands = new ArrayList<Number>();
  648               private CFFOperator operator = null;
  649   
  650               public Number getNumber(int index)
  651               {
  652                   return operands.get(index);
  653               }
  654   
  655               public Boolean getBoolean(int index)
  656               {
  657                   Number operand = operands.get(index);
  658                   if (operand instanceof Integer)
  659                   {
  660                       switch (operand.intValue())
  661                       {
  662                       case 0:
  663                           return Boolean.FALSE;
  664                       case 1:
  665                           return Boolean.TRUE;
  666                       default:
  667                           break;
  668                       }
  669                   }
  670                   throw new IllegalArgumentException();
  671               }
  672   
  673               // TODO unused??
  674               public Integer getSID(int index)
  675               {
  676                   Number operand = operands.get(index);
  677                   if (operand instanceof Integer)
  678                   {
  679                       return (Integer) operand;
  680                   }
  681                   throw new IllegalArgumentException();
  682               }
  683   
  684               // TODO Where is the difference to getDelta??
  685               public List<Number> getArray()
  686               {
  687                   return operands;
  688               }
  689   
  690               // TODO Where is the difference to getArray??
  691               public List<Number> getDelta()
  692               {
  693                   return operands;
  694               }
  695   
  696               @Override
  697               public String toString()
  698               {
  699                   return getClass().getName() + "[operands=" + operands
  700                           + ", operator=" + operator + "]";
  701               }
  702           }
  703       }
  704   
  705       /**
  706        * Inner class representing an embedded CFF encoding. 
  707        */
  708       abstract static class EmbeddedEncoding extends CFFEncoding
  709       {
  710   
  711           private int nSups;
  712           private Supplement[] supplement;
  713   
  714           @Override
  715           public boolean isFontSpecific()
  716           {
  717               return true;
  718           }
  719   
  720           List<Supplement> getSupplements()
  721           {
  722               if(supplement == null){
  723                   return Collections.<Supplement>emptyList();
  724               }
  725               return Arrays.asList(supplement);
  726           }
  727   
  728           /**
  729            * Inner class representing a supplement for an encoding. 
  730            */
  731           static class Supplement
  732           {
  733               private int code;
  734               private int glyph;
  735   
  736               int getCode(){
  737                   return code;
  738               }
  739   
  740               int getGlyph(){
  741                   return glyph;
  742               }
  743   
  744               @Override
  745               public String toString()
  746               {
  747                   return getClass().getName() + "[code=" + code + ", glyph="
  748                           + glyph + "]";
  749               }
  750           }
  751       }
  752   
  753       /**
  754        * Inner class representing a Format0 encoding. 
  755        */
  756       private static class Format0Encoding extends EmbeddedEncoding
  757       {
  758           private int format;
  759           private int nCodes;
  760           private int[] code;
  761   
  762           @Override
  763           public String toString()
  764           {
  765               return getClass().getName() + "[format=" + format + ", nCodes="
  766                       + nCodes + ", code=" + Arrays.toString(code)
  767                       + ", supplement=" + Arrays.toString(super.supplement) + "]";
  768           }
  769       }
  770   
  771       /**
  772        * Inner class representing a Format1 encoding. 
  773        */
  774       private static class Format1Encoding extends EmbeddedEncoding
  775       {
  776           private int format;
  777           private int nRanges;
  778           private Range1[] range;
  779   
  780           @Override
  781           public String toString()
  782           {
  783               return getClass().getName() + "[format=" + format + ", nRanges="
  784                       + nRanges + ", range=" + Arrays.toString(range)
  785                       + ", supplement=" + Arrays.toString(super.supplement) + "]";
  786           }
  787   
  788           /**
  789            * Inner class representing a range of an encoding. 
  790            */
  791           private static class Range1
  792           {
  793               private int first;
  794               private int nLeft;
  795   
  796               @Override
  797               public String toString()
  798               {
  799                   return getClass().getName() + "[first=" + first + ", nLeft="
  800                           + nLeft + "]";
  801               }
  802           }
  803       }
  804   
  805       /**
  806        * Inner class representing an embedded CFF charset. 
  807        */
  808       abstract static class EmbeddedCharset extends CFFCharset
  809       {
  810           @Override
  811           public boolean isFontSpecific()
  812           {
  813               return true;
  814           }
  815       }
  816   
  817       /**
  818        * Inner class representing a Format0 charset. 
  819        */
  820       private static class Format0Charset extends EmbeddedCharset
  821       {
  822           private int format;
  823           private int[] glyph;
  824   
  825           @Override
  826           public String toString()
  827           {
  828               return getClass().getName() + "[format=" + format + ", glyph="
  829                       + Arrays.toString(glyph) + "]";
  830           }
  831       }
  832   
  833       /**
  834        * Inner class representing a Format1 charset. 
  835        */
  836       private static class Format1Charset extends EmbeddedCharset
  837       {
  838           private int format;
  839           private Range1[] range;
  840   
  841           @Override
  842           public String toString()
  843           {
  844               return getClass().getName() + "[format=" + format + ", range="
  845                       + Arrays.toString(range) + "]";
  846           }
  847   
  848           /**
  849            * Inner class representing a range of a charset. 
  850            */
  851           private static class Range1
  852           {
  853               private int first;
  854               private int nLeft;
  855   
  856               @Override
  857               public String toString()
  858               {
  859                   return getClass().getName() + "[first=" + first + ", nLeft="
  860                           + nLeft + "]";
  861               }
  862           }
  863       }
  864   }

Home » pdfbox-1.1.0-src » org.apache.fontbox.cff » [javadoc | source]