/* * $Id: CFFFontSubset.java 5621 2012-12-17 14:22:15Z blowagie $ * * This file is part of the iText (R) project. * Copyright (c) 1998-2012 1T3XT BVBA * Authors: Bruno Lowagie, Paulo Soares, et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3 * as published by the Free Software Foundation with the addition of the * following permission added to Section 15 as permitted in Section 7(a): * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Affero General Public License for more details. * You should have received a copy of the GNU Affero General Public License * along with this program; if not, see http://www.gnu.org/licenses or write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA, 02110-1301 USA, or download the license from the following URL: * http://itextpdf.com/terms-of-use/ * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License. * * In accordance with Section 7(b) of the GNU Affero General Public License, * a covered work must retain the producer line in every PDF that is created * or manipulated using iText. * * You can be released from the requirements of the license by purchasing * a commercial license. Buying such a license is mandatory as soon as you * develop commercial activities involving the iText software without * disclosing the source code of your own applications. * These activities include: offering paid services to customers as an ASP, * serving PDFs on the fly in a web application, shipping iText with a closed * source product. * * For more information, please contact iText Software Corp. at this * address: sales@itextpdf.com */ package com.lowagie.text.pdf; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; /** * This Class subsets a CFF Type Font. The subset is preformed for CID fonts and NON CID fonts. * The Charstring is subsetted for both types. For CID fonts only the FDArray which are used are embedded. * The Lsubroutines of the FDArrays used are subsetted as well. The Subroutine subset supports both Type1 and Type2 * formatting although only tested on Type2 Format. * For Non CID the Lsubroutines are subsetted. On both types the Gsubroutines is subsetted. * A font which was not of CID type is transformed into CID as a part of the subset process. * The CID synthetic creation was written by Sivan Toledo (sivan@math.tau.ac.il) * @author Oren Manor (manorore@post.tau.ac.il) and Ygal Blum (blumygal@post.tau.ac.il) */ public class CFFFontSubset extends CFFFont { /** * The Strings in this array represent Type1/Type2 operator names */ static final String SubrsFunctions[] = { "RESERVED_0","hstem","RESERVED_2","vstem","vmoveto","rlineto","hlineto","vlineto", "rrcurveto","RESERVED_9","callsubr","return","escape","RESERVED_13", "endchar","RESERVED_15","RESERVED_16","RESERVED_17","hstemhm","hintmask", "cntrmask","rmoveto","hmoveto","vstemhm","rcurveline","rlinecurve","vvcurveto", "hhcurveto","shortint","callgsubr","vhcurveto","hvcurveto" }; /** * The Strings in this array represent Type1/Type2 escape operator names */ static final String SubrsEscapeFuncs[] = { "RESERVED_0","RESERVED_1","RESERVED_2","and","or","not","RESERVED_6", "RESERVED_7","RESERVED_8","abs","add","sub","div","RESERVED_13","neg", "eq","RESERVED_16","RESERVED_17","drop","RESERVED_19","put","get","ifelse", "random","mul","RESERVED_25","sqrt","dup","exch","index","roll","RESERVED_31", "RESERVED_32","RESERVED_33","hflex","flex","hflex1","flex1","RESERVED_REST" }; /** * Operator codes for unused CharStrings and unused local and global Subrs */ static final byte ENDCHAR_OP = 14; static final byte RETURN_OP = 11; /** * A HashMap containing the glyphs used in the text after being converted * to glyph number by the CMap */ HashMap GlyphsUsed; /** * The GlyphsUsed keys as an ArrayList */ ArrayList glyphsInList; /** * A HashSet for keeping the FDArrays being used by the font */ HashSet FDArrayUsed = new HashSet(); /** * A HashMaps array for keeping the subroutines used in each FontDict */ HashMap[] hSubrsUsed; /** * The SubroutinesUsed HashMaps as ArrayLists */ ArrayList[] lSubrsUsed; /** * A HashMap for keeping the Global subroutines used in the font */ HashMap hGSubrsUsed = new HashMap(); /** * The Global SubroutinesUsed HashMaps as ArrayLists */ ArrayList lGSubrsUsed = new ArrayList(); /** * A HashMap for keeping the subroutines used in a non-cid font */ HashMap hSubrsUsedNonCID = new HashMap(); /** * The SubroutinesUsed HashMap as ArrayList */ ArrayList lSubrsUsedNonCID = new ArrayList(); /** * An array of the new Indexes for the local Subr. One index for each FontDict */ byte[][] NewLSubrsIndex; /** * The new subroutines index for a non-cid font */ byte[] NewSubrsIndexNonCID; /** * The new global subroutines index of the font */ byte[] NewGSubrsIndex; /** * The new CharString of the font */ byte[] NewCharStringsIndex; /** * The bias for the global subroutines */ int GBias = 0; /** * The linked list for generating the new font stream */ LinkedList OutputList; /** * Number of arguments to the stem operators in a subroutine calculated recursively */ int NumOfHints=0; /** * C'tor for CFFFontSubset * @param rf - The font file * @param GlyphsUsed - a HashMap that contains the glyph used in the subset */ public CFFFontSubset(RandomAccessFileOrArray rf,HashMap GlyphsUsed){ // Use CFFFont c'tor in order to parse the font file. super(rf); this.GlyphsUsed = GlyphsUsed; //Put the glyphs into a list glyphsInList = new ArrayList(GlyphsUsed.keySet()); for (int i=0;i=0) { // Process the FDSelect readFDSelect(i); // Build the FDArrayUsed hashmap BuildFDArrayUsed(i); } if (fonts[i].isCID) // Build the FD Array used Hash Map ReadFDArray(i); // compute the charset length fonts[i].CharsetLength = CountCharset(fonts[i].charsetOffset,fonts[i].nglyphs); } } /** * Calculates the length of the charset according to its format * @param Offset The Charset Offset * @param NumofGlyphs Number of glyphs in the font * @return the length of the Charset */ int CountCharset(int Offset,int NumofGlyphs){ int format; int Length=0; seek(Offset); // Read the format format = getCard8(); // Calc according to format switch (format){ case 0: Length = 1+2*NumofGlyphs; break; case 1: Length = 1+3*CountRange(NumofGlyphs,1); break; case 2: Length = 1+4*CountRange(NumofGlyphs,2); break; default: break; } return Length; } /** * Function calculates the number of ranges in the Charset * @param NumofGlyphs The number of glyphs in the font * @param Type The format of the Charset * @return The number of ranges in the Charset data structure */ int CountRange(int NumofGlyphs,int Type){ int num=0; char Sid; int i=1,nLeft; while (i= 0) GBias = CalcBias(gsubrIndexOffset,j); // Prepare the new CharStrings Index BuildNewCharString(j); // Prepare the new Global and Local Subrs Indices BuildNewLGSubrs(j); // Build the new file byte[] Ret = BuildNewFile(j); return Ret; } finally { try { buf.close(); } catch (Exception e) { // empty on purpose } } } /** * Function calcs bias according to the CharString type and the count * of the subrs * @param Offset The offset to the relevant subrs index * @param Font the font * @return The calculated Bias */ protected int CalcBias(int Offset,int Font) { seek(Offset); int nSubrs = getCard16(); // If type==1 -> bias=0 if (fonts[Font].CharstringType == 1) return 0; // else calc according to the count else if (nSubrs < 1240) return 107; else if (nSubrs < 33900) return 1131; else return 32768; } /** *Function uses BuildNewIndex to create the new index of the subset charstrings * @param FontIndex the font * @throws IOException */ protected void BuildNewCharString(int FontIndex) throws IOException { NewCharStringsIndex = BuildNewIndex(fonts[FontIndex].charstringsOffsets,GlyphsUsed,ENDCHAR_OP); } /** * Function builds the new local & global subsrs indices. IF CID then All of * the FD Array lsubrs will be subsetted. * @param Font the font * @throws IOException */ @SuppressWarnings("unchecked") protected void BuildNewLGSubrs(int Font)throws IOException { // If the font is CID then the lsubrs are divided into FontDicts. // for each FD array the lsubrs will be subsetted. if(fonts[Font].isCID) { // Init the hashmap-array and the arraylist-array to hold the subrs used // in each private dict. hSubrsUsed = new HashMap[fonts[Font].fdprivateOffsets.length]; lSubrsUsed = new ArrayList[fonts[Font].fdprivateOffsets.length]; // A [][] which will store the byte array for each new FD Array lsubs index NewLSubrsIndex = new byte[fonts[Font].fdprivateOffsets.length][]; // An array to hold the offset for each Lsubr index fonts[Font].PrivateSubrsOffset = new int[fonts[Font].fdprivateOffsets.length]; // A [][] which will store the offset array for each lsubr index fonts[Font].PrivateSubrsOffsetsArray = new int[fonts[Font].fdprivateOffsets.length][]; // Put the FDarrayUsed into a list ArrayList FDInList = new ArrayList(FDArrayUsed); // For each FD array which is used subset the lsubr for (int j=0;j(); lSubrsUsed[FD] = new ArrayList(); //Reads the private dicts looking for the subr operator and // store both the offset for the index and its offset array BuildFDSubrsOffsets(Font,FD); // Verify that FDPrivate has a LSubrs index if(fonts[Font].PrivateSubrsOffset[FD]>=0) { //Scans the Charstring data storing the used Local and Global subroutines // by the glyphs. Scans the Subrs recursively. BuildSubrUsed(Font,FD,fonts[Font].PrivateSubrsOffset[FD],fonts[Font].PrivateSubrsOffsetsArray[FD],hSubrsUsed[FD],lSubrsUsed[FD]); // Builds the New Local Subrs index NewLSubrsIndex[FD] = BuildNewIndex(fonts[Font].PrivateSubrsOffsetsArray[FD],hSubrsUsed[FD],RETURN_OP); } } } // If the font is not CID && the Private Subr exists then subset: else if (fonts[Font].privateSubrs>=0) { // Build the subrs offsets; fonts[Font].SubrsOffsets = getIndex(fonts[Font].privateSubrs); //Scans the Charstring data storing the used Local and Global subroutines // by the glyphs. Scans the Subrs recursively. BuildSubrUsed(Font,-1,fonts[Font].privateSubrs,fonts[Font].SubrsOffsets,hSubrsUsedNonCID,lSubrsUsedNonCID); } // For all fonts subset the Global Subroutines // Scan the Global Subr Hashmap recursively on the Gsubrs BuildGSubrsUsed(Font); if (fonts[Font].privateSubrs>=0) // Builds the New Local Subrs index NewSubrsIndexNonCID = BuildNewIndex(fonts[Font].SubrsOffsets,hSubrsUsedNonCID,RETURN_OP); //Builds the New Global Subrs index NewGSubrsIndex = BuildNewIndex(gsubrOffsets,hGSubrsUsed,RETURN_OP); } /** * The function finds for the FD array processed the local subr offset and its * offset array. * @param Font the font * @param FD The FDARRAY processed */ protected void BuildFDSubrsOffsets(int Font,int FD) { // Initiate to -1 to indicate lsubr operator present fonts[Font].PrivateSubrsOffset[FD] = -1; // Goto beginning of objects seek(fonts[Font].fdprivateOffsets[FD]); // While in the same object: while (getPosition() < fonts[Font].fdprivateOffsets[FD]+fonts[Font].fdprivateLengths[FD]) { getDictItem(); // If the dictItem is the "Subrs" then find and store offset, if (key=="Subrs") fonts[Font].PrivateSubrsOffset[FD] = ((Integer)args[0]).intValue()+fonts[Font].fdprivateOffsets[FD]; } //Read the lsubr index if the lsubr was found if (fonts[Font].PrivateSubrsOffset[FD] >= 0) fonts[Font].PrivateSubrsOffsetsArray[FD] = getIndex(fonts[Font].PrivateSubrsOffset[FD]); } /** * Function uses ReadAsubr on the glyph used to build the LSubr & Gsubr HashMap. * The HashMap (of the lsubr only) is then scanned recursively for Lsubr & Gsubrs * calls. * @param Font the font * @param FD FD array processed. 0 indicates function was called by non CID font * @param SubrOffset the offset to the subr index to calc the bias * @param SubrsOffsets the offset array of the subr index * @param hSubr HashMap of the subrs used * @param lSubr ArrayList of the subrs used */ protected void BuildSubrUsed(int Font,int FD,int SubrOffset,int[] SubrsOffsets,HashMap hSubr,ArrayList lSubr) { // Calc the Bias for the subr index int LBias = CalcBias(SubrOffset,Font); // For each glyph used find its GID, start & end pos for (int i=0;i= 0) { EmptyStack(); NumOfHints=0; // Using FDSELECT find the FD Array the glyph belongs to. int GlyphFD = fonts[Font].FDSelect[glyph]; // If the Glyph is part of the FD being processed if (GlyphFD == FD) // Find the Subrs called by the glyph and insert to hash: ReadASubr(Start,End,GBias,LBias,hSubr,lSubr,SubrsOffsets); } else // If the font is not CID //Find the Subrs called by the glyph and insert to hash: ReadASubr(Start,End,GBias,LBias,hSubr,lSubr,SubrsOffsets); } // For all Lsubrs used, check recursively for Lsubr & Gsubr used for (int i=0;i=0) { // Read and process the subr int Start = SubrsOffsets[Subr]; int End = SubrsOffsets[Subr+1]; ReadASubr(Start,End,GBias,LBias,hSubr,lSubr,SubrsOffsets); } } } /** * Function scans the Glsubr used ArrayList to find recursive calls * to Gsubrs and adds to Hashmap & ArrayList * @param Font the font */ protected void BuildGSubrsUsed(int Font) { int LBias = 0; int SizeOfNonCIDSubrsUsed = 0; if (fonts[Font].privateSubrs>=0) { LBias = CalcBias(fonts[Font].privateSubrs,Font); SizeOfNonCIDSubrsUsed = lSubrsUsedNonCID.size(); } // For each global subr used for (int i=0;i=0) { // Read the subr and process int Start = gsubrOffsets[Subr]; int End = gsubrOffsets[Subr+1]; if (fonts[Font].isCID) ReadASubr(Start,End,GBias,0,hGSubrsUsed,lGSubrsUsed,null); else { ReadASubr(Start,End,GBias,LBias,hSubrsUsedNonCID,lSubrsUsedNonCID,fonts[Font].SubrsOffsets); if (SizeOfNonCIDSubrsUsed < lSubrsUsedNonCID.size()) { for (int j=SizeOfNonCIDSubrsUsed;j=0) { // Read the subr and process int LStart = fonts[Font].SubrsOffsets[LSubr]; int LEnd = fonts[Font].SubrsOffsets[LSubr+1]; ReadASubr(LStart,LEnd,GBias,LBias,hSubrsUsedNonCID,lSubrsUsedNonCID,fonts[Font].SubrsOffsets); } } SizeOfNonCIDSubrsUsed = lSubrsUsedNonCID.size(); } } } } } /** * The function reads a subrs (glyph info) between begin and end. * Adds calls to a Lsubr to the hSubr and lSubrs. * Adds calls to a Gsubr to the hGSubr and lGSubrs. * @param begin the start point of the subr * @param end the end point of the subr * @param GBias the bias of the Global Subrs * @param LBias the bias of the Local Subrs * @param hSubr the HashMap for the lSubrs * @param lSubr the ArrayList for the lSubrs */ protected void ReadASubr(int begin,int end,int GBias,int LBias,HashMap hSubr,ArrayList lSubr,int[] LSubrsOffsets) { // Clear the stack for the subrs EmptyStack(); NumOfHints = 0; // Goto beginning of the subr seek(begin); while (getPosition() < end) { // Read the next command ReadCommand(); int pos = getPosition(); Object TopElement=null; if (arg_count > 0) TopElement = args[arg_count-1]; int NumOfArgs = arg_count; // Check the modification needed on the Argument Stack according to key; HandelStack(); // a call to a Lsubr if (key=="callsubr") { // Verify that arguments are passed if (NumOfArgs > 0) { // Calc the index of the Subrs int Subr = ((Integer)TopElement).intValue() + LBias; // If the subr isn't in the HashMap -> Put in if (!hSubr.containsKey(Integer.valueOf (Subr))) { hSubr.put(Integer.valueOf(Subr),null); lSubr.add(Integer.valueOf(Subr)); } CalcHints(LSubrsOffsets[Subr],LSubrsOffsets[Subr+1],LBias,GBias,LSubrsOffsets); seek(pos); } } // a call to a Gsubr else if (key=="callgsubr") { // Verify that arguments are passed if (NumOfArgs > 0) { // Calc the index of the Subrs int Subr = ((Integer)TopElement).intValue() + GBias; // If the subr isn't in the HashMap -> Put in if (!hGSubrsUsed.containsKey(Integer.valueOf (Subr))) { hGSubrsUsed.put(Integer.valueOf(Subr),null); lGSubrsUsed.add(Integer.valueOf(Subr)); } CalcHints(gsubrOffsets[Subr],gsubrOffsets[Subr+1],LBias,GBias,LSubrsOffsets); seek(pos); } } // A call to "stem" else if (key == "hstem" || key == "vstem" || key == "hstemhm" || key == "vstemhm") // Increment the NumOfHints by the number couples of of arguments NumOfHints += NumOfArgs/2; // A call to "mask" else if (key == "hintmask" || key == "cntrmask") { // Compute the size of the mask int SizeOfMask = NumOfHints/8; if (NumOfHints%8 != 0 || SizeOfMask == 0) SizeOfMask++; // Continue the pointer in SizeOfMask steps for (int i=0;i flush the stack */ protected int StackOpp() { if (key == "ifelse") return -3; if (key == "roll" || key == "put") return -2; if (key == "callsubr" || key == "callgsubr" || key == "add" || key == "sub" || key == "div" || key == "mul" || key == "drop" || key == "and" || key == "or" || key == "eq") return -1; if (key == "abs" || key == "neg" || key == "sqrt" || key == "exch" || key == "index" || key == "get" || key == "not" || key == "return") return 0; if (key == "random" || key == "dup") return 1; return 2; } /** * Empty the Type2 Stack * */ protected void EmptyStack() { // Null the arguments for (int i=0; i0) { args[arg_count-1]=null; arg_count--; } } /** * Add an item to the stack * */ protected void PushStack() { arg_count++; } /** * The function reads the next command after the file pointer is set */ protected void ReadCommand() { key = null; boolean gotKey = false; // Until a key is found while (!gotKey) { // Read the first Char char b0 = getCard8(); // decode according to the type1/type2 format if (b0 == 28) // the two next bytes represent a short int; { int first = getCard8(); int second = getCard8(); args[arg_count] = Integer.valueOf(first<<8 | second); arg_count++; continue; } if (b0 >= 32 && b0 <= 246) // The byte read is the byte; { args[arg_count] = Integer.valueOf(b0 - 139); arg_count++; continue; } if (b0 >= 247 && b0 <= 250) // The byte read and the next byte constitute a short int { int w = getCard8(); args[arg_count] = Integer.valueOf((b0-247)*256 + w + 108); arg_count++; continue; } if (b0 >= 251 && b0 <= 254)// Same as above except negative { int w = getCard8(); args[arg_count] = Integer.valueOf(-(b0-251)*256 - w - 108); arg_count++; continue; } if (b0 == 255)// The next for bytes represent a double. { int first = getCard8(); int second = getCard8(); int third = getCard8(); int fourth = getCard8(); args[arg_count] = Integer.valueOf(first<<24 | second<<16 | third<<8 | fourth); arg_count++; continue; } if (b0<=31 && b0 != 28) // An operator was found.. Set Key. { gotKey=true; // 12 is an escape command therefore the next byte is a part // of this command if (b0 == 12) { int b1 = getCard8(); if (b1>SubrsEscapeFuncs.length-1) b1 = SubrsEscapeFuncs.length-1; key = SubrsEscapeFuncs[b1]; } else key = SubrsFunctions[b0]; continue; } } } /** * The function reads the subroutine and returns the number of the hint in it. * If a call to another subroutine is found the function calls recursively. * @param begin the start point of the subr * @param end the end point of the subr * @param LBias the bias of the Local Subrs * @param GBias the bias of the Global Subrs * @param LSubrsOffsets The Offsets array of the subroutines * @return The number of hints in the subroutine read. */ protected int CalcHints(int begin,int end,int LBias,int GBias,int[] LSubrsOffsets) { // Goto beginning of the subr seek(begin); while (getPosition() < end) { // Read the next command ReadCommand(); int pos = getPosition(); Object TopElement = null; if (arg_count>0) TopElement = args[arg_count-1]; int NumOfArgs = arg_count; //Check the modification needed on the Argument Stack according to key; HandelStack(); // a call to a Lsubr if (key=="callsubr") { if (NumOfArgs>0) { int Subr = ((Integer)TopElement).intValue() + LBias; CalcHints(LSubrsOffsets[Subr],LSubrsOffsets[Subr+1],LBias,GBias,LSubrsOffsets); seek(pos); } } // a call to a Gsubr else if (key=="callgsubr") { if (NumOfArgs>0) { int Subr = ((Integer)TopElement).intValue() + GBias; CalcHints(gsubrOffsets[Subr],gsubrOffsets[Subr+1],LBias,GBias,LSubrsOffsets); seek(pos); } } // A call to "stem" else if (key == "hstem" || key == "vstem" || key == "hstemhm" || key == "vstemhm") // Increment the NumOfHints by the number couples of of arguments NumOfHints += NumOfArgs/2; // A call to "mask" else if (key == "hintmask" || key == "cntrmask") { // Compute the size of the mask int SizeOfMask = NumOfHints/8; if (NumOfHints%8 != 0 || SizeOfMask == 0) SizeOfMask++; // Continue the pointer in SizeOfMask steps for (int i=0;i Used,byte OperatorForUnusedEntries) throws IOException { int unusedCount = 0; int Offset=0; int[] NewOffsets = new int[Offsets.length]; // Build the Offsets Array for the Subset for (int i=0;i>> 8 & 0xff); NewIndex[Place++] = (byte) (Count >>> 0 & 0xff); // Write the offsize field NewIndex[Place++] = Offsize; // Write the offset array according to the offsize for (int newOffset : NewOffsets) { // The value to be written int Num = newOffset-NewOffsets[0]+1; // Write in bytes according to the offsize switch (Offsize) { case 4: NewIndex[Place++] = (byte) (Num >>> 24 & 0xff); case 3: NewIndex[Place++] = (byte) (Num >>> 16 & 0xff); case 2: NewIndex[Place++] = (byte) (Num >>> 8 & 0xff); case 1: NewIndex[Place++] = (byte) (Num >>> 0 & 0xff); } } // Write the new object array one by one for (byte newObject : NewObjects) { NewIndex[Place++] = newObject; } // Return the new index return NewIndex; } /** * The function builds the new output stream according to the subset process * @param Font the font * @return the subsetted font stream */ protected byte[] BuildNewFile(int Font) { // Prepare linked list for new font components OutputList = new LinkedList(); // copy the header of the font CopyHeader(); // create a name index BuildIndexHeader(1,1,1); OutputList.addLast(new UInt8Item((char)( 1+fonts[Font].name.length() ))); OutputList.addLast(new StringItem(fonts[Font].name)); // create the topdict Index BuildIndexHeader(1,2,1); OffsetItem topdictIndex1Ref = new IndexOffsetItem(2); OutputList.addLast(topdictIndex1Ref); IndexBaseItem topdictBase = new IndexBaseItem(); OutputList.addLast(topdictBase); // Initialize the Dict Items for later use OffsetItem charsetRef = new DictOffsetItem(); OffsetItem charstringsRef = new DictOffsetItem(); OffsetItem fdarrayRef = new DictOffsetItem(); OffsetItem fdselectRef = new DictOffsetItem(); OffsetItem privateRef = new DictOffsetItem(); // If the font is not CID create the following keys if ( !fonts[Font].isCID ) { // create a ROS key OutputList.addLast(new DictNumberItem(fonts[Font].nstrings)); OutputList.addLast(new DictNumberItem(fonts[Font].nstrings+1)); OutputList.addLast(new DictNumberItem(0)); OutputList.addLast(new UInt8Item((char)12)); OutputList.addLast(new UInt8Item((char)30)); // create a CIDCount key OutputList.addLast(new DictNumberItem(fonts[Font].nglyphs)); OutputList.addLast(new UInt8Item((char)12)); OutputList.addLast(new UInt8Item((char)34)); // Sivan's comments // What about UIDBase (12,35)? Don't know what is it. // I don't think we need FontName; the font I looked at didn't have it. } // Go to the TopDict of the font being processed seek(topdictOffsets[Font]); // Run until the end of the TopDict while (getPosition() < topdictOffsets[Font+1]) { int p1 = getPosition(); getDictItem(); int p2 = getPosition(); // The encoding key is disregarded since CID has no encoding if (key=="Encoding" // These keys will be added manually by the process. || key=="Private" || key=="FDSelect" || key=="FDArray" || key=="charset" || key=="CharStrings" ) { }else { //OtherWise copy key "as is" to the output list OutputList.add(new RangeItem(buf,p1,p2-p1)); } } // Create the FDArray, FDSelect, Charset and CharStrings Keys CreateKeys(fdarrayRef,fdselectRef,charsetRef,charstringsRef); // Mark the end of the top dict area OutputList.addLast(new IndexMarkerItem(topdictIndex1Ref,topdictBase)); // Copy the string index if (fonts[Font].isCID) OutputList.addLast(getEntireIndexRange(stringIndexOffset)); // If the font is not CID we need to append new strings. // We need 3 more strings: Registry, Ordering, and a FontName for one FD. // The total length is at most "Adobe"+"Identity"+63 = 76 else CreateNewStringIndex(Font); // copy the new subsetted global subroutine index OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(NewGSubrsIndex),0,NewGSubrsIndex.length)); // deal with fdarray, fdselect, and the font descriptors // If the font is CID: if (fonts[Font].isCID) { // copy the FDArray, FDSelect, charset // Copy FDSelect // Mark the beginning OutputList.addLast(new MarkerItem(fdselectRef)); // If an FDSelect exists copy it if (fonts[Font].fdselectOffset>=0) OutputList.addLast(new RangeItem(buf,fonts[Font].fdselectOffset,fonts[Font].FDSelectLength)); // Else create a new one else CreateFDSelect(fdselectRef,fonts[Font].nglyphs); // Copy the Charset // Mark the beginning and copy entirely OutputList.addLast(new MarkerItem(charsetRef)); OutputList.addLast(new RangeItem(buf,fonts[Font].charsetOffset,fonts[Font].CharsetLength)); // Copy the FDArray // If an FDArray exists if (fonts[Font].fdarrayOffset>=0) { // Mark the beginning OutputList.addLast(new MarkerItem(fdarrayRef)); // Build a new FDArray with its private dicts and their LSubrs Reconstruct(Font); } else // Else create a new one CreateFDArray(fdarrayRef,privateRef,Font); } // If the font is not CID else { // create FDSelect CreateFDSelect(fdselectRef,fonts[Font].nglyphs); // recreate a new charset CreateCharset(charsetRef,fonts[Font].nglyphs); // create a font dict index (fdarray) CreateFDArray(fdarrayRef,privateRef,Font); } // if a private dict exists insert its subsetted version if (fonts[Font].privateOffset>=0) { // Mark the beginning of the private dict IndexBaseItem PrivateBase = new IndexBaseItem(); OutputList.addLast(PrivateBase); OutputList.addLast(new MarkerItem(privateRef)); OffsetItem Subr = new DictOffsetItem(); // Build and copy the new private dict CreateNonCIDPrivate(Font,Subr); // Copy the new LSubrs index CreateNonCIDSubrs(Font,PrivateBase,Subr); } // copy the charstring index OutputList.addLast(new MarkerItem(charstringsRef)); // Add the subsetted charstring OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(NewCharStringsIndex),0,NewCharStringsIndex.length)); // now create the new CFF font int[] currentOffset = new int[1]; currentOffset[0] = 0; // Count and save the offset for each item Iterator listIter = OutputList.iterator(); while ( listIter.hasNext() ) { Item item = listIter.next(); item.increment(currentOffset); } // Compute the Xref for each of the offset items listIter = OutputList.iterator(); while ( listIter.hasNext() ) { Item item = listIter.next(); item.xref(); } int size = currentOffset[0]; byte[] b = new byte[size]; // Emit all the items into the new byte array listIter = OutputList.iterator(); while ( listIter.hasNext() ) { Item item = listIter.next(); item.emit(b); } // Return the new stream return b; } /** * Function Copies the header from the original fileto the output list */ protected void CopyHeader() { seek(0); int major = getCard8(); int minor = getCard8(); int hdrSize = getCard8(); int offSize = getCard8(); nextIndexOffset = hdrSize; OutputList.addLast(new RangeItem(buf,0,hdrSize)); } /** * Function Build the header of an index * @param Count the count field of the index * @param Offsize the offsize field of the index * @param First the first offset of the index */ protected void BuildIndexHeader(int Count,int Offsize,int First) { // Add the count field OutputList.addLast(new UInt16Item((char)Count)); // count // Add the offsize field OutputList.addLast(new UInt8Item((char)Offsize)); // offSize // Add the first offset according to the offsize switch(Offsize){ case 1: OutputList.addLast(new UInt8Item((char)First)); // first offset break; case 2: OutputList.addLast(new UInt16Item((char)First)); // first offset break; case 3: OutputList.addLast(new UInt24Item((char)First)); // first offset break; case 4: OutputList.addLast(new UInt32Item((char)First)); // first offset break; default: break; } } /** * Function adds the keys into the TopDict * @param fdarrayRef OffsetItem for the FDArray * @param fdselectRef OffsetItem for the FDSelect * @param charsetRef OffsetItem for the CharSet * @param charstringsRef OffsetItem for the CharString */ protected void CreateKeys(OffsetItem fdarrayRef,OffsetItem fdselectRef,OffsetItem charsetRef,OffsetItem charstringsRef) { // create an FDArray key OutputList.addLast(fdarrayRef); OutputList.addLast(new UInt8Item((char)12)); OutputList.addLast(new UInt8Item((char)36)); // create an FDSelect key OutputList.addLast(fdselectRef); OutputList.addLast(new UInt8Item((char)12)); OutputList.addLast(new UInt8Item((char)37)); // create an charset key OutputList.addLast(charsetRef); OutputList.addLast(new UInt8Item((char)15)); // create a CharStrings key OutputList.addLast(charstringsRef); OutputList.addLast(new UInt8Item((char)17)); } /** * Function takes the original string item and adds the new strings * to accommodate the CID rules * @param Font the font */ protected void CreateNewStringIndex(int Font) { String fdFontName = fonts[Font].name+"-OneRange"; if (fdFontName.length() > 127) fdFontName = fdFontName.substring(0,127); String extraStrings = "Adobe"+"Identity"+fdFontName; int origStringsLen = stringOffsets[stringOffsets.length-1] - stringOffsets[0]; int stringsBaseOffset = stringOffsets[0]-1; byte stringsIndexOffSize; if (origStringsLen+extraStrings.length() <= 0xff) stringsIndexOffSize = 1; else if (origStringsLen+extraStrings.length() <= 0xffff) stringsIndexOffSize = 2; else if (origStringsLen+extraStrings.length() <= 0xffffff) stringsIndexOffSize = 3; else stringsIndexOffSize = 4; OutputList.addLast(new UInt16Item((char)(stringOffsets.length-1+3))); // count OutputList.addLast(new UInt8Item((char)stringsIndexOffSize)); // offSize for (int stringOffset : stringOffsets) OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize, stringOffset-stringsBaseOffset)); int currentStringsOffset = stringOffsets[stringOffsets.length-1] - stringsBaseOffset; //l.addLast(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset)); currentStringsOffset += "Adobe".length(); OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset)); currentStringsOffset += "Identity".length(); OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset)); currentStringsOffset += fdFontName.length(); OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset)); OutputList.addLast(new RangeItem(buf,stringOffsets[0],origStringsLen)); OutputList.addLast(new StringItem(extraStrings)); } /** * Function creates new FDSelect for non-CID fonts. * The FDSelect built uses a single range for all glyphs * @param fdselectRef OffsetItem for the FDSelect * @param nglyphs the number of glyphs in the font */ protected void CreateFDSelect(OffsetItem fdselectRef,int nglyphs) { OutputList.addLast(new MarkerItem(fdselectRef)); OutputList.addLast(new UInt8Item((char)3)); // format identifier OutputList.addLast(new UInt16Item((char)1)); // nRanges OutputList.addLast(new UInt16Item((char)0)); // Range[0].firstGlyph OutputList.addLast(new UInt8Item((char)0)); // Range[0].fd OutputList.addLast(new UInt16Item((char)nglyphs)); // sentinel } /** * Function creates new CharSet for non-CID fonts. * The CharSet built uses a single range for all glyphs * @param charsetRef OffsetItem for the CharSet * @param nglyphs the number of glyphs in the font */ protected void CreateCharset(OffsetItem charsetRef,int nglyphs) { OutputList.addLast(new MarkerItem(charsetRef)); OutputList.addLast(new UInt8Item((char)2)); // format identifier OutputList.addLast(new UInt16Item((char)1)); // first glyph in range (ignore .notdef) OutputList.addLast(new UInt16Item((char)(nglyphs-1))); // nLeft } /** * Function creates new FDArray for non-CID fonts. * The FDArray built has only the "Private" operator that points to the font's * original private dict * @param fdarrayRef OffsetItem for the FDArray * @param privateRef OffsetItem for the Private Dict * @param Font the font */ protected void CreateFDArray(OffsetItem fdarrayRef,OffsetItem privateRef,int Font) { OutputList.addLast(new MarkerItem(fdarrayRef)); // Build the header (count=offsize=first=1) BuildIndexHeader(1,1,1); // Mark OffsetItem privateIndex1Ref = new IndexOffsetItem(1); OutputList.addLast(privateIndex1Ref); IndexBaseItem privateBase = new IndexBaseItem(); // Insert the private operands and operator OutputList.addLast(privateBase); // Calc the new size of the private after subsetting // Origianl size int NewSize = fonts[Font].privateLength; // Calc the original size of the Subr offset in the private int OrgSubrsOffsetSize = CalcSubrOffsetSize(fonts[Font].privateOffset,fonts[Font].privateLength); // Increase the ptivate's size if (OrgSubrsOffsetSize != 0) NewSize += 5-OrgSubrsOffsetSize; OutputList.addLast(new DictNumberItem(NewSize)); OutputList.addLast(privateRef); OutputList.addLast(new UInt8Item((char)18)); // Private OutputList.addLast(new IndexMarkerItem(privateIndex1Ref,privateBase)); } /** * Function reconstructs the FDArray, PrivateDict and LSubr for CID fonts * @param Font the font */ void Reconstruct(int Font) { // Init for later use OffsetItem[] fdPrivate = new DictOffsetItem[fonts[Font].FDArrayOffsets.length-1]; IndexBaseItem[] fdPrivateBase = new IndexBaseItem[fonts[Font].fdprivateOffsets.length]; OffsetItem[] fdSubrs = new DictOffsetItem[fonts[Font].fdprivateOffsets.length]; // Reconstruct each type ReconstructFDArray(Font,fdPrivate); ReconstructPrivateDict(Font,fdPrivate,fdPrivateBase,fdSubrs); ReconstructPrivateSubrs(Font,fdPrivateBase,fdSubrs); } /** * Function subsets the FDArray and builds the new one with new offsets * @param Font The font * @param fdPrivate OffsetItem Array (one for each FDArray) */ void ReconstructFDArray(int Font,OffsetItem[] fdPrivate) { // Build the header of the index BuildIndexHeader(fonts[Font].FDArrayCount,fonts[Font].FDArrayOffsize,1); // For each offset create an Offset Item OffsetItem[] fdOffsets = new IndexOffsetItem[fonts[Font].FDArrayOffsets.length-1]; for (int i=0;i= 0) { OutputList.addLast(new SubrMarkerItem(fdSubrs[i],fdPrivateBase[i])); if(NewLSubrsIndex[i]!=null) OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(NewLSubrsIndex[i]),0,NewLSubrsIndex[i].length)); } } } /** * Calculates how many byte it took to write the offset for the subrs in a specific * private dict. * @param Offset The Offset for the private dict * @param Size The size of the private dict * @return The size of the offset of the subrs in the private dict */ int CalcSubrOffsetSize(int Offset,int Size) { // Set the size to 0 int OffsetSize = 0; // Go to the beginning of the private dict seek(Offset); // Go until the end of the private dict while (getPosition() < Offset+Size) { int p1 = getPosition(); getDictItem(); int p2 = getPosition(); // When reached to the subrs offset if (key=="Subrs") { // The Offsize (minus the subrs key) OffsetSize = p2-p1-1; } // All other keys are ignored } // return the size return OffsetSize; } /** * Function computes the size of an index * @param indexOffset The offset for the computed index * @return The size of the index */ protected int countEntireIndexRange(int indexOffset) { // Go to the beginning of the index seek(indexOffset); // Read the count field int count = getCard16(); // If count==0 -> size=2 if (count==0) return 2; else { // Read the offsize field int indexOffSize = getCard8(); // Go to the last element of the offset array seek(indexOffset+2+1+count*indexOffSize); // The size of the object array is the value of the last element-1 int size = getOffset(indexOffSize)-1; // Return the size of the entire index return 2+1+(count+1)*indexOffSize+size; } } /** * The function creates a private dict for a font that was not CID * All the keys are copied as is except for the subrs key * @param Font the font * @param Subr The OffsetItem for the subrs of the private */ void CreateNonCIDPrivate(int Font,OffsetItem Subr) { // Go to the beginning of the private dict and read until the end seek(fonts[Font].privateOffset); while (getPosition() < fonts[Font].privateOffset+fonts[Font].privateLength) { int p1 = getPosition(); getDictItem(); int p2 = getPosition(); // If the dictItem is the "Subrs" then, // use marker for offset and write operator number if (key=="Subrs") { OutputList.addLast(Subr); OutputList.addLast(new UInt8Item((char)19)); // Subrs } // Else copy the entire range else OutputList.addLast(new RangeItem(buf,p1,p2-p1)); } } /** * the function marks the beginning of the subrs index and adds the subsetted subrs * index to the output list. * @param Font the font * @param PrivateBase IndexBaseItem for the private that's referencing to the subrs * @param Subrs OffsetItem for the subrs */ void CreateNonCIDSubrs(int Font,IndexBaseItem PrivateBase,OffsetItem Subrs) { // Mark the beginning of the Subrs index OutputList.addLast(new SubrMarkerItem(Subrs,PrivateBase)); // Put the subsetted new subrs index if (NewSubrsIndexNonCID != null) { OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(NewSubrsIndexNonCID),0,NewSubrsIndexNonCID.length)); } } }