12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223 |
- /*
- * 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.IOException;
- 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 java.util.Set;
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- import org.apache.fontbox.cff.CFFStandardString;
-
- 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;
-
- /**
- * 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 OTFFile {
-
- protected byte[] output;
- protected int currentPos;
- private int realSize;
-
- /** A map containing each glyph to be included in the subset
- * with their existing and new GID's **/
- protected LinkedHashMap<Integer, Integer> subsetGlyphs = new LinkedHashMap<Integer, Integer>();
-
- /** A map of the new GID to SID used to construct the charset table **/
- protected LinkedHashMap<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 */
- private ArrayList<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 LinkedHashMap<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;
- /** The parser used to parse type2 charstring */
- private Type2Parser type2Parser;
-
- public OTFSubSetFile() throws IOException {
- super();
- }
-
- public void readFont(FontFileReader in, String embeddedName, String header,
- MultiByteFont mbFont) throws IOException {
- this.mbFont = mbFont;
- readFont(in, embeddedName, header, 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 header The header of 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, String header,
- Map<Integer, Integer> usedGlyphs) throws IOException {
- fontFile = in;
-
- currentPos = 0;
- realSize = 0;
-
- this.embeddedName = embeddedName;
-
- //Sort by the new GID and store in a LinkedHashMap
- subsetGlyphs = sortByValue(usedGlyphs);
-
- output = new byte[in.getFileSize()];
-
- initializeFont(in);
-
- cffReader = new CFFDataReader(fontFile);
-
- //Create the CIDFontType0C data
- createCFF();
- }
-
- private LinkedHashMap<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());
- }
- });
-
- LinkedHashMap<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(embeddedName.getBytes()));
-
- //Keep offset of the topDICT so it can be updated once all data has been written
- int topDictOffset = currentPos;
- //Top DICT Index and Data
- byte[] topDictIndex = cffReader.getTopDictIndex().getByteData();
- int offSize = topDictIndex[2];
- writeBytes(topDictIndex, 0, 3 + (offSize * 2));
- int topDictDataOffset = currentPos;
- writeTopDICT();
-
- //Create the char string index data and related local / global subroutines
- if (cffReader.getFDSelect() == null) {
- createCharStringData();
- } else {
- createCharStringDataCID();
- }
-
- //If it is a CID-Keyed font, store each FD font and add each SID
- List<Integer> fontNameSIDs = null;
- List<Integer> subsetFDFonts = null;
- if (cffReader.getFDSelect() != null) {
- subsetFDFonts = getUsedFDFonts();
- fontNameSIDs = storeFDStrings(subsetFDFonts);
- }
-
- //String index
- writeStringIndex();
-
- //Global subroutine index
- writeIndex(subsetGlobalIndexSubr);
-
- //Encoding
- int encodingOffset = currentPos;
-
- //Charset table
- int charsetOffset = currentPos;
- writeCharsetTable(cffReader.getFDSelect() != null);
-
- //FDSelect table
- int fdSelectOffset = currentPos;
- if (cffReader.getFDSelect() != null) {
- writeFDSelect();
- }
-
- //Char Strings Index
- int charStringOffset = currentPos;
- writeIndex(subsetCharStringsIndex);
-
- if (cffReader.getFDSelect() == null) {
- //Keep offset to modify later with the local subroutine index offset
- int privateDictOffset = currentPos;
- writePrivateDict();
-
- //Local subroutine index
- int localIndexOffset = currentPos;
- writeIndex(subsetLocalIndexSubr);
-
- //Update the offsets
- updateOffsets(topDictOffset, charsetOffset, charStringOffset, privateDictOffset,
- localIndexOffset, encodingOffset);
- } else {
- List<Integer> privateDictOffsets = writeCIDDictsAndSubrs(subsetFDFonts);
- int fdArrayOffset = writeFDArray(subsetFDFonts, privateDictOffsets, fontNameSIDs);
-
- updateCIDOffsets(topDictDataOffset, fdArrayOffset, fdSelectOffset, charsetOffset,
- charStringOffset, encodingOffset);
- }
- }
-
- protected List<Integer> storeFDStrings(List<Integer> uniqueNewRefs) throws IOException {
- ArrayList<Integer> fontNameSIDs = new ArrayList<Integer>();
- List<FontDict> fdFonts = cffReader.getFDFonts();
- 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);
- stringIndexData.add(cffReader.getStringIndex().getValue(fdFontDict.get("FontName")
- .getOperands().get(0).intValue() - NUM_STANDARD_STRINGS));
- }
- return fontNameSIDs;
- }
-
- protected void writeBytes(byte[] out) {
- for (byte anOut : out) {
- writeByte(anOut);
- }
- }
-
- protected void writeBytes(byte[] out, int offset, int length) {
- for (int i = offset; i < offset + length; i++) {
- output[currentPos++] = out[i];
- realSize++;
- }
- }
-
- protected void writeTopDICT() throws IOException {
- LinkedHashMap<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
- List<String> topDictStringEntries = Arrays.asList("version", "Notice", "Copyright",
- "FullName", "FamilyName", "Weight", "PostScript");
- 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
- if (dictKey.equals("ROS")) {
- writeROSEntry(entry);
- } else if (dictKey.equals("CIDCount")) {
- writeCIDCount(entry);
- } else if (topDictStringEntries.contains(dictKey)) {
- writeTopDictStringEntry(entry);
- } else {
- writeBytes(entry.getByteData());
- }
- }
- }
-
- private void 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());
- }
- int sidBStringIndex = stringIndexData.size() + 390;
- byte[] cidEntryByteData = dictEntry.getByteData();
- cidEntryByteData = updateOffset(cidEntryByteData, 0, dictEntry.getOperandLengths().get(0),
- sidAStringIndex);
- 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), 0);
- writeBytes(cidEntryByteData);
- }
-
- protected void writeCIDCount(DICTEntry dictEntry) throws IOException {
- byte[] cidCountByteData = dictEntry.getByteData();
- cidCountByteData = updateOffset(cidCountByteData, 0, dictEntry.getOperandLengths().get(0),
- subsetGlyphs.size());
- writeBytes(cidCountByteData);
- }
-
- private void 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);
- writeBytes(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 (int gid : subsetGlyphs.keySet()) {
- int sid = cffReader.getSIDFromGID(charsetOffset, gid);
- //Check whether the SID falls into the standard string set
- if (sid < NUM_STANDARD_STRINGS) {
- gidToSID.put(subsetGlyphs.get(gid), sid);
- if (mbFont != null) {
- mbFont.mapUsedGlyphName(subsetGlyphs.get(gid),
- CFFStandardString.getName(sid));
- }
- } else {
- int index = sid - NUM_STANDARD_STRINGS;
- //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)));
- }
- gidToSID.put(subsetGlyphs.get(gid), stringIndexData.size() + 391);
- stringIndexData.add(cffReader.getStringIndex().getValue(index));
- } else {
- if (mbFont != null) {
- mbFont.mapUsedGlyphName(subsetGlyphs.get(gid), ".notdef");
- }
- gidToSID.put(subsetGlyphs.get(gid), 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>();
- for (int gid : subsetGlyphs.keySet()) {
- Set<Integer> rangeKeys = fdSelect.getRanges().keySet();
- Integer[] ranges = rangeKeys.toArray(new Integer[rangeKeys.size()]);
- for (int i = 0; i < ranges.length; i++) {
- int nextRange = -1;
- if (i < ranges.length - 1) {
- nextRange = ranges[i + 1];
- } else {
- nextRange = fdSelect.getSentinelGID();
- }
- if (gid >= ranges[i] && gid < nextRange) {
- subsetGroups.put(gid, fdSelect.getRanges().get(ranges[i]));
- if (!uniqueGroups.contains(fdSelect.getRanges().get(ranges[i]))) {
- uniqueGroups.add(fdSelect.getRanges().get(ranges[i]));
- }
- }
- }
- }
-
- //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 (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
- 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 (Integer uniqueGroup : uniqueGroups) {
- foundLocalUniquesB.add(new ArrayList<Integer>());
- }
- for (Integer gid : subsetGlyphs.keySet()) {
- int group = subsetGroups.get(gid);
- localIndexSubr = cffReader.getFDFonts().get(group).getLocalSubrData();
- localUniques = foundLocalUniquesB.get(subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex());
- byte[] data = charStringsIndex.getValue(gid);
- 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);
- }
- }
- }
-
- protected void writeFDSelect() {
- writeByte(0); //Format
- for (Integer gid : subsetFDSelect.keySet()) {
- writeByte(subsetFDSelect.get(gid).getNewFDIndex());
- }
- }
-
- protected List<Integer> getUsedFDFonts() {
- List<Integer> uniqueNewRefs = new ArrayList<Integer>();
- for (int gid : subsetFDSelect.keySet()) {
- int fdIndex = subsetFDSelect.get(gid).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();
- for (int i = 0; i < uniqueNewRefs.size(); i++) {
- FontDict curFDFont = fdFonts.get(uniqueNewRefs.get(i));
- HashMap<String, DICTEntry> fdPrivateDict = cffReader.parseDictData(
- curFDFont.getPrivateDictData());
- int privateDictOffset = currentPos;
- privateDictOffsets.add(privateDictOffset);
- byte[] fdPrivateDictByteData = curFDFont.getPrivateDictData();
- if (fdPrivateDict.get("Subrs") != null) {
- fdPrivateDictByteData = updateOffset(fdPrivateDictByteData, fdPrivateDict.get("Subrs").getOffset(),
- fdPrivateDict.get("Subrs").getOperandLength(),
- fdPrivateDictByteData.length);
- }
- writeBytes(fdPrivateDictByteData);
- writeIndex(fdSubrs.get(i));
- }
- return privateDictOffsets;
- }
-
- protected int writeFDArray(List<Integer> uniqueNewRefs, List<Integer> privateDictOffsets,
- List<Integer> fontNameSIDs)
- throws IOException {
- int offset = currentPos;
- List<FontDict> fdFonts = cffReader.getFDFonts();
-
- writeCard16(uniqueNewRefs.size());
- writeByte(1); //Offset size
- writeByte(1); //First offset
-
- int count = 1;
- for (Integer uniqueNewRef : uniqueNewRefs) {
- FontDict fdFont = fdFonts.get(uniqueNewRef);
- count += fdFont.getByteData().length;
- writeByte(count);
- }
-
- for (int i = 0; i < uniqueNewRefs.size(); i++) {
- FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i));
- byte[] fdFontByteData = fdFont.getByteData();
- Map<String, DICTEntry> fdFontDict = cffReader.parseDictData(fdFontByteData);
- //Update the SID to the FontName
- fdFontByteData = updateOffset(fdFontByteData, fdFontDict.get("FontName").getOffset() - 1,
- fdFontDict.get("FontName").getOperandLengths().get(0),
- fontNameSIDs.get(i));
- //Update the Private dict reference
- fdFontByteData = updateOffset(fdFontByteData, fdFontDict.get("Private").getOffset()
- + fdFontDict.get("Private").getOperandLengths().get(0),
- fdFontDict.get("Private").getOperandLengths().get(1),
- privateDictOffsets.get(i));
- writeBytes(fdFontByteData);
- }
- return offset;
- }
-
- private 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 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;
- for (int dataPos = 0; dataPos < data.length; dataPos++) {
- int b0 = data[dataPos] & 0xff;
- if (b0 == LOCAL_SUBROUTINE && hasLocalSubroutines) {
- 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");
- }
- } else if (b0 == GLOBAL_SUBROUTINE && hasGlobalSubroutines) {
- 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");
- }
- } else {
- dataPos += type2Parser.exec(b0, data, dataPos);
- }
- }
- }
-
- 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) {
- //Create the new array with the modified reference
- byte[] newData;
- int startRef = curDataPos - operand.getNumBytes();
- int length = operand.getNumBytes() + 1;
- byte[] preBytes = new byte[startRef];
- System.arraycopy(currentData, 0, preBytes, 0, startRef);
- int newBias = getBias(fullSubsetIndexSize);
- int newRef = curSubsetIndexSize - newBias;
- 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,
- currentData.length - (startRef + length));
- return concatArray(newData, postBytes);
- }
-
- public static byte[] createNewRef(int newRef, int[] operatorCode, int forceLength, boolean isDict) {
- byte[] newRefBytes;
- int sizeOfOperator = operatorCode.length;
- 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 && newRef <= 1131) || forceLength == 2) {
- newRefBytes = new byte[2 + sizeOfOperator];
- 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;
- } else if (newRef <= 875) {
- newRefBytes[0] = (byte)249;
- } else {
- newRefBytes[0] = (byte)250;
- }
- 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 >= -32768 && newRef <= 32767) || forceLength == 3) {
- newRefBytes = new byte[3 + sizeOfOperator];
- newRefBytes[0] = 28;
- newRefBytes[1] = (byte)(newRef >> 8);
- newRefBytes[2] = (byte)newRef;
- for (int i = 0; i < operatorCode.length; i++) {
- newRefBytes[3 + i] = (byte)operatorCode[i];
- }
- } else {
- newRefBytes = new byte[5 + sizeOfOperator];
- 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);
- newRefBytes[4] = (byte)newRef;
- for (int i = 0; i < operatorCode.length; i++) {
- newRefBytes[5 + i] = (byte)operatorCode[i];
- }
- }
- return newRefBytes;
- }
-
- public static byte[] concatArray(byte[] a, byte[] b) {
- int aLen = a.length;
- int bLen = b.length;
- byte[] c = new byte[aLen + bLen];
- System.arraycopy(a, 0, c, 0, aLen);
- System.arraycopy(b, 0, c, aLen, bLen);
- return c;
- }
-
- protected int writeIndex(List<byte[]> dataArray) {
- 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.
- int totLength = 1;
- for (byte[] aDataArray1 : dataArray) {
- totLength += aDataArray1.length;
- }
- 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;
- }
- this.writeByte(offSize);
- //Count the first offset 1
- hdrTotal += offSize;
- int total = 0;
- for (int i = 0; i < dataArray.size(); i++) {
- hdrTotal += offSize;
- int length = dataArray.get(i).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.");
- }
- }
- for (byte[] aDataArray : dataArray) {
- writeBytes(aDataArray);
- }
- return hdrTotal + total;
- }
-
- /**
- * 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 {
- writeByte(0);
- for (int gid : gidToSID.keySet()) {
- if (cidFont && gid == 0) {
- continue;
- }
- writeCard16((cidFont) ? gid : gidToSID.get(gid));
- }
- }
-
- 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(int topDictOffset, int charsetOffset, int charStringOffset,
- int privateDictOffset, int localIndexOffset, int encodingOffset)
- 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);
- }
-
- int dataPos = 3 + (cffReader.getTopDictIndex().getOffSize()
- * cffReader.getTopDictIndex().getOffsets().length);
- int dataTopDictOffset = topDictOffset + dataPos;
-
- updateFixedOffsets(topDICT, dataTopDictOffset, charsetOffset, charStringOffset, encodingOffset);
-
- if (privateDICT != null) {
- //Private index offset in the top dict
- int oldPrivateOffset = dataTopDictOffset + privateEntry.getOffset();
- output = updateOffset(output, oldPrivateOffset + privateEntry.getOperandLengths().get(0),
- privateEntry.getOperandLengths().get(1), privateDictOffset);
-
- //Update the local subroutine index offset in the private dict
- DICTEntry subroutines = privateDICT.get("Subrs");
- if (subroutines != null) {
- int oldLocalSubrOffset = privateDictOffset + subroutines.getOffset();
- output = updateOffset(output, oldLocalSubrOffset, subroutines.getOperandLength(),
- (localIndexOffset - privateDictOffset));
- }
- }
- }
-
- protected void updateFixedOffsets(Map<String, DICTEntry> topDICT, int dataTopDictOffset,
- int charsetOffset, int charStringOffset, int encodingOffset) {
- //Charset offset in the top dict
- DICTEntry charset = topDICT.get("charset");
- int oldCharsetOffset = dataTopDictOffset + charset.getOffset();
- output = updateOffset(output, oldCharsetOffset, charset.getOperandLength(), charsetOffset);
-
- //Char string index offset in the private dict
- DICTEntry charString = topDICT.get("CharStrings");
- int oldCharStringOffset = dataTopDictOffset + charString.getOffset();
- output = updateOffset(output, oldCharStringOffset, charString.getOperandLength(), charStringOffset);
-
- DICTEntry encodingEntry = topDICT.get("Encoding");
- if (encodingEntry != null && encodingEntry.getOperands().get(0).intValue() != 0
- && encodingEntry.getOperands().get(0).intValue() != 1) {
- int oldEncodingOffset = dataTopDictOffset + encodingEntry.getOffset();
- output = updateOffset(output, oldEncodingOffset, encodingEntry.getOperandLength(), encodingOffset);
- }
- }
-
- protected void updateCIDOffsets(int topDictDataOffset, int fdArrayOffset, int fdSelectOffset,
- int charsetOffset, int charStringOffset, int encodingOffset) {
- LinkedHashMap<String, DICTEntry> topDict = cffReader.getTopDictEntries();
-
- DICTEntry fdArrayEntry = topDict.get("FDArray");
- if (fdArrayEntry != null) {
- output = updateOffset(output, topDictDataOffset + fdArrayEntry.getOffset() - 1,
- fdArrayEntry.getOperandLength(), fdArrayOffset);
- }
-
- DICTEntry fdSelect = topDict.get("FDSelect");
- if (fdSelect != null) {
- output = updateOffset(output, topDictDataOffset + fdSelect.getOffset() - 1,
- fdSelect.getOperandLength(), fdSelectOffset);
- }
-
- updateFixedOffsets(topDict, topDictDataOffset, charsetOffset, charStringOffset, encodingOffset);
- }
-
- protected byte[] updateOffset(byte[] out, int position, int length, int replacement) {
- switch (length) {
- case 1:
- out[position] = (byte)(replacement + 139);
- break;
- case 2:
- 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:
- 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:
- }
- return out;
- }
-
- /**
- * Appends a byte to the output array,
- * updates currentPost but not realSize
- */
- protected void writeByte(int b) {
- output[currentPos++] = (byte)b;
- realSize++;
- }
-
- /**
- * Appends a USHORT to the output array,
- * updates currentPost but not realSize
- */
- protected void writeCard16(int s) {
- byte b1 = (byte)((s >> 8) & 0xff);
- byte b2 = (byte)(s & 0xff);
- writeByte(b1);
- writeByte(b2);
- }
-
- private void writeThreeByteNumber(int s) {
- byte b1 = (byte)((s >> 16) & 0xFF);
- byte b2 = (byte)((s >> 8) & 0xFF);
- byte b3 = (byte)(s & 0xFF);
- writeByte(b1);
- writeByte(b2);
- writeByte(b3);
- }
-
- /**
- * Appends a ULONG to the output array,
- * at the given position
- */
- private void writeULong(int s) {
- byte b1 = (byte)((s >> 24) & 0xff);
- byte b2 = (byte)((s >> 16) & 0xff);
- byte b3 = (byte)((s >> 8) & 0xff);
- byte b4 = (byte)(s & 0xff);
- writeByte(b1);
- writeByte(b2);
- writeByte(b3);
- writeByte(b4);
- }
-
- /**
- * Returns a subset of the fonts (readFont() MUST be called first in order to create the
- * subset).
- * @return byte array
- */
- public byte[] getFontSubset() {
- byte[] ret = new byte[realSize];
- System.arraycopy(output, 0, ret, 0, realSize);
- return ret;
- }
-
- /**
- * Returns the parsed CFF data for the original font.
- * @return The CFFDataReader contaiing the parsed data
- */
- public CFFDataReader getCFFReader() {
- return cffReader;
- }
- }
|