You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by ss...@apache.org on 2014/07/16 10:51:23 UTC
svn commit: r1610937 [1/2] - in /xmlgraphics/fop-pdf-images/trunk: ./ lib/
lib/build/ src/java/org/apache/fop/render/pdf/pdfbox/
test/java/org/apache/fop/render/pdf/ test/resources/
Author: ssteiner
Date: Wed Jul 16 08:51:22 2014
New Revision: 1610937
URL: http://svn.apache.org/r1610937
Log:
Merge font merging
Added:
xmlgraphics/fop-pdf-images/trunk/checkstyle-5.5.xml
- copied unchanged from r1610934, xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/checkstyle-5.5.xml
xmlgraphics/fop-pdf-images/trunk/lib/build/avalon-framework-4.2.0.jar (with props)
xmlgraphics/fop-pdf-images/trunk/lib/build/qdox-1.12.jar
- copied unchanged from r1610934, xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/build/qdox-1.12.jar
xmlgraphics/fop-pdf-images/trunk/lib/fontbox-2.0.0-SNAPSHOT.jar
- copied unchanged from r1610934, xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/fontbox-2.0.0-SNAPSHOT.jar
xmlgraphics/fop-pdf-images/trunk/lib/fop.jar
- copied unchanged from r1610934, xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/fop.jar
xmlgraphics/fop-pdf-images/trunk/lib/jacocoant.jar
- copied unchanged from r1610934, xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/jacocoant.jar
xmlgraphics/fop-pdf-images/trunk/lib/jempbox-2.0.0-SNAPSHOT.jar
- copied unchanged from r1610934, xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/jempbox-2.0.0-SNAPSHOT.jar
xmlgraphics/fop-pdf-images/trunk/lib/pdfbox-2.0.0-SNAPSHOT.jar
- copied unchanged from r1610934, xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/pdfbox-2.0.0-SNAPSHOT.jar
xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/MergeCFFFonts.java
- copied unchanged from r1610934, xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeCFFFonts.java
xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
- copied unchanged from r1610934, xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/MergeType1Fonts.java
- copied unchanged from r1610934, xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeType1Fonts.java
xmlgraphics/fop-pdf-images/trunk/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java
- copied unchanged from r1610934, xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java
xmlgraphics/fop-pdf-images/trunk/test/resources/
- copied from r1610934, xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/test/resources/
Removed:
xmlgraphics/fop-pdf-images/trunk/lib/build/qdox-1.6.3.jar
xmlgraphics/fop-pdf-images/trunk/lib/fontbox-1.8.5.jar
xmlgraphics/fop-pdf-images/trunk/lib/fop-svn-trunk.jar
xmlgraphics/fop-pdf-images/trunk/lib/jempbox-1.8.5.jar
xmlgraphics/fop-pdf-images/trunk/lib/pdfbox-1.8.5.jar
Modified:
xmlgraphics/fop-pdf-images/trunk/ (props changed)
xmlgraphics/fop-pdf-images/trunk/build.xml
xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java
xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/Cache.java
xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java
xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java
xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxEventProducer.java
xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxImageHandler.java
xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFUtil.java
xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PreloaderPDF.java
xmlgraphics/fop-pdf-images/trunk/test/java/org/apache/fop/render/pdf/PDFRotateTestCase.java
Propchange: xmlgraphics/fop-pdf-images/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (added)
+++ svn:mergeinfo Wed Jul 16 08:51:22 2014
@@ -0,0 +1 @@
+/xmlgraphics/fop-pdf-images/branches/Temp_FontMerging:1600597-1610934
Modified: xmlgraphics/fop-pdf-images/trunk/build.xml
URL: http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/trunk/build.xml?rev=1610937&r1=1610936&r2=1610937&view=diff
==============================================================================
--- xmlgraphics/fop-pdf-images/trunk/build.xml (original)
+++ xmlgraphics/fop-pdf-images/trunk/build.xml Wed Jul 16 08:51:22 2014
@@ -15,7 +15,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<project default="all" basedir="." name="fop-pdf-images">
+<project default="all" basedir="." name="fop-pdf-images" xmlns:jacoco="antlib:org.jacoco.ant">
<!-- See build.properties and build-local.properties for overriding build settings. -->
<!-- build-local.properties is not stored in SVN and overrides values from build.properties -->
@@ -111,16 +111,22 @@
</classpath>
</javac>
</target>
+
+ <property name="jacoco.report.dir" value="${build.dir}/coverage"/>
+ <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml" classpathref="libs-build-classpath"/>
<target name="junit" depends="junit-compile-java" description="Runs PDF Plugin's JUnit basic tests">
<property name="junit.reports.dir" value="${build.dir}/test-reports"/>
<mkdir dir="${junit.reports.dir}"/>
- <junit dir="${basedir}" haltonfailure="yes">
+
+ <jacoco:coverage destfile="${jacoco.report.dir}/jacoco.exec">
+ <junit dir="${basedir}" haltonfailure="yes" fork="true" forkmode="once">
<sysproperty key="jawa.awt.headless" value="true"/>
<formatter type="brief" usefile="false"/>
<formatter type="plain" usefile="true"/>
<formatter type="xml" usefile="true"/>
<classpath>
+ <pathelement location="${coverage.dir}" />
<path refid="standard-junit-classpath"/>
</classpath>
<assertions>
@@ -130,6 +136,27 @@
<fileset dir="${build.unit.tests.dir}" includes="**/*TestCase.class"/>
</batchtest>
</junit>
+ </jacoco:coverage>
+ <jacoco:report>
+ <executiondata>
+ <file file="${jacoco.report.dir}/jacoco.exec"/>
+ </executiondata>
+ <structure name="Apache FOP">
+ <classfiles>
+ <fileset dir="${build.classes.dir}"/>
+ </classfiles>
+ <sourcefiles>
+ <fileset dir="${src.java.dir}"/>
+ </sourcefiles>
+ </structure>
+ <html destdir="${jacoco.report.dir}"/>
+ <xml destfile="${jacoco.report.dir}/report.xml"/>
+ <check>
+ <rule element="PACKAGE">
+ <limit counter="LINE" value="COVEREDRATIO" minimum="0.76"/>
+ </rule>
+ </check>
+ </jacoco:report>
</target>
<!-- =================================================================== -->
@@ -392,4 +419,35 @@
<target name="all" depends="package,junit"/>
+ <target name="checkstyle" description="Runs Checkstyle for a code quality report">
+ <taskdef name="checkstyle" classname="com.puppycrawl.tools.checkstyle.CheckStyleTask" classpathref="libs-build-tools-classpath"/>
+ <checkstyle config="checkstyle-5.5.xml" failonviolation="true" maxWarnings="0">
+ <fileset dir="${src.java.dir}" includes="**/*.java"/>
+ <formatter type="plain"/>
+ </checkstyle>
+ </target>
+
+ <property name="findbugs.lib" value="${findbugs.home.dir}/lib"/>
+ <path id="libs-findbugs">
+ <fileset dir="${findbugs.lib}">
+ <include name="*.jar"/>
+ </fileset>
+ </path>
+ <target name="findbugs">
+ <taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask" classpathref="libs-findbugs"/>
+ <findbugs home="${findbugs.home.dir}" output="text" reportLevel="low" effort="max" jvmargs="-Xmx1024m" warningsProperty="findbugs.warnings">
+ <sourcePath path="${src.java.dir}"/>
+ <class location="${build.classes.dir}"/>
+ <auxClasspath>
+ <path refid="libs-build-classpath"/>
+ <path>
+ <fileset dir="${ant.library.dir}">
+ <include name="ant.jar"/>
+ <include name="ant-launcher.jar"/>
+ </fileset>
+ </path>
+ </auxClasspath>
+ </findbugs>
+ <fail if="findbugs.warnings"/>
+ </target>
</project>
Added: xmlgraphics/fop-pdf-images/trunk/lib/build/avalon-framework-4.2.0.jar
URL: http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/trunk/lib/build/avalon-framework-4.2.0.jar?rev=1610937&view=auto
==============================================================================
Binary file - no diff available.
Propchange: xmlgraphics/fop-pdf-images/trunk/lib/build/avalon-framework-4.2.0.jar
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Modified: xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java?rev=1610937&r1=1610936&r2=1610937&view=diff
==============================================================================
--- xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java (original)
+++ xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java Wed Jul 16 08:51:22 2014
@@ -34,6 +34,7 @@ import org.apache.xmlgraphics.image.load
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.fonts.FontInfo;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFPage;
import org.apache.fop.pdf.PDFResources;
@@ -54,18 +55,17 @@ public abstract class AbstractPDFBoxHand
return Cache.createCache(CACHE_TYPE);
}
- private static final ValueMaker<Map<Object, Object>> MAP_MAKER
- = new ValueMaker<Map<Object, Object>>() {
+ private static final ValueMaker<Map<Object, Object>> MAP_MAKER = new ValueMaker<Map<Object, Object>>() {
public Map<Object, Object> make() throws Exception {
return new HashMap<Object, Object>();
}
};
private static Map<Object, Cache<String, Map<Object, Object>>> objectCacheMap
- = Collections.synchronizedMap(new WeakHashMap<Object, Cache<String, Map<Object, Object>>>());
+ = Collections.synchronizedMap(new WeakHashMap<Object, Cache<String, Map<Object, Object>>>());
- protected String createStreamForPDF(ImagePDF image,
- PDFPage targetPage, FOUserAgent userAgent, AffineTransform at, Rectangle pos) throws IOException {
+ protected String createStreamForPDF(ImagePDF image, PDFPage targetPage, FOUserAgent userAgent,
+ AffineTransform at, FontInfo fontinfo, Rectangle pos) throws IOException {
EventBroadcaster eventBroadcaster = userAgent.getEventBroadcaster();
String originalImageUri = image.getInfo().getOriginalURI();
@@ -112,7 +112,7 @@ public abstract class AbstractPDFBoxHand
PDFBoxAdapter adapter = new PDFBoxAdapter(targetPage, objectCache);
String stream = adapter.createStreamFromPDFBoxPage(pddoc, page, originalImageUri,
- eventBroadcaster, at, pos);
+ eventBroadcaster, at, fontinfo, pos);
return stream;
}
Modified: xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/Cache.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/Cache.java?rev=1610937&r1=1610936&r2=1610937&view=diff
==============================================================================
--- xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/Cache.java (original)
+++ xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/Cache.java Wed Jul 16 08:51:22 2014
@@ -1,3 +1,19 @@
+/*
+ * 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.fop.render.pdf.pdfbox;
import java.lang.ref.SoftReference;
@@ -5,7 +21,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
-abstract class Cache<K,V> {
+abstract class Cache<K, V> {
public enum Type {
WEAK, SOFT, STRONG;
@@ -13,20 +29,24 @@ abstract class Cache<K,V> {
public abstract V getValue(K key, ValueMaker<V> valueMaker) throws Exception;
- public static <K,V> Cache<K,V> createCache(Type cacheType) {
+ public static <K, V> Cache<K, V> createCache(Type cacheType) {
switch (cacheType) {
- case WEAK: return new WeakDocumentCache<K,V>();
- case SOFT: return new SoftDocumentCache<K,V>();
- case STRONG: return new StrongDocumentCache<K,V>();
- default: return createDefaultCache();
+ case WEAK:
+ return new WeakDocumentCache<K, V>();
+ case SOFT:
+ return new SoftDocumentCache<K, V>();
+ case STRONG:
+ return new StrongDocumentCache<K, V>();
+ default:
+ return createDefaultCache();
}
}
- private static <K,V> Cache<K,V> createDefaultCache() {
- return new WeakDocumentCache<K,V>();
+ private static <K, V> Cache<K, V> createDefaultCache() {
+ return new WeakDocumentCache<K, V>();
}
- private static class StrongDocumentCache<K,V> extends Cache<K,V> {
+ private static class StrongDocumentCache<K, V> extends Cache<K, V> {
private final Map<K, V> cache = new HashMap<K, V>();
@@ -41,15 +61,12 @@ abstract class Cache<K,V> {
}
}
- private static class SoftDocumentCache<K,V> extends Cache<K,V> {
+ private static class SoftDocumentCache<K, V> extends Cache<K, V> {
- private final Map<K, SoftReference<Object>> softKeys
- = new HashMap<K, SoftReference<Object>>();
+ private final Map<K, SoftReference<Object>> softKeys = new HashMap<K, SoftReference<Object>>();
private final Map<Object, V> cache = new WeakHashMap<Object, V>();
- private Object currentKey;
-
@Override
public V getValue(K key, ValueMaker<V> valueMaker) throws Exception {
SoftReference<Object> reference = softKeys.get(key);
@@ -61,7 +78,6 @@ abstract class Cache<K,V> {
} else {
softKey = reference.get();
}
- currentKey = softKey;
V value = cache.get(softKey);
if (value == null) {
value = valueMaker.make();
@@ -71,7 +87,7 @@ abstract class Cache<K,V> {
}
}
- private static class WeakDocumentCache<K,V> extends Cache<K,V> {
+ private static class WeakDocumentCache<K, V> extends Cache<K, V> {
private V currentValue;
@@ -87,7 +103,7 @@ abstract class Cache<K,V> {
}
}
- public static interface ValueMaker<V> {
+ public interface ValueMaker<V> {
V make() throws Exception;
}
}
Modified: xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java?rev=1610937&r1=1610936&r2=1610937&view=diff
==============================================================================
--- xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java (original)
+++ xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java Wed Jul 16 08:51:22 2014
@@ -26,10 +26,10 @@ import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.Map;
-import org.apache.pdfbox.pdfviewer.PageDrawer;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.rendering.PageDrawer;
import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.image.loader.ImageException;
@@ -129,8 +129,8 @@ public class ImageConverterPDF2G2D exten
area.getHeight() / pageDimension.height);
g2d.transform(at);
- PageDrawer drawer = new PageDrawer();
- drawer.drawPage(g2d, page, pageDimension);
+ PageDrawer drawer = new PageDrawer(null);
+ drawer.drawPage(g2d, page, mediaBox);
} catch (IOException ioe) {
//TODO Better exception handling
throw new RuntimeException("I/O error while painting PDF page", ioe);
Modified: xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java?rev=1610937&r1=1610936&r2=1610937&view=diff
==============================================================================
--- xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java (original)
+++ xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java Wed Jul 16 08:51:22 2014
@@ -21,7 +21,6 @@ package org.apache.fop.render.pdf.pdfbox
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -31,15 +30,29 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.fontbox.cff.CFFFont;
+import org.apache.fontbox.cff.CFFFontROS;
+import org.apache.fontbox.cff.charset.CFFCharset;
+import org.apache.fontbox.cff.encoding.CFFEncoding;
+import org.apache.fontbox.cmap.CMap;
+import org.apache.fontbox.ttf.CMAPEncodingEntry;
+import org.apache.fontbox.ttf.GlyphData;
+import org.apache.fontbox.ttf.MaximumProfileTable;
+import org.apache.fontbox.ttf.TrueTypeFont;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSBoolean;
@@ -48,9 +61,13 @@ import org.apache.pdfbox.cos.COSFloat;
import org.apache.pdfbox.cos.COSInteger;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSNull;
+import org.apache.pdfbox.cos.COSNumber;
import org.apache.pdfbox.cos.COSObject;
import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.cos.COSString;
+import org.apache.pdfbox.encoding.DictionaryEncoding;
+import org.apache.pdfbox.encoding.Encoding;
+import org.apache.pdfbox.encoding.EncodingManager;
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
@@ -61,10 +78,32 @@ import org.apache.pdfbox.pdmodel.common.
import org.apache.pdfbox.pdmodel.common.COSStreamArray;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.font.PDCIDFont;
+import org.apache.pdfbox.pdmodel.font.PDCIDFontType0Font;
+import org.apache.pdfbox.pdmodel.font.PDCIDFontType2Font;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDFontDescriptorDictionary;
+import org.apache.pdfbox.pdmodel.font.PDFontFactory;
+import org.apache.pdfbox.pdmodel.font.PDSimpleFont;
+import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.util.operator.PDFOperator;
import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.fonts.CIDFontType;
+import org.apache.fop.fonts.CustomFont;
+import org.apache.fop.fonts.EmbeddingMode;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontType;
+import org.apache.fop.fonts.MultiByteFont;
+import org.apache.fop.fonts.SingleByteEncoding;
+import org.apache.fop.fonts.SingleByteFont;
+import org.apache.fop.fonts.Typeface;
+import org.apache.fop.fonts.truetype.FontFileReader;
+import org.apache.fop.fonts.truetype.OTFSubSetFile;
import org.apache.fop.pdf.PDFArray;
import org.apache.fop.pdf.PDFDictionary;
import org.apache.fop.pdf.PDFDocument;
@@ -74,7 +113,9 @@ import org.apache.fop.pdf.PDFObject;
import org.apache.fop.pdf.PDFPage;
import org.apache.fop.pdf.PDFRoot;
import org.apache.fop.pdf.PDFStream;
-import org.apache.pdfbox.util.PDFOperator;
+import org.apache.fop.pdf.PDFText;
+import org.apache.fop.pdf.RefPDFFont;
+import org.apache.fop.util.CharUtilities;
/**
* This class provides an adapter for transferring content from a PDFBox PDDocument to
@@ -84,16 +125,21 @@ import org.apache.pdfbox.util.PDFOperato
public class PDFBoxAdapter {
/** logging instance */
- protected static Log log = LogFactory.getLog(PDFBoxAdapter.class);
+ protected static final Log log = LogFactory.getLog(PDFBoxAdapter.class);
- private static final Set filterFilter = new java.util.HashSet(
+ private static final Set FILTER_FILTER = new java.util.HashSet(
Arrays.asList(new String[] {"Filter", "DecodeParms"}));
+ private static final Pattern SUBSET_PATTERN = Pattern.compile("[A-Z][A-Z][A-Z][A-Z][A-Z][A-Z]\\+.+");
private final PDFPage targetPage;
private final PDFDocument pdfDoc;
private final Map clonedVersion;
+ private final Map<COSDictionary, PDSimpleFont> fontMap = new HashMap<COSDictionary, PDSimpleFont>();
+ private Map<COSName, String> newXObj = new HashMap<COSName, String>();
+ private Collection<String> parentFonts;
+
/**
* Creates a new PDFBoxAdapter.
* @param targetPage The target FOP PDF page object
@@ -113,8 +159,7 @@ public class PDFBoxAdapter {
return cloneForNewDocument(base, keyBase, Collections.EMPTY_LIST);
}
- private Object cloneForNewDocument(Object base, Object keyBase, Collection exclude)
- throws IOException {
+ private Object cloneForNewDocument(Object base, Object keyBase, Collection exclude) throws IOException {
if (base == null) {
return null;
}
@@ -126,8 +171,8 @@ public class PDFBoxAdapter {
PDFArray array = new PDFArray();
cacheClonedObject(keyBase, array);
List list = (List)base;
- for (int i = 0; i < list.size(); i++) {
- array.add(cloneForNewDocument(list.get(i), list.get(i), exclude));
+ for (Object o : list) {
+ array.add(cloneForNewDocument(o, o, exclude));
}
return array;
} else if (base instanceof COSObjectable && !(base instanceof COSBase)) {
@@ -135,29 +180,7 @@ public class PDFBoxAdapter {
Object retval = cloneForNewDocument(o, o, exclude);
return cacheClonedObject(keyBase, retval);
} else if (base instanceof COSObject) {
- COSObject object = (COSObject)base;
- if (log.isTraceEnabled()) {
- log.trace("Cloning indirect object: "
- + object.getObjectNumber().longValue()
- + " " + object.getGenerationNumber().longValue());
- }
- Object obj = cloneForNewDocument(object.getObject(), object, exclude);
- if (obj instanceof PDFObject) {
- PDFObject pdfobj = (PDFObject)obj;
- //pdfDoc.registerObject(pdfobj);
- if (!pdfobj.hasObjectNumber()) {
- throw new IllegalStateException("PDF object was not registered!");
- }
- if (log.isTraceEnabled()) {
- log.trace("Object registered: "
- + pdfobj.getObjectNumber()
- + " " + pdfobj.getGeneration()
- + " for COSObject: "
- + object.getObjectNumber().longValue()
- + " " + object.getGenerationNumber().longValue());
- }
- }
- return obj;
+ return readCOSObject((COSObject) base, exclude);
} else if (base instanceof COSArray) {
PDFArray newArray = new PDFArray();
cacheClonedObject(keyBase, newArray);
@@ -175,70 +198,30 @@ public class PDFBoxAdapter {
}
return newArray;
} else if (base instanceof COSStream) {
- COSStream originalStream = (COSStream)base;
-
- InputStream in;
- Set filter;
- if (pdfDoc.isEncryptionActive()) {
- in = originalStream.getUnfilteredStream();
- filter = filterFilter;
- } else {
- //transfer encoded data (don't reencode)
- in = originalStream.getFilteredStream();
- filter = Collections.EMPTY_SET;
- }
- PDFStream stream = new PDFStream();
- OutputStream out = stream.getBufferOutputStream();
- IOUtils.copyLarge(in, out);
- transferDict(originalStream, stream, filter);
- return cacheClonedObject(keyBase, stream);
+ return readCOSStream((COSStream) base, keyBase);
} else if (base instanceof COSDictionary) {
- COSDictionary dic = (COSDictionary)base;
- List keys = dic.keyList();
- PDFDictionary newDict = new PDFDictionary();
- cacheClonedObject(keyBase, newDict);
- for (int i = 0; i < keys.size(); i++) {
- COSName key = (COSName)keys.get(i);
- if (!exclude.contains(key)) {
- (newDict).put(key.getName(), cloneForNewDocument(dic.getItem(key), dic.getItem(key), exclude));
- }
- }
- return newDict;
+ return readCOSDictionary((COSDictionary) base, keyBase, exclude);
} else if (base instanceof COSName) {
PDFName newName = new PDFName(((COSName)base).getName());
return cacheClonedObject(keyBase, newName);
} else if (base instanceof COSInteger) {
PDFNumber number = new PDFNumber();
- number.setNumber(new Long(((COSInteger)base).longValue()));
+ number.setNumber(((COSInteger)base).longValue());
return cacheClonedObject(keyBase, number);
} else if (base instanceof COSFloat) {
PDFNumber number = new PDFNumber();
- number.setNumber(new Float(((COSFloat)base).floatValue()));
+ number.setNumber(((COSFloat)base).floatValue());
return cacheClonedObject(keyBase, number);
} else if (base instanceof COSBoolean) {
//TODO Do we need a PDFBoolean here?
Boolean retval = ((COSBoolean)base).getValueAsObject();
if (keyBase instanceof COSObject) {
- return cacheClonedObject(keyBase, new PDFBoolean(retval.booleanValue()));
+ return cacheClonedObject(keyBase, new PDFBoolean(retval));
} else {
return cacheClonedObject(keyBase, retval);
}
} else if (base instanceof COSString) {
- COSString string = (COSString)base;
- //retval = ((COSString)base).getString(); //this is unsafe for binary content
- byte[] bytes = string.getBytes();
- //Be on the safe side and use the byte array to avoid encoding problems
- //as PDFBox doesn't indicate whether the string is just
- //a string (PDF 1.4, 3.2.3) or a text string (PDF 1.4, 3.8.1).
- if (keyBase instanceof COSObject) {
- return cacheClonedObject(keyBase, new PDFString(bytes));
- } else {
- if (PDFString.isUSASCII(bytes)) {
- return cacheClonedObject(keyBase, string.getString());
- } else {
- return cacheClonedObject(keyBase, bytes);
- }
- }
+ return readCOSString((COSString) base, keyBase);
} else if (base instanceof COSNull) {
return cacheClonedObject(keyBase, null);
} else {
@@ -246,6 +229,77 @@ public class PDFBoxAdapter {
}
}
+ private PDFDictionary readCOSDictionary(COSDictionary dic, Object keyBase, Collection exclude) throws IOException {
+ PDFDictionary newDict = new PDFDictionary();
+ cacheClonedObject(keyBase, newDict);
+ for (Map.Entry<COSName, COSBase> e : dic.entrySet()) {
+ if (!exclude.contains(e.getKey())) {
+ newDict.put(e.getKey().getName(), cloneForNewDocument(e.getValue(), e.getValue(), exclude));
+ }
+ }
+ return newDict;
+ }
+
+ private Object readCOSObject(COSObject object, Collection exclude) throws IOException {
+ if (log.isTraceEnabled()) {
+ log.trace("Cloning indirect object: "
+ + object.getObjectNumber().longValue()
+ + " " + object.getGenerationNumber().longValue());
+ }
+ Object obj = cloneForNewDocument(object.getObject(), object, exclude);
+ if (obj instanceof PDFObject) {
+ PDFObject pdfobj = (PDFObject)obj;
+ //pdfDoc.registerObject(pdfobj);
+ if (!pdfobj.hasObjectNumber()) {
+ throw new IllegalStateException("PDF object was not registered!");
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("Object registered: "
+ + pdfobj.getObjectNumber()
+ + " " + pdfobj.getGeneration()
+ + " for COSObject: "
+ + object.getObjectNumber().longValue()
+ + " " + object.getGenerationNumber().longValue());
+ }
+ }
+ return obj;
+ }
+
+ private Object readCOSString(COSString string, Object keyBase) {
+ //retval = ((COSString)base).getString(); //this is unsafe for binary content
+ byte[] bytes = string.getBytes();
+ //Be on the safe side and use the byte array to avoid encoding problems
+ //as PDFBox doesn't indicate whether the string is just
+ //a string (PDF 1.4, 3.2.3) or a text string (PDF 1.4, 3.8.1).
+ if (keyBase instanceof COSObject) {
+ return cacheClonedObject(keyBase, new PDFString(bytes));
+ } else {
+ if (PDFString.isUSASCII(bytes)) {
+ return cacheClonedObject(keyBase, string.getString());
+ } else {
+ return cacheClonedObject(keyBase, bytes);
+ }
+ }
+ }
+
+ private Object readCOSStream(COSStream originalStream, Object keyBase) throws IOException {
+ InputStream in;
+ Set filter;
+ if (pdfDoc.isEncryptionActive()) {
+ in = originalStream.getUnfilteredStream();
+ filter = FILTER_FILTER;
+ } else {
+ //transfer encoded data (don't reencode)
+ in = originalStream.getFilteredStream();
+ filter = Collections.EMPTY_SET;
+ }
+ PDFStream stream = new PDFStream();
+ OutputStream out = stream.getBufferOutputStream();
+ IOUtils.copyLarge(in, out);
+ transferDict(originalStream, stream, filter);
+ return cacheClonedObject(keyBase, stream);
+ }
+
private Object getCachedClone(Object base) {
return clonedVersion.get(getBaseKey(base));
}
@@ -283,9 +337,8 @@ public class PDFBoxAdapter {
private void transferDict(COSDictionary orgDict, PDFStream targetDict,
Set filter, boolean inclusive) throws IOException {
- List keys = orgDict.keyList();
- for (int i = 0, ci = keys.size(); i < ci; i++) {
- COSName key = (COSName)keys.get(i);
+ Set<COSName> keys = orgDict.keySet();
+ for (COSName key : keys) {
if (inclusive && !filter.contains(key.getName())) {
continue;
} else if (!inclusive && filter.contains(key.getName())) {
@@ -296,6 +349,801 @@ public class PDFBoxAdapter {
}
}
+ private String getUniqueFontName(COSDictionary fontData) throws IOException {
+ PDSimpleFont font = getFont(fontData);
+ String extra = "";
+ String name = getName(font.getBaseFont()) + "_" + ((COSName)fontData.getItem(COSName.SUBTYPE)).getName();
+ if (font instanceof PDType0Font
+ && ((PDType0Font) font).getDescendantFont() instanceof PDCIDFontType0Font
+ && ((PDCIDFontType0Font) ((PDType0Font) font).getDescendantFont()).getType1CFont() != null) {
+ CFFFont cffFont =
+ ((PDCIDFontType0Font) ((PDType0Font) font).getDescendantFont()).getType1CFont().getCFFFont();
+ if (cffFont instanceof CFFFontROS
+ && ((CFFFontROS)cffFont).getFdSelect().getClass().getName()
+ .equals("org.apache.fontbox.cff.CFFParser$Format0FDSelect")) {
+ extra += "format0";
+ }
+ return name + extra;
+ }
+ if (font instanceof PDType0Font
+ && font.getToUnicode() != null
+ && ((PDType0Font) font).getDescendantFont() instanceof PDCIDFontType2Font) {
+ if (!isSubsetFont(font.getBaseFont())) {
+ extra = "f3";
+ }
+ return name + extra;
+ }
+ if (font instanceof PDTrueTypeFont && isSubsetFont(font.getBaseFont())) {
+ TrueTypeFont tt = ((PDTrueTypeFont) font).getTTFFont();
+ for (CMAPEncodingEntry c : tt.getCMAP().getCmaps()) {
+ if (c.getGlyphId(1) > 0) {
+ extra = "cid";
+ }
+ }
+ return name + extra;
+ }
+// if (!isSubsetFont(font.getBaseFont())) {
+// return font.getBaseFont() + "_" + ((COSName)fontData.getItem(COSName.SUBTYPE)).getName();
+// }
+ if (font instanceof PDType1Font) {
+ if (((PDType1Font) font).getType1CFont() == null
+ || ((PDType1Font) font).getType1CFont().getCFFFont() == null) {
+ if (font.getFontDescriptor() instanceof PDFontDescriptorDictionary) {
+ return name;
+ }
+ return null;
+ }
+ CFFEncoding encoding = ((PDType1Font)font).getType1CFont().getCFFFont().getEncoding();
+ String eClass = encoding.getClass().getName();
+ if (eClass.equals("org.apache.fontbox.cff.CFFParser$Format1Encoding")) {
+ extra = "f1enc";
+ } else if (eClass.equals("org.apache.fontbox.cff.CFFParser$Format0Encoding")) {
+ extra = "f0enc";
+ }
+ CFFCharset cs = ((PDType1Font)font).getType1CFont().getCFFFont().getCharset();
+ if (cs.getEntries().get(0).getSID() < OTFSubSetFile.NUM_STANDARD_STRINGS) {
+ extra += "stdcs";
+ }
+ if (cs.getClass().getName().equals("org.apache.fontbox.cff.CFFParser$Format1Charset")) {
+ extra += "f1cs";
+ }
+ return name + extra;
+ }
+ return null;
+ }
+
+ private static boolean isSubsetFont(String s) {
+ return SUBSET_PATTERN.matcher(s).matches();
+ }
+
+ private static String getName(String name) {
+ if (isSubsetFont(name)) {
+ return name.split("\\+")[1].replace(" ", "");
+ }
+ return name.replace(" ", "");
+ }
+
+ interface FOPPDFFont extends RefPDFFont {
+ String getFontName();
+ void setRef(PDFDictionary d);
+ String addFont(COSDictionary fontdata) throws IOException;
+ int size();
+ }
+
+ public class FOPPDFMultiByteFont extends MultiByteFont implements FOPPDFFont {
+ protected PDFDictionary ref;
+ private Map<Integer, Integer> newWidth = new TreeMap<Integer, Integer>();
+ private Map<String, Integer> charMapGlobal = new LinkedHashMap<String, Integer>();
+ private MergeTTFonts mergeTTFonts = new MergeTTFonts();
+ private MergeCFFFonts mergeCFFFonts = new MergeCFFFonts();
+ private Map<String, GlyphData> glyphs = new HashMap<String, GlyphData>();
+
+ public FOPPDFMultiByteFont(COSDictionary fontData, String name) throws IOException {
+ super(null, EmbeddingMode.SUBSET);
+ //this stops fop modifying font later on
+ setEmbeddingMode(EmbeddingMode.FULL);
+ readFontBBox(fontData);
+ setFontName(name);
+ addFont(fontData);
+ }
+
+ public String addFont(COSDictionary fontData) throws IOException {
+ PDSimpleFont font = getFont(fontData);
+ setProperties(this, font);
+ PDSimpleFont mainFont = font;
+ TrueTypeFont ttf = null;
+ if (font instanceof PDType0Font) {
+ PDCIDFont cidFont = (PDCIDFont) ((PDType0Font) font).getDescendantFont();
+ setDefaultWidth((int) cidFont.getDefaultWidth());
+ mainFont = cidFont;
+ if (cidFont instanceof PDCIDFontType0Font) {
+ setCIDType(CIDFontType.CIDTYPE0);
+ setFontType(FontType.CIDTYPE0);
+ } else {
+ ttf = ((PDCIDFontType2Font) cidFont).getTTFFont();
+ }
+ } else {
+ ttf = ((PDTrueTypeFont) font).getTTFFont();
+ setDefaultWidth(1000);
+ }
+ GlyphData[] glyphData = new GlyphData[0];
+ if (ttf != null) {
+ glyphData = ttf.getGlyph().getGlyphs();
+ }
+ Map<Integer, Integer> oldToNewGIMap = new HashMap<Integer, Integer>();
+ if (charMapGlobal.isEmpty()) {
+ oldToNewGIMap.put(0, 0); // .notdef glyph
+ }
+ CMap c = mainFont.getToUnicodeCMap();
+ if (c == null) {
+ c = font.getToUnicodeCMap();
+ }
+ Map<Integer, String> mapping = getMapping(mainFont, c, glyphData.length);
+ if (glyphData.length > 0 && differentGlyphData(glyphData, mapping)) {
+// return null;
+ }
+ Map<Integer, String> gidToGlyph = new TreeMap<Integer, String>(mapping);
+ if (mainFont instanceof PDTrueTypeFont) {
+ CMAPEncodingEntry cmap = ttf.getCMAP().getCmaps()[0];
+ gidToGlyph.clear();
+ int[] gidToCode = cmap.getGlyphIdToCharacterCode();
+ for (int i = 1; i < glyphData.length; i++) {
+ String mappedChar = mapping.get(gidToCode[i]);
+ gidToGlyph.put(i, mappedChar);
+ }
+ }
+ readCharMap(font, gidToGlyph, glyphData, mainFont, oldToNewGIMap);
+ FontFileReader ffr = readFontFile(mainFont);
+ if (ttf != null) {
+ mergeMaxp(ttf, mergeTTFonts.maxp);
+ int sizeNoCompGlyphs = oldToNewGIMap.size();
+ mergeTTFonts.readFont(ffr, oldToNewGIMap, true);
+ if (oldToNewGIMap.size() > sizeNoCompGlyphs) {
+ cidSet.mapChar(256 * 256, (char) 0);
+ }
+ } else {
+ mergeCFFFonts.readType1CFont(new ByteArrayInputStream(ffr.getAllBytes()), getEmbedFontName());
+ }
+ return getFontName();
+ }
+
+ private void readCharMap(PDSimpleFont font, Map<Integer, String> gidToGlyph, GlyphData[] glyphData,
+ PDSimpleFont mainFont, Map<Integer, Integer> oldToNewGIMap) {
+ int widthPos = font.getFirstChar() + 1;
+ for (Map.Entry<Integer, String> i : gidToGlyph.entrySet()) {
+ String mappedChar = i.getValue();
+ int key = i.getKey();
+ boolean skipWidth = (mappedChar == null) || mappedChar.length() == 0;
+ if (skipWidth) {
+ mappedChar = (char)charMapGlobal.size() + "tmp";
+ } else if (mappedChar.length() > 1) {
+ mappedChar = "" + (char)mappedChar.hashCode();
+ }
+ if (!charMapGlobal.containsKey(mappedChar)) {
+ if (glyphData.length > 0
+ && glyphData[key] == null
+ && !CharUtilities.isAdjustableSpace(mappedChar.charAt(0))) {
+ continue;
+ }
+ boolean addToEnd = charMapGlobal.containsValue(key);
+ if (addToEnd) {
+ addPrivateUseMapping(mappedChar.charAt(0), charMapGlobal.size() + 1);
+ charMapGlobal.put(mappedChar, charMapGlobal.size() + 1);
+ } else {
+ addPrivateUseMapping(mappedChar.charAt(0), key);
+ charMapGlobal.put(mappedChar, key);
+ }
+ int glyph = 0;
+ if (hasChar(mappedChar.charAt(0))) {
+ glyph = (int) mapChar(mappedChar.charAt(0));
+ }
+ oldToNewGIMap.put(key, glyph);
+ if (!skipWidth) {
+ if (!(mainFont instanceof PDTrueTypeFont)) {
+ widthPos = key;
+ }
+ float w = font.getFontWidth(widthPos);
+ if (w >= 0) {
+ if (mainFont instanceof PDCIDFontType0Font) {
+ newWidth.put(key, (int)w);
+ } else {
+ newWidth.put(glyph, (int)w);
+ }
+ }
+ }
+ }
+ if (!skipWidth) {
+ widthPos++;
+ }
+ }
+ }
+
+ private Map<Integer, String> getMapping(PDSimpleFont font, CMap c, int len) throws IOException {
+ Map<Integer, String> mapping = new HashMap<Integer, String>();
+ if (font instanceof PDCIDFontType0Font) {
+ Collection<CFFFont.Mapping> mappings =
+ ((PDCIDFontType0Font) font).getType1CFont().getCFFFont().getMappings();
+ for (CFFFont.Mapping m : mappings) {
+ String character = Encoding.getCharacterForName(m.getName());
+ mapping.put(m.getSID(), character);
+ }
+ }
+ if (c != null) {
+ int last = font.getLastChar();
+ if (last == -1) {
+ last = len;
+ }
+ int size = 1;
+ if (c.hasTwoByteMappings()) {
+ size = 2;
+ }
+ for (int i = font.getFirstChar(); i <= last; i++) {
+ String l = c.lookup(i, size);
+ if (l != null) {
+ mapping.put(i, l);
+ }
+ }
+ }
+ return mapping;
+ }
+
+ private boolean differentGlyphData(GlyphData[] data, Map<Integer, String> mapping) throws IOException {
+ Map<String, Integer> tmpMap = new HashMap<String, Integer>();
+ for (Map.Entry<Integer, String> entry : mapping.entrySet()) {
+ if (!tmpMap.containsKey(entry.getValue())) {
+ tmpMap.put(entry.getValue(), entry.getKey());
+ }
+ }
+ mapping.clear();
+ for (Map.Entry<String, Integer> entry : tmpMap.entrySet()) {
+ mapping.put(entry.getValue(), entry.getKey());
+ }
+
+ for (Map.Entry<Integer, String> n : mapping.entrySet()) {
+ if (data[n.getKey()] != null) {
+ if (glyphs.containsKey(n.getValue()) && !glyphs.get(n.getValue()).equals(data[n.getKey()])) {
+ return true;
+ }
+ glyphs.put(n.getValue(), data[n.getKey()]);
+ }
+ }
+ return false;
+ }
+
+ private FontFileReader readFontFile(PDFont font) throws IOException {
+ PDFontDescriptorDictionary fd = (PDFontDescriptorDictionary) font.getFontDescriptor();
+ PDStream ff = fd.getFontFile3();
+ if (ff == null) {
+ ff = fd.getFontFile2();
+ if (ff == null) {
+ ff = fd.getFontFile();
+ }
+ }
+ InputStream is = ff.createInputStream();
+ return new FontFileReader(new ByteArrayInputStream(IOUtils.toByteArray(is)));
+ }
+
+ public Map<Integer, Integer> getWidthsMap() {
+ return newWidth;
+ }
+
+ public PDFDictionary getRef() {
+ return ref;
+ }
+
+ public void setRef(PDFDictionary d) {
+ ref = d;
+ }
+
+ public int size() {
+ if (getFontType() == FontType.CIDTYPE0) {
+ return 1;
+ }
+ return 0;
+ }
+
+ private void readFontBBox(COSBase b) throws IOException {
+ if (b instanceof COSDictionary) {
+ COSDictionary dict = (COSDictionary)b;
+ for (Map.Entry<COSName, COSBase> n : dict.entrySet()) {
+ readFontBBox(n.getValue());
+ if (n.getKey() == COSName.FONT_BBOX) {
+ COSArray w = (COSArray)n.getValue();
+ float[] bboxf = w.toFloatArray();
+ int[] bbox = new int[bboxf.length];
+ for (int i = 0; i < bbox.length; i++) {
+ bbox[i] = (int)bboxf[i];
+ }
+ setFontBBox(bbox);
+ }
+ }
+ } else if (b instanceof COSObject) {
+ COSObject o = (COSObject)b;
+ readFontBBox(o.getObject());
+ } else if (b instanceof COSArray) {
+ COSArray o = (COSArray)b;
+ for (int i = 0; i < o.size(); i++) {
+ readFontBBox(o.get(i));
+ }
+ }
+ }
+
+ public boolean isEmbeddable() {
+ return true;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ if (getFontType() == FontType.CIDTYPE0) {
+ mergeCFFFonts.writeFont();
+ return new ByteArrayInputStream(mergeCFFFonts.getFontSubset());
+ }
+ mergeTTFonts.writeFont(null);
+ return new ByteArrayInputStream(mergeTTFonts.getFontSubset());
+ }
+ }
+
+ public static class Cmap {
+ int platformId;
+ int platformEncodingId;
+ Map<Integer, Integer> glyphIdToCharacterCode = new TreeMap<Integer, Integer>();
+ }
+
+ public class FOPPDFSingleByteFont extends SingleByteFont implements FOPPDFFont {
+ private int fontCount;
+ private PDSimpleFont font;
+ protected PDFDictionary ref;
+ private Map<String, Integer> charMapGlobal = new LinkedHashMap<String, Integer>();
+ private Map<Integer, Integer> newWidth = new HashMap<Integer, Integer>();
+ private Map<String, byte[]> charStringsDict;
+ private Cmap newCmap = new Cmap();
+ private Map<Integer, String> encodingMap = new TreeMap<Integer, String>();
+ private int encodingSkip;
+ private MergeTTFonts mergeTTFonts = new MergeTTFonts();
+ private MergeCFFFonts mergeCFFFonts = new MergeCFFFonts();
+ private MergeType1Fonts mergeType1Fonts = new MergeType1Fonts();
+ private String embedName;
+
+ public FOPPDFSingleByteFont(COSDictionary fontData, String name) throws IOException {
+ super(null, EmbeddingMode.FULL);
+ if (fontData.getItem(COSName.SUBTYPE) == COSName.TRUE_TYPE) {
+ setFontType(FontType.TRUETYPE);
+ }
+ width = new int[0];
+ font = getFont(fontData);
+ setFirstChar(font.getFirstChar());
+ setLastChar(font.getLastChar());
+ loadFontFile(font);
+ float[] bBoxF = font.getFontBoundingBox().getCOSArray().toFloatArray();
+ int[] bBox = new int[bBoxF.length];
+ for (int i = 0; i < bBox.length; i++) {
+ bBox[i] = (int)bBoxF[i];
+ }
+ setFontBBox(bBox);
+
+ setFontName(name);
+ Object cmap = getCmap(font);
+ for (int i = font.getFirstChar(); i <= font.getLastChar(); i++) {
+ String mappedChar = getChar(cmap, i);
+ if (mappedChar != null && !charMapGlobal.containsKey(mappedChar)) {
+ charMapGlobal.put(mappedChar, i);
+ }
+ }
+ //mark font as used
+ notifyMapOperation();
+ setProperties(this, font);
+ if (font.getWidths() != null) {
+ //if width contains 0 we cant rely on codeToNameMap
+ boolean usesZero = font.getWidths().contains(0);
+ Set<Integer> codeToName = getCodeToName(font.getFontEncoding()).keySet();
+ for (int i = getFirstChar();
+ i <= Math.min(getLastChar(), getFirstChar() + font.getWidths().size()); i++) {
+ if (usesZero || codeToName.contains(i)) {
+ int w = font.getWidths().get(i - getFirstChar()).intValue();
+ newWidth.put(i, w);
+ } else {
+ newWidth.put(i, 0);
+ }
+ }
+ }
+ mapping = new FOPPDFEncoding();
+ encodingSkip = font.getLastChar() + 1;
+ addEncoding(font);
+ }
+
+ private Map<Integer, String> getCodeToName(Encoding encoding) {
+ Map<Integer, String> codeToName = new HashMap<Integer, String>();
+ if (encoding != null) {
+ COSBase cos = encoding.getCOSObject();
+ if (cos instanceof COSDictionary) {
+ COSDictionary enc = (COSDictionary) cos;
+ COSName baseEncodingName = (COSName) enc.getDictionaryObject(COSName.BASE_ENCODING);
+ if (baseEncodingName != null) {
+ try {
+ Encoding baseEncoding = EncodingManager.INSTANCE.getEncoding(baseEncodingName);
+ codeToName.putAll(baseEncoding.getCodeToNameMap());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ COSArray differences = (COSArray)enc.getDictionaryObject(COSName.DIFFERENCES);
+ int currentIndex = -1;
+ for (int i = 0; differences != null && i < differences.size(); i++) {
+ COSBase next = differences.getObject(i);
+ if (next instanceof COSNumber) {
+ currentIndex = ((COSNumber)next).intValue();
+ } else if (next instanceof COSName) {
+ COSName name = (COSName)next;
+ codeToName.put(currentIndex++, name.getName());
+ }
+ }
+ } else {
+ return encoding.getCodeToNameMap();
+ }
+ }
+ return codeToName;
+ }
+
+ private Object getCmap(PDSimpleFont font) throws IOException {
+ if (font.getFontEncoding() != null) {
+ return font.getFontEncoding();
+ }
+ return font.getToUnicodeCMap();
+ }
+
+ private PDStream readFontFile(PDSimpleFont font) throws IOException {
+ PDFontDescriptorDictionary fd = (PDFontDescriptorDictionary) font.getFontDescriptor();
+ setFlags(fd.getFlags());
+ PDStream ff = fd.getFontFile3();
+ if (ff == null) {
+ ff = fd.getFontFile2();
+ if (ff == null) {
+ ff = fd.getFontFile();
+ }
+ } else {
+ setFontType(FontType.TYPE1C);
+ }
+ if (ff == null) {
+ throw new IOException(font.getBaseFont() + " no font file");
+ }
+ return ff;
+ }
+
+ private void loadFontFile(PDSimpleFont font) throws IOException {
+ PDStream ff = readFontFile(font);
+ mergeFontFile(ff.createInputStream(), font);
+ if (font instanceof PDTrueTypeFont) {
+ TrueTypeFont ttfont = ((PDTrueTypeFont) font).getTTFFont();
+ CMAPEncodingEntry[] cmapList = ttfont.getCMAP().getCmaps();
+ for (CMAPEncodingEntry c : cmapList) {
+ newCmap.platformId = c.getPlatformId();
+ newCmap.platformEncodingId = c.getPlatformEncodingId();
+ for (int i = 0; i < 256 * 256; i++) {
+ if (c.getGlyphId(i) != 0) {
+ newCmap.glyphIdToCharacterCode.put(i, c.getGlyphId(i));
+ }
+ }
+ }
+ mergeMaxp(ttfont, mergeTTFonts.maxp);
+ }
+ }
+
+ @Override
+ public boolean hasChar(char c) {
+ return charMapGlobal.containsKey(String.valueOf(c));
+ }
+
+ @Override
+ public char mapChar(char c) {
+ return mapping.mapChar(c);
+ }
+
+ public String getEmbedFontName() {
+ if (embedName == null) {
+ embedName = getName(font.getBaseFont());
+ }
+ return embedName;
+ }
+
+ public int[] getWidths() {
+ width = new int[getLastChar() - getFirstChar() + 1];
+ for (int i = getFirstChar(); i <= getLastChar(); i++) {
+ if (newWidth.containsKey(i)) {
+ width[i - getFirstChar()] = newWidth.get(i);
+ } else {
+ width[i - getFirstChar()] = 0;
+ }
+ }
+ return width.clone();
+ }
+
+ public String addFont(COSDictionary fontData) throws IOException {
+ PDSimpleFont font = getFont(fontData);
+ if (font instanceof PDType1Font && differentGlyphData((PDType1Font) font)) {
+ return null;
+ }
+ mergeWidths(font);
+ if (font.getFirstChar() < getFirstChar()) {
+ setFirstChar(font.getFirstChar());
+ }
+ for (int w : newWidth.keySet()) {
+ if (w > getLastChar()) {
+ setLastChar(w);
+ }
+ }
+ loadFontFile(font);
+ addEncoding(font);
+ return getFontName();
+ }
+
+ public int size() {
+ return fontCount;
+ }
+
+ private Map<String, byte[]> getCharStringsDict(PDType1Font font) throws IOException {
+ if (getFontType() == FontType.TYPE1) {
+ return font.getType1Font().getCharStringsDict();
+ }
+ return font.getType1CFont().getCFFFont().getCharStringsDict();
+ }
+
+ private boolean differentGlyphData(PDType1Font otherFont) throws IOException {
+ if (charStringsDict == null) {
+ charStringsDict = getCharStringsDict((PDType1Font) font);
+ }
+ for (Map.Entry<String, byte[]> s : getCharStringsDict(otherFont).entrySet()) {
+ if (charStringsDict.containsKey(s.getKey())) {
+ int numberDiff = 0;
+ byte[] b1 = charStringsDict.get(s.getKey());
+ byte[] b2 = s.getValue();
+ int b1Index = b1.length - 1;
+ int b2Index = b2.length - 1;
+ while (b1Index >= 0 && b2Index >= 0) {
+ if (b1[b1Index] != b2[b2Index]) {
+ numberDiff++;
+ if (numberDiff > 2) {
+ break;
+ }
+ }
+ b1Index--;
+ b2Index--;
+ }
+ if (numberDiff > 2) {
+// log.info(getFontName() + " " + s.getKey() + " not equal " + numberdiff);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void mergeWidths(PDSimpleFont font) throws IOException {
+ int w = 0;
+ int skipGlyphIndex = getLastChar() + 1;
+ Object cmap = getCmap(font);
+ Set<Integer> codeToName = getCodeToName(font.getFontEncoding()).keySet();
+ for (int i = font.getFirstChar(); i <= font.getLastChar(); i++) {
+ boolean addedWidth = false;
+ int glyphIndexPos = skipGlyphIndex;
+ if (font instanceof PDTrueTypeFont) {
+ glyphIndexPos = i;
+ }
+ int neww = 0;
+ if (font.getWidths() != null) {
+ neww = font.getWidths().get(i - font.getFirstChar()).intValue();
+ if (!newWidth.containsKey(i) || newWidth.get(i) == 0) {
+ if (getFontType() == FontType.TYPE1
+ || font instanceof PDTrueTypeFont
+ || codeToName.contains(i)) {
+ newWidth.put(i, neww);
+ glyphIndexPos = i;
+ } else {
+ newWidth.put(i, 0);
+ }
+ addedWidth = true;
+ }
+ }
+ String mappedChar = getChar(cmap, i);
+ if (mappedChar != null && !charMapGlobal.containsKey(mappedChar)) {
+ charMapGlobal.put(mappedChar, glyphIndexPos);
+ if (!addedWidth && w < font.getWidths().size()) {
+ newWidth.put(newWidth.size() + getFirstChar(), neww);
+ }
+ skipGlyphIndex++;
+ }
+ w++;
+ }
+ }
+
+ private String getChar(Object cmap, int i) throws IOException {
+ if (cmap instanceof CMap) {
+ CMap c = (CMap)cmap;
+ int size = 1;
+ if (c.hasTwoByteMappings()) {
+ size = 2;
+ }
+ return c.lookup(i, size);
+ }
+ Encoding enc = (Encoding)cmap;
+ if (enc instanceof DictionaryEncoding) {
+ return enc.getName(i);
+ }
+ return enc.getCharacter(i);
+ }
+
+ public String getEncodingName() {
+ if (font.getFontEncoding() != null) {
+ COSBase cosObject = font.getFontEncoding().getCOSObject();
+ if (cosObject != null) {
+ if (cosObject instanceof COSDictionary) {
+ COSBase item = ((COSDictionary) cosObject).getItem(COSName.BASE_ENCODING);
+ if (item != null) {
+ return ((COSName)item).getName();
+ }
+ } else if (cosObject instanceof COSName) {
+ return ((COSName) cosObject).getName();
+ } else {
+ throw new RuntimeException(cosObject.toString() + " not supported");
+ }
+ }
+ }
+ return null;
+ }
+
+ private void addEncoding(PDSimpleFont fontForEnc) {
+ List<String> added = new ArrayList<String>(encodingMap.values());
+ Map<Integer, String> codeToName = getCodeToName(fontForEnc.getFontEncoding());
+ for (int i = fontForEnc.getFirstChar(); i <= fontForEnc.getLastChar(); i++) {
+ if (codeToName.keySet().contains(i)) {
+ String s = codeToName.get(i);
+ if (!added.contains(s)) {
+ if (!encodingMap.containsKey(i)) {
+ encodingMap.put(i, s);
+ } else {
+ encodingMap.put(encodingSkip, s);
+ encodingSkip++;
+ }
+ }
+ }
+ }
+ }
+
+ class FOPPDFEncoding implements SingleByteEncoding {
+ private boolean cmap;
+
+ public String getName() {
+ return "FOPPDFEncoding";
+ }
+
+ public char mapChar(char c) {
+ if (charMapGlobal.containsKey(String.valueOf(c))) {
+ return (char)charMapGlobal.get(String.valueOf(c)).intValue();
+ }
+ return 0;
+ }
+
+ public String[] getCharNameMap() {
+ Collection<String> v = encodingMap.values();
+ return v.toArray(new String[v.size()]);
+ }
+
+ public char[] getUnicodeCharMap() {
+ if (cmap) {
+ if (font.getToUnicode() == null) {
+ return new char[0];
+ }
+ List<String> cmapStrings = new ArrayList<String>();
+ Map<Integer, String> cm = new HashMap<Integer, String>();
+ for (Map.Entry<String, Integer> o : charMapGlobal.entrySet()) {
+ cm.put(o.getValue(), o.getKey());
+ }
+ for (int i = 0; i < getLastChar() + 1; i++) {
+ if (cm.containsKey(i)) {
+ cmapStrings.add(cm.get(i));
+ } else {
+ cmapStrings.add(" ");
+ }
+ }
+ return fromStringToCharArray(cmapStrings);
+ }
+ cmap = true;
+ return toCharArray(encodingMap.keySet());
+ }
+
+ private char[] fromStringToCharArray(Collection<String> list) {
+ char[] ret = new char[list.size()];
+ int i = 0;
+ for (String e : list) {
+ if (e.length() > 0) {
+ ret[i++] = e.charAt(0);
+ }
+ }
+ return ret;
+ }
+
+ private char[] toCharArray(Collection<Integer> list) {
+ char[] ret = new char[list.size()];
+ int i = 0;
+ for (int e : list) {
+ ret[i++] = (char)e;
+ }
+ return ret;
+ }
+ }
+
+ public PDFDictionary getRef() {
+ return ref;
+ }
+
+ public void setRef(PDFDictionary d) {
+ ref = d;
+ }
+
+ public boolean isEmbeddable() {
+ return true;
+ }
+
+ public boolean isSymbolicFont() {
+ return false;
+ }
+
+ private void mergeFontFile(InputStream ff, PDSimpleFont pdSimpleFont) throws IOException {
+ if (getFontType() == FontType.TRUETYPE) {
+ Map<Integer, Integer> chars = new HashMap<Integer, Integer>();
+ chars.put(0, 0);
+ mergeTTFonts.readFont(new FontFileReader(ff), chars, false);
+ } else if (getFontType() == FontType.TYPE1) {
+ mergeType1Fonts.readFont(ff, (PDType1Font) pdSimpleFont);
+ } else {
+ mergeCFFFonts.readType1CFont(ff, getEmbedFontName());
+ }
+ fontCount++;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ if (getFontType() == FontType.TYPE1C) {
+ mergeCFFFonts.writeFont();
+ return new ByteArrayInputStream(mergeCFFFonts.getFontSubset());
+ }
+ if (getFontType() == FontType.TRUETYPE) {
+ mergeTTFonts.writeFont(newCmap);
+ return new ByteArrayInputStream(mergeTTFonts.getFontSubset());
+ }
+ if (getFontType() == FontType.TYPE1) {
+ return new ByteArrayInputStream(mergeType1Fonts.writeFont());
+ }
+ return null;
+ }
+ }
+
+ private void setProperties(CustomFont cFont, PDSimpleFont font) {
+ if (font.getFontDescriptor() != null) {
+ cFont.setCapHeight((int) font.getFontDescriptor().getCapHeight());
+ cFont.setAscender((int)font.getFontDescriptor().getAscent());
+ cFont.setDescender((int)font.getFontDescriptor().getDescent());
+ cFont.setXHeight((int)font.getFontDescriptor().getXHeight());
+ cFont.setStemV((int)font.getFontDescriptor().getStemV());
+ }
+ }
+
+ private void mergeMaxp(TrueTypeFont ttf, MaximumProfileTable outMaxp) {
+ MaximumProfileTable mp = ttf.getMaximumProfile();
+ outMaxp.setVersion(mp.getVersion());
+ outMaxp.setNumGlyphs(outMaxp.getNumGlyphs() + mp.getNumGlyphs());
+ outMaxp.setMaxPoints(outMaxp.getMaxPoints() + mp.getMaxPoints());
+ outMaxp.setMaxContours(outMaxp.getMaxContours() + mp.getMaxContours());
+ outMaxp.setMaxCompositePoints(outMaxp.getMaxCompositePoints() + mp.getMaxCompositePoints());
+ outMaxp.setMaxCompositeContours(outMaxp.getMaxCompositeContours() + mp.getMaxCompositeContours());
+ outMaxp.setMaxZones(outMaxp.getMaxZones() + mp.getMaxZones());
+ outMaxp.setMaxTwilightPoints(outMaxp.getMaxTwilightPoints() + mp.getMaxTwilightPoints());
+ outMaxp.setMaxStorage(outMaxp.getMaxStorage() + mp.getMaxStorage());
+ outMaxp.setMaxFunctionDefs(outMaxp.getMaxFunctionDefs() + mp.getMaxFunctionDefs());
+ outMaxp.setMaxInstructionDefs(outMaxp.getMaxInstructionDefs() + mp.getMaxInstructionDefs());
+ outMaxp.setMaxStackElements(outMaxp.getMaxStackElements() + mp.getMaxStackElements());
+ outMaxp.setMaxSizeOfInstructions(outMaxp.getMaxSizeOfInstructions() + mp.getMaxSizeOfInstructions());
+ outMaxp.setMaxComponentElements(outMaxp.getMaxComponentElements() + mp.getMaxComponentElements());
+ outMaxp.setMaxComponentDepth(outMaxp.getMaxComponentDepth() + mp.getMaxComponentDepth());
+ }
+
public class PDFWriter {
protected StringBuilder s = new StringBuilder();
private String key;
@@ -317,7 +1165,7 @@ public class PDFBoxAdapter {
s.append(op.getOperation() + "\n");
arguments.clear();
if (op.getImageParameters() != null) {
- for (Map.Entry<COSName, COSBase> cn : op.getImageParameters().getDictionary().entrySet()) {
+ for (Map.Entry<COSName, COSBase> cn : op.getImageParameters().entrySet()) {
arguments.add(cn.getKey());
arguments.add(cn.getValue());
}
@@ -378,29 +1226,207 @@ public class PDFBoxAdapter {
}
}
}
+
+ public class MergeFontsPDFWriter extends PDFWriter {
+ private COSDictionary fonts;
+ private FontInfo fontInfo;
+ private Typeface font;
+ private PDSimpleFont oldFont = null;
+ private Map<COSName, String> fontsToRemove = new HashMap<COSName, String>();
+
+ public MergeFontsPDFWriter(COSDictionary fonts, FontInfo fontInfo, String key, List<COSName> resourceNames) {
+ super(key, resourceNames);
+ this.fonts = fonts;
+ this.fontInfo = fontInfo;
+ }
+
+ public String writeText(PDStream pdStream) throws IOException {
+ String txt = super.writeText(pdStream);
+ if (fontsToRemove.isEmpty()) {
+ return null;
+ }
+ for (COSName cn : fontsToRemove.keySet()) {
+ fonts.removeItem(cn);
+ }
+ return txt;
+ }
+
+ protected void readPDFArguments(PDFOperator op, Collection<COSBase> arguments) throws IOException {
+ for (COSBase c : arguments) {
+ if (c instanceof COSName) {
+ COSName cn = (COSName)c;
+ COSDictionary fontData = (COSDictionary)fonts.getDictionaryObject(cn.getName());
+ String internalName = fontsToRemove.get(cn);
+ if (internalName == null && fontData != null) {
+ internalName = getNewFont(fontData, fontInfo, fontsToRemove.values());
+ }
+ if (fontData == null || internalName == null) {
+ s.append("/" + cn.getName());
+ addKey(cn);
+ if (op.getOperation().equals("Tf")) {
+ font = null;
+ oldFont = null;
+ }
+ } else {
+ s.append("/" + internalName);
+ fontsToRemove.put(cn, internalName);
+ font = fontInfo.getUsedFonts().get(internalName);
+ oldFont = getFont(fontData);
+ }
+ s.append(" ");
+ } else if (c instanceof COSString && font != null && ((FOPPDFFont)font).size() != 1) {
+ List<String> word = readCOSString((COSString)c, oldFont);
+ if (word == null) {
+ s.append(PDFText.escapeString(((COSString) c).getString()));
+ } else {
+ String x = getMappedWord(word, font, ((COSString) c).getBytes());
+ if (x == null) {
+ s.append(PDFText.escapeString(((COSString) c).getString()));
+ } else {
+ s.append(x);
+ }
+ }
+ } else {
+ processArg(op, c);
+ }
+ }
+ }
+
+ private String getMappedWord(List<String> word, Typeface font, byte[] bytes) throws IOException {
+ StringBuffer newOct = new StringBuffer();
+ StringBuilder newHex = new StringBuilder();
+ int i = 0;
+ for (String str : word) {
+ Integer mapped = getMapping(bytes[i]);
+ if (mapped == null) {
+ char c = str.charAt(0);
+ if (str.length() > 1) {
+ c = (char) str.hashCode();
+ }
+ if (font.hasChar(c)) {
+ mapped = (int)font.mapChar(c);
+ } else {
+ return null;
+ }
+ }
+ newHex.append(String.format("%1$04x", mapped & 0xFFFF).toUpperCase(Locale.getDefault()));
+ PDFText.escapeStringChar((char)mapped.intValue(), newOct);
+ i++;
+ }
+ if (font instanceof SingleByteFont) {
+ return "(" + newOct.toString() + ")";
+ }
+ return "<" + newHex.toString() + ">";
+ }
+
+ private Integer getMapping(byte i) throws IOException {
+ if (oldFont.getFontEncoding() != null && font instanceof FOPPDFSingleByteFont) {
+ String name = oldFont.getFontEncoding().getName(i);
+ if (name != null && ((FOPPDFSingleByteFont)font).charMapGlobal.containsKey(name)) {
+ return ((FOPPDFSingleByteFont)font).charMapGlobal.get(name);
+ }
+ }
+ return null;
+ }
+
+ private List<String> readCOSString(COSString s, PDSimpleFont oldFont) throws IOException {
+ List<String> word = new ArrayList<String>();
+ byte[] string = s.getBytes();
+ int codeLength;
+// String t1Str = new String(string, "UTF-8");
+ for (int i = 0; i < string.length; i += codeLength) {
+ codeLength = 1;
+ String c = oldFont.encode(string, i, codeLength);
+// if (oldFont instanceof PDType1Font && i < t1Str.length()) {
+// c = ((PDType1Font)oldFont).encodetype1(string, i, codeLength);
+// }
+ if (c == null && i + 1 < string.length) {
+ codeLength++;
+ c = oldFont.encode(string, i, codeLength);
+ }
+ if (c == null) {
+ return null;
+ }
+ word.add(c);
+ }
+ return word;
+ }
+ }
+
+ private PDSimpleFont getFont(COSDictionary fontData) throws IOException {
+ if (!fontMap.containsKey(fontData)) {
+ if (fontMap.size() > 10) {
+ fontMap.clear();
+ }
+ fontMap.put(fontData, (PDSimpleFont)PDFontFactory.createFont(fontData));
+ }
+ return fontMap.get(fontData);
+ }
+
/**
* Creates a stream (from FOP's PDF library) from a PDF page parsed with PDFBox.
* @param sourceDoc the source PDF the given page to be copied belongs to
* @param page the page to transform into a stream
* @param key value to use as key for the stream
+ * @param eventBroadcaster events
* @param atdoc adjustment for stream
+ * @param fontinfo fonts
+ * @param pos rectangle
* @return the stream
* @throws IOException if an I/O error occurs
*/
public String createStreamFromPDFBoxPage(PDDocument sourceDoc, PDPage page, String key,
- EventBroadcaster eventBroadcaster, AffineTransform atdoc, Rectangle pos) throws IOException {
+ EventBroadcaster eventBroadcaster, AffineTransform atdoc, FontInfo fontinfo, Rectangle pos)
+ throws IOException {
handleAcroForm(sourceDoc, page, eventBroadcaster, atdoc);
-
PDResources sourcePageResources = page.findResources();
PDFDictionary pageResources = null;
PDStream pdStream = page.getContents();
COSDictionary fonts = (COSDictionary)sourcePageResources.getCOSDictionary().getDictionaryObject(COSName.FONT);
+ COSDictionary fontsBackup = null;
String uniqueName = Integer.toString(key.hashCode());
- String newStream = new PDFWriter(uniqueName, getResourceNames(sourcePageResources.getCOSDictionary())).writeText(pdStream);
+ String newStream = null;
+ if (fonts != null && pdfDoc.isMergeFontsEnabled()) {
+ fontsBackup = new COSDictionary(fonts);
+ MergeFontsPDFWriter m = new MergeFontsPDFWriter(fonts, fontinfo, uniqueName,
+ getResourceNames(sourcePageResources.getCOSDictionary()));
+ newStream = m.writeText(pdStream);
+ parentFonts = m.fontsToRemove.values();
+// if (newStream != null) {
+// for (Object f : fonts.keySet().toArray()) {
+// COSDictionary fontdata = (COSDictionary)fonts.getDictionaryObject((COSName)f);
+// if (getUniqueFontName(fontdata) != null) {
+// fonts.removeItem((COSName)f);
+// }
+// }
+// }
+ }
+ if (newStream == null) {
+ newStream = new PDFWriter(uniqueName,
+ getResourceNames(sourcePageResources.getCOSDictionary())).writeText(pdStream);
+ }
pdStream = new PDStream(sourceDoc, new ByteArrayInputStream(newStream.getBytes("ISO-8859-1")));
- pageResources = (PDFDictionary)cloneForNewDocument(
- sourcePageResources.getCOSDictionary());
- pdfDoc.registerObject(pageResources);
+ mergeXObj(sourcePageResources.getCOSDictionary(), fontinfo, uniqueName);
+ pageResources = (PDFDictionary)cloneForNewDocument(sourcePageResources.getCOSDictionary());
+
+ PDFDictionary fontDict = (PDFDictionary)pageResources.get("Font");
+ if (fontDict != null && pdfDoc.isMergeFontsEnabled()) {
+ for (Map.Entry<String, Typeface> fontEntry : fontinfo.getUsedFonts().entrySet()) {
+ Typeface font = fontEntry.getValue();
+ if (font instanceof FOPPDFFont) {
+ FOPPDFFont pdfFont = (FOPPDFFont)font;
+ if (pdfFont.getRef() == null) {
+ pdfFont.setRef(new PDFDictionary());
+ pdfDoc.assignObjectNumber(pdfFont.getRef());
+ }
+ fontDict.put(fontEntry.getKey(), pdfFont.getRef());
+ }
+ }
+ }
+ updateXObj(sourcePageResources.getCOSDictionary(), pageResources);
+ if (fontsBackup != null) {
+ sourcePageResources.getCOSDictionary().setItem(COSName.FONT, fontsBackup);
+ }
COSStream originalPageContents = (COSStream)pdStream.getCOSObject();
@@ -414,7 +1440,7 @@ public class PDFBoxAdapter {
InputStream in = array.getUnfilteredStream();
OutputStream out = pageStream.getBufferOutputStream();
IOUtils.copyLarge(in, out);
- filter = filterFilter;
+ filter = FILTER_FILTER;
} else {
pageStream = (PDFStream)cloneForNewDocument(originalPageContents);
filter = Collections.EMPTY_SET;
@@ -430,7 +1456,7 @@ public class PDFBoxAdapter {
PDRectangle mediaBox = page.findMediaBox();
PDRectangle cropBox = page.findCropBox();
- PDRectangle viewBox = (cropBox != null ? cropBox : mediaBox);
+ PDRectangle viewBox = cropBox != null ? cropBox : mediaBox;
//Handle the /Rotation entry on the page dict
int rotation = PDFUtil.getNormalizedRotation(page);
@@ -476,6 +1502,59 @@ public class PDFBoxAdapter {
return boxStr.toString() + pdStream.getInputStreamAsString();
}
+ private void mergeXObj(COSDictionary sourcePageResources, FontInfo fontinfo, String uniqueName) throws IOException {
+ COSDictionary xobj = (COSDictionary) sourcePageResources.getDictionaryObject(COSName.XOBJECT);
+ if (xobj != null && pdfDoc.isMergeFontsEnabled()) {
+ for (Map.Entry<COSName, COSBase> i : xobj.entrySet()) {
+ COSObject v = (COSObject) i.getValue();
+ COSStream stream = (COSStream) v.getObject();
+ COSDictionary res = (COSDictionary) stream.getDictionaryObject(COSName.RESOURCES);
+ if (res != null) {
+ COSDictionary src = (COSDictionary) res.getDictionaryObject(COSName.FONT);
+ if (src != null) {
+ COSDictionary target = (COSDictionary) sourcePageResources.getDictionaryObject(COSName.FONT);
+ if (target == null) {
+ sourcePageResources.setItem(COSName.FONT, src);
+ } else {
+ for (Map.Entry<COSName, COSBase> entry : src.entrySet()) {
+ if (!target.keySet().contains(entry.getKey())) {
+ target.setItem(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ PDFWriter writer = new MergeFontsPDFWriter(src, fontinfo, uniqueName,
+ getResourceNames(sourcePageResources));
+ String c = writer.writeText(PDStream.createFromCOS(stream));
+ if (c != null) {
+ stream.removeItem(COSName.FILTER);
+ newXObj.put(i.getKey(), c);
+ for (Object e : src.keySet().toArray()) {
+ COSName name = (COSName) e;
+ src.setItem(name.getName() + uniqueName, src.getItem(name));
+ src.removeItem(name);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void updateXObj(COSDictionary sourcePageResources, PDFDictionary pageResources) throws IOException {
+ COSDictionary xobj = (COSDictionary) sourcePageResources.getDictionaryObject(COSName.XOBJECT);
+ if (xobj != null && pdfDoc.isMergeFontsEnabled()) {
+ PDFDictionary target = (PDFDictionary) pageResources.get("XObject");
+ for (COSName entry : xobj.keySet()) {
+ if (newXObj.containsKey(entry)) {
+ PDFStream s = (PDFStream) target.get(entry.getName());
+ s.setData(newXObj.get(entry).getBytes("UTF-8"));
+ PDFDictionary xobjr = (PDFDictionary) s.get("Resources");
+ xobjr.put("Font", pageResources.get("Font"));
+ }
+ }
+ }
+ }
+
private List<COSName> getResourceNames(COSDictionary sourcePageResources) {
List<COSName> resourceNames = new ArrayList<COSName>();
for (COSBase e : sourcePageResources.getValues()) {
@@ -490,7 +1569,8 @@ public class PDFBoxAdapter {
return resourceNames;
}
- private void transferPageDict(COSDictionary fonts, String uniqueName, PDResources sourcePageResources) throws IOException {
+ private void transferPageDict(COSDictionary fonts, String uniqueName, PDResources sourcePageResources)
+ throws IOException {
if (fonts != null) {
for (Map.Entry<COSName, COSBase> f : fonts.entrySet()) {
String name = f.getKey().getName() + uniqueName;
@@ -522,17 +1602,31 @@ public class PDFBoxAdapter {
targetPage.getPDFResources().put(name, newDict);
}
}
- private List getWidgets(PDPage page) throws IOException {
- List annots = page.getAnnotations();
- List widgets = new java.util.ArrayList();
- Iterator iter = annots.iterator();
- while (iter.hasNext()) {
- PDAnnotation annot = (PDAnnotation)iter.next();
- if (annot.getSubtype().equals("Widget")) {
- widgets.add(annot);
+
+ private String getNewFont(COSDictionary fontData, FontInfo fontinfo, Collection<String> usedFonts)
+ throws IOException {
+ String base = getUniqueFontName(fontData);
+ if (base == null || usedFonts.contains(base) || (parentFonts != null && parentFonts.contains(base))) {
+ return null;
+ }
+ try {
+ for (Typeface t : fontinfo.getUsedFonts().values()) {
+ if (t instanceof FOPPDFFont && base.equals(t.getFontName())) {
+ return ((FOPPDFFont)t).addFont(fontData);
+ }
+ }
+ if (base.endsWith("cid") || fontData.getItem(COSName.SUBTYPE) != COSName.TYPE1
+ && fontData.getItem(COSName.SUBTYPE) != COSName.TRUE_TYPE) {
+ fontinfo.addMetrics(base, new FOPPDFMultiByteFont(fontData, base));
+ } else {
+ fontinfo.addMetrics(base, new FOPPDFSingleByteFont(fontData, base));
}
+ } catch (IOException e) {
+ log.warn(e.getMessage());
+ return null;
}
- return widgets;
+ fontinfo.useFont(base);
+ return base;
}
private void bindOptionalContent(PDDocument sourceDoc) throws IOException {
@@ -555,7 +1649,7 @@ public class PDFBoxAdapter {
PDRectangle mediaBox = page.findMediaBox();
PDRectangle cropBox = page.findCropBox();
- PDRectangle viewBox = (cropBox != null ? cropBox : mediaBox);
+ PDRectangle viewBox = cropBox != null ? cropBox : mediaBox;
for (Object obj : pageAnnotations) {
PDAnnotation annot = (PDAnnotation)obj;
@@ -588,8 +1682,8 @@ public class PDFBoxAdapter {
}
cacheClonedObject(cosPage, this.targetPage);
- COSArray annots = (COSArray)page.getCOSDictionary().getDictionaryObject(COSName.ANNOTS);
- Set fields = Collections.emptySet();
+ COSArray annots = (COSArray) page.getCOSDictionary().getDictionaryObject(COSName.ANNOTS);
+ Set<COSObject> fields = Collections.emptySet();
if (annots != null) {
fields = new HashSet();
Iterator iter = annots.iterator();
@@ -615,7 +1709,7 @@ public class PDFBoxAdapter {
}
}
- boolean formAlreadyCopied = (getCachedClone(srcAcroForm) != null);
+ boolean formAlreadyCopied = getCachedClone(srcAcroForm) != null;
PDFRoot catalog = this.pdfDoc.getRoot();
PDFDictionary destAcroForm = (PDFDictionary)catalog.get(COSName.ACRO_FORM.getName());
if (formAlreadyCopied) {
@@ -635,18 +1729,16 @@ public class PDFBoxAdapter {
destAcroForm = new PDFDictionary(pdfDoc.getRoot());
}
pdfDoc.registerObject(destAcroForm);
- catalog.put(COSName.ACRO_FORM.getName(), destAcroForm );
+ catalog.put(COSName.ACRO_FORM.getName(), destAcroForm);
}
PDFArray clonedFields = (PDFArray) destAcroForm.get(COSName.FIELDS.getName());
if (clonedFields == null) {
clonedFields = new PDFArray();
destAcroForm.put(COSName.FIELDS.getName(), clonedFields);
}
- for (Iterator iter = fields.iterator(); iter.hasNext();) {
- COSObject field = (COSObject) iter.next();
+ for (COSObject field : fields) {
PDFDictionary clone = (PDFDictionary) cloneForNewDocument(field, field, Arrays.asList(COSName.KIDS));
clonedFields.add(clone);
}
}
-
}
Modified: xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxEventProducer.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxEventProducer.java?rev=1610937&r1=1610936&r2=1610937&view=diff
==============================================================================
--- xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxEventProducer.java (original)
+++ xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxEventProducer.java Wed Jul 16 08:51:22 2014
@@ -1,3 +1,19 @@
+/*
+ * 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.fop.render.pdf.pdfbox;
import org.apache.fop.events.EventBroadcaster;
@@ -44,7 +60,7 @@ interface PDFBoxEventProducer extends Ev
/**
* PDF/A mode is active.
*
- * @param source
+ * @param source the event source
* @event.severity WARN
*/
void pdfAActive(Object source);
@@ -52,7 +68,7 @@ interface PDFBoxEventProducer extends Ev
/**
* PDF/X mode is active.
*
- * @param source
+ * @param source the event source
* @event.severity WARN
*/
void pdfXActive(Object source);
---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-help@xmlgraphics.apache.org