All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itextpdf.io.font.CFFFontSubset Maven / Gradle / Ivy

There is a newer version: 9.0.0
Show newest version
/*

    This file is part of the iText (R) project.
    Copyright (c) 1998-2022 iText Group NV
    Authors: Bruno Lowagie, Paulo Soares, et al.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License version 3
    as published by the Free Software Foundation with the addition of the
    following permission added to Section 15 as permitted in Section 7(a):
    FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
    ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
    OF THIRD PARTY RIGHTS

    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    or FITNESS FOR A PARTICULAR PURPOSE.
    See the GNU Affero General Public License for more details.
    You should have received a copy of the GNU Affero General Public License
    along with this program; if not, see http://www.gnu.org/licenses or write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA, 02110-1301 USA, or download the license from the following URL:
    http://itextpdf.com/terms-of-use/

    The interactive user interfaces in modified source and object code versions
    of this program must display Appropriate Legal Notices, as required under
    Section 5 of the GNU Affero General Public License.

    In accordance with Section 7(b) of the GNU Affero General Public License,
    a covered work must retain the producer line in every PDF that is created
    or manipulated using iText.

    You can be released from the requirements of the license by purchasing
    a commercial license. Buying such a license is mandatory as soon as you
    develop commercial activities involving the iText software without
    disclosing the source code of your own applications.
    These activities include: offering paid services to customers as an ASP,
    serving PDFs on the fly in a web application, shipping iText with a closed
    source product.

    For more information, please contact iText Software Corp. at this
    address: [email protected]
 */
package com.itextpdf.io.font;

import com.itextpdf.io.exceptions.IOException;
import com.itextpdf.io.source.RandomAccessFileOrArray;
import com.itextpdf.io.util.GenericArray;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * This Class subsets a CFF Type Font. The subset is preformed for CID fonts and NON CID fonts.
 * The Charstring is subsetted for both types. For CID fonts only the FDArray which are used are embedded.
 * The Lsubroutines of the FDArrays used are subsetted as well. The Subroutine subset supports both Type1 and Type2
 * formatting although only tested on Type2 Format.
 * For Non CID the Lsubroutines are subsetted. On both types the Gsubroutines is subsetted.
 * A font which was not of CID type is transformed into CID as a part of the subset process.
 * The CID synthetic creation was written by Sivan Toledo ([email protected])
 *
 * @author Oren Manor ([email protected]) and Ygal Blum ([email protected])
 */
public class CFFFontSubset extends CFFFont {

    /**
     * The Strings in this array represent Type1/Type2 operator names
     */
    static final String[] SubrsFunctions = {
            "RESERVED_0", "hstem", "RESERVED_2", "vstem", "vmoveto", "rlineto", "hlineto", "vlineto",
            "rrcurveto", "RESERVED_9", "callsubr", "return", "escape", "RESERVED_13",
            "endchar", "RESERVED_15", "RESERVED_16", "RESERVED_17", "hstemhm", "hintmask",
            "cntrmask", "rmoveto", "hmoveto", "vstemhm", "rcurveline", "rlinecurve", "vvcurveto",
            "hhcurveto", "shortint", "callgsubr", "vhcurveto", "hvcurveto"
    };
    /**
     * The Strings in this array represent Type1/Type2 escape operator names
     */
    static final String[] SubrsEscapeFuncs = {
            "RESERVED_0", "RESERVED_1", "RESERVED_2", "and", "or", "not", "RESERVED_6",
            "RESERVED_7", "RESERVED_8", "abs", "add", "sub", "div", "RESERVED_13", "neg",
            "eq", "RESERVED_16", "RESERVED_17", "drop", "RESERVED_19", "put", "get", "ifelse",
            "random", "mul", "RESERVED_25", "sqrt", "dup", "exch", "index", "roll", "RESERVED_31",
            "RESERVED_32", "RESERVED_33", "hflex", "flex", "hflex1", "flex1", "RESERVED_REST"
    };

    /**
     * Operator codes for unused  CharStrings and unused local and global Subrs
     */
    static final byte ENDCHAR_OP = 14;
    static final byte RETURN_OP = 11;

    /**
     * A Map containing the glyphs used in the text after being converted
     * to glyph number by the CMap
     */
    Set GlyphsUsed;
    /**
     * The GlyphsUsed keys as an list
     */
    List glyphsInList;
    /**
     * A Set for keeping the FDArrays being used by the font
     */
    Set FDArrayUsed = new HashSet<>();
    /**
     * A Maps array for keeping the subroutines used in each FontDict
     */
    GenericArray> hSubrsUsed;
    /**
     * The SubroutinesUsed Maps as lists
     */
    GenericArray> lSubrsUsed;
    /**
     * A Map for keeping the Global subroutines used in the font
     */
    Set hGSubrsUsed = new HashSet<>();
    /**
     * The Global SubroutinesUsed Maps as lists
     */
    List lGSubrsUsed = new ArrayList<>();
    /**
     * A Map for keeping the subroutines used in a non-cid font
     */
    Set hSubrsUsedNonCID = new HashSet<>();
    /**
     * The SubroutinesUsed Map as list
     */
    List lSubrsUsedNonCID = new ArrayList<>();
    /**
     * An array of the new Indexes for the local Subr. One index for each FontDict
     */
    byte[][] NewLSubrsIndex;
    /**
     * The new subroutines index for a non-cid font
     */
    byte[] NewSubrsIndexNonCID;
    /**
     * The new global subroutines index of the font
     */
    byte[] NewGSubrsIndex;
    /**
     * The new CharString of the font
     */
    byte[] NewCharStringsIndex;

    /**
     * The bias for the global subroutines
     */
    int GBias = 0;

    /**
     * The linked list for generating the new font stream
     */
    LinkedList OutputList;

    /**
     * Number of arguments to the stem operators in a subroutine calculated recursively
     */
    int NumOfHints = 0;

    /**
     * C'tor for CFFFontSubset
     *
     * @param cff        - The font file
     */
    CFFFontSubset(byte[] cff) {
        this(cff, Collections.emptySet(), true);
    }

    public CFFFontSubset(byte[] cff, Set GlyphsUsed) {
        this(cff, GlyphsUsed, false);
    }

    CFFFontSubset(byte[] cff, Set GlyphsUsed, boolean isCidParsingRequired) {
        // Use CFFFont c'tor in order to parse the font file.
        super(cff);
        this.GlyphsUsed = GlyphsUsed;
        //Put the glyphs into a list
        glyphsInList = new ArrayList<>(GlyphsUsed);

        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);

            if (isCidParsingRequired) {
                initGlyphIdToCharacterIdArray(i, fonts[i].nglyphs, fonts[i].charsetOffset);
            }

            // Process the FDSelect if exist
            if (fonts[i].fdselectOffset >= 0) {
                // Process the FDSelect
                readFDSelect(i);
                // Build the FDArrayUsed Map
                BuildFDArrayUsed(i);
            }
            if (fonts[i].isCID)
                // Build the FD Array used  Map
                ReadFDArray(i);
            // compute the charset length
            fonts[i].CharsetLength = CountCharset(fonts[i].charsetOffset, fonts[i].nglyphs);
        }
    }

    /**
     * Calculates the length of the charset according to its format
     *
     * @param Offset      The Charset Offset
     * @param NumofGlyphs Number of glyphs in the font
     * @return the length of the Charset
     */
    int CountCharset(int Offset, int NumofGlyphs) {
        int format;
        int Length = 0;
        seek(Offset);
        // Read the format
        format = getCard8();
        // Calc according to format
        switch (format) {
            case 0:
                Length = 1 + 2 * NumofGlyphs;
                break;
            case 1:
                Length = 1 + 3 * CountRange(NumofGlyphs, 1);
                break;
            case 2:
                Length = 1 + 4 * CountRange(NumofGlyphs, 2);
                break;
            default:
                break;
        }
        return Length;
    }

    /**
     * Function calculates the number of ranges in the Charset
     *
     * @param NumofGlyphs The number of glyphs in the font
     * @param Type        The format of the Charset
     * @return The number of ranges in the Charset data structure
     */
    int CountRange(int NumofGlyphs, int Type) {
        int num = 0;
        char Sid;
        int i = 1, nLeft;
        while (i < 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
     */
    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 Map 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 (Integer glyphsInList1 : glyphsInList) {
            // Pop the glyphs index
            int glyph = (int) glyphsInList1;
            // Pop the glyph's FD
            int FD = FDSelect[glyph];
            // Put the FD index into the FDArrayUsed Map
            FDArrayUsed.add(FD);
        }
    }

    /**
     * Read the FDArray count, offsize and Offset array
     *
     * @param Font the Number of font being processed
     */
    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
     */
    public byte[] Process(String fontName) {
        try {
            // 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
            return BuildNewFile(j);
        } catch (java.io.IOException e) {
            throw new IOException(IOException.IoException, e);
        } 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 with the first name.
     *
     * @return The new font stream
     */
    public byte[] Process() {
        return Process(getNames()[0]);
    }

    /**
     * Function calcs bias according to the CharString type and the count
     * of the subrs
     *
     * @param Offset The offset to the relevant subrs index
     * @param Font   the font
     * @return The calculated Bias
     */
    protected int CalcBias(int Offset, int Font) {
        seek(Offset);
        int nSubrs = getCard16();
        // If type==1 -> bias=0
        if (fonts[Font].CharstringType == 1)
            return 0;
            // else calc according to the count
        else if (nSubrs < 1240)
            return 107;
        else if (nSubrs < 33900)
            return 1131;
        else
            return 32768;
    }

    /**
     * Function uses BuildNewIndex to create the new index of the subset charstrings.
     *
     * @param FontIndex the font
     * @throws java.io.IOException if an I/O error occurs
     */
    protected void BuildNewCharString(int FontIndex) throws java.io.IOException {
        NewCharStringsIndex = BuildNewIndex(fonts[FontIndex].charstringsOffsets, GlyphsUsed, ENDCHAR_OP);
    }

    /**
     * Function builds the new local and global subsrs indices. IF CID then All of
     * the FD Array lsubrs will be subsetted.
     *
     * @param Font the font
     * @throws java.io.IOException if an I/O error occurs
     */
    @SuppressWarnings("unchecked")
    protected void BuildNewLGSubrs(int Font) throws java.io.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 Map-array and the list-array to hold the subrs used
            // in each private dict.
            hSubrsUsed = new GenericArray<>(fonts[Font].fdprivateOffsets.length);
            lSubrsUsed = new GenericArray<>(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
            List FDInList = new ArrayList<>(FDArrayUsed);
            // For each FD array which is used subset the lsubr
            for (int j = 0; j < FDInList.size(); j++) {
                // The FDArray index,  Map, List to work on
                int FD = (int) FDInList.get(j);
                hSubrsUsed.set(FD, new HashSet());
                lSubrsUsed.set(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.get(FD), lSubrsUsed.get(FD));
                    // Builds the New Local Subrs index
                    NewLSubrsIndex[FD] = BuildNewIndex(fonts[Font].PrivateSubrsOffsetsArray[FD], hSubrsUsed.get(FD), RETURN_OP);
                }
            }
        }
        // If the font is not CID && the Private Subr exists then subset:
        else if (fonts[Font].privateSubrs >= 0) {
            // Build the subrs offsets;
            fonts[Font].SubrsOffsets = getIndex(fonts[Font].privateSubrs);
            //Scans the Charstring data storing the used Local and Global subroutines
            // by the glyphs. Scans the Subrs recursively.
            BuildSubrUsed(Font, -1, fonts[Font].privateSubrs, fonts[Font].SubrsOffsets, hSubrsUsedNonCID, lSubrsUsedNonCID);
        }
        // For all fonts subset the Global Subroutines
        // Scan the Global Subr Map recursively on the Gsubrs
        BuildGSubrsUsed(Font);
        if (fonts[Font].privateSubrs >= 0)
            // Builds the New Local Subrs index
            NewSubrsIndexNonCID = BuildNewIndex(fonts[Font].SubrsOffsets, hSubrsUsedNonCID, RETURN_OP);
        //Builds the New Global Subrs index
        // NOTE We copy all global subroutines to index here.
        // In some fonts (see NotoSansCJKjp-Bold.otf, Version 1.004;PS 1.004;hotconv 1.0.82;makeotf.lib2.5.63406)
        // global subroutines are not derived from local ones. Previously in such cases iText didn't build global subroutines
        // and, if one had set subset as true, produced pdf-document with incorrect cff table.
        // However the code isn't optimised. One can parse all used glyphs and copy not all global subroutines, but only needed.
        NewGSubrsIndex = BuildNewIndexAndCopyAllGSubrs(gsubrOffsets, RETURN_OP);
    }

    /**
     * The function finds for the FD array processed the local subr offset and its
     * offset array.
     *
     * @param Font the font
     * @param FD   The FDARRAY processed
     */
    protected void BuildFDSubrsOffsets(int Font, int FD) {
        // Initiate to -1 to indicate lsubr operator present
        fonts[Font].PrivateSubrsOffset[FD] = -1;
        // Goto beginning of objects
        seek(fonts[Font].fdprivateOffsets[FD]);
        // While in the same object:
        while (getPosition() < fonts[Font].fdprivateOffsets[FD] + fonts[Font].fdprivateLengths[FD]) {
            getDictItem();
            // If the dictItem is the "Subrs" then find and store offset,
            if ("Subrs".equals(key))
                fonts[Font].PrivateSubrsOffset[FD] = (int) ((Integer) args[0]) + fonts[Font].fdprivateOffsets[FD];
        }
        //Read the lsubr index if the lsubr was found
        if (fonts[Font].PrivateSubrsOffset[FD] >= 0)
            fonts[Font].PrivateSubrsOffsetsArray[FD] = getIndex(fonts[Font].PrivateSubrsOffset[FD]);
    }

    /**
     * Function uses ReadAsubr on the glyph used to build the LSubr and Gsubr Map.
     * The Map (of the lsubr only) is then scanned recursively for Lsubr and 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        Map of the subrs used
     * @param lSubr        list of the subrs used
     */
    protected void BuildSubrUsed(int Font, int FD, int SubrOffset, int[] SubrsOffsets, Set hSubr, List lSubr) {

        // Calc the Bias for the subr index
        int LBias = CalcBias(SubrOffset, Font);

        // For each glyph used find its GID, start & end pos
        for (Integer usedGlyph : glyphsInList) {
            int glyph = (int) usedGlyph;
            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 = (int) lSubr.get(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 list to find recursive calls
     * to Gsubrs and adds to Map and list
     *
     * @param Font the font
     */
    protected void BuildGSubrsUsed(int Font) {
        int LBias = 0;
        int SizeOfNonCIDSubrsUsed = 0;
        if (fonts[Font].privateSubrs >= 0) {
            LBias = CalcBias(fonts[Font].privateSubrs, Font);
            SizeOfNonCIDSubrsUsed = lSubrsUsedNonCID.size();
        }

        // For each global subr used
        for (int i = 0; i < lGSubrsUsed.size(); i++) {
            //Pop the value + check valid
            int Subr = (int) lGSubrsUsed.get(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.size()) {
                        for (int j = SizeOfNonCIDSubrsUsed; j < lSubrsUsedNonCID.size(); j++) {
                            //Pop the value + check valid
                            int LSubr = (int) lSubrsUsedNonCID.get(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.size();
                    }
                }
            }
        }
    }

    /**
     * The function reads a subrs (glyph info) between begin and end.
     * Adds calls to a Lsubr to the hSubr and lSubrs.
     * Adds calls to a Gsubr to the hGSubr and lGSubrs.
     *
     * @param begin         the start point of the subr
     * @param end           the end point of the subr
     * @param GBias         the bias of the Global Subrs
     * @param LBias         the bias of the Local Subrs
     * @param hSubr         the subroutines used as set
     * @param lSubr         the subroutines used as list
     * @param LSubrsOffsets the offsets array of the subroutines
     */
    protected void ReadASubr(int begin, int end, int GBias, int LBias, Set hSubr, List 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();
            if (null != key)
                // a call to a Lsubr
                switch (key) {
                    // a call to a Gsubr
                    case "callsubr":
                        // Verify that arguments are passed
                        if (NumOfArgs > 0) {
                            // Calc the index of the Subrs
                            int Subr = (int) ((Integer) TopElement) + LBias;
                            // If the subr isn't in the Map -> Put in
                            if (!hSubr.contains(Subr)) {
                                hSubr.add(Subr);
                                lSubr.add(Subr);
                            }
                            CalcHints(LSubrsOffsets[Subr], LSubrsOffsets[Subr + 1], LBias, GBias, LSubrsOffsets);
                            seek(pos);
                        }
                        break;
                    // A call to "stem"
                    case "callgsubr":
                        // Verify that arguments are passed
                        if (NumOfArgs > 0) {
                            // Calc the index of the Subrs
                            int Subr = (int) ((Integer) TopElement) + GBias;
                            // If the subr isn't in the Map -> Put in
                            if (!hGSubrsUsed.contains(Subr)) {
                                hGSubrsUsed.add(Subr);
                                lGSubrsUsed.add(Subr);
                            }
                            CalcHints(gsubrOffsets[Subr], gsubrOffsets[Subr + 1], LBias, GBias, LSubrsOffsets);
                            seek(pos);
                        }
                        break;
                    case "hstem":
                    case "vstem":
                    case "hstemhm":
                    case "vstemhm":
                        // Increment the NumOfHints by the number couples of of arguments
                        NumOfHints += NumOfArgs / 2;
                        break;
                    case "hintmask":
                    case "cntrmask":
                        // if stack is not empty the reason is vstem implicit definition
                        // See Adobe Technical Note #5177, page 25, hintmask usage example.
                        NumOfHints += NumOfArgs / 2;
                        // 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();
                        }
                        break;
                }
        }
    }

    /**
     * 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() {
        // 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 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() {
        switch (key) {
            case "ifelse":
                return -3;
            case "roll":
            case "put":
                return -2;
            case "callsubr":
            case "callgsubr":
            case "add":
            case "sub":
            case "div":
            case "mul":
            case "drop":
            case "and":
            case "or":
            case "eq":
                return -1;
            case "abs":
            case "neg":
            case "sqrt":
            case "exch":
            case "index":
            case "get":
            case "not":
            case "return":
                return 0;
            case "random":
            case "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;
        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] = first << 8 | second;
                arg_count++;
                continue;
            }

            // The byte read is the byte;
            if (b0 >= 32 && b0 <= 246)
            {
                args[arg_count] = b0 - 139;
                arg_count++;
                continue;
            }

            // The byte read and the next byte constitute a short int
            if (b0 >= 247 && b0 <= 250)
            {
                int w = getCard8();
                args[arg_count] = (b0 - 247) * 256 + w + 108;
                arg_count++;
                continue;
            }

            // Same as above except negative
            if (b0 >= 251 && b0 <= 254)
            {
                int w = getCard8();
                args[arg_count] = -(b0 - 251) * 256 - w - 108;
                arg_count++;
                continue;
            }

            // The next for bytes represent a double.
            if (b0 == 255)
            {
                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;
            }

            // An operator was found.. Set Key.
            if (b0 <= 31 && b0 != 28)
            {
                gotKey = true;

                // 12 is an escape command therefore the next byte is a part
                // of this command
                if (b0 == 12) {
                    int b1 = getCard8();
                    if (b1 > SubrsEscapeFuncs.length - 1)
                        b1 = SubrsEscapeFuncs.length - 1;
                    key = SubrsEscapeFuncs[b1];
                } else
                    key = SubrsFunctions[b0];
                continue;
            }
        }
    }

    /**
     * The function reads the subroutine and returns the number of the hint in it.
     * If a call to another subroutine is found the function calls recursively.
     *
     * @param begin         the start point of the subr
     * @param end           the end point of the subr
     * @param LBias         the bias of the Local Subrs
     * @param GBias         the bias of the Global Subrs
     * @param LSubrsOffsets The Offsets array of the subroutines
     * @return The number of hints in the subroutine read.
     */
    protected int CalcHints(int begin, int end, int LBias, int GBias, int[] LSubrsOffsets) {
        // Goto beginning of the subr
        seek(begin);
        while (getPosition() < end) {
            // Read the next command
            ReadCommand();
            int pos = getPosition();
            Object TopElement = null;
            if (arg_count > 0)
                TopElement = args[arg_count - 1];
            int NumOfArgs = arg_count;
            //Check the modification needed on the Argument Stack according to key;
            HandelStack();
            // a call to a Lsubr
            switch (key) {
                // a call to a Gsubr
                case "callsubr":
                    if (NumOfArgs > 0) {
                        assert TopElement instanceof Integer;
                        int Subr = (int) ((Integer) TopElement) + LBias;
                        CalcHints(LSubrsOffsets[Subr], LSubrsOffsets[Subr + 1], LBias, GBias, LSubrsOffsets);
                        seek(pos);
                    }
                    break;
                // A call to "stem"
                case "callgsubr":
                    if (NumOfArgs > 0) {
                        assert TopElement instanceof Integer;
                        int Subr = (int) ((Integer) TopElement) + GBias;
                        CalcHints(gsubrOffsets[Subr], gsubrOffsets[Subr + 1], LBias, GBias, LSubrsOffsets);
                        seek(pos);
                    }
                    break;
                case "hstem":
                case "vstem":
                case "hstemhm":
                case "vstemhm":
                    // Increment the NumOfHints by the number couples of of arguments
                    NumOfHints += NumOfArgs / 2;
                    break;
                case "hintmask":
                case "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();
                    }
                    break;
            }
        }
        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 Map of the used objects
     * @param OperatorForUnusedEntries the operator inserted into the data stream for unused entries
     * @return the new index subset version
     * @throws java.io.IOException if an I/O error occurs
     */
    protected byte[] BuildNewIndex(int[] Offsets, Set Used, byte OperatorForUnusedEntries) throws java.io.IOException {
        int unusedCount = 0;
        int Offset = 0;
        int[] NewOffsets = new int[Offsets.length];
        // Build the Offsets Array for the Subset
        for (int i = 0; i < Offsets.length; ++i) {
            NewOffsets[i] = Offset;
            // If the object in the offset is also present in the used
            // Map then increment the offset var by its size
            if (Used.contains(i)) {
                Offset += Offsets[i + 1] - Offsets[i];
            } else {
                // Else the same offset is kept in i+1.
                unusedCount++;
            }
        }
        // Offset var determines the size of the object array
        byte[] NewObjects = new byte[Offset + unusedCount];
        // Build the new Object array
        int unusedOffset = 0;
        for (int i = 0; i < Offsets.length - 1; ++i) {
            int start = NewOffsets[i];
            int end = NewOffsets[i + 1];
            NewOffsets[i] = start + unusedOffset;
            // 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 + unusedOffset, end - start);
            } else {
                NewObjects[start + unusedOffset] = OperatorForUnusedEntries;
                unusedOffset++;
            }
        }
        NewOffsets[Offsets.length - 1] += unusedOffset;
        // 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
     *
     * @param Offsets                  the offset array of the original index
     * @param OperatorForUnusedEntries the operator inserted into the data stream for unused entries
     * @return the new index subset version
     * @throws java.io.IOException if an I/O error occurs
     */
    protected byte[] BuildNewIndexAndCopyAllGSubrs(int[] Offsets, byte OperatorForUnusedEntries) throws java.io.IOException {
        int unusedCount = 0;
        int Offset = 0;
        int[] NewOffsets = new int[Offsets.length];
        // Build the Offsets Array for the Subset
        for (int i = 0; i < Offsets.length - 1; ++i) {
            NewOffsets[i] = Offset;
            Offset += Offsets[i + 1] - Offsets[i];
        }
        // Else the same offset is kept in i+1.
        NewOffsets[Offsets.length - 1] = Offset;
        unusedCount++;

        // Offset var determines the size of the object array
        byte[] NewObjects = new byte[Offset + unusedCount];
        // Build the new Object array
        int unusedOffset = 0;
        for (int i = 0; i < Offsets.length - 1; ++i) {
            int start = NewOffsets[i];
            int end = NewOffsets[i + 1];
            NewOffsets[i] = start + unusedOffset;
            // 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 + unusedOffset, end - start);
            } else {
                NewObjects[start + unusedOffset] = OperatorForUnusedEntries;
                unusedOffset++;
            }
        }
        NewOffsets[Offsets.length - 1] += unusedOffset;
        // 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;
        // Previously the condition wasn't strict. However while writing offsets iText adds 1 to them.
        // That can cause overflow (f.e., offset 0xffff will result in 0x0000).
        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
        // There is no sense in >>> for char
        // NewIndex[Place++] = (byte) (Count >>> 8 & 0xff);
        NewIndex[Place++] = (byte) (Count >> 8 & 0xff);
        NewIndex[Place++] = (byte) (Count & 0xff);
        // Write the offsize field
        NewIndex[Place++] = Offsize;
        // Write the offset array according to the offsize
        for (int newOffset : NewOffsets) {
            // The value to be written
            int Num = newOffset - NewOffsets[0] + 1;
            // Write in bytes according to the offsize
            for (int i = Offsize; i > 0; i--) {
                NewIndex[Place++] = (byte) (Num >>> ((i - 1) << 3) & 0xff);
            }
        }
        // Write the new object array one by one
        for (byte newObject : NewObjects) {
            NewIndex[Place++] = newObject;
        }
        // Return the new index
        return NewIndex;
    }

    /**
     * The function builds the new output stream according to the subset process
     *
     * @param Font the font
     * @return the subsetted font stream
     */
    protected byte[] BuildNewFile(int Font) {
        // Prepare linked list for new font components
        OutputList = new LinkedList<>();

        // copy the header of the font
        CopyHeader();

        // create a name index
        BuildIndexHeader(1, 1, 1);
        OutputList.addLast(new UInt8Item((char) (1 + fonts[Font].name.length())));
        OutputList.addLast(new StringItem(fonts[Font].name));

        // create the topdict Index
        BuildIndexHeader(1, 2, 1);
        OffsetItem topdictIndex1Ref = new IndexOffsetItem(2);
        OutputList.addLast(topdictIndex1Ref);
        IndexBaseItem topdictBase = new IndexBaseItem();
        OutputList.addLast(topdictBase);

        // Initialize the Dict Items for later use
        OffsetItem charsetRef = new DictOffsetItem();
        OffsetItem charstringsRef = new DictOffsetItem();
        OffsetItem fdarrayRef = new DictOffsetItem();
        OffsetItem fdselectRef = new DictOffsetItem();
        OffsetItem privateRef = new DictOffsetItem();

        // If the font is not CID create the following keys
        if (!fonts[Font].isCID) {
            // create a ROS key
            OutputList.addLast(new DictNumberItem(fonts[Font].nstrings));
            OutputList.addLast(new DictNumberItem(fonts[Font].nstrings + 1));
            OutputList.addLast(new DictNumberItem(0));
            OutputList.addLast(new UInt8Item((char) 12));
            OutputList.addLast(new UInt8Item((char) 30));
            // create a CIDCount key
            OutputList.addLast(new DictNumberItem(fonts[Font].nglyphs));
            OutputList.addLast(new UInt8Item((char) 12));
            OutputList.addLast(new UInt8Item((char) 34));
            // Sivan's comments
            // What about UIDBase (12,35)? Don't know what is it.
            // I don't think we need FontName; the font I looked at didn't have it.
        }
        // Go to the TopDict of the font being processed
        seek(topdictOffsets[Font]);
        // Run until the end of the TopDict
        while (getPosition() < topdictOffsets[Font + 1]) {
            int p1 = getPosition();
            getDictItem();
            int p2 = getPosition();
            // The encoding key is disregarded since CID has no encoding
            if ("Encoding".equals(key)
                    // These keys will be added manually by the process.
                    || "Private".equals(key)
                    || "FDSelect".equals(key)
                    || "FDArray".equals(key)
                    || "charset".equals(key)
                    || "CharStrings".equals(key)
                    ) {
            } else {
                //OtherWise copy key "as is" to the output list
                OutputList.addLast(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(rasFactory.createSource(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(rasFactory.createSource(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
        for (Item item : OutputList) {
            item.increment(currentOffset);
        }
        // Compute the Xref for each of the offset items
        for (Item item : OutputList) {
            item.xref();
        }

        int size = currentOffset[0];
        byte[] b = new byte[size];

        // Emit all the items into the new byte array
        for (Item item : 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.addLast(new RangeItem(buf, 0, hdrSize));
    }

    /**
     * Function Build the header of an index
     *
     * @param Count   the count field of the index
     * @param Offsize the offsize field of the index
     * @param First   the first offset of the index
     */
    protected void BuildIndexHeader(int Count, int Offsize, int First) {
        // Add the count field
        OutputList.addLast(new UInt16Item((char) Count));
        // Add the offsize field
        OutputList.addLast(new UInt8Item((char) Offsize));
        // Add the first offset according to the offsize
        switch (Offsize) {
            case 1:
                // first offset
                OutputList.addLast(new UInt8Item((char) First));
                break;
            case 2:
                // first offset
                OutputList.addLast(new UInt16Item((char) First));
                break;
            case 3:
                // first offset
                OutputList.addLast(new UInt24Item((char) First));
                break;
            case 4:
                // first offset
                OutputList.addLast(new UInt32Item((char) First));
                break;
            default:
                break;
        }
    }

    /**
     * Function adds the keys into the TopDict
     *
     * @param fdarrayRef     OffsetItem for the FDArray
     * @param fdselectRef    OffsetItem for the FDSelect
     * @param charsetRef     OffsetItem for the CharSet
     * @param charstringsRef OffsetItem for the CharString
     */
    protected void CreateKeys(OffsetItem fdarrayRef, OffsetItem fdselectRef, OffsetItem charsetRef, OffsetItem charstringsRef) {
        // create an FDArray key
        OutputList.addLast(fdarrayRef);
        OutputList.addLast(new UInt8Item((char) 12));
        OutputList.addLast(new UInt8Item((char) 36));
        // create an FDSelect key
        OutputList.addLast(fdselectRef);
        OutputList.addLast(new UInt8Item((char) 12));
        OutputList.addLast(new UInt8Item((char) 37));
        // create an charset key
        OutputList.addLast(charsetRef);
        OutputList.addLast(new UInt8Item((char) 15));
        // create a CharStrings key
        OutputList.addLast(charstringsRef);
        OutputList.addLast(new UInt8Item((char) 17));
    }

    /**
     * Function takes the original string item and adds the new strings
     * to accommodate the CID rules
     *
     * @param Font the font
     */
    protected void CreateNewStringIndex(int Font) {
        String fdFontName = fonts[Font].name + "-OneRange";
        if (fdFontName.length() > 127)
            fdFontName = fdFontName.substring(0, 127);
        String extraStrings = "Adobe" + "Identity" + fdFontName;

        int origStringsLen = stringOffsets[stringOffsets.length - 1]
                - stringOffsets[0];
        int stringsBaseOffset = stringOffsets[0] - 1;

        byte stringsIndexOffSize;
        if (origStringsLen + extraStrings.length() <= 0xff) stringsIndexOffSize = 1;
        else if (origStringsLen + extraStrings.length() <= 0xffff) stringsIndexOffSize = 2;
        else if (origStringsLen + extraStrings.length() <= 0xffffff) stringsIndexOffSize = 3;
        else stringsIndexOffSize = 4;

        // count
        OutputList.addLast(new UInt16Item((char) (stringOffsets.length - 1 + 3)));
        // offSize
        OutputList.addLast(new UInt8Item((char) stringsIndexOffSize));
        for (int stringOffset : stringOffsets)
            OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize,
                    stringOffset - stringsBaseOffset));
        int currentStringsOffset = stringOffsets[stringOffsets.length - 1]
                - stringsBaseOffset;
        // l.addLast(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset));
        currentStringsOffset += "Adobe".length();
        OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize, currentStringsOffset));
        currentStringsOffset += "Identity".length();
        OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize, currentStringsOffset));
        currentStringsOffset += fdFontName.length();
        OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize, currentStringsOffset));

        OutputList.addLast(new RangeItem(buf, stringOffsets[0], origStringsLen));
        OutputList.addLast(new StringItem(extraStrings));
    }

    /**
     * Function creates new FDSelect for non-CID fonts.
     * The FDSelect built uses a single range for all glyphs
     *
     * @param fdselectRef OffsetItem for the FDSelect
     * @param nglyphs     the number of glyphs in the font
     */
    protected void CreateFDSelect(OffsetItem fdselectRef, int nglyphs) {
        OutputList.addLast(new MarkerItem(fdselectRef));
        // format identifier
        OutputList.addLast(new UInt8Item((char) 3));
        // nRanges
        OutputList.addLast(new UInt16Item((char) 1));

        // Range[0].firstGlyph
        OutputList.addLast(new UInt16Item((char) 0));
        // Range[0].fd
        OutputList.addLast(new UInt8Item((char) 0));

        // sentinel
        OutputList.addLast(new UInt16Item((char) nglyphs));
    }

    /**
     * Function creates new CharSet for non-CID fonts.
     * The CharSet built uses a single range for all glyphs
     *
     * @param charsetRef OffsetItem for the CharSet
     * @param nglyphs    the number of glyphs in the font
     */
    protected void CreateCharset(OffsetItem charsetRef, int nglyphs) {
        OutputList.addLast(new MarkerItem(charsetRef));
        // format identifier
        OutputList.addLast(new UInt8Item((char) 2));
        // first glyph in range (ignore .notdef)
        OutputList.addLast(new UInt16Item((char) 1));
        // nLeft
        OutputList.addLast(new UInt16Item((char) (nglyphs - 1)));
    }

    /**
     * Function creates new FDArray for non-CID fonts.
     * The FDArray built has only the "Private" operator that points to the font's
     * original private dict
     *
     * @param fdarrayRef OffsetItem for the FDArray
     * @param privateRef OffsetItem for the Private Dict
     * @param Font       the font
     */
    protected void CreateFDArray(OffsetItem fdarrayRef, OffsetItem privateRef, int Font) {
        OutputList.addLast(new MarkerItem(fdarrayRef));
        // Build the header (count=offsize=first=1)
        BuildIndexHeader(1, 1, 1);

        // Mark
        OffsetItem privateIndex1Ref = new IndexOffsetItem(1);
        OutputList.addLast(privateIndex1Ref);
        IndexBaseItem privateBase = new IndexBaseItem();
        // Insert the private operands and operator
        OutputList.addLast(privateBase);
        // Calc the new size of the private after subsetting
        // Origianl size
        int NewSize = fonts[Font].privateLength;
        // Calc the original size of the Subr offset in the private
        int OrgSubrsOffsetSize = CalcSubrOffsetSize(fonts[Font].privateOffset, fonts[Font].privateLength);
        // Increase the ptivate's size
        if (OrgSubrsOffsetSize != 0)
            NewSize += 5 - OrgSubrsOffsetSize;
        OutputList.addLast(new DictNumberItem(NewSize));
        OutputList.addLast(privateRef);
        // Private
        OutputList.addLast(new UInt8Item((char) 18));

        OutputList.addLast(new IndexMarkerItem(privateIndex1Ref, privateBase));
    }

    /**
     * Function reconstructs the FDArray, PrivateDict and LSubr for CID fonts
     *
     * @param Font the font
     */
    void Reconstruct(int Font) {
        // Init for later use
        OffsetItem[] fdPrivate = new DictOffsetItem[fonts[Font].FDArrayOffsets.length - 1];
        IndexBaseItem[] fdPrivateBase = new IndexBaseItem[fonts[Font].fdprivateOffsets.length];
        OffsetItem[] fdSubrs = new DictOffsetItem[fonts[Font].fdprivateOffsets.length];
        // Reconstruct each type
        ReconstructFDArray(Font, fdPrivate);
        ReconstructPrivateDict(Font, fdPrivate, fdPrivateBase, fdSubrs);
        ReconstructPrivateSubrs(Font, fdPrivateBase, fdSubrs);
    }

    /**
     * Function subsets the FDArray and builds the new one with new offsets
     *
     * @param Font      The font
     * @param fdPrivate OffsetItem Array (one for each FDArray)
     */
    void ReconstructFDArray(int Font, OffsetItem[] fdPrivate) {
        // Build the header of the index
        BuildIndexHeader(fonts[Font].FDArrayCount, fonts[Font].FDArrayOffsize, 1);

        // For each offset create an Offset Item
        OffsetItem[] fdOffsets = new IndexOffsetItem[fonts[Font].FDArrayOffsets.length - 1];
        for (int i = 0; i < 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.contains(Integer.valueOf(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 ("Private".equals(key)) {
                    // Save the original length of the private dict
                    int NewSize = (int) ((Integer) 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.addLast(new DictNumberItem(NewSize));
                    fdPrivate[k] = new DictOffsetItem();
                    OutputList.addLast(fdPrivate[k]);
                    // Private
                    OutputList.addLast(new UInt8Item((char) 18));
                    // 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 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
     */
    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.contains(Integer.valueOf(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 ("Subrs".equals(key)) {
                    fdSubrs[i] = new DictOffsetItem();
                    OutputList.addLast(fdSubrs[i]);
                    // Subrs
                    OutputList.addLast(new UInt8Item((char) 19));
                }
                // Else copy the entire range
                else
                    OutputList.addLast(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
     */

    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]));
                if (NewLSubrsIndex[i] != null)
                    OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(rasFactory.createSource(NewLSubrsIndex[i])), 0, NewLSubrsIndex[i].length));
            }
        }
    }

    /**
     * Calculates how many byte it took to write the offset for the subrs in a specific
     * private dict.
     *
     * @param Offset The Offset for the private dict
     * @param Size   The size of the private dict
     * @return The size of the offset of the subrs in the private dict
     */
    int CalcSubrOffsetSize(int Offset, int Size) {
        // Set the size to 0
        int OffsetSize = 0;
        // Go to the beginning of the private dict
        seek(Offset);
        // Go until the end of the private dict
        while (getPosition() < Offset + Size) {
            int p1 = getPosition();
            getDictItem();
            int p2 = getPosition();
            // When reached to the subrs offset
            if ("Subrs".equals(key)) {
                // The Offsize (minus the subrs key)
                OffsetSize = p2 - p1 - 1;
            }
            // All other keys are ignored
        }
        // return the size
        return OffsetSize;
    }

    /**
     * Function computes the size of an index
     *
     * @param indexOffset The offset for the computed index
     * @return The size of the index
     */
    protected int countEntireIndexRange(int indexOffset) {
        // Go to the beginning of the index
        seek(indexOffset);
        // Read the count field
        int count = getCard16();
        // If count==0 -> size=2
        if (count == 0)
            return 2;
        else {
            // Read the offsize field
            int indexOffSize = getCard8();
            // Go to the last element of the offset array
            seek(indexOffset + 2 + 1 + count * indexOffSize);
            // The size of the object array is the value of the last element-1
            int size = getOffset(indexOffSize) - 1;
            // Return the size of the entire index
            return 2 + 1 + (count + 1) * indexOffSize + size;
        }
    }

    /**
     * The function creates a private dict for a font that was not CID
     * All the keys are copied as is except for the subrs key
     *
     * @param Font the font
     * @param Subr The OffsetItem for the subrs of the private
     */
    void CreateNonCIDPrivate(int Font, OffsetItem Subr) {
        // Go to the beginning of the private dict and read until the end
        seek(fonts[Font].privateOffset);
        while (getPosition() < fonts[Font].privateOffset + fonts[Font].privateLength) {
            int p1 = getPosition();
            getDictItem();
            int p2 = getPosition();
            // If the dictItem is the "Subrs" then,
            // use marker for offset and write operator number
            if ("Subrs".equals(key)) {
                OutputList.addLast(Subr);
                // Subrs
                OutputList.addLast(new UInt8Item((char) 19));
            }
            // Else copy the entire range
            else
                OutputList.addLast(new RangeItem(buf, p1, p2 - p1));
        }
    }

    /**
     * the function marks the beginning of the subrs index and adds the subsetted subrs
     * index to the output list.
     *
     * @param Font        the font
     * @param PrivateBase IndexBaseItem for the private that's referencing to the subrs
     * @param Subrs       OffsetItem for the subrs
     */
    void CreateNonCIDSubrs(int Font, IndexBaseItem PrivateBase, OffsetItem Subrs) {
        // Mark the beginning of the Subrs index
        OutputList.addLast(new SubrMarkerItem(Subrs, PrivateBase));
        // Put the subsetted new subrs index
        if (NewSubrsIndexNonCID != null) {
            OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(rasFactory.createSource(NewSubrsIndexNonCID)), 0, NewSubrsIndexNonCID.length));
        }
    }

    /**
     * Returns the CID to which specified GID is mapped.
     *
     * @param gid glyph identifier
     *
     * @return CID value
     */
    int getCidForGlyphId(int gid) {
        return getCidForGlyphId(0, gid);
    }

    /**
     * Returns the CID to which specified GID is mapped.
     *
     * @param fontIndex index of font for which cid-gid mapping is to be identified
     * @param gid glyph identifier
     *
     * @return CID value
     */
    int getCidForGlyphId(int fontIndex, int gid) {
        if (fonts[fontIndex].gidToCid == null) {
            return gid;
        }

        // gidToCid mapping starts with value corresponding to gid == 1, becuase .notdef is omitted
        int index = gid - 1;
        return index >= 0 && index < fonts[fontIndex].gidToCid.length
                ? fonts[fontIndex].gidToCid[index]
                : gid;
    }

    /**
     * Creates glyph-to-character id array.
     *
     * @param fontIndex   index of font for which charsets data is to be parsed
     * @param numOfGlyphs number of glyphs in the font
     * @param offset      the offset to charsets data
     */
    private void initGlyphIdToCharacterIdArray(int fontIndex, int numOfGlyphs, int offset) {
        // Seek charset offset
        seek(offset);

        // Read the format
        int format = getCard8();

        // .notdef is omitted, therefore remaining number of elements is one less than overall number
        int numOfElements = numOfGlyphs - 1;
        fonts[fontIndex].gidToCid = new int[numOfElements];

        switch (format) {
            case 0:
                for (int i = 0; i < numOfElements; i++) {
                    int cid = getCard16();
                    fonts[fontIndex].gidToCid[i] = cid;
                }
                break;
            case 1:
            case 2:
                int start = 0;
                while (start < numOfElements) {
                    int first = getCard16();
                    int nLeft = format == 1 ? getCard8() : getCard16();
                    for (int i = 0; i <= nLeft && start < numOfElements; i++) {
                        fonts[fontIndex].gidToCid[start++] = first + i;
                    }
                }
                break;
            default:
                break;
        }
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy