Click here to Skip to main content
15,886,199 members
Articles / Programming Languages / Visual Basic

Open Door - Reporting, Charts, Enquiry Drill-Downs

Rate me:
Please Sign up or sign in to vote.
4.37/5 (11 votes)
2 Feb 2009CPOL6 min read 39.2K   2K   59  
A utility for generating user editable reports, charts, documents, enquiries
using System;
using System.Collections;

namespace iTextSharp.text.pdf {
    /**
    * This Class subsets a CFF Type Font. The subset is preformed for CID fonts and NON CID fonts.
    * The Charstring is subseted 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 altough 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> & Ygal Blum <blumygal@post.tau.ac.il>
    */
    public class CFFFontSubset : CFFFont {
        
        /**
        *  The Strings in this array represent Type1/Type2 operator names
        */
        internal static 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
        */
        internal static 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"
        };
        
        /**
        * A HashMap containing the glyphs used in the text after being converted
        * to glyph number by the CMap 
        */
        internal Hashtable GlyphsUsed;
        /**
        * The GlyphsUsed keys as an ArrayList
        */
        internal ArrayList glyphsInList;
        /**
        * A HashMap for keeping the FDArrays being used by the font
        */
        internal Hashtable FDArrayUsed = new Hashtable();
        /**
        * A HashMaps array for keeping the subroutines used in each FontDict
        */
        internal Hashtable[] hSubrsUsed;
        /**
        * The SubroutinesUsed HashMaps as ArrayLists
        */
        internal ArrayList[] lSubrsUsed;
        /**
        * A HashMap for keeping the Global subroutines used in the font
        */
        internal Hashtable hGSubrsUsed  = new Hashtable();
        /**
        * The Global SubroutinesUsed HashMaps as ArrayLists
        */
        internal ArrayList lGSubrsUsed = new ArrayList();
        /**
        * A HashMap for keeping the subroutines used in a non-cid font
        */
        internal Hashtable hSubrsUsedNonCID  = new Hashtable();
        /**
        * The SubroutinesUsed HashMap as ArrayList
        */
        internal ArrayList lSubrsUsedNonCID = new ArrayList();
        /**
        * An array of the new Indexs for the local Subr. One index for each FontDict
        */
        internal byte[][] NewLSubrsIndex;
        /**
        * The new subroutines index for a non-cid font
        */
        internal byte[] NewSubrsIndexNonCID;
        /**
        * The new global subroutines index of the font
        */
        internal byte[] NewGSubrsIndex;
        /**
        * The new CharString of the font
        */
        internal byte[] NewCharStringsIndex;
        
        /**
        * The bias for the global subroutines
        */
        internal int GBias = 0;
        
        /**
        * The linked list for generating the new font stream
        */
        internal ArrayList OutputList;
        
        /**
        * Number of arguments to the stem operators in a subroutine calculated recursivly
        */
        internal 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,Hashtable GlyphsUsed) : base(rf) {
            // Use CFFFont c'tor in order to parse the font file.
            this.GlyphsUsed = GlyphsUsed;
            //Put the glyphs into a list
            glyphsInList = new ArrayList(GlyphsUsed.Keys);
            
            
            for (int i=0;i<fonts.Length;++i)
            {
                // Read the number of glyphs in the font
                Seek(fonts[i].charstringsOffset);
                fonts[i].nglyphs = GetCard16();
                
                // Jump to the count field of the String Index
                Seek(stringIndexOffset);
                fonts[i].nstrings = GetCard16()+standardStrings.Length;
                
                // For each font save the offset array of the charstring
                fonts[i].charstringsOffsets = GetIndex(fonts[i].charstringsOffset);
                
                // Proces the FDSelect if exist 
                if (fonts[i].fdselectOffset>=0)
                {
                    // Proces 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
        */
        internal 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<NumofGlyphs){
                num++;
                Sid = GetCard16();
                if (Type==1)
                    nLeft = GetCard8();
                else
                    nLeft = GetCard16();
                i += nLeft+1;
            }
            return num;
        }


        /**
        * Read the FDSelect of the font and compute the array and its length
        * @param Font The index of the font being processed
        * @return The Processed FDSelect of the font
        */
        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; 
        }
        
        /**
        * Function reads the FDSelect and builds the FDArrayUsed HashMap According to the glyphs used
        * @param Font the Number of font being processed
        */
        protected void BuildFDArrayUsed(int Font)
        {
            int[] FDSelect = fonts[Font].FDSelect;
            // For each glyph used
            for (int i=0;i<glyphsInList.Count;i++)
            {
                // Pop the glyphs index
                int glyph = (int)glyphsInList[i];
                // Pop the glyph's FD
                int FD = FDSelect[glyph];
                // Put the FD index into the FDArrayUsed HashMap
                FDArrayUsed[FD] = null;
            }
        }

        /**
        * Read the FDArray count, offsize and Offset array
        * @param Font
        */
        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);
        }

        
        /**
        * The Process function extracts one font out of the CFF file and returns a
        * subset version of the original.
        * @param fontName - The name of the font to be taken out of the CFF
        * @return The new font stream
        * @throws IOException
        */
        public byte[] Process(String fontName) {
            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 {
                    // empty on purpose
                }
            }
        }

        /**
        * Function calcs bias according to the CharString type and the count
        * of the subrs
        * @param Offset The offset to the relevent 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) 
        {
            NewCharStringsIndex = BuildNewIndex(fonts[FontIndex].charstringsOffsets,GlyphsUsed);
        }
        
        /**
        * 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
        */
        protected void BuildNewLGSubrs(int Font)
        {
            // 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 hasmap-array and the arraylist-array to hold the subrs used
                // in each private dict.
                hSubrsUsed = new Hashtable[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.Keys);
                // For each FD array which is used subset the lsubr 
                for (int j=0;j<FDInList.Count;j++)
                {
                    // The FDArray index, Hash Map, Arrat List to work on
                    int FD = (int)FDInList[j];
                    hSubrsUsed[FD] = new Hashtable();
                    lSubrsUsed[FD] = new ArrayList();
                    //Reads the private dicts looking for the subr operator and 
                    // store both the offest 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 Charsting data storing the used Local and Global subroutines 
                        // by the glyphs. Scans the Subrs recursivley. 
                        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 Charsting data storing the used Local and Global subroutines 
                // by the glyphs. Scans the Subrs recursivley.
                BuildSubrUsed(Font,-1,fonts[Font].privateSubrs,fonts[Font].SubrsOffsets,hSubrsUsedNonCID,lSubrsUsedNonCID);
            }
            // For all fonts susbset the Global Subroutines
            // Scan the Global Subr Hashmap recursivly 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);
        }

        /**
        * 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 begining 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] = (int)args[0]+fonts[Font].fdprivateOffsets[FD];
            }
            //Read the lsub 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 lsub only) is then scaned recursivly 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,Hashtable 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.Count;i++)
            {
                int glyph = (int)glyphsInList[i];
                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 recusrivly for Lsubr & Gsubr used
            for (int i=0;i<lSubr.Count;i++)
            {
                // Pop the subr value from the hash
                int Subr = (int)lSubr[i];
                // 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 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.Count;
            }
            
            // For each global subr used 
            for (int i=0;i<lGSubrsUsed.Count;i++)
            {
                //Pop the value + check valid 
                int Subr = (int)lGSubrsUsed[i];
                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.Count)
                        {
                            for (int j=SizeOfNonCIDSubrsUsed;j<lSubrsUsedNonCID.Count;j++)
                            {
                                //Pop the value + check valid 
                                int LSubr = (int)lSubrsUsedNonCID[j];
                                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.Count;
                        }
                    }
                }
            }
        }

        /**
        * 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,Hashtable hSubr,ArrayList lSubr,int[] LSubrsOffsets)
        {
            // Clear the stack for the subrs
            EmptyStack();
            NumOfHints = 0;
            // Goto begining 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 = (int)TopElement + LBias;
                        // If the subr isn't in the HashMap -> Put in
                        if (!hSubr.ContainsKey(Subr))
                        {
                            hSubr[Subr] = null;
                            lSubr.Add(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 = (int)TopElement + GBias;
                        // If the subr isn't in the HashMap -> Put in
                        if (!hGSubrsUsed.ContainsKey(Subr))
                        {
                            hGSubrsUsed[Subr] = null;
                            lGSubrsUsed.Add(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();
                }
            }
        }

        /**
        * 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 HandelStack()
        {
            // Findout 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 the key and return the change to the stack after the operator
        * @return The change in the stack. 2-> 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; i<arg_count; i++) args[i]=null;
            arg_count = 0;        
        }
        
        /**
        * Pop one element from the stack 
        *
        */
        protected void PopStack()
        {
            if (arg_count>0)
            {
                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;
            bool 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] = first<<8 | second;
                    arg_count++;
                    continue;
                }
                if (b0 >= 32 && b0 <= 246) // The byte read is the byte;
                {
                    args[arg_count] = (int)b0 - 139;
                    arg_count++;
                    continue;
                }
                if (b0 >= 247 && b0 <= 250) // The byte read and the next byte constetute a short int
                {
                    int w = GetCard8();
                    args[arg_count] = ((int)b0-247)*256 + w + 108;
                    arg_count++;
                    continue;
                }
                if (b0 >= 251 && b0 <= 254)// Same as above except negative
                {
                    int w = GetCard8();
                    args[arg_count] = -((int)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] = 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 therefor 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 begining 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 = (int)TopElement + 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 = (int)TopElement + 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;
        }


        /**
        * Function builds the new offset array, object array and assembles the index.
        * used for creating the glyph and subrs subsetted index 
        * @param Offsets the offset array of the original index  
        * @param Used the hashmap of the used objects
        * @return the new index subset version 
        * @throws IOException
        */
        protected byte[] BuildNewIndex(int[] Offsets,Hashtable Used) 
        {
            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(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 begining 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 creates the new index, inserting the count,offsetsize,offset array
        * and object array.
        * @param NewOffsets the subsetted offset array
        * @param NewObjects the subsetted object array
        * @return the new index created
        */
        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);
                        goto case 3;
                    case 3:
                        NewIndex[Place++] = (byte) ((Num >> 16) & 0xff);
                        goto case 2;
                    case 2:
                        NewIndex[Place++] = (byte) ((Num >>  8) & 0xff);
                        goto case 1;
                    case 1:
                        NewIndex[Place++] = (byte) ((Num >>  0) & 0xff);
                        break;
                }                    
            }
            // 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;
        }
        
        /**
        * 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 ArrayList();

            // 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 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.Add(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.Add(new UInt16Item((char)Count)); // count
            // Add the offsize field
            OutputList.Add(new UInt8Item((char)Offsize)); // offSize
            // Add the first offset according to the offsize
            switch (Offsize){
                case 1:
                    OutputList.Add(new UInt8Item((char)First)); // first offset
                    break;
                case 2:
                    OutputList.Add(new UInt16Item((char)First)); // first offset
                    break;
                case 3:
                    OutputList.Add(new UInt24Item((char)First)); // first offset
                    break;
                case 4:
                    OutputList.Add(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.Add(fdarrayRef);
            OutputList.Add(new UInt8Item((char)12));
            OutputList.Add(new UInt8Item((char)36));
            // create an FDSelect key
            OutputList.Add(fdselectRef);
            OutputList.Add(new UInt8Item((char)12));
            OutputList.Add(new UInt8Item((char)37));
            // create an charset key
            OutputList.Add(charsetRef);
            OutputList.Add(new UInt8Item((char)15));
            // create a CharStrings key
            OutputList.Add(charstringsRef);
            OutputList.Add(new UInt8Item((char)17));
        }
        
        /**
        * Function takes the original string item and adds the new strings
        * to accomodate 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.Add(new UInt16Item((char)((stringOffsets.Length-1)+3))); // count
            OutputList.Add(new UInt8Item((char)stringsIndexOffSize)); // offSize
            for (int i=0; i<stringOffsets.Length; i++)
                OutputList.Add(new IndexOffsetItem(stringsIndexOffSize,
                stringOffsets[i]-stringsBaseOffset));
            int currentStringsOffset = stringOffsets[stringOffsets.Length-1]
            - stringsBaseOffset;
            //l.Add(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset));
            currentStringsOffset += ("Adobe").Length;
            OutputList.Add(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset));
            currentStringsOffset += ("Identity").Length;
            OutputList.Add(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset));
            currentStringsOffset += fdFontName.Length;
            OutputList.Add(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset));
            
            OutputList.Add(new RangeItem(buf,stringOffsets[0],origStringsLen));
            OutputList.Add(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.Add(new MarkerItem(fdselectRef));
            OutputList.Add(new UInt8Item((char)3)); // format identifier
            OutputList.Add(new UInt16Item((char)1)); // nRanges
            
            OutputList.Add(new UInt16Item((char)0)); // Range[0].firstGlyph
            OutputList.Add(new UInt8Item((char)0)); // Range[0].fd
            
            OutputList.Add(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.Add(new MarkerItem(charsetRef));
            OutputList.Add(new UInt8Item((char)2)); // format identifier
            OutputList.Add(new UInt16Item((char)1)); // first glyph in range (ignore .notdef)
            OutputList.Add(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.Add(new MarkerItem(fdarrayRef));
            // Build the header (count=offsize=first=1)
            BuildIndexHeader(1,1,1);
            
            // Mark
            OffsetItem privateIndex1Ref = new IndexOffsetItem(1);
            OutputList.Add(privateIndex1Ref);
            IndexBaseItem privateBase = new IndexBaseItem();
            // Insert the private operands and operator
            OutputList.Add(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.Add(new DictNumberItem(NewSize));
            OutputList.Add(privateRef);
            OutputList.Add(new UInt8Item((char)18)); // Private
            
            OutputList.Add(new IndexMarkerItem(privateIndex1Ref,privateBase));
        }
        
        /**
        * 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);
        }

        /**
        * 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));
            }
        }
        /**
        * 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 Adds the new LSubrs dicts (only for the FDs used) to the list
        * @param Font  The index of the font
        * @param fdPrivateBase The IndexBaseItem array for the linked list
        * @param fdSubrs OffsetItem array for the linked list
        * @throws IOException
        */
        
        internal 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.Add(new SubrMarkerItem(fdSubrs[i],fdPrivateBase[i]));
                    OutputList.Add(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
        */
        internal 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 
        */
        internal void CreateNonCIDPrivate(int Font,OffsetItem Subr)
        {
            // Go to the beginning of the private dict and read untill 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.Add(Subr);
                    OutputList.Add(new UInt8Item((char)19)); // Subrs
                }
                // Else copy the entire range
                else
                    OutputList.Add(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
        * @throws IOException
        */
        internal void CreateNonCIDSubrs(int Font,IndexBaseItem PrivateBase,OffsetItem Subrs)
        {
            // Mark the beginning of the Subrs index
            OutputList.Add(new SubrMarkerItem(Subrs,PrivateBase));
            // Put the subsetted new subrs index
            OutputList.Add(new RangeItem(new RandomAccessFileOrArray(NewSubrsIndexNonCID),0,NewSubrsIndexNonCID.Length));
        }    
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions