From 07813e43d1aca3bc67cdb4acea37870b7e764806 Mon Sep 17 00:00:00 2001 From: Simon Steiner Date: Thu, 31 Aug 2017 12:31:03 +0000 Subject: [PATCH] FOP-2699: Fix subsetting large number of glyphs git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1806787 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/fonts/truetype/OTFSubSetFile.java | 61 ++++++++++++------- .../fonts/truetype/OTFSubSetFileTestCase.java | 21 ++++++- 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java index 942bcae79..889b9512d 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java @@ -172,11 +172,7 @@ public class OTFSubSetFile extends OTFSubSetWriter { //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(); + int topDictDataOffset = topDictOffset + writeTopDICT(); //Create the char string index data and related local / global subroutines if (cffReader.getFDSelect() == null) { @@ -251,27 +247,39 @@ public class OTFSubSetFile extends OTFSubSetWriter { return fontNameSIDs; } - protected void writeTopDICT() throws IOException { + protected int writeTopDICT() throws IOException { Map topDICT = cffReader.getTopDictEntries(); List topDictStringEntries = Arrays.asList("version", "Notice", "Copyright", "FullName", "FamilyName", "Weight", "PostScript"); + ByteArrayOutputStream dict = new ByteArrayOutputStream(); + int offsetExtra = 0; for (Map.Entry dictEntry : topDICT.entrySet()) { String dictKey = dictEntry.getKey(); DICTEntry entry = dictEntry.getValue(); //If the value is an SID, update the reference but keep the size the same - if (dictKey.equals("ROS")) { - writeROSEntry(entry); + entry.setOffset(entry.getOffset() + offsetExtra); + if (dictKey.equals("CharStrings") && entry.getOperandLength() == 3) { + byte[] extra = new byte[2]; + offsetExtra += extra.length; + dict.write(extra); + dict.write(entry.getByteData()); + entry.setOperandLength(5); + } else if (dictKey.equals("ROS")) { + dict.write(writeROSEntry(entry)); } else if (dictKey.equals("CIDCount")) { - writeCIDCount(entry); + dict.write(writeCIDCount(entry)); } else if (topDictStringEntries.contains(dictKey)) { - writeTopDictStringEntry(entry); + dict.write(writeTopDictStringEntry(entry)); } else { - writeBytes(entry.getByteData()); + dict.write(entry.getByteData()); } } + byte[] topDictIndex = cffReader.getTopDictIndex().getByteData(); + int offSize = topDictIndex[2]; + return writeIndex(Arrays.asList(dict.toByteArray()), offSize) - dict.size(); } - private void writeROSEntry(DICTEntry dictEntry) throws IOException { + private byte[] writeROSEntry(DICTEntry dictEntry) throws IOException { int sidA = dictEntry.getOperands().get(0).intValue(); if (sidA > 390) { stringIndexData.add(cffReader.getStringIndex().getValue(sidA - NUM_STANDARD_STRINGS)); @@ -289,17 +297,17 @@ public class OTFSubSetFile extends OTFSubSetWriter { dictEntry.getOperandLengths().get(1), sidBStringIndex); updateOffset(cidEntryByteData, dictEntry.getOperandLengths().get(0) + dictEntry.getOperandLengths().get(1), dictEntry.getOperandLengths().get(2), 0); - writeBytes(cidEntryByteData); + return cidEntryByteData; } - protected void writeCIDCount(DICTEntry dictEntry) throws IOException { + protected byte[] writeCIDCount(DICTEntry dictEntry) throws IOException { byte[] cidCountByteData = dictEntry.getByteData(); updateOffset(cidCountByteData, 0, dictEntry.getOperandLengths().get(0), subsetGlyphs.size()); - writeBytes(cidCountByteData); + return cidCountByteData; } - private void writeTopDictStringEntry(DICTEntry dictEntry) throws IOException { + private byte[] writeTopDictStringEntry(DICTEntry dictEntry) throws IOException { int sid = dictEntry.getOperands().get(0).intValue(); if (sid > 391) { stringIndexData.add(cffReader.getStringIndex().getValue(sid - 391)); @@ -307,7 +315,7 @@ public class OTFSubSetFile extends OTFSubSetWriter { byte[] newDictEntry = createNewRef(stringIndexData.size() + 390, dictEntry.getOperator(), dictEntry.getOperandLength(), true); - writeBytes(newDictEntry); + return newDictEntry; } private void writeStringIndex() throws IOException { @@ -906,17 +914,21 @@ public class OTFSubSetFile extends OTFSubSetWriter { } protected int writeIndex(List dataArray) { + int totLength = 1; + for (byte[] aDataArray1 : dataArray) { + totLength += aDataArray1.length; + } + int offSize = getOffSize(totLength); + return writeIndex(dataArray, offSize); + } + + protected int writeIndex(List dataArray, int offSize) { int hdrTotal = 3; //2 byte number of items this.writeCard16(dataArray.size()); //Offset Size: 1 byte = 256, 2 bytes = 65536 etc. //Offsets in the offset array are relative to the byte that precedes the object data. //Therefore the first element of the offset array is always 1. - int totLength = 1; - for (byte[] aDataArray1 : dataArray) { - totLength += aDataArray1.length; - } - int offSize = getOffSize(totLength); this.writeByte(offSize); //Count the first offset 1 hdrTotal += offSize; @@ -1126,11 +1138,15 @@ public class OTFSubSetFile extends OTFSubSetWriter { } protected void updateOffset(byte[] out, int position, int length, int replacement) { + if (length == 2 && replacement < 108 && replacement > -108) { + length = 1; + } switch (length) { case 1: out[position] = (byte)(replacement + 139); break; case 2: + assert replacement <= 1131; if (replacement <= -876) { out[position] = (byte)254; } else if (replacement <= -620) { @@ -1155,6 +1171,7 @@ public class OTFSubSetFile extends OTFSubSetWriter { } break; case 3: + assert replacement <= 32767; out[position] = (byte)28; out[position + 1] = (byte)((replacement >> 8) & 0xFF); out[position + 2] = (byte)(replacement & 0xFF); diff --git a/fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java b/fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java index 608d8c3dc..2b6d8adb5 100644 --- a/fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java @@ -435,8 +435,8 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { @Test public void testFDSelect() throws IOException { - Assert.assertEquals(getSubset(1).length, 41); - Assert.assertEquals(getSubset(2).length, 48); + Assert.assertEquals(getSubset(1).length, 43); + Assert.assertEquals(getSubset(2).length, 50); } private byte[] getSubset(final int opLen) throws IOException { @@ -480,7 +480,7 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { when(cffReader.getHeader()).thenReturn(new byte[0]); when(cffReader.getTopDictIndex()).thenReturn(new CFFDataReader().new CFFIndexData() { public byte[] getByteData() throws IOException { - return new byte[3]; + return new byte[] {0, 0, 1}; } }); @@ -501,4 +501,19 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { super.updateFixedOffsets(topDICT, dataTopDictOffset, charsetOffset, charStringOffset, encodingOffset); } } + + @Test + public void testResizeOfOperand() throws IOException { + OTFSubSetFile otfSubSetFile = new OTFSubSetFile() { + protected void writeFDSelect() { + super.writeFDSelect(); + writeBytes(new byte[1024 * 100]); + } + }; + otfSubSetFile.readFont(sourceSansReader, "StandardOpenType", null, glyphs); + byte[] fontSubset = otfSubSetFile.getFontSubset(); + CFFDataReader cffReader = new CFFDataReader(fontSubset); + assertEquals(cffReader.getTopDictEntries().get("CharStrings").getOperandLength(), 5); + assertEquals(cffReader.getTopDictEntries().get("CharStrings").getByteData().length, 6); + } } -- 2.39.5