]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
FOP-2737: OTF subsetting: Resize privateDict to fit op size
authorSimon Steiner <ssteiner@apache.org>
Wed, 4 Oct 2017 11:25:37 +0000 (11:25 +0000)
committerSimon Steiner <ssteiner@apache.org>
Wed, 4 Oct 2017 11:25:37 +0000 (11:25 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1811062 13f79535-47bb-0310-9956-ffa450edef68

fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java
fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java

index be197c1ec7a489d061803c6f8119ffca4714ebc9..d8e8c28439a87f02c62ceff3ce39d6cbbaddd74b 100644 (file)
@@ -30,7 +30,6 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -69,7 +68,7 @@ public class OTFSubSetFile extends OTFSubSetWriter {
 
     /** For fonts which have an FDSelect or ROS flag in Top Dict, this is used to store the
      * local subroutine indexes for each group as opposed to the above subsetLocalIndexSubr */
-    private List<List<byte[]>> fdSubrs;
+    protected List<List<byte[]>> fdSubrs;
 
     /** The subset FD Select table used to store the mappings between glyphs and their
      * associated FDFont object which point to a private dict and local subroutines. */
@@ -264,7 +263,7 @@ public class OTFSubSetFile extends OTFSubSetWriter {
     protected List<Integer> storeFDStrings(List<Integer> uniqueNewRefs) throws IOException {
         List<Integer> fontNameSIDs = new ArrayList<Integer>();
         List<FontDict> fdFonts = cffReader.getFDFonts();
-        for (Integer uniqueNewRef : uniqueNewRefs) {
+        for (int uniqueNewRef : uniqueNewRefs) {
             FontDict fdFont = fdFonts.get(uniqueNewRef);
             byte[] fdFontByteData = fdFont.getByteData();
             Map<String, DICTEntry> fdFontDict = cffReader.parseDictData(fdFontByteData);
@@ -354,29 +353,29 @@ public class OTFSubSetFile extends OTFSubSetWriter {
 
         for (Entry<Integer, Integer> subsetGlyph : subsetGlyphs.entrySet()) {
             int gid = subsetGlyph.getKey();
+            int v = subsetGlyph.getValue();
             int sid = cffReader.getSIDFromGID(charsetOffset, gid);
             //Check whether the SID falls into the standard string set
             if (sid < NUM_STANDARD_STRINGS) {
-                gidToSID.put(subsetGlyph.getValue(), sid);
+                gidToSID.put(v, sid);
                 if (mbFont != null) {
-                    mbFont.mapUsedGlyphName(subsetGlyph.getValue(),
-                            CFFStandardString.getName(sid));
+                    mbFont.mapUsedGlyphName(v, CFFStandardString.getName(sid));
                 }
             } else {
                 int index = sid - NUM_STANDARD_STRINGS;
                 //index is 0 based, should use < not <=
                 if (index < cffReader.getStringIndex().getNumObjects()) {
+                    byte[] value = cffReader.getStringIndex().getValue(index);
                     if (mbFont != null) {
-                        mbFont.mapUsedGlyphName(subsetGlyph.getValue(),
-                                new String(cffReader.getStringIndex().getValue(index), "UTF-8"));
+                        mbFont.mapUsedGlyphName(v, new String(value, "UTF-8"));
                     }
-                    gidToSID.put(subsetGlyph.getValue(), stringIndexData.size() + 391);
-                    stringIndexData.add(cffReader.getStringIndex().getValue(index));
+                    gidToSID.put(v, stringIndexData.size() + 391);
+                    stringIndexData.add(value);
                 } else {
                     if (mbFont != null) {
-                        mbFont.mapUsedGlyphName(subsetGlyph.getValue(), ".notdef");
+                        mbFont.mapUsedGlyphName(v, ".notdef");
                     }
-                    gidToSID.put(subsetGlyph.getValue(), index);
+                    gidToSID.put(v, index);
                 }
             }
         }
@@ -395,22 +394,25 @@ public class OTFSubSetFile extends OTFSubSetWriter {
             Map<Integer, Integer> subsetGroups = new HashMap<Integer, Integer>();
 
             List<Integer> uniqueGroups = new ArrayList<Integer>();
+            Map<Integer, Integer> rangeMap = fdSelect.getRanges();
+            Integer[] ranges = rangeMap.keySet().toArray(new Integer[rangeMap.size()]);
             for (int gid : subsetGlyphs.keySet()) {
-                Set<Integer> rangeKeys = fdSelect.getRanges().keySet();
-                Integer[] ranges = rangeKeys.toArray(new Integer[rangeKeys.size()]);
-                for (int i = 0; i < ranges.length; i++) {
-                    int nextRange = -1;
+                int i = 0;
+                for (Entry<Integer, Integer> entry : rangeMap.entrySet()) {
+                    int nextRange;
                     if (i < ranges.length - 1) {
                         nextRange = ranges[i + 1];
                     } else {
                         nextRange = fdSelect.getSentinelGID();
                     }
-                    if (gid >= ranges[i] && gid < nextRange) {
-                        subsetGroups.put(gid, fdSelect.getRanges().get(ranges[i]));
-                        if (!uniqueGroups.contains(fdSelect.getRanges().get(ranges[i]))) {
-                            uniqueGroups.add(fdSelect.getRanges().get(ranges[i]));
+                    if (gid >= entry.getKey() && gid < nextRange) {
+                        int r = entry.getValue();
+                        subsetGroups.put(gid, r);
+                        if (!uniqueGroups.contains(r)) {
+                            uniqueGroups.add(r);
                         }
                     }
+                    i++;
                 }
             }
 
@@ -425,7 +427,7 @@ public class OTFSubSetFile extends OTFSubSetWriter {
             subsetFDSelect = new LinkedHashMap<Integer, FDIndexReference>();
 
             List<List<Integer>> foundLocalUniques = new ArrayList<List<Integer>>();
-            for (Integer uniqueGroup1 : uniqueGroups) {
+            for (int u : uniqueGroups) {
                 foundLocalUniques.add(new ArrayList<Integer>());
             }
             Map<Integer, Integer> gidHintMaskLengths = new HashMap<Integer, Integer>();
@@ -455,7 +457,7 @@ public class OTFSubSetFile extends OTFSubSetWriter {
                 fdSubrs.add(new ArrayList<byte[]>());
             }
             List<List<Integer>> foundLocalUniquesB = new ArrayList<List<Integer>>();
-            for (Integer uniqueGroup : uniqueGroups) {
+            for (int u : uniqueGroups) {
                 foundLocalUniquesB.add(new ArrayList<Integer>());
             }
             for (Entry<Integer, Integer> subsetGlyph : subsetGlyphs.entrySet()) {
@@ -463,11 +465,11 @@ public class OTFSubSetFile extends OTFSubSetWriter {
                 int value = subsetGlyph.getValue();
                 int group = subsetGroups.get(gid);
                 localIndexSubr = cffReader.getFDFonts().get(group).getLocalSubrData();
-                localUniques = foundLocalUniquesB.get(subsetFDSelect.get(value).getNewFDIndex());
+                int newFDIndex = subsetFDSelect.get(value).getNewFDIndex();
+                localUniques = foundLocalUniquesB.get(newFDIndex);
                 byte[] data = charStringsIndex.getValue(gid);
-                subsetLocalIndexSubr = fdSubrs.get(subsetFDSelect.get(value).getNewFDIndex());
-                subsetLocalSubrCount = foundLocalUniques.get(subsetFDSelect.get(value)
-                        .getNewFDIndex()).size();
+                subsetLocalIndexSubr = fdSubrs.get(newFDIndex);
+                subsetLocalSubrCount = foundLocalUniques.get(newFDIndex).size();
                 type2Parser = new Type2Parser();
                 type2Parser.setMaskLength(gidHintMaskLengths.get(gid));
                 data = readCharStringData(data, subsetLocalSubrCount);
@@ -528,23 +530,37 @@ public class OTFSubSetFile extends OTFSubSetWriter {
             throws IOException {
         List<Integer> privateDictOffsets = new ArrayList<Integer>();
         List<FontDict> fdFonts = cffReader.getFDFonts();
-        for (int i = 0; i < uniqueNewRefs.size(); i++) {
-            FontDict curFDFont = fdFonts.get(uniqueNewRefs.get(i));
+        int i = 0;
+        for (int ref : uniqueNewRefs) {
+            FontDict curFDFont = fdFonts.get(ref);
             byte[] fdPrivateDictByteData = curFDFont.getPrivateDictData();
             Map<String, DICTEntry> fdPrivateDict = cffReader.parseDictData(fdPrivateDictByteData);
             int privateDictOffset = currentPos;
             privateDictOffsets.add(privateDictOffset);
-            if (fdPrivateDict.get("Subrs") != null) {
-                updateOffset(fdPrivateDictByteData, fdPrivateDict.get("Subrs").getOffset(),
-                        fdPrivateDict.get("Subrs").getOperandLength(),
+            DICTEntry subrs = fdPrivateDict.get("Subrs");
+            if (subrs != null) {
+                fdPrivateDictByteData = resizeToFitOpLen(fdPrivateDictByteData, subrs);
+                updateOffset(fdPrivateDictByteData, subrs.getOffset(),
+                        subrs.getOperandLength(),
                         fdPrivateDictByteData.length);
             }
             writeBytes(fdPrivateDictByteData);
             writeIndex(fdSubrs.get(i));
+            i++;
         }
         return privateDictOffsets;
     }
 
+    private byte[] resizeToFitOpLen(byte[] fdPrivateDictByteData, DICTEntry subrs) throws IOException {
+        if (subrs.getOperandLength() == 2 && fdPrivateDictByteData.length < 108) {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            bos.write(fdPrivateDictByteData);
+            bos.write(new byte[108 - fdPrivateDictByteData.length]);
+            fdPrivateDictByteData = bos.toByteArray();
+        }
+        return fdPrivateDictByteData;
+    }
+
     protected int writeFDArray(List<Integer> uniqueNewRefs, List<Integer> privateDictOffsets,
             List<Integer> fontNameSIDs)
             throws IOException {
@@ -552,8 +568,9 @@ public class OTFSubSetFile extends OTFSubSetWriter {
         List<FontDict> fdFonts = cffReader.getFDFonts();
         List<byte[]> index = new ArrayList<byte[]>();
 
-        for (int i = 0; i < uniqueNewRefs.size(); i++) {
-            FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i));
+        int i = 0;
+        for (int ref : uniqueNewRefs) {
+            FontDict fdFont = fdFonts.get(ref);
             byte[] fdFontByteData = fdFont.getByteData();
             Map<String, DICTEntry> fdFontDict = cffReader.parseDictData(fdFontByteData);
             //Update the SID to the FontName
@@ -566,6 +583,7 @@ public class OTFSubSetFile extends OTFSubSetWriter {
                     fdFontDict.get("Private").getOperandLengths().get(1),
                     privateDictOffsets.get(i));
             index.add(fdFontByteData);
+            i++;
         }
         writeIndex(index);
         return offset;
@@ -817,7 +835,7 @@ public class OTFSubSetFile extends OTFSubSetWriter {
                 if (newRef != -1) {
                     byte[] newData = constructNewRefData(dataPos, data, operand, subsetGlobalSubrCount,
                             newRef, new int[] {29});
-                    dataPos -= (data.length - newData.length);
+                    dataPos -= data.length - newData.length;
                     data = newData;
                 }
             } else {
@@ -933,8 +951,8 @@ public class OTFSubSetFile extends OTFSubSetWriter {
 
     protected int writeIndex(List<byte[]> dataArray) {
         int totLength = 1;
-        for (byte[] aDataArray1 : dataArray) {
-            totLength += aDataArray1.length;
+        for (byte[] data : dataArray) {
+            totLength += data.length;
         }
         int offSize = getOffSize(totLength);
         return writeIndex(dataArray, offSize);
@@ -951,9 +969,10 @@ public class OTFSubSetFile extends OTFSubSetWriter {
         //Count the first offset 1
         hdrTotal += offSize;
         int total = 0;
-        for (int i = 0; i < dataArray.size(); i++) {
+        int i = 0;
+        for (byte[] data : dataArray) {
             hdrTotal += offSize;
-            int length = dataArray.get(i).length;
+            int length = data.length;
             switch (offSize) {
             case 1:
                 if (i == 0) {
@@ -986,6 +1005,7 @@ public class OTFSubSetFile extends OTFSubSetWriter {
             default:
                 throw new AssertionError("Offset Size was not an expected value.");
             }
+            i++;
         }
         for (byte[] aDataArray : dataArray) {
             writeBytes(aDataArray);
@@ -1148,9 +1168,6 @@ 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);
index f95f6ffd9dbe1318f1a6fbfc7c6b4403b74bef60..186f99a542c66e2c8424a4f886a93b6fe9900783 100644 (file)
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.when;
 import org.apache.fontbox.cff.CFFFont;
 import org.apache.fontbox.cff.CFFParser;
 
+import org.apache.fop.fonts.MultiByteFont;
 import org.apache.fop.fonts.cff.CFFDataReader;
 import org.apache.fop.fonts.cff.CFFDataReader.CFFIndexData;
 import org.apache.fop.fonts.cff.CFFDataReader.DICTEntry;
@@ -500,7 +501,9 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase {
             map.put("charset", dict);
             map.put("CharStrings", dict);
             when((cffReader.getTopDictEntries())).thenReturn(map);
-            when(cffReader.getFDSelect()).thenReturn(new CFFDataReader().new Format3FDSelect());
+            CFFDataReader.Format3FDSelect fdSelect = new CFFDataReader().new Format3FDSelect();
+            fdSelect.setRanges(new HashMap<Integer, Integer>());
+            when(cffReader.getFDSelect()).thenReturn(fdSelect);
             cffReader.getTopDictEntries().get("CharStrings").setOperandLength(opLen);
             super.createCFF();
         }
@@ -558,14 +561,7 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase {
                     return new byte[128];
                 }
             };
-            cffReader = mock(CFFDataReader.class);
-            LinkedHashMap<String, DICTEntry> map = new LinkedHashMap<String, DICTEntry>();
-            DICTEntry e = new DICTEntry();
-            e.setOffset(1);
-            e.setOperandLengths(Arrays.asList(0, 0));
-            map.put("FontName", e);
-            map.put("Private", e);
-            when(cffReader.parseDictData(any(byte[].class))).thenReturn(map);
+            cffReader = makeCFFDataReader();
             when(cffReader.getFDFonts()).thenReturn(fdFonts);
 
             fdFonts.clear();
@@ -611,15 +607,7 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase {
         }
 
         protected void createCFF() throws IOException {
-            cffReader = mock(CFFDataReader.class);
-            when(cffReader.getHeader()).thenReturn(new byte[0]);
-            when(cffReader.getTopDictIndex()).thenReturn(cffReader.new CFFIndexData() {
-                public byte[] getByteData() throws IOException {
-                    return new byte[]{0, 0, 1};
-                }
-            });
-            when(cffReader.getFDSelect()).thenReturn(cffReader.new Format3FDSelect());
-
+            cffReader = makeCFFDataReader();
             LinkedHashMap<String, DICTEntry> topDict = new LinkedHashMap<String, DICTEntry>();
             DICTEntry entry = new DICTEntry();
             entry.setOperands(Arrays.<Number>asList(0));
@@ -638,4 +626,54 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase {
             this.offsets = offsets;
         }
     }
+
+    private static CFFDataReader makeCFFDataReader() throws IOException {
+        CFFDataReader cffReader = mock(CFFDataReader.class);
+        when(cffReader.getHeader()).thenReturn(new byte[0]);
+        when(cffReader.getTopDictIndex()).thenReturn(cffReader.new CFFIndexData() {
+            public byte[] getByteData() throws IOException {
+                return new byte[]{0, 0, 1};
+            }
+        });
+        CFFDataReader.Format3FDSelect fdSelect = cffReader.new Format3FDSelect();
+        fdSelect.setRanges(new HashMap<Integer, Integer>());
+        when(cffReader.getFDSelect()).thenReturn(fdSelect);
+        CFFDataReader.FontDict fd = mock(CFFDataReader.FontDict.class);
+        when(fd.getPrivateDictData()).thenReturn(new byte[0]);
+        when(cffReader.getFDFonts()).thenReturn(Arrays.asList(fd));
+
+        LinkedHashMap<String, DICTEntry> map = new LinkedHashMap<String, DICTEntry>();
+        DICTEntry e = new DICTEntry();
+        e.setOffset(1);
+        e.setOperandLengths(Arrays.asList(0, 0));
+        e.setOperandLength(2);
+        map.put("FontName", e);
+        map.put("Private", e);
+        map.put("Subrs", e);
+        when(cffReader.parseDictData(any(byte[].class))).thenReturn(map);
+        return cffReader;
+    }
+
+    @Test
+    public void testWriteCIDDictsAndSubrs() throws IOException {
+        OTFSubSetFile subSetFile = new OTFSubSetFile() {
+            public void readFont(FontFileReader in, String embeddedName, MultiByteFont mbFont) throws IOException {
+                output = new byte[128];
+                cffReader = makeCFFDataReader();
+                fdSubrs = new ArrayList<List<byte[]>>();
+                fdSubrs.add(new ArrayList<byte[]>());
+                writeCIDDictsAndSubrs(Arrays.asList(0));
+            }
+        };
+        subSetFile.readFont(null, null, (MultiByteFont) null);
+
+        ByteArrayInputStream is = new ByteArrayInputStream(subSetFile.getFontSubset());
+        is.skip(1);
+        Assert.assertEquals(is.read(), 247);
+        Assert.assertEquals(is.read(), 0);
+        final int sizeOfPrivateDictByteData = 108;
+        is.skip(sizeOfPrivateDictByteData - 3);
+        is.skip(2); //start index
+        Assert.assertEquals(is.read(), 1);
+    }
 }