| Method from com.lowagie.text.pdf.CFFFontSubset Detail: |
protected byte[] AssembleIndex(int[] NewOffsets,
byte[] NewObjects) {
// Calc the index' count field
char Count = (char)(NewOffsets.length-1);
// Calc the size of the object array
int Size = NewOffsets[NewOffsets.length-1];
// Calc the Offsize
byte Offsize;
if (Size < = 0xff) Offsize = 1;
else if (Size < = 0xffff) Offsize = 2;
else if (Size < = 0xffffff) Offsize = 3;
else Offsize = 4;
// The byte array for the new index. The size is calc by
// Count=2, Offsize=1, OffsetArray = Offsize*(Count+1), The object array
byte[] NewIndex = new byte[2+1+Offsize*(Count+1)+NewObjects.length];
// The counter for writing
int Place = 0;
// Write the count field
NewIndex[Place++] = (byte) ((Count > > > 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 i=0;i< NewOffsets.length;i++)
{
// The value to be written
int Num = NewOffsets[i]-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 (int i=0;i< NewObjects.length;i++)
{
NewIndex[Place++] = NewObjects[i];
}
// Return the new index
return NewIndex;
}
Function creates the new index, inserting the count,offsetsize,offset array
and object array. |
protected void BuildFDArrayUsed(int Font) {
int[] FDSelect = fonts[Font].FDSelect;
// For each glyph used
for (int i=0;i< glyphsInList.size();i++)
{
// Pop the glyphs index
int glyph = ((Integer)glyphsInList.get(i)).intValue();
// Pop the glyph's FD
int FD = FDSelect[glyph];
// Put the FD index into the FDArrayUsed HashMap
FDArrayUsed.put(new Integer(FD),null);
}
}
Function reads the FDSelect and builds the FDArrayUsed HashMap According to the glyphs used |
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]);
}
The function finds for the FD array processed the local subr offset and its
offset array. |
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< lGSubrsUsed.size();i++)
{
//Pop the value + check valid
int Subr = ((Integer)lGSubrsUsed.get(i)).intValue();
if (Subr < gsubrOffsets.length-1 && Subr >=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< lSubrsUsedNonCID.size();j++)
{
//Pop the value + check valid
int LSubr = ((Integer)lSubrsUsedNonCID.get(j)).intValue();
if (LSubr < fonts[Font].SubrsOffsets.length-1 && LSubr >=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();
}
}
}
}
}
Function scans the Glsubr used ArrayList to find recursive calls
to Gsubrs and adds to Hashmap & ArrayList |
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 Build the header of an index |
protected void BuildNewCharString(int FontIndex) throws IOException {
NewCharStringsIndex = BuildNewIndex(fonts[FontIndex].charstringsOffsets,GlyphsUsed);
}
Function uses BuildNewIndex to create the new index of the subset charstrings |
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 = (Item) listIter.next();
item.increment(currentOffset);
}
// Compute the Xref for each of the offset items
listIter = OutputList.iterator();
while ( listIter.hasNext() ) {
Item 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 = (Item) listIter.next();
item.emit(b);
}
// Return the new stream
return b;
}
The function builds the new output stream according to the subset process |
protected byte[] BuildNewIndex(int[] Offsets,
HashMap Used) throws IOException {
int Offset=0;
int[] NewOffsets = new int[Offsets.length];
// Build the Offsets Array for the Subset
for (int i=0;i< Offsets.length;++i)
{
NewOffsets[i] = Offset;
// If the object in the offset is also present in the used
// HashMap then increment the offset var by its size
if (Used.containsKey(new Integer(i)))
Offset += Offsets[i+1] - Offsets[i];
// Else the same offset is kept in i+1.
}
// Offset var determines the size of the object array
byte[] NewObjects = new byte[Offset];
// Build the new Object array
for (int i=0;i< Offsets.length-1;++i)
{
int start = NewOffsets[i];
int end = NewOffsets[i+1];
// If start != End then the Object is used
// So, we will copy the object data from the font file
if (start != end)
{
// All offsets are Global Offsets relative to the beginning of the font file.
// Jump the file pointer to the start address to read from.
buf.seek(Offsets[i]);
// Read from the buffer and write into the array at start.
buf.readFully(NewObjects, start, end-start);
}
}
// Use AssembleIndex to build the index from the offset & object arrays
return AssembleIndex(NewOffsets,NewObjects);
}
Function builds the new offset array, object array and assembles the index.
used for creating the glyph and subrs subsetted index |
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.keySet());
// For each FD array which is used subset the lsubr
for (int j=0;j< FDInList.size();j++)
{
// The FDArray index, Hash Map, Array List to work on
int FD = ((Integer)FDInList.get(j)).intValue();
hSubrsUsed[FD] = new HashMap();
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]);
}
}
}
// 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);
//Builds the New Global Subrs index
NewGSubrsIndex = BuildNewIndex(gsubrOffsets,hGSubrsUsed);
}
Function builds the new local & global subsrs indices. IF CID then All of
the FD Array lsubrs will be subsetted. |
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< glyphsInList.size();i++)
{
int glyph = ((Integer)glyphsInList.get(i)).intValue();
int Start = fonts[Font].charstringsOffsets[glyph];
int End = fonts[Font].charstringsOffsets[glyph+1];
// IF CID:
if (FD >= 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< lSubr.size();i++)
{
// Pop the subr value from the hash
int Subr = ((Integer)lSubr.get(i)).intValue();
// Ensure the Lsubr call is valid
if (Subr < SubrsOffsets.length-1 && Subr >=0)
{
// Read and process the subr
int Start = SubrsOffsets[Subr];
int End = SubrsOffsets[Subr+1];
ReadASubr(Start,End,GBias,LBias,hSubr,lSubr,SubrsOffsets);
}
}
}
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. |
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 calcs bias according to the CharString type and the count
of the subrs |
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< SizeOfMask;i++)
getCard8();
}
}
return NumOfHints;
}
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. |
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;
}
Calculates how many byte it took to write the offset for the subrs in a specific
private dict. |
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 Copies the header from the original fileto the output list |
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;
}
Calculates the length of the charset according to its format |
int CountRange(int NumofGlyphs,
int Type) {
int num=0;
char Sid;
int i=1,nLeft;
while (i< NumofGlyphs){
num++;
Sid = getCard16();
if (Type==1)
nLeft = getCard8();
else
nLeft = getCard16();
i += nLeft+1;
}
return num;
}
Function calculates the number of ranges in the Charset |
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 CharSet for non-CID fonts.
The CharSet built uses a single range for all glyphs |
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 creates new FDArray for non-CID fonts.
The FDArray built has only the "Private" operator that points to the font's
original private dict |
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 FDSelect for non-CID fonts.
The FDSelect built uses a single range for all glyphs |
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 adds the keys into the TopDict |
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 i=0; i< stringOffsets.length; i++)
OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize,
stringOffsets[i]-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 takes the original string item and adds the new strings
to accommodate the CID rules |
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 creates a private dict for a font that was not CID
All the keys are copied as is except for the subrs key |
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
OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(NewSubrsIndexNonCID),0,NewSubrsIndexNonCID.length));
}
the function marks the beginning of the subrs index and adds the subsetted subrs
index to the output list. |
protected void EmptyStack() {
// Null the arguments
for (int i=0; i< arg_count; i++) args[i]=null;
arg_count = 0;
}
|
protected void HandelStack() {
// Find out what the operator does to the stack
int StackHandel = StackOpp();
if (StackHandel < 2)
{
// The operators that enlarge the stack by one
if (StackHandel==1)
PushStack();
// The operators that pop the stack
else
{
// Abs value for the for loop
StackHandel *= -1;
for (int i=0;i< StackHandel;i++)
PopStack();
}
}
// All other flush the stack
else
EmptyStack();
}
Function Checks how the current operator effects the run time stack after being run
An operator may increase or decrease the stack size |
protected void PopStack() {
if (arg_count >0)
{
args[arg_count-1]=null;
arg_count--;
}
}
Pop one element from the stack |
public byte[] Process(String fontName) throws IOException {
try
{
// Verify that the file is open
buf.reOpen();
// Find the Font that we will be dealing with
int j;
for (j=0; j< fonts.length; j++)
if (fontName.equals(fonts[j].name)) break;
if (j==fonts.length) return null;
// Calc the bias for the global subrs
if (gsubrIndexOffset >= 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
}
}
}
The Process function extracts one font out of the CFF file and returns a
subset version of the original. |
protected void PushStack() {
arg_count++;
}
|
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(new Integer (Subr)))
{
hSubr.put(new Integer(Subr),null);
lSubr.add(new Integer(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(new Integer (Subr)))
{
hGSubrsUsed.put(new Integer(Subr),null);
lGSubrsUsed.add(new Integer(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< SizeOfMask;i++)
getCard8();
}
}
}
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. |
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] = new Integer(first< < 8 | second);
arg_count++;
continue;
}
if (b0 >= 32 && b0 < = 246) // The byte read is the byte;
{
args[arg_count] = new Integer(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] = new Integer((b0-247)*256 + w + 108);
arg_count++;
continue;
}
if (b0 >= 251 && b0 < = 254)// Same as above except negative
{
int w = getCard8();
args[arg_count] = new Integer(-(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] = new Integer(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 next command after the file pointer is set |
protected void ReadFDArray(int Font) {
seek(fonts[Font].fdarrayOffset);
fonts[Font].FDArrayCount = getCard16();
fonts[Font].FDArrayOffsize = getCard8();
// Since we will change values inside the FDArray objects
// We increase its offsize to prevent errors
if (fonts[Font].FDArrayOffsize < 4)
fonts[Font].FDArrayOffsize++;
fonts[Font].FDArrayOffsets = getIndex(fonts[Font].fdarrayOffset);
}
Read the FDArray count, offsize and Offset array |
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 reconstructs the FDArray, PrivateDict and LSubr for CID fonts |
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.addLast(fdOffsets[i]);
}
// Declare beginning of the object array
IndexBaseItem fdArrayBase = new IndexBaseItem();
OutputList.addLast(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(new Integer (k)))
{
// Goto beginning 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 = ((Integer)args[0]).intValue();
// 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.addLast(new DictNumberItem(NewSize));
fdPrivate[k] = new DictOffsetItem();
OutputList.addLast(fdPrivate[k]);
OutputList.addLast(new UInt8Item((char)18)); // Private
// Go back to place
seek(p2);
}
// Else copy the entire range
else // other than private
OutputList.addLast(new RangeItem(buf,p1,p2-p1));
}
}
// Mark the ending of the object (even if wasn't written)
OutputList.addLast(new IndexMarkerItem(fdOffsets[k],fdArrayBase));
}
}
Function subsets the FDArray and builds the new one with new offsets |
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(new Integer (i)))
{
// Mark beginning
OutputList.addLast(new MarkerItem(fdPrivate[i]));
fdPrivateBase[i] = new IndexBaseItem();
OutputList.addLast(fdPrivateBase[i]);
// Goto beginning 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.addLast(fdSubrs[i]);
OutputList.addLast(new UInt8Item((char)19)); // Subrs
}
// Else copy the entire range
else
OutputList.addLast(new RangeItem(buf,p1,p2-p1));
}
}
}
}
Function Adds the new private dicts (only for the FDs used) to the list |
void ReconstructPrivateSubrs(int Font,
IndexBaseItem[] fdPrivateBase,
OffsetItem[] fdSubrs) {
// For each private dict
for (int i=0;i< fonts[Font].fdprivateLengths.length;i++)
{
// If that private dict's Subrs are used insert the new LSubrs
// computed earlier
if (fdSubrs[i]!= null && fonts[Font].PrivateSubrsOffset[i] >= 0)
{
OutputList.addLast(new SubrMarkerItem(fdSubrs[i],fdPrivateBase[i]));
OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(NewLSubrsIndex[i]),0,NewLSubrsIndex[i].length));
}
}
}
Function Adds the new LSubrs dicts (only for the FDs used) to the list |
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;
}
Function checks the key and return the change to the stack after the operator |
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;
}
}
Function computes the size of an index |
protected void readFDSelect(int Font) {
// Restore the number of glyphs
int NumOfGlyphs = fonts[Font].nglyphs;
int[] FDSelect = new int[NumOfGlyphs];
// Go to the beginning of the FDSelect
seek(fonts[Font].fdselectOffset);
// Read the FDSelect's format
fonts[Font].FDSelectFormat = getCard8();
switch(fonts[Font].FDSelectFormat){
// Format==0 means each glyph has an entry that indicated
// its FD.
case 0:
for (int i=0;i< NumOfGlyphs;i++)
{
FDSelect[i] = getCard8();
}
// The FDSelect's Length is one for each glyph + the format
// for later use
fonts[Font].FDSelectLength = fonts[Font].nglyphs+1;
break;
case 3:
// Format==3 means the ranges version
// The number of ranges
int nRanges = getCard16();
int l=0;
// Read the first in the first range
int first = getCard16();
for (int i=0;i< nRanges;i++)
{
// Read the FD index
int fd = getCard8();
// Read the first of the next range
int last = getCard16();
// Calc the steps and write to the array
int steps = last-first;
for (int k=0;k< steps;k++)
{
FDSelect[l] = fd;
l++;
}
// The last from this iteration is the first of the next
first = last;
}
// Store the length for later use
fonts[Font].FDSelectLength = 1+2+nRanges*3+2;
break;
default:
break;
}
// Save the FDSelect of the font
fonts[Font].FDSelect = FDSelect;
}
Read the FDSelect of the font and compute the array and its length |