diff options
author | Glenn Adams <gadams@apache.org> | 2016-03-16 19:19:24 +0000 |
---|---|---|
committer | Glenn Adams <gadams@apache.org> | 2016-03-16 19:19:24 +0000 |
commit | d01662c1739adc090ff03ecd08f6631953e66ccc (patch) | |
tree | bdf8eb73ac585950f0d92c0315bbb7549d526f76 | |
parent | 091049e90edd8d3adc731aedab4319fcdbe8f49d (diff) | |
parent | 2b3e761a15723dc5be98a2fb773c947c036a8e60 (diff) | |
download | xmlgraphics-fop-maven.tar.gz xmlgraphics-fop-maven.zip |
Merge ^/xmlgraphics/fop/trunk.maven
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/maven@1735284 13f79535-47bb-0310-9956-ffa450edef68
8 files changed, 374 insertions, 179 deletions
@@ -78,6 +78,18 @@ FOP. Otherwise, please follow the instructions found here: http://xmlgraphics.apache.org/fop/stable/compiling.html +[TEMPORARY] + +Until the above referenced instructions are updated, one of the following +may be used to build FOP: + +Building with Maven (preferred) + +% mvn clean install + +Building with Ant (deprecated) + +% ant -f fop/build.xml clean all How do I run FOP? ----------------- @@ -89,10 +101,25 @@ command-line. For more details, see: http://xmlgraphics.apache.org/fop/stable/running.html +[TEMPORARY] + +Note that FOP command line scripts and build results from ant will be found +under the ./fop sub-directory. + ============================================================================== RELEASE NOTES ============================================================================== +Version 2.2 (Forthcoming) +=========== + +Major Changes in Version 2.2 +---------------------------- + +* Transition from Ant to Maven Build Process + +This release also contains a number of bug fixes. + Version 2.1 =========== diff --git a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OFFontLoader.java b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OFFontLoader.java index 7c8774933..1585b0d2e 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OFFontLoader.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OFFontLoader.java @@ -105,7 +105,7 @@ public class OFFontLoader extends FontLoader { FontFileReader reader = new FontFileReader(in); String header = readHeader(reader); boolean isCFF = header.equals("OTTO"); - OpenFont otf = (isCFF) ? new OTFFile() : new TTFFile(useKerning, useAdvanced); + OpenFont otf = (isCFF) ? new OTFFile(useKerning, useAdvanced) : new TTFFile(useKerning, useAdvanced); boolean supported = otf.readFont(reader, header, ttcFontName); if (!supported) { throw new IOException("The font does not have a Unicode cmap table: " + fontFileURI); diff --git a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFFile.java b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFFile.java index 1ac7a565e..9e6fed9d4 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFFile.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFFile.java @@ -37,6 +37,11 @@ public class OTFFile extends OpenFont { protected CFFFont fileFont; public OTFFile() throws IOException { + this(true, false); + } + + public OTFFile(boolean useKerning, boolean useAdvanced) throws IOException { + super(useKerning, useAdvanced); checkForFontbox(); } 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 2c083add2..a19399247 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 @@ -31,6 +31,9 @@ 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; + import org.apache.fontbox.cff.CFFStandardString; import org.apache.fontbox.cff.encoding.CFFEncoding; @@ -105,6 +108,8 @@ public class OTFSubSetFile extends OTFFile { private static final int LOCAL_SUBROUTINE = 10; /** The operator used to identify a global subroutine reference */ private static final int GLOBAL_SUBROUTINE = 29; + /** The parser used to parse type2 charstring */ + private Type2Parser type2Parser; public OTFSubSetFile() throws IOException { super(); @@ -120,9 +125,9 @@ public class OTFSubSetFile extends OTFFile { * Reads and creates a subset of the font. * * @param in FontFileReader to read from - * @param name Name to be checked for in the font file + * @param embeddedName Name to be checked for in the font file * @param header The header of the font file - * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and + * @param usedGlyphs 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 */ @@ -242,8 +247,8 @@ public class OTFSubSetFile extends OTFFile { protected List<Integer> storeFDStrings(List<Integer> uniqueNewRefs) throws IOException { ArrayList<Integer> fontNameSIDs = new ArrayList<Integer>(); List<FontDict> fdFonts = cffReader.getFDFonts(); - for (int i = 0; i < uniqueNewRefs.size(); i++) { - FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i)); + for (Integer uniqueNewRef : uniqueNewRefs) { + FontDict fdFont = fdFonts.get(uniqueNewRef); byte[] fdFontByteData = fdFont.getByteData(); Map<String, DICTEntry> fdFontDict = cffReader.parseDictData(fdFontByteData); fontNameSIDs.add(stringIndexData.size() + NUM_STANDARD_STRINGS); @@ -254,8 +259,8 @@ public class OTFSubSetFile extends OTFFile { } protected void writeBytes(byte[] out) { - for (int i = 0; i < out.length; i++) { - writeByte(out[i]); + for (byte anOut : out) { + writeByte(anOut); } } @@ -317,7 +322,7 @@ public class OTFSubSetFile extends OTFFile { cidEntryByteData = updateOffset(cidEntryByteData, dictEntry.getOperandLengths().get(0), dictEntry.getOperandLengths().get(1), sidBStringIndex); cidEntryByteData = updateOffset(cidEntryByteData, dictEntry.getOperandLengths().get(0) - + dictEntry.getOperandLengths().get(1), dictEntry.getOperandLengths().get(2), 139); + + dictEntry.getOperandLengths().get(1), dictEntry.getOperandLengths().get(2), 0); writeBytes(cidEntryByteData); } @@ -335,7 +340,7 @@ public class OTFSubSetFile extends OTFFile { } byte[] newDictEntry = createNewRef(stringIndexData.size() + 390, dictEntry.getOperator(), - dictEntry.getOperandLength()); + dictEntry.getOperandLength(), true); writeBytes(newDictEntry); } @@ -356,7 +361,8 @@ public class OTFSubSetFile extends OTFFile { } } else { int index = sid - NUM_STANDARD_STRINGS; - if (index <= cffReader.getStringIndex().getNumObjects()) { + //index is 0 based, should use < not <= + if (index < cffReader.getStringIndex().getNumObjects()) { if (mbFont != null) { mbFont.mapUsedGlyphName(subsetGlyphs.get(gid), new String(cffReader.getStringIndex().getValue(index))); @@ -416,19 +422,22 @@ public class OTFSubSetFile extends OTFFile { subsetFDSelect = new LinkedHashMap<Integer, FDIndexReference>(); List<List<Integer>> foundLocalUniques = new ArrayList<List<Integer>>(); - for (int i = 0; i < uniqueGroups.size(); i++) { + for (Integer uniqueGroup1 : uniqueGroups) { foundLocalUniques.add(new ArrayList<Integer>()); } + Map<Integer, Integer> gidHintMaskLengths = new HashMap<Integer, Integer>(); for (int gid : subsetGlyphs.keySet()) { int group = subsetGroups.get(gid); localIndexSubr = cffReader.getFDFonts().get(group).getLocalSubrData(); localUniques = foundLocalUniques.get(uniqueGroups.indexOf(subsetGroups.get(gid))); + type2Parser = new Type2Parser(); FDIndexReference newFDReference = new FDIndexReference( uniqueGroups.indexOf(subsetGroups.get(gid)), subsetGroups.get(gid)); subsetFDSelect.put(subsetGlyphs.get(gid), newFDReference); byte[] data = charStringsIndex.getValue(gid); preScanForSubsetIndexSize(data); + gidHintMaskLengths.put(gid, type2Parser.getMaskLength()); } //Create the two lists which are to store the local and global subroutines @@ -439,11 +448,11 @@ public class OTFSubSetFile extends OTFFile { globalUniques.clear(); localUniques = null; - for (int l = 0; l < foundLocalUniques.size(); l++) { + for (List<Integer> foundLocalUnique : foundLocalUniques) { fdSubrs.add(new ArrayList<byte[]>()); } List<List<Integer>> foundLocalUniquesB = new ArrayList<List<Integer>>(); - for (int k = 0; k < uniqueGroups.size(); k++) { + for (Integer uniqueGroup : uniqueGroups) { foundLocalUniquesB.add(new ArrayList<Integer>()); } for (Integer gid : subsetGlyphs.keySet()) { @@ -454,6 +463,8 @@ public class OTFSubSetFile extends OTFFile { subsetLocalIndexSubr = fdSubrs.get(subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex()); subsetLocalSubrCount = foundLocalUniques.get(subsetFDSelect.get(subsetGlyphs.get(gid)) .getNewFDIndex()).size(); + type2Parser = new Type2Parser(); + type2Parser.setMaskLength(gidHintMaskLengths.get(gid)); data = readCharStringData(data, subsetLocalSubrCount); subsetCharStringsIndex.add(data); } @@ -490,13 +501,9 @@ public class OTFSubSetFile extends OTFFile { privateDictOffsets.add(privateDictOffset); byte[] fdPrivateDictByteData = curFDFont.getPrivateDictData(); if (fdPrivateDict.get("Subrs") != null) { - int encodingValue = 0; - if (fdPrivateDict.get("Subrs").getOperandLength() == 1) { - encodingValue = 139; - } fdPrivateDictByteData = updateOffset(fdPrivateDictByteData, fdPrivateDict.get("Subrs").getOffset(), fdPrivateDict.get("Subrs").getOperandLength(), - fdPrivateDictByteData.length + encodingValue); + fdPrivateDictByteData.length); } writeBytes(fdPrivateDictByteData); writeIndex(fdSubrs.get(i)); @@ -515,8 +522,8 @@ public class OTFSubSetFile extends OTFFile { writeByte(1); //First offset int count = 1; - for (int i = 0; i < uniqueNewRefs.size(); i++) { - FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i)); + for (Integer uniqueNewRef : uniqueNewRefs) { + FontDict fdFont = fdFonts.get(uniqueNewRef); count += fdFont.getByteData().length; writeByte(count); } @@ -586,10 +593,12 @@ public class OTFSubSetFile extends OTFFile { localUniques = new ArrayList<Integer>(); globalUniques = new ArrayList<Integer>(); - + Map<Integer, Integer> gidHintMaskLengths = new HashMap<Integer, Integer>(); for (int gid : subsetGlyphs.keySet()) { + type2Parser = new Type2Parser(); byte[] data = charStringsIndex.getValue(gid); preScanForSubsetIndexSize(data); + gidHintMaskLengths.put(gid, type2Parser.getMaskLength()); } //Store the size of each subset index and clear the unique arrays @@ -600,44 +609,153 @@ public class OTFSubSetFile extends OTFFile { for (int gid : subsetGlyphs.keySet()) { byte[] data = charStringsIndex.getValue(gid); + type2Parser = new Type2Parser(); //Retrieve modified char string data and fill local / global subroutine arrays + type2Parser.setMaskLength(gidHintMaskLengths.get(gid)); data = readCharStringData(data, subsetLocalSubrCount); subsetCharStringsIndex.add(data); } } + static class Type2Parser { + /** + * logging instance + */ + protected Log log = LogFactory.getLog(Type2Parser.class); + + private ArrayList<BytesNumber> stack = new ArrayList<BytesNumber>(); + private int hstemCount; + private int vstemCount; + private int lastOp = -1; + private int maskLength = -1; + + public void pushOperand(BytesNumber v) { + stack.add(v); + } + + public BytesNumber popOperand() { + return stack.remove(stack.size() - 1); + } + + public void clearStack() { + stack.clear(); + } + + public int[] getOperands(int numbers) { + int[] ret = new int[numbers]; + while (numbers > 0) { + numbers--; + ret[numbers] = this.popOperand().getNumber(); + } + return ret; + } + + public void setMaskLength(int maskLength) { + this.maskLength = maskLength; + } + + public int getMaskLength() { + // The number of data bytes for mask is exactly the number needed, one + // bit per hint, to reference the number of stem hints declared + // at the beginning of the charstring program. + if (maskLength > 0) { + return maskLength; + } + return 1 + (hstemCount + vstemCount - 1) / 8; + } + + public int exec(int b0, byte[] data, int dataPos) { + int posDelta = 0; + if ((b0 >= 0 && b0 <= 27) || (b0 >= 29 && b0 <= 31)) { + if (b0 == 12) { + dataPos += 1; + log.warn("May not guess the operand count correctly."); + posDelta = 1; + } else if (b0 == 1 || b0 == 18) { + // hstem(hm) operator + hstemCount += stack.size() / 2; + clearStack(); + } else if (b0 == 19 || b0 == 20) { + if (lastOp == 1 || lastOp == 18) { + //If hstem and vstem hints are both declared at the beginning of + //a charstring, and this sequence is followed directly by the + //hintmask or cntrmask operators, the vstem hint operator need + //not be included. + vstemCount += stack.size() / 2; + } + clearStack(); + posDelta = getMaskLength(); + } else if (b0 == 3 || b0 == 23) { + // vstem(hm) operator + vstemCount += stack.size() / 2; + clearStack(); + } + if (b0 != 11 && b0 != 12) { + lastOp = b0; + } + } else if (b0 == 28 || (b0 >= 32 && b0 <= 255)) { + BytesNumber operand = readNumber(b0, data, dataPos); + pushOperand(operand); + posDelta = operand.getNumBytes() - 1; + } else { + throw new UnsupportedOperationException("Operator:" + b0 + " is not supported"); + } + return posDelta; + } + + private BytesNumber readNumber(int b0, byte[] input, int curPos) { + if (b0 == 28) { + int b1 = input[curPos + 1] & 0xff; + int b2 = input[curPos + 2] & 0xff; + return new BytesNumber((int) (short) (b1 << 8 | b2), 3); + } else if (b0 >= 32 && b0 <= 246) { + return new BytesNumber(b0 - 139, 1); + } else if (b0 >= 247 && b0 <= 250) { + int b1 = input[curPos + 1] & 0xff; + return new BytesNumber((b0 - 247) * 256 + b1 + 108, 2); + } else if (b0 >= 251 && b0 <= 254) { + int b1 = input[curPos + 1] & 0xff; + return new BytesNumber(-(b0 - 251) * 256 - b1 - 108, 2); + } else if (b0 == 255) { + int b1 = input[curPos + 1] & 0xff; + int b2 = input[curPos + 2] & 0xff; + int b3 = input[curPos + 3] & 0xff; + int b4 = input[curPos + 4] & 0xff; + return new BytesNumber((b1 << 24 | b2 << 16 | b3 << 8 | b4), 5); + } else { + throw new IllegalArgumentException(); + } + } + } private void preScanForSubsetIndexSize(byte[] data) throws IOException { boolean hasLocalSubroutines = localIndexSubr != null && localIndexSubr.getNumObjects() > 0; boolean hasGlobalSubroutines = globalIndexSubr != null && globalIndexSubr.getNumObjects() > 0; - BytesNumber operand = new BytesNumber(-1, -1); for (int dataPos = 0; dataPos < data.length; dataPos++) { int b0 = data[dataPos] & 0xff; if (b0 == LOCAL_SUBROUTINE && hasLocalSubroutines) { - int subrNumber = getSubrNumber(localIndexSubr.getNumObjects(), operand.getNumber()); - + int subrNumber = getSubrNumber(localIndexSubr.getNumObjects(), type2Parser.popOperand().getNumber()); if (!localUniques.contains(subrNumber) && subrNumber < localIndexSubr.getNumObjects()) { localUniques.add(subrNumber); + } + if (subrNumber < localIndexSubr.getNumObjects()) { byte[] subr = localIndexSubr.getValue(subrNumber); preScanForSubsetIndexSize(subr); + } else { + throw new IllegalArgumentException("callsubr out of range"); } - operand.clearNumber(); } else if (b0 == GLOBAL_SUBROUTINE && hasGlobalSubroutines) { - int subrNumber = getSubrNumber(globalIndexSubr.getNumObjects(), operand.getNumber()); - + int subrNumber = getSubrNumber(globalIndexSubr.getNumObjects(), type2Parser.popOperand().getNumber()); if (!globalUniques.contains(subrNumber) && subrNumber < globalIndexSubr.getNumObjects()) { globalUniques.add(subrNumber); + } + if (subrNumber < globalIndexSubr.getNumObjects()) { byte[] subr = globalIndexSubr.getValue(subrNumber); preScanForSubsetIndexSize(subr); + } else { + throw new IllegalArgumentException("callgsubr out of range"); } - operand.clearNumber(); - } else if ((b0 >= 0 && b0 <= 27) || (b0 >= 29 && b0 <= 31)) { - operand.clearNumber(); - if (b0 == 19 || b0 == 20) { - dataPos += 1; - } - } else if (b0 == 28 || (b0 >= 32 && b0 <= 255)) { - operand = readNumber(b0, data, dataPos); - dataPos += operand.getNumBytes() - 1; + } else { + dataPos += type2Parser.exec(b0, data, dataPos); } } } @@ -650,10 +768,10 @@ public class OTFSubSetFile extends OTFFile { private byte[] readCharStringData(byte[] data, int subsetLocalSubrCount) throws IOException { boolean hasLocalSubroutines = localIndexSubr != null && localIndexSubr.getNumObjects() > 0; boolean hasGlobalSubroutines = globalIndexSubr != null && globalIndexSubr.getNumObjects() > 0; - BytesNumber operand = new BytesNumber(-1, -1); for (int dataPos = 0; dataPos < data.length; dataPos++) { int b0 = data[dataPos] & 0xff; if (b0 == 10 && hasLocalSubroutines) { + BytesNumber operand = type2Parser.popOperand(); int subrNumber = getSubrNumber(localIndexSubr.getNumObjects(), operand.getNumber()); int newRef = getNewRefForReference(subrNumber, localUniques, localIndexSubr, subsetLocalIndexSubr, @@ -665,9 +783,8 @@ public class OTFSubSetFile extends OTFFile { dataPos -= data.length - newData.length; data = newData; } - - operand.clearNumber(); } else if (b0 == 29 && hasGlobalSubroutines) { + BytesNumber operand = type2Parser.popOperand(); int subrNumber = getSubrNumber(globalIndexSubr.getNumObjects(), operand.getNumber()); int newRef = getNewRefForReference(subrNumber, globalUniques, globalIndexSubr, subsetGlobalIndexSubr, @@ -679,16 +796,8 @@ public class OTFSubSetFile extends OTFFile { dataPos -= (data.length - newData.length); data = newData; } - - operand.clearNumber(); - } else if ((b0 >= 0 && b0 <= 27) || (b0 >= 29 && b0 <= 31)) { - operand.clearNumber(); - if (b0 == 19) { - dataPos += 1; - } - } else if (b0 == 28 || (b0 >= 32 && b0 <= 255)) { - operand = readNumber(b0, data, dataPos); - dataPos += operand.getNumBytes() - 1; + } else { + dataPos += type2Parser.exec(b0, data, dataPos); } } @@ -698,18 +807,16 @@ public class OTFSubSetFile extends OTFFile { private int getNewRefForReference(int subrNumber, List<Integer> uniquesArray, CFFIndexData indexSubr, List<byte[]> subsetIndexSubr, int subrCount) throws IOException { - int newRef = -1; + int newRef; if (!uniquesArray.contains(subrNumber)) { if (subrNumber < indexSubr.getNumObjects()) { byte[] subr = indexSubr.getValue(subrNumber); subr = readCharStringData(subr, subrCount); - if (!uniquesArray.contains(subrNumber)) { - uniquesArray.add(subrNumber); - subsetIndexSubr.add(subr); - newRef = subsetIndexSubr.size() - 1; - } else { - newRef = uniquesArray.indexOf(subrNumber); - } + uniquesArray.add(subrNumber); + subsetIndexSubr.add(subr); + newRef = subsetIndexSubr.size() - 1; + } else { + throw new IllegalArgumentException("subrNumber out of range"); } } else { newRef = uniquesArray.indexOf(subrNumber); @@ -737,7 +844,7 @@ public class OTFSubSetFile extends OTFFile { System.arraycopy(currentData, 0, preBytes, 0, startRef); int newBias = getBias(fullSubsetIndexSize); int newRef = curSubsetIndexSize - newBias; - byte[] newRefBytes = createNewRef(newRef, operatorCode, -1); + byte[] newRefBytes = createNewRef(newRef, operatorCode, -1, false); newData = concatArray(preBytes, newRefBytes); byte[] postBytes = new byte[currentData.length - (startRef + length)]; System.arraycopy(currentData, startRef + length, postBytes, 0, @@ -745,19 +852,27 @@ public class OTFSubSetFile extends OTFFile { return concatArray(newData, postBytes); } - public static byte[] createNewRef(int newRef, int[] operatorCode, int forceLength) { + public static byte[] createNewRef(int newRef, int[] operatorCode, int forceLength, boolean isDict) { byte[] newRefBytes; int sizeOfOperator = operatorCode.length; - if ((forceLength == -1 && newRef <= 107) || forceLength == 1) { + if ((forceLength == -1 && newRef >= -107 && newRef <= 107) || forceLength == 1) { newRefBytes = new byte[1 + sizeOfOperator]; //The index values are 0 indexed newRefBytes[0] = (byte)(newRef + 139); for (int i = 0; i < operatorCode.length; i++) { newRefBytes[1 + i] = (byte)operatorCode[i]; } - } else if ((forceLength == -1 && newRef <= 1131) || forceLength == 2) { + } else if ((forceLength == -1 && newRef >= -1131 && newRef <= 1131) || forceLength == 2) { newRefBytes = new byte[2 + sizeOfOperator]; - if (newRef <= 363) { + if (newRef <= -876) { + newRefBytes[0] = (byte)254; + } else if (newRef <= -620) { + newRefBytes[0] = (byte)253; + } else if (newRef <= -364) { + newRefBytes[0] = (byte)252; + } else if (newRef <= -108) { + newRefBytes[0] = (byte)251; + } else if (newRef <= 363) { newRefBytes[0] = (byte)247; } else if (newRef <= 619) { newRefBytes[0] = (byte)248; @@ -766,11 +881,15 @@ public class OTFSubSetFile extends OTFFile { } else { newRefBytes[0] = (byte)250; } - newRefBytes[1] = (byte)(newRef - 108); + if (newRef > 0) { + newRefBytes[1] = (byte)(newRef - 108); + } else { + newRefBytes[1] = (byte)(-newRef - 108); + } for (int i = 0; i < operatorCode.length; i++) { newRefBytes[2 + i] = (byte)operatorCode[i]; } - } else if ((forceLength == -1 && newRef <= 32767) || forceLength == 3) { + } else if ((forceLength == -1 && newRef >= -32768 && newRef <= 32767) || forceLength == 3) { newRefBytes = new byte[3 + sizeOfOperator]; newRefBytes[0] = 28; newRefBytes[1] = (byte)(newRef >> 8); @@ -780,7 +899,11 @@ public class OTFSubSetFile extends OTFFile { } } else { newRefBytes = new byte[5 + sizeOfOperator]; - newRefBytes[0] = 29; + if (isDict) { + newRefBytes[0] = 29; + } else { + newRefBytes[0] = (byte)255; + } newRefBytes[1] = (byte)(newRef >> 24); newRefBytes[2] = (byte)(newRef >> 16); newRefBytes[3] = (byte)(newRef >> 8); @@ -806,16 +929,18 @@ public class OTFSubSetFile extends OTFFile { //2 byte number of items this.writeCard16(dataArray.size()); //Offset Size: 1 byte = 256, 2 bytes = 65536 etc. - int totLength = 0; - for (int i = 0; i < dataArray.size(); i++) { - totLength += dataArray.get(i).length; + //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 = 1; - if (totLength <= (1 << 8)) { + if (totLength < (1 << 8)) { offSize = 1; - } else if (totLength <= (1 << 16)) { + } else if (totLength < (1 << 16)) { offSize = 2; - } else if (totLength <= (1 << 24)) { + } else if (totLength < (1 << 24)) { offSize = 3; } else { offSize = 4; @@ -860,34 +985,12 @@ public class OTFSubSetFile extends OTFFile { throw new AssertionError("Offset Size was not an expected value."); } } - for (int i = 0; i < dataArray.size(); i++) { - writeBytes(dataArray.get(i)); + for (byte[] aDataArray : dataArray) { + writeBytes(aDataArray); } return hdrTotal + total; } - private BytesNumber readNumber(int b0, byte[] input, int curPos) throws IOException { - if (b0 == 28) { - int b1 = input[curPos + 1] & 0xff; - int b2 = input[curPos + 2] & 0xff; - return new BytesNumber(Integer.valueOf((short) (b1 << 8 | b2)), 3); - } else if (b0 >= 32 && b0 <= 246) { - return new BytesNumber(Integer.valueOf(b0 - 139), 1); - } else if (b0 >= 247 && b0 <= 250) { - int b1 = input[curPos + 1] & 0xff; - return new BytesNumber(Integer.valueOf((b0 - 247) * 256 + b1 + 108), 2); - } else if (b0 >= 251 && b0 <= 254) { - int b1 = input[curPos + 1] & 0xff; - return new BytesNumber(Integer.valueOf(-(b0 - 251) * 256 - b1 - 108), 2); - } else if (b0 == 255) { - int b1 = input[curPos + 1] & 0xff; - int b2 = input[curPos + 2] & 0xff; - return new BytesNumber(Integer.valueOf((short)(b1 << 8 | b2)), 5); - } else { - throw new IllegalArgumentException(); - } - } - /** * A class used to store the last number operand and also it's size in bytes */ @@ -980,13 +1083,8 @@ public class OTFSubSetFile extends OTFFile { DICTEntry subroutines = privateDICT.get("Subrs"); if (subroutines != null) { int oldLocalSubrOffset = privateDictOffset + subroutines.getOffset(); - //Value needs to be converted to -139 etc. - int encodeValue = 0; - if (subroutines.getOperandLength() == 1) { - encodeValue = 139; - } output = updateOffset(output, oldLocalSubrOffset, subroutines.getOperandLength(), - (localIndexOffset - privateDictOffset) + encodeValue); + (localIndexOffset - privateDictOffset)); } } } @@ -1033,10 +1131,18 @@ public class OTFSubSetFile extends OTFFile { protected byte[] updateOffset(byte[] out, int position, int length, int replacement) { switch (length) { case 1: - out[position] = (byte)(replacement & 0xFF); + out[position] = (byte)(replacement + 139); break; case 2: - if (replacement <= 363) { + if (replacement <= -876) { + out[position] = (byte)254; + } else if (replacement <= -620) { + out[position] = (byte)253; + } else if (replacement <= -364) { + out[position] = (byte)252; + } else if (replacement <= -108) { + out[position] = (byte)251; + } else if (replacement <= 363) { out[position] = (byte)247; } else if (replacement <= 619) { out[position] = (byte)248; @@ -1045,7 +1151,11 @@ public class OTFSubSetFile extends OTFFile { } else { out[position] = (byte)250; } - out[position + 1] = (byte)(replacement - 108); + if (replacement > 0) { + out[position + 1] = (byte)(replacement - 108); + } else { + out[position + 1] = (byte)(-replacement - 108); + } break; case 3: out[position] = (byte)28; diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java index 075199e5e..4d798cc19 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java @@ -392,22 +392,20 @@ public class LineLayoutManager extends InlineStackingLayoutManager addedPositions = 0; } - if (log.isWarnEnabled()) { - int lack = difference + bestActiveNode.availableShrink; - // if this LLM is nested inside a BlockContainerLayoutManager that is constraining - // the available width and thus responsible for the overflow then we do not issue - // warning event here and instead let the BCLM handle that at a later stage - if (lack < 0 && !handleOverflow(-lack)) { - InlineLevelEventProducer eventProducer - = InlineLevelEventProducer.Provider.get( - getFObj().getUserAgent().getEventBroadcaster()); - if (curChildLM.getFObj() == null) { - eventProducer.lineOverflows(this, getFObj().getName(), bestActiveNode.line, - -lack, getFObj().getLocator()); - } else { - eventProducer.lineOverflows(this, curChildLM.getFObj().getName(), bestActiveNode.line, - -lack, curChildLM.getFObj().getLocator()); - } + int lack = difference + bestActiveNode.availableShrink; + // if this LLM is nested inside a BlockContainerLayoutManager that is constraining + // the available width and thus responsible for the overflow then we do not issue + // warning event here and instead let the BCLM handle that at a later stage + if (lack < 0 && !handleOverflow(-lack)) { + InlineLevelEventProducer eventProducer + = InlineLevelEventProducer.Provider.get( + getFObj().getUserAgent().getEventBroadcaster()); + if (curChildLM.getFObj() == null) { + eventProducer.lineOverflows(this, getFObj().getName(), bestActiveNode.line, + -lack, getFObj().getLocator()); + } else { + eventProducer.lineOverflows(this, curChildLM.getFObj().getName(), bestActiveNode.line, + -lack, curChildLM.getFObj().getLocator()); } } diff --git a/fop-core/src/test/java/org/apache/fop/fonts/FOPFontsTestSuite.java b/fop-core/src/test/java/org/apache/fop/fonts/FOPFontsTestSuite.java index 8e1a0040d..205cffb18 100644 --- a/fop-core/src/test/java/org/apache/fop/fonts/FOPFontsTestSuite.java +++ b/fop-core/src/test/java/org/apache/fop/fonts/FOPFontsTestSuite.java @@ -23,7 +23,9 @@ import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; +import org.apache.fop.fonts.cff.CFFDataReaderTestCase; import org.apache.fop.fonts.truetype.FontFileReaderTestCase; +import org.apache.fop.fonts.truetype.OTFSubSetFileTestCase; import org.apache.fop.fonts.truetype.TTFFileTestCase; import org.apache.fop.fonts.truetype.TTFSubSetFileTestCase; import org.apache.fop.fonts.truetype.TTFTableNameTestCase; @@ -37,6 +39,8 @@ import org.apache.fop.fonts.truetype.TTFTableNameTestCase; FontFileReaderTestCase.class, TTFFileTestCase.class, TTFSubSetFileTestCase.class, - TTFTableNameTestCase.class }) + TTFTableNameTestCase.class, + CFFDataReaderTestCase.class, + OTFSubSetFileTestCase.class }) public final class FOPFontsTestSuite { } diff --git a/fop-core/src/test/java/org/apache/fop/fonts/cff/CFFDataReaderTestCase.java b/fop-core/src/test/java/org/apache/fop/fonts/cff/CFFDataReaderTestCase.java index 97ea5c52b..2e112de7b 100644 --- a/fop-core/src/test/java/org/apache/fop/fonts/cff/CFFDataReaderTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/fonts/cff/CFFDataReaderTestCase.java @@ -60,43 +60,43 @@ public class CFFDataReaderTestCase { byte[] testDictData = new byte[0]; //Version testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - 392, new int[] { 0 }, -1)); + 392, new int[] { 0 }, -1, true)); //Notice testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - 393, new int[] { 1 }, -1)); + 393, new int[] { 1 }, -1, true)); //Copyright testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - 394, new int[] { 12, 0 }, -1)); + 394, new int[] { 12, 0 }, -1, true)); //FullName testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - 395, new int[] { 2 }, -1)); + 395, new int[] { 2 }, -1, true)); //FamilyName testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - 396, new int[] { 3 }, -1)); + 396, new int[] { 3 }, -1, true)); //Weight testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - 397, new int[] { 4 }, -1)); + 397, new int[] { 4 }, -1, true)); //isFixedPitch (boolean = false) testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - 0, new int[] { 12, 1 }, -1)); + 0, new int[] { 12, 1 }, -1, true)); //FontBBox testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - -50, new int[0], -1)); + -50, new int[0], -1, true)); testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - -40, new int[0], -1)); + -40, new int[0], -1, true)); testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - 100, new int[0], -1)); + 100, new int[0], -1, true)); testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - 120, new int[] { 5 }, -1)); + 120, new int[] { 5 }, -1, true)); //charset testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - 1234, new int[] { 15 }, -1)); + 1234, new int[] { 15 }, -1, true)); //CharStrings testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - 3654, new int[] { 17 }, -1)); + 3654, new int[] { 17 }, -1, true)); //Private testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( - 11454, new int[] { 18 }, -1)); + 11454, new int[] { 18 }, -1, true)); return testDictData; } 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 fecb1e9f1..2048bcf46 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 @@ -21,7 +21,6 @@ package org.apache.fop.fonts.truetype; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -44,7 +43,6 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { CFFDataReader cffReaderSourceSans; private OTFSubSetFile sourceSansSubset; private byte[] sourceSansData; - CFFDataReader cffReaderHeitiStd; /** * Initialises the test by creating the font subset. A CFFDataReader is @@ -109,14 +107,84 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { byte[] origCharData = origCharStringData.get(origCharStringData.keySet().toArray( new String[0])[i]); byte[] charData = charStrings.getValue(i); - List<BytesNumber> origOperands = getFullCharString(origCharData, origCFF); - List<BytesNumber> subsetOperands = getFullCharString(charData, subsetCFF); + List<BytesNumber> origOperands = getFullCharString(new Context(), origCharData, origCFF); + List<BytesNumber> subsetOperands = getFullCharString(new Context(), charData, subsetCFF); for (int j = 0; j < origOperands.size(); j++) { - assertTrue(origOperands.get(j).equals(subsetOperands.get(j))); + assertTrue(origOperands.get(j).equals(subsetOperands.get(j))); } } } + static class Context { + private ArrayList<BytesNumber> operands = new ArrayList<BytesNumber>(); + private ArrayList<BytesNumber> stack = new ArrayList<BytesNumber>(); + private int hstemCount; + private int vstemCount; + private int lastOp = -1; + private int maskLength = -1; + + public void pushOperand(BytesNumber v) { + operands.add(v); + if (v instanceof Operator) { + if (v.getNumber() != 11 && v.getNumber() != 12) { + lastOp = v.getNumber(); + } + } else { + stack.add(v); + } + } + + public BytesNumber popOperand() { + operands.remove(operands.size() - 1); + return stack.remove(stack.size() - 1); + } + + public BytesNumber lastOperand() { + return operands.get(operands.size() - 1); + } + + public void clearStack() { + stack.clear(); + } + + public int getMaskLength() { + // The number of data bytes for mask is exactly the number needed, one + // bit per hint, to reference the number of stem hints declared + // at the beginning of the charstring program. + if (maskLength > 0) { + return maskLength; + } + return 1 + (hstemCount + vstemCount - 1) / 8; + } + + public List<BytesNumber> getFullOperandsList() { + return operands; + } + + public void countHstem() { + // hstem(hm) operator + hstemCount += stack.size() / 2; + clearStack(); + } + + public void countVstem() { + // vstem(hm) operator + vstemCount += stack.size() / 2; + clearStack(); + } + + public int calcMaskLength() { + if (lastOp == 1 || lastOp == 18) { + //If hstem and vstem hints are both declared at the beginning of + //a charstring, and this sequence is followed directly by the + //hintmask or cntrmask operators, the vstem hint operator need + //not be included. + vstemCount += stack.size() / 2; + } + clearStack(); + return getMaskLength(); + } + } /** * Recursively reads and constructs the full CharString for comparison * @param data The original byte data of the CharString @@ -124,65 +192,46 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { * @return Returns a list of parsed operands and operators * @throws IOException */ - private List<BytesNumber> getFullCharString(byte[] data, CFFDataReader cffData) throws IOException { + private List<BytesNumber> getFullCharString(Context context, byte[] data, CFFDataReader cffData) + throws IOException { CFFIndexData localIndexSubr = cffData.getLocalIndexSubr(); CFFIndexData globalIndexSubr = cffData.getGlobalIndexSubr(); boolean hasLocalSubroutines = localIndexSubr != null && localIndexSubr.getNumObjects() > 0; boolean hasGlobalSubroutines = globalIndexSubr != null && globalIndexSubr.getNumObjects() > 0; - ArrayList<BytesNumber> operands = new ArrayList<BytesNumber>(); for (int dataPos = 0; dataPos < data.length; dataPos++) { int b0 = data[dataPos] & 0xff; if (b0 == 10 && hasLocalSubroutines) { int subrNumber = getSubrNumber(localIndexSubr.getNumObjects(), - operands.get(operands.size() - 1).getNumber()); + context.popOperand().getNumber()); byte[] subr = localIndexSubr.getValue(subrNumber); - List<BytesNumber> subrOperands = getFullCharString(subr, cffData); - operands = mergeOperands(operands, subrOperands); + getFullCharString(context, subr, cffData); } else if (b0 == 29 && hasGlobalSubroutines) { int subrNumber = getSubrNumber(globalIndexSubr.getNumObjects(), - operands.get(operands.size() - 1).getNumber()); + context.popOperand().getNumber()); byte[] subr = globalIndexSubr.getValue(subrNumber); - ArrayList<BytesNumber> subrOperands = (ArrayList<BytesNumber>)getFullCharString(subr, cffData); - operands = mergeOperands(operands, subrOperands); + getFullCharString(context, subr, cffData); } else if ((b0 >= 0 && b0 <= 27) || (b0 >= 29 && b0 <= 31)) { int size = 1; int b1 = -1; if (b0 == 12) { b1 = data[dataPos++] & 0xff; size = 2; + } else if (b0 == 1 || b0 == 18) { + context.countHstem(); + } else if (b0 == 3 || b0 == 23) { + context.countVstem(); + } else if (b0 == 19 || b0 == 20) { + int length = context.calcMaskLength(); + dataPos += length; + size = length + 1; } - if (b0 == 19 || b0 == 20) { - dataPos += 1; - size = 2; - } - operands.add(new Operator(b0, size, getOperatorName(b0, b1))); + context.pushOperand(new Operator(b0, size, getOperatorName(b0, b1))); } else if (b0 == 28 || (b0 >= 32 && b0 <= 255)) { - operands.add(readNumber(b0, data, dataPos)); - dataPos += operands.get(operands.size() - 1).getNumBytes() - 1; + context.pushOperand(readNumber(b0, data, dataPos)); + dataPos += context.lastOperand().getNumBytes() - 1; } } - return operands; - } - - /** - * Merges two lists of operands. This is typically used to merge the CharString - * data with that of a parsed and referenced subroutine. - * @param charString The parsed CharString data so far - * @param subroutine The parsed elements from a subroutine - * @return Returns a merged list of both CharString and subroutine elements. - */ - private ArrayList<BytesNumber> mergeOperands(List<BytesNumber> charString, - List<BytesNumber> subroutine) { - BytesNumber[] charStringOperands = charString.toArray(new BytesNumber[0]); - BytesNumber[] subroutineOperands = subroutine.toArray(new BytesNumber[0]); - BytesNumber[] mergeData = new BytesNumber[charStringOperands.length - 1 - + subroutineOperands.length - 1]; - System.arraycopy(charStringOperands, 0, mergeData, 0, charStringOperands.length - 1); - System.arraycopy(subroutineOperands, 0, mergeData, charStringOperands.length - 1, - subroutineOperands.length - 1); - ArrayList<BytesNumber> hello = new ArrayList<BytesNumber>(); - hello.addAll(Arrays.asList(mergeData)); - return hello; + return context.getFullOperandsList(); } /** @@ -209,7 +258,9 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { } else if (b0 == 255) { int b1 = input[curPos + 1] & 0xff; int b2 = input[curPos + 2] & 0xff; - return new BytesNumber(Integer.valueOf((short)(b1 << 8 | b2)), 5); + int b3 = input[curPos + 3] & 0xff; + int b4 = input[curPos + 4] & 0xff; + return new BytesNumber(Integer.valueOf((b1 << 24 | b2 << 16 | b3 << 8 | b4)), 5); } else { throw new IllegalArgumentException(); } @@ -264,7 +315,7 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { * used for debugging purposes. See the Type 2 CharString Format specification * document (Technical Note #5177) Appendix A (Command Codes). * @param operator The operator code - * @param codeb The second byte of the operator + * @param operatorB The second byte of the operator * @return Returns the operator name. */ private String getOperatorName(int operator, int operatorB) { |