![JAR search and dependency download from the Maven repository](/logo.png)
com.itextpdf.io.font.CFFFontSubset Maven / Gradle / Ivy
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2023 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
package com.itextpdf.io.font;
import com.itextpdf.io.exceptions.IOException;
import com.itextpdf.io.exceptions.IoExceptionMessageConstant;
import com.itextpdf.io.source.RandomAccessFileOrArray;
import com.itextpdf.io.util.GenericArray;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* This Class subsets a CFF Type Font. The subset is preformed for CID fonts and NON CID fonts.
* The Charstring is subsetted for both types. For CID fonts only the FDArray which are used are embedded.
* The Lsubroutines of the FDArrays used are subsetted as well. The Subroutine subset supports both Type1 and Type2
* formatting although only tested on Type2 Format.
* For Non CID the Lsubroutines are subsetted. On both types the Gsubroutines is subsetted.
* A font which was not of CID type is transformed into CID as a part of the subset process.
* The CID synthetic creation was written by Sivan Toledo ([email protected])
*
* @author Oren Manor ([email protected]) and Ygal Blum ([email protected])
*/
public class CFFFontSubset extends CFFFont {
/**
* The Strings in this array represent Type1/Type2 operator names
*/
static final String[] SubrsFunctions = {
"RESERVED_0", "hstem", "RESERVED_2", "vstem", "vmoveto", "rlineto", "hlineto", "vlineto",
"rrcurveto", "RESERVED_9", "callsubr", "return", "escape", "RESERVED_13",
"endchar", "RESERVED_15", "RESERVED_16", "RESERVED_17", "hstemhm", "hintmask",
"cntrmask", "rmoveto", "hmoveto", "vstemhm", "rcurveline", "rlinecurve", "vvcurveto",
"hhcurveto", "shortint", "callgsubr", "vhcurveto", "hvcurveto"
};
/**
* The Strings in this array represent Type1/Type2 escape operator names
*/
static final String[] SubrsEscapeFuncs = {
"RESERVED_0", "RESERVED_1", "RESERVED_2", "and", "or", "not", "RESERVED_6",
"RESERVED_7", "RESERVED_8", "abs", "add", "sub", "div", "RESERVED_13", "neg",
"eq", "RESERVED_16", "RESERVED_17", "drop", "RESERVED_19", "put", "get", "ifelse",
"random", "mul", "RESERVED_25", "sqrt", "dup", "exch", "index", "roll", "RESERVED_31",
"RESERVED_32", "RESERVED_33", "hflex", "flex", "hflex1", "flex1", "RESERVED_REST"
};
/**
* Operator codes for unused CharStrings and unused local and global Subrs
*/
static final byte ENDCHAR_OP = 14;
static final byte RETURN_OP = 11;
/**
* A Map containing the glyphs used in the text after being converted
* to glyph number by the CMap
*/
Set GlyphsUsed;
/**
* The GlyphsUsed keys as an list
*/
List glyphsInList;
/**
* A Set for keeping the FDArrays being used by the font
*/
Set FDArrayUsed = new HashSet<>();
/**
* A Maps array for keeping the subroutines used in each FontDict
*/
GenericArray> hSubrsUsed;
/**
* The SubroutinesUsed Maps as lists
*/
GenericArray> lSubrsUsed;
/**
* A Map for keeping the Global subroutines used in the font
*/
Set hGSubrsUsed = new HashSet<>();
/**
* The Global SubroutinesUsed Maps as lists
*/
List lGSubrsUsed = new ArrayList<>();
/**
* A Map for keeping the subroutines used in a non-cid font
*/
Set hSubrsUsedNonCID = new HashSet<>();
/**
* The SubroutinesUsed Map as list
*/
List lSubrsUsedNonCID = new ArrayList<>();
/**
* An array of the new Indexes for the local Subr. One index for each FontDict
*/
byte[][] NewLSubrsIndex;
/**
* The new subroutines index for a non-cid font
*/
byte[] NewSubrsIndexNonCID;
/**
* The new global subroutines index of the font
*/
byte[] NewGSubrsIndex;
/**
* The new CharString of the font
*/
byte[] NewCharStringsIndex;
/**
* The bias for the global subroutines
*/
int GBias = 0;
/**
* The linked list for generating the new font stream
*/
LinkedList- OutputList;
/**
* Number of arguments to the stem operators in a subroutine calculated recursively
*/
int NumOfHints = 0;
/**
* C'tor for CFFFontSubset
*
* @param cff - The font file
*/
CFFFontSubset(byte[] cff) {
this(cff, Collections.
emptySet(), true);
}
public CFFFontSubset(byte[] cff, Set GlyphsUsed) {
this(cff, GlyphsUsed, false);
}
CFFFontSubset(byte[] cff, Set GlyphsUsed, boolean isCidParsingRequired) {
// Use CFFFont c'tor in order to parse the font file.
super(cff);
this.GlyphsUsed = GlyphsUsed;
//Put the glyphs into a list
glyphsInList = new ArrayList<>(GlyphsUsed);
for (int i = 0; i < fonts.length; ++i) {
// Read the number of glyphs in the font
seek(fonts[i].charstringsOffset);
fonts[i].nglyphs = getCard16();
// Jump to the count field of the String Index
seek(stringIndexOffset);
fonts[i].nstrings = getCard16() + standardStrings.length;
// For each font save the offset array of the charstring
fonts[i].charstringsOffsets = getIndex(fonts[i].charstringsOffset);
if (isCidParsingRequired) {
initGlyphIdToCharacterIdArray(i, fonts[i].nglyphs, fonts[i].charsetOffset);
}
// Process the FDSelect if exist
if (fonts[i].fdselectOffset >= 0) {
// Process the FDSelect
readFDSelect(i);
// Build the FDArrayUsed Map
BuildFDArrayUsed(i);
}
if (fonts[i].isCID)
// Build the FD Array used Map
ReadFDArray(i);
// compute the charset length
fonts[i].CharsetLength = CountCharset(fonts[i].charsetOffset, fonts[i].nglyphs);
}
}
/**
* Calculates the length of the charset according to its format
*
* @param Offset The Charset Offset
* @param NumofGlyphs Number of glyphs in the font
* @return the length of the Charset
*/
int CountCharset(int Offset, int NumofGlyphs) {
int format;
int Length = 0;
seek(Offset);
// Read the format
format = getCard8();
// Calc according to format
switch (format) {
case 0:
Length = 1 + 2 * NumofGlyphs;
break;
case 1:
Length = 1 + 3 * CountRange(NumofGlyphs, 1);
break;
case 2:
Length = 1 + 4 * CountRange(NumofGlyphs, 2);
break;
default:
break;
}
return Length;
}
/**
* Function calculates the number of ranges in the Charset
*
* @param NumofGlyphs The number of glyphs in the font
* @param Type The format of the Charset
* @return The number of ranges in the Charset data structure
*/
int CountRange(int NumofGlyphs, int Type) {
int num = 0;
char Sid;
int i = 1, nLeft;
while (i < NumofGlyphs) {
num++;
Sid = getCard16();
if (Type == 1)
nLeft = getCard8();
else
nLeft = getCard16();
i += nLeft + 1;
}
return num;
}
/**
* Read the FDSelect of the font and compute the array and its length
*
* @param Font The index of the font being processed
*/
protected void readFDSelect(int Font) {
// Restore the number of glyphs
int NumOfGlyphs = fonts[Font].nglyphs;
int[] FDSelect = new int[NumOfGlyphs];
// Go to the beginning of the FDSelect
seek(fonts[Font].fdselectOffset);
// Read the FDSelect's format
fonts[Font].FDSelectFormat = getCard8();
switch (fonts[Font].FDSelectFormat) {
// Format==0 means each glyph has an entry that indicated
// its FD.
case 0:
for (int i = 0; i < NumOfGlyphs; i++) {
FDSelect[i] = getCard8();
}
// The FDSelect's Length is one for each glyph + the format
// for later use
fonts[Font].FDSelectLength = fonts[Font].nglyphs + 1;
break;
case 3:
// Format==3 means the ranges version
// The number of ranges
int nRanges = getCard16();
int l = 0;
// Read the first in the first range
int first = getCard16();
for (int i = 0; i < nRanges; i++) {
// Read the FD index
int fd = getCard8();
// Read the first of the next range
int last = getCard16();
// Calc the steps and write to the array
int steps = last - first;
for (int k = 0; k < steps; k++) {
FDSelect[l] = fd;
l++;
}
// The last from this iteration is the first of the next
first = last;
}
// Store the length for later use
fonts[Font].FDSelectLength = 1 + 2 + nRanges * 3 + 2;
break;
default:
break;
}
// Save the FDSelect of the font
fonts[Font].FDSelect = FDSelect;
}
/**
* Function reads the FDSelect and builds the FDArrayUsed Map According to the glyphs used
*
* @param Font the Number of font being processed
*/
protected void BuildFDArrayUsed(int Font) {
int[] FDSelect = fonts[Font].FDSelect;
// For each glyph used
for (Integer glyphsInList1 : glyphsInList) {
// Pop the glyphs index
int glyph = (int) glyphsInList1;
// Pop the glyph's FD
int FD = FDSelect[glyph];
// Put the FD index into the FDArrayUsed Map
FDArrayUsed.add(FD);
}
}
/**
* Read the FDArray count, offsize and Offset array
*
* @param Font the Number of font being processed
*/
protected void ReadFDArray(int Font) {
seek(fonts[Font].fdarrayOffset);
fonts[Font].FDArrayCount = getCard16();
fonts[Font].FDArrayOffsize = getCard8();
// Since we will change values inside the FDArray objects
// We increase its offsize to prevent errors
if (fonts[Font].FDArrayOffsize < 4)
fonts[Font].FDArrayOffsize++;
fonts[Font].FDArrayOffsets = getIndex(fonts[Font].fdarrayOffset);
}
/**
* The Process function extracts one font out of the CFF file and returns a
* subset version of the original.
*
* @param fontName - The name of the font to be taken out of the CFF
* @return The new font stream
*/
public byte[] Process(String fontName) {
try {
// Find the Font that we will be dealing with
int j;
for (j = 0; j < fonts.length; j++)
if (fontName.equals(fonts[j].name)) break;
if (j == fonts.length) return null;
// Calc the bias for the global subrs
if (gsubrIndexOffset >= 0)
GBias = CalcBias(gsubrIndexOffset, j);
// Prepare the new CharStrings Index
BuildNewCharString(j);
// Prepare the new Global and Local Subrs Indices
BuildNewLGSubrs(j);
// Build the new file
return BuildNewFile(j);
} catch (java.io.IOException e) {
throw new IOException(IoExceptionMessageConstant.IO_EXCEPTION, e);
} finally {
try {
buf.close();
} catch (Exception e) {
// empty on purpose
}
}
}
/**
* The Process function extracts one font out of the CFF file and returns a
* subset version of the original with the first name.
*
* @return The new font stream
*/
public byte[] Process() {
return Process(getNames()[0]);
}
/**
* Function calcs bias according to the CharString type and the count
* of the subrs
*
* @param Offset The offset to the relevant subrs index
* @param Font the font
* @return The calculated Bias
*/
protected int CalcBias(int Offset, int Font) {
seek(Offset);
int nSubrs = getCard16();
// If type==1 -> bias=0
if (fonts[Font].CharstringType == 1)
return 0;
// else calc according to the count
else if (nSubrs < 1240)
return 107;
else if (nSubrs < 33900)
return 1131;
else
return 32768;
}
/**
* Function uses BuildNewIndex to create the new index of the subset charstrings.
*
* @param FontIndex the font
* @throws java.io.IOException if an I/O error occurs
*/
protected void BuildNewCharString(int FontIndex) throws java.io.IOException {
NewCharStringsIndex = BuildNewIndex(fonts[FontIndex].charstringsOffsets, GlyphsUsed, ENDCHAR_OP);
}
/**
* Function builds the new local and global subsrs indices. IF CID then All of
* the FD Array lsubrs will be subsetted.
*
* @param Font the font
* @throws java.io.IOException if an I/O error occurs
*/
@SuppressWarnings("unchecked")
protected void BuildNewLGSubrs(int Font) throws java.io.IOException {
// If the font is CID then the lsubrs are divided into FontDicts.
// for each FD array the lsubrs will be subsetted.
if (fonts[Font].isCID) {
// Init the Map-array and the list-array to hold the subrs used
// in each private dict.
hSubrsUsed = new GenericArray<>(fonts[Font].fdprivateOffsets.length);
lSubrsUsed = new GenericArray<>(fonts[Font].fdprivateOffsets.length);
// A [][] which will store the byte array for each new FD Array lsubs index
NewLSubrsIndex = new byte[fonts[Font].fdprivateOffsets.length][];
// An array to hold the offset for each Lsubr index
fonts[Font].PrivateSubrsOffset = new int[fonts[Font].fdprivateOffsets.length];
// A [][] which will store the offset array for each lsubr index
fonts[Font].PrivateSubrsOffsetsArray = new int[fonts[Font].fdprivateOffsets.length][];
// Put the FDarrayUsed into a list
List FDInList = new ArrayList<>(FDArrayUsed);
// For each FD array which is used subset the lsubr
for (int j = 0; j < FDInList.size(); j++) {
// The FDArray index, Map, List to work on
int FD = (int) FDInList.get(j);
hSubrsUsed.set(FD, new HashSet());
lSubrsUsed.set(FD, new ArrayList());
//Reads the private dicts looking for the subr operator and
// store both the offset for the index and its offset array
BuildFDSubrsOffsets(Font, FD);
// Verify that FDPrivate has a LSubrs index
if (fonts[Font].PrivateSubrsOffset[FD] >= 0) {
//Scans the Charstring data storing the used Local and Global subroutines
// by the glyphs. Scans the Subrs recursively.
BuildSubrUsed(Font, FD, fonts[Font].PrivateSubrsOffset[FD], fonts[Font].PrivateSubrsOffsetsArray[FD], hSubrsUsed.get(FD), lSubrsUsed.get(FD));
// Builds the New Local Subrs index
NewLSubrsIndex[FD] = BuildNewIndex(fonts[Font].PrivateSubrsOffsetsArray[FD], hSubrsUsed.get(FD), RETURN_OP);
}
}
}
// If the font is not CID && the Private Subr exists then subset:
else if (fonts[Font].privateSubrs >= 0) {
// Build the subrs offsets;
fonts[Font].SubrsOffsets = getIndex(fonts[Font].privateSubrs);
//Scans the Charstring data storing the used Local and Global subroutines
// by the glyphs. Scans the Subrs recursively.
BuildSubrUsed(Font, -1, fonts[Font].privateSubrs, fonts[Font].SubrsOffsets, hSubrsUsedNonCID, lSubrsUsedNonCID);
}
// For all fonts subset the Global Subroutines
// Scan the Global Subr Map recursively on the Gsubrs
BuildGSubrsUsed(Font);
if (fonts[Font].privateSubrs >= 0)
// Builds the New Local Subrs index
NewSubrsIndexNonCID = BuildNewIndex(fonts[Font].SubrsOffsets, hSubrsUsedNonCID, RETURN_OP);
//Builds the New Global Subrs index
// NOTE We copy all global subroutines to index here.
// In some fonts (see NotoSansCJKjp-Bold.otf, Version 1.004;PS 1.004;hotconv 1.0.82;makeotf.lib2.5.63406)
// global subroutines are not derived from local ones. Previously in such cases iText didn't build global subroutines
// and, if one had set subset as true, produced pdf-document with incorrect cff table.
// However the code isn't optimised. One can parse all used glyphs and copy not all global subroutines, but only needed.
NewGSubrsIndex = BuildNewIndexAndCopyAllGSubrs(gsubrOffsets, RETURN_OP);
}
/**
* The function finds for the FD array processed the local subr offset and its
* offset array.
*
* @param Font the font
* @param FD The FDARRAY processed
*/
protected void BuildFDSubrsOffsets(int Font, int FD) {
// Initiate to -1 to indicate lsubr operator present
fonts[Font].PrivateSubrsOffset[FD] = -1;
// Goto beginning of objects
seek(fonts[Font].fdprivateOffsets[FD]);
// While in the same object:
while (getPosition() < fonts[Font].fdprivateOffsets[FD] + fonts[Font].fdprivateLengths[FD]) {
getDictItem();
// If the dictItem is the "Subrs" then find and store offset,
if ("Subrs".equals(key))
fonts[Font].PrivateSubrsOffset[FD] = (int) ((Integer) args[0]) + fonts[Font].fdprivateOffsets[FD];
}
//Read the lsubr index if the lsubr was found
if (fonts[Font].PrivateSubrsOffset[FD] >= 0)
fonts[Font].PrivateSubrsOffsetsArray[FD] = getIndex(fonts[Font].PrivateSubrsOffset[FD]);
}
/**
* Function uses ReadAsubr on the glyph used to build the LSubr and Gsubr Map.
* The Map (of the lsubr only) is then scanned recursively for Lsubr and Gsubrs
* calls.
*
* @param Font the font
* @param FD FD array processed. 0 indicates function was called by non CID font
* @param SubrOffset the offset to the subr index to calc the bias
* @param SubrsOffsets the offset array of the subr index
* @param hSubr Map of the subrs used
* @param lSubr list of the subrs used
*/
protected void BuildSubrUsed(int Font, int FD, int SubrOffset, int[] SubrsOffsets, Set hSubr, List lSubr) {
// Calc the Bias for the subr index
int LBias = CalcBias(SubrOffset, Font);
// For each glyph used find its GID, start & end pos
for (Integer usedGlyph : glyphsInList) {
int glyph = (int) usedGlyph;
int Start = fonts[Font].charstringsOffsets[glyph];
int End = fonts[Font].charstringsOffsets[glyph + 1];
// IF CID:
if (FD >= 0) {
EmptyStack();
NumOfHints = 0;
// Using FDSELECT find the FD Array the glyph belongs to.
int GlyphFD = fonts[Font].FDSelect[glyph];
// If the Glyph is part of the FD being processed
if (GlyphFD == FD)
// Find the Subrs called by the glyph and insert to hash:
ReadASubr(Start, End, GBias, LBias, hSubr, lSubr, SubrsOffsets);
} else
// If the font is not CID
//Find the Subrs called by the glyph and insert to hash:
ReadASubr(Start, End, GBias, LBias, hSubr, lSubr, SubrsOffsets);
}
// For all Lsubrs used, check recursively for Lsubr & Gsubr used
for (int i = 0; i < lSubr.size(); i++) {
// Pop the subr value from the hash
int Subr = (int) lSubr.get(i);
// Ensure the Lsubr call is valid
if (Subr < SubrsOffsets.length - 1 && Subr >= 0) {
// Read and process the subr
int Start = SubrsOffsets[Subr];
int End = SubrsOffsets[Subr + 1];
ReadASubr(Start, End, GBias, LBias, hSubr, lSubr, SubrsOffsets);
}
}
}
/**
* Function scans the Glsubr used list to find recursive calls
* to Gsubrs and adds to Map and list
*
* @param Font the font
*/
protected void BuildGSubrsUsed(int Font) {
int LBias = 0;
int SizeOfNonCIDSubrsUsed = 0;
if (fonts[Font].privateSubrs >= 0) {
LBias = CalcBias(fonts[Font].privateSubrs, Font);
SizeOfNonCIDSubrsUsed = lSubrsUsedNonCID.size();
}
// For each global subr used
for (int i = 0; i < lGSubrsUsed.size(); i++) {
//Pop the value + check valid
int Subr = (int) lGSubrsUsed.get(i);
if (Subr < gsubrOffsets.length - 1 && Subr >= 0) {
// Read the subr and process
int Start = gsubrOffsets[Subr];
int End = gsubrOffsets[Subr + 1];
if (fonts[Font].isCID)
ReadASubr(Start, End, GBias, 0, hGSubrsUsed, lGSubrsUsed, null);
else {
ReadASubr(Start, End, GBias, LBias, hSubrsUsedNonCID, lSubrsUsedNonCID, fonts[Font].SubrsOffsets);
if (SizeOfNonCIDSubrsUsed < lSubrsUsedNonCID.size()) {
for (int j = SizeOfNonCIDSubrsUsed; j < lSubrsUsedNonCID.size(); j++) {
//Pop the value + check valid
int LSubr = (int) lSubrsUsedNonCID.get(j);
if (LSubr < fonts[Font].SubrsOffsets.length - 1 && LSubr >= 0) {
// Read the subr and process
int LStart = fonts[Font].SubrsOffsets[LSubr];
int LEnd = fonts[Font].SubrsOffsets[LSubr + 1];
ReadASubr(LStart, LEnd, GBias, LBias, hSubrsUsedNonCID, lSubrsUsedNonCID, fonts[Font].SubrsOffsets);
}
}
SizeOfNonCIDSubrsUsed = lSubrsUsedNonCID.size();
}
}
}
}
}
/**
* The function reads a subrs (glyph info) between begin and end.
* Adds calls to a Lsubr to the hSubr and lSubrs.
* Adds calls to a Gsubr to the hGSubr and lGSubrs.
*
* @param begin the start point of the subr
* @param end the end point of the subr
* @param GBias the bias of the Global Subrs
* @param LBias the bias of the Local Subrs
* @param hSubr the subroutines used as set
* @param lSubr the subroutines used as list
* @param LSubrsOffsets the offsets array of the subroutines
*/
protected void ReadASubr(int begin, int end, int GBias, int LBias, Set hSubr, List lSubr, int[] LSubrsOffsets) {
// Clear the stack for the subrs
EmptyStack();
NumOfHints = 0;
// Goto beginning of the subr
seek(begin);
while (getPosition() < end) {
// Read the next command
ReadCommand();
int pos = getPosition();
Object TopElement = null;
if (arg_count > 0)
TopElement = args[arg_count - 1];
int NumOfArgs = arg_count;
// Check the modification needed on the Argument Stack according to key;
HandelStack();
if (null != key)
// a call to a Lsubr
switch (key) {
// a call to a Gsubr
case "callsubr":
// Verify that arguments are passed
if (NumOfArgs > 0) {
// Calc the index of the Subrs
int Subr = (int) ((Integer) TopElement) + LBias;
// If the subr isn't in the Map -> Put in
if (!hSubr.contains(Subr)) {
hSubr.add(Subr);
lSubr.add(Subr);
}
CalcHints(LSubrsOffsets[Subr], LSubrsOffsets[Subr + 1], LBias, GBias, LSubrsOffsets);
seek(pos);
}
break;
// A call to "stem"
case "callgsubr":
// Verify that arguments are passed
if (NumOfArgs > 0) {
// Calc the index of the Subrs
int Subr = (int) ((Integer) TopElement) + GBias;
// If the subr isn't in the Map -> Put in
if (!hGSubrsUsed.contains(Subr)) {
hGSubrsUsed.add(Subr);
lGSubrsUsed.add(Subr);
}
CalcHints(gsubrOffsets[Subr], gsubrOffsets[Subr + 1], LBias, GBias, LSubrsOffsets);
seek(pos);
}
break;
case "hstem":
case "vstem":
case "hstemhm":
case "vstemhm":
// Increment the NumOfHints by the number couples of of arguments
NumOfHints += NumOfArgs / 2;
break;
case "hintmask":
case "cntrmask":
// if stack is not empty the reason is vstem implicit definition
// See Adobe Technical Note #5177, page 25, hintmask usage example.
NumOfHints += NumOfArgs / 2;
// Compute the size of the mask
int SizeOfMask = NumOfHints / 8;
if (NumOfHints % 8 != 0 || SizeOfMask == 0)
SizeOfMask++;
// Continue the pointer in SizeOfMask steps
for (int i = 0; i < SizeOfMask; i++) {
getCard8();
}
break;
}
}
}
/**
* Function Checks how the current operator effects the run time stack after being run
* An operator may increase or decrease the stack size
*/
protected void HandelStack() {
// Find out what the operator does to the stack
int StackHandel = StackOpp();
if (StackHandel < 2) {
// The operators that enlarge the stack by one
if (StackHandel == 1)
PushStack();
// The operators that pop the stack
else {
// Abs value for the for loop
StackHandel *= -1;
for (int i = 0; i < StackHandel; i++)
PopStack();
}
}
// All other flush the stack
else
EmptyStack();
}
/**
* Function checks the key and return the change to the stack after the operator
*
* @return The change in the stack. 2-> flush the stack
*/
protected int StackOpp() {
switch (key) {
case "ifelse":
return -3;
case "roll":
case "put":
return -2;
case "callsubr":
case "callgsubr":
case "add":
case "sub":
case "div":
case "mul":
case "drop":
case "and":
case "or":
case "eq":
return -1;
case "abs":
case "neg":
case "sqrt":
case "exch":
case "index":
case "get":
case "not":
case "return":
return 0;
case "random":
case "dup":
return 1;
}
return 2;
}
/**
* Empty the Type2 Stack
*/
protected void EmptyStack() {
// Null the arguments
for (int i = 0; i < arg_count; i++) args[i] = null;
arg_count = 0;
}
/**
* Pop one element from the stack
*/
protected void PopStack() {
if (arg_count > 0) {
args[arg_count - 1] = null;
arg_count--;
}
}
/**
* Add an item to the stack
*/
protected void PushStack() {
arg_count++;
}
/**
* The function reads the next command after the file pointer is set
*/
protected void ReadCommand() {
key = null;
boolean gotKey = false;
// Until a key is found
while (!gotKey) {
// Read the first Char
char b0 = getCard8();
// decode according to the type1/type2 format
if (b0 == 28)
{
// the two next bytes represent a short int;
int first = getCard8();
int second = getCard8();
args[arg_count] = first << 8 | second;
arg_count++;
continue;
}
// The byte read is the byte;
if (b0 >= 32 && b0 <= 246)
{
args[arg_count] = b0 - 139;
arg_count++;
continue;
}
// The byte read and the next byte constitute a short int
if (b0 >= 247 && b0 <= 250)
{
int w = getCard8();
args[arg_count] = (b0 - 247) * 256 + w + 108;
arg_count++;
continue;
}
// Same as above except negative
if (b0 >= 251 && b0 <= 254)
{
int w = getCard8();
args[arg_count] = -(b0 - 251) * 256 - w - 108;
arg_count++;
continue;
}
// The next for bytes represent a double.
if (b0 == 255)
{
int first = getCard8();
int second = getCard8();
int third = getCard8();
int fourth = getCard8();
args[arg_count] = first << 24 | second << 16 | third << 8 | fourth;
arg_count++;
continue;
}
// An operator was found.. Set Key.
if (b0 <= 31 && b0 != 28)
{
gotKey = true;
// 12 is an escape command therefore the next byte is a part
// of this command
if (b0 == 12) {
int b1 = getCard8();
if (b1 > SubrsEscapeFuncs.length - 1)
b1 = SubrsEscapeFuncs.length - 1;
key = SubrsEscapeFuncs[b1];
} else
key = SubrsFunctions[b0];
continue;
}
}
}
/**
* The function reads the subroutine and returns the number of the hint in it.
* If a call to another subroutine is found the function calls recursively.
*
* @param begin the start point of the subr
* @param end the end point of the subr
* @param LBias the bias of the Local Subrs
* @param GBias the bias of the Global Subrs
* @param LSubrsOffsets The Offsets array of the subroutines
* @return The number of hints in the subroutine read.
*/
protected int CalcHints(int begin, int end, int LBias, int GBias, int[] LSubrsOffsets) {
// Goto beginning of the subr
seek(begin);
while (getPosition() < end) {
// Read the next command
ReadCommand();
int pos = getPosition();
Object TopElement = null;
if (arg_count > 0)
TopElement = args[arg_count - 1];
int NumOfArgs = arg_count;
//Check the modification needed on the Argument Stack according to key;
HandelStack();
// a call to a Lsubr
switch (key) {
// a call to a Gsubr
case "callsubr":
if (NumOfArgs > 0) {
assert TopElement instanceof Integer;
int Subr = (int) ((Integer) TopElement) + LBias;
CalcHints(LSubrsOffsets[Subr], LSubrsOffsets[Subr + 1], LBias, GBias, LSubrsOffsets);
seek(pos);
}
break;
// A call to "stem"
case "callgsubr":
if (NumOfArgs > 0) {
assert TopElement instanceof Integer;
int Subr = (int) ((Integer) TopElement) + GBias;
CalcHints(gsubrOffsets[Subr], gsubrOffsets[Subr + 1], LBias, GBias, LSubrsOffsets);
seek(pos);
}
break;
case "hstem":
case "vstem":
case "hstemhm":
case "vstemhm":
// Increment the NumOfHints by the number couples of of arguments
NumOfHints += NumOfArgs / 2;
break;
case "hintmask":
case "cntrmask":
// Compute the size of the mask
int SizeOfMask = NumOfHints / 8;
if (NumOfHints % 8 != 0 || SizeOfMask == 0)
SizeOfMask++;
// Continue the pointer in SizeOfMask steps
for (int i = 0; i < SizeOfMask; i++) {
getCard8();
}
break;
}
}
return NumOfHints;
}
/**
* Function builds the new offset array, object array and assembles the index.
* used for creating the glyph and subrs subsetted index
*
* @param Offsets the offset array of the original index
* @param Used the Map of the used objects
* @param OperatorForUnusedEntries the operator inserted into the data stream for unused entries
* @return the new index subset version
* @throws java.io.IOException if an I/O error occurs
*/
protected byte[] BuildNewIndex(int[] Offsets, Set Used, byte OperatorForUnusedEntries) throws java.io.IOException {
int unusedCount = 0;
int Offset = 0;
int[] NewOffsets = new int[Offsets.length];
// Build the Offsets Array for the Subset
for (int i = 0; i < Offsets.length; ++i) {
NewOffsets[i] = Offset;
// If the object in the offset is also present in the used
// Map then increment the offset var by its size
if (Used.contains(i)) {
Offset += Offsets[i + 1] - Offsets[i];
} else {
// Else the same offset is kept in i+1.
unusedCount++;
}
}
// Offset var determines the size of the object array
byte[] NewObjects = new byte[Offset + unusedCount];
// Build the new Object array
int unusedOffset = 0;
for (int i = 0; i < Offsets.length - 1; ++i) {
int start = NewOffsets[i];
int end = NewOffsets[i + 1];
NewOffsets[i] = start + unusedOffset;
// If start != End then the Object is used
// So, we will copy the object data from the font file
if (start != end) {
// All offsets are Global Offsets relative to the beginning of the font file.
// Jump the file pointer to the start address to read from.
buf.seek(Offsets[i]);
// Read from the buffer and write into the array at start.
buf.readFully(NewObjects, start + unusedOffset, end - start);
} else {
NewObjects[start + unusedOffset] = OperatorForUnusedEntries;
unusedOffset++;
}
}
NewOffsets[Offsets.length - 1] += unusedOffset;
// Use AssembleIndex to build the index from the offset & object arrays
return AssembleIndex(NewOffsets, NewObjects);
}
/**
* Function builds the new offset array, object array and assembles the index.
* used for creating the glyph and subrs subsetted index
*
* @param Offsets the offset array of the original index
* @param OperatorForUnusedEntries the operator inserted into the data stream for unused entries
* @return the new index subset version
* @throws java.io.IOException if an I/O error occurs
*/
protected byte[] BuildNewIndexAndCopyAllGSubrs(int[] Offsets, byte OperatorForUnusedEntries) throws java.io.IOException {
int unusedCount = 0;
int Offset = 0;
int[] NewOffsets = new int[Offsets.length];
// Build the Offsets Array for the Subset
for (int i = 0; i < Offsets.length - 1; ++i) {
NewOffsets[i] = Offset;
Offset += Offsets[i + 1] - Offsets[i];
}
// Else the same offset is kept in i+1.
NewOffsets[Offsets.length - 1] = Offset;
unusedCount++;
// Offset var determines the size of the object array
byte[] NewObjects = new byte[Offset + unusedCount];
// Build the new Object array
int unusedOffset = 0;
for (int i = 0; i < Offsets.length - 1; ++i) {
int start = NewOffsets[i];
int end = NewOffsets[i + 1];
NewOffsets[i] = start + unusedOffset;
// If start != End then the Object is used
// So, we will copy the object data from the font file
if (start != end) {
// All offsets are Global Offsets relative to the beginning of the font file.
// Jump the file pointer to the start address to read from.
buf.seek(Offsets[i]);
// Read from the buffer and write into the array at start.
buf.readFully(NewObjects, start + unusedOffset, end - start);
} else {
NewObjects[start + unusedOffset] = OperatorForUnusedEntries;
unusedOffset++;
}
}
NewOffsets[Offsets.length - 1] += unusedOffset;
// Use AssembleIndex to build the index from the offset & object arrays
return AssembleIndex(NewOffsets, NewObjects);
}
/**
* Function creates the new index, inserting the count,offsetsize,offset array
* and object array.
*
* @param NewOffsets the subsetted offset array
* @param NewObjects the subsetted object array
* @return the new index created
*/
protected byte[] AssembleIndex(int[] NewOffsets, byte[] NewObjects) {
// Calc the index' count field
char Count = (char) (NewOffsets.length - 1);
// Calc the size of the object array
int Size = NewOffsets[NewOffsets.length - 1];
// Calc the Offsize
byte Offsize;
// Previously the condition wasn't strict. However while writing offsets iText adds 1 to them.
// That can cause overflow (f.e., offset 0xffff will result in 0x0000).
if (Size < 0xff) {
Offsize = 1;
} else if (Size < 0xffff) {
Offsize = 2;
} else if (Size < 0xffffff) {
Offsize = 3;
} else {
Offsize = 4;
}
// The byte array for the new index. The size is calc by
// Count=2, Offsize=1, OffsetArray = Offsize*(Count+1), The object array
byte[] NewIndex = new byte[2 + 1 + Offsize * (Count + 1) + NewObjects.length];
// The counter for writing
int Place = 0;
// Write the count field
// There is no sense in >>> for char
// NewIndex[Place++] = (byte) (Count >>> 8 & 0xff);
NewIndex[Place++] = (byte) (Count >> 8 & 0xff);
NewIndex[Place++] = (byte) (Count & 0xff);
// Write the offsize field
NewIndex[Place++] = Offsize;
// Write the offset array according to the offsize
for (int newOffset : NewOffsets) {
// The value to be written
int Num = newOffset - NewOffsets[0] + 1;
// Write in bytes according to the offsize
for (int i = Offsize; i > 0; i--) {
NewIndex[Place++] = (byte) (Num >>> ((i - 1) << 3) & 0xff);
}
}
// Write the new object array one by one
for (byte newObject : NewObjects) {
NewIndex[Place++] = newObject;
}
// Return the new index
return NewIndex;
}
/**
* The function builds the new output stream according to the subset process
*
* @param Font the font
* @return the subsetted font stream
*/
protected byte[] BuildNewFile(int Font) {
// Prepare linked list for new font components
OutputList = new LinkedList<>();
// copy the header of the font
CopyHeader();
// create a name index
BuildIndexHeader(1, 1, 1);
OutputList.addLast(new UInt8Item((char) (1 + fonts[Font].name.length())));
OutputList.addLast(new StringItem(fonts[Font].name));
// create the topdict Index
BuildIndexHeader(1, 2, 1);
OffsetItem topdictIndex1Ref = new IndexOffsetItem(2);
OutputList.addLast(topdictIndex1Ref);
IndexBaseItem topdictBase = new IndexBaseItem();
OutputList.addLast(topdictBase);
// Initialize the Dict Items for later use
OffsetItem charsetRef = new DictOffsetItem();
OffsetItem charstringsRef = new DictOffsetItem();
OffsetItem fdarrayRef = new DictOffsetItem();
OffsetItem fdselectRef = new DictOffsetItem();
OffsetItem privateRef = new DictOffsetItem();
// If the font is not CID create the following keys
if (!fonts[Font].isCID) {
// create a ROS key
OutputList.addLast(new DictNumberItem(fonts[Font].nstrings));
OutputList.addLast(new DictNumberItem(fonts[Font].nstrings + 1));
OutputList.addLast(new DictNumberItem(0));
OutputList.addLast(new UInt8Item((char) 12));
OutputList.addLast(new UInt8Item((char) 30));
// create a CIDCount key
OutputList.addLast(new DictNumberItem(fonts[Font].nglyphs));
OutputList.addLast(new UInt8Item((char) 12));
OutputList.addLast(new UInt8Item((char) 34));
// Sivan's comments
// What about UIDBase (12,35)? Don't know what is it.
// I don't think we need FontName; the font I looked at didn't have it.
}
// Go to the TopDict of the font being processed
seek(topdictOffsets[Font]);
// Run until the end of the TopDict
while (getPosition() < topdictOffsets[Font + 1]) {
int p1 = getPosition();
getDictItem();
int p2 = getPosition();
// The encoding key is disregarded since CID has no encoding
if ("Encoding".equals(key)
// These keys will be added manually by the process.
|| "Private".equals(key)
|| "FDSelect".equals(key)
|| "FDArray".equals(key)
|| "charset".equals(key)
|| "CharStrings".equals(key)
) {
} else {
//OtherWise copy key "as is" to the output list
OutputList.addLast(new RangeItem(buf, p1, p2 - p1));
}
}
// Create the FDArray, FDSelect, Charset and CharStrings Keys
CreateKeys(fdarrayRef, fdselectRef, charsetRef, charstringsRef);
// Mark the end of the top dict area
OutputList.addLast(new IndexMarkerItem(topdictIndex1Ref, topdictBase));
// Copy the string index
if (fonts[Font].isCID)
OutputList.addLast(getEntireIndexRange(stringIndexOffset));
// If the font is not CID we need to append new strings.
// We need 3 more strings: Registry, Ordering, and a FontName for one FD.
// The total length is at most "Adobe"+"Identity"+63 = 76
else
CreateNewStringIndex(Font);
// copy the new subsetted global subroutine index
OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(rasFactory.createSource(NewGSubrsIndex)), 0, NewGSubrsIndex.length));
// deal with fdarray, fdselect, and the font descriptors
// If the font is CID:
if (fonts[Font].isCID) {
// copy the FDArray, FDSelect, charset
// Copy FDSelect
// Mark the beginning
OutputList.addLast(new MarkerItem(fdselectRef));
// If an FDSelect exists copy it
if (fonts[Font].fdselectOffset >= 0)
OutputList.addLast(new RangeItem(buf, fonts[Font].fdselectOffset, fonts[Font].FDSelectLength));
// Else create a new one
else
CreateFDSelect(fdselectRef, fonts[Font].nglyphs);
// Copy the Charset
// Mark the beginning and copy entirely
OutputList.addLast(new MarkerItem(charsetRef));
OutputList.addLast(new RangeItem(buf, fonts[Font].charsetOffset, fonts[Font].CharsetLength));
// Copy the FDArray
// If an FDArray exists
if (fonts[Font].fdarrayOffset >= 0) {
// Mark the beginning
OutputList.addLast(new MarkerItem(fdarrayRef));
// Build a new FDArray with its private dicts and their LSubrs
Reconstruct(Font);
} else
// Else create a new one
CreateFDArray(fdarrayRef, privateRef, Font);
}
// If the font is not CID
else {
// create FDSelect
CreateFDSelect(fdselectRef, fonts[Font].nglyphs);
// recreate a new charset
CreateCharset(charsetRef, fonts[Font].nglyphs);
// create a font dict index (fdarray)
CreateFDArray(fdarrayRef, privateRef, Font);
}
// if a private dict exists insert its subsetted version
if (fonts[Font].privateOffset >= 0) {
// Mark the beginning of the private dict
IndexBaseItem PrivateBase = new IndexBaseItem();
OutputList.addLast(PrivateBase);
OutputList.addLast(new MarkerItem(privateRef));
OffsetItem Subr = new DictOffsetItem();
// Build and copy the new private dict
CreateNonCIDPrivate(Font, Subr);
// Copy the new LSubrs index
CreateNonCIDSubrs(Font, PrivateBase, Subr);
}
// copy the charstring index
OutputList.addLast(new MarkerItem(charstringsRef));
// Add the subsetted charstring
OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(rasFactory.createSource(NewCharStringsIndex)), 0, NewCharStringsIndex.length));
// now create the new CFF font
int[] currentOffset = new int[1];
currentOffset[0] = 0;
// Count and save the offset for each item
for (Item item : OutputList) {
item.increment(currentOffset);
}
// Compute the Xref for each of the offset items
for (Item item : OutputList) {
item.xref();
}
int size = currentOffset[0];
byte[] b = new byte[size];
// Emit all the items into the new byte array
for (Item item : OutputList) {
item.emit(b);
}
// Return the new stream
return b;
}
/**
* Function Copies the header from the original fileto the output list
*/
protected void CopyHeader() {
seek(0);
int major = getCard8();
int minor = getCard8();
int hdrSize = getCard8();
int offSize = getCard8();
nextIndexOffset = hdrSize;
OutputList.addLast(new RangeItem(buf, 0, hdrSize));
}
/**
* Function Build the header of an index
*
* @param Count the count field of the index
* @param Offsize the offsize field of the index
* @param First the first offset of the index
*/
protected void BuildIndexHeader(int Count, int Offsize, int First) {
// Add the count field
OutputList.addLast(new UInt16Item((char) Count));
// Add the offsize field
OutputList.addLast(new UInt8Item((char) Offsize));
// Add the first offset according to the offsize
switch (Offsize) {
case 1:
// first offset
OutputList.addLast(new UInt8Item((char) First));
break;
case 2:
// first offset
OutputList.addLast(new UInt16Item((char) First));
break;
case 3:
// first offset
OutputList.addLast(new UInt24Item((char) First));
break;
case 4:
// first offset
OutputList.addLast(new UInt32Item((char) First));
break;
default:
break;
}
}
/**
* Function adds the keys into the TopDict
*
* @param fdarrayRef OffsetItem for the FDArray
* @param fdselectRef OffsetItem for the FDSelect
* @param charsetRef OffsetItem for the CharSet
* @param charstringsRef OffsetItem for the CharString
*/
protected void CreateKeys(OffsetItem fdarrayRef, OffsetItem fdselectRef, OffsetItem charsetRef, OffsetItem charstringsRef) {
// create an FDArray key
OutputList.addLast(fdarrayRef);
OutputList.addLast(new UInt8Item((char) 12));
OutputList.addLast(new UInt8Item((char) 36));
// create an FDSelect key
OutputList.addLast(fdselectRef);
OutputList.addLast(new UInt8Item((char) 12));
OutputList.addLast(new UInt8Item((char) 37));
// create an charset key
OutputList.addLast(charsetRef);
OutputList.addLast(new UInt8Item((char) 15));
// create a CharStrings key
OutputList.addLast(charstringsRef);
OutputList.addLast(new UInt8Item((char) 17));
}
/**
* Function takes the original string item and adds the new strings
* to accommodate the CID rules
*
* @param Font the font
*/
protected void CreateNewStringIndex(int Font) {
String fdFontName = fonts[Font].name + "-OneRange";
if (fdFontName.length() > 127)
fdFontName = fdFontName.substring(0, 127);
String extraStrings = "Adobe" + "Identity" + fdFontName;
int origStringsLen = stringOffsets[stringOffsets.length - 1]
- stringOffsets[0];
int stringsBaseOffset = stringOffsets[0] - 1;
byte stringsIndexOffSize;
if (origStringsLen + extraStrings.length() <= 0xff) stringsIndexOffSize = 1;
else if (origStringsLen + extraStrings.length() <= 0xffff) stringsIndexOffSize = 2;
else if (origStringsLen + extraStrings.length() <= 0xffffff) stringsIndexOffSize = 3;
else stringsIndexOffSize = 4;
// count
OutputList.addLast(new UInt16Item((char) (stringOffsets.length - 1 + 3)));
// offSize
OutputList.addLast(new UInt8Item((char) stringsIndexOffSize));
for (int stringOffset : stringOffsets)
OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize,
stringOffset - stringsBaseOffset));
int currentStringsOffset = stringOffsets[stringOffsets.length - 1]
- stringsBaseOffset;
// l.addLast(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset));
currentStringsOffset += "Adobe".length();
OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize, currentStringsOffset));
currentStringsOffset += "Identity".length();
OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize, currentStringsOffset));
currentStringsOffset += fdFontName.length();
OutputList.addLast(new IndexOffsetItem(stringsIndexOffSize, currentStringsOffset));
OutputList.addLast(new RangeItem(buf, stringOffsets[0], origStringsLen));
OutputList.addLast(new StringItem(extraStrings));
}
/**
* Function creates new FDSelect for non-CID fonts.
* The FDSelect built uses a single range for all glyphs
*
* @param fdselectRef OffsetItem for the FDSelect
* @param nglyphs the number of glyphs in the font
*/
protected void CreateFDSelect(OffsetItem fdselectRef, int nglyphs) {
OutputList.addLast(new MarkerItem(fdselectRef));
// format identifier
OutputList.addLast(new UInt8Item((char) 3));
// nRanges
OutputList.addLast(new UInt16Item((char) 1));
// Range[0].firstGlyph
OutputList.addLast(new UInt16Item((char) 0));
// Range[0].fd
OutputList.addLast(new UInt8Item((char) 0));
// sentinel
OutputList.addLast(new UInt16Item((char) nglyphs));
}
/**
* Function creates new CharSet for non-CID fonts.
* The CharSet built uses a single range for all glyphs
*
* @param charsetRef OffsetItem for the CharSet
* @param nglyphs the number of glyphs in the font
*/
protected void CreateCharset(OffsetItem charsetRef, int nglyphs) {
OutputList.addLast(new MarkerItem(charsetRef));
// format identifier
OutputList.addLast(new UInt8Item((char) 2));
// first glyph in range (ignore .notdef)
OutputList.addLast(new UInt16Item((char) 1));
// nLeft
/*
Maintenance note: Here's the rationale for subtracting 2:
- The .notdef glyph is included in the nglyphs count, but
we excluded it by starting our range at 1 => decrement once.
- The CFF specification mandates that the nLeft field _exclude_
the first glyph => decrement once more.
This line used to say "nglyphs - 1" for the better part of two decades,
so many PDFs out there contain wrong charset extents.
*/
OutputList.addLast(new UInt16Item((char) (nglyphs - 2)));
}
/**
* Function creates new FDArray for non-CID fonts.
* The FDArray built has only the "Private" operator that points to the font's
* original private dict
*
* @param fdarrayRef OffsetItem for the FDArray
* @param privateRef OffsetItem for the Private Dict
* @param Font the font
*/
protected void CreateFDArray(OffsetItem fdarrayRef, OffsetItem privateRef, int Font) {
OutputList.addLast(new MarkerItem(fdarrayRef));
// Build the header (count=offsize=first=1)
BuildIndexHeader(1, 1, 1);
// Mark
OffsetItem privateIndex1Ref = new IndexOffsetItem(1);
OutputList.addLast(privateIndex1Ref);
IndexBaseItem privateBase = new IndexBaseItem();
// Insert the private operands and operator
OutputList.addLast(privateBase);
// Calc the new size of the private after subsetting
// Origianl size
int NewSize = fonts[Font].privateLength;
// Calc the original size of the Subr offset in the private
int OrgSubrsOffsetSize = CalcSubrOffsetSize(fonts[Font].privateOffset, fonts[Font].privateLength);
// Increase the ptivate's size
if (OrgSubrsOffsetSize != 0)
NewSize += 5 - OrgSubrsOffsetSize;
OutputList.addLast(new DictNumberItem(NewSize));
OutputList.addLast(privateRef);
// Private
OutputList.addLast(new UInt8Item((char) 18));
OutputList.addLast(new IndexMarkerItem(privateIndex1Ref, privateBase));
}
/**
* Function reconstructs the FDArray, PrivateDict and LSubr for CID fonts
*
* @param Font the font
*/
void Reconstruct(int Font) {
// Init for later use
OffsetItem[] fdPrivate = new DictOffsetItem[fonts[Font].FDArrayOffsets.length - 1];
IndexBaseItem[] fdPrivateBase = new IndexBaseItem[fonts[Font].fdprivateOffsets.length];
OffsetItem[] fdSubrs = new DictOffsetItem[fonts[Font].fdprivateOffsets.length];
// Reconstruct each type
ReconstructFDArray(Font, fdPrivate);
ReconstructPrivateDict(Font, fdPrivate, fdPrivateBase, fdSubrs);
ReconstructPrivateSubrs(Font, fdPrivateBase, fdSubrs);
}
/**
* Function subsets the FDArray and builds the new one with new offsets
*
* @param Font The font
* @param fdPrivate OffsetItem Array (one for each FDArray)
*/
void ReconstructFDArray(int Font, OffsetItem[] fdPrivate) {
// Build the header of the index
BuildIndexHeader(fonts[Font].FDArrayCount, fonts[Font].FDArrayOffsize, 1);
// For each offset create an Offset Item
OffsetItem[] fdOffsets = new IndexOffsetItem[fonts[Font].FDArrayOffsets.length - 1];
for (int i = 0; i < fonts[Font].FDArrayOffsets.length - 1; i++) {
fdOffsets[i] = new IndexOffsetItem(fonts[Font].FDArrayOffsize);
OutputList.addLast(fdOffsets[i]);
}
// Declare beginning of the object array
IndexBaseItem fdArrayBase = new IndexBaseItem();
OutputList.addLast(fdArrayBase);
// For each object check if that FD is used.
// if is used build a new one by changing the private object
// Else do nothing
// At the end of each object mark its ending (Even if wasn't written)
for (int k = 0; k < fonts[Font].FDArrayOffsets.length - 1; k++) {
// if (FDArrayUsed.contains(Integer.valueOf(k)))
// {
// Goto beginning of objects
seek(fonts[Font].FDArrayOffsets[k]);
while (getPosition() < fonts[Font].FDArrayOffsets[k + 1]) {
int p1 = getPosition();
getDictItem();
int p2 = getPosition();
// If the dictItem is the "Private" then compute and copy length,
// use marker for offset and write operator number
if ("Private".equals(key)) {
// Save the original length of the private dict
int NewSize = (int) ((Integer) args[0]);
// Save the size of the offset to the subrs in that private
int OrgSubrsOffsetSize = CalcSubrOffsetSize(fonts[Font].fdprivateOffsets[k], fonts[Font].fdprivateLengths[k]);
// Increase the private's length accordingly
if (OrgSubrsOffsetSize != 0)
NewSize += 5 - OrgSubrsOffsetSize;
// Insert the new size, OffsetItem and operator key number
OutputList.addLast(new DictNumberItem(NewSize));
fdPrivate[k] = new DictOffsetItem();
OutputList.addLast(fdPrivate[k]);
// Private
OutputList.addLast(new UInt8Item((char) 18));
// Go back to place
seek(p2);
}
// Else copy the entire range
else // other than private
OutputList.addLast(new RangeItem(buf, p1, p2 - p1));
}
// }
// Mark the ending of the object (even if wasn't written)
OutputList.addLast(new IndexMarkerItem(fdOffsets[k], fdArrayBase));
}
}
/**
* Function Adds the new private dicts (only for the FDs used) to the list
*
* @param Font the font
* @param fdPrivate OffsetItem array one element for each private
* @param fdPrivateBase IndexBaseItem array one element for each private
* @param fdSubrs OffsetItem array one element for each private
*/
void ReconstructPrivateDict(int Font, OffsetItem[] fdPrivate, IndexBaseItem[] fdPrivateBase,
OffsetItem[] fdSubrs) {
// For each fdarray private dict check if that FD is used.
// if is used build a new one by changing the subrs offset
// Else do nothing
for (int i = 0; i < fonts[Font].fdprivateOffsets.length; i++) {
// if (FDArrayUsed.contains(Integer.valueOf(i)))
// {
// Mark beginning
OutputList.addLast(new MarkerItem(fdPrivate[i]));
fdPrivateBase[i] = new IndexBaseItem();
OutputList.addLast(fdPrivateBase[i]);
// Goto beginning of objects
seek(fonts[Font].fdprivateOffsets[i]);
while (getPosition() < fonts[Font].fdprivateOffsets[i] + fonts[Font].fdprivateLengths[i]) {
int p1 = getPosition();
getDictItem();
int p2 = getPosition();
// If the dictItem is the "Subrs" then,
// use marker for offset and write operator number
if ("Subrs".equals(key)) {
fdSubrs[i] = new DictOffsetItem();
OutputList.addLast(fdSubrs[i]);
// Subrs
OutputList.addLast(new UInt8Item((char) 19));
}
// Else copy the entire range
else
OutputList.addLast(new RangeItem(buf, p1, p2 - p1));
}
// }
}
}
/**
* Function Adds the new LSubrs dicts (only for the FDs used) to the list
*
* @param Font The index of the font
* @param fdPrivateBase The IndexBaseItem array for the linked list
* @param fdSubrs OffsetItem array for the linked list
*/
void ReconstructPrivateSubrs(int Font, IndexBaseItem[] fdPrivateBase,
OffsetItem[] fdSubrs) {
// For each private dict
for (int i = 0; i < fonts[Font].fdprivateLengths.length; i++) {
// If that private dict's Subrs are used insert the new LSubrs
// computed earlier
if (fdSubrs[i] != null && fonts[Font].PrivateSubrsOffset[i] >= 0) {
OutputList.addLast(new SubrMarkerItem(fdSubrs[i], fdPrivateBase[i]));
if (NewLSubrsIndex[i] != null)
OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(rasFactory.createSource(NewLSubrsIndex[i])), 0, NewLSubrsIndex[i].length));
}
}
}
/**
* Calculates how many byte it took to write the offset for the subrs in a specific
* private dict.
*
* @param Offset The Offset for the private dict
* @param Size The size of the private dict
* @return The size of the offset of the subrs in the private dict
*/
int CalcSubrOffsetSize(int Offset, int Size) {
// Set the size to 0
int OffsetSize = 0;
// Go to the beginning of the private dict
seek(Offset);
// Go until the end of the private dict
while (getPosition() < Offset + Size) {
int p1 = getPosition();
getDictItem();
int p2 = getPosition();
// When reached to the subrs offset
if ("Subrs".equals(key)) {
// The Offsize (minus the subrs key)
OffsetSize = p2 - p1 - 1;
}
// All other keys are ignored
}
// return the size
return OffsetSize;
}
/**
* Function computes the size of an index
*
* @param indexOffset The offset for the computed index
* @return The size of the index
*/
protected int countEntireIndexRange(int indexOffset) {
// Go to the beginning of the index
seek(indexOffset);
// Read the count field
int count = getCard16();
// If count==0 -> size=2
if (count == 0)
return 2;
else {
// Read the offsize field
int indexOffSize = getCard8();
// Go to the last element of the offset array
seek(indexOffset + 2 + 1 + count * indexOffSize);
// The size of the object array is the value of the last element-1
int size = getOffset(indexOffSize) - 1;
// Return the size of the entire index
return 2 + 1 + (count + 1) * indexOffSize + size;
}
}
/**
* The function creates a private dict for a font that was not CID
* All the keys are copied as is except for the subrs key
*
* @param Font the font
* @param Subr The OffsetItem for the subrs of the private
*/
void CreateNonCIDPrivate(int Font, OffsetItem Subr) {
// Go to the beginning of the private dict and read until the end
seek(fonts[Font].privateOffset);
while (getPosition() < fonts[Font].privateOffset + fonts[Font].privateLength) {
int p1 = getPosition();
getDictItem();
int p2 = getPosition();
// If the dictItem is the "Subrs" then,
// use marker for offset and write operator number
if ("Subrs".equals(key)) {
OutputList.addLast(Subr);
// Subrs
OutputList.addLast(new UInt8Item((char) 19));
}
// Else copy the entire range
else
OutputList.addLast(new RangeItem(buf, p1, p2 - p1));
}
}
/**
* the function marks the beginning of the subrs index and adds the subsetted subrs
* index to the output list.
*
* @param Font the font
* @param PrivateBase IndexBaseItem for the private that's referencing to the subrs
* @param Subrs OffsetItem for the subrs
*/
void CreateNonCIDSubrs(int Font, IndexBaseItem PrivateBase, OffsetItem Subrs) {
// Mark the beginning of the Subrs index
OutputList.addLast(new SubrMarkerItem(Subrs, PrivateBase));
// Put the subsetted new subrs index
if (NewSubrsIndexNonCID != null) {
OutputList.addLast(new RangeItem(new RandomAccessFileOrArray(rasFactory.createSource(NewSubrsIndexNonCID)), 0, NewSubrsIndexNonCID.length));
}
}
/**
* Returns the CID to which specified GID is mapped.
*
* @param gid glyph identifier
*
* @return CID value
*/
int getCidForGlyphId(int gid) {
return getCidForGlyphId(0, gid);
}
/**
* Returns the CID to which specified GID is mapped.
*
* @param fontIndex index of font for which cid-gid mapping is to be identified
* @param gid glyph identifier
*
* @return CID value
*/
int getCidForGlyphId(int fontIndex, int gid) {
if (fonts[fontIndex].gidToCid == null) {
return gid;
}
// gidToCid mapping starts with value corresponding to gid == 1, becuase .notdef is omitted
int index = gid - 1;
return index >= 0 && index < fonts[fontIndex].gidToCid.length
? fonts[fontIndex].gidToCid[index]
: gid;
}
/**
* Creates glyph-to-character id array.
*
* @param fontIndex index of font for which charsets data is to be parsed
* @param numOfGlyphs number of glyphs in the font
* @param offset the offset to charsets data
*/
private void initGlyphIdToCharacterIdArray(int fontIndex, int numOfGlyphs, int offset) {
// Seek charset offset
seek(offset);
// Read the format
int format = getCard8();
// .notdef is omitted, therefore remaining number of elements is one less than overall number
int numOfElements = numOfGlyphs - 1;
fonts[fontIndex].gidToCid = new int[numOfElements];
switch (format) {
case 0:
for (int i = 0; i < numOfElements; i++) {
int cid = getCard16();
fonts[fontIndex].gidToCid[i] = cid;
}
break;
case 1:
case 2:
int start = 0;
while (start < numOfElements) {
int first = getCard16();
int nLeft = format == 1 ? getCard8() : getCard16();
for (int i = 0; i <= nLeft && start < numOfElements; i++) {
fonts[fontIndex].gidToCid[start++] = first + i;
}
}
break;
default:
break;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy