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

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

/*
 * $Id: CFFFontSubset.java 3573 2008-07-21 15:08:04Z blowagie $
 *
 * Copyright 2004 Oren Manor and Ygal Blum
 *
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the License.
 *
 * The Original Code is 'iText, a free JAVA-PDF library'.
 *
 * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
 * the Initial Developer are Copyright (C) 1999-2005 by Bruno Lowagie.
 * All Rights Reserved.
 * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
 * are Copyright (C) 2000-2005 by Paulo Soares. All Rights Reserved.
 *
 * Contributor(s): all the names of the contributors are added in the source code
 * where applicable.
 *
 * Alternatively, the contents of this file may be used under the terms of the
 * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
 * provisions of LGPL are applicable instead of those above.  If you wish to
 * allow use of your version of this file only under the terms of the LGPL
 * License and not to allow others to use your version of this file under
 * the MPL, indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by the LGPL.
 * If you do not delete the provisions above, a recipient may use your version
 * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the MPL as stated above or under the terms of the GNU
 * Library General Public License as published by the Free Software Foundation;
 * either version 2 of the License, or any later version.
 *
 * This library 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 Library general Public License for more
 * details.
 *
 * If you didn't download this code from the following link, you should check if
 * you aren't using an obsolete version:
 * http://www.lowagie.com/iText/
 */
package com.aowagie.text.pdf;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
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])
 */
class CFFFontSubset extends CFFFont {

	/**
	 *  The Strings in this array represent Type1/Type2 operator names
	 */
	private 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
	 */
	private 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
	*/
	private static final byte ENDCHAR_OP = 14;
	private static final byte RETURN_OP = 11;

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

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

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

	/**
	 * Number of arguments to the stem operators in a subroutine calculated recursively
	 */
	private 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(final RandomAccessFileOrArray rf,final HashMap GlyphsUsed){
		// Use CFFFont c'tor in order to parse the font file.
    	super(rf);
		this.GlyphsUsed = GlyphsUsed;
		//Put the glyphs into a list
		this.glyphsInList = new ArrayList(GlyphsUsed.keySet());


		for (int i=0;i=0)
			{
				// Process the FDSelect
	            readFDSelect(i);
	            // Build the FDArrayUsed hashmap
            	BuildFDArrayUsed(i);
			}
			if (this.fonts[i].isCID) {
				// Build the FD Array used Hash Map
				ReadFDArray(i);
			}
			// compute the charset length
			this.fonts[i].CharsetLength = CountCharset(this.fonts[i].charsetOffset,this.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
     */
    private int CountCharset(final int Offset,final 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
     */
    private int CountRange(final int NumofGlyphs,final int Type){
    	int num=0;
    	char Sid;
    	int i=1,nLeft;
    	while (i= 0) {
				this.GBias = CalcBias(this.gsubrIndexOffset,j);
			}

	        // Prepare the new CharStrings Index
			BuildNewCharString(j);
			 // Prepare the new Global and Local Subrs Indices
			BuildNewLGSubrs(j);
			// Build the new file
			final byte[] Ret = BuildNewFile(j);
			return Ret;
		}
		finally {
            try {
                this.buf.close();
            }
            catch (final 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
	 */
	private int CalcBias(final int Offset,final int Font)
	{
		seek(Offset);
		final int nSubrs = getCard16();
		// If type==1 -> bias=0
		if (this.fonts[Font].CharstringType == 1) {
			return 0;
		} 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
	 */
	private void BuildNewCharString(final int FontIndex) throws IOException
	{
		this.NewCharStringsIndex = BuildNewIndex(this.fonts[FontIndex].charstringsOffsets,this.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
	 */
	private void BuildNewLGSubrs(final 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(this.fonts[Font].isCID)
		{
			// Init the hashmap-array and the arraylist-array to hold the subrs used
			// in each private dict.
			this.hSubrsUsed = new HashMap[this.fonts[Font].fdprivateOffsets.length];
			this.lSubrsUsed = new ArrayList[this.fonts[Font].fdprivateOffsets.length];
			// A [][] which will store the byte array for each new FD Array lsubs index
			this.NewLSubrsIndex = new byte[this.fonts[Font].fdprivateOffsets.length][];
			// An array to hold the offset for each Lsubr index
			this.fonts[Font].PrivateSubrsOffset = new int[this.fonts[Font].fdprivateOffsets.length];
			// A [][] which will store the offset array for each lsubr index
			this.fonts[Font].PrivateSubrsOffsetsArray = new int[this.fonts[Font].fdprivateOffsets.length][];

			// Put the FDarrayUsed into a list
			final ArrayList FDInList = new ArrayList(this.FDArrayUsed.keySet());
			// For each FD array which is used subset the lsubr
			for (int j=0;j=0)
				{
					//Scans the Charstring data storing the used Local and Global subroutines
					// by the glyphs. Scans the Subrs recursively.
					BuildSubrUsed(Font,FD,this.fonts[Font].PrivateSubrsOffset[FD],this.fonts[Font].PrivateSubrsOffsetsArray[FD],this.hSubrsUsed[FD],this.lSubrsUsed[FD]);
					// Builds the New Local Subrs index
					this.NewLSubrsIndex[FD] = BuildNewIndex(this.fonts[Font].PrivateSubrsOffsetsArray[FD],this.hSubrsUsed[FD],RETURN_OP);
				}
			}
		}
		// If the font is not CID && the Private Subr exists then subset:
		else if (this.fonts[Font].privateSubrs>=0)
		{
			// Build the subrs offsets;
			this.fonts[Font].SubrsOffsets = getIndex(this.fonts[Font].privateSubrs);
			//Scans the Charstring data storing the used Local and Global subroutines
			// by the glyphs. Scans the Subrs recursively.
			BuildSubrUsed(Font,-1,this.fonts[Font].privateSubrs,this.fonts[Font].SubrsOffsets,this.hSubrsUsedNonCID,this.lSubrsUsedNonCID);
		}
		// For all fonts subset the Global Subroutines
		// Scan the Global Subr Hashmap recursively on the Gsubrs
		BuildGSubrsUsed(Font);
		if (this.fonts[Font].privateSubrs>=0) {
			// Builds the New Local Subrs index
			this.NewSubrsIndexNonCID = BuildNewIndex(this.fonts[Font].SubrsOffsets,this.hSubrsUsedNonCID,RETURN_OP);
		}
		//Builds the New Global Subrs index
		this.NewGSubrsIndex = BuildNewIndex(this.gsubrOffsets,this.hGSubrsUsed,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
	 */
	private void BuildFDSubrsOffsets(final int Font,final int FD)
	{
		// Initiate to -1 to indicate lsubr operator present
		this.fonts[Font].PrivateSubrsOffset[FD] = -1;
		// Goto beginning of objects
        seek(this.fonts[Font].fdprivateOffsets[FD]);
        // While in the same object:
        while (getPosition() < this.fonts[Font].fdprivateOffsets[FD]+this.fonts[Font].fdprivateLengths[FD])
        {
        	getDictItem();
        	// If the dictItem is the "Subrs" then find and store offset,
        	if (this.key=="Subrs") {
				this.fonts[Font].PrivateSubrsOffset[FD] = ((Integer)this.args[0]).intValue()+this.fonts[Font].fdprivateOffsets[FD];
			}
        }
        //Read the lsubr index if the lsubr was found
        if (this.fonts[Font].PrivateSubrsOffset[FD] >= 0) {
			this.fonts[Font].PrivateSubrsOffsetsArray[FD] = getIndex(this.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
	 */
	private void BuildSubrUsed(final int Font,final int FD,final int SubrOffset,final int[] SubrsOffsets,final HashMap hSubr,final ArrayList lSubr)
	{

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

		// For each glyph used find its GID, start & end pos
		for (int i=0;i= 0)
			{
				EmptyStack();
				this.NumOfHints=0;
				// Using FDSELECT find the FD Array the glyph belongs to.
				final int GlyphFD = this.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,this.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,this.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
				final int Start = SubrsOffsets[Subr];
				final int End = SubrsOffsets[Subr+1];
				ReadASubr(Start,End,this.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
	 */
	private void BuildGSubrsUsed(final int Font)
	{
		int LBias = 0;
		int SizeOfNonCIDSubrsUsed = 0;
		if (this.fonts[Font].privateSubrs>=0)
		{
			LBias = CalcBias(this.fonts[Font].privateSubrs,Font);
			SizeOfNonCIDSubrsUsed = this.lSubrsUsedNonCID.size();
		}

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

				if (this.fonts[Font].isCID) {
					ReadASubr(Start,End,this.GBias,0,this.hGSubrsUsed,this.lGSubrsUsed,null);
				} else
				{
					ReadASubr(Start,End,this.GBias,LBias,this.hSubrsUsedNonCID,this.lSubrsUsedNonCID,this.fonts[Font].SubrsOffsets);
					if (SizeOfNonCIDSubrsUsed < this.lSubrsUsedNonCID.size())
					{
						for (int j=SizeOfNonCIDSubrsUsed;j=0)
							{
								// Read the subr and process
								final int LStart = this.fonts[Font].SubrsOffsets[LSubr];
								final int LEnd = this.fonts[Font].SubrsOffsets[LSubr+1];
								ReadASubr(LStart,LEnd,this.GBias,LBias,this.hSubrsUsedNonCID,this.lSubrsUsedNonCID,this.fonts[Font].SubrsOffsets);
							}
						}
						SizeOfNonCIDSubrsUsed = this.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
	 */
	private void ReadASubr(final int begin,final int end,final int GBias,final int LBias,final HashMap hSubr,final ArrayList lSubr,final int[] LSubrsOffsets)
	{
		// Clear the stack for the subrs
		EmptyStack();
		this.NumOfHints = 0;
		// Goto beginning of the subr
        seek(begin);
        while (getPosition() < end)
        {
        	// Read the next command
        	ReadCommand();
        	final int pos = getPosition();
        	Object TopElement=null;
        	if (this.arg_count > 0) {
				TopElement = this.args[this.arg_count-1];
			}
        	final int NumOfArgs = this.arg_count;
        	// Check the modification needed on the Argument Stack according to key;
        	HandelStack();
        	// a call to a Lsubr
        	if (this.key=="callsubr")
        	{
        		// Verify that arguments are passed
        		if (NumOfArgs > 0)
        		{
            		// Calc the index of the Subrs
            		final 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 (this.key=="callgsubr")
        	{
        		// Verify that arguments are passed
        		if (NumOfArgs > 0)
        		{
	        		// Calc the index of the Subrs
	        		final int Subr = ((Integer)TopElement).intValue() + GBias;
	        		// If the subr isn't in the HashMap -> Put in
	        		if (!this.hGSubrsUsed.containsKey(Integer.valueOf(Subr)))
	        		{
	        			this.hGSubrsUsed.put(Integer.valueOf(Subr),null);
	        			this.lGSubrsUsed.add(Integer.valueOf(Subr));
	        		}
	        		CalcHints(this.gsubrOffsets[Subr],this.gsubrOffsets[Subr+1],LBias,GBias,LSubrsOffsets);
	        		seek(pos);
        		}
        	}
        	// A call to "stem"
        	else if (this.key == "hstem" || this.key == "vstem" || this.key == "hstemhm" || this.key == "vstemhm") {
				// Increment the NumOfHints by the number couples of of arguments
        		this.NumOfHints += NumOfArgs/2;
			} else if (this.key == "hintmask" || this.key == "cntrmask")
        	{
        		// Compute the size of the mask
        		int SizeOfMask = this.NumOfHints/8;
        		if (this.NumOfHints%8 != 0 || SizeOfMask == 0) {
					SizeOfMask++;
				}
        		// Continue the pointer in SizeOfMask steps
        		for (int i=0;i flush the stack
	 */
	private int StackOpp()
	{
		if (this.key == "ifelse") {
			return -3;
		}
		if (this.key == "roll" || this.key == "put") {
			return -2;
		}
		if (this.key == "callsubr" || this.key == "callgsubr" || this.key == "add" || this.key == "sub" ||
			this.key == "div" || this.key == "mul" || this.key == "drop" || this.key == "and" ||
			this.key == "or" || this.key == "eq") {
			return -1;
		}
		if (this.key == "abs" || this.key == "neg" || this.key == "sqrt" || this.key == "exch" ||
			this.key == "index" || this.key == "get" || this.key == "not" || this.key == "return") {
			return 0;
		}
		if (this.key == "random" || this.key == "dup") {
			return 1;
		}
		return 2;
	}

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

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

	/**
	 * The function reads the next command after the file pointer is set
	 */
	private void ReadCommand()
	{
        this.key = null;
        boolean gotKey = false;
        // Until a key is found
        while (!gotKey) {
        	// Read the first Char
            final char b0 = getCard8();
            // decode according to the type1/type2 format
            if (b0 == 28) // the two next bytes represent a short int;
            {
            	final int first = getCard8();
            	final int second = getCard8();
            	this.args[this.arg_count] = Integer.valueOf(first<<8 | second);
            	this.arg_count++;
            	continue;
            }
            if (b0 >= 32 && b0 <= 246) // The byte read is the byte;
            {
            	this.args[this.arg_count] = Integer.valueOf(b0 - 139);
            	this.arg_count++;
            	continue;
            }
            if (b0 >= 247 && b0 <= 250) // The byte read and the next byte constitute a short int
            {
            	final int w = getCard8();
            	this.args[this.arg_count] = Integer.valueOf((b0-247)*256 + w + 108);
            	this.arg_count++;
            	continue;
            }
            if (b0 >= 251 && b0 <= 254)// Same as above except negative
            {
            	final int w = getCard8();
            	this.args[this.arg_count] = Integer.valueOf(-(b0-251)*256 - w - 108);
            	this.arg_count++;
            	continue;
            }
            if (b0 == 255)// The next for bytes represent a double.
            {
            	final int first = getCard8();
            	final int second = getCard8();
            	final int third = getCard8();
            	final int fourth = getCard8();
            	this.args[this.arg_count] = Integer.valueOf(first<<24 | second<<16 | third<<8 | fourth);
            	this.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;
					}
            		this.key = SubrsEscapeFuncs[b1];
            	} else {
					this.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.
	 */
	private int CalcHints(final int begin,final int end,final int LBias,final int GBias,final int[] LSubrsOffsets)
	{
		// Goto beginning of the subr
        seek(begin);
        while (getPosition() < end)
        {
        	// Read the next command
        	ReadCommand();
        	final int pos = getPosition();
        	Object TopElement = null;
        	if (this.arg_count>0) {
				TopElement = this.args[this.arg_count-1];
			}
        	final int NumOfArgs = this.arg_count;
            //Check the modification needed on the Argument Stack according to key;
        	HandelStack();
        	// a call to a Lsubr
        	if (this.key=="callsubr")
        	{
        		if (NumOfArgs>0)
        		{
            		final int Subr = ((Integer)TopElement).intValue() + LBias;
            		CalcHints(LSubrsOffsets[Subr],LSubrsOffsets[Subr+1],LBias,GBias,LSubrsOffsets);
            		seek(pos);
        		}
        	}
        	// a call to a Gsubr
        	else if (this.key=="callgsubr")
        	{
        		if (NumOfArgs>0)
        		{
            		final int Subr = ((Integer)TopElement).intValue() + GBias;
            		CalcHints(this.gsubrOffsets[Subr],this.gsubrOffsets[Subr+1],LBias,GBias,LSubrsOffsets);
            		seek(pos);
        		}
        	}
        	// A call to "stem"
        	else if (this.key == "hstem" || this.key == "vstem" || this.key == "hstemhm" || this.key == "vstemhm") {
				// Increment the NumOfHints by the number couples of of arguments
        		this.NumOfHints += NumOfArgs/2;
			} else if (this.key == "hintmask" || this.key == "cntrmask")
        	{
        		// Compute the size of the mask
        		int SizeOfMask = this.NumOfHints/8;
        		if (this.NumOfHints%8 != 0 || SizeOfMask == 0) {
					SizeOfMask++;
				}
        		// Continue the pointer in SizeOfMask steps
        		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 (final int newOffset : NewOffsets) {
        	// The value to be written
        	final 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 (final 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
	 */
	private byte[] BuildNewFile(final int Font)
    {
		// Prepare linked list for new font components
		this.OutputList = new LinkedList();

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

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

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

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

        // If the font is not CID create the following keys
        if ( !this.fonts[Font].isCID ) {
            // create a ROS key
        	this.OutputList.addLast(new DictNumberItem(this.fonts[Font].nstrings));
        	this.OutputList.addLast(new DictNumberItem(this.fonts[Font].nstrings+1));
        	this.OutputList.addLast(new DictNumberItem(0));
        	this.OutputList.addLast(new UInt8Item((char)12));
        	this.OutputList.addLast(new UInt8Item((char)30));
            // create a CIDCount key
        	this.OutputList.addLast(new DictNumberItem(this.fonts[Font].nglyphs));
        	this.OutputList.addLast(new UInt8Item((char)12));
            this.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(this.topdictOffsets[Font]);
        // Run until the end of the TopDict
        while (getPosition() < this.topdictOffsets[Font+1]) {
            final int p1 = getPosition();
            getDictItem();
            final int p2 = getPosition();
            // The encoding key is disregarded since CID has no encoding
            if (this.key=="Encoding"
            // These keys will be added manually by the process.
            || this.key=="Private"
            || this.key=="FDSelect"
            || this.key=="FDArray"
            || this.key=="charset"
            || this.key=="CharStrings"
            ) {
            }else {
            //OtherWise copy key "as is" to the output list
                this.OutputList.add(new RangeItem(this.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
        this.OutputList.addLast(new IndexMarkerItem(topdictIndex1Ref,topdictBase));

        // Copy the string index

        if (this.fonts[Font].isCID) {
			this.OutputList.addLast(getEntireIndexRange(this.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
        this.OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(this.NewGSubrsIndex),0,this.NewGSubrsIndex.length));

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

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

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

            // Copy the FDArray
            // If an FDArray exists
            if (this.fonts[Font].fdarrayOffset>=0)
            {
                // Mark the beginning
	            this.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,this.fonts[Font].nglyphs);
            // recreate a new charset
        	CreateCharset(charsetRef,this.fonts[Font].nglyphs);
            // create a font dict index (fdarray)
        	CreateFDArray(fdarrayRef,privateRef,Font);
        }

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

        	final 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
        this.OutputList.addLast(new MarkerItem(charstringsRef));

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

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

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

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

	/**
	 * Function Copies the header from the original fileto the output list
	 */
	private void CopyHeader()
	{
		seek(0);
        final int major = getCard8();
        final int minor = getCard8();
        final int hdrSize = getCard8();
        final int offSize = getCard8();
        this.OutputList.addLast(new RangeItem(this.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
	 */
	private void BuildIndexHeader(final int Count,final int Offsize,final int First)
	{
		// Add the count field
		this.OutputList.addLast(new UInt16Item((char)Count)); // count
		// Add the offsize field
		this.OutputList.addLast(new UInt8Item((char)Offsize)); // offSize
		// Add the first offset according to the offsize
        switch(Offsize){
        	case 1:
        		this.OutputList.addLast(new UInt8Item((char)First)); // first offset
        		break;
        	case 2:
        		this.OutputList.addLast(new UInt16Item((char)First)); // first offset
        		break;
        	case 3:
        		this.OutputList.addLast(new UInt24Item((char)First)); // first offset
        		break;
        	case 4:
        		this.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
	 */
	private void CreateKeys(final OffsetItem fdarrayRef,final OffsetItem fdselectRef,final OffsetItem charsetRef,final OffsetItem charstringsRef)
	{
	    // create an FDArray key
        this.OutputList.addLast(fdarrayRef);
        this.OutputList.addLast(new UInt8Item((char)12));
        this.OutputList.addLast(new UInt8Item((char)36));
        // create an FDSelect key
        this.OutputList.addLast(fdselectRef);
        this.OutputList.addLast(new UInt8Item((char)12));
        this.OutputList.addLast(new UInt8Item((char)37));
        // create an charset key
        this.OutputList.addLast(charsetRef);
        this.OutputList.addLast(new UInt8Item((char)15));
        // create a CharStrings key
        this.OutputList.addLast(charstringsRef);
        this.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
	 */
	private void CreateNewStringIndex(final int Font)
	{
        String fdFontName = this.fonts[Font].name+"-OneRange";
        if (fdFontName.length() > 127) {
			fdFontName = fdFontName.substring(0,127);
		}
        final String extraStrings = "Adobe"+"Identity"+fdFontName;

        final int origStringsLen = this.stringOffsets[this.stringOffsets.length-1]
        - this.stringOffsets[0];
        final int stringsBaseOffset = this.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;
		}

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

        this.OutputList.addLast(new RangeItem(this.buf,this.stringOffsets[0],origStringsLen));
        this.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
	 */
	private void CreateFDSelect(final OffsetItem fdselectRef,final int nglyphs)
	{
		this.OutputList.addLast(new MarkerItem(fdselectRef));
	    this.OutputList.addLast(new UInt8Item((char)3)); // format identifier
	    this.OutputList.addLast(new UInt16Item((char)1)); // nRanges

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

	    this.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
	 */
	private void CreateCharset(final OffsetItem charsetRef,final int nglyphs)
	{
		this.OutputList.addLast(new MarkerItem(charsetRef));
	    this.OutputList.addLast(new UInt8Item((char)2)); // format identifier
	    this.OutputList.addLast(new UInt16Item((char)1)); // first glyph in range (ignore .notdef)
	    this.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
	 */
	private void CreateFDArray(final OffsetItem fdarrayRef,final OffsetItem privateRef,final int Font)
	{
		this.OutputList.addLast(new MarkerItem(fdarrayRef));
		// Build the header (count=offsize=first=1)
		BuildIndexHeader(1,1,1);

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

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

	/**
	 * Function reconstructs the FDArray, PrivateDict and LSubr for CID fonts
	 * @param Font the font
	 */
	private void Reconstruct(final int Font)
	{
		// Init for later use
		final OffsetItem[] fdPrivate = new DictOffsetItem[this.fonts[Font].FDArrayOffsets.length-1];
		final IndexBaseItem[] fdPrivateBase = new IndexBaseItem[this.fonts[Font].fdprivateOffsets.length];
		final OffsetItem[] fdSubrs = new DictOffsetItem[this.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)
	 */
	private void ReconstructFDArray(final int Font,final OffsetItem[] fdPrivate)
	{
		// Build the header of the index
		BuildIndexHeader(this.fonts[Font].FDArrayCount,this.fonts[Font].FDArrayOffsize,1);

		// For each offset create an Offset Item
		final OffsetItem[] fdOffsets = new IndexOffsetItem[this.fonts[Font].FDArrayOffsets.length-1];
		for (int i=0;i= 0)
        	{
        		this.OutputList.addLast(new SubrMarkerItem(fdSubrs[i],fdPrivateBase[i]));
        		this.OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(this.NewLSubrsIndex[i]),0,this.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
	 */
	private int CalcSubrOffsetSize(final int Offset,final 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)
        {
        	final int p1 = getPosition();
        	getDictItem();
        	final int p2 = getPosition();
        	// When reached to the subrs offset
        	if (this.key=="Subrs") {
        		// The Offsize (minus the subrs key)
        		OffsetSize = p2-p1-1;
        	}
        	// All other keys are ignored
        }
		// return the size
		return OffsetSize;
	}



	/**
	 * 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
	 */
	private void CreateNonCIDPrivate(final int Font,final OffsetItem Subr)
	{
		// Go to the beginning of the private dict and read until the end
		seek(this.fonts[Font].privateOffset);
        while (getPosition() < this.fonts[Font].privateOffset+this.fonts[Font].privateLength)
        {
        	final int p1 = getPosition();
        	getDictItem();
        	final int p2 = getPosition();
        	// If the dictItem is the "Subrs" then,
        	// use marker for offset and write operator number
        	if (this.key=="Subrs") {
        		this.OutputList.addLast(Subr);
        	    this.OutputList.addLast(new UInt8Item((char)19)); // Subrs
        	}
        	// Else copy the entire range
 else {
				this.OutputList.addLast(new RangeItem(this.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
	 */
	private void CreateNonCIDSubrs(final int Font,final IndexBaseItem PrivateBase,final OffsetItem Subrs)
	{
		// Mark the beginning of the Subrs index
		this.OutputList.addLast(new SubrMarkerItem(Subrs,PrivateBase));
		// Put the subsetted new subrs index
		this.OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(this.NewSubrsIndexNonCID),0,this.NewSubrsIndexNonCID.length));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy