Browse Source

FOP-2737: OTF subsetting: Resize privateDict to fit op size

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1811062 13f79535-47bb-0310-9956-ffa450edef68
tags/fop-2_3
Simon Steiner 6 years ago
parent
commit
6f8ace54a8

+ 58
- 41
fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java View 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);

+ 56
- 18
fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java View 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);
}
}

Loading…
Cancel
Save