Return-Path: X-Original-To: apmail-pdfbox-commits-archive@www.apache.org Delivered-To: apmail-pdfbox-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 526981848B for ; Sat, 27 Jun 2015 00:18:23 +0000 (UTC) Received: (qmail 22488 invoked by uid 500); 27 Jun 2015 00:18:23 -0000 Delivered-To: apmail-pdfbox-commits-archive@pdfbox.apache.org Received: (qmail 22422 invoked by uid 500); 27 Jun 2015 00:18:23 -0000 Mailing-List: contact commits-help@pdfbox.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@pdfbox.apache.org Delivered-To: mailing list commits@pdfbox.apache.org Received: (qmail 22402 invoked by uid 99); 27 Jun 2015 00:18:23 -0000 Received: from eris.apache.org (HELO hades.apache.org) (140.211.11.105) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 27 Jun 2015 00:18:23 +0000 Received: from hades.apache.org (localhost [127.0.0.1]) by hades.apache.org (ASF Mail Server at hades.apache.org) with ESMTP id F089CAC012B for ; Sat, 27 Jun 2015 00:18:22 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1687878 [1/2] - in /pdfbox/trunk: fontbox/src/main/java/org/apache/fontbox/ fontbox/src/main/java/org/apache/fontbox/cff/ fontbox/src/main/java/org/apache/fontbox/ttf/ fontbox/src/main/java/org/apache/fontbox/type1/ pdfbox/ pdfbox/src/main... Date: Sat, 27 Jun 2015 00:18:22 -0000 To: commits@pdfbox.apache.org From: jahewson@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20150627001822.F089CAC012B@hades.apache.org> Author: jahewson Date: Sat Jun 27 00:18:21 2015 New Revision: 1687878 URL: http://svn.apache.org/r1687878 Log: PDFBOX-2842: Overhaul font substitution Added: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/EncodedFont.java (with props) pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/FontBoxFont.java (with props) pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/CIDFontMapping.java (with props) pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontCache.java (with props) pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontFormat.java (with props) pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontInfo.java (with props) pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontMapper.java - copied, changed from r1687667, pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/ExternalFonts.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontMapping.java (with props) pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDVectorFont.java (with props) pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/encoding/BuiltInEncoding.java (with props) Removed: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/Type1Equivalent.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/ExternalFonts.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType1Equivalent.java pdfbox/trunk/pdfbox/src/main/resources/org/apache/pdfbox/resources/otf/AdobeBlank.otf Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFCIDFont.java pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFType1Font.java pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/IndexData.java pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/OpenTypeFont.java pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/type1/Type1Font.java pdfbox/trunk/pdfbox/pom.xml pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FileSystemFontProvider.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontProvider.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType0.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDSystemInfo.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDSimpleFont.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType1CFont.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType1Font.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType3Font.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/CIDType0Glyph2D.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/TTFGlyph2D.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/Type1Glyph2D.java pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/Type1FontValidator.java pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/container/Type1Container.java pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/descriptor/Type1DescriptorHelper.java Added: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/EncodedFont.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/EncodedFont.java?rev=1687878&view=auto ============================================================================== --- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/EncodedFont.java (added) +++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/EncodedFont.java Sat Jun 27 00:18:21 2015 @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.fontbox; + +import java.io.IOException; +import org.apache.fontbox.encoding.Encoding; + +/** + * A PostScript font which uses an encoding vector. + * + * @author John Hewson + */ +public interface EncodedFont +{ + /** + * Returns the PostScript Encoding vector for the font. + */ + Encoding getEncoding() throws IOException; +} Propchange: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/EncodedFont.java ------------------------------------------------------------------------------ svn:eol-style = native Added: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/FontBoxFont.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/FontBoxFont.java?rev=1687878&view=auto ============================================================================== --- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/FontBoxFont.java (added) +++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/FontBoxFont.java Sat Jun 27 00:18:21 2015 @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.fontbox; + +import java.awt.geom.GeneralPath; +import java.io.IOException; +import java.util.List; +import org.apache.fontbox.util.BoundingBox; + +/** + * Common interface for all FontBox fonts. + * + * @author John Hewson + */ +public interface FontBoxFont +{ + /** + * The PostScript name of the font. + */ + String getName() throws IOException; + + /** + * Returns the font's bounding box in PostScript units. + */ + BoundingBox getFontBBox() throws IOException; + + /** + * Returns the FontMatrix in PostScript units. + */ + List getFontMatrix() throws IOException; + + /** + * Returns the path for the character with the given name. + * + * @return glyph path + * @throws IOException if the path could not be read + */ + GeneralPath getPath(String name) throws IOException; + + /** + * Returns the advance width for the character with the given name. + * + * @return glyph advance width + * @throws IOException if the path could not be read + */ + float getWidth(String name) throws IOException; + + /** + * Returns true if the font contains the given glyph. + * + * @param name PostScript glyph name + */ + boolean hasGlyph(String name) throws IOException; +} Propchange: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/FontBoxFont.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFCIDFont.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFCIDFont.java?rev=1687878&r1=1687877&r2=1687878&view=diff ============================================================================== --- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFCIDFont.java (original) +++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFCIDFont.java Sat Jun 27 00:18:21 2015 @@ -17,13 +17,13 @@ package org.apache.fontbox.cff; -import org.apache.fontbox.type1.Type1CharStringReader; - +import java.awt.geom.GeneralPath; import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.apache.fontbox.type1.Type1CharStringReader; /** * A Type 0 CIDFont represented in a CFF file. Thread safe. @@ -247,6 +247,39 @@ public class CFFCIDFont extends CFFFont return (List)topDict.get("FontMatrix"); } + @Override + public GeneralPath getPath(String selector) throws IOException + { + int cid = selectorToCID(selector); + return getType2CharString(cid).getPath(); + } + + @Override + public float getWidth(String selector) throws IOException + { + int cid = selectorToCID(selector); + return getType2CharString(cid).getWidth(); + } + + @Override + public boolean hasGlyph(String selector) throws IOException + { + int cid = selectorToCID(selector); + return cid != 0; + } + + /** + * Parses a CID selector of the form \ddddd. + */ + private int selectorToCID(String selector) + { + if (!selector.startsWith("\\")) + { + throw new IllegalArgumentException("Invalid selector"); + } + return Integer.parseInt(selector.substring(1)); + } + /** * Private implementation of Type1CharStringReader, because only CFFType1Font can * expose this publicly, as CIDFonts only support this for legacy 'seac' commands. Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java?rev=1687878&r1=1687877&r2=1687878&view=diff ============================================================================== --- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java (original) +++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java Sat Jun 27 00:18:21 2015 @@ -16,11 +16,13 @@ */ package org.apache.fontbox.cff; +import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.util.BoundingBox; /** @@ -29,7 +31,7 @@ import org.apache.fontbox.util.BoundingB * @author Villu Ruusmann * @author John Hewson */ -public abstract class CFFFont +public abstract class CFFFont implements FontBoxFont { protected String fontName; protected final Map topDict = new LinkedHashMap(); @@ -174,6 +176,14 @@ public abstract class CFFFont return globalSubrIndex; } + /** + * Returns the Type 2 charstring for the given CID. + * + * @param cidOrGid CID for CIFFont, or GID for Type 1 font + * @throws IOException if the charstring could not be read + */ + public abstract Type2CharString getType2CharString(int cidOrGid) throws IOException; + @Override public String toString() { Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFType1Font.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFType1Font.java?rev=1687878&r1=1687877&r2=1687878&view=diff ============================================================================== --- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFType1Font.java (original) +++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFType1Font.java Sat Jun 27 00:18:21 2015 @@ -16,15 +16,14 @@ */ package org.apache.fontbox.cff; -import org.apache.fontbox.ttf.Type1Equivalent; -import org.apache.fontbox.type1.Type1CharStringReader; - import java.awt.geom.GeneralPath; import java.io.IOException; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.apache.fontbox.EncodedFont; +import org.apache.fontbox.type1.Type1CharStringReader; /** * A Type 1-equivalent font program represented in a CFF file. Thread safe. @@ -32,7 +31,7 @@ import java.util.concurrent.ConcurrentHa * @author Villu Ruusmann * @author John Hewson */ -public class CFFType1Font extends CFFFont implements Type1Equivalent +public class CFFType1Font extends CFFFont implements EncodedFont { private final Map privateDict = new LinkedHashMap(); private CFFEncoding encoding; Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/IndexData.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/IndexData.java?rev=1687878&r1=1687877&r2=1687878&view=diff ============================================================================== --- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/IndexData.java (original) +++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/IndexData.java Sat Jun 27 00:18:21 2015 @@ -104,4 +104,4 @@ public class IndexData { data[index] = value; } -} \ No newline at end of file +} Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/OpenTypeFont.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/OpenTypeFont.java?rev=1687878&r1=1687877&r2=1687878&view=diff ============================================================================== --- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/OpenTypeFont.java (original) +++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/OpenTypeFont.java Sat Jun 27 00:18:21 2015 @@ -17,6 +17,7 @@ package org.apache.fontbox.ttf; +import java.awt.geom.GeneralPath; import java.io.IOException; /** @@ -33,7 +34,6 @@ public class OpenTypeFont extends TrueTy { super(fontData); } - /** * Get the "cmap" table for this TTF. * @@ -41,12 +41,25 @@ public class OpenTypeFont extends TrueTy */ public synchronized CFFTable getCFF() throws IOException { - CFFTable cmap = (CFFTable)tables.get(CFFTable.TAG); - if (cmap != null && !cmap.getInitialized()) + CFFTable cff = (CFFTable)tables.get(CFFTable.TAG); + if (cff != null && !cff.getInitialized()) { - readTable(cmap); + readTable(cff); } - return cmap; + return cff; + } + + @Override + public synchronized GlyphTable getGlyph() throws IOException + { + throw new UnsupportedOperationException("OTF fonts do not have a glyf table"); + } + + @Override + public GeneralPath getPath(String name) throws IOException + { + int gid = nameToGID(name); + return getCFF().getFont().getType2CharString(gid).getPath(); } /** Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java?rev=1687878&r1=1687877&r2=1687878&view=diff ============================================================================== --- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java (original) +++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java Sat Jun 27 00:18:21 2015 @@ -19,13 +19,14 @@ package org.apache.fontbox.ttf; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.io.IOException; -import java.io.InputStream; - -import org.apache.fontbox.encoding.Encoding; +import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.util.BoundingBox; /** @@ -33,7 +34,7 @@ import org.apache.fontbox.util.BoundingB * * @author Ben Litchfield */ -public class TrueTypeFont implements Type1Equivalent, Closeable +public class TrueTypeFont implements FontBoxFont, Closeable { private float version; private int numberOfGlyphs = -1; @@ -644,12 +645,6 @@ public class TrueTypeFont implements Typ } @Override - public Encoding getEncoding() - { - return null; - } - - @Override public BoundingBox getFontBBox() throws IOException { short xMin = getHeader().getXMin(); @@ -661,6 +656,13 @@ public class TrueTypeFont implements Typ } @Override + public List getFontMatrix() throws IOException + { + float scale = 1000f / getUnitsPerEm(); + return Arrays.asList(0.001f * scale, 0, 0, 0.001f * scale, 0, 0); + } + + @Override public String toString() { try Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/type1/Type1Font.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/type1/Type1Font.java?rev=1687878&r1=1687877&r2=1687878&view=diff ============================================================================== --- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/type1/Type1Font.java (original) +++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/type1/Type1Font.java Sat Jun 27 00:18:21 2015 @@ -17,13 +17,6 @@ package org.apache.fontbox.type1; -import org.apache.fontbox.cff.Type1CharString; -import org.apache.fontbox.cff.Type1CharStringParser; -import org.apache.fontbox.encoding.Encoding; -import org.apache.fontbox.pfb.PfbParser; -import org.apache.fontbox.ttf.Type1Equivalent; -import org.apache.fontbox.util.BoundingBox; - import java.awt.geom.GeneralPath; import java.io.IOException; import java.io.InputStream; @@ -33,13 +26,20 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.apache.fontbox.FontBoxFont; +import org.apache.fontbox.EncodedFont; +import org.apache.fontbox.cff.Type1CharString; +import org.apache.fontbox.cff.Type1CharStringParser; +import org.apache.fontbox.encoding.Encoding; +import org.apache.fontbox.pfb.PfbParser; +import org.apache.fontbox.util.BoundingBox; /** * Represents an Adobe Type 1 (.pfb) font. Thread safe. * * @author John Hewson */ -public final class Type1Font implements Type1CharStringReader, Type1Equivalent +public final class Type1Font implements Type1CharStringReader, EncodedFont, FontBoxFont { /** * Constructs a new Type1Font object from a .pfb stream. Modified: pdfbox/trunk/pdfbox/pom.xml URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/pom.xml?rev=1687878&r1=1687877&r2=1687878&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/pom.xml (original) +++ pdfbox/trunk/pdfbox/pom.xml Sat Jun 27 00:18:21 2015 @@ -97,7 +97,7 @@ maven-surefire-plugin - -Xmx128m + -Xmx768m org/apache/pdfbox/TestAll.java org/apache/pdfbox/rendering/TestPDFToImage.java Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java?rev=1687878&r1=1687877&r2=1687878&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java Sat Jun 27 00:18:21 2015 @@ -453,7 +453,6 @@ public final class COSName extends COSBa public static final COSName SOFT_LIGHT = new COSName("SoftLight"); public static final COSName SS = new COSName("SS"); public static final COSName ST = new COSName("St"); - public static final COSName STYLE = new COSName("Style"); public static final COSName STANDARD_ENCODING = new COSName("StandardEncoding"); public static final COSName STATE = new COSName("State"); public static final COSName STATE_MODEL = new COSName("StateModel"); @@ -466,6 +465,7 @@ public final class COSName extends COSBa public static final COSName STRUCT_PARENT = new COSName("StructParent"); public static final COSName STRUCT_PARENTS = new COSName("StructParents"); public static final COSName STRUCT_TREE_ROOT = new COSName("StructTreeRoot"); + public static final COSName STYLE = new COSName("Style"); public static final COSName SUB_FILTER = new COSName("SubFilter"); public static final COSName SUBJ = new COSName("Subj"); public static final COSName SUBJECT = new COSName("Subject"); Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/CIDFontMapping.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/CIDFontMapping.java?rev=1687878&view=auto ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/CIDFontMapping.java (added) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/CIDFontMapping.java Sat Jun 27 00:18:21 2015 @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.font; + +import org.apache.fontbox.FontBoxFont; +import org.apache.fontbox.ttf.OpenTypeFont; + +/** + * A CIDFontMapping is a kind of FontMapping which allows for an additional TrueTypeFont substitute + * to be provided if a CID font is not available. + * + * @author John Hewson + */ +public final class CIDFontMapping extends FontMapping +{ + private final FontBoxFont ttf; + + CIDFontMapping(OpenTypeFont font, FontBoxFont fontBoxFont, boolean isFallback) + { + super(font, isFallback); + this.ttf = fontBoxFont; + } + + /** + * Returns a TrueType font when isCIDFont() is true, otherwise null. + */ + public FontBoxFont getTrueTypeFont() + { + return ttf; + } + + /** + * Returns true if this is a CID font. + */ + public boolean isCIDFont() + { + return getFont() != null; + } +} Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/CIDFontMapping.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FileSystemFontProvider.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FileSystemFontProvider.java?rev=1687878&r1=1687877&r2=1687878&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FileSystemFontProvider.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FileSystemFontProvider.java Sat Jun 27 00:18:21 2015 @@ -16,12 +16,21 @@ */ package org.apache.pdfbox.pdmodel.font; -import java.util.Set; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fontbox.FontBoxFont; +import org.apache.fontbox.cff.CFFCIDFont; import org.apache.fontbox.cff.CFFFont; -import org.apache.fontbox.cff.CFFParser; import org.apache.fontbox.ttf.NamingTable; +import org.apache.fontbox.ttf.OTFParser; +import org.apache.fontbox.ttf.OpenTypeFont; import org.apache.fontbox.ttf.TTFParser; import org.apache.fontbox.ttf.TrueTypeCollection; import org.apache.fontbox.ttf.TrueTypeFont; @@ -29,78 +38,132 @@ import org.apache.fontbox.type1.Type1Fon import org.apache.fontbox.util.autodetect.FontFileFinder; import org.apache.pdfbox.io.IOUtils; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - /** - * External font provider which searches for fonts on the local filesystem. + * A FontProvider which searches for fonts on the local filesystem. * * @author John Hewson */ final class FileSystemFontProvider extends FontProvider { private static final Log LOG = LogFactory.getLog(FileSystemFontProvider.class); + + private final List fontInfoList = new ArrayList(); + private final FontCache cache; + + private class FSFontInfo extends FontInfo + { + private final String postScriptName; + private final FontFormat format; + private final PDCIDSystemInfo cidSystemInfo; + private final File file; - // cache of font files on the system (populated in constructor) - private final Map ttfFontFiles = new HashMap(); - private final Map cffFontFiles = new HashMap(); - private final Map type1FontFiles = new HashMap(); - - // cache of loaded fonts which are in use (populated on-the-fly) - private final Map ttfFonts = new HashMap(); - private final Map cffFonts = new HashMap(); - private final Map type1Fonts = new HashMap(); + private FSFontInfo(File file, FontFormat format, String postScriptName, + PDCIDSystemInfo cidSystemInfo) + { + this.file = file; + this.format = format; + this.postScriptName = postScriptName; + this.cidSystemInfo = cidSystemInfo; + } + + @Override + public String getPostScriptName() + { + return postScriptName; + } + + @Override + public FontFormat getFormat() + { + return format; + } + + @Override + public PDCIDSystemInfo getCIDSystemInfo() + { + return cidSystemInfo; + } + + @Override + public FontBoxFont getFont() + { + FontBoxFont cached = cache.getFont(this); + if (cached != null) + { + return cached; + } + else + { + FontBoxFont font; + switch (format) + { + case PFB: font = getType1Font(postScriptName, file); break; + case TTF: font = getTrueTypeFont(postScriptName, file); break; + case OTF: font = getOTFFont(postScriptName, file); break; + default: throw new RuntimeException("can't happen"); + } + cache.addFont(this, font); + return font; + } + } + + @Override + public String toString() + { + return super.toString() + " " + file; + } + } /** * Constructor. */ - FileSystemFontProvider() + FileSystemFontProvider(FontCache cache) { + this.cache = cache; + if (LOG.isTraceEnabled()) { LOG.trace("Will search the local system for fonts"); } - int count = 0; + List files = new ArrayList(); FontFileFinder fontFileFinder = new FontFileFinder(); List fonts = fontFileFinder.find(); for (URI font : fonts) { - count++; - File fontFile = new File(font); + files.add(new File(font)); + } + + if (LOG.isTraceEnabled()) + { + LOG.trace("Found " + files.size() + " fonts on the local system"); + } + + // todo: loading all of these fonts is slow, can we cache this? + for (File file : files) + { try { - if (fontFile.getPath().toLowerCase().endsWith(".ttf") || - fontFile.getPath().toLowerCase().endsWith(".otf")) + if (file.getPath().toLowerCase().endsWith(".ttf") || + file.getPath().toLowerCase().endsWith(".otf")) { - addTrueTypeFont(fontFile); + addTrueTypeFont(file); } - else if (fontFile.getPath().toLowerCase().endsWith(".ttc") || - fontFile.getPath().toLowerCase().endsWith(".otc")) + else if (file.getPath().toLowerCase().endsWith(".ttc") || + file.getPath().toLowerCase().endsWith(".otc")) { - addTrueTypeCollection(fontFile); + addTrueTypeCollection(file); } - else if (fontFile.getPath().toLowerCase().endsWith(".pfb")) + else if (file.getPath().toLowerCase().endsWith(".pfb")) { - addType1Font(fontFile); + addType1Font(file); } } catch (IOException e) { - LOG.error("Error parsing font " + fontFile.getPath(), e); + LOG.error("Error parsing font " + file.getPath(), e); } } - - if (LOG.isTraceEnabled()) - { - LOG.trace("Found " + count + " fonts on the local system"); - } } /** @@ -133,17 +196,26 @@ final class FileSystemFontProvider exten } } } - + /** * Adds an OTF or TTF font to the file cache. To reduce memory, the parsed font is not cached. */ private void addTrueTypeFont(File ttfFile) throws IOException { - TTFParser ttfParser = new TTFParser(false, true); try { - TrueTypeFont ttf = ttfParser.parse(ttfFile); - addTrueTypeFontImpl(ttf, ttfFile); + if (ttfFile.getPath().endsWith(".otf")) + { + OTFParser parser = new OTFParser(false, true); + OpenTypeFont otf = parser.parse(ttfFile); + addTrueTypeFontImpl(otf, ttfFile); + } + else + { + TTFParser parser = new TTFParser(false, true); + TrueTypeFont ttf = parser.parse(ttfFile); + addTrueTypeFontImpl(ttf, ttfFile); + } } catch (NullPointerException e) // TTF parser is buggy { @@ -154,7 +226,7 @@ final class FileSystemFontProvider exten LOG.error("Could not load font file: " + ttfFile, e); } } - + /** * Adds an OTF or TTF font to the file cache. To reduce memory, the parsed font is not cached. */ @@ -168,9 +240,10 @@ final class FileSystemFontProvider exten // ttf could still be null if (ttf != null) { + // todo: this is a performance bottleneck, we don't actually need to read this table nameTable = ttf.getNaming(); } - + if (nameTable == null) { LOG.warn("Missing 'name' table in font " + file); @@ -178,26 +251,35 @@ final class FileSystemFontProvider exten else { // read PostScript name, if any - if (nameTable.getPostScriptName() != null) + if (ttf.getName() != null) { - String psName = nameTable.getPostScriptName(); - String format; - if (ttf.getTableMap().get("CFF ") != null) + if (ttf instanceof OpenTypeFont) { format = "OTF"; - cffFontFiles.putAll(toMap(getNames(ttf), file)); + CFFFont cff = ((OpenTypeFont)ttf).getCFF().getFont(); + PDCIDSystemInfo ros = null; + if (cff instanceof CFFCIDFont) + { + CFFCIDFont cidFont = (CFFCIDFont)cff; + String registry = cidFont.getRegistry(); + String ordering = cidFont.getOrdering(); + int supplement = cidFont.getSupplement(); + ros = new PDCIDSystemInfo(registry, ordering, supplement); + } + fontInfoList.add(new FSFontInfo(file, FontFormat.OTF, ttf.getName(), ros)); } else { format = "TTF"; - ttfFontFiles.putAll(toMap(getNames(ttf), file)); + fontInfoList.add(new FSFontInfo(file, FontFormat.TTF, ttf.getName(), null)); } if (LOG.isTraceEnabled()) { - LOG.trace(format +": '" + psName + "' / '" + nameTable.getFontFamily() + - "' / '" + nameTable.getFontSubFamily() + "'"); + LOG.trace(format +": '" + ttf.getName() + "' / '" + + nameTable.getFontFamily() + "' / '" + + nameTable.getFontSubFamily() + "'"); } } else @@ -206,6 +288,10 @@ final class FileSystemFontProvider exten } } } + catch (IOException e) + { + LOG.error("Could not load font file: " + file, e); + } finally { if (ttf != null) @@ -224,56 +310,43 @@ final class FileSystemFontProvider exten try { Type1Font type1 = Type1Font.createWithPFB(input); - - String psName = type1.getFontName(); - type1FontFiles.putAll(toMap(getNames(type1), pfbFile)); + fontInfoList.add(new FSFontInfo(pfbFile, FontFormat.PFB, type1.getName(), null)); if (LOG.isTraceEnabled()) { - LOG.trace("PFB: '" + psName + "' / '" + type1.getFamilyName() + "' / '" + + LOG.trace("PFB: '" + type1.getName() + "' / '" + type1.getFamilyName() + "' / '" + type1.getWeight() + "'"); } } + catch (IOException e) + { + LOG.error("Could not load font file: " + pfbFile, e); + } finally { input.close(); } } - - @Override - public synchronized TrueTypeFont getTrueTypeFont(String postScriptName) - { - TrueTypeFont ttf = ttfFonts.get(postScriptName); - if (ttf != null) - { - return ttf; - } - File file = ttfFontFiles.get(postScriptName); - if (file != null) + private TrueTypeFont getTrueTypeFont(String postScriptName, File file) + { + try { - try - { - ttf = readTrueTypeFont(postScriptName, file); + TrueTypeFont ttf = readTrueTypeFont(postScriptName, file); - for (String name : getNames(ttf)) - { - ttfFonts.put(name, ttf); - } - if (LOG.isDebugEnabled()) - { - LOG.debug("Loaded " + postScriptName + " from " + file); - } - return ttf; - } - catch (NullPointerException e) // TTF parser is buggy + if (LOG.isDebugEnabled()) { - LOG.error("Could not load font file: " + file, e); - } - catch (IOException e) - { - LOG.error("Could not load font file: " + file, e); + LOG.debug("Loaded " + postScriptName + " from " + file); } + return ttf; + } + catch (NullPointerException e) // TTF parser is buggy + { + LOG.error("Could not load font file: " + file, e); + } + catch (IOException e) + { + LOG.error("Could not load font file: " + file, e); } return null; } @@ -298,129 +371,72 @@ final class FileSystemFontProvider exten return ttfParser.parse(file); } } - - @Override - public synchronized CFFFont getCFFFont(String postScriptName) + + private OpenTypeFont getOTFFont(String postScriptName, File file) { - CFFFont cff = cffFonts.get(postScriptName); - if (cff != null) + try { - return cff; - } + // todo JH: we don't yet support loading CFF fonts from OTC collections
 + OTFParser parser = new OTFParser(false, true); + OpenTypeFont otf = parser.parse(file); - File file = cffFontFiles.get(postScriptName); - if (file != null) - { - InputStream input = null; - try + if (LOG.isDebugEnabled()) { - // todo JH: we don't yet support loading CFF fonts from OTC collections
 - input = new FileInputStream(file); - byte[] bytes = IOUtils.toByteArray(input); - CFFParser cffParser = new CFFParser(); - cff = cffParser.parse(bytes).get(0); - for (String name : getNames(cff)) - { - cffFonts.put(name, cff); - } - if (LOG.isDebugEnabled()) - { - LOG.debug("Loaded " + postScriptName + " from " + file); - } - return cff; - } - catch (IOException e) - { - LOG.error("Could not load font file: " + file, e); - } - finally - { - IOUtils.closeQuietly(input); + LOG.debug("Loaded " + postScriptName + " from " + file); } + return otf; + } + catch (IOException e) + { + LOG.error("Could not load font file: " + file, e); } return null; } - @Override - public synchronized Type1Font getType1Font(String postScriptName) + private Type1Font getType1Font(String postScriptName, File file) { - Type1Font type1 = type1Fonts.get(postScriptName); - if (type1 != null) + InputStream input = null; + try { - return type1; - } + input = new FileInputStream(file); + Type1Font type1 = Type1Font.createWithPFB(input); - File file = type1FontFiles.get(postScriptName); - if (file != null) - { - InputStream input = null; - try + if (LOG.isDebugEnabled()) { - input = new FileInputStream(file); - type1 = Type1Font.createWithPFB(input); - for (String name : getNames(type1)) - { - type1Fonts.put(name, type1); - } - if (LOG.isDebugEnabled()) - { - LOG.debug("Loaded " + postScriptName + " from " + file); - } - return type1; - } - catch (IOException e) - { - LOG.error("Could not load font file: " + file, e); - } - finally - { - IOUtils.closeQuietly(input); + LOG.debug("Loaded " + postScriptName + " from " + file); } + return type1; } - return null; - } - - /** - * Returns a map containing the given file for each string key. - */ - private Map toMap(Set names, File file) - { - Map map = new HashMap(); - for (String name : names) + catch (IOException e) + { + LOG.error("Could not load font file: " + file, e); + } + finally { - map.put(name, file); + IOUtils.closeQuietly(input); } - return map; + return null; } @Override public String toDebugString() { StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : ttfFontFiles.entrySet()) + for (FSFontInfo info : fontInfoList) { - sb.append("TTF: "); - sb.append(entry.getKey()); + sb.append(info.getFormat()); sb.append(": "); - sb.append(entry.getValue().getPath()); - sb.append('\n'); - } - for (Map.Entry entry : cffFontFiles.entrySet()) - { - sb.append("OTF: "); - sb.append(entry.getKey()); - sb.append(": "); - sb.append(entry.getValue().getPath()); - sb.append('\n'); - } - for (Map.Entry entry : type1FontFiles.entrySet()) - { - sb.append("PFB: "); - sb.append(entry.getKey()); + sb.append(info.getPostScriptName()); sb.append(": "); - sb.append(entry.getValue().getPath()); + sb.append(info.file.getPath()); sb.append('\n'); } return sb.toString(); } + + @Override + public List getFontInfo() + { + return fontInfoList; + } } Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontCache.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontCache.java?rev=1687878&view=auto ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontCache.java (added) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontCache.java Sat Jun 27 00:18:21 2015 @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.font; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.fontbox.FontBoxFont; + +/** + * A cache for system fonts. This allows PDFBox to manage caching for a {@link FontProvider}. + * PDFBox is free to purge this cache at will. + * + * @author John Hewson + */ +public final class FontCache +{ + private final Map cache = new ConcurrentHashMap(); + + /** + * Adds the given FontBox font to the cache. + */ + public void addFont(FontInfo info, FontBoxFont font) + { + cache.put(info, font); + } + + /** + * Returns the FontBox font associated with the given FontInfo. + */ + public FontBoxFont getFont(FontInfo info) + { + return cache.get(info); + } +} Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontCache.java ------------------------------------------------------------------------------ svn:eol-style = native Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontFormat.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontFormat.java?rev=1687878&view=auto ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontFormat.java (added) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontFormat.java Sat Jun 27 00:18:21 2015 @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.font; + +/** + * Font file format. + * + * @author John Hewson + */ +public enum FontFormat +{ + /** + * TrueType font. + */ + TTF, + + /** + * OpenType font. + */ + OTF, + + /** + * Type 1 (binary) font. + */ + PFB +} Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontFormat.java ------------------------------------------------------------------------------ svn:eol-style = native Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontInfo.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontInfo.java?rev=1687878&view=auto ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontInfo.java (added) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontInfo.java Sat Jun 27 00:18:21 2015 @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.font; + +import org.apache.fontbox.FontBoxFont; + +/** + * Information about a font on the system. + * + * @author John Hewson + */ +public abstract class FontInfo +{ + /** + * Returns the PostScript name of the font. + */ + public abstract String getPostScriptName(); + + /** + * Returns the font's format. + */ + public abstract FontFormat getFormat(); + + /** + * Returns the CIDSystemInfo associated with the font, if any. + */ + public abstract PDCIDSystemInfo getCIDSystemInfo(); + + /** + * Returns a new FontBox font instance for the font. Implementors of this method must not + * cache the return value of this method unless doing so via the current {@link FontCache}. + */ + public abstract FontBoxFont getFont(); + + @Override + public String toString() + { + return getPostScriptName() + " (" + getFormat() + ")"; + } +} Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontInfo.java ------------------------------------------------------------------------------ svn:eol-style = native Copied: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontMapper.java (from r1687667, pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/ExternalFonts.java) URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontMapper.java?p2=pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontMapper.java&p1=pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/ExternalFonts.java&r1=1687667&r2=1687878&rev=1687878&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/ExternalFonts.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontMapper.java Sat Jun 27 00:18:21 2015 @@ -23,68 +23,48 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.fontbox.cff.CFFCIDFont; +import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.cff.CFFFont; -import org.apache.fontbox.cff.CFFParser; import org.apache.fontbox.cff.CFFType1Font; +import org.apache.fontbox.ttf.OpenTypeFont; import org.apache.fontbox.ttf.TTFParser; -import org.apache.fontbox.ttf.Type1Equivalent; import org.apache.fontbox.ttf.TrueTypeFont; import org.apache.fontbox.type1.Type1Font; -import org.apache.pdfbox.io.IOUtils; /** - * External font service, locates non-embedded fonts via a pluggable FontProvider. + * Font mapper, locates non-embedded fonts via a pluggable FontProvider. * * @author John Hewson */ -public final class ExternalFonts +public final class FontMapper { - private ExternalFonts() {} - - // lazy thread safe singleton - private static class DefaultFontProvider - { - private static final FontProvider INSTANCE = new FileSystemFontProvider(); - } + private FontMapper() {} - private static final Log log = LogFactory.getLog(ExternalFonts.class); + private static final FontCache fontCache = new FontCache(); // todo: static cache isn't ideal + private static final Log log = LogFactory.getLog(FontMapper.class); private static FontProvider fontProvider; + private static Map fontInfoByName; /** fallback fonts, used as as a last resort */ - private static final TrueTypeFont ttfFallbackFont; - private static final CFFCIDFont cidFallbackFont; + private static final TrueTypeFont lastResortFont; static { try { - // ttf String ttfName = "org/apache/pdfbox/resources/ttf/LiberationSans-Regular.ttf"; - URL url = ExternalFonts.class.getClassLoader().getResource(ttfName); + URL url = FontMapper.class.getClassLoader().getResource(ttfName); if (url == null) { throw new IOException("Error loading resource: " + ttfName); } InputStream ttfStream = url.openStream(); TTFParser ttfParser = new TTFParser(); - ttfFallbackFont = ttfParser.parse(ttfStream); - - // cff - String cffName = "org/apache/pdfbox/resources/otf/AdobeBlank.otf"; - url = ExternalFonts.class.getClassLoader().getResource(cffName); - if (url == null) - { - throw new IOException("Error loading resource: " + ttfName); - } - InputStream cffStream = url.openStream(); - byte[] bytes = IOUtils.toByteArray(cffStream); - CFFParser cffParser = new CFFParser(); - cidFallbackFont = (CFFCIDFont)cffParser.parse(bytes).get(0); + lastResortFont = ttfParser.parse(ttfStream); } catch (IOException e) { @@ -92,26 +72,52 @@ public final class ExternalFonts } } + // lazy thread safe singleton + private static class DefaultFontProvider + { + private static final FontProvider INSTANCE = new FileSystemFontProvider(fontCache); + } + /** * Sets the font service provider. */ - public static void setProvider(FontProvider fontProvider) + public synchronized static void setProvider(FontProvider fontProvider) { - ExternalFonts.fontProvider = fontProvider; + FontMapper.fontProvider = fontProvider; + fontInfoByName = createFontInfoByName(fontProvider.getFontInfo()); } /** * Returns the font service provider. Defaults to using FileSystemFontProvider. */ - public static FontProvider getProvider() + public synchronized static FontProvider getProvider() { if (fontProvider == null) { - fontProvider = DefaultFontProvider.INSTANCE; + setProvider(DefaultFontProvider.INSTANCE); } return fontProvider; } + /** + * Returns the font cache associated with this FontMapper. This method is needed by + * FontProvider subclasses. + */ + public static FontCache getFontCache() + { + return fontCache; + } + + private static Map createFontInfoByName(List fontInfoList) + { + Map map = new LinkedHashMap(); + for (FontInfo info : fontInfoList) + { + map.put(info.getPostScriptName(), info); + } + return map; + } + /** Map of PostScript name substitutes, in priority order. */ private static final Map> substitutes = new HashMap>(); static @@ -156,13 +162,7 @@ public final class ExternalFonts "NimbusRomNo9L-MediItal")); substitutes.put("Symbol", Arrays.asList("Symbol", "SymbolMT", "StandardSymL")); substitutes.put("ZapfDingbats", Arrays.asList("ZapfDingbatsITC", "Dingbats", "MS-Gothic")); - - // extra substitute mechanism for CJK CIDFonts when all we know is the ROS - substitutes.put("$Adobe-CNS1", Arrays.asList("AdobeMingStd-Light")); - substitutes.put("$Adobe-Japan1", Arrays.asList("KozMinPr6N-Regular")); - substitutes.put("$Adobe-Korea1", Arrays.asList("AdobeGothicStd-Bold")); - substitutes.put("$Adobe-GB1", Arrays.asList("AdobeHeitiStd-Regular")); - + // Acrobat also uses alternative names for Standard 14 fonts, which we map to those above // these include names such as "Arial" and "TimesNewRoman" for (String baseName : Standard14Fonts.getNames()) @@ -215,73 +215,6 @@ public final class ExternalFonts } /** - * Windows name (ArialNarrow,Bold) to PostScript name (ArialNarrow-Bold) - */ - private static String windowsToPs(String windowsName) - { - return windowsName.replaceAll(",", "-"); - } - - /** - * Finds a CFF CID-Keyed font with the given PostScript name, or a suitable substitute, or null. - * - * @param registryOrdering the CID system registry and ordering e.g. "Adobe-Japan1", if any - * @param fontDescriptor the font descriptor, if any - */ - public static CFFCIDFont getCFFCIDFontFallback(String registryOrdering, - PDFontDescriptor fontDescriptor) - { - // try ROS substitutes - // todo: this is a fairly primitive mechanism and could be improved - if (registryOrdering != null) - { - for (String substituteName : getSubstitutes("$" + registryOrdering)) - { - CFFFont cff = getProvider().getCFFFont(substituteName); - if (cff instanceof CFFCIDFont) - { - return (CFFCIDFont) cff; - } - } - } - return cidFallbackFont; - } - - /** - * Returns the fallback font, used for rendering when no other fonts are available, - * we attempt to find a good fallback based on the font descriptor. - */ - public static Type1Equivalent getType1FallbackFont(PDFontDescriptor fontDescriptor) - { - String fontName = getFallbackFontName(fontDescriptor); - Type1Equivalent type1Equivalent = getType1EquivalentFont(fontName); - if (type1Equivalent == null) - { - // only systems with no fonts should reach this point, so we return a basic fallback - log.error("No fallback font for '" + fontName + "'"); - return ttfFallbackFont; - } - return type1Equivalent; - } - - /** - * Returns the fallback font, used for rendering when no other fonts are available, - * we attempt to find a good fallback based on the font descriptor. - */ - public static TrueTypeFont getTrueTypeFallbackFont(PDFontDescriptor fontDescriptor) - { - String fontName = getFallbackFontName(fontDescriptor); - TrueTypeFont ttf = getTrueTypeFont(fontName); - if (ttf == null) - { - // we have to return something here as TTFs aren't strictly required on the system - log.error("No TTF fallback font for '" + fontName + "'"); - return ttfFallbackFont; - } - return ttf; - } - - /** * Attempts to find a good fallback based on the font descriptor. */ private static String getFallbackFontName(PDFontDescriptor fontDescriptor) @@ -365,138 +298,203 @@ public final class ExternalFonts /** * Finds a TrueType font with the given PostScript name, or a suitable substitute, or null. * - * @param postScriptName PostScript font name + * @param fontDescriptor FontDescriptor */ - public static TrueTypeFont getTrueTypeFont(String postScriptName) + public static FontMapping getTrueTypeFont(PDFontDescriptor fontDescriptor) { - // first ask the font provider for the font - TrueTypeFont ttf = getProvider().getTrueTypeFont(postScriptName); - if (ttf == null) + TrueTypeFont ttf = (TrueTypeFont)findFont(FontFormat.TTF, fontDescriptor.getFontName()); + if (ttf != null) + { + return new FontMapping(ttf, false); + } + else { - // then try substitutes - for (String substituteName : getSubstitutes(postScriptName)) + // fallback - todo: i.e. fuzzy match + String fontName = getFallbackFontName(fontDescriptor); + ttf = (TrueTypeFont) findFont(FontFormat.TTF, fontName); + if (ttf == null) { - ttf = getProvider().getTrueTypeFont(substituteName); - if (ttf != null) - { - return ttf; - } + // we have to return something here as TTFs aren't strictly required on the system + log.error("Using last-resort fallback for TTF font '" + fontName + "'"); + ttf = lastResortFont; } - // then Windows name - ttf = getProvider().getTrueTypeFont(windowsToPs(postScriptName)); + return new FontMapping(ttf, true); } - return ttf; } /** - * Finds a TrueType font with the given PostScript name, or a suitable substitute, or null. + * Finds a font with the given PostScript name, or a suitable substitute, or null. This allows + * any font to be substituted with a PFB, TTF or OTF. * - * @param postScriptName PostScript font name + * @param fontDescriptor the FontDescriptor of the font to find */ - public static Type1Font getType1Font(String postScriptName) + public static FontMapping getFontBoxFont(PDFontDescriptor fontDescriptor) { - // first ask the font provider for the font - Type1Font t1 = getProvider().getType1Font(postScriptName); - if (t1 == null) + FontBoxFont font = findFontBoxFont(fontDescriptor.getFontName()); + if (font != null) { - // then try substitutes - for (String substituteName : getSubstitutes(postScriptName)) + return new FontMapping(font, false); + } + else + { + // fallback - todo: i.e. fuzzy match + String fontName = getFallbackFontName(fontDescriptor); + font = findFontBoxFont(fontName); + if (font == null) { - t1 = getProvider().getType1Font(substituteName); - if (t1 != null) - { - return t1; - } + // we have to return something here as TTFs aren't strictly required on the system + log.error("Using last-resort fallback for font '" + fontName + "'"); + font = lastResortFont; } - // then Windows name - t1 = getProvider().getType1Font(windowsToPs(postScriptName)); + return new FontMapping(font, true); } - return t1; } /** - * Finds a CFF Type 1 font with the given PostScript name, or a suitable substitute, or null. + * Finds a font with the given PostScript name, or a suitable substitute, or null. * * @param postScriptName PostScript font name */ - public static CFFType1Font getCFFType1Font(String postScriptName) + private static FontBoxFont findFontBoxFont(String postScriptName) { - CFFFont cff = getCFFFont(postScriptName); + Type1Font t1 = (Type1Font)findFont(FontFormat.PFB, postScriptName); + if (t1 != null) + { + return t1; + } + + CFFFont cff = (CFFFont)findFont(FontFormat.OTF, postScriptName); if (cff instanceof CFFType1Font) { - return (CFFType1Font)cff; + return cff; + } + + TrueTypeFont ttf = (TrueTypeFont)findFont(FontFormat.TTF, postScriptName); + if (ttf != null) + { + return ttf; } + return null; } /** - * Finds a CFF CID-Keyed font with the given PostScript name, or a suitable substitute, or null. + * Finds a font with the given PostScript name, or a suitable substitute, or null. * * @param postScriptName PostScript font name */ - public static CFFCIDFont getCFFCIDFont(String postScriptName) + private static FontBoxFont findFont(FontFormat format, String postScriptName) { - CFFFont cff = getCFFFont(postScriptName); - if (cff instanceof CFFCIDFont) + // make sure the font provider is initialized + if (fontProvider == null) + { + getProvider(); + } + + // first try to match the PostScript name + FontInfo info = getFont(format, postScriptName); + if (info != null) + { + return info.getFont(); + } + + // remove hyphens (e.g. Arial-Black -> ArialBlack) + info = getFont(format, postScriptName.replaceAll("-", "")); + if (info != null) + { + return info.getFont(); + } + + // then try named substitutes + for (String substituteName : getSubstitutes(postScriptName)) { - return (CFFCIDFont)cff; + info = getFont(format, substituteName); + if (info != null) + { + return info.getFont(); + } + } + + // then try converting Windows names e.g. (ArialNarrow,Bold) -> (ArialNarrow-Bold) + info = getFont(format, postScriptName.replaceAll(",", "-")); + if (info != null) + { + return info.getFont(); } + + // no matches return null; } /** - * Finds a CFF font with the given PostScript name, or a suitable substitute, or null. - * - * @param postScriptName PostScript font name + * Finds the named font with the given format. */ - private static CFFFont getCFFFont(String postScriptName) + private static FontInfo getFont(FontFormat format, String postScriptName) { - // first ask the font provider for the font - CFFFont cff = getProvider().getCFFFont(postScriptName); - if (cff == null) + // strip subset tag (happens when we substitute a corrupt embedded font, see PDFBOX-2642) + if (postScriptName.contains("+")) { - // then try substitutes - for (String substituteName : getSubstitutes(postScriptName)) - { - cff = getProvider().getCFFFont(substituteName); - if (cff != null) - { - return cff; - } - } - - // then Windows name - cff = getProvider().getCFFFont(windowsToPs(postScriptName)); + postScriptName = postScriptName.substring(postScriptName.indexOf("+") + 1); + } + + // look up the PostScript name + FontInfo info = fontInfoByName.get(postScriptName); + if (info != null && info.getFormat() == format) + { + return info; } - return cff; + return null; } - + /** - * Finds a Type 1-equivalent font with the given PostScript name, or a suitable substitute, - * or null. This allows a Type 1 font to be substituted with a PFB, TTF or OTF. - * - * @param postScriptName PostScript font name + * Finds a CFF CID-Keyed font with the given PostScript name, or a suitable substitute, or null. + * This method can also map CJK fonts via their CIDSystemInfo (ROS). + * + * @param fontDescriptor FontDescriptor + * @param cidSystemInfo the CID system info, e.g. "Adobe-Japan1", if any. */ - public static Type1Equivalent getType1EquivalentFont(String postScriptName) + public static CIDFontMapping getCIDFont(PDFontDescriptor fontDescriptor, + PDCIDSystemInfo cidSystemInfo) { - Type1Font t1 = getType1Font(postScriptName); - if (t1 != null) + // try name match or substitute with OTF + OpenTypeFont otf1 = (OpenTypeFont)findFont(FontFormat.OTF, fontDescriptor.getFontName()); + if (otf1 != null) { - return t1; + return new CIDFontMapping(otf1, null, false); } - CFFType1Font cff = getCFFType1Font(postScriptName); - if (cff != null) + // try name match or substitute with TTF + TrueTypeFont ttf = (TrueTypeFont)findFont(FontFormat.TTF, fontDescriptor.getFontName()); + if (ttf != null) { - return cff; + return new CIDFontMapping(null, ttf, false); } - TrueTypeFont ttf = getTrueTypeFont(postScriptName); - if (ttf != null) + if (cidSystemInfo != null) { - return ttf; + // "In Acrobat 3.0.1 and later, Type 0 fonts that use a CMap whose CIDSystemInfo + // dictionary defines the Adobe-GB1, Adobe-CNS1 Adobe-Japan1, or Adobe-Korea1 character + // collection can also be substituted." - Adobe Supplement to the ISO 32000 + + String collection = cidSystemInfo.getRegistry() + "-" + cidSystemInfo.getOrdering(); + + if (collection.equals("Adobe-GB1") || collection.equals("Adobe-CNS1") || + collection.equals("Adobe-Japan1") || collection.equals("Adobe-Korea1")) + { + // try automatic substitutes via character collection + for (FontInfo info : fontInfoByName.values()) + { + if (info.getCIDSystemInfo() != null && + info.getCIDSystemInfo().getRegistry().equals(cidSystemInfo.getRegistry()) && + info.getCIDSystemInfo().getOrdering().equals(cidSystemInfo.getOrdering())) + { + return new CIDFontMapping((OpenTypeFont)info.getFont(), null, true); + } + } + } } - return null; + // last-resort fallback + return new CIDFontMapping(null, lastResortFont, true); } } Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontMapping.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontMapping.java?rev=1687878&view=auto ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontMapping.java (added) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontMapping.java Sat Jun 27 00:18:21 2015 @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.font; + +import org.apache.fontbox.FontBoxFont; + +/** + * A font mapping from a PDF font to a FontBox font. + * + * @author John Hewson + */ +public class FontMapping +{ + private final T font; + private final boolean isFallback; + + FontMapping(T font, boolean isFallback) + { + this.font = font; + this.isFallback = isFallback; + } + + /** + * Returns the mapped, FontBox font. This is never null. + */ + public T getFont() + { + return font; + } + + /** + * Returns true if the mapped font is a fallback, i.e. a substitute based on basic font style, + * such as bold/italic, rather than font name. + */ + public boolean isFallback() + { + return isFallback; + } +} Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontMapping.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontProvider.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontProvider.java?rev=1687878&r1=1687877&r2=1687878&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontProvider.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/FontProvider.java Sat Jun 27 00:18:21 2015 @@ -16,93 +16,23 @@ */ package org.apache.pdfbox.pdmodel.font; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; -import org.apache.fontbox.cff.CFFFont; -import org.apache.fontbox.ttf.TrueTypeFont; -import org.apache.fontbox.type1.Type1Font; +import java.util.List; /** - * External font service provider interface. Implementations are expected to be thread safe. + * External font service provider interface. * * @author John Hewson */ public abstract class FontProvider { /** - * Returns a TrueType which corresponds to the given PostScript name. If there is no - * suitable font, then this method will return null. - * - * @param postScriptName PostScript font name - */ - public abstract TrueTypeFont getTrueTypeFont(String postScriptName); - - /** - * Returns a CFF font which corresponds to the given PostScript name. If there is no - * suitable font, then this method will return null. - * - * @param postScriptName PostScript font name - */ - public abstract CFFFont getCFFFont(String postScriptName); - - /** - * Returns a Type 1 which corresponds to the given PostScript name. If there is no - * suitable font, then this method will return null. - * - * @param postScriptName PostScript font name - */ - public abstract Type1Font getType1Font(String postScriptName); - - /** * Returns a string containing debugging information. This will be written to the log if no * suitable fonts are found and no fallback fonts are available. May be null. */ public abstract String toDebugString(); /** - * Returns the font names for a given font. This allows substitution based on the PostScript - * name of the external font, instead of just the BaseName in the PDF. + * Returns a list of information about fonts on the system. */ - protected final Set getNames(TrueTypeFont font) throws IOException - { - return getPostScriptNames(font.getName()); - // could add format-specific names here if needed - } - - /** - * Returns the font names for a given font. This allows substitution based on the PostScript - * name of the external font, instead of just the BaseName in the PDF. - */ - protected final Set getNames(Type1Font font) throws IOException - { - return getPostScriptNames(font.getName()); - // could add format-specific names here if needed - } - - /** - * Returns the font names for a given font. This allows substitution based on the PostScript - * name of the external font, instead of just the BaseName in the PDF. - */ - protected final Set getNames(CFFFont font) throws IOException - { - return getPostScriptNames(font.getName()); - // could add format-specific names here if needed - } - - /** - * Returns a list of alternative names for the given PostScript name. - */ - private Set getPostScriptNames(String postScriptName) throws IOException - { - Set names = new HashSet(); - - // built-in PostScript name - names.add(postScriptName); - - // remove hyphens (e.g. Arial-Black -> ArialBlack) - names.add(postScriptName.replaceAll("-", "")); - - return names; - } + public abstract List getFontInfo(); } 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=1687878&r1=1687877&r2=1687878&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 Jun 27 00:18:21 2015 @@ -38,7 +38,7 @@ import org.apache.pdfbox.util.Vector; * * @author Ben Litchfield */ -public abstract class PDCIDFont implements COSObjectable, PDFontLike +public abstract class PDCIDFont implements COSObjectable, PDFontLike, PDVectorFont { protected final PDType0Font parent; Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType0.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType0.java?rev=1687878&r1=1687877&r2=1687878&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType0.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType0.java Sat Jun 27 00:18:21 2015 @@ -17,6 +17,7 @@ package org.apache.pdfbox.pdmodel.font; import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.io.IOException; import java.util.HashMap; @@ -24,6 +25,7 @@ import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.cff.CFFCIDFont; import org.apache.fontbox.cff.CFFFont; import org.apache.fontbox.cff.CFFParser; @@ -31,7 +33,6 @@ import org.apache.fontbox.cff.CFFType1Fo import org.apache.fontbox.cff.Type2CharString; import org.apache.fontbox.util.BoundingBox; import org.apache.pdfbox.cos.COSDictionary; -import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.io.IOUtils; import org.apache.pdfbox.pdmodel.common.PDStream; import org.apache.pdfbox.util.Matrix; @@ -47,7 +48,7 @@ public class PDCIDFontType0 extends PDCI private static final Log LOG = LogFactory.getLog(PDCIDFontType0.class); private final CFFCIDFont cidFont; // Top DICT that uses CIDFont operators - private final CFFType1Font t1Font; // Top DICT that does not use CIDFont operators + private final FontBoxFont t1Font; // Top DICT that does not use CIDFont operators private final Map glyphHeights = new HashMap(); private final boolean isEmbedded; @@ -81,8 +82,8 @@ public class PDCIDFontType0 extends PDCI CFFFont cffFont = null; if (bytes != null && bytes.length > 0 && (bytes[0] & 0xff) == '%') { - // todo: PDFBOX-2642 contains a Type1 PFB font in a CIDFont, but we can't handle it yet - LOG.error("Unsupported: Type1 font instead of CFF in " + fd.getFontName()); + // PDFBOX-2642 contains a corrupt PFB font instead of a CFF + LOG.warn("Found PFB but expected embedded CFF font " + fd.getFontName()); fontIsDamaged = true; } else if (bytes != null) @@ -110,53 +111,30 @@ public class PDCIDFontType0 extends PDCI else { cidFont = null; - t1Font = (CFFType1Font)cffFont; + t1Font = cffFont; } isEmbedded = true; isDamaged = false; } else { - // substitute - CFFCIDFont cidSub = ExternalFonts.getCFFCIDFont(getBaseFont()); - if (cidSub != null) + // find font or substitute + CIDFontMapping mapping = FontMapper.getCIDFont(getFontDescriptor(), getCIDSystemInfo()); + + if (mapping.isCIDFont()) { - cidFont = cidSub; + cidFont = (CFFCIDFont)mapping.getFont().getCFF().getFont(); t1Font = null; } else { - COSDictionary cidSystemInfo = (COSDictionary) - dict.getDictionaryObject(COSName.CIDSYSTEMINFO); - - String registryOrdering = null; - if (cidSystemInfo != null) - { - String registry = cidSystemInfo.getNameAsString(COSName.REGISTRY); - String ordering = cidSystemInfo.getNameAsString(COSName.ORDERING); - if (registry != null && ordering != null) - { - registryOrdering = registry + "-" + ordering; - } - } - - cidSub = ExternalFonts.getCFFCIDFontFallback(registryOrdering, getFontDescriptor()); - cidFont = cidSub; - t1Font = null; + cidFont = null; + t1Font = mapping.getTrueTypeFont(); + } - if (cidSub.getName().equals("AdobeBlank")) - { - // this error often indicates that the user needs to install the Adobe Reader - // Asian and Extended Language Pack - if (!fontIsDamaged) - { - LOG.error("Missing CID-keyed font " + getBaseFont()); - } - } - else - { - LOG.warn("Using fallback for CID-keyed font " + getBaseFont()); - } + if (mapping.isFallback()) + { + LOG.warn("Using fallback for CID-keyed font " + getBaseFont()); } isEmbedded = false; isDamaged = fontIsDamaged; @@ -164,7 +142,7 @@ public class PDCIDFontType0 extends PDCI fontMatrixTransform = getFontMatrix().createAffineTransform(); fontMatrixTransform.scale(1000, 1000); } - + @Override public Matrix getFontMatrix() { @@ -177,7 +155,14 @@ public class PDCIDFontType0 extends PDCI } else { - numbers = t1Font.getFontMatrix(); + try + { + numbers = t1Font.getFontMatrix(); + } + catch (IOException e) + { + return new Matrix(0.001f, 0, 0, 0.001f, 0, 0); + } } if (numbers != null && numbers.size() == 6) @@ -203,12 +188,19 @@ public class PDCIDFontType0 extends PDCI } else { - return t1Font.getFontBBox(); + try + { + return t1Font.getFontBBox(); + } + catch (IOException e) + { + return new BoundingBox(); + } } } /** - * Returns the embedded CFF CIDFont. + * Returns the embedded CFF CIDFont, or null if the substitute is not a CFF font. */ public CFFFont getCFFFont() { @@ -216,14 +208,19 @@ public class PDCIDFontType0 extends PDCI { return cidFont; } + else if (t1Font instanceof CFFType1Font) + { + return (CFFType1Font)t1Font; + } else { - return t1Font; + return null; } } /** - * Returns the Type 2 charstring for the given CID. + * Returns the Type 2 charstring for the given CID, or null if the substituted font does not + * contain Type 2 charstrings. * * @param cid CID * @throws IOException if the charstring could not be read @@ -234,9 +231,65 @@ public class PDCIDFontType0 extends PDCI { return cidFont.getType2CharString(cid); } + else if (t1Font instanceof CFFType1Font) + { + return ((CFFType1Font)t1Font).getType2CharString(cid); + } + else + { + return null; + } + } + + /** + * Returns the name of the glyph with the given character code. This is done by looking up the + * code in the parent font's ToUnicode map and generating a glyph name from that. + */ + private String getGlyphName(int code) throws IOException + { + String unicodes = parent.toUnicode(code); + if (unicodes == null) + { + return ".notdef"; + } + return String.format("uni%04X", unicodes.codePointAt(0)); + } + + @Override + public GeneralPath getPath(int code) throws IOException + { + int cid = codeToCID(code); + Type2CharString charstring = getType2CharString(cid); + if (charstring != null) + { + return charstring.getPath(); + } + else if (isEmbedded && t1Font instanceof CFFType1Font) + { + return ((CFFType1Font)t1Font).getType2CharString(cid).getPath(); + } else { - return t1Font.getType2CharString(cid); + return t1Font.getPath(getGlyphName(code)); + } + } + + @Override + public boolean hasGlyph(int code) throws IOException + { + int cid = codeToCID(code); + Type2CharString charstring = getType2CharString(cid); + if (charstring != null) + { + return charstring.getGID() != 0; + } + else if (isEmbedded && t1Font instanceof CFFType1Font) + { + return ((CFFType1Font)t1Font).getType2CharString(cid).getGID() != 0; + } + else + { + return t1Font.hasGlyph(getGlyphName(code)); } } @@ -281,8 +334,20 @@ public class PDCIDFontType0 extends PDCI public float getWidthFromFont(int code) throws IOException { int cid = codeToCID(code); - int width = getType2CharString(cid).getWidth(); - + float width; + if (cidFont != null) + { + width = getType2CharString(cid).getWidth(); + } + else if (isEmbedded && t1Font instanceof CFFType1Font) + { + width = ((CFFType1Font)t1Font).getType2CharString(cid).getWidth(); + } + else + { + width = t1Font.getWidth(getGlyphName(code)); + } + Point2D p = new Point2D.Float(width, 0); fontMatrixTransform.transform(p, p); return (float)p.getX();