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