/** * Function Adds the new private dicts (only for the FDs used) to the list * @param Font the font * @param fdPrivate OffsetItem array one element for each private * @param fdPrivateBase IndexBaseItem array one element for each private * @param fdSubrs OffsetItem array one element for each private * @throws IOException */ internal void ReconstructPrivateDict(int Font,OffsetItem[] fdPrivate,IndexBaseItem[] fdPrivateBase, OffsetItem[] fdSubrs) { // For each fdarray private dict check if that FD is used. // if is used build a new one by changing the subrs offset // Else do nothing for (int i=0;i<fonts[Font].fdprivateOffsets.Length;i++) { //if (FDArrayUsed.ContainsKey(i)) //{ // Mark beginning OutputList.Add(new MarkerItem(fdPrivate[i])); fdPrivateBase[i] = new IndexBaseItem(); OutputList.Add(fdPrivateBase[i]); // Goto begining of objects Seek(fonts[Font].fdprivateOffsets[i]); while (GetPosition() < fonts[Font].fdprivateOffsets[i]+fonts[Font].fdprivateLengths[i]) { 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") { fdSubrs[i] = new DictOffsetItem(); OutputList.Add(fdSubrs[i]); OutputList.Add(new UInt8Item((char)19)); // Subrs } // Else copy the entire range else OutputList.Add(new RangeItem(buf,p1,p2-p1)); } //} } }
/** * Function subsets the FDArray and builds the new one with new offsets * @param Font The font * @param fdPrivate OffsetItem Array (one for each FDArray) * @throws IOException */ 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<fonts[Font].FDArrayOffsets.Length-1;i++) { fdOffsets[i] = new IndexOffsetItem(fonts[Font].FDArrayOffsize); OutputList.Add(fdOffsets[i]); } // Declare beginning of the object array IndexBaseItem fdArrayBase = new IndexBaseItem(); OutputList.Add(fdArrayBase); // For each object check if that FD is used. // if is used build a new one by changing the private object // Else do nothing // At the end of each object mark its ending (Even if wasn't written) for (int k=0; k<fonts[Font].FDArrayOffsets.Length-1; k++) { //if (FDArrayUsed.ContainsKey(k)) //{ // Goto begining of objects Seek(fonts[Font].FDArrayOffsets[k]); while (GetPosition() < fonts[Font].FDArrayOffsets[k+1]) { int p1 = GetPosition(); GetDictItem(); int p2 = GetPosition(); // If the dictItem is the "Private" then compute and copy length, // use marker for offset and write operator number if (key=="Private") { // Save the original length of the private dict int NewSize = (int)args[0]; // Save the size of the offset to the subrs in that private int OrgSubrsOffsetSize = CalcSubrOffsetSize(fonts[Font].fdprivateOffsets[k],fonts[Font].fdprivateLengths[k]); // Increase the private's length accordingly if (OrgSubrsOffsetSize != 0) NewSize += 5-OrgSubrsOffsetSize; // Insert the new size, OffsetItem and operator key number OutputList.Add(new DictNumberItem(NewSize)); fdPrivate[k] = new DictOffsetItem(); OutputList.Add(fdPrivate[k]); OutputList.Add(new UInt8Item((char)18)); // Private // Go back to place Seek(p2); } // Else copy the entire range else // other than private OutputList.Add(new RangeItem(buf,p1,p2-p1)); } //} // Mark the ending of the object (even if wasn't written) OutputList.Add(new IndexMarkerItem(fdOffsets[k],fdArrayBase)); } }
/** * The function builds the new output stream according to the subset process * @param Font the font * @return the subseted font stream * @throws IOException */ protected byte[] BuildNewFile(int Font) { // Prepare linked list for new font components OutputList = new List<Item>(); // copy the header of the font CopyHeader(); // create a name index BuildIndexHeader(1,1,1); OutputList.Add(new UInt8Item((char)( 1+fonts[Font].name.Length))); OutputList.Add(new StringItem(fonts[Font].name)); // create the topdict Index BuildIndexHeader(1,2,1); OffsetItem topdictIndex1Ref = new IndexOffsetItem(2); OutputList.Add(topdictIndex1Ref); IndexBaseItem topdictBase = new IndexBaseItem(); OutputList.Add(topdictBase); // Initialise 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.Add(new DictNumberItem(fonts[Font].nstrings)); OutputList.Add(new DictNumberItem(fonts[Font].nstrings+1)); OutputList.Add(new DictNumberItem(0)); OutputList.Add(new UInt8Item((char)12)); OutputList.Add(new UInt8Item((char)30)); // create a CIDCount key OutputList.Add(new DictNumberItem(fonts[Font].nglyphs)); OutputList.Add(new UInt8Item((char)12)); OutputList.Add(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 untill 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 manualy 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.Add(new IndexMarkerItem(topdictIndex1Ref,topdictBase)); // Copy the string index if (fonts[Font].isCID) OutputList.Add(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.Add(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.Add(new MarkerItem(fdselectRef)); // If an FDSelect exists copy it if (fonts[Font].fdselectOffset>=0) OutputList.Add(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 entirly OutputList.Add(new MarkerItem(charsetRef)); OutputList.Add(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.Add(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.Add(PrivateBase); OutputList.Add(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.Add(new MarkerItem(charstringsRef)); // Add the subsetted charstring OutputList.Add(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 foreach (Item item in OutputList) { item.Increment(currentOffset); } // Compute the Xref for each of the offset items foreach (Item item in OutputList) { item.Xref(); } int size = currentOffset[0]; byte[] b = new byte[size]; // Emit all the items into the new byte array foreach (Item item in OutputList) { item.Emit(b); } // Return the new stream return b; }
/** * Function reconstructs the FDArray, PrivateDict and LSubr for CID fonts * @param Font the font * @throws IOException */ 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); }
/** get a single CID font. The PDF architecture (1.4) * supports 16-bit strings only with CID CFF fonts, not * in Type-1 CFF fonts, so we convert the font to CID if * it is in the Type-1 format. * Two other tasks that we need to do are to select * only a single font from the CFF package (this again is * a PDF restriction) and to subset the CharStrings glyph * description. */ public byte[] GetCID(String fontName) //throws java.io.FileNotFoundException { int j; for (j=0; j<fonts.Length; j++) if (fontName.Equals(fonts[j].name)) break; if (j==fonts.Length) return null; List<Item> l = new List<Item>(); // copy the header Seek(0); int major = GetCard8(); int minor = GetCard8(); int hdrSize = GetCard8(); int offSize = GetCard8(); nextIndexOffset = hdrSize; l.Add(new RangeItem(buf,0,hdrSize)); int nglyphs=-1, nstrings=-1; if ( ! fonts[j].isCID ) { // count the glyphs Seek(fonts[j].charstringsOffset); nglyphs = GetCard16(); Seek(stringIndexOffset); nstrings = GetCard16()+standardStrings.Length; //System.err.Println("number of glyphs = "+nglyphs); } // create a name index l.Add(new UInt16Item((char)1)); // count l.Add(new UInt8Item((char)1)); // offSize l.Add(new UInt8Item((char)1)); // first offset l.Add(new UInt8Item((char)( 1+fonts[j].name.Length ))); l.Add(new StringItem(fonts[j].name)); // create the topdict Index l.Add(new UInt16Item((char)1)); // count l.Add(new UInt8Item((char)2)); // offSize l.Add(new UInt16Item((char)1)); // first offset OffsetItem topdictIndex1Ref = new IndexOffsetItem(2); l.Add(topdictIndex1Ref); IndexBaseItem topdictBase = new IndexBaseItem(); l.Add(topdictBase); /* int maxTopdictLen = (topdictOffsets[j+1]-topdictOffsets[j]) + 9*2 // at most 9 new keys + 8*5 // 8 new integer arguments + 3*2;// 3 new SID arguments */ //int topdictNext = 0; //byte[] topdict = new byte[maxTopdictLen]; OffsetItem charsetRef = new DictOffsetItem(); OffsetItem charstringsRef = new DictOffsetItem(); OffsetItem fdarrayRef = new DictOffsetItem(); OffsetItem fdselectRef = new DictOffsetItem(); if ( !fonts[j].isCID ) { // create a ROS key l.Add(new DictNumberItem(nstrings)); l.Add(new DictNumberItem(nstrings+1)); l.Add(new DictNumberItem(0)); l.Add(new UInt8Item((char)12)); l.Add(new UInt8Item((char)30)); // create a CIDCount key l.Add(new DictNumberItem(nglyphs)); l.Add(new UInt8Item((char)12)); l.Add(new UInt8Item((char)34)); // 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. } // create an FDArray key l.Add(fdarrayRef); l.Add(new UInt8Item((char)12)); l.Add(new UInt8Item((char)36)); // create an FDSelect key l.Add(fdselectRef); l.Add(new UInt8Item((char)12)); l.Add(new UInt8Item((char)37)); // create an charset key l.Add(charsetRef); l.Add(new UInt8Item((char)15)); // create a CharStrings key l.Add(charstringsRef); l.Add(new UInt8Item((char)17)); Seek(topdictOffsets[j]); while (GetPosition() < topdictOffsets[j+1]) { int p1 = GetPosition(); GetDictItem(); int p2 = GetPosition(); if (key=="Encoding" || key=="Private" || key=="FDSelect" || key=="FDArray" || key=="charset" || key=="CharStrings" ) { // just drop them } else { l.Add(new RangeItem(buf,p1,p2-p1)); } } l.Add(new IndexMarkerItem(topdictIndex1Ref,topdictBase)); // Copy the string index and 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 if (fonts[j].isCID) { l.Add(GetEntireIndexRange(stringIndexOffset)); } else { String fdFontName = fonts[j].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; l.Add(new UInt16Item((char)((stringOffsets.Length-1)+3))); // count l.Add(new UInt8Item((char)stringsIndexOffSize)); // offSize foreach (int stringOffset in stringOffsets) l.Add(new IndexOffsetItem(stringsIndexOffSize, stringOffset-stringsBaseOffset)); int currentStringsOffset = stringOffsets[stringOffsets.Length-1] - stringsBaseOffset; //l.Add(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset)); currentStringsOffset += ("Adobe").Length; l.Add(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset)); currentStringsOffset += ("Identity").Length; l.Add(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset)); currentStringsOffset += fdFontName.Length; l.Add(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset)); l.Add(new RangeItem(buf,stringOffsets[0],origStringsLen)); l.Add(new StringItem(extraStrings)); } // copy the global subroutine index l.Add(GetEntireIndexRange(gsubrIndexOffset)); // deal with fdarray, fdselect, and the font descriptors if (fonts[j].isCID) { // copy the FDArray, FDSelect, charset } else { // create FDSelect l.Add(new MarkerItem(fdselectRef)); l.Add(new UInt8Item((char)3)); // format identifier l.Add(new UInt16Item((char)1)); // nRanges l.Add(new UInt16Item((char)0)); // Range[0].firstGlyph l.Add(new UInt8Item((char)0)); // Range[0].fd l.Add(new UInt16Item((char)nglyphs)); // sentinel // recreate a new charset // This format is suitable only for fonts without subsetting l.Add(new MarkerItem(charsetRef)); l.Add(new UInt8Item((char)2)); // format identifier l.Add(new UInt16Item((char)1)); // first glyph in range (ignore .notdef) l.Add(new UInt16Item((char)(nglyphs-1))); // nLeft // now all are covered, the data structure is complete. // create a font dict index (fdarray) l.Add(new MarkerItem(fdarrayRef)); l.Add(new UInt16Item((char)1)); l.Add(new UInt8Item((char)1)); // offSize l.Add(new UInt8Item((char)1)); // first offset OffsetItem privateIndex1Ref = new IndexOffsetItem(1); l.Add(privateIndex1Ref); IndexBaseItem privateBase = new IndexBaseItem(); l.Add(privateBase); // looking at the PS that acrobat generates from a PDF with // a CFF opentype font embeded with an identity-H encoding, // it seems that it does not need a FontName. //l.Add(new DictNumberItem((standardStrings.length+(stringOffsets.length-1)+2))); //l.Add(new UInt8Item((char)12)); //l.Add(new UInt8Item((char)38)); // FontName l.Add(new DictNumberItem(fonts[j].privateLength)); OffsetItem privateRef = new DictOffsetItem(); l.Add(privateRef); l.Add(new UInt8Item((char)18)); // Private l.Add(new IndexMarkerItem(privateIndex1Ref,privateBase)); // copy the private index & local subroutines l.Add(new MarkerItem(privateRef)); // copy the private dict and the local subroutines. // the length of the private dict seems to NOT include // the local subroutines. l.Add(new RangeItem(buf,fonts[j].privateOffset,fonts[j].privateLength)); if (fonts[j].privateSubrs >= 0) { //System.err.Println("has subrs="+fonts[j].privateSubrs+" ,len="+fonts[j].privateLength); l.Add(GetEntireIndexRange(fonts[j].privateSubrs)); } } // copy the charstring index l.Add(new MarkerItem(charstringsRef)); l.Add(GetEntireIndexRange(fonts[j].charstringsOffset)); // now create the new CFF font int[] currentOffset = new int[1]; currentOffset[0] = 0; foreach (Item item in l) { item.Increment(currentOffset); } foreach (Item item in l) { item.Xref(); } int size = currentOffset[0]; byte[] b = new byte[size]; foreach (Item item in l) { item.Emit(b); } return b; }