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

com.itextpdf.text.pdf.CFFFontSubset Maven / Gradle / Ivy

The 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.text.pdf;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * This Class subsets a CFF Type Font. The subset is preformed for CID fonts and NON CID fonts.
 * The Charstring is subsetted for both types. For CID fonts only the FDArray which are used are embedded.
 * The Lsubroutines of the FDArrays used are subsetted as well. The Subroutine subset supports both Type1 and Type2
 * formatting although only tested on Type2 Format.
 * For Non CID the Lsubroutines are subsetted. On both types the Gsubroutines is subsetted.
 * A font which was not of CID type is transformed into CID as a part of the subset process.
 * The CID synthetic creation was written by Sivan Toledo ([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 HashMap containing the glyphs used in the text after being converted
	 * to glyph number by the CMap
	 */
	HashMap GlyphsUsed;
	/**
	 * The GlyphsUsed keys as an ArrayList
	 */
	ArrayList glyphsInList;
	/**
	 * A HashSet for keeping the FDArrays being used by the font
	 */
	HashSet FDArrayUsed = new HashSet();
	/**
	 * A HashMaps array for keeping the subroutines used in each FontDict
	 */
	HashMap[] hSubrsUsed;
	/**
	 * The SubroutinesUsed HashMaps as ArrayLists
	 */
	ArrayList[] lSubrsUsed;
	/**
	 * A HashMap for keeping the Global subroutines used in the font
	 */
	HashMap hGSubrsUsed  = new HashMap();
	/**
	 * The Global SubroutinesUsed HashMaps as ArrayLists
	 */
	ArrayList lGSubrsUsed = new ArrayList();
	/**
	 * A HashMap for keeping the subroutines used in a non-cid font
	 */
	HashMap hSubrsUsedNonCID  = new HashMap();
	/**
	 * The SubroutinesUsed HashMap as ArrayList
	 */
	ArrayList lSubrsUsedNonCID = new ArrayList();
	/**
	 * An array of the new Indexes for the local Subr. One index for each FontDict
	 */
	byte[][] NewLSubrsIndex;
	/**
	 * The new subroutines index for a non-cid font
	 */
	byte[] NewSubrsIndexNonCID;
	/**
	 * The new global subroutines index of the font
	 */
	byte[] NewGSubrsIndex;
	/**
	 * The new CharString of the font
	 */
	byte[] NewCharStringsIndex;

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

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

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


	/**
	 * C'tor for CFFFontSubset
	 * @param rf - The font file
	 * @param GlyphsUsed - a HashMap that contains the glyph used in the subset
	 */
    public CFFFontSubset(RandomAccessFileOrArray rf,HashMap GlyphsUsed){
		// Use CFFFont c'tor in order to parse the font file.
    	super(rf);
		this.GlyphsUsed = GlyphsUsed;
		//Put the glyphs into a list
		glyphsInList = new ArrayList(GlyphsUsed.keySet());


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

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

    /**
     * Function calculates the number of ranges in the Charset
     * @param NumofGlyphs The number of glyphs in the font
     * @param Type The format of the Charset
     * @return The number of ranges in the Charset data structure
     */
    int CountRange(int NumofGlyphs,int Type){
    	int num=0;
    	char Sid;
    	int i=1,nLeft;
    	while (i= 0)
				GBias = CalcBias(gsubrIndexOffset,j);

	        // Prepare the new CharStrings Index
			BuildNewCharString(j);
			 // Prepare the new Global and Local Subrs Indices
			BuildNewLGSubrs(j);
			// Build the new file
			byte[] Ret = BuildNewFile(j);
			return Ret;
		}
		finally {
            try {
                buf.close();
            }
            catch (Exception e) {
                // empty on purpose
            }
		}
	}

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

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

	/**
	 * Function builds the new local & global subsrs indices. IF CID then All of
	 * the FD Array lsubrs will be subsetted.
	 * @param Font the font
	 * @throws IOException
	 */
    @SuppressWarnings("unchecked")
    protected void BuildNewLGSubrs(int Font)throws IOException
	{
		// If the font is CID then the lsubrs are divided into FontDicts.
		// for each FD array the lsubrs will be subsetted.
		if(fonts[Font].isCID)
		{
			// Init the hashmap-array and the arraylist-array to hold the subrs used
			// in each private dict.
			hSubrsUsed = new HashMap[fonts[Font].fdprivateOffsets.length];
			lSubrsUsed = new ArrayList[fonts[Font].fdprivateOffsets.length];
			// A [][] which will store the byte array for each new FD Array lsubs index
			NewLSubrsIndex = new byte[fonts[Font].fdprivateOffsets.length][];
			// An array to hold the offset for each Lsubr index
			fonts[Font].PrivateSubrsOffset = new int[fonts[Font].fdprivateOffsets.length];
			// A [][] which will store the offset array for each lsubr index
			fonts[Font].PrivateSubrsOffsetsArray = new int[fonts[Font].fdprivateOffsets.length][];

			// Put the FDarrayUsed into a list
			ArrayList FDInList = new ArrayList(FDArrayUsed);
			// For each FD array which is used subset the lsubr
			for (int j=0;j();
				lSubrsUsed[FD] = new ArrayList();
				//Reads the private dicts looking for the subr operator and
				// store both the offset for the index and its offset array
				BuildFDSubrsOffsets(Font,FD);
				// Verify that FDPrivate has a LSubrs index
				if(fonts[Font].PrivateSubrsOffset[FD]>=0)
				{
					//Scans the Charstring data storing the used Local and Global subroutines
					// by the glyphs. Scans the Subrs recursively.
					BuildSubrUsed(Font,FD,fonts[Font].PrivateSubrsOffset[FD],fonts[Font].PrivateSubrsOffsetsArray[FD],hSubrsUsed[FD],lSubrsUsed[FD]);
					// Builds the New Local Subrs index
					NewLSubrsIndex[FD] = BuildNewIndex(fonts[Font].PrivateSubrsOffsetsArray[FD],hSubrsUsed[FD],RETURN_OP);
				}
			}
		}
		// If the font is not CID && the Private Subr exists then subset:
		else if (fonts[Font].privateSubrs>=0)
		{
			// Build the subrs offsets;
			fonts[Font].SubrsOffsets = getIndex(fonts[Font].privateSubrs);
			//Scans the Charstring data storing the used Local and Global subroutines
			// by the glyphs. Scans the Subrs recursively.
			BuildSubrUsed(Font,-1,fonts[Font].privateSubrs,fonts[Font].SubrsOffsets,hSubrsUsedNonCID,lSubrsUsedNonCID);
		}
		// For all fonts subset the Global Subroutines
		// Scan the Global Subr Hashmap recursively on the Gsubrs
		BuildGSubrsUsed(Font);
		if (fonts[Font].privateSubrs>=0)
			// Builds the New Local Subrs index
			NewSubrsIndexNonCID = BuildNewIndex(fonts[Font].SubrsOffsets,hSubrsUsedNonCID,RETURN_OP);
		//Builds the New Global Subrs index
		NewGSubrsIndex = 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 (key=="Subrs")
        		fonts[Font].PrivateSubrsOffset[FD] = ((Integer)args[0]).intValue()+fonts[Font].fdprivateOffsets[FD];
        }
        //Read the lsubr index if the lsubr was found
        if (fonts[Font].PrivateSubrsOffset[FD] >= 0)
        	fonts[Font].PrivateSubrsOffsetsArray[FD] = getIndex(fonts[Font].PrivateSubrsOffset[FD]);
	}

	/**
	 * Function uses ReadAsubr on the glyph used to build the LSubr & Gsubr HashMap.
	 * The HashMap (of the lsubr only) is then scanned recursively for Lsubr & Gsubrs
	 * calls.
	 * @param Font the font
	 * @param FD FD array processed. 0 indicates function was called by non CID font
	 * @param SubrOffset the offset to the subr index to calc the bias
	 * @param SubrsOffsets the offset array of the subr index
	 * @param hSubr HashMap of the subrs used
	 * @param lSubr ArrayList of the subrs used
	 */
	protected void BuildSubrUsed(int Font,int FD,int SubrOffset,int[] SubrsOffsets,HashMap hSubr,ArrayList lSubr)
	{

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

		// For each glyph used find its GID, start & end pos
		for (int i=0;i= 0)
			{
				EmptyStack();
				NumOfHints=0;
				// Using FDSELECT find the FD Array the glyph belongs to.
				int GlyphFD = fonts[Font].FDSelect[glyph];
				// If the Glyph is part of the FD being processed
				if (GlyphFD == FD)
					// Find the Subrs called by the glyph and insert to hash:
					ReadASubr(Start,End,GBias,LBias,hSubr,lSubr,SubrsOffsets);
			}
			else
				// If the font is not CID
				//Find the Subrs called by the glyph and insert to hash:
				ReadASubr(Start,End,GBias,LBias,hSubr,lSubr,SubrsOffsets);
		}
		// For all Lsubrs used, check recursively for Lsubr & Gsubr used
		for (int i=0;i=0)
			{
				// Read and process the subr
				int Start = SubrsOffsets[Subr];
				int End = SubrsOffsets[Subr+1];
				ReadASubr(Start,End,GBias,LBias,hSubr,lSubr,SubrsOffsets);
			}
		}
	}

	/**
	 * Function scans the Glsubr used ArrayList to find recursive calls
	 * to Gsubrs and adds to Hashmap & ArrayList
	 * @param Font the font
	 */
	protected void BuildGSubrsUsed(int Font)
	{
		int LBias = 0;
		int SizeOfNonCIDSubrsUsed = 0;
		if (fonts[Font].privateSubrs>=0)
		{
			LBias = CalcBias(fonts[Font].privateSubrs,Font);
			SizeOfNonCIDSubrsUsed = lSubrsUsedNonCID.size();
		}

		// For each global subr used
		for (int i=0;i=0)
			{
				// Read the subr and process
				int Start = gsubrOffsets[Subr];
				int End = gsubrOffsets[Subr+1];

				if (fonts[Font].isCID)
					ReadASubr(Start,End,GBias,0,hGSubrsUsed,lGSubrsUsed,null);
				else
				{
					ReadASubr(Start,End,GBias,LBias,hSubrsUsedNonCID,lSubrsUsedNonCID,fonts[Font].SubrsOffsets);
					if (SizeOfNonCIDSubrsUsed < lSubrsUsedNonCID.size())
					{
						for (int j=SizeOfNonCIDSubrsUsed;j=0)
							{
								// Read the subr and process
								int LStart = fonts[Font].SubrsOffsets[LSubr];
								int LEnd = fonts[Font].SubrsOffsets[LSubr+1];
								ReadASubr(LStart,LEnd,GBias,LBias,hSubrsUsedNonCID,lSubrsUsedNonCID,fonts[Font].SubrsOffsets);
							}
						}
						SizeOfNonCIDSubrsUsed = lSubrsUsedNonCID.size();
					}
				}
			}
		}
	}

	/**
	 * The function reads a subrs (glyph info) between begin and end.
	 * Adds calls to a Lsubr to the hSubr and lSubrs.
	 * Adds calls to a Gsubr to the hGSubr and lGSubrs.
	 * @param begin the start point of the subr
	 * @param end the end point of the subr
	 * @param GBias the bias of the Global Subrs
	 * @param LBias the bias of the Local Subrs
	 * @param hSubr the HashMap for the lSubrs
	 * @param lSubr the ArrayList for the lSubrs
	 */
	protected void ReadASubr(int begin,int end,int GBias,int LBias,HashMap hSubr,ArrayList lSubr,int[] LSubrsOffsets)
	{
		// Clear the stack for the subrs
		EmptyStack();
		NumOfHints = 0;
		// Goto beginning of the subr
        seek(begin);
        while (getPosition() < end)
        {
        	// Read the next command
        	ReadCommand();
        	int pos = getPosition();
        	Object TopElement=null;
        	if (arg_count > 0)
        		TopElement = args[arg_count-1];
        	int NumOfArgs = arg_count;
        	// Check the modification needed on the Argument Stack according to key;
        	HandelStack();
        	// a call to a Lsubr
        	if (key=="callsubr")
        	{
        		// Verify that arguments are passed
        		if (NumOfArgs > 0)
        		{
            		// Calc the index of the Subrs
            		int Subr = ((Integer)TopElement).intValue() + LBias;
            		// If the subr isn't in the HashMap -> Put in
        			if (!hSubr.containsKey(Integer.valueOf (Subr)))
            		{
            			hSubr.put(Integer.valueOf(Subr),null);
            			lSubr.add(Integer.valueOf(Subr));
            		}
        			CalcHints(LSubrsOffsets[Subr],LSubrsOffsets[Subr+1],LBias,GBias,LSubrsOffsets);
        			seek(pos);
        		}
        	}
        	// a call to a Gsubr
        	else if (key=="callgsubr")
        	{
        		// Verify that arguments are passed
        		if (NumOfArgs > 0)
        		{
	        		// Calc the index of the Subrs
	        		int Subr = ((Integer)TopElement).intValue() + GBias;
	        		// If the subr isn't in the HashMap -> Put in
	        		if (!hGSubrsUsed.containsKey(Integer.valueOf (Subr)))
	        		{
	        			hGSubrsUsed.put(Integer.valueOf(Subr),null);
	        			lGSubrsUsed.add(Integer.valueOf(Subr));
	        		}
	        		CalcHints(gsubrOffsets[Subr],gsubrOffsets[Subr+1],LBias,GBias,LSubrsOffsets);
	        		seek(pos);
        		}
        	}
        	// A call to "stem"
        	else if (key == "hstem" || key == "vstem" || key == "hstemhm" || key == "vstemhm") {
				// Increment the NumOfHints by the number couples of of arguments
				NumOfHints += NumOfArgs / 2;
			}
        	// A call to "mask"
        	else if (key == "hintmask" || key == "cntrmask")
        	{
				// 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 flush the stack
	 */
	protected int StackOpp()
	{
		if (key == "ifelse")
			return -3;
		if (key == "roll" || key == "put")
			return -2;
		if (key == "callsubr" || key == "callgsubr" || key == "add" || key == "sub" ||
			key == "div" || key == "mul" || key == "drop" || key == "and" ||
			key == "or" || key == "eq")
			return -1;
		if (key == "abs" || key == "neg" || key == "sqrt" || key == "exch" ||
			key == "index" || key == "get" || key == "not" || key == "return")
			return 0;
		if (key == "random" || key == "dup")
			return 1;
		return 2;
	}

	/**
	 * Empty the Type2 Stack
	 *
	 */
	protected void EmptyStack()
	{
		// Null the arguments
    	for (int i=0; i0)
		{
			args[arg_count-1]=null;
			arg_count--;
		}
	}

	/**
	 * Add an item to the stack
	 *
	 */
	protected void PushStack()
	{
		arg_count++;
	}

	/**
	 * The function reads the next command after the file pointer is set
	 */
	protected void ReadCommand()
	{
        key = null;
        boolean gotKey = false;
        // Until a key is found
        while (!gotKey) {
        	// Read the first Char
            char b0 = getCard8();
            // decode according to the type1/type2 format
            if (b0 == 28) // the two next bytes represent a short int;
            {
            	int first = getCard8();
            	int second = getCard8();
            	args[arg_count] = Integer.valueOf(first<<8 | second);
            	arg_count++;
            	continue;
            }
            if (b0 >= 32 && b0 <= 246) // The byte read is the byte;
            {
            	args[arg_count] = Integer.valueOf(b0 - 139);
            	arg_count++;
            	continue;
            }
            if (b0 >= 247 && b0 <= 250) // The byte read and the next byte constitute a short int
            {
            	int w = getCard8();
            	args[arg_count] = Integer.valueOf((b0-247)*256 + w + 108);
            	arg_count++;
            	continue;
            }
            if (b0 >= 251 && b0 <= 254)// Same as above except negative
            {
            	int w = getCard8();
            	args[arg_count] = Integer.valueOf(-(b0-251)*256 - w - 108);
            	arg_count++;
            	continue;
            }
            if (b0 == 255)// The next for bytes represent a double.
            {
            	int first = getCard8();
            	int second = getCard8();
            	int third = getCard8();
            	int fourth = getCard8();
            	args[arg_count] = Integer.valueOf(first<<24 | second<<16 | third<<8 | fourth);
            	arg_count++;
            	continue;
            }
            if (b0<=31 && b0 != 28) // An operator was found.. Set Key.
            {
            	gotKey=true;
            	// 12 is an escape command therefore the next byte is a part
            	// of this command
            	if (b0 == 12)
            	{
            		int b1 = getCard8();
            		if (b1>SubrsEscapeFuncs.length-1)
            			b1 = SubrsEscapeFuncs.length-1;
            		key = SubrsEscapeFuncs[b1];
            	}
            	else
            		key = SubrsFunctions[b0];
                continue;
            }
        }
	}

	/**
	 * The function reads the subroutine and returns the number of the hint in it.
	 * If a call to another subroutine is found the function calls recursively.
	 * @param begin the start point of the subr
	 * @param end the end point of the subr
	 * @param LBias the bias of the Local Subrs
	 * @param GBias the bias of the Global Subrs
	 * @param LSubrsOffsets The Offsets array of the subroutines
	 * @return The number of hints in the subroutine read.
	 */
	protected int CalcHints(int begin,int end,int LBias,int GBias,int[] LSubrsOffsets)
	{
		// Goto beginning of the subr
        seek(begin);
        while (getPosition() < end)
        {
        	// Read the next command
        	ReadCommand();
        	int pos = getPosition();
        	Object TopElement = null;
        	if (arg_count>0)
        		TopElement = args[arg_count-1];
        	int NumOfArgs = arg_count;
            //Check the modification needed on the Argument Stack according to key;
        	HandelStack();
        	// a call to a Lsubr
        	if (key=="callsubr")
        	{
        		if (NumOfArgs>0)
        		{
            		int Subr = ((Integer)TopElement).intValue() + LBias;
            		CalcHints(LSubrsOffsets[Subr],LSubrsOffsets[Subr+1],LBias,GBias,LSubrsOffsets);
            		seek(pos);
        		}
        	}
        	// a call to a Gsubr
        	else if (key=="callgsubr")
        	{
        		if (NumOfArgs>0)
        		{
            		int Subr = ((Integer)TopElement).intValue() + GBias;
            		CalcHints(gsubrOffsets[Subr],gsubrOffsets[Subr+1],LBias,GBias,LSubrsOffsets);
            		seek(pos);
        		}
        	}
        	// A call to "stem"
        	else if (key == "hstem" || key == "vstem" || key == "hstemhm" || key == "vstemhm")
        		// Increment the NumOfHints by the number couples of of arguments
        		NumOfHints += NumOfArgs/2;
        	// A call to "mask"
        	else if (key == "hintmask" || key == "cntrmask")
        	{
        		// Compute the size of the mask
        		int SizeOfMask = NumOfHints/8;
        		if (NumOfHints%8 != 0 || SizeOfMask == 0)
        			SizeOfMask++;
        		// Continue the pointer in SizeOfMask steps
        		for (int i=0;i Used,byte OperatorForUnusedEntries) throws IOException
	{
		int unusedCount = 0;
		int Offset=0;
		int[] NewOffsets = new int[Offsets.length];
		// Build the Offsets Array for the Subset
		for (int i=0;i>> 8 & 0xff);
        NewIndex[Place++] = (byte) (Count >>> 0 & 0xff);
        // Write the offsize field
        NewIndex[Place++] = Offsize;
        // Write the offset array according to the offsize
        for (int newOffset : NewOffsets) {
        	// The value to be written
        	int Num = newOffset-NewOffsets[0]+1;
        	// Write in bytes according to the offsize
        	switch (Offsize) {
                case 4:
                	NewIndex[Place++] = (byte) (Num >>> 24 & 0xff);
                case 3:
                	NewIndex[Place++] = (byte) (Num >>> 16 & 0xff);
                case 2:
                	NewIndex[Place++] = (byte) (Num >>>  8 & 0xff);
                case 1:
                	NewIndex[Place++] = (byte) (Num >>>  0 & 0xff);
        	}
        }
        // Write the new object array one by one
        for (byte newObject : NewObjects) {
        	NewIndex[Place++] = newObject;
        }
        // Return the new index
        return NewIndex;
	}

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

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

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

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

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

        // If the font is not CID create the following keys
        if ( !fonts[Font].isCID ) {
            // create a ROS key
        	OutputList.addLast(new DictNumberItem(fonts[Font].nstrings));
        	OutputList.addLast(new DictNumberItem(fonts[Font].nstrings+1));
        	OutputList.addLast(new DictNumberItem(0));
        	OutputList.addLast(new UInt8Item((char)12));
        	OutputList.addLast(new UInt8Item((char)30));
            // create a CIDCount key
        	OutputList.addLast(new DictNumberItem(fonts[Font].nglyphs));
        	OutputList.addLast(new UInt8Item((char)12));
            OutputList.addLast(new UInt8Item((char)34));
            // Sivan's comments
            // What about UIDBase (12,35)? Don't know what is it.
            // I don't think we need FontName; the font I looked at didn't have it.
        }
        // Go to the TopDict of the font being processed
        seek(topdictOffsets[Font]);
        // Run until the end of the TopDict
        while (getPosition() < topdictOffsets[Font+1]) {
            int p1 = getPosition();
            getDictItem();
            int p2 = getPosition();
            // The encoding key is disregarded since CID has no encoding
            if (key=="Encoding"
            // These keys will be added manually by the process.
            || key=="Private"
            || key=="FDSelect"
            || key=="FDArray"
            || key=="charset"
            || key=="CharStrings"
            ) {
            }else {
            //OtherWise copy key "as is" to the output list
                OutputList.add(new RangeItem(buf,p1,p2-p1));
            }
        }
        // Create the FDArray, FDSelect, Charset and CharStrings Keys
        CreateKeys(fdarrayRef,fdselectRef,charsetRef,charstringsRef);

        // Mark the end of the top dict area
        OutputList.addLast(new IndexMarkerItem(topdictIndex1Ref,topdictBase));

        // Copy the string index

        if (fonts[Font].isCID)
            OutputList.addLast(getEntireIndexRange(stringIndexOffset));
        // If the font is not CID we need to append new strings.
        // We need 3 more strings: Registry, Ordering, and a FontName for one FD.
        // The total length is at most "Adobe"+"Identity"+63 = 76
        else
        	CreateNewStringIndex(Font);

        // copy the new subsetted global subroutine index
        OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(NewGSubrsIndex),0,NewGSubrsIndex.length));

        // deal with fdarray, fdselect, and the font descriptors
        // If the font is CID:
        if (fonts[Font].isCID) {
            // copy the FDArray, FDSelect, charset

            // Copy FDSelect
        	// Mark the beginning
            OutputList.addLast(new MarkerItem(fdselectRef));
            // If an FDSelect exists copy it
            if (fonts[Font].fdselectOffset>=0)
        		OutputList.addLast(new RangeItem(buf,fonts[Font].fdselectOffset,fonts[Font].FDSelectLength));
            // Else create a new one
            else
            	CreateFDSelect(fdselectRef,fonts[Font].nglyphs);

          	// Copy the Charset
            // Mark the beginning and copy entirely
            OutputList.addLast(new MarkerItem(charsetRef));
            OutputList.addLast(new RangeItem(buf,fonts[Font].charsetOffset,fonts[Font].CharsetLength));

            // Copy the FDArray
            // If an FDArray exists
            if (fonts[Font].fdarrayOffset>=0)
            {
                // Mark the beginning
	            OutputList.addLast(new MarkerItem(fdarrayRef));
	            // Build a new FDArray with its private dicts and their LSubrs
	            Reconstruct(Font);
            }
            else
            	// Else create a new one
            	CreateFDArray(fdarrayRef,privateRef,Font);

        }
        // If the font is not CID
        else
        {
            // create FDSelect
        	CreateFDSelect(fdselectRef,fonts[Font].nglyphs);
            // recreate a new charset
        	CreateCharset(charsetRef,fonts[Font].nglyphs);
            // create a font dict index (fdarray)
        	CreateFDArray(fdarrayRef,privateRef,Font);
        }

        // if a private dict exists insert its subsetted version
        if (fonts[Font].privateOffset>=0)
        {
        	// Mark the beginning of the private dict
        	IndexBaseItem PrivateBase = new IndexBaseItem();
	        OutputList.addLast(PrivateBase);
        	OutputList.addLast(new MarkerItem(privateRef));

        	OffsetItem Subr = new DictOffsetItem();
        	// Build and copy the new private dict
        	CreateNonCIDPrivate(Font,Subr);
        	// Copy the new LSubrs index
        	CreateNonCIDSubrs(Font,PrivateBase,Subr);
        }

        // copy the charstring index
        OutputList.addLast(new MarkerItem(charstringsRef));

        // Add the subsetted charstring
        OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(NewCharStringsIndex),0,NewCharStringsIndex.length));

        // now create the new CFF font
        int[] currentOffset = new int[1];
        currentOffset[0] = 0;
        // Count and save the offset for each item
        Iterator listIter = OutputList.iterator();
        while ( listIter.hasNext() ) {
            Item item = listIter.next();
            item.increment(currentOffset);
        }
        // Compute the Xref for each of the offset items
        listIter = OutputList.iterator();
        while ( listIter.hasNext() ) {
            Item item = listIter.next();
            item.xref();
        }

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

        // Emit all the items into the new byte array
        listIter = OutputList.iterator();
        while ( listIter.hasNext() ) {
            Item item = listIter.next();
            item.emit(b);
        }
        // Return the new stream
        return b;
    }

	/**
	 * Function Copies the header from the original fileto the output list
	 */
	protected void CopyHeader()
	{
		seek(0);
        int major = getCard8();
        int minor = getCard8();
        int hdrSize = getCard8();
        int offSize = getCard8();
        nextIndexOffset = hdrSize;
        OutputList.addLast(new RangeItem(buf,0,hdrSize));
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		// For each offset create an Offset Item
		OffsetItem[] fdOffsets = new IndexOffsetItem[fonts[Font].FDArrayOffsets.length-1];
		for (int i=0;i= 0)
        	{
        		OutputList.addLast(new SubrMarkerItem(fdSubrs[i],fdPrivateBase[i]));
        		if(NewLSubrsIndex[i]!=null)
        			OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(NewLSubrsIndex[i]),0,NewLSubrsIndex[i].length));
        	}
        }
    }

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

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

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

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy