From 507202d1e748194a11ed554b23a64af3a06ea001 Mon Sep 17 00:00:00 2001 From: Simon Steiner Date: Fri, 20 Jun 2014 09:27:37 +0000 Subject: [PATCH] Add t1 merging git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_FontMerging@1604113 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/fop/fonts/truetype/OpenFont.java | 2 +- .../fop/fonts/type1/PostscriptParser.java | 2 +- .../fop/fonts/type1/Type1SubsetFile.java | 106 ++++++++---------- src/java/org/apache/fop/pdf/PDFFactory.java | 2 +- .../org/apache/fop/render/ps/PSFontUtils.java | 2 +- .../fonts/type1/Type1SubsetFileTestCase.java | 19 +--- 6 files changed, 53 insertions(+), 80 deletions(-) diff --git a/src/java/org/apache/fop/fonts/truetype/OpenFont.java b/src/java/org/apache/fop/fonts/truetype/OpenFont.java index 98fced01f..7b259b926 100644 --- a/src/java/org/apache/fop/fonts/truetype/OpenFont.java +++ b/src/java/org/apache/fop/fonts/truetype/OpenFont.java @@ -168,7 +168,7 @@ public abstract class OpenFont { protected List unicodeMappings; private int upem; // unitsPerEm from "head" table - private int nhmtx; // Number of horizontal metrics + protected int nhmtx; // Number of horizontal metrics private PostScriptVersion postScriptVersion; protected int locaFormat; /** diff --git a/src/java/org/apache/fop/fonts/type1/PostscriptParser.java b/src/java/org/apache/fop/fonts/type1/PostscriptParser.java index 05c3c6453..87ff6d609 100644 --- a/src/java/org/apache/fop/fonts/type1/PostscriptParser.java +++ b/src/java/org/apache/fop/fonts/type1/PostscriptParser.java @@ -31,7 +31,7 @@ import java.util.Scanner; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -class PostscriptParser { +public class PostscriptParser { protected static final Log LOG = LogFactory.getLog(PostscriptParser.class); /* Patterns used to identify Postscript elements */ diff --git a/src/java/org/apache/fop/fonts/type1/Type1SubsetFile.java b/src/java/org/apache/fop/fonts/type1/Type1SubsetFile.java index 6e6a7f26b..7b05e567d 100644 --- a/src/java/org/apache/fop/fonts/type1/Type1SubsetFile.java +++ b/src/java/org/apache/fop/fonts/type1/Type1SubsetFile.java @@ -51,32 +51,31 @@ public class Type1SubsetFile { protected static final Log LOG = LogFactory.getLog(Type1SubsetFile.class); /* The subset list of char strings */ - private HashMap subsetCharStrings; + protected HashMap subsetCharStrings; /* The list of character names in the subset font */ - private List charNames = null; + protected List charNames = null; /* A list of unique subroutines references */ - private LinkedHashMap uniqueSubs; + protected LinkedHashMap uniqueSubs; private SingleByteFont sbfont = null; /* New line character */ - private String eol = "\n"; + protected String eol = "\n"; /* An option to determine whether the subroutines are subset */ - private boolean subsetSubroutines = true; + protected boolean subsetSubroutines = true; private byte[] fullFont; //List of parsed Postscript elements - private List headerSection; - private List mainSection; + protected List headerSection; + protected List mainSection; //Determines whether the current font uses standard encoding - private boolean standardEncoding = false; + protected boolean standardEncoding = false; //Type 1 operators private static final int OP_SEAC = 6; private static final int OP_CALLSUBR = 10; private static final int OP_CALLOTHERSUBR = 16; - public byte[] createSubset(InputStream in, SingleByteFont sbfont, - String fontPrefix) throws IOException { + public byte[] createSubset(InputStream in, SingleByteFont sbfont) throws IOException { fullFont = IOUtils.toByteArray(in); - byte[] subsetFont = createSubset(sbfont, fontPrefix, true); + byte[] subsetFont = createSubset(sbfont, true); //This should never happen but ensure that subset is shorter than original font return (subsetFont.length == 0 || subsetFont.length > fullFont.length) ? fullFont : subsetFont; @@ -84,17 +83,14 @@ public class Type1SubsetFile { /** * Creates a new subset from the given type 1 font input stream - * @param in The type 1 font to subset * @param sbfont The font object containing information such as the * characters from which to create the subset - * @param fontPrefix The prefix used in identifying the subset font - * @param allSubroutines This option will force the subset to include all + * @param subsetSubroutines This option will force the subset to include all * subroutines. * @return Returns the subset as a byte array * @throws IOException */ - private byte[] createSubset(SingleByteFont sbfont, - String fontPrefix, boolean subsetSubroutines) throws IOException { + private byte[] createSubset(SingleByteFont sbfont, boolean subsetSubroutines) throws IOException { this.subsetSubroutines = subsetSubroutines; InputStream in = new ByteArrayInputStream(fullFont); //Initialise resources used for the font creation @@ -132,8 +128,8 @@ public class Type1SubsetFile { //Process and write the main section PSElement charStrings = getElement("/CharStrings", mainSection); - int result = readMainSection(mainSection, decoded, subsetEncodingEntries, charStrings); - if (result == 0) { + boolean result = readMainSection(mainSection, decoded, subsetEncodingEntries, charStrings); + if (!result) { /* This check handles the case where a font uses a postscript method to return a * subroutine index. As there is currently no java postscript interpreter and writing * one would be very difficult it prevents us from handling this eventuality. The way @@ -142,16 +138,16 @@ public class Type1SubsetFile { uniqueSubs.clear(); subsetCharStrings.clear(); charNames.clear(); - return createSubset(sbfont, fontPrefix, false); + return createSubset(sbfont, false); } //Write header section - ByteArrayOutputStream boasHeader = writeHeader(pfbData, encoding, subsetEncodingEntries); + ByteArrayOutputStream boasHeader = writeHeader(pfbData, encoding); ByteArrayOutputStream boasMain = writeMainSection(decoded, mainSection, charStrings); byte[] mainSectionBytes = boasMain.toByteArray(); mainSectionBytes = BinaryCoder.encodeBytes(mainSectionBytes, 55665, 4); - boasMain = new ByteArrayOutputStream(); + boasMain.reset(); boasMain.write(mainSectionBytes); ByteArrayOutputStream baosTrailer = new ByteArrayOutputStream(); @@ -160,8 +156,8 @@ public class Type1SubsetFile { return stitchFont(boasHeader, boasMain, baosTrailer); } - byte[] stitchFont(ByteArrayOutputStream boasHeader, ByteArrayOutputStream boasMain, - ByteArrayOutputStream boasTrailer) throws IOException { + protected byte[] stitchFont(ByteArrayOutputStream boasHeader, ByteArrayOutputStream boasMain, + ByteArrayOutputStream boasTrailer) throws IOException { int headerLength = boasHeader.size(); int mainLength = boasMain.size(); @@ -205,7 +201,7 @@ public class Type1SubsetFile { /* If no matches are found, create a new entry for the character so * that it can be added even if it's not in the current encoding. */ if (matches.size() == 0) { - matches = new ArrayList(); + matches.clear(); if (glyph == 0) { matches.add("dup 0 /.notdef put"); } else { @@ -237,7 +233,7 @@ public class Type1SubsetFile { return subsetEncodingEntries; } - private List searchEntries(HashMap encodingEntries, int glyph) { + protected List searchEntries(HashMap encodingEntries, int glyph) { List matches = new ArrayList(); for (Entry entry : encodingEntries.entrySet()) { String tag = getEntryPart(entry.getValue(), 3); @@ -249,15 +245,13 @@ public class Type1SubsetFile { return matches; } - private ByteArrayOutputStream writeHeader(PFBData pfbData, PSElement encoding, - List subsetEncodingEntries) throws UnsupportedEncodingException, - IOException { + protected ByteArrayOutputStream writeHeader(PFBData pfbData, PSElement encoding) throws IOException { ByteArrayOutputStream boasHeader = new ByteArrayOutputStream(); boasHeader.write(pfbData.getHeaderSegment(), 0, encoding.getStartPoint() - 1); if (!standardEncoding) { //Write out the new encoding table for the subset font - String encodingArray = eol + String.format("/Encoding %d array", 256) + eol + String encodingArray = eol + "/Encoding 256 array" + eol + "0 1 255 {1 index exch /.notdef put } for" + eol; byte[] encodingDefinition = encodingArray.getBytes("ASCII"); boasHeader.write(encodingDefinition, 0, encodingDefinition.length); @@ -287,7 +281,7 @@ public class Type1SubsetFile { return boas; } - private int readMainSection(List mainSection, byte[] decoded, + private boolean readMainSection(List mainSection, byte[] decoded, List subsetEncodingEntries, PSElement charStrings) { subsetEncodingEntries.add(0, "dup 0 /.notdef put"); /* Reads and parses the charStrings section to subset the charString @@ -322,19 +316,19 @@ public class Type1SubsetFile { /* Recursively scan the charString array for subroutines and if found, copy the * entry to our subset entries and update any references. */ charStringEntry = createSubsetCharStrings(decoded, charStringEntry, subroutines, - subsetEncodingEntries, tag); + subsetEncodingEntries); } if (charStringEntry.length == 0) { - return 0; + return false; } charStringEntry = BinaryCoder.encodeBytes(charStringEntry, 4330, skipBytes); subsetCharStrings.put(tag, charStringEntry); } - return 1; + return true; } private byte[] createSubsetCharStrings(byte[] decoded, byte[] data, PSFixedArray subroutines, - List subsetEncodingEntries, String glyphName) { + List subsetEncodingEntries) { List operands = new ArrayList(); for (int i = 0; i < data.length; i++) { int cur = data[i] & 0xFF; @@ -348,11 +342,11 @@ public class Type1SubsetFile { if (uniqueSubs.get(operands.get(operands.size() - 1).getNumber()) == null) { uniqueSubs.put(operands.get(operands.size() - 1).getNumber(), new byte[0]); data = addSubroutine(subroutines, operands, decoded, subsetEncodingEntries, - glyphName, data, i, 1, -1, operands.get( + data, i, 1, -1, operands.get( operands.size() - 1).getNumber()); } else { data = addSubroutine(subroutines, operands, decoded, subsetEncodingEntries, - glyphName, data, i, 1, getSubrIndex(operands.get( + data, i, 1, getSubrIndex(operands.get( operands.size() - 1).getNumber()), operands.get( operands.size() - 1).getNumber()); } @@ -390,7 +384,7 @@ public class Type1SubsetFile { return new byte[0]; } data = addSubroutine(subroutines, operands, decoded, subsetEncodingEntries, - glyphName, data, i, 2, -1, operands.get(0).getNumber()); + data, i, 2, -1, operands.get(0).getNumber()); } } if (data.length == 0) { @@ -431,14 +425,14 @@ public class Type1SubsetFile { } private byte[] addSubroutine(PSFixedArray subroutines, List operands, byte[] decoded, - List subsetEncodingEntries, String glyphName, byte[] data, int i, int opLength, + List subsetEncodingEntries, byte[] data, int i, int opLength, int existingSubrRef, int subrID) { if (existingSubrRef == -1) { int[] subrData = subroutines.getBinaryEntryByIndex(subrID); byte[] subroutine = getBinaryEntry(subrData, decoded); subroutine = BinaryCoder.decodeBytes(subroutine, 4330, 4); subroutine = createSubsetCharStrings(decoded, subroutine, subroutines, - subsetEncodingEntries, glyphName); + subsetEncodingEntries); if (subroutine.length == 0) { return new byte[0]; } @@ -451,8 +445,8 @@ public class Type1SubsetFile { return data; } - private ByteArrayOutputStream writeMainSection(byte[] decoded, List mainSection, - PSElement charStrings) throws IOException { + protected ByteArrayOutputStream writeMainSection(byte[] decoded, List mainSection, + PSElement charStrings) throws IOException { ByteArrayOutputStream main = new ByteArrayOutputStream(); PSElement subrs = getElement("/Subrs", mainSection); @@ -470,11 +464,9 @@ public class Type1SubsetFile { writeString(eol + String.format("/Subrs %d array", uniqueSubs.size()), main); int count = 0; for (Entry entry : uniqueSubs.entrySet()) { - byte[] newSubrBytes = (eol + String.format("dup %d %d %s ", count++, - entry.getValue().length, rd)).getBytes("ASCII"); - newSubrBytes = concatArray(newSubrBytes, entry.getValue()); - newSubrBytes = concatArray(newSubrBytes, String.format(" %s", np).getBytes("ASCII")); - main.write(newSubrBytes); + writeString(eol + String.format("dup %d %d %s ", count++, entry.getValue().length, rd), main); + main.write(entry.getValue()); + writeString(" " + np, main); } writeString(eol + nd, main); } else { @@ -498,8 +490,8 @@ public class Type1SubsetFile { return main; } - private String findVariable(byte[] decoded, List elements, String[] matches, - String fallback) throws UnsupportedEncodingException { + protected String findVariable(byte[] decoded, List elements, String[] matches, + String fallback) throws UnsupportedEncodingException { for (PSElement element : elements) { if (element instanceof PSSubroutine) { byte[] var = new byte[element.getEndPoint() - element.getStartPoint()]; @@ -575,8 +567,8 @@ public class Type1SubsetFile { sbfont.mapUsedGlyphName(charIndex, charName); } - private void writeString(String entry, ByteArrayOutputStream boas) - throws UnsupportedEncodingException, IOException { + protected void writeString(String entry, ByteArrayOutputStream boas) + throws IOException { byte[] byteEntry = entry.getBytes("ASCII"); boas.write(byteEntry); } @@ -696,7 +688,7 @@ public class Type1SubsetFile { * @param decoded The array from which to copy a section of data * @return Returns the copy of the data section */ - byte[] getBinaryEntry(int[] position, byte[] decoded) { + protected byte[] getBinaryEntry(int[] position, byte[] decoded) { int start = position[0]; int finish = position[1]; byte[] line = new byte[finish - start]; @@ -704,7 +696,7 @@ public class Type1SubsetFile { return line; } - private String getEntryPart(String entry, int part) { + protected String getEntryPart(String entry, int part) { Scanner s = new Scanner(entry).useDelimiter(" "); for (int i = 1; i < part; i++) { s.next(); @@ -712,7 +704,7 @@ public class Type1SubsetFile { return s.next(); } - private PSElement getElement(String elementID, List elements) { + protected PSElement getElement(String elementID, List elements) { for (PSElement element : elements) { if (element.getOperator().equals(elementID)) { return element; @@ -721,14 +713,6 @@ public class Type1SubsetFile { return null; } - /** - * Gets the list of subset character names - * @return Returns the subset character names - */ - public List getCharNames() { - return charNames; - } - /** * A class to encode and decode sections of a type 1 font file. See Adobe * Type 1 Font Format Section 7.2 for more details. diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 1d93e9979..0f723d228 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -1699,7 +1699,7 @@ public class PDFFactory { assert font instanceof SingleByteFont; SingleByteFont sbfont = (SingleByteFont)font; Type1SubsetFile pfbFile = new Type1SubsetFile(); - byte[] subsetData = pfbFile.createSubset(in, sbfont, fontPrefix); + byte[] subsetData = pfbFile.createSubset(in, sbfont); InputStream subsetStream = new ByteArrayInputStream(subsetData); PFBParser parser = new PFBParser(); PFBData pfb = parser.parsePFB(subsetStream); diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java index 5bb723eea..b98919ca2 100644 --- a/src/java/org/apache/fop/render/ps/PSFontUtils.java +++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java @@ -324,7 +324,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { boolean embed = true; if (font.getEmbeddingMode() == EmbeddingMode.SUBSET) { Type1SubsetFile subset = new Type1SubsetFile(); - byte[] byteSubset = subset.createSubset(fontStream, font, ""); + byte[] byteSubset = subset.createSubset(fontStream, font); fontStream = new ByteArrayInputStream(byteSubset); } embedType1Font(gen, fontStream); diff --git a/test/java/org/apache/fop/fonts/type1/Type1SubsetFileTestCase.java b/test/java/org/apache/fop/fonts/type1/Type1SubsetFileTestCase.java index e18173a6b..3d7093bd3 100644 --- a/test/java/org/apache/fop/fonts/type1/Type1SubsetFileTestCase.java +++ b/test/java/org/apache/fop/fonts/type1/Type1SubsetFileTestCase.java @@ -54,7 +54,7 @@ public class Type1SubsetFileTestCase { @Test public void test() throws IOException { InputStream in = new FileInputStream(TEST_FONT_A); - compareCharStringData(in, TEST_FONT_A, createFontASubset(in, TEST_FONT_A)); + compareCharStringData(TEST_FONT_A, createFontASubset(in, TEST_FONT_A)); } @Test @@ -135,12 +135,12 @@ public class Type1SubsetFileTestCase { assertEquals(segment[3], 65); } - private void compareCharStringData(InputStream in, String font, byte[] subsetFont) + private void compareCharStringData(String font, byte[] subsetFont) throws IOException { decodedSections = new ArrayList(); //Reinitialise the input stream as reset only supports 1000 bytes. - in = new FileInputStream(font); + InputStream in = new FileInputStream(font); List origElements = parseElements(in); List subsetElements = parseElements(new ByteArrayInputStream(subsetFont)); @@ -173,8 +173,6 @@ public class Type1SubsetFileTestCase { SingleByteFont sbfont = mock(SingleByteFont.class); //Glyph index & selector Map glyphs = new HashMap(); - //Selector & unicode - Map usedCharsIndex = new HashMap(); Map usedCharNames = new HashMap(); int count = 0; for (int i = 32; i < 127; i++) { @@ -185,7 +183,6 @@ public class Type1SubsetFileTestCase { } for (int i = 161; i < 204; i++) { glyphs.put(i, count++); - usedCharsIndex.put(count, (char)i); when(sbfont.getUnicodeFromSelector(count)).thenReturn((char)i); usedCharNames.put(i, String.format("/%s", Glyphs.charToGlyphName((char)i))); when(sbfont.getGlyphName(i)).thenReturn(AdobeStandardEncoding.getCharFromCodePoint(i)); @@ -195,14 +192,12 @@ public class Type1SubsetFileTestCase { }; for (int i = 0; i < randomGlyphs.length; i++) { glyphs.put(randomGlyphs[i], count++); - usedCharsIndex.put(count, (char)randomGlyphs[i]); when(sbfont.getUnicodeFromSelector(count)).thenReturn((char)randomGlyphs[i]); usedCharNames.put(i, String.format("/%s", Glyphs.charToGlyphName((char)i))); when(sbfont.getGlyphName(i)).thenReturn(AdobeStandardEncoding.getCharFromCodePoint(i)); } for (int i = 256; i < 335; i++) { glyphs.put(i, count++); - usedCharsIndex.put(count, (char)i); when(sbfont.getUnicodeFromSelector(count)).thenReturn((char)i); usedCharNames.put(i, String.format("/%s", Glyphs.charToGlyphName((char)i))); when(sbfont.getGlyphName(i)).thenReturn(AdobeStandardEncoding.getCharFromCodePoint(i)); @@ -211,7 +206,7 @@ public class Type1SubsetFileTestCase { when(sbfont.getUsedGlyphs()).thenReturn(glyphs); when(sbfont.getEmbedFileURI()).thenReturn(URI.create(font)); Type1SubsetFile subset = new Type1SubsetFile(); - return subset.createSubset(in, sbfont, "AAAAAA"); + return subset.createSubset(in, sbfont); } private List parseElements(InputStream in) @@ -235,7 +230,6 @@ public class Type1SubsetFileTestCase { private byte[] readFullCharString(byte[] decoded, byte[] data, PSFixedArray subroutines) { List operands = new ArrayList(); - List fullList = new ArrayList(); for (int i = 0; i < data.length; i++) { int cur = data[i] & 0xFF; if (cur >= 0 && cur <= 31) { @@ -256,20 +250,16 @@ public class Type1SubsetFileTestCase { } BytesNumber operand = new BytesNumber(cur, i); operand.setName(getName(cur, next)); - fullList.add(operand); } operands.clear(); } if (cur >= 32 && cur <= 246) { operands.add(new BytesNumber(cur - 139, 1)); - fullList.add(operands.get(operands.size() - 1)); } else if (cur >= 247 && cur <= 250) { operands.add(new BytesNumber((cur - 247) * 256 + (data[i + 1] & 0xFF) + 108, 2)); - fullList.add(operands.get(operands.size() - 1)); i++; } else if (cur >= 251 && cur <= 254) { operands.add(new BytesNumber(-(cur - 251) * 256 - (data[i + 1] & 0xFF) - 108, 2)); - fullList.add(operands.get(operands.size() - 1)); i++; } else if (cur == 255) { int b1 = data[i + 1] & 0xFF; @@ -278,7 +268,6 @@ public class Type1SubsetFileTestCase { int b4 = data[i + 4] & 0xFF; int value = b1 << 24 | b2 << 16 | b3 << 8 | b4; operands.add(new BytesNumber(value, 5)); - fullList.add(operands.get(operands.size() - 1)); i += 4; } } -- 2.39.5