Author: ssteiner
Date: Thu Jun  5 11:41:26 2014
New Revision: 1600613

URL: http://svn.apache.org/r1600613
Log:
Merge fonts in pdfs

Added:
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/build/qdox-1.12.jar   
(with props)
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/fontbox-2.0.0-SNAPSHOT.jar
   (with props)
    xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/fop.jar   (with 
props)
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/jempbox-2.0.0-SNAPSHOT.jar
   (with props)
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/pdfbox-2.0.0-SNAPSHOT.jar
   (with props)
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeCFFFonts.java
   (with props)
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
   (with props)
Removed:
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/build/qdox-1.6.3.jar
    xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/fontbox-1.8.5.jar
    xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/fop-svn-trunk.jar
    xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/jempbox-1.8.5.jar
    xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/pdfbox-1.8.5.jar
Modified:
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxImageHandler.java
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/PreloaderPDF.java
    
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/test/java/org/apache/fop/render/pdf/PDFRotateTestCase.java

Added: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/build/qdox-1.12.jar
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/build/qdox-1.12.jar?rev=1600613&view=auto
==============================================================================
Binary file - no diff available.

Propchange: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/build/qdox-1.12.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/fontbox-2.0.0-SNAPSHOT.jar
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/fontbox-2.0.0-SNAPSHOT.jar?rev=1600613&view=auto
==============================================================================
Binary file - no diff available.

Propchange: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/fontbox-2.0.0-SNAPSHOT.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/fop.jar
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/fop.jar?rev=1600613&view=auto
==============================================================================
Binary file - no diff available.

Propchange: xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/fop.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/jempbox-2.0.0-SNAPSHOT.jar
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/jempbox-2.0.0-SNAPSHOT.jar?rev=1600613&view=auto
==============================================================================
Binary file - no diff available.

Propchange: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/jempbox-2.0.0-SNAPSHOT.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/pdfbox-2.0.0-SNAPSHOT.jar
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/pdfbox-2.0.0-SNAPSHOT.jar?rev=1600613&view=auto
==============================================================================
Binary file - no diff available.

Propchange: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/lib/pdfbox-2.0.0-SNAPSHOT.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Modified: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java?rev=1600613&r1=1600612&r2=1600613&view=diff
==============================================================================
--- 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java
 (original)
+++ 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java
 Thu Jun  5 11:41:26 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;
@@ -64,8 +65,8 @@ public abstract class AbstractPDFBoxHand
     private static Map<Object, Cache<String, Map<Object, Object>>> 
objectCacheMap
             = 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 +113,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/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java?rev=1600613&r1=1600612&r2=1600613&view=diff
==============================================================================
--- 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java
 (original)
+++ 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java
 Thu Jun  5 11:41:26 2014
@@ -26,11 +26,12 @@ 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;
 import org.apache.xmlgraphics.image.loader.ImageFlavor;
@@ -129,8 +130,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);

Added: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeCFFFonts.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeCFFFonts.java?rev=1600613&view=auto
==============================================================================
--- 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeCFFFonts.java
 (added)
+++ 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeCFFFonts.java
 Thu Jun  5 11:41:26 2014
@@ -0,0 +1,472 @@
+/*
+ * 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.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.fontbox.cff.CFFFont;
+import org.apache.fontbox.cff.CFFFontROS;
+import org.apache.fontbox.cff.CFFParser;
+import org.apache.fontbox.cff.charset.CFFCharset;
+import org.apache.fontbox.cff.charset.CFFISOAdobeCharset;
+import org.apache.fontbox.cff.encoding.CFFEncoding;
+import org.apache.fontbox.cff.encoding.CFFStandardEncoding;
+
+import org.apache.fop.fonts.cff.CFFDataReader;
+import org.apache.fop.fonts.truetype.FontFileReader;
+import org.apache.fop.fonts.truetype.OTFSubSetFile;
+
+public class MergeCFFFonts extends OTFSubSetFile {
+    protected List<LinkedHashMap<Integer, Integer>> subsetGlyphsList = new 
ArrayList<LinkedHashMap<Integer, Integer>>();
+    private boolean fallbackIndex;
+    private int charsetOffset;
+    private int fontFileSize = 0;
+    private Set<String> used = new HashSet<String>();
+    private List<String> strings = new ArrayList<String>();
+    private List<Integer> chars = new ArrayList<Integer>();
+    private List<String> added = new ArrayList<String>();
+    private Map<Integer, Integer> range = new LinkedHashMap<Integer, 
Integer>();
+    private Set<String> stringsForRange = new HashSet<String>();
+    private int noOfFonts = 0;
+    private CFFEncoding encoding = null;
+
+    public MergeCFFFonts() throws IOException {
+        gidToSID = new LinkedHashMap<Integer, Integer>();
+        subsetCharStringsIndex = new ArrayList<byte[]>();
+    }
+
+    public void readType1CFont(InputStream stream, String embeddedName) throws 
IOException {
+        this.embeddedName = embeddedName;
+        FontFileReader fontFile = new FontFileReader(stream);
+        CFFParser p = new CFFParser();
+        CFFFont ff = p.parse(fontFile.getAllBytes()).get(0);
+        if (used.containsAll(ff.getCharStringsDict().keySet())) {
+            return;
+        }
+        fontFileSize += fontFile.getFileSize();
+        this.fontFile = fontFile;
+        used.addAll(ff.getCharStringsDict().keySet());
+        if (fileFont == null) {
+            fileFont = ff;
+        }
+        LinkedHashMap<Integer, Integer> sg = new LinkedHashMap<Integer, 
Integer>();
+        for (int i = 0; i < ff.getCharset().getEntries().size() + 1; i++) {
+            sg.put(i, i);
+        }
+        subsetGlyphsList.add(sg);
+        cffReader = new CFFDataReader(fontFile);
+        for (CFFCharset.Entry e : ff.getCharset().getEntries()) {
+            int sid = e.getSID();
+            if (sid >= NUM_STANDARD_STRINGS) {
+                int index = sid - NUM_STANDARD_STRINGS;
+                if (index <= cffReader.getStringIndex().getNumObjects()) {
+                    String data = new 
String(cffReader.getStringIndex().getValue(index), "US-ASCII");
+                    if (!strings.contains(data)) {
+                        strings.add(data);
+                    }
+                }
+            }
+        }
+
+        encoding = ff.getEncoding();
+        if (!(encoding instanceof CFFStandardEncoding)) {
+            for (CFFEncoding.Entry e : encoding.getEntries()) {
+                int c = e.getCode();
+                if (!chars.contains(c)) {
+                    chars.add(c);
+                }
+            }
+        }
+
+        int subsetGlyphIndex = 0;
+        for (CFFCharset.Entry e : ff.getCharset().getEntries()) {
+            int sid = e.getSID();
+            int gid = sg.get(subsetGlyphIndex);
+
+            //Check whether the SID falls into the standard string set
+            if (sid < NUM_STANDARD_STRINGS) {
+                gidToSID.put(sg.get(gid), sid);
+            } else {
+                int index = sid - NUM_STANDARD_STRINGS;
+                if (index <= cffReader.getStringIndex().getNumObjects()) {
+                    gidToSID.put(sg.get(gid), stringIndexData.size() + 
NUM_STANDARD_STRINGS - 1);
+                } else {
+                    gidToSID.put(sg.get(gid), index);
+                }
+            }
+            subsetGlyphIndex++;
+        }
+
+        for (Map.Entry<String, byte[]> s : ff.getCharStringsDict().entrySet()) 
{
+            if (!added.contains(s.getKey())) {
+                subsetCharStringsIndex.add(s.getValue());
+                added.add(s.getKey());
+            }
+        }
+
+        CFFCharset cSet = ff.getCharset();
+        String cClass = cSet.getClass().getName();
+        if (cClass.equals("org.apache.fontbox.cff.CFFParser$Format1Charset")
+                || 
cClass.equals("org.apache.fontbox.cff.CFFParser$Format0Charset")) {
+            for (CFFCharset.Entry m : cSet.getEntries()) {
+                if (!stringsForRange.contains(m.getName())) {
+                    range.put(m.getSID(), 0);
+                    stringsForRange.add(m.getName());
+                }
+            }
+        }
+        noOfFonts++;
+    }
+
+    public void writeFont() throws IOException {
+        output = new byte[fontFileSize * 2];
+        if (noOfFonts == 1) {
+            writeBytes(fontFile.getAllBytes());
+            return;
+        }
+        subsetGlyphs = subsetGlyphsList.get(0);
+        createCFF();
+    }
+
+    @Override
+    protected void createCFF() throws IOException {
+        //Header
+        writeBytes(cffReader.getHeader());
+
+        //Name Index
+        writeIndex(Arrays.asList(fileFont.getName().getBytes("UTF-8")));
+
+        //Keep offset of the topDICT so it can be updated once all data has 
been written
+        int topDictOffset = currentPos;
+        //Top DICT Index and Data
+        byte[] topDictIndex = cffReader.getTopDictIndex().getByteData();
+        int offSize = topDictIndex[2];
+        writeBytes(topDictIndex, 0, 3 + (offSize * 2));
+        int topDictDataOffset = currentPos;
+        writeTopDICT();
+        createCharStringData();
+
+        //String index
+        writeStringIndex();
+
+        Map<String, CFFDataReader.DICTEntry> topDICT = 
cffReader.getTopDictEntries();
+        final CFFDataReader.DICTEntry charString = topDICT.get("CharStrings");
+        final CFFDataReader.DICTEntry encodingEntry = topDICT.get("Encoding");
+
+        int encodingOffset;
+        if (encodingEntry != null && charString.getOffset() > 
encodingEntry.getOffset()) {
+            charsetOffset = currentPos;
+            if (!fallbackIndex) {
+                charsetOffset += 2;
+            }
+            writeCharsetTable(cffReader.getFDSelect() != null, !fallbackIndex);
+            encodingOffset = currentPos;
+            writeEncoding();
+        } else {
+            writeCard16(0);
+            encodingOffset = currentPos;
+            writeEncoding();
+            charsetOffset = currentPos;
+            writeCharsetTable(cffReader.getFDSelect() != null, false);
+        }
+
+        int fdSelectOffset = currentPos;
+        if (cffReader.getFDSelect() != null) {
+            writeByte(0);
+            for (int i = 0; i < subsetCharStringsIndex.size(); i++) {
+                writeByte(0);
+            }
+        }
+
+        //Keep offset to modify later with the local subroutine index offset
+        int privateDictOffset = currentPos;
+        writePrivateDict();
+
+        //Char Strings Index
+        int charStringOffset = currentPos;
+        writeIndex(subsetCharStringsIndex);
+
+        //Local subroutine index
+        int localIndexOffset = currentPos;
+        if (!subsetLocalIndexSubr.isEmpty()) {
+            writeIndex(subsetLocalIndexSubr);
+        }
+
+        if (cffReader.getFDSelect() != null) {
+            int fdArrayOffset = currentPos;
+            writeCard16(1);
+            writeByte(1); //Offset size
+            writeByte(1); //First offset
+            int count = 1;
+            for (CFFDataReader.FontDict fdFont : cffReader.getFDFonts()) {
+                count += fdFont.getByteData().length;
+                writeByte(count);
+            }
+            int fdByteData = currentPos;
+            for (CFFDataReader.FontDict fdFont : cffReader.getFDFonts()) {
+                writeBytes(fdFont.getByteData());
+            }
+            List<Integer> privateDictOffsets = new ArrayList<Integer>();
+            for (CFFDataReader.FontDict curFDFont : cffReader.getFDFonts()) {
+                privateDictOffsets.add(currentPos);
+                writeBytes(curFDFont.getPrivateDictData());
+                writeIndex(new ArrayList<byte[]>());
+            }
+            currentPos = fdByteData;
+            int i = 0;
+            for (CFFDataReader.FontDict fdFont : cffReader.getFDFonts()) {
+                byte[] fdFontByteData = fdFont.getByteData();
+                Map<String, CFFDataReader.DICTEntry> fdFontDict = 
cffReader.parseDictData(fdFontByteData);
+                //Update the Private dict reference
+                CFFDataReader.DICTEntry fdPrivate = fdFontDict.get("Private");
+                fdFontByteData = updateOffset(fdFontByteData,
+                        fdPrivate.getOffset() + 
fdPrivate.getOperandLengths().get(0),
+                        fdPrivate.getOperandLengths().get(1),
+                        privateDictOffsets.get(i));
+                writeBytes(fdFontByteData);
+                i++;
+            }
+
+            updateCIDOffsets(topDictDataOffset, fdArrayOffset, fdSelectOffset, 
charsetOffset, charStringOffset, encodingOffset);
+        } else {
+            //Update the offsets
+            updateOffsets(topDictOffset, charsetOffset, charStringOffset, 
privateDictOffset, localIndexOffset, encodingOffset);
+        }
+    }
+
+    protected void writeEncoding() throws IOException {
+        if (encoding instanceof CFFStandardEncoding) {
+            LinkedHashMap<String, CFFDataReader.DICTEntry> topDICT = 
cffReader.getTopDictEntries();
+            final CFFDataReader.DICTEntry encodingEntry = 
topDICT.get("Encoding");
+            if (encodingEntry != null && 
encodingEntry.getOperands().get(0).intValue() != 0
+                    && encodingEntry.getOperands().get(0).intValue() != 1) {
+                int len = encoding.getEntries().size();
+                if (len != gidToSID.size() - 1) {
+                    return;
+                }
+                writeByte(0);
+                writeByte(len);
+                for (Map.Entry<Integer, Integer> gid : gidToSID.entrySet()) {
+                    if (gid.getKey() == 0) {
+                        continue;
+                    }
+                    int code = encoding.getCode(gid.getValue());
+                    writeByte(code);
+                }
+            }
+        }
+        if (!chars.isEmpty()) {
+            writeCard16(chars.size());
+            for (int i : chars) {
+                writeByte(i);
+            }
+        }
+    }
+
+    protected void writeStringIndex() throws IOException {
+        for (String s : strings) {
+            stringIndexData.add(s.getBytes("US-ASCII"));
+        }
+
+        //Write the String Index
+        if (!stringIndexData.isEmpty()) {
+            if (!strings.isEmpty() && !new String(stringIndexData.get(0), 
"UTF-8").equals(strings.get(0))) {
+                //Move copyright string to end
+                stringIndexData.add(stringIndexData.remove(0));
+            } else {
+                String notice = (String)fileFont.getProperty("Notice");
+                if (notice != null && !(fileFont instanceof CFFFontROS)) {
+                    stringIndexData.add(notice.getBytes("ISO-8859-1"));
+                }
+            }
+            stringIndexData.add(embeddedName.getBytes("UTF-8"));
+            writeIndex(stringIndexData);
+        } else {
+            String notice = (String)fileFont.getProperty("Notice");
+            if (notice != null) {
+                
writeIndex(Arrays.<byte[]>asList(notice.getBytes("ISO-8859-1"), 
embeddedName.getBytes("UTF-8")));
+            } else {
+                List<byte[]> sindex = new ArrayList<byte[]>();
+                sindex.add(cffReader.getStringIndex().getData());
+                if (sindex.size() > 1) {
+                    fallbackIndex = true;
+                    writeIndex(sindex);
+                } else if (sindex.size() == 1) {
+                    writeIndex(Arrays.asList(embeddedName.getBytes("UTF-8")));
+                } else {
+                    writeCard16(0);
+                }
+            }
+        }
+    }
+
+    protected void createCharStringData() throws IOException {
+        //Create the new char string index
+        for (int i = 0; i < subsetGlyphsList.size(); i++) {
+            Map<String, CFFDataReader.DICTEntry> topDICT = 
cffReader.getTopDictEntries();
+            final CFFDataReader.DICTEntry privateEntry = 
topDICT.get("Private");
+            if (privateEntry != null) {
+                int privateOffset = 
privateEntry.getOperands().get(1).intValue();
+                Map<String, CFFDataReader.DICTEntry> privateDICT = 
cffReader.getPrivateDict(privateEntry);
+
+                if (privateDICT.containsKey("Subrs")) {
+                    int localSubrOffset = privateOffset + 
privateDICT.get("Subrs").getOperands().get(0).intValue();
+                    localIndexSubr = cffReader.readIndex(localSubrOffset);
+                }
+            }
+
+            globalIndexSubr = cffReader.getGlobalIndexSubr();
+        }
+        //Create the two lists which are to store the local and global 
subroutines
+        subsetLocalIndexSubr = new ArrayList<byte[]>();
+        subsetGlobalIndexSubr = new ArrayList<byte[]>();
+
+        localUniques = new ArrayList<Integer>();
+        globalUniques = new ArrayList<Integer>();
+
+        //Store the size of each subset index and clear the unique arrays
+        subsetLocalSubrCount = localUniques.size();
+        subsetGlobalSubrCount = globalUniques.size();
+        localUniques.clear();
+        globalUniques.clear();
+    }
+
+    protected void writeCharsetTable(boolean cidFont, boolean 
afterstringindex) throws IOException {
+        if (range.isEmpty()) {
+            writeByte(0);
+            for (Map.Entry<Integer, Integer> gid : gidToSID.entrySet()) {
+                if (cidFont && gid.getKey() == 0) {
+                    continue;
+                }
+                writeCard16((cidFont) ? gid.getKey() : gid.getValue());
+            }
+        } else {
+            writeFormat1CS(range, afterstringindex);
+        }
+    }
+
+    private void writeFormat1CS(Map<Integer, Integer> range, boolean 
afterstringindex) {
+        if (!afterstringindex) {
+            charsetOffset += 2;
+        }
+        writeByte(0);
+        writeCard16(1);
+        updateStandardRange(range);
+        for (Map.Entry<Integer, Integer> i : range.entrySet()) {
+            writeCard16(i.getKey());
+            writeByte(i.getValue());
+        }
+        writeByte(1);
+    }
+
+    private void updateStandardRange(Map<Integer, Integer> range) {
+        if (range.containsKey(NUM_STANDARD_STRINGS) && 
range.containsKey(NUM_STANDARD_STRINGS + 1)) {
+            boolean mixedCS = false;
+            for (int i : range.keySet()) {
+                if (i < NUM_STANDARD_STRINGS && i > 1) {
+                    mixedCS = true;
+                    break;
+                }
+            }
+            if (!mixedCS) {
+                if (range.containsKey(1)) {
+                    range.clear();
+                    range.put(1, 0);
+                }
+                int last = -1;
+                boolean simpleRange = false;
+                for (int i : range.keySet()) {
+                    simpleRange = (last + 1 == i);
+                    last = i;
+                }
+                if (simpleRange) {
+                    for (int i = NUM_STANDARD_STRINGS; i < 
NUM_STANDARD_STRINGS + subsetCharStringsIndex.size(); i++) {
+                        range.put(i, 0);
+                    }
+                } else {
+                    range.put(NUM_STANDARD_STRINGS, 
subsetCharStringsIndex.size());
+                }
+            }
+        } else if (cffReader.getFDSelect() instanceof 
CFFDataReader.Format3FDSelect) {
+            int last = -1;
+            int count = 1;
+            Set<Integer> r = new TreeSet<Integer>(range.keySet());
+            for (int i : r) {
+                if (last + count == i) {
+                    range.remove(i);
+                    range.put(last, count);
+                    count++;
+                } else {
+                    last = i;
+                    count = 1;
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void updateFixedOffsets(Map<String, CFFDataReader.DICTEntry> 
topDICT, int dataTopDictOffset,
+                                      int charsetOffset, int charStringOffset, 
int encodingOffset) {
+        //Charset offset in the top dict
+        final CFFDataReader.DICTEntry charset = topDICT.get("charset");
+        if (charset != null) {
+            int oldCharsetOffset = dataTopDictOffset + charset.getOffset();
+            int oldCharset = Integer.parseInt(String.format("%02x", 
output[oldCharsetOffset] & 0xff), 16);
+            if (oldCharset >= 32 && oldCharset <= 246) {
+                charsetOffset += 139;
+            }
+            output = updateOffset(output, oldCharsetOffset, 
charset.getOperandLength(), charsetOffset);
+        }
+
+        //Char string index offset in the private dict
+        final CFFDataReader.DICTEntry charString = topDICT.get("CharStrings");
+        int oldCharStringOffset = dataTopDictOffset + charString.getOffset();
+        int oldString = Integer.parseInt(String.format("%02x", 
output[oldCharStringOffset] & 0xff), 16);
+        if (oldString >= 32 && oldString <= 246) {
+            charStringOffset += 139;
+        }
+        if (!(fileFont.getCharset() instanceof CFFISOAdobeCharset)) {
+            output = updateOffset(output, oldCharStringOffset, 
charString.getOperandLength(), charStringOffset);
+        }
+
+        final CFFDataReader.DICTEntry encodingEntry = topDICT.get("Encoding");
+        if (encodingEntry != null && 
encodingEntry.getOperands().get(0).intValue() != 0
+                && encodingEntry.getOperands().get(0).intValue() != 1) {
+            int oldEncodingOffset = dataTopDictOffset + 
encodingEntry.getOffset();
+            int oldEnc = Integer.parseInt(String.format("%02x", 
output[oldEncodingOffset] & 0xff), 16);
+            if (oldEnc >= 32 && oldEnc <= 246) {
+                encodingOffset += 139;
+            } else {
+                encodingOffset--;
+            }
+            output = updateOffset(output, oldEncodingOffset, 
encodingEntry.getOperandLength(), encodingOffset);
+        }
+    }
+
+    protected void writeCIDCount(CFFDataReader.DICTEntry dictEntry) throws 
IOException {
+        writeBytes(dictEntry.getByteData());
+    }
+}

Propchange: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeCFFFonts.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java?rev=1600613&view=auto
==============================================================================
--- 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
 (added)
+++ 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
 Thu Jun  5 11:41:26 2014
@@ -0,0 +1,407 @@
+/*
+ * 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.io.IOException;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.fontbox.ttf.MaximumProfileTable;
+
+import org.apache.fop.fonts.truetype.FontFileReader;
+import org.apache.fop.fonts.truetype.GlyfTable;
+import org.apache.fop.fonts.truetype.OFDirTabEntry;
+import org.apache.fop.fonts.truetype.OFMtxEntry;
+import org.apache.fop.fonts.truetype.OFTableName;
+import org.apache.fop.fonts.truetype.TTFSubSetFile;
+
+public class MergeTTFonts extends TTFSubSetFile {
+    private Map<Integer, Glyph> added = new TreeMap<Integer, Glyph>();
+    private int origIndexesLen = 0;
+    private int size = 0;
+    protected MaximumProfileTable maxp = new MaximumProfileTable();
+
+    static class Glyph {
+        final byte[] data;
+        final OFMtxEntry mtx;
+        Glyph(byte[] d, OFMtxEntry m) {
+            data = d;
+            mtx = m;
+        }
+    }
+
+    /**
+     * Create the glyf table and fill in loca table
+     */
+    private void readGlyf(Map<Integer, Integer> glyphs, FontFileReader in) 
throws IOException {
+        OFDirTabEntry entry = dirTabs.get(OFTableName.GLYF);
+        if (entry != null) {
+            int[] origIndexes = buildSubsetIndexToOrigIndexMap(glyphs);
+            for (int i = 0; i < origIndexes.length; i++) {
+                int nextOffset = 0;
+                int origGlyphIndex = origIndexes[i];
+                if (origGlyphIndex >= (mtxTab.length - 1)) {
+                    nextOffset = (int)lastLoca;
+                } else {
+                    nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset();
+                }
+                int glyphOffset = (int)mtxTab[origGlyphIndex].getOffset();
+                int glyphLength = nextOffset - glyphOffset;
+                if (glyphLength < 0) {
+                    continue;
+                }
+                byte[] glyphData = in.getBytes(
+                        (int)entry.getOffset() + glyphOffset,
+                        glyphLength);
+
+                Glyph g = new Glyph(glyphData, mtxTab[origGlyphIndex]);
+                if (!cid && (origIndexesLen == 0 || (glyphLength > 0 && i > 
0))) {
+                    added.put(i, g);
+                } else if (cid) {
+                    added.put(i + origIndexesLen, g);
+                }
+            }
+            if (!cid) {
+                origIndexesLen = origIndexes.length;
+            } else {
+                origIndexesLen += origIndexes.length;
+            }
+        } else {
+            throw new IOException("Can't find glyf table");
+        }
+    }
+
+    private void createGlyf() throws IOException {
+        OFDirTabEntry entry = dirTabs.get(OFTableName.GLYF);
+        int size = 0;
+        int startPos = 0;
+        int endOffset = 0;    // Store this as the last loca
+
+        if (entry != null) {
+            pad4();
+            startPos = currentPos;
+            /* Loca table must be in order by glyph index, so build
+             * an array first and then write the glyph info and
+             * location offset.
+             */
+            glyphOffsets = new int[origIndexesLen];
+            for (Map.Entry<Integer, Glyph> gly : added.entrySet()) {
+                byte[] glyphData = gly.getValue().data;
+                int glyphLength = glyphData.length;
+                int i = gly.getKey();
+                int endOffset1 = endOffset;
+                // Copy glyph
+                writeBytes(glyphData);
+                // Update loca table
+                if (cid || locaFormat == 1) {
+                    writeULong(locaOffset + i * 4, currentPos - startPos);
+                }
+                if ((currentPos - startPos + glyphLength) > endOffset1) {
+                    endOffset1 = (currentPos - startPos + glyphLength);
+                }
+
+                // Store the glyph boundary positions relative to the start of 
the font
+                glyphOffsets[i] = currentPos;
+                currentPos += glyphLength;
+                realSize += glyphLength;
+
+                endOffset = endOffset1;
+            }
+
+            size = currentPos - startPos;
+
+            currentPos += 12;
+            realSize += 12;
+            updateCheckSum(startPos, size + 12, OFTableName.GLYF);
+
+            // Update loca checksum and last loca index
+            if (cid || locaFormat == 1) {
+                writeULong(locaOffset + added.size() * 4, endOffset);
+            }
+            int locaSize = added.size() * 4 + 4;
+            int checksum = getCheckSum(output, locaOffset, locaSize);
+            writeULong(offsets.get(OFTableName.LOCA), checksum);
+            int padSize = (locaOffset + locaSize) % 4;
+            newDirTabs.put(OFTableName.LOCA,
+                    new OFDirTabEntry(locaOffset, locaSize + padSize));
+
+            if (!cid && locaFormat == 0) {
+                int i = 0;
+                int offset = 0;
+                for (Glyph e : added.values()) {
+                    writeUShort(locaOffset + i * 2, offset / 2);
+                    offset += e.data.length;
+                    i++;
+                }
+                writeUShort(locaOffset + i * 2, offset / 2);
+            }
+        } else {
+            throw new IOException("Can't find glyf table");
+        }
+    }
+
+    /**
+     * Create the hmtx table by copying metrics from original
+     * font to subset font. The glyphs Map contains an
+     * Integer key and Integer value that maps the original
+     * metric (key) to the subset metric (value)
+     */
+    protected void createHmtx() throws IOException {
+        OFTableName hmtx = OFTableName.HMTX;
+        OFDirTabEntry entry = dirTabs.get(hmtx);
+        if (entry != null) {
+            pad4();
+            // int offset = (int)entry.offset;
+
+            int longHorMetricSize = added.size() * 4;
+            int leftSideBearingSize = added.size() * 4;
+            int hmtxSize = longHorMetricSize + leftSideBearingSize;
+
+            for (Map.Entry<Integer, Glyph> e : added.entrySet()) {
+                Integer subsetIndex = e.getKey();
+                OFMtxEntry mtx = e.getValue().mtx;
+                writeUShort(currentPos + subsetIndex * 4,
+                        mtx.getWx());
+                writeUShort(currentPos + subsetIndex * 4 + 2,
+                        mtx.getLsb());
+            }
+
+            updateCheckSum(currentPos, hmtxSize, hmtx);
+            currentPos += hmtxSize;
+            realSize += hmtxSize;
+        } else {
+            throw new IOException("Can't find hmtx table");
+        }
+    }
+
+    /**
+     * Returns a subset of the original font.
+     *
+     *
+     * @param subsetGlyphs Map of glyphs (glyphs has old index as (Integer) 
key and
+     * new index as (Integer) value)
+     * @throws IOException in case of an I/O problem
+     */
+    public void readFont(FontFileReader fontFile, Map<Integer, Integer> 
subsetGlyphs, boolean cid) throws IOException {
+        this.cid = cid;
+        if (subsetGlyphs.isEmpty()) {
+            return;
+        }
+        this.fontFile = fontFile;
+        size += fontFile.getAllBytes().length;
+
+        readDirTabs();
+        readFontHeader();
+        getNumGlyphs();
+        readHorizontalHeader();
+        readHorizontalMetrics();
+        readIndexToLocation();
+        if (!cid && subsetGlyphs.size() <= 1) {
+            for (int i = 0; i < mtxTab.length; i++) {
+                subsetGlyphs.put(i, i);
+            }
+        }
+        scanGlyphs(fontFile, subsetGlyphs);
+        readGlyf(subsetGlyphs, fontFile);
+    }
+
+    protected void scanGlyphs(FontFileReader in, Map<Integer, Integer> 
subsetGlyphs)
+            throws IOException {
+        OFDirTabEntry glyfTableInfo = dirTabs.get(OFTableName.GLYF);
+        if (glyfTableInfo == null) {
+            throw new IOException("Glyf table could not be found");
+        }
+        new MergeGlyfTable(in, mtxTab, glyfTableInfo, subsetGlyphs);
+    }
+
+    static class MergeGlyfTable extends GlyfTable {
+        public MergeGlyfTable(FontFileReader in, OFMtxEntry[] metrics, 
OFDirTabEntry dirTableEntry, Map<Integer, Integer> glyphs)
+                throws IOException {
+            super(in, metrics, dirTableEntry, glyphs);
+            populateGlyphsWithComposites();
+        }
+
+        @Override
+        protected void addAllComposedGlyphsToSubset() {
+            int newIndex = -1;
+            for (int v : subset.values()) {
+                if (v > newIndex) {
+                    newIndex = v;
+                }
+            }
+            for (int composedGlyph : composedGlyphs) {
+                subset.put(composedGlyph, ++newIndex);
+            }
+        }
+    }
+
+    public void writeFont(PDFBoxAdapter.Cmap cmap) throws IOException {
+        output = new byte[size * 2];
+        createDirectory();     // Create the TrueType header and directory
+        int sgsize = added.size();
+        if (!cid) {
+            writeCMAP(cmap);
+//            copyTable(fontFile, OFTableName.CMAP);
+        }
+        createHmtx();           // Create hmtx table
+        if (cid || locaFormat == 1) {
+            createLoca(sgsize);    // create empty loca table
+        } else {
+            createLoca(numberOfGlyphs / 2);    // create empty loca table
+        }
+        createHead(fontFile);
+        createOS2(fontFile);                          // copy the OS/2 table
+        if (!cid) {
+            createHhea(fontFile, sgsize - 2);    // Create the hhea table
+        } else {
+            createHhea(fontFile, sgsize);    // Create the hhea table
+        }
+        if (maxp.getVersion() == 0) {
+            createMaxp(fontFile, sgsize);    // copy the maxp table
+        } else {
+            writeMaxp();
+        }
+        boolean optionalTableFound;
+        optionalTableFound = createCvt(fontFile);    // copy the cvt table
+        if (!optionalTableFound) {
+            // cvt is optional (used in TrueType fonts only)
+            log.debug("TrueType: ctv table not present. Skipped.");
+        }
+        optionalTableFound = createFpgm(fontFile);    // copy fpgm table
+        if (!optionalTableFound) {
+            // fpgm is optional (used in TrueType fonts only)
+            log.debug("TrueType: fpgm table not present. Skipped.");
+        }
+        createPost(fontFile);                         // copy the post table
+        optionalTableFound = createPrep(fontFile);    // copy prep table
+        if (!optionalTableFound) {
+            // prep is optional (used in TrueType fonts only)
+            log.debug("TrueType: prep table not present. Skipped.");
+        }
+        createName(fontFile);                         // copy the name table
+        createGlyf(); //create glyf table and update loca table
+        pad4();
+        createCheckSumAdjustment();
+    }
+
+    private void writeMaxp() {
+        int checksum = currentPos;
+        pad4();
+        int startPos = currentPos;
+        writeUShort((int) maxp.getVersion()); //version
+        writeUShort(0);
+        writeUShort(added.size()); //numGlyphs
+        writeUShort(maxp.getMaxPoints()); //maxPoints
+        writeUShort(maxp.getMaxContours()); //maxContours
+        writeUShort(maxp.getMaxCompositePoints()); //maxCompositePoints
+        writeUShort(maxp.getMaxCompositeContours()); //maxCompositeContours
+        writeUShort(maxp.getMaxZones()); //maxZones
+        writeUShort(maxp.getMaxTwilightPoints()); //maxTwilightPoints
+        writeUShort(maxp.getMaxStorage()); //maxStorage
+        writeUShort(maxp.getMaxFunctionDefs()); //maxFunctionDefs
+        writeUShort(maxp.getMaxInstructionDefs()); //maxInstructionDefs
+        writeUShort(maxp.getMaxStackElements()); //maxStackElements
+        writeUShort(maxp.getMaxSizeOfInstructions()); //maxSizeOfInstructions
+        writeUShort(maxp.getMaxComponentElements()); //maxComponentElements
+        writeUShort(maxp.getMaxComponentDepth()); //maxComponentDepth
+        updateCheckSum(checksum, currentPos - startPos, OFTableName.MAXP);
+        realSize += currentPos - startPos;
+    }
+
+    private void writeCMAP(PDFBoxAdapter.Cmap cmap) {
+        int checksum = currentPos;
+        pad4();
+        int cmapPos = currentPos;
+        writeUShort(0); //version
+        writeUShort(1); //number of tables
+
+        Map<Integer, Integer> glyphIdToCharacterCode = 
cmap.glyphIdToCharacterCode;
+        writeUShort(cmap.platformId); //platformid
+        writeUShort(cmap.platformEncodingId); //platformEncodingId
+        writeULong(currentPos, currentPos - cmapPos + 12); //subTableOffset
+        currentPos += 12;
+
+        writeUShort(12); //subtableFormat
+        writeUShort(0);
+        writeULong(currentPos, (glyphIdToCharacterCode.size() * 12) + 16);
+        currentPos += 4;
+        writeULong(currentPos, 0);
+        currentPos += 4;
+        writeULong(currentPos, glyphIdToCharacterCode.size());
+        currentPos += 4;
+
+        for (Map.Entry<Integer, Integer> g : 
glyphIdToCharacterCode.entrySet()) {
+            writeULong(currentPos, g.getKey());
+            currentPos += 4;
+            writeULong(currentPos, g.getKey());
+            currentPos += 4;
+            writeULong(currentPos, g.getValue());
+            currentPos += 4;
+        }
+
+//            writeUShort(6); //subtableFormat
+//            int firstCode = -1;
+//            int lastCode = -1;
+//            List<Integer> codes = new ArrayList<Integer>();
+//            for (Map.Entry<Integer, Integer> g : 
glyphIdToCharacterCode.entrySet()) {
+//                if (firstCode < 0) {
+//                    firstCode = g.getKey();
+//                }
+//                while (lastCode > 0 && lastCode + 1 < g.getKey()) {
+//                    codes.add(0);
+//                    lastCode++;
+//                }
+//                codes.add(g.getValue());
+//
+//                lastCode = g.getKey();
+//            }
+//
+//            writeUShort((codes.size() * 2) + 6); //length
+//            writeUShort(0); //version
+//
+//            writeUShort(firstCode); //firstCode
+//            writeUShort(codes.size()); //entryCount
+//
+//            for (int i : codes) {
+//                writeUShort(i);
+//            }
+
+        updateCheckSum(checksum, currentPos - cmapPos, OFTableName.CMAP);
+        realSize += currentPos - cmapPos;
+    }
+
+    /**
+     * Get index from starting at lowest glyph position
+     * @param glyphs map
+     * @return index map
+     */
+    protected int[] buildSubsetIndexToOrigIndexMap(Map<Integer, Integer> 
glyphs) {
+        int[] origIndexes = new int[glyphs.size()];
+        int minIndex = Integer.MAX_VALUE;
+        for (int glyph : glyphs.values()) {
+            if (minIndex > glyph) {
+                minIndex = glyph;
+            }
+        }
+        for (Map.Entry<Integer, Integer> glyph : glyphs.entrySet()) {
+            int origIndex = glyph.getKey();
+            int subsetIndex = glyph.getValue() - minIndex;
+            origIndexes[subsetIndex] = origIndex;
+        }
+        return origIndexes;
+    }
+}

Propchange: 
xmlgraphics/fop-pdf-images/branches/Temp_FontMerging/src/java/org/apache/fop/render/pdf/pdfbox/MergeTTFonts.java
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscr...@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-h...@xmlgraphics.apache.org

Reply via email to