Index: fontbox/src/main/java/org/apache/fontbox/cff/CFFParser.java =================================================================== --- fontbox/src/main/java/org/apache/fontbox/cff/CFFParser.java (revision 1546564) +++ fontbox/src/main/java/org/apache/fontbox/cff/CFFParser.java (working copy) @@ -36,7 +36,7 @@ import org.apache.fontbox.cff.encoding.CFFStandardEncoding; /** - * This class represents a parser for a CFF font. + * This class represents a parser for a CFF font. * @author Villu Ruusmann * @version $Revision: 1.0 $ */ @@ -107,7 +107,11 @@ { input.setPosition(0); } + return parse(input); + } + public List parse(CFFDataInput input) throws IOException { + this.input = input; header = readHeader(input); nameIndex = readIndexData(input); topDictIndex = readIndexData(input); @@ -119,6 +123,7 @@ { CFFFont font = parseFont(i); font.setGlobalSubrIndex(globalSubrIndex); + font.constructMappings(); fonts.add(font); } return fonts; @@ -145,7 +150,7 @@ return cffHeader; } - private static IndexData readIndexData(CFFDataInput input) throws IOException + public static IndexData readIndexData(CFFDataInput input) throws IOException { int count = input.readCard16(); IndexData index = new IndexData(count); @@ -179,7 +184,8 @@ return dict; } - private static DictData.Entry readEntry(CFFDataInput input) throws IOException + private static DictData.Entry readEntry(CFFDataInput input) + throws IOException { DictData.Entry entry = new DictData.Entry(); while (true) @@ -211,13 +217,15 @@ return entry; } - private static CFFOperator readOperator(CFFDataInput input, int b0) throws IOException + private static CFFOperator readOperator(CFFDataInput input, int b0) + throws IOException { CFFOperator.Key key = readOperatorKey(input, b0); return CFFOperator.getOperator(key); } - private static CFFOperator.Key readOperatorKey(CFFDataInput input, int b0) throws IOException + private static CFFOperator.Key readOperatorKey(CFFDataInput input, int b0) + throws IOException { if (b0 == 12) { @@ -227,7 +235,8 @@ return new CFFOperator.Key(b0); } - private static Integer readIntegerNumber(CFFDataInput input, int b0) throws IOException + private static Integer readIntegerNumber(CFFDataInput input, int b0) + throws IOException { if (b0 == 28) { @@ -263,7 +272,8 @@ } } - private static Double readRealNumber(CFFDataInput input, int b0) throws IOException + private static Double readRealNumber(CFFDataInput input, int b0) + throws IOException { StringBuffer sb = new StringBuffer(); boolean done = false; @@ -446,9 +456,9 @@ throw new IOException("FDArray is missing for a CIDKeyed Font."); } - int fontDictOffset = fdArrayEntry.getNumber(0).intValue(); - input.setPosition(fontDictOffset); - IndexData fdIndex = readIndexData(input); + int fontDictOffset = fdArrayEntry.getNumber(0).intValue(); + input.setPosition(fontDictOffset); + IndexData fdIndex = readIndexData(input); List> privateDictionaries = new LinkedList>(); List> fontDictionaries = new LinkedList>(); @@ -577,8 +587,8 @@ { return CFFStandardString.getName(index); } - if (index - 391 <= stringIndex.getCount()) - { + if (index - 391 < stringIndex.getCount()) + { DataInput dataInput = new DataInput(stringIndex.getBytes(index - 391)); return dataInput.getString(); } @@ -620,7 +630,8 @@ return entry != null ? entry.getArray() : defaultValue; } - private CFFEncoding readEncoding(CFFDataInput dataInput, int[] gids) throws IOException + private CFFEncoding readEncoding(CFFDataInput dataInput, int[] gids) + throws IOException { int format = dataInput.readCard8(); int baseFormat = format & 0x7f; @@ -639,7 +650,8 @@ } } - private Format0Encoding readFormat0Encoding(CFFDataInput dataInput, int format, int[] gids) throws IOException + private Format0Encoding readFormat0Encoding(CFFDataInput dataInput, int format, + int[] gids) throws IOException { Format0Encoding encoding = new Format0Encoding(); encoding.format = format; @@ -657,7 +669,8 @@ return encoding; } - private Format1Encoding readFormat1Encoding(CFFDataInput dataInput, int format, int[] gids) throws IOException + private Format1Encoding readFormat1Encoding(CFFDataInput dataInput, int format, + int[] gids) throws IOException { Format1Encoding encoding = new Format1Encoding(); encoding.format = format; @@ -683,7 +696,8 @@ return encoding; } - private void readSupplement(CFFDataInput dataInput, EmbeddedEncoding encoding) throws IOException + private void readSupplement(CFFDataInput dataInput, EmbeddedEncoding encoding) + throws IOException { encoding.nSups = dataInput.readCard8(); encoding.supplement = new EmbeddedEncoding.Supplement[encoding.nSups]; @@ -738,15 +752,14 @@ fdselect.fds = new int[nGlyphs]; for (int i = 0; i < fdselect.fds.length; i++) { - fdselect.fds[i] = dataInput.readCard8(); - + fdselect.fds[i] = dataInput.readCard8(); } return fdselect; } /** * Read the Format 3 of the FDSelect data structure. - * + * * @param dataInput * @param format * @param nGlyphs @@ -768,7 +781,6 @@ r3.first = dataInput.readCard16(); r3.fd = dataInput.readCard8(); fdselect.range3[i] = r3; - } fdselect.sentinel = dataInput.readCard16(); @@ -902,7 +914,8 @@ } } - private CFFCharset readCharset(CFFDataInput dataInput, int nGlyphs) throws IOException + private CFFCharset readCharset(CFFDataInput dataInput, int nGlyphs) + throws IOException { int format = dataInput.readCard8(); if (format == 0) @@ -923,7 +936,8 @@ } } - private Format0Charset readFormat0Charset(CFFDataInput dataInput, int format, int nGlyphs) throws IOException + private Format0Charset readFormat0Charset(CFFDataInput dataInput, int format, + int nGlyphs) throws IOException { Format0Charset charset = new Format0Charset(); charset.format = format; @@ -936,7 +950,8 @@ return charset; } - private Format1Charset readFormat1Charset(CFFDataInput dataInput, int format, int nGlyphs) throws IOException + private Format1Charset readFormat1Charset(CFFDataInput dataInput, int format, + int nGlyphs) throws IOException { Format1Charset charset = new Format1Charset(); charset.format = format; @@ -957,7 +972,8 @@ return charset; } - private Format2Charset readFormat2Charset(CFFDataInput dataInput, int format, int nGlyphs) throws IOException + private Format2Charset readFormat2Charset(CFFDataInput dataInput, int format, + int nGlyphs) throws IOException { Format2Charset charset = new Format2Charset(); charset.format = format; @@ -981,7 +997,7 @@ } /** - * Inner class holding the header of a CFF font. + * Inner class holding the header of a CFF font. */ private static class Header { @@ -999,7 +1015,7 @@ } /** - * Inner class holding the DictData of a CFF font. + * Inner class holding the DictData of a CFF font. */ private static class DictData { @@ -1030,7 +1046,7 @@ } /** - * {@inheritDoc} + * {@inheritDoc} */ public String toString() { @@ -1038,7 +1054,7 @@ } /** - * Inner class holding an operand of a CFF font. + * Inner class holding an operand of a CFF font. */ private static class Entry { @@ -1100,7 +1116,7 @@ } /** - * Inner class representing an embedded CFF encoding. + * Inner class representing an embedded CFF encoding. */ abstract static class EmbeddedEncoding extends CFFEncoding { @@ -1124,7 +1140,7 @@ } /** - * Inner class representing a supplement for an encoding. + * Inner class representing a supplement for an encoding. */ static class Supplement { @@ -1150,7 +1166,7 @@ } /** - * Inner class representing a Format0 encoding. + * Inner class representing a Format0 encoding. */ private static class Format0Encoding extends EmbeddedEncoding { @@ -1167,7 +1183,7 @@ } /** - * Inner class representing a Format1 encoding. + * Inner class representing a Format1 encoding. */ private static class Format1Encoding extends EmbeddedEncoding { @@ -1183,7 +1199,7 @@ } /** - * Inner class representing a range of an encoding. + * Inner class representing a range of an encoding. */ private static class Range1 { @@ -1193,13 +1209,20 @@ @Override public String toString() { - return getClass().getName() + "[first=" + first + ", nLeft=" + nLeft + "]"; + return getClass().getName() + "[first=" + first + ", nLeft=" + + nLeft + "]"; } + + @Override + public boolean equals(Object obj) { + Range1 r = (Range1)obj; + return (first == r.first && nLeft == r.nLeft); + } } } /** - * Inner class representing an embedded CFF charset. + * Inner class representing an embedded CFF charset. */ abstract static class EmbeddedCharset extends CFFCharset { @@ -1211,7 +1234,7 @@ } /** - * Inner class representing a Format0 charset. + * Inner class representing a Format0 charset. */ private static class Format0Charset extends EmbeddedCharset { @@ -1226,7 +1249,7 @@ } /** - * Inner class representing a Format1 charset. + * Inner class representing a Format1 charset. */ private static class Format1Charset extends EmbeddedCharset { @@ -1240,7 +1263,7 @@ } /** - * Inner class representing a range of a charset. + * Inner class representing a range of a charset. */ private static class Range1 { @@ -1256,7 +1279,7 @@ } /** - * Inner class representing a Format2 charset. + * Inner class representing a Format2 charset. */ private static class Format2Charset extends EmbeddedCharset { @@ -1270,7 +1293,7 @@ } /** - * Inner class representing a range of a charset. + * Inner class representing a range of a charset. */ private static class Range2 { @@ -1284,4 +1307,8 @@ } } } + + public IndexData getStringIndex() { + return stringIndex; + } } Index: fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java =================================================================== --- fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java (revision 1546564) +++ fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java (working copy) @@ -31,7 +31,7 @@ /** * This class represents a CFF/Type2 Font. - * + * * @author Villu Ruusmann * @version $Revision$ */ @@ -44,6 +44,8 @@ private CFFEncoding fontEncoding = null; private CFFCharset fontCharset = null; private Map charStringsDict = new LinkedHashMap(); + Map sidMappings = null; + ArrayList gidMappings = null; private IndexData globalSubrIndex = null; private IndexData localSubrIndex = null; @@ -97,6 +99,7 @@ topDict.put(name, value); } } + /** * Returns the top dictionary. * @return the dictionary @@ -107,7 +110,7 @@ } /** - * Adds the given key/value pair to the private dictionary. + * Adds the given key/value pair to the private dictionary. * @param name the given key * @param value the given value */ @@ -118,7 +121,8 @@ privateDict.put(name, value); } } - /** + + /** * Returns the private dictionary. * @return the dictionary */ @@ -127,14 +131,60 @@ return privateDict; } + /** + * Returns a mapping for a given GID + * @param GID The given GID + * @return The found mapping + */ + public Mapping getMapping(int GID) { + return sidMappings.get(GID); + } + /** - * Get the mapping (code/SID/charname/bytes) for this font. - * @return mappings for codes < 256 and for codes > = 256 - */ - public Collection getMappings() + * Get the mapping (code/SID/charname/bytes) for this font. + * @return mappings for codes < 256 and for codes > = 256 + */ + public Collection getMappings() { + constructMappings(); + return sidMappings.values(); + } + + /** + * Gets the GID mappings list. + */ + public ArrayList getGIDMappings() { + return gidMappings; + } + + private void constructGIDMap() { + gidMappings = new ArrayList(); + Mapping notdef = new Mapping(); + notdef.setName(".notdef"); + gidMappings.add(notdef); + for (CFFCharset.Entry entry : fontCharset.getEntries()) + { + String name = entry.getName(); + byte[] bytes = this.charStringsDict.get(name); + if (bytes == null) + { + continue; + } + Mapping mapping = new Mapping(); + mapping.setSID(entry.getSID()); + mapping.setName(name); + mapping.setBytes(bytes); + gidMappings.add(mapping); + } + } + + /** + * Construct the mappings. + */ + public void constructMappings() { - List mappings = new ArrayList(); - Set mappedNames = new HashSet(); + constructGIDMap(); + sidMappings = new LinkedHashMap(); + Set mappedNames = new HashSet(); for (CFFEncoding.Entry entry : fontEncoding.getEntries()) { String charName = fontCharset.getName(entry.getSID()); @@ -153,7 +203,7 @@ mapping.setSID(entry.getSID()); mapping.setName(charName); mapping.setBytes(bytes); - mappings.add(mapping); + sidMappings.put(mapping.getSID(), mapping); mappedNames.add(charName); } if (fontEncoding instanceof CFFParser.EmbeddedEncoding) @@ -177,7 +227,7 @@ mapping.setSID(supplement.getGlyph()); mapping.setName(charName); mapping.setBytes(bytes); - mappings.add(mapping); + sidMappings.put(mapping.getSID(), mapping); mappedNames.add(charName); } } @@ -185,7 +235,7 @@ int code = 256; for (CFFCharset.Entry entry : fontCharset.getEntries()) { - String name = entry.getName(); + String name = entry.getName(); if (mappedNames.contains(name)) { continue; @@ -201,11 +251,10 @@ mapping.setName(name); mapping.setBytes(bytes); - mappings.add(mapping); + sidMappings.put(mapping.getSID(), mapping); mappedNames.add(name); } - return mappings; } /** @@ -215,34 +264,43 @@ * @return -1 if the SID is missing from the Font. * @throws IOException */ - public int getWidth(int SID) throws IOException { - int nominalWidth = privateDict.containsKey("nominalWidthX") ? ((Number)privateDict.get("nominalWidthX")).intValue() : 0; - int defaultWidth = privateDict.containsKey("defaultWidthX") ? ((Number)privateDict.get("defaultWidthX")).intValue() : 1000 ; - - for (Mapping m : getMappings() ){ - if (m.getSID() == SID) { + public int getWidth(int SID) throws IOException { + int nominalWidth = privateDict.containsKey("nominalWidthX") ? ((Number) privateDict.get("nominalWidthX")).intValue() : 0; + int defaultWidth = privateDict.containsKey("defaultWidthX") ? ((Number) privateDict.get("defaultWidthX")).intValue() : 1000; + Mapping m = sidMappings.get(SID); + if (m != null) { + CharStringRenderer csr = getRendererForMapping(m); + // ---- If the CharString has a Width nominalWidthX must be added, + // otherwise it is the default width. + return csr.getWidth() != 0 ? csr.getWidth() + nominalWidth : defaultWidth; + } - CharStringRenderer csr = null; - if (((Number)getProperty("CharstringType")).intValue() == 2 ) { - List lSeq = m.toType2Sequence(); - csr = new CharStringRenderer(false); - csr.render(lSeq); - } else { - List lSeq = m.toType1Sequence(); - csr = new CharStringRenderer(); - csr.render(lSeq); - } + // ---- SID Width not found, return the nodef width + return getNotDefWidth(defaultWidth, nominalWidth); + } - // ---- If the CharString has a Width nominalWidthX must be added, - // otherwise it is the default width. - return csr.getWidth() != 0 ? csr.getWidth() + nominalWidth : defaultWidth; - } - } + private CharStringRenderer getRendererForMapping(Mapping m) throws IOException { + CharStringRenderer csr = null; + if (((Number) getProperty("CharstringType")).intValue() == 2) { + List lSeq = m.toType2Sequence(); + csr = new CharStringRenderer(false); + csr.render(lSeq); + } else { + List lSeq = m.toType1Sequence(); + csr = new CharStringRenderer(); + csr.render(lSeq); + } + return csr; + } - // ---- SID Width not found, return the nodef width - return getNotDefWidth(defaultWidth, nominalWidth); - } - + /** + * Returns the witdth of the .notdef character. + * + * @param defaultWidth default width + * @param nominalWidth nominal width + * @return the calculated width for the .notdef character + * @throws IOException if something went wrong + */ protected int getNotDefWidth(int defaultWidth, int nominalWidth) throws IOException { CharStringRenderer csr; byte[] glyphDesc = this.getCharStringsDict().get(".notdef"); @@ -260,6 +318,36 @@ return csr.getWidth() != 0 ? csr.getWidth() + nominalWidth : defaultWidth; } + /** + * Return the Width value of the given Glyph identifier + * + * @param SID + * @return -1 if the SID is missing from the Font. + * @throws IOException + */ + public int[] getBoundingBox(int SID) throws IOException { + Mapping m = sidMappings.get(SID); + if (m != null) { + CharStringRenderer csr = getRendererForMapping(m); + return csr.getBounds(); + } + // ---- SID Width not found, return the nodef width + return new int[4]; + } + + /** + * Gets the name of a character from the given SID + * @param SID The given SID + * @return The name of the found character + */ + public String getNameOfCharFromCode(int SID) { + if (sidMappings.get(SID) != null) { + return sidMappings.get(SID).getName(); + } else { + return ""; + } + } + /** * Returns the CFFEncoding of the font. * @return the encoding @@ -336,50 +424,51 @@ + charStringsDict + "]"; } + /** + * Sets the global subroutine index data. + * @param globalSubrIndex the IndexData object containing the global subroutines + */ + public void setGlobalSubrIndex(IndexData globalSubrIndexValue) { + globalSubrIndex = globalSubrIndexValue; + } - /** - * Sets the global subroutine index data. - * @param globalSubrIndex the IndexData object containing the global subroutines - */ - public void setGlobalSubrIndex(IndexData globalSubrIndex) { - this.globalSubrIndex = globalSubrIndex; - } + /** + * Returns the global subroutine index data. + * @return the dictionary + */ + public IndexData getGlobalSubrIndex() + { + return globalSubrIndex; + } - /** - * Returns the global subroutine index data. - * @return the dictionary - */ - public IndexData getGlobalSubrIndex() { - return globalSubrIndex; - } + /** + * Returns the local subroutine index data. + * @return the dictionary + */ + public IndexData getLocalSubrIndex() + { + return localSubrIndex; + } - /** - * Returns the local subroutine index data. - * @return the dictionary - */ - public IndexData getLocalSubrIndex() { - return localSubrIndex; - } + /** + * Sets the local subroutine index data. + * @param localSubrIndexValue the IndexData object containing the local subroutines + */ + public void setLocalSubrIndex(IndexData localSubrIndexValue) { + localSubrIndex = localSubrIndexValue; + } - /** - * Sets the local subroutine index data. - * @param localSubrIndex the IndexData object containing the local subroutines - */ - public void setLocalSubrIndex(IndexData localSubrIndex) { - this.localSubrIndex = localSubrIndex; - } + /** + * This class is used for the font mapping. + * + */ + public class Mapping + { + private int mappedCode; + private int mappedSID; + private String mappedName; + private byte[] mappedBytes; - /** - * This class is used for the font mapping. - * - */ - public class Mapping - { - private int mappedCode; - private int mappedSID; - private String mappedName; - private byte[] mappedBytes; - /** * Converts the mapping into a Type1-sequence. * @return the Type1-sequence @@ -458,4 +547,4 @@ this.mappedBytes = bytes; } } -} \ No newline at end of file +}