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

core.com.lowagie.text.pdf.CFFFontSubset Maven / Gradle / Ivy

The newest version!
/*
 * $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.lowagie.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])
 */
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 HashMap for keeping the FDArrays being used by the font
	 */
	HashMap FDArrayUsed = new HashMap();
	/**
	 * 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
	 */
	protected void BuildNewLGSubrs(int Font)throws IOException
	{
		// If the font is CID then the lsubrs are divided into FontDicts.
		// for each FD array the lsubrs will be subsetted.
		if(fonts[Font].isCID)
		{
			// Init the hashmap-array and the arraylist-array to hold the subrs used
			// in each private dict.
			hSubrsUsed = new HashMap[fonts[Font].fdprivateOffsets.length];
			lSubrsUsed = new ArrayList[fonts[Font].fdprivateOffsets.length];
			// A [][] which will store the byte array for each new FD Array lsubs index
			NewLSubrsIndex = new byte[fonts[Font].fdprivateOffsets.length][];
			// An array to hold the offset for each Lsubr index 
			fonts[Font].PrivateSubrsOffset = new int[fonts[Font].fdprivateOffsets.length];
			// A [][] which will store the offset array for each lsubr index			
			fonts[Font].PrivateSubrsOffsetsArray = new int[fonts[Font].fdprivateOffsets.length][];
			
			// Put the FDarrayUsed into a list
			ArrayList FDInList = new ArrayList(FDArrayUsed.keySet());
			// For each FD array which is used subset the lsubr 
			for (int j=0;j=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 = BuildNewIndex(gsubrOffsets,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
	 */
	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(new Integer (Subr)))
            		{
            			hSubr.put(new Integer(Subr),null);
            			lSubr.add(new Integer(Subr));
            		}
        			CalcHints(LSubrsOffsets[Subr],LSubrsOffsets[Subr+1],LBias,GBias,LSubrsOffsets);
        			seek(pos);
        		}        		
        	}
        	// a call to a Gsubr
        	else if (key=="callgsubr")
        	{
        		// Verify that arguments are passed 
        		if (NumOfArgs > 0)
        		{
	        		// Calc the index of the Subrs
	        		int Subr = ((Integer)TopElement).intValue() + GBias;
	        		// If the subr isn't in the HashMap -> Put in
	        		if (!hGSubrsUsed.containsKey(new Integer (Subr)))
	        		{
	        			hGSubrsUsed.put(new Integer(Subr),null);
	        			lGSubrsUsed.add(new Integer(Subr));
	        		}
	        		CalcHints(gsubrOffsets[Subr],gsubrOffsets[Subr+1],LBias,GBias,LSubrsOffsets);
	        		seek(pos);
        		}
        	}
        	// A call to "stem"
        	else if (key == "hstem" || key == "vstem" || key == "hstemhm" || key == "vstemhm")
        		// Increment the NumOfHints by the number couples of of arguments
        		NumOfHints += NumOfArgs/2;
        	// A call to "mask"
        	else if (key == "hintmask" || key == "cntrmask")
        	{
        		// Compute the size of the mask
        		int SizeOfMask = NumOfHints/8;
        		if (NumOfHints%8 != 0 || SizeOfMask == 0)
        			SizeOfMask++;
        		// Continue the pointer in SizeOfMask steps
        		for (int i=0;i 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] = new Integer(first<<8 | second);
            	arg_count++;
            	continue;
            }
            if (b0 >= 32 && b0 <= 246) // The byte read is the byte;
            {
            	args[arg_count] = new Integer(b0 - 139);
            	arg_count++;
            	continue;
            }
            if (b0 >= 247 && b0 <= 250) // The byte read and the next byte constitute a short int
            {
            	int w = getCard8();
            	args[arg_count] = new Integer((b0-247)*256 + w + 108);
            	arg_count++;
            	continue;
            }
            if (b0 >= 251 && b0 <= 254)// Same as above except negative
            {
            	int w = getCard8();
            	args[arg_count] = new Integer(-(b0-251)*256 - w - 108);
            	arg_count++;
            	continue;
            }
            if (b0 == 255)// The next for bytes represent a double.
            {
            	int first = getCard8();
            	int second = getCard8();
            	int third = getCard8();
            	int fourth = getCard8();
            	args[arg_count] = new Integer(first<<24 | second<<16 | third<<8 | fourth);
            	arg_count++;
            	continue;
            }
            if (b0<=31 && b0 != 28) // An operator was found.. Set Key.
            {
            	gotKey=true;
            	// 12 is an escape command therefore the next byte is a part
            	// of this command
            	if (b0 == 12)
            	{
            		int b1 = getCard8();
            		if (b1>SubrsEscapeFuncs.length-1)
            			b1 = SubrsEscapeFuncs.length-1;
            		key = SubrsEscapeFuncs[b1];
            	}
            	else
            		key = SubrsFunctions[b0];
                continue;
            }
        }		
	}
	
	/**
	 * The function reads the 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>> 8) & 0xff);
        NewIndex[Place++] = (byte) ((Count >>> 0) & 0xff);
        // Write the offsize field
        NewIndex[Place++] = Offsize;
        // Write the offset array according to the offsize
        for (int i=0;i>> 24) & 0xff);
                case 3:
                	NewIndex[Place++] = (byte) ((Num >>> 16) & 0xff);
                case 2:
                	NewIndex[Place++] = (byte) ((Num >>>  8) & 0xff);
                case 1:
                	NewIndex[Place++] = (byte) ((Num >>>  0) & 0xff);
        	}                	
        }
        // Write the new object array one by one
        for (int i=0;i=0)
        		OutputList.addLast(new RangeItem(buf,fonts[Font].fdselectOffset,fonts[Font].FDSelectLength));
            // Else create a new one
            else
            	CreateFDSelect(fdselectRef,fonts[Font].nglyphs);
            	           
          	// Copy the Charset
            // Mark the beginning and copy entirely 
            OutputList.addLast(new MarkerItem(charsetRef));
            OutputList.addLast(new RangeItem(buf,fonts[Font].charsetOffset,fonts[Font].CharsetLength));
            
            // Copy the FDArray
            // If an FDArray exists
            if (fonts[Font].fdarrayOffset>=0)
            {
                // Mark the beginning
	            OutputList.addLast(new MarkerItem(fdarrayRef));
	            // Build a new FDArray with its private dicts and their LSubrs
	            Reconstruct(Font);
            }
            else
            	// Else create a new one
            	CreateFDArray(fdarrayRef,privateRef,Font);
           
        }
        // If the font is not CID
        else 
        {
            // create FDSelect
        	CreateFDSelect(fdselectRef,fonts[Font].nglyphs);            
            // recreate a new charset
        	CreateCharset(charsetRef,fonts[Font].nglyphs);            
            // create a font dict index (fdarray)
        	CreateFDArray(fdarrayRef,privateRef,Font);            
        }
        
        // if a private dict exists insert its subsetted version
        if (fonts[Font].privateOffset>=0)
        {
        	// Mark the beginning of the private dict
        	IndexBaseItem PrivateBase = new IndexBaseItem();
	        OutputList.addLast(PrivateBase);
        	OutputList.addLast(new MarkerItem(privateRef));

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

        // Add the subsetted charstring
        OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(NewCharStringsIndex),0,NewCharStringsIndex.length));
        
        // now create the new CFF font        
        int[] currentOffset = new int[1];
        currentOffset[0] = 0;
        // Count and save the offset for each item
        Iterator listIter = OutputList.iterator();
        while ( listIter.hasNext() ) {
            Item item = (Item) listIter.next();
            item.increment(currentOffset);
        }
        // Compute the Xref for each of the offset items
        listIter = OutputList.iterator();
        while ( listIter.hasNext() ) {
            Item item = (Item) listIter.next();
            item.xref();
        }
        
        int size = currentOffset[0];
        byte[] b = new byte[size];
        
        // Emit all the items into the new byte array
        listIter = OutputList.iterator();
        while ( listIter.hasNext() ) {
            Item item = (Item) listIter.next();
            item.emit(b);
        }
        // Return the new stream
        return b;
    }

	/**
	 * 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 i=0; i= 0)
        	{        		
        		OutputList.addLast(new SubrMarkerItem(fdSubrs[i],fdPrivateBase[i]));
        		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
		OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(NewSubrsIndexNonCID),0,NewSubrsIndexNonCID.length));
    }	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy