com.lowagie.text.pdf.CFFFontSubset Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of itext2 Show documentation
Show all versions of itext2 Show documentation
Itext is a java library to create and manipulate PDFs.
This is a fork of version 2.1.7 the last MPL/LGPL version.
It's focused basically on mantain compatibility with newer bouncycastle releases and small bugfixes.
/*
* $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;
@SuppressWarnings("unused")
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(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")
{
// 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>> 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);
@SuppressWarnings("unused")
int major = getCard8();
@SuppressWarnings("unused")
int minor = getCard8();
int hdrSize = getCard8();
@SuppressWarnings("unused")
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));
}
}