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