Categories:
Audio (13)
Biotech (29)
Bytecode (36)
Database (77)
Framework (7)
Game (7)
General (507)
Graphics (53)
I/O (35)
IDE (2)
JAR Tools (101)
JavaBeans (21)
JDBC (121)
JDK (426)
JSP (20)
Logging (108)
Mail (58)
Messaging (8)
Network (84)
PDF (97)
Report (7)
Scripting (84)
Security (32)
Server (121)
Servlet (26)
SOAP (24)
Testing (54)
Web (15)
XML (309)
Collections:
Other Resources:
What Is fop.jar in fop-2.7-bin.zip
What Is fop.jar? I got it from the fop-2.7-bin.zip.
✍: FYIcenter.com
fop.jar in fop-2.7-bin.zip is the JAR file for FOP 2.7, which
is a print formatter driven by XSL formatting objects (XSL-FO).
You can obtain fop.jar from the build folder of the fop-2.7-bin.zip file.
Below is the information about the fop.jar (2.2) file:
JAR File Size and Download Location:
JAR name: fop.jar, fop-2.7.jar Target JDK version: 1.7 File name: fop.jar File size: 4442817 bytes Release date: 20-Jan-2022 Download: Apache FOP Website
Java source code files for fop.jar:
⏎ org/apache/fop/fonts/truetype/OTFSubSetFile.java
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* $Id$ */ package org.apache.fop.fonts.truetype; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fontbox.cff.CFFStandardString; import org.apache.fontbox.cff.CFFType1Font; import org.apache.fontbox.cff.CharStringCommand; import org.apache.fontbox.cff.Type2CharString; 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; import org.apache.fop.fonts.cff.CFFDataReader.FDSelect; import org.apache.fop.fonts.cff.CFFDataReader.FontDict; import org.apache.fop.fonts.cff.CFFDataReader.Format0FDSelect; import org.apache.fop.fonts.cff.CFFDataReader.Format3FDSelect; import org.apache.fop.fonts.type1.AdobeStandardEncoding; /** * Reads an OpenType CFF file and generates a subset * The OpenType specification can be found at the Microsoft * Typography site: http://www.microsoft.com/typography/otspec/ */ public class OTFSubSetFile extends OTFSubSetWriter { /** A map containing each glyph to be included in the subset * with their existing and new GID's **/ protected Map<Integer, Integer> subsetGlyphs = new LinkedHashMap<Integer, Integer>(); /** A map of the new GID to SID used to construct the charset table **/ protected Map<Integer, Integer> gidToSID; protected CFFIndexData localIndexSubr; protected CFFIndexData globalIndexSubr; /** List of subroutines to write to the local / global indexes in the subset font **/ protected List<byte[]> subsetLocalIndexSubr; protected List<byte[]> subsetGlobalIndexSubr; /** 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 */ 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. */ private Map<Integer, FDIndexReference> subsetFDSelect; /** A list of unique subroutines from the global / local subroutine indexes */ protected List<Integer> localUniques; protected List<Integer> globalUniques; /** A store of the number of subroutines each global / local subroutine will store **/ protected int subsetLocalSubrCount; protected int subsetGlobalSubrCount; /** A list of char string data for each glyph to be stored in the subset font **/ protected List<byte[]> subsetCharStringsIndex; /** The embedded name to change in the name table **/ protected String embeddedName; /** An array used to hold the string index data for the subset font **/ protected List<byte[]> stringIndexData = new ArrayList<byte[]>(); /** The CFF reader object used to read data and offsets from the original font file */ protected CFFDataReader cffReader; /** The class used to represent this font **/ private MultiByteFont mbFont; /** The number of standard strings in CFF **/ public static final int NUM_STANDARD_STRINGS = 391; /** The operator used to identify a local subroutine reference */ private static final int LOCAL_SUBROUTINE = 10; /** The operator used to identify a global subroutine reference */ private static final int GLOBAL_SUBROUTINE = 29; private static final String ACCENT_CMD = "seac"; /** The parser used to parse type2 charstring */ private Type2Parser type2Parser; public OTFSubSetFile() throws IOException { super(); } public void readFont(FontFileReader in, String embeddedName, MultiByteFont mbFont) throws IOException { readFont(in, embeddedName, mbFont, mbFont.getUsedGlyphs()); } /** * Reads and creates a subset of the font. * * @param in FontFileReader to read from * @param embeddedName Name to be checked for in the font file * @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 */ void readFont(FontFileReader in, String embeddedName, MultiByteFont mbFont, Map<Integer, Integer> usedGlyphs) throws IOException { this.mbFont = mbFont; fontFile = in; this.embeddedName = embeddedName; initializeFont(in); cffReader = new CFFDataReader(fontFile); mapChars(usedGlyphs); //Sort by the new GID and store in a LinkedHashMap subsetGlyphs = sortByValue(usedGlyphs); //Create the CIDFontType0C data createCFF(); } private void mapChars(Map<Integer, Integer> usedGlyphs) throws IOException { if (fileFont instanceof CFFType1Font) { CFFType1Font cffType1Font = (CFFType1Font) fileFont; subsetGlyphs = sortByValue(usedGlyphs); for (int gid : subsetGlyphs.keySet()) { Type2CharString type2CharString = cffType1Font.getType2CharString(gid); List<Number> stack = new ArrayList<Number>(); for (Object obj : type2CharString.getType1Sequence()) { if (obj instanceof CharStringCommand) { String name = CharStringCommand.TYPE1_VOCABULARY.get(((CharStringCommand) obj).getKey()); if (ACCENT_CMD.equals(name)) { int first = stack.get(3).intValue(); int second = stack.get(4).intValue(); mbFont.mapChar(AdobeStandardEncoding.getUnicodeFromCodePoint(first)); mbFont.mapChar(AdobeStandardEncoding.getUnicodeFromCodePoint(second)); } stack.clear(); } else { stack.add((Number) obj); } } } } } private Map<Integer, Integer> sortByValue(Map<Integer, Integer> map) { List<Entry<Integer, Integer>> list = new ArrayList<Entry<Integer, Integer>>(map.entrySet()); Collections.sort(list, new Comparator<Entry<Integer, Integer>>() { public int compare(Entry<Integer, Integer> o1, Entry<Integer, Integer> o2) { return ((Comparable<Integer>) o1.getValue()).compareTo(o2.getValue()); } }); Map<Integer, Integer> result = new LinkedHashMap<Integer, Integer>(); for (Entry<Integer, Integer> entry : list) { result.put(entry.getKey(), entry.getValue()); } return result; } protected void createCFF() throws IOException { //Header writeBytes(cffReader.getHeader()); //Name Index writeIndex(Arrays.asList(embedFontName.getBytes("UTF-8"))); Offsets offsets = new Offsets(); //Top DICT Index and Data offsets.topDictData = currentPos + writeTopDICT(); boolean hasFDSelect = cffReader.getFDSelect() != null; //Create the char string index data and related local / global subroutines if (hasFDSelect) { createCharStringDataCID(); } else { createCharStringData(); } //If it is a CID-Keyed font, store each FD font and add each SID List<Integer> fontNameSIDs = null; List<Integer> subsetFDFonts = null; if (hasFDSelect) { subsetFDFonts = getUsedFDFonts(); fontNameSIDs = storeFDStrings(subsetFDFonts); } //String index writeStringIndex(); //Global subroutine index writeIndex(subsetGlobalIndexSubr); //Encoding offsets.encoding = currentPos; //Charset table offsets.charset = currentPos; writeCharsetTable(hasFDSelect); //FDSelect table offsets.fdSelect = currentPos; if (hasFDSelect) { writeFDSelect(); if (!isCharStringBeforeFD()) { offsets.fdArray = writeFDArray(subsetFDFonts, fontNameSIDs); } } //Char Strings Index offsets.charString = currentPos; writeIndex(subsetCharStringsIndex); if (hasFDSelect) { if (isCharStringBeforeFD()) { offsets.fdArray = writeFDArray(subsetFDFonts, fontNameSIDs); } updateCIDOffsets(offsets); } else { //Keep offset to modify later with the local subroutine index offset offsets.privateDict = currentPos; writePrivateDict(); //Local subroutine index offsets.localIndex = currentPos; writeIndex(subsetLocalIndexSubr); //Update the offsets updateOffsets(offsets); } } static class Offsets { Integer topDictData; Integer encoding; Integer charset; Integer fdSelect; Integer charString; Integer fdArray; Integer privateDict; Integer localIndex; } private int writeFDArray(List<Integer> subsetFDFonts, List<Integer> fontNameSIDs) throws IOException { List<Integer> privateDictOffsets = writeCIDDictsAndSubrs(subsetFDFonts); return writeFDArray(subsetFDFonts, privateDictOffsets, fontNameSIDs); } private boolean isCharStringBeforeFD() { LinkedHashMap<String, DICTEntry> entries = cffReader.getTopDictEntries(); int len = entries.get("CharStrings").getOperandLength(); if (entries.containsKey("FDArray")) { int len2 = entries.get("FDArray").getOperandLength(); return len < len2; } return true; } protected List<Integer> storeFDStrings(List<Integer> uniqueNewRefs) throws IOException { List<Integer> fontNameSIDs = new ArrayList<Integer>(); List<FontDict> fdFonts = cffReader.getFDFonts(); for (int 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); stringIndexData.add(cffReader.getStringIndex().getValue(fdFontDict.get("FontName") .getOperands().get(0).intValue() - NUM_STANDARD_STRINGS)); } return fontNameSIDs; } protected int writeTopDICT() throws IOException { Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries(); List<String> topDictStringEntries = Arrays.asList("version", "Notice", "Copyright", "FullName", "FamilyName", "Weight", "PostScript"); ByteArrayOutputStream dict = new ByteArrayOutputStream(); int offsetExtra = 0; for (Map.Entry<String, DICTEntry> 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 entry.setOffset(entry.getOffset() + offsetExtra); if (dictKey.equals("CharStrings") && entry.getOperandLength() < 5) { byte[] extra = new byte[5 - entry.getOperandLength()]; 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")) { dict.write(writeCIDCount(entry)); } else if (topDictStringEntries.contains(dictKey)) { if (entry.getOperandLength() < 2) { entry.setOperandLength(2); offsetExtra++; } dict.write(writeTopDictStringEntry(entry)); } else { dict.write(entry.getByteData()); } } byte[] topDictIndex = cffReader.getTopDictIndex().getByteData(); int offSize = topDictIndex[2]; return writeIndex(Arrays.asList(dict.toByteArray()), offSize) - dict.size(); } 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)); } int sidAStringIndex = stringIndexData.size() + 390; int sidB = dictEntry.getOperands().get(1).intValue(); if (sidB > 390) { stringIndexData.add("Identity".getBytes("UTF-8")); } int sidBStringIndex = stringIndexData.size() + 390; byte[] cidEntryByteData = dictEntry.getByteData(); updateOffset(cidEntryByteData, 0, dictEntry.getOperandLengths().get(0), sidAStringIndex); updateOffset(cidEntryByteData, dictEntry.getOperandLengths().get(0), dictEntry.getOperandLengths().get(1), sidBStringIndex); updateOffset(cidEntryByteData, dictEntry.getOperandLengths().get(0) + dictEntry.getOperandLengths().get(1), dictEntry.getOperandLengths().get(2), 0); return cidEntryByteData; } protected byte[] writeCIDCount(DICTEntry dictEntry) throws IOException { byte[] cidCountByteData = dictEntry.getByteData(); updateOffset(cidCountByteData, 0, dictEntry.getOperandLengths().get(0), subsetGlyphs.size()); return cidCountByteData; } private byte[] writeTopDictStringEntry(DICTEntry dictEntry) throws IOException { int sid = dictEntry.getOperands().get(0).intValue(); if (sid > 391) { stringIndexData.add(cffReader.getStringIndex().getValue(sid - 391)); } byte[] newDictEntry = createNewRef(stringIndexData.size() + 390, dictEntry.getOperator(), dictEntry.getOperandLength(), true); return newDictEntry; } private void writeStringIndex() throws IOException { Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries(); int charsetOffset = topDICT.get("charset").getOperands().get(0).intValue(); gidToSID = new LinkedHashMap<Integer, Integer>(); 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(v, sid); if (mbFont != null) { 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(v, new String(value, "UTF-8")); } gidToSID.put(v, stringIndexData.size() + 391); stringIndexData.add(value); } else { if (mbFont != null) { mbFont.mapUsedGlyphName(v, ".notdef"); } gidToSID.put(v, index); } } } //Write the String Index writeIndex(stringIndexData); } protected void createCharStringDataCID() throws IOException { CFFIndexData charStringsIndex = cffReader.getCharStringIndex(); FDSelect fontDictionary = cffReader.getFDSelect(); if (fontDictionary instanceof Format0FDSelect) { throw new UnsupportedOperationException("OTF CFF CID Format0 currently not implemented"); } else if (fontDictionary instanceof Format3FDSelect) { Format3FDSelect fdSelect = (Format3FDSelect)fontDictionary; 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()) { 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 >= entry.getKey() && gid < nextRange) { int r = entry.getValue(); subsetGroups.put(gid, r); if (!uniqueGroups.contains(r)) { uniqueGroups.add(r); } } i++; } } //Prepare resources globalIndexSubr = cffReader.getGlobalIndexSubr(); //Create the new char string index subsetCharStringsIndex = new ArrayList<byte[]>(); globalUniques = new ArrayList<Integer>(); subsetFDSelect = new LinkedHashMap<Integer, FDIndexReference>(); List<List<Integer>> foundLocalUniques = new ArrayList<List<Integer>>(); for (int u : uniqueGroups) { foundLocalUniques.add(new ArrayList<Integer>()); } Map<Integer, Integer> gidHintMaskLengths = new HashMap<Integer, Integer>(); for (Entry<Integer, Integer> subsetGlyph : subsetGlyphs.entrySet()) { int gid = subsetGlyph.getKey(); int group = subsetGroups.get(gid); localIndexSubr = cffReader.getFDFonts().get(group).getLocalSubrData(); localUniques = foundLocalUniques.get(uniqueGroups.indexOf(group)); type2Parser = new Type2Parser(); FDIndexReference newFDReference = new FDIndexReference(uniqueGroups.indexOf(group), group); subsetFDSelect.put(subsetGlyph.getValue(), 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 subsetGlobalIndexSubr = new ArrayList<byte[]>(); fdSubrs = new ArrayList<List<byte[]>>(); subsetGlobalSubrCount = globalUniques.size(); globalUniques.clear(); localUniques = null; for (List<Integer> foundLocalUnique : foundLocalUniques) { fdSubrs.add(new ArrayList<byte[]>()); } List<List<Integer>> foundLocalUniquesB = new ArrayList<List<Integer>>(); for (int u : uniqueGroups) { foundLocalUniquesB.add(new ArrayList<Integer>()); } for (Entry<Integer, Integer> subsetGlyph : subsetGlyphs.entrySet()) { int gid = subsetGlyph.getKey(); int value = subsetGlyph.getValue(); int group = subsetGroups.get(gid); localIndexSubr = cffReader.getFDFonts().get(group).getLocalSubrData(); int newFDIndex = subsetFDSelect.get(value).getNewFDIndex(); localUniques = foundLocalUniquesB.get(newFDIndex); byte[] data = charStringsIndex.getValue(gid); subsetLocalIndexSubr = fdSubrs.get(newFDIndex); subsetLocalSubrCount = foundLocalUniques.get(newFDIndex).size(); type2Parser = new Type2Parser(); type2Parser.setMaskLength(gidHintMaskLengths.get(gid)); data = readCharStringData(data, subsetLocalSubrCount); subsetCharStringsIndex.add(data); } } } protected void writeFDSelect() { if (cffReader.getTopDictEntries().get("CharStrings").getOperandLength() == 2) { Map<Integer, Integer> indexs = getFormat3Index(); writeByte(3); //Format writeCard16(indexs.size()); int count = 0; for (Entry<Integer, Integer> x : indexs.entrySet()) { writeCard16(count); writeByte(x.getKey()); count += x.getValue(); } writeCard16(subsetFDSelect.size()); } else { writeByte(0); //Format for (FDIndexReference e : subsetFDSelect.values()) { writeByte(e.getNewFDIndex()); } } } private Map<Integer, Integer> getFormat3Index() { Map<Integer, Integer> indexs = new LinkedHashMap<Integer, Integer>(); int last = -1; int count = 0; for (FDIndexReference e : subsetFDSelect.values()) { int i = e.getNewFDIndex(); count++; if (i != last) { indexs.put(i, count); count = 1; } last = i; } indexs.put(last, count); return indexs; } protected List<Integer> getUsedFDFonts() { List<Integer> uniqueNewRefs = new ArrayList<Integer>(); for (FDIndexReference e : subsetFDSelect.values()) { int fdIndex = e.getOldFDIndex(); if (!uniqueNewRefs.contains(fdIndex)) { uniqueNewRefs.add(fdIndex); } } return uniqueNewRefs; } protected List<Integer> writeCIDDictsAndSubrs(List<Integer> uniqueNewRefs) throws IOException { List<Integer> privateDictOffsets = new ArrayList<Integer>(); List<FontDict> fdFonts = cffReader.getFDFonts(); 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); 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 { int offset = currentPos; List<FontDict> fdFonts = cffReader.getFDFonts(); List<byte[]> index = new ArrayList<byte[]>(); 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 updateOffset(fdFontByteData, fdFontDict.get("FontName").getOffset() - 1, fdFontDict.get("FontName").getOperandLengths().get(0), fontNameSIDs.get(i)); //Update the Private dict reference updateOffset(fdFontByteData, fdFontDict.get("Private").getOffset() + fdFontDict.get("Private").getOperandLengths().get(0), fdFontDict.get("Private").getOperandLengths().get(1), privateDictOffsets.get(i)); index.add(fdFontByteData); i++; } writeIndex(index); return offset; } private static class FDIndexReference { private int newFDIndex; private int oldFDIndex; public FDIndexReference(int newFDIndex, int oldFDIndex) { this.newFDIndex = newFDIndex; this.oldFDIndex = oldFDIndex; } public int getNewFDIndex() { return newFDIndex; } public int getOldFDIndex() { return oldFDIndex; } } private void createCharStringData() throws IOException { Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries(); CFFIndexData charStringsIndex = cffReader.getCharStringIndex(); DICTEntry privateEntry = topDICT.get("Private"); if (privateEntry != null) { int privateOffset = privateEntry.getOperands().get(1).intValue(); Map<String, DICTEntry> privateDICT = cffReader.getPrivateDict(privateEntry); if (privateDICT.get("Subrs") != null) { int localSubrOffset = privateOffset + privateDICT.get("Subrs").getOperands().get(0).intValue(); localIndexSubr = cffReader.readIndex(localSubrOffset); } else { localIndexSubr = cffReader.readIndex(null); } } globalIndexSubr = cffReader.getGlobalIndexSubr(); //Create the two lists which are to store the local and global subroutines subsetLocalIndexSubr = new ArrayList<byte[]>(); subsetGlobalIndexSubr = new ArrayList<byte[]>(); //Create the new char string index subsetCharStringsIndex = new ArrayList<byte[]>(); 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 subsetLocalSubrCount = localUniques.size(); subsetGlobalSubrCount = globalUniques.size(); localUniques.clear(); globalUniques.clear(); 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 List<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; } private int exec(int b0, byte[] input, int curPos) throws IOException { ByteArrayInputStream bis = new ByteArrayInputStream(input); bis.skip(curPos + 1); return exec(b0, bis); } public int exec(int b0, InputStream data) throws IOException { int posDelta = 0; if ((b0 >= 0 && b0 <= 27) || (b0 >= 29 && b0 <= 31)) { if (b0 == 12) { 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); pushOperand(operand); posDelta = operand.getNumBytes() - 1; } else { throw new UnsupportedOperationException("Operator:" + b0 + " is not supported"); } return posDelta; } private BytesNumber readNumber(int b0, InputStream input) throws IOException { if (b0 == 28) { int b1 = input.read(); int b2 = input.read(); 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.read(); return new BytesNumber((b0 - 247) * 256 + b1 + 108, 2); } else if (b0 >= 251 && b0 <= 254) { int b1 = input.read(); return new BytesNumber(-(b0 - 251) * 256 - b1 - 108, 2); } else if (b0 == 255) { int b1 = input.read(); int b2 = input.read(); int b3 = input.read(); int b4 = input.read(); 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; for (int dataPos = 0; dataPos < data.length; dataPos++) { int b0 = data[dataPos] & 0xff; if (b0 == LOCAL_SUBROUTINE && hasLocalSubroutines) { preScanForSubsetIndexSize(localIndexSubr, localUniques); } else if (b0 == GLOBAL_SUBROUTINE && hasGlobalSubroutines) { preScanForSubsetIndexSize(globalIndexSubr, globalUniques); } else { dataPos += type2Parser.exec(b0, data, dataPos); } } } private void preScanForSubsetIndexSize(CFFIndexData indexSubr, List<Integer> uniques) throws IOException { int subrNumber = getSubrNumber(indexSubr.getNumObjects(), type2Parser.popOperand().getNumber()); if (!uniques.contains(subrNumber) && subrNumber < indexSubr.getNumObjects()) { uniques.add(subrNumber); } if (subrNumber < indexSubr.getNumObjects()) { byte[] subr = indexSubr.getValue(subrNumber); preScanForSubsetIndexSize(subr); } else { throw new IllegalArgumentException("callgsubr out of range"); } } private int getSubrNumber(int numSubroutines, int operand) { int bias = getBias(numSubroutines); return bias + operand; } private byte[] readCharStringData(byte[] data, int subsetLocalSubrCount) throws IOException { boolean hasLocalSubroutines = localIndexSubr != null && localIndexSubr.getNumObjects() > 0; boolean hasGlobalSubroutines = globalIndexSubr != null && globalIndexSubr.getNumObjects() > 0; 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, subsetLocalSubrCount); if (newRef != -1) { byte[] newData = constructNewRefData(dataPos, data, operand, subsetLocalSubrCount, newRef, new int[] {10}); dataPos -= data.length - newData.length; data = newData; } } else if (b0 == 29 && hasGlobalSubroutines) { BytesNumber operand = type2Parser.popOperand(); int subrNumber = getSubrNumber(globalIndexSubr.getNumObjects(), operand.getNumber()); int newRef = getNewRefForReference(subrNumber, globalUniques, globalIndexSubr, subsetGlobalIndexSubr, subsetGlobalSubrCount); if (newRef != -1) { byte[] newData = constructNewRefData(dataPos, data, operand, subsetGlobalSubrCount, newRef, new int[] {29}); dataPos -= data.length - newData.length; data = newData; } } else { dataPos += type2Parser.exec(b0, data, dataPos); } } //Return the data with the modified references to our arrays return data; } private int getNewRefForReference(int subrNumber, List<Integer> uniquesArray, CFFIndexData indexSubr, List<byte[]> subsetIndexSubr, int subrCount) throws IOException { int newRef; if (!uniquesArray.contains(subrNumber)) { if (subrNumber < indexSubr.getNumObjects()) { byte[] subr = indexSubr.getValue(subrNumber); subr = readCharStringData(subr, subrCount); uniquesArray.add(subrNumber); subsetIndexSubr.add(subr); newRef = subsetIndexSubr.size() - 1; } else { throw new IllegalArgumentException("subrNumber out of range"); } } else { newRef = uniquesArray.indexOf(subrNumber); } return newRef; } private int getBias(int subrCount) { if (subrCount < 1240) { return 107; } else if (subrCount < 33900) { return 1131; } else { return 32768; } } private byte[] constructNewRefData(int curDataPos, byte[] currentData, BytesNumber operand, int fullSubsetIndexSize, int curSubsetIndexSize, int[] operatorCode) throws IOException { //Create the new array with the modified reference ByteArrayOutputStream newData = new ByteArrayOutputStream(); int startRef = curDataPos - operand.getNumBytes(); int length = operand.getNumBytes() + 1; int newBias = getBias(fullSubsetIndexSize); int newRef = curSubsetIndexSize - newBias; byte[] newRefBytes = createNewRef(newRef, operatorCode, -1, false); newData.write(currentData, 0, startRef); newData.write(newRefBytes); newData.write(currentData, startRef + length, currentData.length - (startRef + length)); return newData.toByteArray(); } public static byte[] createNewRef(int newRef, int[] operatorCode, int forceLength, boolean isDict) { ByteArrayOutputStream newRefBytes = new ByteArrayOutputStream(); if ((forceLength == -1 && newRef >= -107 && newRef <= 107) || forceLength == 1) { //The index values are 0 indexed newRefBytes.write(newRef + 139); } else if ((forceLength == -1 && newRef >= -1131 && newRef <= 1131) || forceLength == 2) { if (newRef <= -876) { newRefBytes.write(254); } else if (newRef <= -620) { newRefBytes.write(253); } else if (newRef <= -364) { newRefBytes.write(252); } else if (newRef <= -108) { newRefBytes.write(251); } else if (newRef <= 363) { newRefBytes.write(247); } else if (newRef <= 619) { newRefBytes.write(248); } else if (newRef <= 875) { newRefBytes.write(249); } else { newRefBytes.write(250); } if (newRef > 0) { newRefBytes.write(newRef - 108); } else { newRefBytes.write(-newRef - 108); } } else if ((forceLength == -1 && newRef >= -32768 && newRef <= 32767) || forceLength == 3) { newRefBytes.write(28); newRefBytes.write(newRef >> 8); newRefBytes.write(newRef); } else { if (isDict) { newRefBytes.write(29); } else { newRefBytes.write(255); } newRefBytes.write(newRef >> 24); newRefBytes.write(newRef >> 16); newRefBytes.write(newRef >> 8); newRefBytes.write(newRef); } for (int i : operatorCode) { newRefBytes.write(i); } return newRefBytes.toByteArray(); } protected int writeIndex(List<byte[]> dataArray) { int totLength = 1; for (byte[] data : dataArray) { totLength += data.length; } int offSize = getOffSize(totLength); return writeIndex(dataArray, offSize); } protected int writeIndex(List<byte[]> 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. this.writeByte(offSize); //Count the first offset 1 hdrTotal += offSize; int total = 0; int i = 0; for (byte[] data : dataArray) { hdrTotal += offSize; int length = data.length; switch (offSize) { case 1: if (i == 0) { writeByte(1); } total += length; writeByte(total + 1); break; case 2: if (i == 0) { writeCard16(1); } total += length; writeCard16(total + 1); break; case 3: if (i == 0) { writeThreeByteNumber(1); } total += length; writeThreeByteNumber(total + 1); break; case 4: if (i == 0) { writeULong(1); } total += length; writeULong(total + 1); break; default: throw new AssertionError("Offset Size was not an expected value."); } i++; } for (byte[] aDataArray : dataArray) { writeBytes(aDataArray); } return hdrTotal + total; } private int getOffSize(int totLength) { int offSize = 1; if (totLength < (1 << 8)) { offSize = 1; } else if (totLength < (1 << 16)) { offSize = 2; } else if (totLength < (1 << 24)) { offSize = 3; } else { offSize = 4; } return offSize; } /** * A class used to store the last number operand and also it's size in bytes */ static class BytesNumber { private int number; private int numBytes; public BytesNumber(int number, int numBytes) { this.number = number; this.numBytes = numBytes; } public int getNumber() { return this.number; } public int getNumBytes() { return this.numBytes; } public void clearNumber() { this.number = -1; this.numBytes = -1; } public String toString() { return Integer.toString(number); } @Override public boolean equals(Object entry) { assert entry instanceof BytesNumber; BytesNumber bnEntry = (BytesNumber)entry; return this.number == bnEntry.getNumber() && this.numBytes == bnEntry.getNumBytes(); } @Override public int hashCode() { int hash = 1; hash = hash * 17 + number; hash = hash * 31 + numBytes; return hash; } } private void writeCharsetTable(boolean cidFont) throws IOException { if (cidFont) { writeByte(2); for (int entry : gidToSID.keySet()) { if (entry == 0) { continue; } writeCard16(entry); writeCard16(gidToSID.size() - 1); break; } } else { writeByte(0); for (int entry : gidToSID.values()) { if (entry == 0) { continue; } writeCard16(entry); } } } protected void writePrivateDict() throws IOException { Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries(); DICTEntry privateEntry = topDICT.get("Private"); if (privateEntry != null) { writeBytes(cffReader.getPrivateDictBytes(privateEntry)); } } protected void updateOffsets(Offsets offsets) throws IOException { Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries(); Map<String, DICTEntry> privateDICT = null; DICTEntry privateEntry = topDICT.get("Private"); if (privateEntry != null) { privateDICT = cffReader.getPrivateDict(privateEntry); } updateFixedOffsets(topDICT, offsets); if (privateDICT != null) { //Private index offset in the top dict int oldPrivateOffset = offsets.topDictData + privateEntry.getOffset(); updateOffset(oldPrivateOffset + privateEntry.getOperandLengths().get(0), privateEntry.getOperandLengths().get(1), offsets.privateDict); //Update the local subroutine index offset in the private dict DICTEntry subroutines = privateDICT.get("Subrs"); if (subroutines != null) { int oldLocalSubrOffset = offsets.privateDict + subroutines.getOffset(); updateOffset(oldLocalSubrOffset, subroutines.getOperandLength(), (offsets.localIndex - offsets.privateDict)); } } } protected void updateFixedOffsets(Map<String, DICTEntry> topDICT, Offsets offsets) throws IOException { //Charset offset in the top dict DICTEntry charset = topDICT.get("charset"); int oldCharsetOffset = offsets.topDictData + charset.getOffset(); updateOffset(oldCharsetOffset, charset.getOperandLength(), offsets.charset); //Char string index offset in the private dict DICTEntry charString = topDICT.get("CharStrings"); int oldCharStringOffset = offsets.topDictData + charString.getOffset(); updateOffset(oldCharStringOffset, charString.getOperandLength(), offsets.charString); DICTEntry encodingEntry = topDICT.get("Encoding"); if (encodingEntry != null && encodingEntry.getOperands().get(0).intValue() != 0 && encodingEntry.getOperands().get(0).intValue() != 1) { int oldEncodingOffset = offsets.topDictData + encodingEntry.getOffset(); updateOffset(oldEncodingOffset, encodingEntry.getOperandLength(), offsets.encoding); } } protected void updateCIDOffsets(Offsets offsets) throws IOException { Map<String, DICTEntry> topDict = cffReader.getTopDictEntries(); DICTEntry fdArrayEntry = topDict.get("FDArray"); if (fdArrayEntry != null) { updateOffset(offsets.topDictData + fdArrayEntry.getOffset() - 1, fdArrayEntry.getOperandLength(), offsets.fdArray); } DICTEntry fdSelect = topDict.get("FDSelect"); if (fdSelect != null) { updateOffset(offsets.topDictData + fdSelect.getOffset() - 1, fdSelect.getOperandLength(), offsets.fdSelect); } updateFixedOffsets(topDict, offsets); } private void updateOffset(int position, int length, int replacement) throws IOException { byte[] outBytes = output.toByteArray(); updateOffset(outBytes, position, length, replacement); output.reset(); output.write(outBytes); } private void updateOffset(byte[] out, int position, int length, int replacement) { 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) { 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; } else if (replacement <= 875) { out[position] = (byte)249; } else { out[position] = (byte)250; } if (replacement > 0) { out[position + 1] = (byte)(replacement - 108); } else { out[position + 1] = (byte)(-replacement - 108); } break; case 3: assert replacement <= 32767; out[position] = (byte)28; out[position + 1] = (byte)((replacement >> 8) & 0xFF); out[position + 2] = (byte)(replacement & 0xFF); break; case 5: out[position] = (byte)29; out[position + 1] = (byte)((replacement >> 24) & 0xFF); out[position + 2] = (byte)((replacement >> 16) & 0xFF); out[position + 3] = (byte)((replacement >> 8) & 0xFF); out[position + 4] = (byte)(replacement & 0xFF); break; default: } } /** * Returns the parsed CFF data for the original font. * @return The CFFDataReader contaiing the parsed data */ public CFFDataReader getCFFReader() { return cffReader; } }
⏎ org/apache/fop/fonts/truetype/OTFSubSetFile.java
Or download all of them as a single archive file:
File name: fop-2.7-src.zip File size: 3401312 bytes Release date: 2022-01-20 Download
⇒ "fop" Command in fop-2.7-bin.zip
2016-07-07, 21909👍, 0💬
Popular Posts:
What Is jtds-1.2.2.jar? jtds-1.2.2.jar is the JAR files of jTDS Java library 1.2.2, which is a JDBC ...
JDK 8 tools.jar is the JAR file for JDK 8 tools. It contains Java classes to support different JDK t...
ASM is an all purpose Java bytecode manipulation and analysis framework. It can be used to modify ex...
SLF4J API is a simple API that allows to plug in any desired logging library at deployment time. Her...
Where to find answers to frequently asked questions on Downloading and Installing ojdbc.jar - JDBC D...