Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java?rev=1621411&r1=1621410&r2=1621411&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java Sat Aug 30 02:26:57 2014 @@ -17,47 +17,185 @@ package org.apache.pdfbox.pdmodel.font; import java.io.IOException; -import java.io.InputStream; import java.util.HashMap; import java.util.Map; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.fontbox.util.BoundingBox; import org.apache.pdfbox.cos.COSArray; import org.apache.pdfbox.cos.COSBase; import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSNumber; -import org.apache.pdfbox.io.IOUtils; -import org.apache.pdfbox.pdmodel.common.PDRectangle; -import org.apache.pdfbox.util.ResourceLoader; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.Vector; /** - * A CIDFont. + * A CIDFont. A CIDFont is a PDF object that contains information about a CIDFont program. Although + * its Type value is Font, a CIDFont is not actually a font. * * @author Ben Litchfield */ -public abstract class PDCIDFont extends PDFont +public abstract class PDCIDFont implements COSObjectable { - private static final Log LOG = LogFactory.getLog(PDCIDFont.class); + protected final PDType0Font parent; - private PDType0Font parent; - private Map<Integer, Float> widthCache; - private long defaultWidth; + private Map<Integer, Float> widths; + private float defaultWidth; + + private Map<Integer, Float> verticalDisplacementY = new HashMap<Integer, Float>(); // w1y + private Map<Integer, Vector> positionVectors = new HashMap<Integer, Vector>(); // v + private float[] dw2; + + protected final COSDictionary dict; + private PDFontDescriptor fontDescriptor; /** * Constructor. * * @param fontDictionary The font dictionary according to the PDF specification. */ - protected PDCIDFont(COSDictionary fontDictionary, PDType0Font parent) + protected PDCIDFont(COSDictionary fontDictionary, PDType0Font parent) throws IOException { - super(fontDictionary); + this.dict = fontDictionary; this.parent = parent; - extractWidths(); + readWidths(); + readVerticalDisplacements(); + } + + private void readWidths() + { + widths = new HashMap<Integer, Float>(); + COSArray widths = (COSArray) dict.getDictionaryObject(COSName.W); + if (widths != null) + { + int size = widths.size(); + int counter = 0; + while (counter < size) + { + COSNumber firstCode = (COSNumber) widths.getObject(counter++); + COSBase next = widths.getObject(counter++); + if (next instanceof COSArray) + { + COSArray array = (COSArray) next; + int startRange = firstCode.intValue(); + int arraySize = array.size(); + for (int i = 0; i < arraySize; i++) + { + COSNumber width = (COSNumber) array.get(i); + this.widths.put(startRange + i, width.floatValue()); + } + } + else + { + COSNumber secondCode = (COSNumber) next; + COSNumber rangeWidth = (COSNumber) widths.getObject(counter++); + int startRange = firstCode.intValue(); + int endRange = secondCode.intValue(); + float width = rangeWidth.floatValue(); + for (int i = startRange; i <= endRange; i++) + { + this.widths.put(i, width); + } + } + } + } + + } + + private void readVerticalDisplacements() + { + // default position vector and vertical displacement vector + COSArray cosDW2 = (COSArray) dict.getDictionaryObject(COSName.DW2); + if (cosDW2 != null) + { + dw2 = new float[2]; + dw2[0] = ((COSNumber)cosDW2.get(0)).floatValue(); + dw2[1] = ((COSNumber)cosDW2.get(1)).floatValue(); + } + else + { + dw2 = new float[] { 880, -1000 }; + } + + // vertical metrics for individual CIDs. + COSArray w2 = (COSArray) dict.getDictionaryObject(COSName.W2); + if (w2 != null) + { + for (int i = 0; i < w2.size(); i++) + { + COSNumber c = (COSNumber)w2.get(i); + COSBase next = w2.get(++i); + if (next instanceof COSArray) + { + COSArray array = (COSArray)next; + for (int j = 0; j < array.size(); j++) + { + int cid = c.intValue() + j; + COSNumber w1y = (COSNumber) array.get(j); + COSNumber v1x = (COSNumber) array.get(++j); + COSNumber v1y = (COSNumber) array.get(++j); + verticalDisplacementY.put(cid, w1y.floatValue()); + positionVectors.put(cid, new Vector(v1x.floatValue(), v1y.floatValue())); + } + } + else + { + int first = c.intValue(); + int last = ((COSNumber) next).intValue(); + COSNumber w1y = (COSNumber) w2.get(++i); + COSNumber v1x = (COSNumber) w2.get(++i); + COSNumber v1y = (COSNumber) w2.get(++i); + for (int cid = first; cid <= last; cid++) + { + verticalDisplacementY.put(cid, w1y.floatValue()); + positionVectors.put(cid, new Vector(v1x.floatValue(), v1y.floatValue())); + } + } + } + } + } + + @Override + public COSDictionary getCOSObject() + { + return dict; + } + + /** + * The PostScript name of the font. + * + * @return The postscript name of the font. + */ + public String getBaseFont() + { + return dict.getNameAsString(COSName.BASE_FONT); } /** + * This will get the font descriptor for this font. A font descriptor is required for a CIDFont. + * + * @return The font descriptor for this font. + */ + public PDFontDescriptor getFontDescriptor() + { + if (fontDescriptor == null) + { + COSDictionary fd = (COSDictionary) dict.getDictionaryObject(COSName.FONT_DESC); + if (fd != null) + { + fontDescriptor = new PDFontDescriptorDictionary(fd); + } + } + return fontDescriptor; + } + + /** + * Returns the font matrix, which represents the transformation from glyph space to text space. + */ + public abstract Matrix getFontMatrix(); + + /** * Returns the Type 0 font which is the parent of this font. * * @return parent Type 0 font @@ -68,30 +206,23 @@ public abstract class PDCIDFont extends } /** - * This will get the fonts bounding box. - * - * @return The fonts bounding box. - * @throws IOException If there is an error getting the font bounding box. + * Returns the font's bounding box. */ - @Override - public PDRectangle getFontBoundingBox() throws IOException - { - throw new RuntimeException("getFontBoundingBox(): Not yet implemented"); - } + public abstract BoundingBox getBoundingBox() throws IOException; /** - * This will get the default width. The default value for the default width is 1000. + * This will get the default width. The default value for the default width is 1000. * * @return The default width for the glyphs in this font. */ - public long getDefaultWidth() + private float getDefaultWidth() { if (defaultWidth == 0) { COSNumber number = (COSNumber) dict.getDictionaryObject(COSName.DW); if (number != null) { - defaultWidth = number.intValue(); + defaultWidth = number.floatValue(); } else { @@ -102,121 +233,131 @@ public abstract class PDCIDFont extends } /** - * This will get the font width for a character. + * Returns the default position vector (v). * - * @param c The character code to get the width for. - * @param offset The offset into the array. - * @param length The length of the data. - * @return The width is in 1000 unit of text space, ie 333 or 777 - * @throws IOException If an error occurs while parsing. + * @param cid CID */ - @Override - public float getFontWidth(byte[] c, int offset, int length) throws IOException + private Vector getDefaultPositionVector(int cid) { - float retval = getDefaultWidth(); - int code = getCodeFromArray(c, offset, length); - - Float widthFloat = widthCache.get(code); - if (widthFloat != null) + float w0; + if (widths.containsKey(cid)) { - retval = widthFloat; + Float w = widths.get(cid); + if (w != null) + { + w0 = w; + } + else + { + w0 = getDefaultWidth(); + } } - return retval; + else + { + w0 = getDefaultWidth(); + } + + return new Vector(w0 / 2, dw2[0]); } - private void extractWidths() + /** + * Returns the position vector (v) in 1/1000 text space, for the given character code. + * + * @param code character code + * @return position vector (v) + */ + public Vector getPositionVector(int code) { - if (widthCache == null) + int cid = codeToCID(code); + Vector v = positionVectors.get(cid); + if (v != null) { - widthCache = new HashMap<Integer, Float>(); - COSArray widths = (COSArray) dict.getDictionaryObject(COSName.W); - if (widths != null) - { - int size = widths.size(); - int counter = 0; - while (counter < size) - { - COSNumber firstCode = (COSNumber) widths.getObject(counter++); - COSBase next = widths.getObject(counter++); - if (next instanceof COSArray) - { - COSArray array = (COSArray) next; - int startRange = firstCode.intValue(); - int arraySize = array.size(); - for (int i = 0; i < arraySize; i++) - { - COSNumber width = (COSNumber) array.get(i); - widthCache.put(startRange + i, width.floatValue()); - } - } - else - { - COSNumber secondCode = (COSNumber) next; - COSNumber rangeWidth = (COSNumber) widths.getObject(counter++); - int startRange = firstCode.intValue(); - int endRange = secondCode.intValue(); - float width = rangeWidth.floatValue(); - for (int i = startRange; i <= endRange; i++) - { - widthCache.put(i, width); - } - } - } - } + return v; + } + else + { + return getDefaultPositionVector(cid); } } /** - * This will get the font height for a character. - * - * @param c The character code to get the height for. - * @param offset The offset into the array. - * @param length The length of the data. - * - * @return The width is in 1000 unit of text space, ie 333 or 777 + * Returns the y-component of the vertical displacement vector (w1). * - * @throws IOException If an error occurs while parsing. + * @param code character code + * @return w1y */ - @Override - public float getFontHeight(byte[] c, int offset, int length) throws IOException + public float getVerticalDisplacementVectorY(int code) { - float retval = 0; - PDFontDescriptor desc = getFontDescriptor(); - float xHeight = desc.getXHeight(); - float capHeight = desc.getCapHeight(); - if (xHeight != 0f && capHeight != 0) + int cid = codeToCID(code); + Float w1y = verticalDisplacementY.get(cid); + if (w1y != null) { - // do an average of these two. Can we do better??? - retval = (xHeight + capHeight) / 2f; + return w1y; } - else if (xHeight != 0) + else { - retval = xHeight; + return dw2[1]; } - else if (capHeight != 0) + } + + /** + * This will get the font height for a character. + * + * @param code character code + * @return The height is in 1000 unit of text space, ie 333 or 777 + */ + public abstract float getHeight(int code) throws IOException; + + /** + * Returns the width of the given character. + * + * @param code character code + */ + public float getWidth(int code) throws IOException + { + // these widths are supposed to be consistent with the actual widths given in the CIDFont + // program, but PDFBOX-563 shows that when they are not, Acrobat overrides the embedded + // font widths with the widths given in the font dictionary + + int cid = codeToCID(code); + if (widths.containsKey(cid)) { - retval = capHeight; + Float w = widths.get(cid); + if (w != null) + { + return w; + } + else + { + return getDefaultWidth(); + } } else { - retval = 0; - } - if (retval == 0) - { - retval = desc.getAscent(); + return getWidthFromFont(code); } - return retval; } /** + * Returns the width of a glyph in the embedded font file. + * + * @param code character code + * @return width in glyph space + * @throws IOException if the font could not be read + */ + protected abstract float getWidthFromFont(int code) throws IOException; + + /** + * Returns true if the font file is embedded in the PDF. + */ + public abstract boolean isEmbedded(); + + /** * This will get the average font width for all characters. * * @return The width is in 1000 unit of text space, ie 333 or 777 - * - * @throws IOException If an error occurs while parsing. */ - @Override - public float getAverageFontWidth() throws IOException + public float getAverageFontWidth() { float totalWidths = 0.0f; float characterCount = 0.0f; @@ -258,117 +399,28 @@ public abstract class PDCIDFont extends return average; } - @Override - public float getFontWidth(int charCode) - { - float width = getDefaultWidth(); - if (widthCache.containsKey(charCode)) - { - width = widthCache.get(charCode); - } - return width; - } - /** - * Extract the CIDSystemInfo. - * @return the CIDSystemInfo as String + * Returns the CID for the given character code. If not found then CID 0 is returned. + * + * @param code character code + * @return CID */ - private String getCIDSystemInfo() - { - String cidSystemInfo = null; - COSDictionary dict = (COSDictionary) this.dict.getDictionaryObject(COSName.CIDSYSTEMINFO); - if (dict != null) - { - String ordering = dict.getString(COSName.ORDERING); - String registry = dict.getString(COSName.REGISTRY); - int supplement = dict.getInt(COSName.SUPPLEMENT); - cidSystemInfo = registry + "-" + ordering + "-" + supplement; - } - return cidSystemInfo; - } - - // todo: do we want to do this at all? Isn't the parent Type0 font responsible for this? - @Override - protected void determineEncoding() - { - String cidSystemInfo = getCIDSystemInfo(); - if (cidSystemInfo == null) - { - // todo: CIDSystemInfo is required, so this is an error (perform recovery?) - LOG.error("Missing CIDSystemInfo in CIDFont dictionary"); - return; - } + public abstract int codeToCID(int code); - if (cidSystemInfo.contains("Identity")) - { - cidSystemInfo = "Identity-H"; - } - else if (cidSystemInfo.startsWith("Adobe-UCS-")) - { - cidSystemInfo = "Adobe-Identity-UCS"; - } - else - { - cidSystemInfo = cidSystemInfo.substring(0, cidSystemInfo.lastIndexOf('-')) + "-UCS2"; - } - - cmap = cmapObjects.get(cidSystemInfo); - if (cmap == null) - { - InputStream cmapStream = null; - try - { - // look for a predefined CMap with the given name - cmapStream = ResourceLoader.loadResource(resourceRootCMAP + cidSystemInfo); - if (cmapStream != null) - { - cmap = parseCmap(resourceRootCMAP, cmapStream); - if (cmap == null) - { - LOG.error("Could not parse predefined CMAP file for '" + - cidSystemInfo + "'"); - } - } - else - { - LOG.debug("'" + cidSystemInfo + "' isn't a predefined CMap, most " + - "likely it's embedded in the pdf itself."); - } - } - catch (IOException exception) - { - LOG.error("Could not find predefined CMAP file for '" + cidSystemInfo + "'"); - } - finally - { - IOUtils.closeQuietly(cmapStream); - } - } - } + /** + * Returns the GID for the given character code. + * + * @param code character code + * @return GID + */ + public abstract int codeToGID(int code) throws IOException; - @Override - public String encode(byte[] c, int offset, int length) throws IOException - { - String result; - if (cmap != null) - { - result = cmapEncoding(getCodeFromArray(c, offset, length), length, true, cmap); - } - else - { - result = super.encode(c, offset, length); - } - return result; - } - - @Override public void clear() { - super.clear(); - if (widthCache != null) + if (widths != null) { - widthCache.clear(); - widthCache = null; + widths.clear(); + widths = null; } } }
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFont.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFont.java?rev=1621411&r1=1621410&r2=1621411&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFont.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFont.java Sat Aug 30 02:26:57 2014 @@ -18,33 +18,24 @@ package org.apache.pdfbox.pdmodel.font; import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.fontbox.afm.FontMetric; import org.apache.fontbox.cmap.CMap; -import org.apache.fontbox.cmap.CMapParser; +import org.apache.fontbox.util.BoundingBox; import org.apache.pdfbox.cos.COSArray; import org.apache.pdfbox.cos.COSBase; import org.apache.pdfbox.cos.COSDictionary; -import org.apache.pdfbox.cos.COSFloat; -import org.apache.pdfbox.cos.COSInteger; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSNumber; import org.apache.pdfbox.cos.COSStream; -import org.apache.pdfbox.encoding.DictionaryEncoding; -import org.apache.pdfbox.encoding.Encoding; import org.apache.pdfbox.io.IOUtils; import org.apache.pdfbox.pdmodel.common.COSArrayList; import org.apache.pdfbox.pdmodel.common.COSObjectable; -import org.apache.pdfbox.pdmodel.common.PDMatrix; -import org.apache.pdfbox.pdmodel.common.PDRectangle; -import org.apache.pdfbox.util.ResourceLoader; +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.Vector; /** * This is the base class for all PDF fonts. @@ -54,334 +45,203 @@ import org.apache.pdfbox.util.ResourceLo public abstract class PDFont implements COSObjectable { private static final Log LOG = LogFactory.getLog(PDFont.class); - private static final byte[] SPACE_BYTES = { (byte) 32 }; // formerly in PDSimpleFont + private static final Matrix DEFAULT_FONT_MATRIX = new Matrix(0.001f, 0, 0, 0.001f, 0, 0); - protected static final String resourceRootCMAP = "org/apache/pdfbox/resources/cmap/"; - protected static Map<String, CMap> cmapObjects = - Collections.synchronizedMap(new HashMap<String, CMap>()); // todo: why synchronized? + protected final COSDictionary dict; + private final CMap toUnicodeCMap; + private PDFontDescriptor fontDescriptor; - private static final String[] SINGLE_CHAR_STRING = new String[256]; - private static final String[][] DOUBLE_CHAR_STRING = new String[256][256]; - static + private List<Integer> widths; + private float avgFontWidth; + private float fontWidthOfSpace = -1f; + private Boolean isSymbolic; + + /** + * Constructor for embedding. + */ + protected PDFont() { - for (int i = 0; i < 256; i++) - { - try - { - SINGLE_CHAR_STRING[i] = new String(new byte[] { (byte) i }, "ISO-8859-1"); - } - catch (UnsupportedEncodingException e) - { - // Nothing should happen here - LOG.error(e,e); - } - for (int j = 0; j < 256; j++) - { - try - { - DOUBLE_CHAR_STRING[i][j] = new String(new byte[] { (byte) i, (byte) j }, - "UTF-16BE"); - } - catch (UnsupportedEncodingException e) - { - // Nothing should happen here - LOG.error(e, e); - } - } - } + dict = new COSDictionary(); + dict.setItem(COSName.TYPE, COSName.FONT); + toUnicodeCMap = null; } - private static String getStringFromArray(byte[] c, int offset, int length) throws IOException + /** + * Constructor. + * + * @param fontDictionary Font dictionary. + */ + protected PDFont(COSDictionary fontDictionary) throws IOException { - String retval; - if (length == 1) + dict = fontDictionary; + + // font descriptor + COSDictionary fd = (COSDictionary) dict.getDictionaryObject(COSName.FONT_DESC); + if (fd != null) { - retval = SINGLE_CHAR_STRING[(c[offset] + 256) % 256]; + fontDescriptor = new PDFontDescriptorDictionary(fd); } - else if (length == 2) + else { - retval = DOUBLE_CHAR_STRING[(c[offset] + 256) % 256][(c[offset + 1] + 256) % 256]; + fontDescriptor = null; + } + + // ToUnicode CMap + COSBase toUnicode = dict.getDictionaryObject(COSName.TO_UNICODE); + if (toUnicode != null) + { + toUnicodeCMap = readCMap(toUnicode); + if (toUnicodeCMap != null && !toUnicodeCMap.hasUnicodeMappings()) + { + LOG.warn("Invalid ToUnicode CMap in font " + getName()); + } } else { - throw new IOException("Error:Unknown character length:" + length); + toUnicodeCMap = null; } - return retval; } /** - * The Font dictionary. + * Returns the font descriptor, may be null. */ - protected COSDictionary dict; + public PDFontDescriptor getFontDescriptor() + { + return fontDescriptor; + } /** - * The font matrix. + * Sets the font descriptor. */ - protected PDMatrix fontMatrix = null; - - // CMap / Encoding - protected CMap cmap = null; // only used when this is a Type0 font with a CMap - protected Encoding fontEncoding = null; // only used when this font has an encoding - - // the CMap holding the ToUnicode mapping - private CMap toUnicodeCmap = null; - private boolean hasToUnicode = false; - - private List<Integer> widths = null; - - protected PDFontDescriptor fontDescriptor = null; - private boolean widthsAreMissing = false; - - // formerly in PDSimpleFont - private final HashMap<Integer, Float> fontSizes = new HashMap<Integer, Float>(128); - private float avgFontWidth = 0.0f; - private float avgFontHeight = 0.0f; - private float fontWidthOfSpace = -1f; + void setFontDescriptor(PDFontDescriptor fontDescriptor) + { + this.fontDescriptor = fontDescriptor; + } /** - * This will clear AFM resources that are stored statically. This is usually not a problem - * unless you want to reclaim resources for a long running process. - * - * SPECIAL NOTE: The font calculations are currently in COSObject, which is where they will - * reside until PDFont is mature enough to take them over. PDFont is the appropriate place for - * them and not in COSObject but we need font calculations for text extraction. THIS METHOD WILL - * BE MOVED OR REMOVED TO ANOTHER LOCATION IN A FUTURE VERSION OF PDFBOX. + * Reads a CMap given a COS Stream or Name. May return null if a predefined CMap does not exist. * - * @deprecated This method will be removed in a future version of PDFBox. + * @param base COSName or COSStream */ - @Deprecated - public static void clearResources() + protected final CMap readCMap(COSBase base) throws IOException { - cmapObjects.clear(); + if (base instanceof COSName) + { + // predefined CMap + String name = ((COSName)base).getName(); + return CMapManager.getPredefinedCMap(name); + } + else if (base instanceof COSStream) + { + // embedded CMap + InputStream input = null; + try + { + input = ((COSStream)base).getUnfilteredStream(); + return CMapManager.parseCMap(input); + } + finally + { + IOUtils.closeQuietly(input); + } + } + else + { + throw new IOException("Expected Name or Stream"); + } } - /** - * Constructor. - */ - protected PDFont() + @Override + public COSDictionary getCOSObject() { - dict = new COSDictionary(); - dict.setItem(COSName.TYPE, COSName.FONT); + return dict; } /** - * Constructor. - * - * @param fontDictionary The font dictionary according to the PDF specification. + * Returns the position vector (v), in text space, for the given character. + * This represents the position of vertical origin relative to horizontal origin, for + * horizontal writing it will always be (0, 0). For vertical writing both x and y are set. + * + * @param code character code + * @return position vector */ - protected PDFont(COSDictionary fontDictionary) + public Vector getPositionVector(int code) { - dict = fontDictionary; - determineEncoding(); + throw new UnsupportedOperationException("Horizontal fonts have no position vector"); } /** - * This will get the font descriptor for this font. - * - * @return The font descriptor for this font. + * Returns the displacement vector (w0, w1) in text space, for the given character. + * For horizontal text only the x component is used, for vertical text only the y component. + * + * @param code character code + * @return displacement vector */ - public PDFontDescriptor getFontDescriptor() + public Vector getDisplacement(int code) throws IOException { - if (fontDescriptor == null) - { - COSDictionary fd = (COSDictionary) dict.getDictionaryObject(COSName.FONT_DESC); - if (fd != null) - { - fontDescriptor = new PDFontDescriptorDictionary(fd); - } - else - { - FontMetric afm = getAFM(); - if (afm != null) - { - fontDescriptor = new PDFontDescriptorAFM(afm); - } - } - } - return fontDescriptor; + return new Vector(getWidth(code) / 1000, 0); } /** - * Determines the encoding for the font. This method as to be overwritten, as there are - * different possibilities to define a mapping. + * Returns the advance width of the given character, in glyph space. + * + * @param code character code */ - protected void determineEncoding() + public float getWidth(int code) throws IOException { - COSBase encoding = dict.getDictionaryObject(COSName.ENCODING); - Encoding fontEncoding = null; - if (encoding != null) + // Acrobat overrides the widths in the font program on the conforming reader's system with + // the widths specified in the font dictionary." (Adobe Supplement to the ISO 32000) + // + // Note: The Adobe Supplement says that the override happens "If the font program is not + // embedded", however PDFBOX-427 shows that it also applies to embedded fonts. + + // Type1, Type1C, Type3 + int firstChar = dict.getInt(COSName.FIRST_CHAR, -1); + int lastChar = dict.getInt(COSName.LAST_CHAR, -1); + if (getWidths().size() > 0 && code >= firstChar && code <= lastChar) { - if (encoding instanceof COSName) - { - COSName encodingName = (COSName)encoding; - try - { - fontEncoding = Encoding.getInstance(encodingName); - } - catch (IOException exception) - { - LOG.debug("Debug: Could not find encoding for " + encodingName); - } - } - else if (encoding instanceof COSDictionary) - { - try - { - fontEncoding = new DictionaryEncoding((COSDictionary) encoding); - } - catch (IOException exception) - { - LOG.error("Error: Could not create the DictionaryEncoding"); - } - } + return getWidths().get(code - firstChar).floatValue(); } - this.fontEncoding = fontEncoding; - extractToUnicodeEncoding(); - } - - protected final void extractToUnicodeEncoding() - { - COSName encodingName; - String cmapName; - COSBase toUnicode = dict.getDictionaryObject(COSName.TO_UNICODE); - if (toUnicode != null) + else { - hasToUnicode = true; - if (toUnicode instanceof COSStream) + PDFontDescriptor fd = getFontDescriptor(); + if (fd instanceof PDFontDescriptorDictionary && + ((PDFontDescriptorDictionary) fd).hasWidths()) { - try - { - InputStream is = ((COSStream) toUnicode).getUnfilteredStream(); - toUnicodeCmap = parseCmap(resourceRootCMAP, is); - IOUtils.closeQuietly(is); - } - catch (IOException exception) - { - LOG.error("Error: Could not load embedded ToUnicode CMap"); - } + return fd.getMissingWidth(); } - else if (toUnicode instanceof COSName) + else { - encodingName = (COSName) toUnicode; - toUnicodeCmap = cmapObjects.get(encodingName.getName()); - if (toUnicodeCmap == null) - { - cmapName = encodingName.getName(); - String resourceName = resourceRootCMAP + cmapName; - try - { - toUnicodeCmap = parseCmap(resourceRootCMAP, - ResourceLoader.loadResource(resourceName)); - } - catch (IOException exception) - { - LOG.error("Error: Could not find predefined ToUnicode CMap file for '" + - cmapName + "'"); - } - if (toUnicodeCmap == null) - { - LOG.error("Error: Could not parse predefined ToUnicode CMap file for '" + - cmapName + "'"); - } - } + // if there's nothing to override with, then obviously we fall back to the font + return getWidthFromFont(code); } } } - @Override - public COSBase getCOSObject() - { - return dict; - } + /** + * Returns the width of a glyph in the embedded font file. + * + * @param code character code + * @return width in glyph space + * @throws IOException if the font could not be read + */ + protected abstract float getWidthFromFont(int code) throws IOException; /** - * This will get the font width for a character. - * - * @param c The character code to get the width for. - * @param offset The offset into the array. - * @param length The length of the data. - * @return The width is in 1000 unit of text space, ie 333 or 777 - * @throws IOException If an error occurs while parsing. + * Returns true if the font file is embedded in the PDF. */ - public float getFontWidth(byte[] c, int offset, int length) throws IOException - { - int code = getCodeFromArray(c, offset, length); - Float fontWidth = fontSizes.get(code); - if (fontWidth == null) - { - fontWidth = getFontWidth(code); - if (fontWidth <= 0) - { - // TODO should this be in PDType1Font?? - fontWidth = getFontWidthFromAFMFile(code); - } - fontSizes.put(code, fontWidth); - } - return fontWidth; - } + public abstract boolean isEmbedded(); /** - * This will get the font height for a character. + * Returns the height of the given character, in glyph space. This can be expensive to + * calculate. Results are only approximate. * - * @param c The character code to get the height for. - * @param offset The offset into the array. - * @param length The length of the data. - * @return The height is in 1000 unit of text space, ie 333 or 777 - * @throws IOException If an error occurs while parsing. - */ - public float getFontHeight(byte[] c, int offset, int length) throws IOException - { - // maybe there is already a precalculated value - if (avgFontHeight > 0) - { - return avgFontHeight; - } - float retval = 0; - FontMetric metric = getAFM(); - if (metric != null) - { - int code = getCodeFromArray(c, offset, length); - Encoding encoding = getFontEncoding(); - String characterName = encoding.getName(code); - retval = metric.getCharacterHeight(characterName); - } - else - { - PDFontDescriptor desc = getFontDescriptor(); - if (desc != null) - { - // the following values are all more or less accurate at least all are average - // values. Maybe we'll find another way to get those value for every single glyph - // in the future if needed - PDRectangle fontBBox = desc.getFontBoundingBox(); - if (fontBBox != null) - { - retval = fontBBox.getHeight() / 2; - } - if (retval == 0) - { - retval = desc.getCapHeight(); - } - if (retval == 0) - { - retval = desc.getAscent(); - } - if (retval == 0) - { - retval = desc.getXHeight(); - if (retval > 0) - { - retval -= desc.getDescent(); - } - } - avgFontHeight = retval; - } - } - return retval; - } + * @param code character code + */ + public abstract float getHeight(int code) throws IOException; /** - * This will get the width of this string for this font. + * Returns the width of the given Unicode string. * * @param string The string to get the width of. * @return The width of the string in 1000 units of text space, ie 333 567... @@ -389,11 +249,11 @@ public abstract class PDFont implements */ public float getStringWidth(String string) throws IOException { - byte[] data = string.getBytes("ISO-8859-1"); + byte[] data = string.getBytes("ISO-8859-1"); // todo: *no*, these are *not* character codes float totalWidth = 0; for (int i = 0; i < data.length; i++) { - totalWidth += getFontWidth(data, i, 1); + totalWidth += getWidth(data[i]); } return totalWidth; } @@ -402,9 +262,9 @@ public abstract class PDFont implements * This will get the average font width for all characters. * * @return The width is in 1000 unit of text space, ie 333 or 777 - * @throws IOException If an error occurs while parsing. */ - public float getAverageFontWidth() throws IOException + // todo: this method is highly suspicious, the average glyph width is not usually a good metric + public float getAverageFontWidth() { float average; if (avgFontWidth != 0.0f) @@ -435,7 +295,7 @@ public abstract class PDFont implements } else { - average = getAverageFontWidthFromAFMFile(); + average = 0; } avgFontWidth = average; } @@ -443,185 +303,42 @@ public abstract class PDFont implements } /** - * Used for multibyte encodings. - * - * @param data The array of data. - * @param offset The offset into the array. - * @param length The number of bytes to use. - * @return The int value of data from the array. - */ - public int getCodeFromArray(byte[] data, int offset, int length) - { - int code = 0; - for (int i = 0; i < length; i++) - { - code <<= 8; - code |= (data[offset + i] + 256) % 256; - } - return code; - } - - /** - * This will attempt to get the font width from an AFM file. - * - * @param code The character code we are trying to get. - * @return The font width from the AFM file. - * @throws IOException if we cannot find the width. - */ - private float getFontWidthFromAFMFile(int code) throws IOException - { - float retval = 0; - FontMetric metric = getAFM(); - if (metric != null) - { - String characterName = fontEncoding.getName(code); - retval = metric.getCharacterWidth(characterName); - } - return retval; - } - - /** - * This will attempt to get the average font width from an AFM file. - * - * @return The average font width from the AFM file. - * @throws IOException if we cannot find the width. - */ - private float getAverageFontWidthFromAFMFile() throws IOException - { - float retval = 0; - FontMetric metric = getAFM(); - if (metric != null) - { - retval = metric.getAverageCharacterWidth(); - } - return retval; - } - - /** - * This will get an AFM object if one exists. - * - * @return The afm object from the name. - */ - protected FontMetric getAFM() - { - return null; - } - - /** - * Encode the given value using the CMap of the font. - * - * @param code the code to encode. - * @param length the byte length of the given code. - * @param isCIDFont indicates that the used font is a CID font. - * - * @return The value of the encoded character. - * @throws IOException if something went wrong - */ - protected final String cmapEncoding(int code, int length, boolean isCIDFont, CMap sourceCmap) - throws IOException - { - String retval = null; - // there is not sourceCmap if this is a descendant font - if (sourceCmap == null) - { - sourceCmap = cmap; - } - if (sourceCmap != null) - { - retval = sourceCmap.lookup(code, length); - if (retval == null && isCIDFont) - { - retval = sourceCmap.lookupCID(code); - } - } - return retval; - } - - /** - * This will perform the encoding of a character if needed. - * - * @param c The character to encode. - * @param offset The offset into the array to get the data - * @param length The number of bytes to read. - * @return The value of the encoded character. - * @throws IOException If there is an error during the encoding. + * Reads a character code from a content stream string. Codes may be up to 4 bytes long. + * + * @param in string stream + * @return character code + * @throws IOException if the CMap or stream cannot be read */ - public String encode(byte[] c, int offset, int length) throws IOException - { - String retval = null; - int code = getCodeFromArray(c, offset, length); - if (toUnicodeCmap != null) - { - retval = cmapEncoding(code, length, false, toUnicodeCmap); - } - if (retval == null && cmap != null) - { - retval = cmapEncoding(code, length, false, cmap); - } - - // there is no cmap but probably an encoding with a suitable mapping - if (retval == null) - { - if (fontEncoding != null) - { - retval = fontEncoding.getCharacter(code); - } - if (retval == null && (cmap == null || length == 2)) - { - retval = getStringFromArray(c, offset, length); - } - } - return retval; - } - - public int encodeToCID(byte[] c, int offset, int length) throws IOException - { - int code = -1; - if (encode(c, offset, length) != null) - { - code = getCodeFromArray(c, offset, length); - } - return code; - } + public abstract int readCode(InputStream in) throws IOException; /** - * Parse the given CMap. - * - * @param cmapRoot the root path pointing to the provided CMaps - * @param cmapStream the CMap to be read - * @return the parsed CMap + * Returns the Unicode character sequence which corresponds to the given character code. + * + * @param code character code + * @return Unicode character(s) */ - protected final CMap parseCmap(String cmapRoot, InputStream cmapStream) + public String toUnicode(int code) { - CMap targetCmap = null; - if (cmapStream != null) + // if the font dictionary contains a ToUnicode CMap, use that CMap + if (toUnicodeCMap != null) { - CMapParser parser = new CMapParser(); - try + if (toUnicodeCMap.getName() != null && toUnicodeCMap.getName().startsWith("Identity-")) { - targetCmap = parser.parse(cmapRoot, cmapStream); - // limit the cache to external CMaps - if (cmapRoot != null) - { - cmapObjects.put(targetCmap.getName(), targetCmap); - } + // handle the undocumented case of using Identity-H/V as a ToUnicode CMap, this + // isn't actually valid as the Identity-x CMaps are code->CID maps, not + // code->Unicode maps. See sample_fonts_solidconvertor.pdf for an example. + return new String(new char[] { (char) code }); } - catch (IOException exception) + else { - LOG.error("An error occurs while reading a CMap", exception); + // proceed as normal + return toUnicodeCMap.toUnicode(code); } } - return targetCmap; - } - /** - * This will get or create the encoder. - * - * @return The encoding to use. - */ - public Encoding getFontEncoding() - { - return fontEncoding; + // if no value has been produced, there is no way to obtain Unicode for the character. + // this behaviour can be overridden is subclasses, but this method *must* return null here + return null; } /** @@ -636,8 +353,6 @@ public abstract class PDFont implements /** * This will get the subtype of font. - * - * @return The type of font that this is. */ public String getSubType() { @@ -645,59 +360,52 @@ public abstract class PDFont implements } /** - * Determines if the font is a type 1 font. - * - * @return returns true if the font is a type 1 font - */ - public boolean isType1Font() - { - return "Type1".equals(getSubType()); - } - - /** - * Determines if the font is a type 3 font. - * - * @return returns true if the font is a type 3 font - */ - public boolean isType3Font() - { - return "Type3".equals(getSubType()); - } - - /** - * Determines if the font is a type 0 font. - * - * @return returns true if the font is a type 0 font + * Returns true the font is a symbolic (that is, it does not use the Adobe Standard Roman + * character set). */ - public boolean isType0Font() + public final boolean isSymbolic() { - return "Type0".equals(getSubType()); + if (isSymbolic == null) + { + Boolean result = isFontSymbolic(); + if (result != null) + { + isSymbolic = result; + } + else + { + // unless we can prove that the font is symbolic, we assume that it is not + isSymbolic = true; + } + } + return isSymbolic; } /** - * Determines if the font is a true type font. - * - * @return returns true if the font is a true type font + * Internal implementation of isSymbolic, allowing for the fact that the result may be + * indeterminate. */ - public boolean isTrueTypeFont() + protected Boolean isFontSymbolic() { - return "TrueType".equals(getSubType()); + return getSymbolicFlag(); } /** - * Determines if the font is a symbolic font. - * - * @return returns true if the font is a symbolic font + * Returns the value of the symbolic flag, allowing for the fact that the result may be + * indeterminate. */ - public boolean isSymbolicFont() + protected Boolean getSymbolicFlag() { - return getFontDescriptor().isSymbolic(); + if (getFontDescriptor() != null) + { + // fixme: isSymbolic() defaults to false if the flag is missing so we can't trust this + return getFontDescriptor().isSymbolic(); + } + return null; } /** - * The PostScript name of the font. - * - * @return The postscript name of the font. + * Returns the PostScript name of the font. */ public String getBaseFont() { @@ -705,33 +413,26 @@ public abstract class PDFont implements } /** - * The code for the first char or -1 if there is none. - * - * @return The code for the first character. + * Returns the name of this font, either the PostScript "BaseName" or the Type 3 "Name". */ - public int getFirstChar() + public String getName() { - return dict.getInt(COSName.FIRST_CHAR, -1); + return getBaseFont(); } /** - * The code for the last char or -1 if there is none. - * - * @return The code for the last character. + * Returns the font's bounding box. */ - public int getLastChar() - { - return dict.getInt(COSName.LAST_CHAR, -1); - } + public abstract BoundingBox getBoundingBox() throws IOException; /** * The widths of the characters. This will be null for the standard 14 fonts. - * + * * @return The widths of the characters. */ - public List<Integer> getWidths() + protected final List<Integer> getWidths() { - if (widths == null && !widthsAreMissing) + if (widths == null) { COSArray array = (COSArray) dict.getDictionaryObject(COSName.WIDTHS); if (array != null) @@ -740,94 +441,18 @@ public abstract class PDFont implements } else { - widthsAreMissing = true; + widths = Collections.emptyList(); } } return widths; } /** - * This will get the matrix that is used to transform glyph space to text space. By default - * there are 1000 glyph units to 1 text space unit, but type3 fonts can use any value. - * - * Note: If this is a type3 font then it can be modified via the PDType3Font.setFontMatrix, - * otherwise this is a read-only property. - * - * @return The matrix to transform from glyph space to text space. + * Returns the font matrix, which represents the transformation from glyph space to text space. */ - public PDMatrix getFontMatrix() + public Matrix getFontMatrix() { - if (fontMatrix == null) - { - COSArray array = (COSArray) dict.getDictionaryObject(COSName.FONT_MATRIX); - if (array == null) - { - array = new COSArray(); - array.add(new COSFloat(0.001f)); - array.add(COSInteger.ZERO); - array.add(COSInteger.ZERO); - array.add(new COSFloat(0.001f)); - array.add(COSInteger.ZERO); - array.add(COSInteger.ZERO); - } - fontMatrix = new PDMatrix(array); - } - return fontMatrix; - } - - /** - * This will get the fonts bounding box. - * - * @return The fonts bounding box. - * @throws IOException If there is an error getting the bounding box. - */ - public PDRectangle getFontBoundingBox() throws IOException - { - return getFontDescriptor().getFontBoundingBox(); - } - - /** - * Determines the width of the given character. - * - * @param charCode the code of the given character - * @return the width of the character - */ - public float getFontWidth(int charCode) - { - float width = -1; - int firstChar = getFirstChar(); - int lastChar = getLastChar(); - if (charCode >= firstChar && charCode <= lastChar) - { - // maybe the font doesn't provide any widths - if (!widthsAreMissing) - { - getWidths(); - if (widths != null) - { - width = widths.get(charCode - firstChar).floatValue(); - } - } - } - else - { - PDFontDescriptor fd = getFontDescriptor(); - if (fd instanceof PDFontDescriptorDictionary) - { - width = fd.getMissingWidth(); - } - } - return width; - } - - /** - * Determines if a font as a ToUnicode entry. - * - * @return true if the font has a ToUnicode entry - */ - public boolean hasToUnicode() - { - return hasToUnicode; + return DEFAULT_FONT_MATRIX; } /** @@ -844,15 +469,15 @@ public abstract class PDFont implements { if (toUnicode != null) { - int spaceMapping = toUnicodeCmap.getSpaceMapping(); + int spaceMapping = toUnicodeCMap.getSpaceMapping(); if (spaceMapping > -1) { - fontWidthOfSpace = getFontWidth(spaceMapping); + fontWidthOfSpace = getWidth(spaceMapping); } } else { - fontWidthOfSpace = getFontWidth(SPACE_BYTES, 0, 1); + fontWidthOfSpace = getWidth(32); } // use the average font width as fall back if (fontWidthOfSpace <= 0) @@ -870,24 +495,9 @@ public abstract class PDFont implements } /** - * Returns the toUnicode mapping if present. - * - * @return the CMap representing the toUnicode mapping + * Returns true if the font uses vertical writing mode. */ - public CMap getToUnicodeCMap() - { - return toUnicodeCmap; - } - - /** - * Returns the CMap if present. - * - * @return the CMap representing the character encoding - */ - public CMap getCMap() - { - return cmap; - } + public abstract boolean isVertical(); /** * Calling this will release all cached information. @@ -907,4 +517,10 @@ public abstract class PDFont implements { return this.getCOSObject().hashCode(); } + + @Override + public String toString() + { + return getClass().getSimpleName() + " " + getName(); + } } Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFontDescriptorAFM.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFontDescriptorAFM.java?rev=1621411&r1=1621410&r2=1621411&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFontDescriptorAFM.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFontDescriptorAFM.java Sat Aug 30 02:26:57 2014 @@ -17,30 +17,26 @@ package org.apache.pdfbox.pdmodel.font; import java.io.IOException; - -import org.apache.fontbox.afm.FontMetric; - +import org.apache.fontbox.afm.FontMetrics; import org.apache.pdfbox.pdmodel.common.PDRectangle; - import org.apache.fontbox.util.BoundingBox; /** * This class represents the font descriptor when the font information * is coming from an AFM file. * - * @author <a href="mailto:b...@benlitchfield.com">Ben Litchfield</a> - * @version $Revision: 1.3 $ + * @author Ben Litchfield */ public class PDFontDescriptorAFM extends PDFontDescriptor { - private FontMetric afm; + private FontMetrics afm; /** * Constructor. * * @param afmFile The AFM file. */ - public PDFontDescriptorAFM( FontMetric afmFile ) + public PDFontDescriptorAFM( FontMetrics afmFile ) { afm = afmFile; } @@ -158,6 +154,12 @@ public class PDFontDescriptorAFM extends throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" ); } + @Override + public boolean isSymbolic() + { + return afm.getEncodingScheme().equals("FontSpecific"); + } + /** * This will get the fonts bouding box. * Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFontDescriptorDictionary.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFontDescriptorDictionary.java?rev=1621411&r1=1621410&r2=1621411&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFontDescriptorDictionary.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFontDescriptorDictionary.java Sat Aug 30 02:26:57 2014 @@ -42,16 +42,16 @@ public class PDFontDescriptorDictionary private int flags = -1; /** - * Constructor. + * Package-private constructor, for internal PDFBox use only. */ - public PDFontDescriptorDictionary() + PDFontDescriptorDictionary() { dic = new COSDictionary(); dic.setItem( COSName.TYPE, COSName.FONT_DESC ); } /** - * Constructor. + * Creates a PDFontDescriptor from a COS dictionary. * * @param desc The wrapped COS Dictionary. */ @@ -475,6 +475,14 @@ public class PDFontDescriptorDictionary } /** + * Returns true if widths are present in the font descriptor. + */ + public boolean hasWidths() + { + return dic.containsKey(COSName.WIDTHS) || dic.containsKey(COSName.MISSING_WIDTH); + } + + /** * This will get the missing width for the font. * * @return The missing width value. Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFontFactory.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFontFactory.java?rev=1621411&r1=1621410&r2=1621411&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFontFactory.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFontFactory.java Sat Aug 30 02:26:57 2014 @@ -18,6 +18,7 @@ package org.apache.pdfbox.pdmodel.font; import java.io.IOException; +import org.apache.pdfbox.cos.COSBase; import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSName; import org.apache.commons.logging.Log; @@ -53,6 +54,14 @@ public class PDFontFactory COSName subType = dictionary.getCOSName(COSName.SUBTYPE); if (COSName.TYPE1.equals(subType)) { + COSBase fd = dictionary.getDictionaryObject(COSName.FONT_DESC); + if (fd != null && fd instanceof COSDictionary) + { + if (((COSDictionary)fd).containsKey(COSName.FONT_FILE3)) + { + return new PDType1CFont(dictionary); + } + } return new PDType1Font(dictionary); } else if (COSName.MM_TYPE1.equals(subType)) @@ -101,17 +110,17 @@ public class PDFontFactory COSName type = dictionary.getCOSName(COSName.TYPE, COSName.FONT); if (!COSName.FONT.equals(type)) { - throw new IOException("Expected 'Font' dictionary but found '" + type.getName() + "'"); + throw new IllegalArgumentException("Expected 'Font' dictionary but found '" + type.getName() + "'"); } COSName subType = dictionary.getCOSName(COSName.SUBTYPE); if (COSName.CID_FONT_TYPE0.equals(subType)) { - return new PDCIDFontType0Font(dictionary, parent); + return new PDCIDFontType0(dictionary, parent); } else if (COSName.CID_FONT_TYPE2.equals(subType)) { - return new PDCIDFontType2Font(dictionary, parent); + return new PDCIDFontType2(dictionary, parent); } else { Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDMMType1Font.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDMMType1Font.java?rev=1621411&r1=1621410&r2=1621411&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDMMType1Font.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDMMType1Font.java Sat Aug 30 02:26:57 2014 @@ -17,7 +17,8 @@ package org.apache.pdfbox.pdmodel.font; import org.apache.pdfbox.cos.COSDictionary; -import org.apache.pdfbox.cos.COSName; + +import java.io.IOException; /** * Type 1 Multiple Master Font. @@ -27,19 +28,11 @@ import org.apache.pdfbox.cos.COSName; public class PDMMType1Font extends PDType1Font { /** - * Constructor. - */ - public PDMMType1Font() - { - dict.setItem(COSName.SUBTYPE, COSName.MM_TYPE1); - } - - /** - * Constructor. + * Creates an MMType1Font from a Font dictionary in a PDF. * - * @param fontDictionary The font dictionary according to the PDF specification. + * @param fontDictionary font dictionary */ - public PDMMType1Font(COSDictionary fontDictionary) + public PDMMType1Font(COSDictionary fontDictionary) throws IOException { super(fontDictionary); }