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: [email protected]
For additional commands, e-mail: [email protected]