Files
core/PdfFile/SrcWriter/FontOTWriter.cpp
2026-04-02 08:10:57 +03:00

5049 lines
162 KiB
C++

/*
* (c) Copyright Ascensio System SIA 2010-2023
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#include "FontTTWriter.h"
#include <map>
#include <vector>
#include <list>
#include <set>
namespace PdfWriter
{
#define N_STD_STRINGS 391
static const char* scStandardStrings[N_STD_STRINGS] = {
".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft",
"parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two",
"three","four","five","six","seven","eight","nine","colon","semicolon","less",
"equal","greater","question","at","A","B","C","D","E","F",
"G","H","I","J","K","L","M","N","O","P",
"Q","R","S","T","U","V","W","X","Y","Z",
"bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d",
"e","f","g","h","i","j","k","l","m","n",
"o","p","q","r","s","t","u","v","w","x",
"y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction",
"yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi",
"fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright",
"guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve",
"dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine",
"Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls",
"onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide",
"brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior",
"copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex",
"Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis",
"Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron",
"aacute","acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis",
"egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve",
"otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron","exclamsmall",
"Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","zerooldstyle",
"oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior",
"threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior",
"msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","ffi","ffl","parenleftinferior",
"parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall",
"Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall",
"Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall",
"colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall",
"Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall",
"oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","foursuperior","fivesuperior","sixsuperior",
"sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior",
"seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall",
"Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall",
"Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall",
"OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall","001.000",
"001.001","001.002","001.003","Black","Bold","Book","Light","Medium","Regular","Roman",
"Semibold"
};
#define CHARSET_ISOADOBE_SIZE 228
static const unsigned short scCharsetIsoadobeSids[CHARSET_ISOADOBE_SIZE] =
{
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,
78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,
111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,
138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,
165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,
219,220,221,222,223,224,225,226,227,228
};
#define CHARSET_EXPERT_SIZE 165
static const unsigned short scCharsetExpertSids[CHARSET_EXPERT_SIZE] =
{
1,229,230,231,232,233,234,235,236,237,238,13,14,15,99,239,240,241,242,243,244,245,246,247,248,27,28,
249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,109,110,267,268,269,270,271,272,
273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,
299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,158,155,163,319,320,321,
322,323,324,325,326,150,164,169,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,
345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,
370,371,372,373,374,375,376,377,378
};
#define CHARSET_EXPERT_SUBSET_SIZE 86
static const unsigned short scCharsetExpertSubsetSids[CHARSET_EXPERT_SUBSET_SIZE] =
{
1,231,232,235,236,237,238,13,14,15,99,239,240,241,242,243,244,245,246,247,248,27,28,
249,250,251,253,254,255,256,257,258,259,260,261,262,263,264,265,266,109,110,267,268,
269,270,272,300,301,302,305,314,315,158,155,163,320,321,322,323,324,325,326,150,164,
169,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346
};
static const unsigned short* scDefaultCharsets[3] =
{
scCharsetIsoadobeSids,
scCharsetExpertSids,
scCharsetExpertSubsetSids
};
static const unsigned short scDefaultCharsetsSizes[3] =
{
CHARSET_ISOADOBE_SIZE,
CHARSET_EXPERT_SIZE,
CHARSET_EXPERT_SUBSET_SIZE
};
static const unsigned short scCharset = 15;
static const unsigned short scEncoding = 16;
static const unsigned short scCharStrings = 17;
static const unsigned short scPrivate = 18;
static const unsigned short scSubrs = 19;
static const unsigned short scFDArray = 0xC24;
static const unsigned short scFDSelect = 0xC25;
static const unsigned short scROS = 0xC1E;
struct DictOperand
{
bool IsInteger;
long IntegerValue;
double RealValue;
long RealValueFractalEnd;
};
typedef std::list<DictOperand> DictOperandList;
typedef std::map<unsigned short, DictOperandList> UShortToDictOperandListMap;
enum EEncodingType
{
eEncodingStandard = 0,
eEncodingExpert,
eEncodingCustom
};
typedef std::list<BYTE> ByteList;
typedef std::map<unsigned short, ByteList> UShortToByteList;
struct EncodingsInfo
{
EncodingsInfo() { mEncoding = NULL; mEncodingsCount = 0; }
long long mEncodingStart;
long long mEncodingEnd;
EEncodingType mType;
BYTE mEncodingsCount;
BYTE* mEncoding;
UShortToByteList mSupplements;
};
struct IndexElement
{
IndexElement() { mStartPosition = 0; mEndPosition = 0; mIndex = 0; }
long long mStartPosition;
long long mEndPosition;
unsigned short mIndex;
};
typedef IndexElement CharString;
typedef CharString* CharStringsIndex;
struct CharStrings
{
CharStrings(){mCharStringsIndex = NULL; mCharStringsType = 0; mCharStringsCount = 0;}
BYTE mCharStringsType;
unsigned short mCharStringsCount;
CharStringsIndex mCharStringsIndex;
};
struct PrivateDictInfo
{
PrivateDictInfo() {mPrivateDictStart=0;mPrivateDictEnd=0;mLocalSubrs=NULL;}
long long mPrivateDictStart;
long long mPrivateDictEnd;
UShortToDictOperandListMap mPrivateDict;
CharStrings* mLocalSubrs;
};
struct FontDictInfo
{
long long mFontDictStart;
long long mFontDictEnd;
UShortToDictOperandListMap mFontDict;
PrivateDictInfo mPrivateDict;
};
typedef std::set<unsigned short> UShortSet;
struct CharString2Dependencies
{
UShortSet mCharCodes; // from seac-like endchar operator
UShortSet mGlobalSubrs; // from callgsubr
UShortSet mLocalSubrs; // from callsubr
};
struct CharStringOperand
{
bool IsInteger;
long IntegerValue;
double RealValue;
};
typedef std::list<CharStringOperand> CharStringOperandList;
typedef std::vector<CharStringOperand> CharStringOperandVector;
namespace FSType
{
bool CanEmbed(unsigned short mFSTypeValue)
{
return (mFSTypeValue != 0x2) && (mFSTypeValue != 0x0200) && (mFSTypeValue != 0x0202);
}
}
//----------------------------------------------------------------------------------------
// CPrimitiveWriter
//----------------------------------------------------------------------------------------
struct CPrimitiveWriter
{
BYTE mCurrentOffsize;
CStream* mStream;
public:
CPrimitiveWriter(CStream* pStream);
~CPrimitiveWriter();
void SetOffSize(BYTE inOffSize);
bool WriteByte(BYTE inValue);
bool WriteCard8(BYTE inValue);
bool WriteCard16(unsigned short inValue);
bool WriteOffSize(BYTE inValue);
bool WriteOffset(unsigned long inValue);
bool Write3ByteUnsigned(unsigned long inValue);
bool Write4ByteUnsigned(unsigned long inValue);
bool Write(const BYTE* inBuffer, unsigned long inBufferSize);
bool WriteDictOperand(const DictOperand& inOperand);
bool WriteDictItems(unsigned short inOperator, const DictOperandList& inOperands);
bool WriteIntegerOperand(long inValue);
bool WriteRealOperand(double inValue, long inFractalLength);
bool Write5ByteDictInteger(long inValue);
bool WriteIntegerOfReal(double inIntegerValue, BYTE& ioBuffer, bool& ioUsedFirst);
bool SetOrWriteNibble(BYTE inValue, BYTE& ioBuffer, bool& ioUsedFirst);
bool WriteDictOperator(unsigned short inOperator);
bool Pad5Bytes();
bool WriteSID(unsigned short inValue);
};
CPrimitiveWriter::CPrimitiveWriter(CStream* pStream)
{
mCurrentOffsize = 1;
mStream = pStream;
}
CPrimitiveWriter::~CPrimitiveWriter()
{
mCurrentOffsize = 1;
mStream = NULL;
}
void CPrimitiveWriter::SetOffSize(BYTE inOffSize)
{
mCurrentOffsize = inOffSize;
}
bool CPrimitiveWriter::WriteByte(BYTE inValue)
{
mStream->WriteUChar(inValue);
return true;
}
bool CPrimitiveWriter::WriteCard8(BYTE inValue)
{
return WriteByte(inValue);
}
bool CPrimitiveWriter::WriteCard16(unsigned short inValue)
{
mStream->WriteUChar((inValue >> 8) & 0xff);
mStream->WriteUChar(inValue & 0xff);
return true;
}
bool CPrimitiveWriter::WriteOffSize(BYTE inValue)
{
return WriteCard8(inValue);
}
bool CPrimitiveWriter::WriteOffset(unsigned long inValue)
{
switch (mCurrentOffsize)
{
case 1:
WriteCard8((BYTE)inValue);
break;
case 2:
WriteCard16((unsigned short)inValue);
break;
case 3:
Write3ByteUnsigned(inValue);
break;
case 4:
Write4ByteUnsigned(inValue);
break;
}
return true;
}
bool CPrimitiveWriter::Write3ByteUnsigned(unsigned long inValue)
{
mStream->WriteUChar((inValue >> 16) & 0xff);
mStream->WriteUChar((inValue >> 8) & 0xff);
mStream->WriteUChar(inValue & 0xff);
return true;
}
bool CPrimitiveWriter::Write4ByteUnsigned(unsigned long inValue)
{
mStream->WriteUChar((inValue >> 24) & 0xff);
mStream->WriteUChar((inValue >> 16) & 0xff);
mStream->WriteUChar((inValue >> 8) & 0xff);
mStream->WriteUChar(inValue & 0xff);
return true;
}
bool CPrimitiveWriter::Write(const BYTE* inBuffer, unsigned long inBufferSize)
{
mStream->Write(inBuffer, inBufferSize);
return true;
}
bool CPrimitiveWriter::WriteDictOperand(const DictOperand& inOperand)
{
if (inOperand.IsInteger)
return WriteIntegerOperand(inOperand.IntegerValue);
else
return WriteRealOperand(inOperand.RealValue, inOperand.RealValueFractalEnd);
}
bool CPrimitiveWriter::WriteDictItems(unsigned short inOperator, const DictOperandList& inOperands)
{
bool status = true;
DictOperandList::const_iterator it = inOperands.begin();
for (; it != inOperands.end() && status; ++it)
status = WriteDictOperand(*it);
if (status)
status = WriteDictOperator(inOperator);
return status;
}
bool CPrimitiveWriter::WriteIntegerOperand(long inValue)
{
if (-107 <= inValue && inValue <= 107)
return WriteByte((BYTE)(inValue + 139));
else if (108 <= inValue && inValue <= 1131)
{
inValue -= 108;
WriteByte(((inValue >> 8) & 0xff) + 247);
WriteByte(inValue & 0xff);
}
else if (-1131 <= inValue && inValue <= -108)
{
inValue = -(inValue + 108);
WriteByte(((inValue >> 8) & 0xff) + 251);
WriteByte(inValue & 0xff);
}
else if (-32768 <= inValue && inValue<= 32767)
{
WriteByte(28);
WriteByte((inValue >> 8) & 0xff);
WriteByte(inValue & 0xff);
}
else // -2^31 <= inValue <= 2^31 - 1
return Write5ByteDictInteger(inValue);
return true;
}
bool CPrimitiveWriter::WriteRealOperand(double inValue, long inFractalLength)
{
// first, calculate the proper formatting
bool minusSign = inValue < 0;
bool minusExponent = false;
bool plusExponent = false;
unsigned short exponentSize = 0;
if (minusSign)
inValue = -inValue;
double integerValue = floor(inValue);
double fractalValue = inValue - integerValue;
if (0 == fractalValue)
{
if (long(integerValue) % 1000 == 0 && integerValue >= 1000) // bother only if > 1000
{
plusExponent = true;
while (long(integerValue) % 10 == 0)
{
++exponentSize;
integerValue = integerValue / 10;
}
}
}
else if (0 == integerValue)
{
if (fractalValue <= 0.001) // bother only if < 0.001
{
minusExponent = true;
while (fractalValue < 0.1)
{
++exponentSize;
fractalValue = fractalValue * 10;
}
}
}
// now let's get to work
if (!WriteByte(30))
return false;
// first, take care of minus sign
BYTE buffer = minusSign ? 0xe0 : 0;
bool usedFirst = minusSign;
// Integer part
if (integerValue != 0)
{
if (!WriteIntegerOfReal(integerValue, buffer, usedFirst))
return false;
}
else
{
if (!SetOrWriteNibble(0, buffer, usedFirst))
return false;
}
// Fractal part (if there was an integer or not)
if (fractalValue != 0 && inFractalLength > 0)
{
if (!SetOrWriteNibble(0xa, buffer, usedFirst))
return false;
while (fractalValue != 0 && inFractalLength > 0)
{
if (!SetOrWriteNibble((BYTE)floor(fractalValue * 10), buffer, usedFirst))
return false;
fractalValue = fractalValue * 10 - floor(fractalValue * 10);
--inFractalLength;
}
}
// now, if there's any exponent, write it
if (minusExponent)
{
if (!SetOrWriteNibble(0xc, buffer, usedFirst))
return false;
if (!WriteIntegerOfReal(exponentSize, buffer, usedFirst))
return false;
}
if (plusExponent)
{
if (!SetOrWriteNibble(0xb, buffer, usedFirst))
return false;
if (!WriteIntegerOfReal(exponentSize, buffer, usedFirst))
return false;
}
// final f or ff
if (usedFirst)
return SetOrWriteNibble(0xf, buffer, usedFirst);
else
return WriteByte(0xff);
}
bool CPrimitiveWriter::Write5ByteDictInteger(long inValue)
{
WriteByte(29);
WriteByte((inValue >> 24) & 0xff);
WriteByte((inValue >> 16)& 0xff);
WriteByte((inValue >> 8) & 0xff);
WriteByte(inValue & 0xff);
return true;
}
bool CPrimitiveWriter::WriteIntegerOfReal(double inIntegerValue, BYTE& ioBuffer, bool& ioUsedFirst)
{
if (0 == inIntegerValue)
return true;
bool status = WriteIntegerOfReal(floor(inIntegerValue / 10), ioBuffer, ioUsedFirst);
if (!status)
return false;
return SetOrWriteNibble((BYTE)(long(inIntegerValue) % 10), ioBuffer, ioUsedFirst);
}
bool CPrimitiveWriter::SetOrWriteNibble(BYTE inValue, BYTE& ioBuffer, bool& ioUsedFirst)
{
bool status = true;
if (ioUsedFirst)
{
ioBuffer |= inValue;
status = WriteByte(ioBuffer);
ioBuffer = 0;
ioUsedFirst = false;
}
else
{
ioBuffer = (inValue << 4) & 0xf0;
ioUsedFirst = true;
}
return status;
}
bool CPrimitiveWriter::WriteDictOperator(unsigned short inOperator)
{
if (((inOperator >> 8) & 0xff) == 12)
return WriteCard16(inOperator);
else
return WriteCard8((BYTE)(inOperator & 0xff));
}
bool CPrimitiveWriter::Pad5Bytes()
{
BYTE BytesPad5[5] = {'0','0','0','0','0'};
return Write(BytesPad5, 5);
}
bool CPrimitiveWriter::WriteSID(unsigned short inValue)
{
return WriteCard16(inValue);
}
//----------------------------------------------------------------------------------------
// CCFFReader
//----------------------------------------------------------------------------------------
struct CCFFReader
{
struct CFFHeader
{
BYTE major;
BYTE minor;
BYTE hdrSize;
BYTE offSize;
};
typedef std::map<std::string, unsigned short> StringToUShort;
enum ECharSetType
{
eCharSetISOAdobe = 0,
eCharSetExpert,
eCharSetExpertSubset,
eCharSetCustom
};
typedef std::map<unsigned short, CharString*> UShortToCharStringMap;
struct CharSetInfo
{
CharSetInfo() {mSIDs = NULL;}
ECharSetType mType;
UShortToCharStringMap mSIDToGlyphMap;
unsigned short* mSIDs; // count is like glyphs count
};
typedef std::vector<CharSetInfo*> CharSetInfoVector;
struct TopDictInfo
{
TopDictInfo()
{
mFDArray = NULL;
mFDSelect = NULL;
mCharSet = NULL;
mEncoding = NULL;
}
UShortToDictOperandListMap mTopDict;
CharSetInfo* mCharSet;
EncodingsInfo* mEncoding;
FontDictInfo* mFDArray;
FontDictInfo** mFDSelect; // size is like glyphsize. each cell references the relevant FontDict
};
typedef std::vector<EncodingsInfo*> EncodingsInfoVector;
struct StringLess
{
bool operator() (const char* left, const char* right) const
{
return strcmp(left, right) < 0;
}
};
typedef std::map<const char*, unsigned short, StringLess> CharPToUShortMap;
typedef std::map<long long, CharStrings*> LongFilePositionTypeToCharStringsMap;
typedef std::map<long long, CharSetInfo*> LongFilePositionTypeToCharSetInfoMap;
typedef std::map<long long, EncodingsInfo*> LongFilePositionTypeToEncodingsInfoMap;
long long mCFFOffset;
CFFHeader mHeader;
unsigned short mFontsCount;
std::list<std::string> mName;
TopDictInfo* mTopDictIndex; // count is same as fonts count
char** mStrings;
unsigned short mStringsCount;
long long mStringIndexPosition;
long long mGlobalSubrsPosition;
PrivateDictInfo* mPrivateDicts;
CharStrings mGlobalSubrs;
CharStrings* mCharStrings; // count is same as fonts count
LongFilePositionTypeToCharStringsMap mLocalSubrs; // count is NOT the same as fonts count [some may be shared, plus there might be more because of CID usage]
CharSetInfoVector mCharSets;// count is NOT the same as fonts count [some charsets may be shared]. consult the top dict charset pointer for the right charset
EncodingsInfoVector mEncodings; // count is NOT the same as fonts count [some encodinds may be shared].
CMemoryStream* mPrimitivesReader; // external, no need to free
StringToUShort mNameToIndex;
long long mNameIndexPosition;
long long mTopDictIndexPosition;
CharPToUShortMap mStringToSID;
CharString2Dependencies* mCurrentDependencies;
CharStrings* mCurrentLocalSubrs;
CharSetInfo* mCurrentCharsetInfo;
public:
CCFFReader();
~CCFFReader();
// parses the whole CFF file, with all contained fonts
bool ReadCFFFile(CMemoryStream* inCFFFile);
void FreeData();
void Reset();
bool ReadIntegerOperand(BYTE inFirstByte, long& outValue);
bool ReadRealOperand(double& outValue, long& outRealValueFractalEnd);
bool IsDictOperator(BYTE inCandidate);
bool ReadDictOperator(BYTE inFirstByte, unsigned short& outOperator);
bool ReadDictOperand(BYTE inFirstByte, DictOperand& outOperand);
bool ReadHeader();
bool ReadIndexHeader(unsigned long** outOffsets, unsigned short& outItemsCount);
bool ReadNameIndex();
bool ReadTopDictIndex();
bool ReadDict(unsigned long inReadAmount, UShortToDictOperandListMap& outDict);
bool ReadStringIndex();
bool ReadGlobalSubrs();
bool ReadSubrsFromIndex(unsigned short& outSubrsCount, CharStringsIndex* outSubrsIndex);
bool ReadCharStrings();
long long GetCharStringsPosition(unsigned short inFontIndex);
long GetSingleIntegerValue(unsigned short inFontIndex, unsigned short inKey, long inDefault);
long GetSingleIntegerValueFromDict(const UShortToDictOperandListMap& inDict, unsigned short inKey, long inDefault);
static const unsigned short scCharstringType = 0x0C06;
long GetCharStringType(unsigned short inFontIndex);
bool ReadPrivateDicts();
bool ReadPrivateDict(const UShortToDictOperandListMap& inReferencingDict, PrivateDictInfo* outPrivateDict);
bool ReadLocalSubrs();
bool ReadLocalSubrsForPrivateDict(PrivateDictInfo* inPrivateDict, BYTE inCharStringType);
bool ReadCharsets();
bool ReadEncodings();
void ReadEncoding(EncodingsInfo* inEncoding, long long inEncodingPosition);
void SetupSIDToGlyphMapWithStandard(const unsigned short* inStandardCharSet, unsigned short inStandardCharSetLength, UShortToCharStringMap& ioCharMap, const CharStrings& inCharStrings);
bool ReadFormat0Charset(bool inIsCID, UShortToCharStringMap& ioGlyphMap, unsigned short** inSIDArray, const CharStrings& inCharStrings);
bool ReadFormat1Charset(bool inIsCID, UShortToCharStringMap& ioGlyphMap, unsigned short** inSIDArray, const CharStrings& inCharStrings);
bool ReadFormat2Charset(bool inIsCID, UShortToCharStringMap& ioGlyphMap, unsigned short** inSIDArray, const CharStrings& inCharStrings);
long long GetCharsetPosition(unsigned short inFontIndex);
long long GetEncodingPosition(unsigned short inFontIndex);
bool ReadCIDInformation();
bool ReadFDArray(unsigned short inFontIndex);
long long GetFDArrayPosition(unsigned short inFontIndex);
bool ReadFDSelect(unsigned short inFontIndex);
long long GetFDSelectPosition(unsigned short inFontIndex);
unsigned short GetGlyphSID(unsigned short inFontIndex, unsigned short inGlyphIndex);
bool PrepareForGlyphIntepretation(unsigned short inFontIndex, unsigned short inCharStringIndex);
CharString* GetGlyphCharString(unsigned short inFontIndex, unsigned short inCharStringIndex);
bool ReadCharString(long long inCharStringStart, long long inCharStringEnd, BYTE** outCharString);
CharString* GetLocalSubr(long inSubrIndex);
unsigned short GetBiasedIndex(unsigned short inSubroutineCollectionSize, long inSubroutineIndex);
CharString* GetGlobalSubr(long inSubrIndex);
};
CCFFReader::CCFFReader()
{
mTopDictIndex = NULL;
mStrings = NULL;
mGlobalSubrs.mCharStringsIndex = NULL;
mCharStrings = NULL;
mPrivateDicts = NULL;
mPrimitivesReader = NULL;
}
CCFFReader::~CCFFReader()
{
FreeData();
}
void CCFFReader::FreeData()
{
mName.clear();
mNameToIndex.clear();
if (mTopDictIndex != NULL)
{
for (unsigned int i = 0; i < mFontsCount; ++i)
{
delete[] mTopDictIndex[i].mFDArray;
delete[] mTopDictIndex[i].mFDSelect;
}
RELEASEARRAYOBJECTS(mTopDictIndex);
}
if (mStrings != NULL)
{
for (unsigned short i = 0; i < mStringsCount; ++i)
delete[] mStrings[i];
RELEASEARRAYOBJECTS(mStrings);
}
mStringToSID.clear();
RELEASEARRAYOBJECTS(mGlobalSubrs.mCharStringsIndex);
if (mCharStrings != NULL)
{
for (unsigned int i = 0; i < mFontsCount; ++i)
delete[] mCharStrings[i].mCharStringsIndex;
RELEASEARRAYOBJECTS(mCharStrings);
}
RELEASEARRAYOBJECTS(mPrivateDicts);
LongFilePositionTypeToCharStringsMap::iterator itLocalSubrs = mLocalSubrs.begin();
for (; itLocalSubrs != mLocalSubrs.end(); ++itLocalSubrs)
{
delete[] itLocalSubrs->second->mCharStringsIndex;
delete itLocalSubrs->second;
}
CharSetInfoVector::iterator itCharSets = mCharSets.begin();
for (; itCharSets != mCharSets.end(); ++itCharSets)
{
delete[] (*itCharSets)->mSIDs;
(*itCharSets)->mSIDToGlyphMap.clear();
delete (*itCharSets);
}
mCharSets.clear();
EncodingsInfoVector::iterator itEncodings = mEncodings.begin();
for (; itEncodings != mEncodings.end(); ++itEncodings)
{
delete[] (*itEncodings)->mEncoding;
delete (*itEncodings);
}
mEncodings.clear();
RELEASEOBJECT(mPrimitivesReader);
}
void CCFFReader::Reset()
{
FreeData();
}
bool CCFFReader::ReadIntegerOperand(BYTE inFirstByte, long& outValue)
{
BYTE byte0, byte1;
bool status = true;
byte0 = inFirstByte;
if (byte0 >= 32 && byte0 <= 246)
{
outValue = (long)byte0 - 139;
}
else if (byte0 >= 247 && byte0 <= 250)
{
if (mPrimitivesReader->IsEof())
return false;
byte1 = mPrimitivesReader->ReadUChar();
outValue = (byte0 - 247) * 256 + byte1 + 108;
}
else if (byte0 >= 251 && byte0 <= 254)
{
if (mPrimitivesReader->IsEof())
return false;
byte1 = mPrimitivesReader->ReadUChar();
outValue = -(long)((long)byte0 - 251) * 256 - byte1 - 108;
}
else if (28 == byte0)
{
if (mPrimitivesReader->IsEof())
return false;
outValue = mPrimitivesReader->ReadUShort();
}
else if (29 == byte0)
{
if (mPrimitivesReader->IsEof())
return false;
outValue = mPrimitivesReader->ReadUInt();
}
else
status = false;
return status;
}
bool CCFFReader::ReadRealOperand(double& outValue, long& outRealValueFractalEnd)
{
double integerPart = 0;
double fractionPart = 0;
double powerPart = 0;
double result;
bool hasNegative = false;
bool hasFraction = false;
bool hasPositivePower = false;
bool hasNegativePower = false;
bool notDone = true;
double fractionDecimal = 1;
outRealValueFractalEnd = 0;
BYTE buffer;
BYTE nibble[2];
bool status = true;
do
{
if (mPrimitivesReader->IsEof())
return false;
buffer = mPrimitivesReader->ReadUChar();
nibble[0] = (buffer >> 4) & 0xf;
nibble[1] = buffer & 0xf;
for (int i = 0; i < 2; ++i)
{
switch (nibble[i])
{
case 0xa:
hasFraction = true;
break;
case 0xb:
hasPositivePower = true;
break;
case 0xc:
hasNegativePower = true;
break;
case 0xd:
// reserved
break;
case 0xe:
hasNegative = true;
break;
case 0xf:
notDone = false;
break;
default: // numbers
if (hasPositivePower || hasNegativePower)
{
powerPart = powerPart * 10 + nibble[i];
}
else if (hasFraction)
{
fractionPart = fractionPart * 10 + nibble[i];
fractionDecimal *= 10;
++outRealValueFractalEnd;
}
else
integerPart = integerPart * 10 + nibble[i];
}
}
} while(notDone);
if (status)
{
result = integerPart + fractionPart / fractionDecimal;
if (hasNegativePower || hasPositivePower)
result = result * pow(10, hasNegativePower ? -powerPart : powerPart);
if (hasNegative)
result = -result;
outValue = result;
}
return status;
}
bool CCFFReader::IsDictOperator(BYTE inCandidate)
{
return (inCandidate <= 27 || 31 == inCandidate);
}
bool CCFFReader::ReadDictOperator(BYTE inFirstByte, unsigned short& outOperator)
{
if (12 == inFirstByte)
{
if (!mPrimitivesReader->IsEof())
{
BYTE buffer = mPrimitivesReader->ReadUChar();
outOperator = ((unsigned short)inFirstByte << 8) | buffer;
return true;
}
return false;
}
outOperator = inFirstByte;
return true;
}
bool CCFFReader::ReadDictOperand(BYTE inFirstByte, DictOperand& outOperand)
{
if (30 == inFirstByte) // real
{
outOperand.IsInteger = false;
return ReadRealOperand(outOperand.RealValue, outOperand.RealValueFractalEnd);
}
else if (28 == inFirstByte ||
29 == inFirstByte ||
(32 <= inFirstByte && inFirstByte <= 246) ||
(247 <= inFirstByte && inFirstByte <= 250) ||
(251 <= inFirstByte && inFirstByte <= 254))
{
outOperand.IsInteger = true;
return ReadIntegerOperand(inFirstByte, outOperand.IntegerValue);
}
return false; // not an operand
}
bool CCFFReader::ReadCFFFile(CMemoryStream* inCFFFile)
{
FreeData();
mCFFOffset = inCFFFile->Tell();
mPrimitivesReader = new CMemoryStream(inCFFFile->GetCurBuffer(), inCFFFile->Size() - inCFFFile->Tell());
mPrimitivesReader->Seek(0, SeekSet);
bool status = ReadHeader();
if (!status)
return false;
if (mHeader.hdrSize > 4)
mPrimitivesReader->Seek(mHeader.hdrSize - 4, SeekCur);
status = ReadNameIndex();
if (!status)
return false;
status = ReadTopDictIndex();
if (!status)
return false;
status = ReadStringIndex();
if (!status)
return false;
status = ReadGlobalSubrs();
if (!status)
return false;
status = ReadCharStrings();
if (!status)
return false;
status = ReadPrivateDicts();
if (!status)
return false;
status = ReadLocalSubrs();
if (!status)
return false;
status = ReadCharsets();
if (!status)
return false;
status = ReadEncodings();
if (!status)
return false;
status = ReadCIDInformation();
if (!status)
return false;
return true;
}
bool CCFFReader::ReadHeader()
{
mHeader.major = mPrimitivesReader->ReadUChar();
mHeader.minor = mPrimitivesReader->ReadUChar();
mHeader.hdrSize = mPrimitivesReader->ReadUChar();
mHeader.offSize = mPrimitivesReader->ReadUChar();
return true;
}
bool CCFFReader::ReadIndexHeader(unsigned long** outOffsets, unsigned short& outItemsCount)
{
outItemsCount = mPrimitivesReader->ReadUShort();
if (0 == outItemsCount)
{
*outOffsets = NULL;
return true;
}
if (mPrimitivesReader->IsEof())
return false;
BYTE offSizeForIndex = mPrimitivesReader->ReadUChar();
*outOffsets = new unsigned long[outItemsCount + 1];
for (unsigned int i = 0; i <= outItemsCount && !mPrimitivesReader->IsEof(); ++i)
(*outOffsets)[i] = mPrimitivesReader->ReadOffset(offSizeForIndex);
return true;
}
bool CCFFReader::ReadNameIndex()
{
mNameIndexPosition = mPrimitivesReader->Tell();
unsigned long* offsets = NULL;
bool status = ReadIndexHeader(&offsets, mFontsCount);
if (!status || !offsets)
return false;
if (offsets[0] != 1)
mPrimitivesReader->Seek(offsets[0] - 1, SeekCur);
BYTE* buffer;
for (unsigned int i = 0; i < mFontsCount; ++i)
{
unsigned int nLength = offsets[i + 1] - offsets[i];
buffer = new BYTE[nLength];
mPrimitivesReader->Read(buffer, &nLength);
std::string aName((char*)buffer, nLength);
mName.push_back(aName);
if (buffer[0] != 0) // put in map only valid names
mNameToIndex.insert(StringToUShort::value_type(aName, i));
delete[] buffer;
}
delete[] offsets;
return true;
}
bool CCFFReader::ReadTopDictIndex()
{
mTopDictIndexPosition = mPrimitivesReader->Tell();
unsigned long* offsets;
unsigned short dictionariesCount;
bool status = ReadIndexHeader(&offsets, dictionariesCount);
if (!status || !offsets)
return false;
if (offsets[0] != 1)
mPrimitivesReader->Seek(offsets[0] - 1, SeekCur);
mTopDictIndex = new TopDictInfo[dictionariesCount];
for (unsigned int i = 0; i < dictionariesCount && status; ++i)
status = ReadDict(offsets[i + 1] - offsets[i], mTopDictIndex[i].mTopDict);
delete[] offsets;
return status;
}
bool CCFFReader::ReadDict(unsigned long inReadAmount, UShortToDictOperandListMap& outDict)
{
long long dictStartPosition = mPrimitivesReader->Tell();
DictOperandList operands;
bool status = true;
unsigned short anOperator;
DictOperand anOperand;
BYTE aBuffer;
while (status && (mPrimitivesReader->Tell() - dictStartPosition < (long long)inReadAmount))
{
aBuffer = mPrimitivesReader->ReadUChar();
if (IsDictOperator(aBuffer))
{ // operator
status = ReadDictOperator(aBuffer, anOperator);
if (!status)
break;
outDict.insert(UShortToDictOperandListMap::value_type(anOperator, operands));
operands.clear();
}
else // operand
{
status = ReadDictOperand(aBuffer, anOperand);
if (!status)
break;
operands.push_back(anOperand);
}
}
return status;
}
bool CCFFReader::ReadStringIndex()
{
mStringIndexPosition = mPrimitivesReader->Tell();
unsigned long* offsets;
bool status = ReadIndexHeader(&offsets, mStringsCount);
if (!status)
return false;
if (0 == mStringsCount)
{
mStrings = NULL;
return true;
}
if (offsets[0] != 1)
mPrimitivesReader->Seek(offsets[0] - 1, SeekCur);
mStrings = new char*[mStringsCount];
unsigned long i;
for (i = 0; i < mStringsCount && status; ++i)
{
unsigned int nLength = offsets[i + 1] - offsets[i];
mStrings[i] = new char[nLength + 1];
if (mPrimitivesReader->IsEof())
{
status = false;
break;
}
mPrimitivesReader->Read((BYTE*)mStrings[i], &nLength);
mStrings[i][nLength] = 0;
}
// failure case, null all the rest of the strings for later delete to not perofrm errors
if (!status)
{
for (; i < mStringsCount; ++i)
mStrings[i] = NULL;
}
// now create the string to SID map
for (i = 0; i < N_STD_STRINGS; ++i)
mStringToSID.insert(CharPToUShortMap::value_type(scStandardStrings[i], i));
for (; i < N_STD_STRINGS + mStringsCount; ++i)
mStringToSID.insert(CharPToUShortMap::value_type(mStrings[i - N_STD_STRINGS], i));
delete[] offsets;
return status;
}
bool CCFFReader::ReadGlobalSubrs()
{
mGlobalSubrsPosition = mPrimitivesReader->Tell();
mGlobalSubrs.mCharStringsType = 2; // always 2
return ReadSubrsFromIndex(mGlobalSubrs.mCharStringsCount, &(mGlobalSubrs.mCharStringsIndex));
}
bool CCFFReader::ReadSubrsFromIndex(unsigned short& outSubrsCount, CharStringsIndex* outSubrsIndex)
{
unsigned long* offsets;
bool status = ReadIndexHeader(&offsets, outSubrsCount);
if (!status)
return false;
if (0 == outSubrsCount)
{
*outSubrsIndex = NULL;
return true;
}
// just taking a snapshot of positions here
*outSubrsIndex = new CharString[outSubrsCount];
long long dataStartPosition = mPrimitivesReader->Tell();
for (unsigned int i = 0; i < outSubrsCount; ++i)
{
(*outSubrsIndex)[i].mStartPosition = dataStartPosition + offsets[i] - 1;
(*outSubrsIndex)[i].mEndPosition = dataStartPosition + offsets[i + 1] - 1;
(*outSubrsIndex)[i].mIndex = i;
}
// for good faith put the pointer at the end now (if someone wants to take a snapshot)
mPrimitivesReader->Seek(offsets[outSubrsCount] - 1, SeekCur);
delete[] offsets;
return true;
}
bool CCFFReader::ReadCharStrings()
{
// scan all charstrings of all included fonts
mCharStrings = new CharStrings[mFontsCount];
bool status = true;
for (unsigned int i = 0; i < mFontsCount && status; ++i)
{
long long charStringsPosition = GetCharStringsPosition(i);
mCharStrings[i].mCharStringsType = (BYTE)GetCharStringType(i);
if (0 == charStringsPosition)
{
mCharStrings[i].mCharStringsCount = 0;
mCharStrings[i].mCharStringsIndex = NULL;
}
else
{
mPrimitivesReader->Seek(charStringsPosition, SeekSet);
status = ReadSubrsFromIndex(mCharStrings[i].mCharStringsCount, &(mCharStrings[i].mCharStringsIndex));
}
}
return status;
}
long long CCFFReader::GetCharStringsPosition(unsigned short inFontIndex)
{
return GetSingleIntegerValue(inFontIndex, scCharStrings, 0);
}
long CCFFReader::GetSingleIntegerValue(unsigned short inFontIndex, unsigned short inKey, long inDefault)
{
return GetSingleIntegerValueFromDict(mTopDictIndex[inFontIndex].mTopDict, inKey, inDefault);
}
long CCFFReader::GetSingleIntegerValueFromDict(const UShortToDictOperandListMap& inDict, unsigned short inKey, long inDefault)
{
UShortToDictOperandListMap::const_iterator it = inDict.find(inKey);
if (it != inDict.end())
return it->second.front().IntegerValue;
return inDefault;
}
long CCFFReader::GetCharStringType(unsigned short inFontIndex)
{
return GetSingleIntegerValue(inFontIndex, scCharstringType, 2);
}
bool CCFFReader::ReadPrivateDicts()
{
mPrivateDicts = new PrivateDictInfo[mFontsCount];
bool status = true;
for (unsigned int i = 0; i < mFontsCount && status; ++i)
status = ReadPrivateDict(mTopDictIndex[i].mTopDict, mPrivateDicts + i);
return status;
}
bool CCFFReader::ReadPrivateDict(const UShortToDictOperandListMap& inReferencingDict, PrivateDictInfo* outPrivateDict)
{
bool status = true;
UShortToDictOperandListMap::const_iterator it = inReferencingDict.find(scPrivate);
outPrivateDict->mLocalSubrs = NULL;
if (it == inReferencingDict.end())
{
outPrivateDict->mPrivateDictStart = 0;
outPrivateDict->mPrivateDictEnd = 0;
}
else
{
outPrivateDict->mPrivateDictStart = (long long)it->second.back().IntegerValue;
outPrivateDict->mPrivateDictEnd = (long long)(it->second.back().IntegerValue + it->second.front().IntegerValue);
mPrimitivesReader->Seek(it->second.back().IntegerValue, SeekSet);
status = ReadDict(it->second.front().IntegerValue, outPrivateDict->mPrivateDict);
}
return status;
}
bool CCFFReader::ReadLocalSubrs()
{
// scan all subrs of all included fonts
bool status = true;
for (unsigned int i = 0; i < mFontsCount && status; ++i)
status = ReadLocalSubrsForPrivateDict(mPrivateDicts + i, (BYTE)GetCharStringType(i));
return status;
}
bool CCFFReader::ReadLocalSubrsForPrivateDict(PrivateDictInfo* inPrivateDict, BYTE inCharStringType)
{
bool status = true;
long long subrsPosition = GetSingleIntegerValueFromDict(inPrivateDict->mPrivateDict, scSubrs, 0);
if (0 == subrsPosition)
{
inPrivateDict->mLocalSubrs = NULL;
}
else
{
LongFilePositionTypeToCharStringsMap::iterator it = mLocalSubrs.find(inPrivateDict->mPrivateDictStart + subrsPosition);
if (it == mLocalSubrs.end())
{
CharStrings* charStrings = new CharStrings();
charStrings->mCharStringsType = inCharStringType;
mPrimitivesReader->Seek(inPrivateDict->mPrivateDictStart + subrsPosition, SeekSet);
status = ReadSubrsFromIndex(charStrings->mCharStringsCount, &(charStrings->mCharStringsIndex));
if (status)
it = mLocalSubrs.insert(LongFilePositionTypeToCharStringsMap::value_type(inPrivateDict->mPrivateDictStart + subrsPosition, charStrings)).first;
}
inPrivateDict->mLocalSubrs = it->second;
}
return status;
}
bool CCFFReader::ReadCharsets()
{
// read all charsets
bool status = true;
LongFilePositionTypeToCharSetInfoMap offsetToIndex;
LongFilePositionTypeToCharSetInfoMap::iterator it;
for (unsigned int i = 0; i < mFontsCount && status; ++i)
{
long long charsetPosition = GetCharsetPosition(i);
it = offsetToIndex.find(charsetPosition);
if (it == offsetToIndex.end())
{
CharSetInfo* charSet = new CharSetInfo();
bool isCID = mTopDictIndex[i].mTopDict.find(scROS) != mTopDictIndex[i].mTopDict.end();
if (charsetPosition <= 2)
{
charSet->mType = (ECharSetType)charsetPosition;
charSet->mSIDs = NULL;
if (!isCID) // collect SID->Glyph map only if not CID, in which case SIDs are CIDs...and what i'm using the map for is irrelevant
SetupSIDToGlyphMapWithStandard(scDefaultCharsets[charsetPosition], scDefaultCharsetsSizes[charsetPosition], charSet->mSIDToGlyphMap, mCharStrings[i]);
}
else
{
BYTE charsetFormat;
charSet->mType = eCharSetCustom;
mPrimitivesReader->Seek((int)charsetPosition, SeekSet);
charsetFormat = mPrimitivesReader->ReadUChar();
if (0 == charsetFormat)
status = ReadFormat0Charset(isCID, charSet->mSIDToGlyphMap, &charSet->mSIDs, mCharStrings[i]);
else if (1 == charsetFormat)
status = ReadFormat1Charset(isCID, charSet->mSIDToGlyphMap, &charSet->mSIDs, mCharStrings[i]);
else // 2 charset format
status = ReadFormat2Charset(isCID, charSet->mSIDToGlyphMap, &charSet->mSIDs, mCharStrings[i]);
}
mCharSets.push_back(charSet);
it = offsetToIndex.insert(LongFilePositionTypeToCharSetInfoMap::value_type(charsetPosition, charSet)).first;
}
mTopDictIndex[i].mCharSet = it->second;
}
return status;
}
bool CCFFReader::ReadEncodings()
{
// read all encodings positions
bool status = true;
LongFilePositionTypeToEncodingsInfoMap offsetToEncoding;
LongFilePositionTypeToEncodingsInfoMap::iterator it;
for (unsigned int i = 0; i < mFontsCount && status; ++i)
{
long long encodingPosition = GetEncodingPosition(i);
it = offsetToEncoding.find(encodingPosition);
if (it == offsetToEncoding.end())
{
EncodingsInfo* encoding = new EncodingsInfo();
ReadEncoding(encoding, encodingPosition);
mEncodings.push_back(encoding);
it = offsetToEncoding.insert(LongFilePositionTypeToEncodingsInfoMap::value_type(encodingPosition, encoding)).first;
}
mTopDictIndex[i].mEncoding = it->second;
}
return status;
}
void CCFFReader::ReadEncoding(EncodingsInfo* inEncoding, long long inEncodingPosition)
{
if (inEncodingPosition <= 1)
{
inEncoding->mEncodingStart = inEncoding->mEncodingEnd = inEncodingPosition;
inEncoding->mType = (EEncodingType)inEncodingPosition;
return;
}
inEncoding->mType = eEncodingCustom;
inEncoding->mEncodingStart = inEncodingPosition;
mPrimitivesReader->Seek(inEncodingPosition, SeekSet);
BYTE encodingFormat = mPrimitivesReader->ReadUChar();
if (0 == (encodingFormat & 0x1))
{
inEncoding->mEncodingsCount = mPrimitivesReader->ReadUChar();
if (inEncoding->mEncodingsCount > 0)
{
inEncoding->mEncoding = new BYTE[inEncoding->mEncodingsCount];
for (BYTE i = 0; i < inEncoding->mEncodingsCount; ++i)
inEncoding->mEncoding[i] = mPrimitivesReader->ReadUChar();
}
}
else // format = 1
{
BYTE rangesCount = mPrimitivesReader->ReadUChar();
if (rangesCount > 0)
{
BYTE firstCode;
BYTE left;
inEncoding->mEncodingsCount = 0;
// get the encoding count (yap, reading twice here)
for (BYTE i = 0; i < rangesCount; ++i)
{
firstCode = mPrimitivesReader->ReadUChar();
left = mPrimitivesReader->ReadUChar();
inEncoding->mEncodingsCount += left;
}
inEncoding->mEncoding = new BYTE[inEncoding->mEncodingsCount];
mPrimitivesReader->Seek(inEncodingPosition + 2, SeekSet); // reset encoding to beginning of range reading
// now read the encoding array
BYTE encodingIndex = 0;
for (BYTE i = 0; i < rangesCount; ++i)
{
firstCode = mPrimitivesReader->ReadUChar();
left = mPrimitivesReader->ReadUChar();
for (BYTE j = 0;j < left; ++j)
inEncoding->mEncoding[encodingIndex + j] = firstCode + j;
encodingIndex += left;
}
}
}
if ((encodingFormat & 0x80) != 0) // supplaments exist, need to add to encoding end
{
mPrimitivesReader->Seek(inEncoding->mEncodingEnd, SeekSet); // set position to end of encoding, and start of supplamental, so that can read their count
BYTE supplamentalsCount = mPrimitivesReader->ReadUChar();
if (supplamentalsCount > 0)
{
BYTE encoding;
unsigned short SID;
for (BYTE i = 0; i < supplamentalsCount; ++i)
{
encoding = mPrimitivesReader->ReadUChar();
SID = mPrimitivesReader->ReadUShort();
UShortToByteList::iterator it = inEncoding->mSupplements.find(SID);
if (it == inEncoding->mSupplements.end())
it = inEncoding->mSupplements.insert(UShortToByteList::value_type(SID, ByteList())).first;
it->second.push_back(encoding);
}
}
}
inEncoding->mEncodingEnd = mPrimitivesReader->Tell();
}
void CCFFReader::SetupSIDToGlyphMapWithStandard(const unsigned short* inStandardCharSet, unsigned short inStandardCharSetLength, UShortToCharStringMap& ioCharMap, const CharStrings& inCharStrings)
{
ioCharMap.insert(UShortToCharStringMap::value_type(0, inCharStrings.mCharStringsIndex));
for (unsigned int i = 1; i < inCharStrings.mCharStringsCount && i < inStandardCharSetLength;++i)
ioCharMap.insert(UShortToCharStringMap::value_type(inStandardCharSet[i], inCharStrings.mCharStringsIndex + i));
}
bool CCFFReader::ReadFormat0Charset(bool inIsCID, UShortToCharStringMap& ioGlyphMap, unsigned short** inSIDArray, const CharStrings& inCharStrings)
{
// for CIDs don't bother filling up the SID->glyph map. it ain't SIDs
if (!inIsCID)
ioGlyphMap.insert(UShortToCharStringMap::value_type(0, inCharStrings.mCharStringsIndex));
*inSIDArray = new unsigned short[inCharStrings.mCharStringsCount];
(*inSIDArray)[0] = 0;
if (inIsCID)
{
for (unsigned int i = 1; i < inCharStrings.mCharStringsCount; ++i)
(*inSIDArray)[i] = mPrimitivesReader->ReadUShort();
}
else
{
for (unsigned int i = 1; i < inCharStrings.mCharStringsCount; ++i)
{
unsigned short sid = mPrimitivesReader->ReadUShort();
(*inSIDArray)[i] = sid;
ioGlyphMap.insert(UShortToCharStringMap::value_type(sid, inCharStrings.mCharStringsIndex + i));
}
}
return true;
}
bool CCFFReader::ReadFormat1Charset(bool inIsCID, UShortToCharStringMap& ioGlyphMap, unsigned short** inSIDArray, const CharStrings& inCharStrings)
{
if (!inIsCID)
ioGlyphMap.insert(UShortToCharStringMap::value_type(0, inCharStrings.mCharStringsIndex));
*inSIDArray = new unsigned short[inCharStrings.mCharStringsCount];
(*inSIDArray)[0] = 0;
unsigned long glyphIndex = 1;
unsigned short sid;
BYTE left;
if (inIsCID)
{
while (glyphIndex < inCharStrings.mCharStringsCount)
{
sid = mPrimitivesReader->ReadUShort();
left = mPrimitivesReader->ReadUChar();
for (BYTE i = 0; i <= left && glyphIndex < inCharStrings.mCharStringsCount; ++i, ++glyphIndex)
(*inSIDArray)[glyphIndex] = sid + i;
}
}
else
{
while (glyphIndex < inCharStrings.mCharStringsCount)
{
sid = mPrimitivesReader->ReadUShort();
left = mPrimitivesReader->ReadUChar();
for (BYTE i = 0; i <= left && glyphIndex < inCharStrings.mCharStringsCount; ++i, ++glyphIndex)
{
ioGlyphMap.insert(UShortToCharStringMap::value_type(sid + i, inCharStrings.mCharStringsIndex + glyphIndex));
(*inSIDArray)[glyphIndex] = sid + i;
}
}
}
return true;
}
bool CCFFReader::ReadFormat2Charset(bool inIsCID, UShortToCharStringMap& ioGlyphMap, unsigned short** inSIDArray, const CharStrings& inCharStrings)
{
if (!inIsCID)
ioGlyphMap.insert(UShortToCharStringMap::value_type(0, inCharStrings.mCharStringsIndex));
*inSIDArray = new unsigned short[inCharStrings.mCharStringsCount];
(*inSIDArray)[0] = 0;
unsigned short glyphIndex = 1;
unsigned short sid;
unsigned short left;
if (inIsCID)
{
while (glyphIndex < inCharStrings.mCharStringsCount)
{
sid = mPrimitivesReader->ReadUShort();
left = mPrimitivesReader->ReadUShort();
for (unsigned int i = 0; i <= left && glyphIndex < inCharStrings.mCharStringsCount; ++i, ++glyphIndex)
(*inSIDArray)[glyphIndex] = sid + i;
}
}
else
{
while (glyphIndex < inCharStrings.mCharStringsCount)
{
sid = mPrimitivesReader->ReadUShort();
left = mPrimitivesReader->ReadUShort();
for (unsigned int i = 0; i <= left && glyphIndex < inCharStrings.mCharStringsCount; ++i ,++glyphIndex)
{
ioGlyphMap.insert(UShortToCharStringMap::value_type(sid + i, inCharStrings.mCharStringsIndex + glyphIndex));
(*inSIDArray)[glyphIndex] = sid + i;
}
}
}
return true;
}
long long CCFFReader::GetCharsetPosition(unsigned short inFontIndex)
{
return (long long)GetSingleIntegerValue(inFontIndex, scCharset, 0);
}
long long CCFFReader::GetEncodingPosition(unsigned short inFontIndex)
{
return (long long)GetSingleIntegerValue(inFontIndex, scEncoding, 0);
}
bool CCFFReader::ReadCIDInformation()
{
bool status = true;
for (unsigned int i = 0; i < mFontsCount && status; ++i)
{
// CID font will be identified by the existance of the ROS entry
if (mTopDictIndex[i].mTopDict.find(scROS) != mTopDictIndex[i].mTopDict.end())
{
status = ReadFDArray(i);
if (!status)
break;
status = ReadFDSelect(i);
if (!status)
break;
}
}
return status;
}
bool CCFFReader::ReadFDArray(unsigned short inFontIndex)
{
long long fdArrayLocation = GetFDArrayPosition(inFontIndex);
// supposed to get here only for CIDs. and they must have an FDArray...so if it doesn't - fail
if (0 == fdArrayLocation)
return false;
mPrimitivesReader->Seek(fdArrayLocation, SeekSet);
unsigned long* offsets;
unsigned short dictionariesCount;
unsigned short i;
bool status = ReadIndexHeader(&offsets, dictionariesCount);
if (!status)
return false;
if (offsets[0] != 1)
mPrimitivesReader->Seek(offsets[0] - 1, SeekCur);
mTopDictIndex[inFontIndex].mFDArray = new FontDictInfo[dictionariesCount];
for (i = 0; i < dictionariesCount && status; ++i)
{
mTopDictIndex[inFontIndex].mFDArray[i].mFontDictStart = mPrimitivesReader->Tell();
status = ReadDict(offsets[i + 1] - offsets[i], mTopDictIndex[inFontIndex].mFDArray[i].mFontDict);
if (!status)
break;
mTopDictIndex[inFontIndex].mFDArray[i].mFontDictEnd = mPrimitivesReader->Tell();
}
// another loop for reading the privates [should be one per font dict]. make sure to get their font subrs reference right
for (i = 0; i < dictionariesCount && status; ++i)
{
status = ReadPrivateDict(mTopDictIndex[inFontIndex].mFDArray[i].mFontDict, &(mTopDictIndex[inFontIndex].mFDArray[i].mPrivateDict));
if (status)
status = ReadLocalSubrsForPrivateDict(&(mTopDictIndex[inFontIndex].mFDArray[i].mPrivateDict), (BYTE)GetCharStringType(inFontIndex));
}
delete[] offsets;
return status;
}
long long CCFFReader::GetFDArrayPosition(unsigned short inFontIndex)
{
return GetSingleIntegerValue(inFontIndex, scFDArray, 0);
}
bool CCFFReader::ReadFDSelect(unsigned short inFontIndex)
{
long long fdSelectLocation = GetFDSelectPosition(inFontIndex);
unsigned short glyphCount = mCharStrings[inFontIndex].mCharStringsCount;
bool status = true;
// supposed to get here only for CIDs. and they must have an FDSelect...so if it doesn't - fail
if (0 == fdSelectLocation)
return false;
mTopDictIndex[inFontIndex].mFDSelect = new FontDictInfo*[glyphCount];
mPrimitivesReader->Seek(fdSelectLocation, SeekSet);
BYTE format = mPrimitivesReader->ReadUChar();
if (0 == format)
{
BYTE fdIndex;
for (unsigned int i = 0; i < glyphCount && !mPrimitivesReader->IsEof(); ++i)
{
fdIndex = mPrimitivesReader->ReadUChar();
if (!mPrimitivesReader->IsEof())
mTopDictIndex[inFontIndex].mFDSelect[i] = mTopDictIndex[inFontIndex].mFDArray + fdIndex;
}
}
else // format 3
{
unsigned short rangesCount;
unsigned short firstGlyphIndex;
unsigned short nextRangeGlyphIndex;
BYTE fdIndex;
rangesCount = mPrimitivesReader->ReadUShort();
if (!mPrimitivesReader->IsEof())
{
firstGlyphIndex = mPrimitivesReader->ReadUShort();
for (unsigned int i = 0; i < rangesCount && !mPrimitivesReader->IsEof(); ++i)
{
fdIndex = mPrimitivesReader->ReadUChar();
nextRangeGlyphIndex = mPrimitivesReader->ReadUShort();
if (!mPrimitivesReader->IsEof())
for (unsigned int j = firstGlyphIndex; j < nextRangeGlyphIndex; ++j)
mTopDictIndex[inFontIndex].mFDSelect[j] = mTopDictIndex[inFontIndex].mFDArray + fdIndex;
firstGlyphIndex = nextRangeGlyphIndex;
}
}
}
return status;
}
long long CCFFReader::GetFDSelectPosition(unsigned short inFontIndex)
{
return GetSingleIntegerValue(inFontIndex, scFDSelect, 0);
}
unsigned short CCFFReader::GetGlyphSID(unsigned short inFontIndex, unsigned short inGlyphIndex)
{
if (inFontIndex >= mFontsCount || inGlyphIndex >= mCharStrings[inFontIndex].mCharStringsCount)
{
return 0;
}
else
{
unsigned short sid;
if (0 == inGlyphIndex)
{
sid = 0;
}
else
{
if (eCharSetCustom == mTopDictIndex[inFontIndex].mCharSet->mType)
{
sid = mTopDictIndex[inFontIndex].mCharSet->mSIDs[inGlyphIndex];
}
else
{
// SID 0 is omitted for the default charsets
sid = scDefaultCharsets[(BYTE)mTopDictIndex[inFontIndex].mCharSet->mType][inGlyphIndex - 1];
}
}
return sid;
}
}
bool CCFFReader::PrepareForGlyphIntepretation(unsigned short inFontIndex, unsigned short inCharStringIndex)
{
if (inFontIndex >= mFontsCount)
return false;
if (mCharStrings[inFontIndex].mCharStringsCount <= inCharStringIndex)
return false;
if (2 == mCharStrings[inFontIndex].mCharStringsType)
{
if (mTopDictIndex[inFontIndex].mFDSelect) // CIDs have FDSelect
{
mCurrentLocalSubrs = mTopDictIndex[inFontIndex].mFDSelect[inCharStringIndex]->mPrivateDict.mLocalSubrs;
mCurrentCharsetInfo = mTopDictIndex[inFontIndex].mCharSet;
mCurrentDependencies = NULL;
}
else
{
mCurrentLocalSubrs = mPrivateDicts[inFontIndex].mLocalSubrs;
mCurrentCharsetInfo = mTopDictIndex[inFontIndex].mCharSet;
mCurrentDependencies = NULL;
}
return true;
}
return false;
}
CharString* CCFFReader::GetGlyphCharString(unsigned short inFontIndex, unsigned short inCharStringIndex)
{
if (inFontIndex >= mFontsCount)
return NULL;
if (mCharStrings[inFontIndex].mCharStringsCount <= inCharStringIndex)
return NULL;
return mCharStrings[inFontIndex].mCharStringsIndex + inCharStringIndex;
}
bool CCFFReader::ReadCharString(long long inCharStringStart, long long inCharStringEnd, BYTE** outCharString)
{
bool status = true;
mPrimitivesReader->Seek(inCharStringStart, SeekSet);
*outCharString = NULL;
unsigned int nReadLength = inCharStringEnd - inCharStringStart;
*outCharString = new BYTE[nReadLength];
mPrimitivesReader->Read(*outCharString, &nReadLength);
if (nReadLength != inCharStringEnd - inCharStringStart)
status = false;
if (!status && *outCharString)
delete[] *outCharString;
return status;
}
CharString* CCFFReader::GetLocalSubr(long inSubrIndex)
{
// locate local subr and return. also - push it to the dependendecy stack to start calculating dependencies for it
// also - record dependency on this subr.
unsigned short biasedIndex = GetBiasedIndex(mCurrentLocalSubrs->mCharStringsCount, inSubrIndex);
if (biasedIndex < mCurrentLocalSubrs->mCharStringsCount)
{
CharString* returnValue = mCurrentLocalSubrs->mCharStringsIndex + biasedIndex;
if (mCurrentDependencies)
mCurrentDependencies->mLocalSubrs.insert(biasedIndex);
return returnValue;
}
return NULL;
}
unsigned short CCFFReader::GetBiasedIndex(unsigned short inSubroutineCollectionSize, long inSubroutineIndex)
{
if (inSubroutineCollectionSize < 1240)
return (unsigned short)(107 + inSubroutineIndex);
else if (inSubroutineCollectionSize < 33900)
return (unsigned short)(1131 + inSubroutineIndex);
return (unsigned short)(32768 + inSubroutineIndex);
}
CharString* CCFFReader::GetGlobalSubr(long inSubrIndex)
{
unsigned short biasedIndex = GetBiasedIndex(mGlobalSubrs.mCharStringsCount, inSubrIndex);
if (biasedIndex < mGlobalSubrs.mCharStringsCount)
{
CharString* returnValue = mGlobalSubrs.mCharStringsIndex + biasedIndex;
if (mCurrentDependencies)
mCurrentDependencies->mGlobalSubrs.insert(biasedIndex);
return returnValue;
}
return NULL;
}
//----------------------------------------------------------------------------------------
// COpenTypeReader
//----------------------------------------------------------------------------------------
struct COpenTypeReader
{
enum class EOpenTypeInputType
{
EOpenTypeTrueType,
EOpenTypeCFF
};
struct TableEntry
{
unsigned long CheckSum;
unsigned long Offset;
unsigned long Length;
};
typedef std::map<unsigned int, TableEntry> UIntToTableEntryMap;
struct HeadTable
{
double TableVersionNumber;
double FontRevision;
unsigned long CheckSumAdjustment;
unsigned long MagicNumber;
unsigned short Flags;
unsigned short UnitsPerEm;
long long Created;
long long Modified;
short XMin;
short YMin;
short XMax;
short YMax;
unsigned short MacStyle;
unsigned short LowerRectPPEM;
short FontDirectionHint;
short IndexToLocFormat;
short GlyphDataFormat;
};
struct MaxpTable
{
double TableVersionNumber;
unsigned short NumGlyphs;
unsigned short MaxPoints;
unsigned short MaxCountours;
unsigned short MaxCompositePoints;
unsigned short MaxCompositeContours;
unsigned short MaxZones;
unsigned short MaxTwilightPoints;
unsigned short MaxStorage;
unsigned short MaxFunctionDefs;
unsigned short MaxInstructionDefs;
unsigned short MaxStackElements;
unsigned short MaxSizeOfInstructions;
unsigned short MaxComponentElements;
unsigned short MaxCompontentDepth;
};
struct HHeaTable
{
double TableVersionNumber;
short Ascender;
short Descender;
short LineGap;
unsigned short AdvanceWidthMax;
short MinLeftSideBearing;
short MinRightSideBearing;
short XMaxExtent;
short CaretSlopeRise;
short CaretSlopeRun;
short CaretOffset;
short MetricDataFormat;
unsigned short NumberOfHMetrics;
};
struct HMtxTableEntry
{
unsigned short AdvanceWidth;
short LeftSideBearing;
};
typedef HMtxTableEntry* HMtxTable;
struct OS2Table
{
unsigned short Version;
short AvgCharWidth;
unsigned short WeightClass;
unsigned short WidthClass;
unsigned short fsType;
short SubscriptXSize;
short SubscriptYSize;
short SubscriptXOffset;
short SubscriptYOffset;
short SuperscriptXSize;
short SuperscriptYSize;
short SuperscriptXOffset;
short SuperscriptYOffset;
short StrikeoutSize;
short StrikeoutPosition;
short FamilyClass;
BYTE Panose[10];
unsigned long UnicodeRange1;
unsigned long UnicodeRange2;
unsigned long UnicodeRange3;
unsigned long UnicodeRange4;
char AchVendID[4];
unsigned short FSSelection;
unsigned short FirstCharIndex;
unsigned short LastCharIndex;
short TypoAscender;
short TypoDescender;
short TypoLineGap;
unsigned short WinAscent;
unsigned short WinDescent;
unsigned long CodePageRange1;
unsigned long CodePageRange2;
short XHeight;
short CapHeight;
unsigned short DefaultChar;
unsigned short BreakChar;
unsigned short MaxContext;
};
struct NameTableEntry
{
unsigned short PlatformID;
unsigned short EncodingID;
unsigned short LanguageID;
unsigned short NameID;
unsigned short Length;
unsigned short Offset;
BYTE* String;
};
struct NameTable
{
unsigned short mNameEntriesCount;
NameTableEntry* mNameEntries;
};
typedef unsigned long* LocaTable;
// this time it's gonna be just what's intersting for my subsetting purposes - which is the dependencies ('n some other stuff)
struct GlyphEntry
{
short NumberOfContours;
short XMin;
short YMin;
short XMax;
short YMax;
std::list<unsigned int> mComponentGlyphs; // will be empty for simple glyphs, and with component glyph indexes for components
};
typedef GlyphEntry** GlyfTable;
typedef std::map<unsigned short, GlyphEntry*> UShortToGlyphEntryMap;
unsigned long mHeaderOffset;
unsigned long mTableOffset;
unsigned short mFaceIndex;
HeadTable mHead;
MaxpTable mMaxp;
HHeaTable mHHea;
HMtxTable mHMtx;
OS2Table mOS2;
NameTable mName;
LocaTable mLoca;
GlyfTable mGlyf;
// OS2 (surprise may not always exist. in dfonts for instance)
bool mOS2Exists;
// not read, but can tell if they are there
bool mCVTExists;
bool mFPGMExists;
bool mPREPExists;
CCFFReader mCFF;
CMemoryStream* mPrimitivesReader;
EOpenTypeInputType mFontType;
unsigned short mTablesCount;
UIntToTableEntryMap mTables;
UShortToGlyphEntryMap mActualGlyphs;
public:
COpenTypeReader();
~COpenTypeReader();
void FreeTables();
unsigned short GetGlyphsCount();
TableEntry* GetTableEntry(const char* inTagName);
EOpenTypeInputType GetOpenTypeFontType();
bool ReadOpenTypeFile(BYTE* pData, unsigned int nDataLength, unsigned short ushFaceIndex);
bool ReadOpenTypeHeader();
bool ReadOpenTypeSFNT();
bool ReadOpenTypeSFNTFromDfont();
bool ReadHead();
bool ReadMaxP();
bool ReadHHea();
bool ReadHMtx();
bool ReadOS2();
bool ReadName();
bool ReadLoca();
bool ReadGlyfForDependencies();
bool ReadCFF();
unsigned long GetTag(const char* inTagName);
};
COpenTypeReader::COpenTypeReader()
{
mHeaderOffset = 0;
mTableOffset = 0;
mHMtx = NULL;
mName.mNameEntries = NULL;
mLoca = NULL;
mGlyf = NULL;
mPrimitivesReader = NULL;
mFaceIndex = 0;
mFontType = EOpenTypeInputType::EOpenTypeCFF;
mOS2Exists = false;
}
COpenTypeReader::~COpenTypeReader()
{
FreeTables();
}
void COpenTypeReader::FreeTables()
{
RELEASEOBJECT(mPrimitivesReader);
RELEASEARRAYOBJECTS(mHMtx);
if (mName.mNameEntries)
{
for (int i = 0; i < mName.mNameEntriesCount; ++i)
RELEASEARRAYOBJECTS(mName.mNameEntries[i].String);
}
RELEASEARRAYOBJECTS(mName.mNameEntries);
RELEASEARRAYOBJECTS(mLoca);
RELEASEARRAYOBJECTS(mGlyf);
UShortToGlyphEntryMap::iterator it = mActualGlyphs.begin();
for (; it != mActualGlyphs.end(); ++it)
RELEASEOBJECT(it->second);
mActualGlyphs.clear();
}
unsigned short COpenTypeReader::GetGlyphsCount()
{
return mMaxp.NumGlyphs;
}
COpenTypeReader::TableEntry* COpenTypeReader::GetTableEntry(const char* inTagName)
{
UIntToTableEntryMap::iterator it = mTables.find(GetTag(inTagName));
if (it == mTables.end())
return NULL;
else
return &(it->second);
}
COpenTypeReader::EOpenTypeInputType COpenTypeReader::GetOpenTypeFontType()
{
return mFontType;
}
bool COpenTypeReader::ReadOpenTypeFile(BYTE* pData, unsigned int nDataLength, unsigned short ushFaceIndex)
{
mFaceIndex = ushFaceIndex;
FreeTables();
mPrimitivesReader = new CMemoryStream(pData, nDataLength);
mPrimitivesReader->Seek(0, SeekSet);
mHeaderOffset = mPrimitivesReader->Tell();
mTableOffset = mPrimitivesReader->Tell();
bool status = ReadOpenTypeHeader();
if (!status)
return false;
status = ReadHead();
if (!status)
return false;
status = ReadMaxP();
if (!status)
return false;
status = ReadHHea();
if (!status)
return false;
status = ReadHMtx();
if (!status)
return false;
status = ReadOS2(); // Note that OS/2 is supposedly required, but some dfonts don't contain it...and it's fine
if (!status)
return false;
status = ReadName();
if (!status)
return false;
if (EOpenTypeInputType::EOpenTypeTrueType == mFontType)
{
// true type specifics
status = ReadLoca();
if (!status)
return false;
status = ReadGlyfForDependencies();
if (!status)
return false;
mCVTExists = mTables.find(GetTag("cvt ")) != mTables.end();
mFPGMExists = mTables.find(GetTag("fpgm")) != mTables.end();
mPREPExists = mTables.find(GetTag("prep")) != mTables.end();
// zero cff items
mCFF.Reset();
}
else
{
// CFF specifics
status = ReadCFF();
if (!status)
return false;
// zero true type items
mCVTExists = false;
mFPGMExists = false;
mPREPExists = false;
mGlyf = NULL;
mLoca = NULL;
}
return status;
}
bool COpenTypeReader::ReadOpenTypeHeader()
{
bool status;
TableEntry tableEntry;
unsigned int tableTag;
status = ReadOpenTypeSFNT();
if (!status)
return false;
mPrimitivesReader->Seek(mHeaderOffset, SeekSet);
mPrimitivesReader->ReadUInt(); // sfntVersion
mTablesCount = mPrimitivesReader->ReadUShort();
// skip the next 6
mPrimitivesReader->Seek(6, SeekCur);
for (unsigned int i = 0; i < mTablesCount; ++i)
{
tableTag = mPrimitivesReader->ReadUInt();
tableEntry.CheckSum = mPrimitivesReader->ReadUInt();
tableEntry.Offset = mPrimitivesReader->ReadUInt();
tableEntry.Length = mPrimitivesReader->ReadUInt();
tableEntry.Offset += mTableOffset;
mTables.insert(UIntToTableEntryMap::value_type(tableTag, tableEntry));
}
return true;
}
bool COpenTypeReader::ReadOpenTypeSFNT()
{
mPrimitivesReader->Seek(mHeaderOffset, EWhenceMode::SeekSet);
unsigned int sfntVersion = mPrimitivesReader->ReadUInt();
if (mPrimitivesReader->IsEof())
return false;
if (0x74746366 /* ttcf */ == sfntVersion)
{
// mgubi: a TrueType composite font, just get to the right face table
// for the format see http://www.microsoft.com/typography/otspec/otff.htm
mPrimitivesReader->ReadUInt(); // ttcVersion
unsigned int numFonts = mPrimitivesReader->ReadUInt();
if (mFaceIndex >= numFonts)
return false;
unsigned int offsetTable;
for (int i = 0; i <= mFaceIndex; ++i)
offsetTable = mPrimitivesReader->ReadUInt();
mHeaderOffset = mHeaderOffset + offsetTable;
return ReadOpenTypeSFNT();
}
else if ((0x10000 == sfntVersion) || (0x74727565 /* true */ == sfntVersion))
{
mFontType = EOpenTypeInputType::EOpenTypeTrueType;
return true;
}
else if (0x4F54544F /* OTTO */ == sfntVersion)
{
mFontType = EOpenTypeInputType::EOpenTypeCFF;
return true;
}
else if (ReadOpenTypeSFNTFromDfont())
return true;
return false;
}
bool COpenTypeReader::ReadOpenTypeSFNTFromDfont()
{
bool status = true;
// mac resource fork header parsing
// see: https://developer.apple.com/legacy/mac/library/documentation/mac/pdf/MoreMacintoshToolbox.pdf
unsigned int rdata_pos, map_pos, rdata_len, map_offset;
(void) rdata_len;
// verify that the header is composed as expected
BYTE head[16], head2[16];
mPrimitivesReader->Seek(mHeaderOffset, SeekSet);
for (unsigned short i = 0; i < 16; ++i)
head[i] = mPrimitivesReader->ReadUChar();
rdata_pos = ( head[0] << 24 ) | ( head[1] << 16 ) | ( head[2] << 8 ) | head[3] ;
map_pos = ( head[4] << 24 ) | ( head[5] << 16 ) | ( head[6] << 8 ) | head[7] ;
rdata_len = ( head[8] << 24 ) | ( head[9] << 16 ) | ( head[10] << 8 ) | head[11];
mPrimitivesReader->Seek(map_pos, SeekSet);
for (unsigned short i = 0; i < 16; ++i)
head2[i] = mPrimitivesReader->ReadUChar();
if (mPrimitivesReader->IsEof())
return false;
/* If we have reached this point then it is probably a mac resource */
/* file. Now, does it contain any interesting resources? */
mPrimitivesReader->Seek(4 /* skip handle to next resource map */
+ 2 /* skip file resource number */
+ 2 /* skip attributes */
, SeekCur);
unsigned short type_list = mPrimitivesReader->ReadUShort();
map_offset = map_pos + type_list;
mPrimitivesReader->Seek(map_offset, SeekSet);
// read the resource type list
unsigned short cnt = mPrimitivesReader->ReadUShort();
bool foundSfnt = false;
for (int i = 0; i < cnt + 1 && !mPrimitivesReader->IsEof() && !foundSfnt; ++i)
{
unsigned short subcnt, rpos;
int tag = mPrimitivesReader->ReadUInt();
if (mPrimitivesReader->IsEof())
break;
subcnt = mPrimitivesReader->ReadUShort();
if (mPrimitivesReader->IsEof())
break;
rpos = mPrimitivesReader->ReadUShort();
if (mPrimitivesReader->IsEof())
break;
if ( (unsigned long)tag == GetTag("sfnt") )
{
mPrimitivesReader->Seek(map_offset + rpos, SeekSet);
// read the reference list for the 'sfnt' resources
// the map is used to order the references by reference id
std::map<unsigned short, unsigned long> resOffsetsMap;
for (int j = 0; j < subcnt + 1 && !mPrimitivesReader->IsEof(); ++j )
{
unsigned short res_id;
unsigned int temp, res_offset;
res_id = mPrimitivesReader->ReadUShort();
if (mPrimitivesReader->IsEof())
break;
mPrimitivesReader->ReadUShort(); // res_name
if (mPrimitivesReader->IsEof())
break;
temp = mPrimitivesReader->ReadUInt();
if (mPrimitivesReader->IsEof())
break;
mPrimitivesReader->ReadUInt(); // mbz
if (mPrimitivesReader->IsEof())
break;
res_offset = temp & 0xFFFFFFL;
resOffsetsMap.insert(std::pair<unsigned short, unsigned long>(res_id, rdata_pos + res_offset));
}
if (mPrimitivesReader->IsEof())
break;
int face_index = mFaceIndex, cur_face = 0;
unsigned long fontOffset = 0;
for (std::map<unsigned short, unsigned long>::iterator it = resOffsetsMap.begin(); it != resOffsetsMap.end(); ++it, ++cur_face)
{
if (cur_face == face_index)
{
fontOffset = it->second;
break;
}
}
if (cur_face != face_index)
{
status = false;
break;
}
mHeaderOffset = fontOffset + 4; // skip the size of the resource
mTableOffset = mHeaderOffset;
// try to open the resource as a TrueType font specification
foundSfnt = true;
}
}
if (status && foundSfnt)
return ReadOpenTypeSFNT();
return false;
}
unsigned long COpenTypeReader::GetTag(const char* inTagName)
{
BYTE buffer[4];
unsigned short i = 0;
for (; i < strlen(inTagName); ++i)
buffer[i] = (BYTE)inTagName[i];
for (; i < 4; ++i)
buffer[i] = 0x20;
return ((unsigned long)buffer[0] << 24) + ((unsigned long)buffer[1] << 16) +
((unsigned long)buffer[2] << 8) + buffer[3];
}
bool COpenTypeReader::ReadHead()
{
UIntToTableEntryMap::iterator it = mTables.find(GetTag("head"));
if (it == mTables.end())
return false;
mPrimitivesReader->Seek(it->second.Offset, SeekSet);
mHead.TableVersionNumber = mPrimitivesReader->ReadFixed();
mHead.FontRevision = mPrimitivesReader->ReadFixed();
mHead.CheckSumAdjustment = mPrimitivesReader->ReadUInt();
mHead.MagicNumber = mPrimitivesReader->ReadUInt();
mHead.Flags = mPrimitivesReader->ReadUShort();
mHead.UnitsPerEm = mPrimitivesReader->ReadUShort();
mHead.Created = mPrimitivesReader->ReadLongDateTime();
mHead.Modified = mPrimitivesReader->ReadLongDateTime();
mHead.XMin = mPrimitivesReader->ReadUShort();
mHead.YMin = mPrimitivesReader->ReadUShort();
mHead.XMax = mPrimitivesReader->ReadUShort();
mHead.YMax = mPrimitivesReader->ReadUShort();
mHead.MacStyle = mPrimitivesReader->ReadUShort();
mHead.LowerRectPPEM = mPrimitivesReader->ReadUShort();
mHead.FontDirectionHint = mPrimitivesReader->ReadUShort();
mHead.IndexToLocFormat = mPrimitivesReader->ReadUShort();
mHead.GlyphDataFormat = mPrimitivesReader->ReadUShort();
return true;
}
bool COpenTypeReader::ReadMaxP()
{
UIntToTableEntryMap::iterator it = mTables.find(GetTag("maxp"));
if (it == mTables.end())
return false;
mPrimitivesReader->Seek(it->second.Offset, SeekSet);
memset(&mMaxp, 0, sizeof(MaxpTable)); // set all with 0's in case the table's too short, so we'll have nice lookin values
mMaxp.TableVersionNumber = mPrimitivesReader->ReadFixed();
mMaxp.NumGlyphs = mPrimitivesReader->ReadUShort();
if (1.0 == mMaxp.TableVersionNumber)
{
mMaxp.MaxPoints = mPrimitivesReader->ReadUShort();
mMaxp.MaxCountours = mPrimitivesReader->ReadUShort();
mMaxp.MaxCompositePoints = mPrimitivesReader->ReadUShort();
mMaxp.MaxCompositeContours = mPrimitivesReader->ReadUShort();
mMaxp.MaxZones = mPrimitivesReader->ReadUShort();
mMaxp.MaxTwilightPoints = mPrimitivesReader->ReadUShort();
mMaxp.MaxStorage = mPrimitivesReader->ReadUShort();
mMaxp.MaxFunctionDefs = mPrimitivesReader->ReadUShort();
mMaxp.MaxInstructionDefs = mPrimitivesReader->ReadUShort();
mMaxp.MaxStackElements = mPrimitivesReader->ReadUShort();
mMaxp.MaxSizeOfInstructions = mPrimitivesReader->ReadUShort();
mMaxp.MaxComponentElements = mPrimitivesReader->ReadUShort();
mMaxp.MaxCompontentDepth = mPrimitivesReader->ReadUShort();
}
return true;
}
bool COpenTypeReader::ReadHHea()
{
UIntToTableEntryMap::iterator it = mTables.find(GetTag("hhea"));
if (it == mTables.end())
return false;
mPrimitivesReader->Seek(it->second.Offset, SeekSet);
mHHea.TableVersionNumber = mPrimitivesReader->ReadFixed();
mHHea.Ascender = mPrimitivesReader->ReadUShort();
mHHea.Descender = mPrimitivesReader->ReadUShort();
mHHea.LineGap = mPrimitivesReader->ReadUShort();
mHHea.AdvanceWidthMax = mPrimitivesReader->ReadUShort();
mHHea.MinLeftSideBearing = mPrimitivesReader->ReadUShort();
mHHea.MinRightSideBearing = mPrimitivesReader->ReadUShort();
mHHea.XMaxExtent = mPrimitivesReader->ReadUShort();
mHHea.CaretSlopeRise = mPrimitivesReader->ReadUShort();
mHHea.CaretSlopeRun = mPrimitivesReader->ReadUShort();
mHHea.CaretOffset = mPrimitivesReader->ReadUShort();
mPrimitivesReader->Seek(8, SeekCur);
mHHea.MetricDataFormat = mPrimitivesReader->ReadUShort();
mHHea.NumberOfHMetrics = mPrimitivesReader->ReadUShort();
return true;
}
bool COpenTypeReader::ReadHMtx()
{
UIntToTableEntryMap::iterator it = mTables.find(GetTag("hmtx"));
if (it == mTables.end())
return false;
mPrimitivesReader->Seek(it->second.Offset, SeekSet);
mHMtx = new HMtxTableEntry[mMaxp.NumGlyphs];
unsigned int i = 0;
for (; i < mHHea.NumberOfHMetrics; ++i)
{
mHMtx[i].AdvanceWidth = mPrimitivesReader->ReadUShort();
mHMtx[i].LeftSideBearing = mPrimitivesReader->ReadUShort();
}
for (; i < mMaxp.NumGlyphs; ++i)
{
mHMtx[i].AdvanceWidth = mHMtx[mHHea.NumberOfHMetrics - 1].AdvanceWidth;
mHMtx[i].LeftSideBearing = mPrimitivesReader->ReadUShort();
}
return true;
}
bool COpenTypeReader::ReadOS2()
{
memset(&mOS2, 0, sizeof(OS2Table));
UIntToTableEntryMap::iterator it = mTables.find(GetTag("OS/2"));
if (it == mTables.end())
{
mOS2Exists = false;
return true;
}
mOS2Exists = true;
mPrimitivesReader->Seek(it->second.Offset, SeekSet);
mOS2.Version = mPrimitivesReader->ReadUShort();
mOS2.AvgCharWidth = mPrimitivesReader->ReadUShort();
mOS2.WeightClass = mPrimitivesReader->ReadUShort();
mOS2.WidthClass = mPrimitivesReader->ReadUShort();
mOS2.fsType = mPrimitivesReader->ReadUShort();
mOS2.SubscriptXSize = mPrimitivesReader->ReadUShort();
mOS2.SubscriptYSize = mPrimitivesReader->ReadUShort();
mOS2.SubscriptXOffset = mPrimitivesReader->ReadUShort();
mOS2.SubscriptYOffset = mPrimitivesReader->ReadUShort();
mOS2.SuperscriptXSize = mPrimitivesReader->ReadUShort();
mOS2.SuperscriptYSize = mPrimitivesReader->ReadUShort();
mOS2.SuperscriptXOffset = mPrimitivesReader->ReadUShort();
mOS2.SuperscriptYOffset = mPrimitivesReader->ReadUShort();
mOS2.StrikeoutSize = mPrimitivesReader->ReadUShort();
mOS2.StrikeoutPosition = mPrimitivesReader->ReadUShort();
mOS2.FamilyClass = mPrimitivesReader->ReadUShort();
for (int i = 0; i < 10; ++i)
mOS2.Panose[i] = mPrimitivesReader->ReadUChar();
mOS2.UnicodeRange1 = mPrimitivesReader->ReadUInt();
mOS2.UnicodeRange2 = mPrimitivesReader->ReadUInt();
mOS2.UnicodeRange3 = mPrimitivesReader->ReadUInt();
mOS2.UnicodeRange4 = mPrimitivesReader->ReadUInt();
for (int i = 0; i < 4; ++i)
mOS2.AchVendID[i] = mPrimitivesReader->ReadUChar();
mOS2.FSSelection = mPrimitivesReader->ReadUShort();
mOS2.FirstCharIndex = mPrimitivesReader->ReadUShort();
mOS2.LastCharIndex = mPrimitivesReader->ReadUShort();
mOS2.TypoAscender = mPrimitivesReader->ReadUShort();
mOS2.TypoDescender = mPrimitivesReader->ReadUShort();
mOS2.TypoLineGap = mPrimitivesReader->ReadUShort();
mOS2.WinAscent = mPrimitivesReader->ReadUShort();
mOS2.WinDescent = mPrimitivesReader->ReadUShort();
// version 1 OS/2 table may end here [see that there's enough to continue]
if (it->second.Length >= (mPrimitivesReader->Tell() - it->second.Offset) + 18)
{
mOS2.CodePageRange1 = mPrimitivesReader->ReadUInt();
mOS2.CodePageRange2 = mPrimitivesReader->ReadUInt();
mOS2.XHeight = mPrimitivesReader->ReadUShort();
mOS2.CapHeight = mPrimitivesReader->ReadUShort();
mOS2.DefaultChar = mPrimitivesReader->ReadUShort();
mOS2.BreakChar = mPrimitivesReader->ReadUShort();
mOS2.MaxContext = mPrimitivesReader->ReadUShort();
}
return true;
}
bool COpenTypeReader::ReadName()
{
UIntToTableEntryMap::iterator it = mTables.find(GetTag("name"));
if (it == mTables.end())
return false;
mPrimitivesReader->Seek(it->second.Offset + 2, SeekSet);
mName.mNameEntriesCount = mPrimitivesReader->ReadUShort();
mName.mNameEntries = new NameTableEntry[mName.mNameEntriesCount];
unsigned short stringOffset = mPrimitivesReader->ReadUShort();
for (int i = 0; i < mName.mNameEntriesCount; ++i)
{
mName.mNameEntries[i].PlatformID = mPrimitivesReader->ReadUShort();
mName.mNameEntries[i].EncodingID = mPrimitivesReader->ReadUShort();
mName.mNameEntries[i].LanguageID = mPrimitivesReader->ReadUShort();
mName.mNameEntries[i].NameID = mPrimitivesReader->ReadUShort();
mName.mNameEntries[i].Length = mPrimitivesReader->ReadUShort();
mName.mNameEntries[i].Offset = mPrimitivesReader->ReadUShort();
}
for (int i = 0; i < mName.mNameEntriesCount; ++i)
{
mName.mNameEntries[i].String = new BYTE[mName.mNameEntries[i].Length];
mPrimitivesReader->Seek(it->second.Offset + stringOffset + mName.mNameEntries[i].Offset, SeekSet);
unsigned int nLength = mName.mNameEntries[i].Length;
mPrimitivesReader->Read(mName.mNameEntries[i].String, &nLength);
mName.mNameEntries[i].Length = nLength;
}
return true;
}
bool COpenTypeReader::ReadLoca()
{
UIntToTableEntryMap::iterator it = mTables.find(GetTag("loca"));
if (it == mTables.end())
return false;
mPrimitivesReader->Seek(it->second.Offset, SeekSet);
mLoca = new unsigned long[mMaxp.NumGlyphs + 1];
if (0 == mHead.IndexToLocFormat)
{
unsigned short buffer;
for (int i = 0; i < mMaxp.NumGlyphs + 1; ++i)
{
buffer = mPrimitivesReader->ReadUShort();
mLoca[i] = buffer << 1;
}
}
else
{
for (int i = 0; i < mMaxp.NumGlyphs + 1; ++i)
mLoca[i] = mPrimitivesReader->ReadUInt();
}
return true;
}
bool COpenTypeReader::ReadGlyfForDependencies()
{
UIntToTableEntryMap::iterator it = mTables.find(GetTag("glyf"));
if (it == mTables.end())
return false;
// it->second.Offset, is the offset to the beginning of the table
mGlyf = new GlyphEntry*[mMaxp.NumGlyphs];
for (int i = 0; i < mMaxp.NumGlyphs; ++i)
{
if (mLoca[i + 1] == mLoca[i])
mGlyf[i] = NULL;
else
{
mGlyf[i] = new GlyphEntry;
mPrimitivesReader->Seek(it->second.Offset + mLoca[i], SeekSet);
mGlyf[i]->NumberOfContours = mPrimitivesReader->ReadUShort();
mGlyf[i]->XMin = mPrimitivesReader->ReadUShort();
mGlyf[i]->YMin = mPrimitivesReader->ReadUShort();
mGlyf[i]->XMax = mPrimitivesReader->ReadUShort();
mGlyf[i]->YMax = mPrimitivesReader->ReadUShort();
// Now look for dependencies
if (mGlyf[i]->NumberOfContours < 0)
{
bool hasMoreComponents;
unsigned short flags;
unsigned short glyphIndex;
do
{
flags = mPrimitivesReader->ReadUShort();
glyphIndex = mPrimitivesReader->ReadUShort();
if (glyphIndex >= mMaxp.NumGlyphs)
return false;
mGlyf[i]->mComponentGlyphs.push_back(glyphIndex);
if ((flags & 1) != 0)
mPrimitivesReader->Seek(4, SeekCur); // skip 2 shorts, ARG_1_AND_2_ARE_WORDS
else
mPrimitivesReader->Seek(2, SeekCur); // skip 1 short, nah - they are bytes
if ((flags & 8) != 0)
mPrimitivesReader->Seek(2, SeekCur); // WE_HAVE_SCALE
else if ((flags & 64) != 0)
mPrimitivesReader->Seek(4, SeekCur); // WE_HAVE_AN_X_AND_Y_SCALE
else if ((flags & 128) != 0)
mPrimitivesReader->Seek(8, SeekCur); // WE_HAVE_A_TWO_BY_TWO
hasMoreComponents = ((flags & 32) != 0);
} while(hasMoreComponents);
}
mActualGlyphs.insert(UShortToGlyphEntryMap::value_type(i, mGlyf[i]));
}
}
return true;
}
bool COpenTypeReader::ReadCFF()
{
UIntToTableEntryMap::iterator it = mTables.find(GetTag("CFF "));
if (it == mTables.end())
return false;
mPrimitivesReader->Seek(it->second.Offset, SeekSet);
return mCFF.ReadCFFFile(mPrimitivesReader);
}
//----------------------------------------------------------------------------------------
// IType2InterpreterImplementation
//----------------------------------------------------------------------------------------
struct IType2InterpreterImplementation
{
virtual bool ReadCharString(long long inCharStringStart, long long inCharStringEnd, BYTE** outCharString) = 0;
virtual bool Type2InterpretNumber(const CharStringOperand& inOperand) = 0;
virtual bool Type2Hstem(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Vstem(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Vmoveto(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Rlineto(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Hlineto(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Vlineto(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2RRCurveto(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Return(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Endchar(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Hstemhm(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Hintmask(const CharStringOperandList& inOperandList, BYTE* inProgramCounter, long long inReadLimit) = 0;
virtual bool Type2Cntrmask(const CharStringOperandList& inOperandList, BYTE* inProgramCounter, long long inReadLimit) = 0;
virtual bool Type2Rmoveto(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Hmoveto(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Vstemhm(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Rcurveline(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Rlinecurve(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Vvcurveto(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Hvcurveto(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Hhcurveto(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Vhcurveto(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Hflex(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Hflex1(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Flex(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Flex1(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2And(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Or(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Not(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Abs(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Add(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Sub(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Div(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Neg(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Eq(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Drop(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Put(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Get(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Ifelse(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Random(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Mul(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Sqrt(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Dup(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Exch(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Index(const CharStringOperandList& inOperandList) = 0;
virtual bool Type2Roll(const CharStringOperandList& inOperandList) = 0;
virtual CharString* GetLocalSubr(long inSubrIndex) = 0;
virtual CharString* GetGlobalSubr(long inSubrIndex) = 0;
};
//----------------------------------------------------------------------------------------
// CharStringType2Interpreter
//----------------------------------------------------------------------------------------
struct CharStringType2Interpreter
{
#define MAX_ARGUMENTS_STACK_SIZE 48
#define MAX_STEM_HINTS_SIZE 96
#define MAX_SUBR_NESTING_STACK_SIZE 10
CharStringOperandList mOperandStack;
unsigned short mStemsCount;
IType2InterpreterImplementation* mImplementationHelper;
bool mGotEndChar;
CharStringOperandVector mStorage;
bool mCheckedWidth;
unsigned short mSubrsNesting;
public:
CharStringType2Interpreter();
~CharStringType2Interpreter();
bool Intepret(const CharString& inCharStringToIntepret, IType2InterpreterImplementation* inImplementationHelper);
bool ProcessCharString(BYTE* inCharString, long long inCharStringLength);
bool IsOperator(BYTE inCurrentByte);
BYTE* InterpretNumber(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretOperator(BYTE* inProgramCounter, bool& outGotEndExecutionCommand, long long inReadLimit);
void CheckWidth();
bool AddStemsCount(unsigned short inBy);
BYTE* InterpretHStem(BYTE* inProgramCounter, long long inReadLimit);
void ClearStack();
BYTE* InterpretVStem(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretVMoveto(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretRLineto(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretHLineto(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretVLineto(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretRRCurveto(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretCallSubr(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretReturn(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretEndChar(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretHStemHM(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretHintMask(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretCntrMask(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretRMoveto(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretHMoveto(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretVStemHM(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretRCurveLine(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretRLineCurve(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretVVCurveto(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretHHCurveto(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretCallGSubr(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretVHCurveto(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretHVCurveto(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretAnd(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretOr(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretNot(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretAbs(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretAdd(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretSub(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretDiv(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretNeg(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretEq(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretDrop(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretPut(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretGet(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretIfelse(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretRandom(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretMul(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretSqrt(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretDup(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretExch(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretIndex(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretRoll(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretHFlex(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretFlex(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretHFlex1(BYTE* inProgramCounter, long long inReadLimit);
BYTE* InterpretFlex1(BYTE* inProgramCounter, long long inReadLimit);
};
CharStringType2Interpreter::CharStringType2Interpreter()
{
mImplementationHelper = NULL;
}
CharStringType2Interpreter::~CharStringType2Interpreter()
{
}
bool CharStringType2Interpreter::Intepret(const CharString& inCharStringToIntepret, IType2InterpreterImplementation* inImplementationHelper)
{
BYTE* charString = NULL;
bool status = false;
mImplementationHelper = inImplementationHelper;
mGotEndChar = false;
mStemsCount = 0;
mCheckedWidth = false;
mSubrsNesting = 0;
if (!inImplementationHelper)
return false;
status = mImplementationHelper->ReadCharString(inCharStringToIntepret.mStartPosition, inCharStringToIntepret.mEndPosition, &charString);
if (!status)
return false;
status = ProcessCharString(charString, inCharStringToIntepret.mEndPosition - inCharStringToIntepret.mStartPosition);
delete charString;
return status;
}
bool CharStringType2Interpreter::ProcessCharString(BYTE* inCharString, long long inCharStringLength)
{
bool status = true;
BYTE* pointer = inCharString;
bool gotEndExecutionOperator = false;
while (pointer - inCharString < inCharStringLength && status && !gotEndExecutionOperator && !mGotEndChar)
{
long long readLimit = inCharStringLength - (pointer - inCharString); // should be at least 1
if (IsOperator(*pointer))
{
pointer = InterpretOperator(pointer, gotEndExecutionOperator, readLimit);
if (!pointer)
status = false;
}
else
{
pointer = InterpretNumber(pointer, readLimit);
if (!pointer)
status = false;
if (mOperandStack.size() > MAX_ARGUMENTS_STACK_SIZE)
status = false;
}
}
return status;
}
bool CharStringType2Interpreter::IsOperator(BYTE inCurrentByte)
{
return ((inCurrentByte) <= 27) || (29 <= (inCurrentByte) && (inCurrentByte) <= 31);
}
BYTE* CharStringType2Interpreter::InterpretNumber(BYTE* inProgramCounter, long long inReadLimit)
{
CharStringOperand operand;
BYTE* newPosition = inProgramCounter;
if (inReadLimit < 1)
return NULL; // error, cant read a single byte
if (28 == *newPosition && inReadLimit >= 3)
{
operand.IsInteger = true;
operand.IntegerValue = (short)(((unsigned short)(*(newPosition + 1)) << 8) + (*(newPosition + 2)));
newPosition += 3;
}
else if (32 <= *newPosition && *newPosition <= 246)
{
operand.IsInteger = true;
operand.IntegerValue = (short)*newPosition - 139;
++newPosition;
}
else if (247 <= *newPosition && *newPosition <= 250 && inReadLimit >= 2)
{
operand.IsInteger = true;
operand.IntegerValue = (*newPosition - 247) * 256 + *(newPosition + 1) + 108;
newPosition += 2;
}
else if (251 <= *newPosition && *newPosition <= 254 && inReadLimit >= 2)
{
operand.IsInteger = true;
operand.IntegerValue = -(short)(*newPosition - 251) * 256 - *(newPosition + 1) - 108;
newPosition += 2;
}
else if (255 == *newPosition && inReadLimit >= 5)
{
operand.IsInteger = false;
operand.RealValue = (short)(((unsigned short)(*(newPosition + 1)) << 8) + (*(newPosition + 2)));
if (operand.RealValue > 0)
operand.RealValue += (double)(((unsigned short)(*(newPosition + 3)) << 8) + (*(newPosition + 4))) / (1 << 16);
else
operand.RealValue -= (double)(((unsigned short)(*(newPosition + 3)) << 8) + (*(newPosition + 4))) / (1 << 16);
newPosition += 5;
}
else
newPosition = NULL; // error
if (newPosition)
{
mOperandStack.push_back(operand);
bool status = mImplementationHelper->Type2InterpretNumber(operand);
if (!status)
return NULL;
}
return newPosition;
}
BYTE* CharStringType2Interpreter::InterpretOperator(BYTE* inProgramCounter, bool& outGotEndExecutionCommand, long long inReadLimit)
{
unsigned short operatorValue;
BYTE* newPosition = inProgramCounter;
outGotEndExecutionCommand = false;
if (inReadLimit < 1)
return NULL; // error, cant read a single byte
if (12 == *newPosition)
{
if (inReadLimit < 2)
return NULL;
operatorValue = 0x0c00 + *(newPosition + 1);
newPosition += 2;
inReadLimit -= 2;
}
else
{
operatorValue = *newPosition;
++newPosition;
--inReadLimit;
}
switch (operatorValue)
{
case 1: // hstem
CheckWidth();
newPosition = InterpretHStem(newPosition, inReadLimit);
break;
case 3: // vstem
CheckWidth();
newPosition = InterpretVStem(newPosition, inReadLimit);
break;
case 4: // vmoveto
CheckWidth();
newPosition = InterpretVMoveto(newPosition, inReadLimit);
break;
case 5: // rlineto
newPosition = InterpretRLineto(newPosition, inReadLimit);
break;
case 6: // hlineto
newPosition = InterpretHLineto(newPosition, inReadLimit);
break;
case 7: // vlineto
newPosition = InterpretVLineto(newPosition, inReadLimit);
break;
case 8: // rrcurveto
newPosition = InterpretRRCurveto(newPosition, inReadLimit);
break;
case 10: // callsubr
newPosition = InterpretCallSubr(newPosition, inReadLimit);
break;
case 11: // return
newPosition = InterpretReturn(newPosition, inReadLimit);
outGotEndExecutionCommand = true;
break;
case 14: // endchar
CheckWidth();
newPosition = InterpretEndChar(newPosition, inReadLimit);
break;
case 18: // hstemhm
CheckWidth();
newPosition = InterpretHStemHM(newPosition, inReadLimit);
break;
case 19: // hintmask
CheckWidth();
newPosition = InterpretHintMask(newPosition, inReadLimit);
break;
case 20: // cntrmask
CheckWidth();
newPosition = InterpretCntrMask(newPosition, inReadLimit);
break;
case 21: // rmoveto
CheckWidth();
newPosition = InterpretRMoveto(newPosition, inReadLimit);
break;
case 22: // hmoveto
CheckWidth();
newPosition = InterpretHMoveto(newPosition, inReadLimit);
break;
case 23: // vstemhm
CheckWidth();
newPosition = InterpretVStemHM(newPosition, inReadLimit);
break;
case 24: // rcurveline
newPosition = InterpretRCurveLine(newPosition, inReadLimit);
break;
case 25: // rlinecurve
newPosition = InterpretRLineCurve(newPosition, inReadLimit);
break;
case 26: // vvcurveto
newPosition = InterpretVVCurveto(newPosition, inReadLimit);
break;
case 27: // hhcurveto
newPosition = InterpretHHCurveto(newPosition, inReadLimit);
break;
case 29: // callgsubr
newPosition = InterpretCallGSubr(newPosition, inReadLimit);
break;
case 30: // vhcurveto
newPosition = InterpretVHCurveto(newPosition, inReadLimit);
break;
case 31: // hvcurveto
newPosition = InterpretHVCurveto(newPosition, inReadLimit);
break;
case 0x0c00: // dotsection, depracated
// ignore
break;
case 0x0c03: // and
newPosition = InterpretAnd(newPosition, inReadLimit);
break;
case 0x0c04: // or
newPosition = InterpretOr(newPosition, inReadLimit);
break;
case 0x0c05: // not
newPosition = InterpretNot(newPosition, inReadLimit);
break;
case 0x0c09: // abs
newPosition = InterpretAbs(newPosition, inReadLimit);
break;
case 0x0c0a: // add
newPosition = InterpretAdd(newPosition, inReadLimit);
break;
case 0x0c0b: // sub
newPosition = InterpretSub(newPosition, inReadLimit);
break;
case 0x0c0c: // div
newPosition = InterpretDiv(newPosition, inReadLimit);
break;
case 0x0c0e: // neg
newPosition = InterpretNeg(newPosition, inReadLimit);
break;
case 0x0c0f: // eq
newPosition = InterpretEq(newPosition, inReadLimit);
break;
case 0x0c12: // drop
newPosition = InterpretDrop(newPosition, inReadLimit);
break;
case 0x0c14: // put
newPosition = InterpretPut(newPosition, inReadLimit);
break;
case 0x0c15: // get
newPosition = InterpretGet(newPosition, inReadLimit);
break;
case 0x0c16: // ifelse
newPosition = InterpretIfelse(newPosition, inReadLimit);
break;
case 0x0c17: // random
newPosition = InterpretRandom(newPosition, inReadLimit);
break;
case 0x0c18: // mul
newPosition = InterpretMul(newPosition, inReadLimit);
break;
case 0x0c1a: // sqrt
newPosition = InterpretSqrt(newPosition, inReadLimit);
break;
case 0x0c1b: // dup
newPosition = InterpretDup(newPosition, inReadLimit);
break;
case 0x0c1c: // exch
newPosition = InterpretExch(newPosition, inReadLimit);
break;
case 0x0c1d: // index
newPosition = InterpretIndex(newPosition, inReadLimit);
break;
case 0x0c1e: // roll
newPosition = InterpretRoll(newPosition, inReadLimit);
break;
case 0x0c22: // hflex
newPosition = InterpretHFlex(newPosition, inReadLimit);
break;
case 0x0c23: // flex
newPosition = InterpretFlex(newPosition, inReadLimit);
break;
case 0x0c24: // hflex1
newPosition = InterpretHFlex1(newPosition, inReadLimit);
break;
case 0x0c25: // flex1
newPosition = InterpretFlex1(newPosition, inReadLimit);
break;
}
return newPosition;
}
void CharStringType2Interpreter::CheckWidth()
{
if (!mCheckedWidth)
{
if (mOperandStack.size() % 2 != 0) // has width
mOperandStack.pop_front();
mCheckedWidth = true;
}
}
bool CharStringType2Interpreter::AddStemsCount(unsigned short inBy)
{
if (mStemsCount + inBy > MAX_STEM_HINTS_SIZE)
return false;
mStemsCount += inBy;
return true;
}
BYTE* CharStringType2Interpreter::InterpretHStem(BYTE* inProgramCounter, long long)
{
bool status = AddStemsCount((unsigned short)(mOperandStack.size() / 2));
if (!status)
return NULL;
status = mImplementationHelper->Type2Hstem(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
void CharStringType2Interpreter::ClearStack()
{
mOperandStack.clear();
}
BYTE* CharStringType2Interpreter::InterpretVStem(BYTE* inProgramCounter, long long)
{
bool status = AddStemsCount((unsigned short)(mOperandStack.size() / 2));
if (!status)
return NULL;
status = mImplementationHelper->Type2Vstem(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretVMoveto(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Vmoveto(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretRLineto(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Rlineto(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretHLineto(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Hlineto(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretVLineto(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Vlineto(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretRRCurveto(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2RRCurveto(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretCallSubr(BYTE* inProgramCounter, long long)
{
CharString* aCharString = NULL;
if (mOperandStack.size() < 1)
return NULL;
aCharString = mImplementationHelper->GetLocalSubr(mOperandStack.back().IntegerValue);
mOperandStack.pop_back();
if (aCharString != NULL)
{
BYTE* charString = NULL;
bool status = mImplementationHelper->ReadCharString(aCharString->mStartPosition, aCharString->mEndPosition, &charString);
if (!status)
{
delete charString;
return NULL;
}
++mSubrsNesting;
if (mSubrsNesting > MAX_SUBR_NESTING_STACK_SIZE)
{
delete charString;
return NULL;
}
status = ProcessCharString(charString, aCharString->mEndPosition - aCharString->mStartPosition);
--mSubrsNesting;
delete charString;
if (!status)
return NULL;
return inProgramCounter;
}
return NULL;
}
BYTE* CharStringType2Interpreter::InterpretReturn(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Return(mOperandStack);
if (!status)
return NULL;
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretEndChar(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Endchar(mOperandStack);
if (!status)
return NULL;
mGotEndChar = true;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretHStemHM(BYTE* inProgramCounter, long long)
{
bool status = AddStemsCount((unsigned short)(mOperandStack.size() / 2));
if (!status)
return NULL;
status = mImplementationHelper->Type2Hstemhm(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretHintMask(BYTE* inProgramCounter, long long inReadLimit)
{
bool status = AddStemsCount((unsigned short)(mOperandStack.size() / 2)); // assuming this is a shortcut of dropping vstem if got arguments
if (!status)
return NULL;
status = mImplementationHelper->Type2Hintmask(mOperandStack, inProgramCounter, inReadLimit);
if (!status)
return NULL;
ClearStack();
long long programCounterStemReadSize = (mStemsCount / 8 + (mStemsCount % 8 != 0 ? 1 : 0));
if (programCounterStemReadSize > inReadLimit)
return NULL;
return inProgramCounter + programCounterStemReadSize;
}
BYTE* CharStringType2Interpreter::InterpretCntrMask(BYTE* inProgramCounter, long long inReadLimit)
{
bool status = AddStemsCount((unsigned short)(mOperandStack.size() / 2)); // assuming this is a shortcut of dropping vstem if got arguments
if (!status)
return NULL;
status = mImplementationHelper->Type2Cntrmask(mOperandStack, inProgramCounter, inReadLimit);
if (!status)
return NULL;
ClearStack();
long long programCounterStemReadSize = (mStemsCount / 8 + (mStemsCount % 8 != 0 ? 1 : 0));
if (programCounterStemReadSize > inReadLimit)
return NULL;
return inProgramCounter + programCounterStemReadSize;
}
BYTE* CharStringType2Interpreter::InterpretRMoveto(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Rmoveto(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretHMoveto(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Hmoveto(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretVStemHM(BYTE* inProgramCounter, long long)
{
bool status = AddStemsCount((unsigned short)(mOperandStack.size() / 2));
if (!status)
return NULL;
status = mImplementationHelper->Type2Vstemhm(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretRCurveLine(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Rcurveline(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretRLineCurve(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Rlinecurve(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretVVCurveto(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Vvcurveto(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretHHCurveto(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Hhcurveto(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretCallGSubr(BYTE* inProgramCounter, long long)
{
CharString* aCharString = NULL;
if (mOperandStack.size() < 1)
return NULL;
aCharString = mImplementationHelper->GetGlobalSubr(mOperandStack.back().IntegerValue);
mOperandStack.pop_back();
if (aCharString != NULL)
{
BYTE* charString = NULL;
bool status = mImplementationHelper->ReadCharString(aCharString->mStartPosition, aCharString->mEndPosition, &charString);
if (!status)
{
delete charString;
return NULL;
}
++mSubrsNesting;
if (mSubrsNesting > MAX_SUBR_NESTING_STACK_SIZE)
{
delete charString;
return NULL;
}
status = ProcessCharString(charString, aCharString->mEndPosition - aCharString->mStartPosition);
--mSubrsNesting;
delete charString;
if (!status)
return NULL;
return inProgramCounter;
}
return NULL;
}
BYTE* CharStringType2Interpreter::InterpretVHCurveto(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Vhcurveto(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretHVCurveto(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Hvcurveto(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretAnd(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2And(mOperandStack);
if (!status)
return NULL;
CharStringOperand valueA;
CharStringOperand valueB;
CharStringOperand newOperand;
newOperand.IsInteger = true;
if (mOperandStack.size() < 2)
return NULL;
valueB = mOperandStack.back();
mOperandStack.pop_back();
valueA = mOperandStack.back();
mOperandStack.pop_back();
newOperand.IntegerValue = (
(valueB.IsInteger ? valueB.IntegerValue : valueB.RealValue) &&
(valueA.IsInteger ? valueA.IntegerValue : valueA.RealValue)
) ? 1 : 0;
mOperandStack.push_back(newOperand);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretOr(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Or(mOperandStack);
if (!status)
return NULL;
CharStringOperand valueA;
CharStringOperand valueB;
CharStringOperand newOperand;
newOperand.IsInteger = true;
if (mOperandStack.size() < 2)
return NULL;
valueB = mOperandStack.back();
mOperandStack.pop_back();
valueA = mOperandStack.back();
mOperandStack.pop_back();
newOperand.IntegerValue = (
(valueB.IsInteger ? valueB.IntegerValue : valueB.RealValue) ||
(valueA.IsInteger ? valueA.IntegerValue : valueA.RealValue)
) ? 1:0; mOperandStack.push_back(newOperand);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretNot(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Not(mOperandStack);
if (!status)
return NULL;
CharStringOperand value;
CharStringOperand newOperand;
newOperand.IsInteger = true;
if (mOperandStack.size() < 1)
return NULL;
value = mOperandStack.back();
mOperandStack.pop_back();
newOperand.IntegerValue = (value.IsInteger ? value.IntegerValue : value.RealValue) ? 1 : 0;
mOperandStack.push_back(newOperand);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretAbs(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Abs(mOperandStack);
if (!status)
return NULL;
CharStringOperand value;
CharStringOperand newOperand;
if (mOperandStack.size() < 1)
return NULL;
value = mOperandStack.back();
newOperand.IsInteger = value.IsInteger;
mOperandStack.pop_back();
if (value.IsInteger)
newOperand.IntegerValue = labs(value.IntegerValue);
else
newOperand.RealValue = fabs(value.RealValue);
mOperandStack.push_back(newOperand);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretAdd(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Add(mOperandStack);
if (!status)
return NULL;
CharStringOperand valueA;
CharStringOperand valueB;
CharStringOperand newOperand;
if (mOperandStack.size() < 2)
return NULL;
valueB = mOperandStack.back();
mOperandStack.pop_back();
valueA = mOperandStack.back();
mOperandStack.pop_back();
if (!valueA.IsInteger || !valueB.IsInteger)
{
newOperand.IsInteger = false;
newOperand.RealValue =
(valueA.IsInteger ? (double)valueA.IntegerValue : valueA.RealValue)
+
(valueB.IsInteger ? (double)valueB.IntegerValue : valueB.RealValue);
}
else
{
newOperand.IsInteger = true;
newOperand.IntegerValue = valueA.IntegerValue + valueB.IntegerValue;
}
mOperandStack.push_back(newOperand);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretSub(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Sub(mOperandStack);
if (!status)
return NULL;
CharStringOperand valueA;
CharStringOperand valueB;
CharStringOperand newOperand;
if (mOperandStack.size() < 2)
return NULL;
valueB = mOperandStack.back();
mOperandStack.pop_back();
valueA = mOperandStack.back();
mOperandStack.pop_back();
if (!valueA.IsInteger || !valueB.IsInteger)
{
newOperand.IsInteger = false;
newOperand.RealValue =
(valueA.IsInteger ? (double)valueA.IntegerValue : valueA.RealValue)
-
(valueB.IsInteger ? (double)valueB.IntegerValue : valueB.RealValue);
}
else
{
newOperand.IsInteger = true;
newOperand.IntegerValue = valueA.IntegerValue - valueB.IntegerValue;
}
mOperandStack.push_back(newOperand);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretDiv(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Div(mOperandStack);
if (!status)
return NULL;
CharStringOperand valueA;
CharStringOperand valueB;
CharStringOperand newOperand;
if (mOperandStack.size() < 2)
return NULL;
valueB = mOperandStack.back();
mOperandStack.pop_back();
valueA = mOperandStack.back();
mOperandStack.pop_back();
if (!valueA.IsInteger || !valueB.IsInteger)
{
newOperand.IsInteger = false;
newOperand.RealValue =
(valueA.IsInteger ? (double)valueA.IntegerValue : valueA.RealValue)
/
(valueB.IsInteger ? (double)valueB.IntegerValue : valueB.RealValue);
}
else
{
newOperand.IsInteger = true;
newOperand.IntegerValue = valueA.IntegerValue / valueB.IntegerValue;
}
mOperandStack.push_back(newOperand);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretNeg(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Neg(mOperandStack);
if (!status)
return NULL;
CharStringOperand value;
CharStringOperand newOperand;
if (mOperandStack.size() < 1)
return NULL;
value = mOperandStack.back();
newOperand.IsInteger = value.IsInteger;
mOperandStack.pop_back();
if (value.IsInteger)
newOperand.IntegerValue = -value.IntegerValue;
else
newOperand.RealValue = -value.RealValue;
mOperandStack.push_back(newOperand);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretEq(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Eq(mOperandStack);
if (!status)
return NULL;
CharStringOperand valueA;
CharStringOperand valueB;
CharStringOperand newOperand;
if (mOperandStack.size() < 2)
return NULL;
valueB = mOperandStack.back();
mOperandStack.pop_back();
valueA = mOperandStack.back();
mOperandStack.pop_back();
newOperand.IsInteger = true;
newOperand.IntegerValue = (
(valueB.IsInteger ? valueB.IntegerValue : valueB.RealValue) ==
(valueA.IsInteger ? valueA.IntegerValue : valueA.RealValue)
) ? 1 : 0;
mOperandStack.push_back(newOperand);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretDrop(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Drop(mOperandStack);
if (!status)
return NULL;
if (mOperandStack.size() < 1)
return NULL;
mOperandStack.pop_back();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretPut(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Put(mOperandStack);
if (!status)
return NULL;
CharStringOperand valueA;
CharStringOperand valueB;
if (mOperandStack.size() < 2)
return NULL;
valueB = mOperandStack.back();
mOperandStack.pop_back();
valueA = mOperandStack.back();
mOperandStack.pop_back();
mStorage[(valueB.IsInteger ? valueB.IntegerValue : (long)valueB.RealValue)] = valueA;
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretGet(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Get(mOperandStack);
if (!status)
return NULL;
CharStringOperand value;
if (mOperandStack.size() < 1)
return NULL;
value = mOperandStack.back();
mOperandStack.pop_back();
long index = (value.IsInteger ? value.IntegerValue : (long)value.RealValue);
if ((mStorage.size() > (unsigned long)index) && (index >= 0))
{
mOperandStack.push_back(mStorage[index]);
return inProgramCounter;
}
return NULL;
}
BYTE* CharStringType2Interpreter::InterpretIfelse(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Ifelse(mOperandStack);
if (!status)
return NULL;
CharStringOperand valueA;
CharStringOperand valueB;
CharStringOperand valueC;
CharStringOperand valueD;
if (mOperandStack.size() < 4)
return NULL;
valueD = mOperandStack.back();
mOperandStack.pop_back();
valueC = mOperandStack.back();
mOperandStack.pop_back();
valueB = mOperandStack.back();
mOperandStack.pop_back();
valueA = mOperandStack.back();
mOperandStack.pop_back();
if (!valueC.IsInteger || !valueD.IsInteger)
{
if ((valueC.IsInteger ? (double)valueC.IntegerValue : valueC.RealValue) >
(valueD.IsInteger ? (double)valueD.IntegerValue : valueD.RealValue))
mOperandStack.push_back(valueB);
else
mOperandStack.push_back(valueA);
}
else
{
if (valueC.IntegerValue > valueD.IntegerValue)
mOperandStack.push_back(valueB);
else
mOperandStack.push_back(valueA);
}
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretRandom(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Random(mOperandStack);
if (!status)
return NULL;
CharStringOperand newOperand;
newOperand.IsInteger = false;
newOperand.RealValue = ((double)rand() + 1) / ((double)RAND_MAX + 1);
mOperandStack.push_back(newOperand);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretMul(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Mul(mOperandStack);
if (!status)
return NULL;
CharStringOperand valueA;
CharStringOperand valueB;
CharStringOperand newOperand;
if (mOperandStack.size() < 2)
return NULL;
valueB = mOperandStack.back();
mOperandStack.pop_back();
valueA = mOperandStack.back();
mOperandStack.pop_back();
if (!valueA.IsInteger || !valueB.IsInteger)
{
newOperand.IsInteger = false;
newOperand.RealValue =
(valueA.IsInteger ? (double)valueA.IntegerValue : valueA.RealValue)
*
(valueB.IsInteger ? (double)valueB.IntegerValue : valueB.RealValue);
}
else
{
newOperand.IsInteger = true;
newOperand.IntegerValue = valueA.IntegerValue * valueB.IntegerValue;
}
mOperandStack.push_back(newOperand);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretSqrt(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Sqrt(mOperandStack);
if (!status)
return NULL;
CharStringOperand value;
CharStringOperand newOperand;
if (mOperandStack.size() < 1)
return NULL;
value = mOperandStack.back();
mOperandStack.pop_back();
newOperand.IsInteger = false;
newOperand.RealValue = sqrt(value.IsInteger ? value.IntegerValue:value.RealValue);
mOperandStack.push_back(newOperand);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretDup(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Dup(mOperandStack);
if (!status)
return NULL;
if (mOperandStack.size() < 1)
return NULL;
mOperandStack.push_back(mOperandStack.back());
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretExch(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Exch(mOperandStack);
if (!status)
return NULL;
CharStringOperand valueA;
CharStringOperand valueB;
if (mOperandStack.size() < 2)
return NULL;
valueB = mOperandStack.back();
mOperandStack.pop_back();
valueA = mOperandStack.back();
mOperandStack.pop_back();
mOperandStack.push_back(valueB);
mOperandStack.push_back(valueA);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretIndex(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Index(mOperandStack);
if (!status)
return NULL;
CharStringOperand value;
if (mOperandStack.size() < 1)
return NULL;
value = mOperandStack.back();
mOperandStack.pop_back();
long index = (value.IsInteger ? value.IntegerValue : (long)value.RealValue);
CharStringOperandList::reverse_iterator it = mOperandStack.rbegin();
while (index > 0 && it != mOperandStack.rend())
++it;
mOperandStack.push_back(*it);
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretRoll(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Roll(mOperandStack);
if (!status)
return NULL;
CharStringOperand valueA;
CharStringOperand valueB;
if (mOperandStack.size() < 1)
return NULL;
valueB = mOperandStack.back();
mOperandStack.pop_back();
valueA = mOperandStack.back();
mOperandStack.pop_back();
long shiftAmount = (valueB.IsInteger ? valueB.IntegerValue : (long)valueB.RealValue);
long itemsCount = (valueA.IsInteger ? valueA.IntegerValue : (long)valueA.RealValue);
if (itemsCount > 0)
{
CharStringOperandList groupToShift;
for (long i = 0; i < itemsCount && mOperandStack.size() > 0; ++i)
{
groupToShift.push_front(mOperandStack.back());
mOperandStack.pop_back();
}
if (shiftAmount > 0)
{
for (long j = 0; j < shiftAmount; ++j)
{
groupToShift.push_front(groupToShift.back());
groupToShift.pop_back();
}
}
else
{
for (long j = 0; j < -shiftAmount; ++j)
{
groupToShift.push_back(groupToShift.front());
groupToShift.pop_front();
}
}
// put back the rolled group
for (long i = 0; i < itemsCount; ++i)
{
mOperandStack.push_back(groupToShift.front());
groupToShift.pop_front();
}
}
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretHFlex(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Hflex(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretFlex(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Flex(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretHFlex1(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Hflex1(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
BYTE* CharStringType2Interpreter::InterpretFlex1(BYTE* inProgramCounter, long long)
{
bool status = mImplementationHelper->Type2Flex1(mOperandStack);
if (!status)
return NULL;
ClearStack();
return inProgramCounter;
}
//----------------------------------------------------------------------------------------
// CharStringType2Flattener
//----------------------------------------------------------------------------------------
struct CharStringType2Flattener : public IType2InterpreterImplementation
{
CMemoryStream* mWriter;
CCFFReader* mHelper;
unsigned short mStemsCount;
CharStringOperandList mOperandsToWrite;
public:
CharStringType2Flattener();
~CharStringType2Flattener();
// will write a font program to another stream, flattening the references to subrs and gsubrs, so that
// the charstring becomes independent (with possible references to other charachters through seac-like endchar)
bool WriteFlattenedGlyphProgram(unsigned short inFontIndex, unsigned short inGlyphIndex, CCFFReader* inCFFFileInput, CMemoryStream* inWriter);
virtual bool ReadCharString(long long inCharStringStart, long long inCharStringEnd, BYTE** outCharString) override;
virtual bool Type2InterpretNumber(const CharStringOperand& inOperand) override;
virtual bool Type2Hstem(const CharStringOperandList& inOperandList) override;
bool WriteRegularOperator(unsigned short inOperatorCode);
bool WriteCharStringOperand(const CharStringOperand& inOperand);
bool WriteCharStringOperator(unsigned short inOperatorCode);
bool WriteByte(BYTE inValue);
virtual bool Type2Vstem(const CharStringOperandList& inOperandList) override;
virtual bool Type2Vmoveto(const CharStringOperandList& inOperandList) override;
virtual bool Type2Rlineto(const CharStringOperandList& inOperandList) override;
virtual bool Type2Hlineto(const CharStringOperandList& inOperandList) override;
virtual bool Type2Vlineto(const CharStringOperandList& inOperandList) override;
virtual bool Type2RRCurveto(const CharStringOperandList& inOperandList) override;
virtual bool Type2Return(const CharStringOperandList& inOperandList) override;
virtual bool Type2Endchar(const CharStringOperandList& inOperandList) override;
virtual bool Type2Hstemhm(const CharStringOperandList& inOperandList) override;
virtual bool Type2Hintmask(const CharStringOperandList& inOperandList, BYTE* inProgramCounter, long long inReadLimit) override;
bool WriteStemMask(BYTE* inProgramCounter, long long inReadLimit);
bool WriteSubrOperator(unsigned short inOperatorCode);
virtual bool Type2Cntrmask(const CharStringOperandList& inOperandList, BYTE* inProgramCounter, long long inReadLimit) override;
virtual bool Type2Rmoveto(const CharStringOperandList& inOperandList) override;
virtual bool Type2Hmoveto(const CharStringOperandList& inOperandList) override;
virtual bool Type2Vstemhm(const CharStringOperandList& inOperandList) override;
virtual bool Type2Rcurveline(const CharStringOperandList& inOperandList) override;
virtual bool Type2Rlinecurve(const CharStringOperandList& inOperandList) override;
virtual bool Type2Vvcurveto(const CharStringOperandList& inOperandList) override;
virtual bool Type2Hvcurveto(const CharStringOperandList& inOperandList) override;
virtual bool Type2Hhcurveto(const CharStringOperandList& inOperandList) override;
virtual bool Type2Vhcurveto(const CharStringOperandList& inOperandList) override;
virtual bool Type2Hflex(const CharStringOperandList& inOperandList) override;
virtual bool Type2Hflex1(const CharStringOperandList& inOperandList) override;
virtual bool Type2Flex(const CharStringOperandList& inOperandList) override;
virtual bool Type2Flex1(const CharStringOperandList& inOperandList) override;
virtual bool Type2And(const CharStringOperandList& inOperandList) override;
virtual bool Type2Or(const CharStringOperandList& inOperandList) override;
virtual bool Type2Not(const CharStringOperandList& inOperandList) override;
virtual bool Type2Abs(const CharStringOperandList& inOperandList) override;
virtual bool Type2Add(const CharStringOperandList& inOperandList) override;
virtual bool Type2Sub(const CharStringOperandList& inOperandList) override;
virtual bool Type2Div(const CharStringOperandList& inOperandList) override;
virtual bool Type2Neg(const CharStringOperandList& inOperandList) override;
virtual bool Type2Eq(const CharStringOperandList& inOperandList) override;
virtual bool Type2Drop(const CharStringOperandList& inOperandList) override;
virtual bool Type2Put(const CharStringOperandList& inOperandList) override;
virtual bool Type2Get(const CharStringOperandList& inOperandList) override;
virtual bool Type2Ifelse(const CharStringOperandList& inOperandList) override;
virtual bool Type2Random(const CharStringOperandList& inOperandList) override;
virtual bool Type2Mul(const CharStringOperandList& inOperandList) override;
virtual bool Type2Sqrt(const CharStringOperandList& inOperandList) override;
virtual bool Type2Dup(const CharStringOperandList& inOperandList) override;
virtual bool Type2Exch(const CharStringOperandList& inOperandList) override;
virtual bool Type2Index(const CharStringOperandList& inOperandList) override;
virtual bool Type2Roll(const CharStringOperandList& inOperandList) override;
virtual CharString* GetLocalSubr(long inSubrIndex) override;
virtual CharString* GetGlobalSubr(long inSubrIndex) override;
};
CharStringType2Flattener::CharStringType2Flattener()
{
}
CharStringType2Flattener::~CharStringType2Flattener()
{
}
bool CharStringType2Flattener::WriteFlattenedGlyphProgram(unsigned short inFontIndex, unsigned short inGlyphIndex, CCFFReader* inCFFFileInput, CMemoryStream* inWriter)
{
CharStringType2Interpreter interpreter;
bool status = inCFFFileInput->PrepareForGlyphIntepretation(inFontIndex, inGlyphIndex);
mWriter = inWriter;
mHelper = inCFFFileInput;
mOperandsToWrite.clear();
mStemsCount = 0;
if (!status)
return false;
CharString* charString = inCFFFileInput->GetGlyphCharString(inFontIndex, inGlyphIndex);
if (!charString)
return false;
status = interpreter.Intepret(*charString, this);
/*
The alrogithm for writing a flattened charstring is as follows:
1. enumerator, through interpretation, the charstring
2. hit an operand? accumulate.
3. hit an operator? if it's not callgsubr or callsubr just write the operand stack, and continue.
if it is callgsubr/callsubr pop the last element on the operand stack and write it, then continue.
4. an exception would be when callgsubr/callsubr follow an operator, in which case their index operand is already written. just call drop.
*/
return status;
}
bool CharStringType2Flattener::ReadCharString(long long inCharStringStart, long long inCharStringEnd, BYTE** outCharString)
{
return mHelper->ReadCharString(inCharStringStart, inCharStringEnd, outCharString);
}
bool CharStringType2Flattener::Type2InterpretNumber(const CharStringOperand& inOperand)
{
mOperandsToWrite.push_back(inOperand);
return true;
}
bool CharStringType2Flattener::Type2Hstem(const CharStringOperandList& inOperandList)
{
mStemsCount += (unsigned short)(inOperandList.size() / 2);
return WriteRegularOperator(1);
}
bool CharStringType2Flattener::WriteRegularOperator(unsigned short inOperatorCode)
{
CharStringOperandList::iterator it = mOperandsToWrite.begin();
bool status = true;
for (; it != mOperandsToWrite.end() && status; ++it)
status = WriteCharStringOperand(*it);
if (status)
status = WriteCharStringOperator(inOperatorCode);
mOperandsToWrite.clear();
return status;
}
bool CharStringType2Flattener::WriteCharStringOperand(const CharStringOperand& inOperand)
{
if (inOperand.IsInteger)
{
long value = inOperand.IntegerValue;
if (-107 <= value && value <= 107)
{
return WriteByte((BYTE)(value + 139));
}
else if (108 <= value && value <= 1131)
{
value -= 108;
WriteByte(((value >> 8) & 0xff) + 247);
WriteByte(value & 0xff);
}
else if (-1131 <= value && value <= -108)
{
value = -(value + 108);
WriteByte(((value >> 8) & 0xff) + 251);
WriteByte(value & 0xff);
}
else if (-32768 <= value && value<= 32767)
{
WriteByte(28);
WriteByte((value >> 8) & 0xff);
WriteByte(value & 0xff);
}
else
return false;
}
else
{
double value = inOperand.RealValue;
bool sign = inOperand.RealValue < 0;
if (sign)
value = -value;
long integerPart = (long)floor(value);
long realPart = (long)((value - floor(value)) * 65536);
if (sign)
integerPart = -integerPart;
WriteByte(BYTE(0xff));
WriteByte(BYTE((integerPart >> 8) & 0xff));
WriteByte(BYTE(integerPart & 0xff));
WriteByte(BYTE((realPart >> 8) & 0xff));
WriteByte(BYTE(realPart & 0xff));
}
return true;
}
bool CharStringType2Flattener::WriteCharStringOperator(unsigned short inOperatorCode)
{
if ((inOperatorCode & 0xff00) == 0x0c00)
{
if (!WriteByte(0x0c))
return false;
}
return WriteByte(BYTE(inOperatorCode & 0xff));
}
bool CharStringType2Flattener::WriteByte(BYTE inValue)
{
mWriter->Write(&inValue, 1);
return true;
}
bool CharStringType2Flattener::Type2Vstem(const CharStringOperandList& inOperandList)
{
mStemsCount += (unsigned short)(inOperandList.size() / 2);
return WriteRegularOperator(3);
}
bool CharStringType2Flattener::Type2Vmoveto(const CharStringOperandList&)
{
return WriteRegularOperator(4);
}
bool CharStringType2Flattener::Type2Rlineto(const CharStringOperandList&)
{
return WriteRegularOperator(5);
}
bool CharStringType2Flattener::Type2Hlineto(const CharStringOperandList&)
{
return WriteRegularOperator(6);
}
bool CharStringType2Flattener::Type2Vlineto(const CharStringOperandList&)
{
return WriteRegularOperator(7);
}
bool CharStringType2Flattener::Type2RRCurveto(const CharStringOperandList&)
{
return WriteRegularOperator(8);
}
bool CharStringType2Flattener::Type2Return(const CharStringOperandList&)
{
// ignore returns
return true;
}
bool CharStringType2Flattener::Type2Endchar(const CharStringOperandList&)
{
return WriteRegularOperator(14);
}
bool CharStringType2Flattener::Type2Hstemhm(const CharStringOperandList& inOperandList)
{
mStemsCount += (unsigned short)(inOperandList.size() / 2);
return WriteRegularOperator(18);
}
bool CharStringType2Flattener::Type2Hintmask(const CharStringOperandList& inOperandList, BYTE* inProgramCounter, long long inReadLimit)
{
mStemsCount += (unsigned short)(inOperandList.size() / 2);
if (!WriteRegularOperator(19))
return false;
return WriteStemMask(inProgramCounter, inReadLimit);
}
bool CharStringType2Flattener::WriteStemMask(BYTE* inProgramCounter, long long inReadLimit)
{
unsigned short maskSize = mStemsCount / 8 + (mStemsCount % 8 != 0 ? 1 : 0);
if (maskSize > inReadLimit)
return false;
mWriter->Write(inProgramCounter, maskSize);
return true;
}
bool CharStringType2Flattener::WriteSubrOperator(unsigned short)
{
if (mOperandsToWrite.size() > 0)
{
bool status = true;
mOperandsToWrite.pop_back(); // pop back parameter, which is the subr index
// now continue writing all operands
CharStringOperandList::iterator it = mOperandsToWrite.begin();
for (; it != mOperandsToWrite.end() && status; ++it)
status = WriteCharStringOperand(*it);
mOperandsToWrite.clear();
return status;
}
else // no current operands. either result of calculation or just multiple operators one of the other
return WriteCharStringOperator(0x0c12); // write a "drop" command for the subr index
}
bool CharStringType2Flattener::Type2Cntrmask(const CharStringOperandList& inOperandList, BYTE* inProgramCounter, long long inReadLimit)
{
mStemsCount += (unsigned short)(inOperandList.size() / 2);
if (!WriteRegularOperator(20))
return false;
return WriteStemMask(inProgramCounter, inReadLimit);
}
bool CharStringType2Flattener::Type2Rmoveto(const CharStringOperandList&)
{
return WriteRegularOperator(21);
}
bool CharStringType2Flattener::Type2Hmoveto(const CharStringOperandList&)
{
return WriteRegularOperator(22);
}
bool CharStringType2Flattener::Type2Vstemhm(const CharStringOperandList& inOperandList)
{
mStemsCount += (unsigned short)(inOperandList.size() / 2);
return WriteRegularOperator(23);
}
bool CharStringType2Flattener::Type2Rcurveline(const CharStringOperandList&)
{
return WriteRegularOperator(24);
}
bool CharStringType2Flattener::Type2Rlinecurve(const CharStringOperandList&)
{
return WriteRegularOperator(25);
}
bool CharStringType2Flattener::Type2Vvcurveto(const CharStringOperandList&)
{
return WriteRegularOperator(26);
}
bool CharStringType2Flattener::Type2Hvcurveto(const CharStringOperandList&)
{
return WriteRegularOperator(31);
}
bool CharStringType2Flattener::Type2Hhcurveto(const CharStringOperandList&)
{
return WriteRegularOperator(27);
}
bool CharStringType2Flattener::Type2Vhcurveto(const CharStringOperandList&)
{
return WriteRegularOperator(30);
}
bool CharStringType2Flattener::Type2Hflex(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c22);
}
bool CharStringType2Flattener::Type2Hflex1(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c24);
}
bool CharStringType2Flattener::Type2Flex(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c23);
}
bool CharStringType2Flattener::Type2Flex1(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c25);
}
bool CharStringType2Flattener::Type2And(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c03);
}
bool CharStringType2Flattener::Type2Or(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c04);
}
bool CharStringType2Flattener::Type2Not(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c05);
}
bool CharStringType2Flattener::Type2Abs(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c09);
}
bool CharStringType2Flattener::Type2Add(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c0a);
}
bool CharStringType2Flattener::Type2Sub(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c0b);
}
bool CharStringType2Flattener::Type2Div(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c0c);
}
bool CharStringType2Flattener::Type2Neg(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c0e);
}
bool CharStringType2Flattener::Type2Eq(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c0f);
}
bool CharStringType2Flattener::Type2Drop(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c12);
}
bool CharStringType2Flattener::Type2Put(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c14);
}
bool CharStringType2Flattener::Type2Get(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c15);
}
bool CharStringType2Flattener::Type2Ifelse(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c16);
}
bool CharStringType2Flattener::Type2Random(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c17);
}
bool CharStringType2Flattener::Type2Mul(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c18);
}
bool CharStringType2Flattener::Type2Sqrt(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c1a);
}
bool CharStringType2Flattener::Type2Dup(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c1b);
}
bool CharStringType2Flattener::Type2Exch(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c1c);
}
bool CharStringType2Flattener::Type2Index(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c1d);
}
bool CharStringType2Flattener::Type2Roll(const CharStringOperandList&)
{
return WriteRegularOperator(0x0c1e);
}
CharString* CharStringType2Flattener::GetLocalSubr(long inSubrIndex)
{
if (!WriteSubrOperator(10))
return NULL;
return mHelper->GetLocalSubr(inSubrIndex);
}
CharString* CharStringType2Flattener::GetGlobalSubr(long inSubrIndex)
{
if (!WriteSubrOperator(29))
return NULL;
return mHelper->GetGlobalSubr(inSubrIndex);
}
//----------------------------------------------------------------------------------------
// CCFFWriter
//----------------------------------------------------------------------------------------
const unsigned short scEmbeddedPostscript = 0xC15;
struct CCFFWriter
{
typedef std::pair<BYTE, unsigned short> ByteAndUShort;
typedef std::list<ByteAndUShort> ByteAndUShortList;
typedef std::map<FontDictInfo*, BYTE> FontDictInfoToByteMap;
typedef std::set<FontDictInfo*> FontDictInfoSet;
typedef std::pair<long long, long long> LongFilePositionTypePair;
typedef std::map<FontDictInfo*, LongFilePositionTypePair> FontDictInfoToLongFilePositionTypePairMap;
BYTE* mFile;
COpenTypeReader mOpenTypeInput;
CPrimitiveWriter* mPrimitivesWriter;
CStream* mFontFileStream;
bool mIsCID;
std::string mOptionalEmbeddedPostscript;
unsigned short mSubsetFontGlyphsCount;
int mCharsetPlaceHolderPosition;
int mEncodingPlaceHolderPosition;
int mCharstringsPlaceHolderPosition;
int mPrivatePlaceHolderPosition;
int mFDArrayPlaceHolderPosition;
int mFDSelectPlaceHolderPosition;
long long mEncodingPosition;
long long mCharsetPosition;
long long mCharStringPosition;
long long mPrivatePosition;
long long mPrivateSize;
long long mFDArrayPosition;
long long mFDSelectPosition;
public:
CCFFWriter();
~CCFFWriter();
bool CreateCFFSubset(BYTE* pFile, unsigned int nLen, unsigned short unFontIndex, const std::string& inSubsetFontName, CStream* pOutputStream, unsigned short* pCodeToGID, unsigned int unCodesCount);
bool WriteCFFHeader();
bool WriteName(const std::string& inSubsetFontName);
BYTE GetMostCompressedOffsetSize(unsigned long inOffset);
bool WriteTopIndex();
bool WriteTopDictSegment(CMemoryStream* ioTopDictSegment);
bool WriteStringIndex();
bool WriteGlobalSubrsIndex();
bool WriteEncodings(const std::vector<unsigned int>& inSubsetGlyphIDs);
bool WriteCharsets(const std::vector<unsigned int>& inSubsetGlyphIDs, std::vector<unsigned short>* inCIDMapping);
void DetermineFDArrayIndexes(const std::vector<unsigned int>& inSubsetGlyphIDs, FontDictInfoToByteMap& outNewFontDictsIndexes);
bool WriteFDSelect(const std::vector<unsigned int>& inSubsetGlyphIDs, const FontDictInfoToByteMap& inNewFontDictsIndexes);
bool WriteCharStrings(const std::vector<unsigned int>& inSubsetGlyphIDs);
bool WritePrivateDictionary();
void WritePrivateDictionaryBody(const PrivateDictInfo& inPrivateDictionary, long long& outWriteSize, long long& outWritePosition);
bool WriteFDArray(const std::vector<unsigned int>& inSubsetGlyphIDs, const FontDictInfoToByteMap& inNewFontDictsIndexes);
bool UpdateIndexesAtTopDict();
};
CCFFWriter::CCFFWriter()
{
mPrimitivesWriter = NULL;
}
CCFFWriter::~CCFFWriter()
{
RELEASEOBJECT(mPrimitivesWriter);
}
bool CCFFWriter::CreateCFFSubset(BYTE* pFile, unsigned int nLen, unsigned short unFontIndex, const std::string& inSubsetFontName, CStream* pOutputStream, unsigned short* pCodeToGID, unsigned int unCodesCount)
{
mFile = pFile;
bool status = mOpenTypeInput.ReadOpenTypeFile(pFile, nLen, unFontIndex);
if (!status)
return false;
if (mOpenTypeInput.GetOpenTypeFontType() != COpenTypeReader::EOpenTypeInputType::EOpenTypeCFF)
return false;
// see if font may be embedded
if (mOpenTypeInput.mOS2Exists && !FSType::CanEmbed(mOpenTypeInput.mOS2.fsType))
return true;
std::vector<unsigned int> subsetGlyphIDs;
for (unsigned long i = 0; i < unCodesCount; ++i)
subsetGlyphIDs.push_back(pCodeToGID[i]);
if (subsetGlyphIDs.empty())
subsetGlyphIDs.push_back(0);
// Add dependent glyphs
// They exist in m_vCodeToGid from pCodeToGID. They also exist in pUseGlyfs from m_mGlyphs, but only in m_mGlyphs they have false
mSubsetFontGlyphsCount = subsetGlyphIDs.size(); // == unCodesCount
mIsCID = mOpenTypeInput.mCFF.mTopDictIndex[0].mTopDict.find(scROS) != mOpenTypeInput.mCFF.mTopDictIndex[0].mTopDict.end();
mFontFileStream = pOutputStream;
mPrimitivesWriter = new CPrimitiveWriter(pOutputStream);
status = WriteCFFHeader();
if (!status)
return false;
status = WriteName(inSubsetFontName);
if (!status)
return false;
status = WriteTopIndex();
if (!status)
return false;
status = WriteStringIndex();
if (!status)
return false;
status = WriteGlobalSubrsIndex();
if (!status)
return false;
status = WriteEncodings(subsetGlyphIDs);
if (!status)
return false;
std::vector<unsigned short>* inCIDMapping = NULL;
status = WriteCharsets(subsetGlyphIDs, inCIDMapping);
if (!status)
return false;
FontDictInfoToByteMap newFDIndexes;
if (mIsCID)
{
DetermineFDArrayIndexes(subsetGlyphIDs, newFDIndexes);
status = WriteFDSelect(subsetGlyphIDs, newFDIndexes);
if (!status)
return false;
}
status = WriteCharStrings(subsetGlyphIDs);
if (!status)
return false;
status = WritePrivateDictionary();
if (!status)
return false;
if (mIsCID)
{
status = WriteFDArray(subsetGlyphIDs, newFDIndexes);
if (!status)
return false;
}
status = UpdateIndexesAtTopDict();
if (!status)
return false;
return true;
}
bool CCFFWriter::WriteCFFHeader()
{
// i'm just gonna copy the header of the original CFF
// content.
// One thing i never got - OffSize does not seem to be important.
// all offeet references to (0) are dictionary items (like in Top Dict),
// and reading them follows the Integer operand rules. so why signify their size.
// it's probably even not true, cause i guess font writers write integers using the most
// compressed method, so if the number is small they'll use less bytes, and if large more.
// so i don't get it. hope it won't screw up my implementation. in any case, for the sake of a single pass.
// i'll probably just set it to something.
mPrimitivesWriter->Write(mFile + mOpenTypeInput.mCFF.mCFFOffset, mOpenTypeInput.mCFF.mHeader.hdrSize);
return true;
}
bool CCFFWriter::WriteName(const std::string& inSubsetFontName)
{
// get the first name from the name table, and write it here
std::string fontName = inSubsetFontName.size() == 0 ? mOpenTypeInput.mCFF.mName.front() : inSubsetFontName;
BYTE sizeOfOffset = GetMostCompressedOffsetSize((unsigned long)fontName.size() + 1);
mPrimitivesWriter->WriteCard16(1);
mPrimitivesWriter->WriteOffSize(sizeOfOffset);
mPrimitivesWriter->SetOffSize(sizeOfOffset);
mPrimitivesWriter->WriteOffset(1);
mPrimitivesWriter->WriteOffset((unsigned long)fontName.size() + 1);
mPrimitivesWriter->Write((const BYTE*)fontName.c_str(), fontName.size());
return true;
}
BYTE CCFFWriter::GetMostCompressedOffsetSize(unsigned long inOffset)
{
if (inOffset < 256)
return 1;
if (inOffset < 65536)
return 2;
if (inOffset < 1 << 24)
return 3;
return 4;
}
bool CCFFWriter::WriteTopIndex()
{
/*
what do i have to do:
- write the top dictionary to a separate segment
- make sure to write the ROS variable first, if one exists.
- make sure to avoid writing any of the offset variables.
- leave placeholders for any of the offset variables. make them maximum size. note
to leave items for fdarray and fdselect only if required being a CID
- be aware of the placeholders locations relative to the beginning of the segment
- calculate the size of the segment
- write the appropriate index header
- write the segment
- adjust the placeholders offset relative to the beginning of the file.
*/
bool status = true;
CMemoryStream* topDictSegment = new CMemoryStream();
status = WriteTopDictSegment(topDictSegment);
if (!status)
{
RELEASEOBJECT(topDictSegment);
return status;
}
// write index section
BYTE sizeOfOffset = GetMostCompressedOffsetSize((unsigned long)topDictSegment->Tell() + 1);
mPrimitivesWriter->WriteCard16(1);
mPrimitivesWriter->WriteOffSize(sizeOfOffset);
mPrimitivesWriter->SetOffSize(sizeOfOffset);
mPrimitivesWriter->WriteOffset(1);
mPrimitivesWriter->WriteOffset((unsigned long)topDictSegment->Tell() + 1);
topDictSegment->Seek(0, SeekSet);
int topDictDataOffset = mFontFileStream->Tell();
// Write data
mFontFileStream->WriteStream(topDictSegment, 0, NULL);
// Adjust position locators for important placeholders
mCharsetPlaceHolderPosition += topDictDataOffset;
mEncodingPlaceHolderPosition += topDictDataOffset;
mCharstringsPlaceHolderPosition += topDictDataOffset;
mPrivatePlaceHolderPosition += topDictDataOffset;
mFDArrayPlaceHolderPosition += topDictDataOffset;
mFDSelectPlaceHolderPosition += topDictDataOffset;
return status;
}
bool CCFFWriter::WriteTopDictSegment(CMemoryStream* ioTopDictSegment)
{
CPrimitiveWriter dictPrimitiveWriter(ioTopDictSegment);
UShortToDictOperandListMap::iterator itROS;
UShortToDictOperandListMap::iterator it;
UShortToDictOperandListMap& originalTopDictRef = mOpenTypeInput.mCFF.mTopDictIndex[0].mTopDict;
itROS = originalTopDictRef.find(scROS);
// make sure to write ROS first, if one exists
if (mIsCID)
dictPrimitiveWriter.WriteDictItems(itROS->first, itROS->second);
// write all keys, excluding those that we want to write on our own
for (it = originalTopDictRef.begin(); it != originalTopDictRef.end(); ++it)
{
if (it->first != scROS &&
it->first != scCharset &&
it->first != scEncoding &&
it->first != scCharStrings &&
it->first != scPrivate &&
it->first != scFDArray &&
it->first != scFDSelect)
dictPrimitiveWriter.WriteDictItems(it->first, it->second);
}
// check if it had an embedded postscript (which would normally be the FSType implementation).
// if not...create one to implement the FSType
if (originalTopDictRef.find(scEmbeddedPostscript) == originalTopDictRef.end() && mOpenTypeInput.mOS2Exists)
{
// no need for sophistication here...you can consider this as the only string to be added.
// so can be sure that its index would be the current count
mOptionalEmbeddedPostscript = "/FSType " + std::to_string(mOpenTypeInput.mOS2.fsType) + " def";
dictPrimitiveWriter.WriteIntegerOperand(mOpenTypeInput.mCFF.mStringsCount + N_STD_STRINGS);
dictPrimitiveWriter.WriteDictOperator(scEmbeddedPostscript);
}
else
mOptionalEmbeddedPostscript = "";
// now leave placeholders, record their positions
mCharsetPlaceHolderPosition = ioTopDictSegment->Tell();
dictPrimitiveWriter.Pad5Bytes();
dictPrimitiveWriter.WriteDictOperator(scCharset);
mCharstringsPlaceHolderPosition = ioTopDictSegment->Tell();
dictPrimitiveWriter.Pad5Bytes();
dictPrimitiveWriter.WriteDictOperator(scCharStrings);
if (mOpenTypeInput.mCFF.mPrivateDicts[0].mPrivateDictStart != 0)
{
mPrivatePlaceHolderPosition = ioTopDictSegment->Tell();
dictPrimitiveWriter.Pad5Bytes(); // for private it's two places - size and position
dictPrimitiveWriter.Pad5Bytes();
dictPrimitiveWriter.WriteDictOperator(scPrivate);
}
else
{
mPrivatePlaceHolderPosition = 0;
}
if (mIsCID)
{
mEncodingPlaceHolderPosition = 0;
mFDArrayPlaceHolderPosition = ioTopDictSegment->Tell();
dictPrimitiveWriter.Pad5Bytes();
dictPrimitiveWriter.WriteDictOperator(scFDArray);
mFDSelectPlaceHolderPosition = ioTopDictSegment->Tell();
dictPrimitiveWriter.Pad5Bytes();
dictPrimitiveWriter.WriteDictOperator(scFDSelect);
}
else
{
mEncodingPlaceHolderPosition = ioTopDictSegment->Tell();
dictPrimitiveWriter.Pad5Bytes();
dictPrimitiveWriter.WriteDictOperator(scEncoding);
mFDArrayPlaceHolderPosition = 0;
mFDSelectPlaceHolderPosition = 0;
}
return true;
}
bool CCFFWriter::WriteStringIndex()
{
// if added a new string...needs to work hard, otherwise just copy the strings.
if (mOptionalEmbeddedPostscript.size() == 0)
{
// copy as is from the original file. note that the global subroutines
// starting position is equal to the strings end position. hence length is...
mFontFileStream->Write(mFile + mOpenTypeInput.mCFF.mCFFOffset + mOpenTypeInput.mCFF.mStringIndexPosition,
mOpenTypeInput.mCFF.mGlobalSubrsPosition - mOpenTypeInput.mCFF.mStringIndexPosition);
}
else
{
// need to write the bloody strings...[remember that i'm adding one more string at the end]
mPrimitivesWriter->WriteCard16(mOpenTypeInput.mCFF.mStringsCount + 1);
// calculate the total data size to determine the required offset size
unsigned long totalSize = 0;
for (int i = 0; i < mOpenTypeInput.mCFF.mStringsCount; ++i)
totalSize += (unsigned long)strlen(mOpenTypeInput.mCFF.mStrings[i]);
totalSize += (unsigned long)mOptionalEmbeddedPostscript.size();
BYTE sizeOfOffset = GetMostCompressedOffsetSize(totalSize + 1);
mPrimitivesWriter->WriteOffSize(sizeOfOffset);
mPrimitivesWriter->SetOffSize(sizeOfOffset);
unsigned long currentOffset = 1;
// write the offsets
for (int i = 0; i < mOpenTypeInput.mCFF.mStringsCount; ++i)
{
mPrimitivesWriter->WriteOffset(currentOffset);
currentOffset += (unsigned long)strlen(mOpenTypeInput.mCFF.mStrings[i]);
}
mPrimitivesWriter->WriteOffset(currentOffset);
currentOffset += (unsigned long)mOptionalEmbeddedPostscript.size();
mPrimitivesWriter->WriteOffset(currentOffset);
// write the data
for (int i = 0; i < mOpenTypeInput.mCFF.mStringsCount; ++i)
{
mFontFileStream->Write((const BYTE*)(mOpenTypeInput.mCFF.mStrings[i]), strlen(mOpenTypeInput.mCFF.mStrings[i]));
}
mFontFileStream->Write((const BYTE*)(mOptionalEmbeddedPostscript.c_str()), mOptionalEmbeddedPostscript.size());
}
return true;
}
bool CCFFWriter::WriteGlobalSubrsIndex()
{
// global subrs index is empty!. no subrs in my CFF outputs. all charstrings are flattened
return mPrimitivesWriter->WriteCard16(0);
}
bool CCFFWriter::WriteEncodings(const std::vector<unsigned int>& inSubsetGlyphIDs)
{
// if it's a CID. don't bother with encodings (marks as 0)
if (mIsCID)
{
mEncodingPosition = 0;
return true;
}
// not CID, write encoding, according to encoding values from the original font
EncodingsInfo* encodingInfo = mOpenTypeInput.mCFF.mTopDictIndex[0].mEncoding;
if (encodingInfo->mEncodingStart <= 1)
{
mEncodingPosition = encodingInfo->mEncodingStart;
return true;
}
else
{
// original font had custom encoding, let's subset it according to just the glyphs we
// actually have. but cause i'm lazy i'll just do the first format.
// figure out if we got supplements
std::vector<unsigned int>::const_iterator it = inSubsetGlyphIDs.begin();
ByteAndUShortList supplements;
for (; it != inSubsetGlyphIDs.end();++it)
{
// don't be confused! the supplements is by SID! not GID!
unsigned short sid = mOpenTypeInput.mCFF.GetGlyphSID(0, *it);
UShortToByteList::iterator itSupplements = encodingInfo->mSupplements.find(sid);
if (itSupplements != encodingInfo->mSupplements.end())
{
ByteList::iterator itMoreEncoding = itSupplements->second.begin();
for (; itMoreEncoding != itSupplements->second.end(); ++itMoreEncoding)
supplements.push_back(ByteAndUShort(*itMoreEncoding, sid));
}
}
mEncodingPosition = mFontFileStream->Tell();
if (supplements.size() > 0)
mPrimitivesWriter->WriteCard8(0x80);
else
mPrimitivesWriter->WriteCard8(0);
// assuming that 0 is in the subset glyphs IDs, which does not require encoding
// get the encodings count
BYTE encodingGlyphsCount = inSubsetGlyphIDs.empty() ? 0 : std::min((BYTE)(inSubsetGlyphIDs.size() - 1), encodingInfo->mEncodingsCount);
mPrimitivesWriter->WriteCard8(encodingGlyphsCount);
for (BYTE i = 0; i < encodingGlyphsCount; ++i)
{
if (inSubsetGlyphIDs[i + 1] < encodingInfo->mEncodingsCount && inSubsetGlyphIDs[i + 1] > 0)
mPrimitivesWriter->WriteCard8(encodingInfo->mEncoding[inSubsetGlyphIDs[i + 1] - 1]);
else
mPrimitivesWriter->WriteCard8(0);
}
if (supplements.size() > 0)
{
mPrimitivesWriter->WriteCard8(BYTE(supplements.size()));
ByteAndUShortList::iterator itCollectedSupplements = supplements.begin();
for (; itCollectedSupplements != supplements.end(); ++itCollectedSupplements)
{
mPrimitivesWriter->WriteCard8(itCollectedSupplements->first);
mPrimitivesWriter->WriteCard16(itCollectedSupplements->second);
}
}
}
return true;
}
bool CCFFWriter::WriteCharsets(const std::vector<unsigned int>& inSubsetGlyphIDs, std::vector<unsigned short>* inCIDMapping)
{
// since this is a subset the chances that i'll get a defult charset are 0.
// hence i'll always do some charset. and using format 0 !!1
std::vector<unsigned int>::const_iterator it = inSubsetGlyphIDs.begin();
++it; // skip the 0
mCharsetPosition = mFontFileStream->Tell();
mPrimitivesWriter->WriteCard8(0);
if (mIsCID)
{
int i = 1;
for (; it != inSubsetGlyphIDs.end(); ++it, ++i)
mPrimitivesWriter->WriteSID(inCIDMapping && i < (int)inCIDMapping->size() ? (*inCIDMapping)[i] : i);
}
else
{
// note that this also works for CIDs! cause in this case the SIDs are actually
// CIDs
for (; it != inSubsetGlyphIDs.end(); ++it)
mPrimitivesWriter->WriteSID(mOpenTypeInput.mCFF.GetGlyphSID(0, *it));
}
return true;
}
void CCFFWriter::DetermineFDArrayIndexes(const std::vector<unsigned int>& inSubsetGlyphIDs, FontDictInfoToByteMap& outNewFontDictsIndexes)
{
std::vector<unsigned int>::const_iterator itGlyphs = inSubsetGlyphIDs.begin();
FontDictInfoSet fontDictInfos;
for (; itGlyphs != inSubsetGlyphIDs.end(); ++itGlyphs)
if (mOpenTypeInput.mCFF.mTopDictIndex[0].mFDSelect[*itGlyphs])
fontDictInfos.insert(mOpenTypeInput.mCFF.mTopDictIndex[0].mFDSelect[*itGlyphs]);
FontDictInfoSet::iterator itFontInfos;
BYTE i = 0;
for (itFontInfos = fontDictInfos.begin(); itFontInfos != fontDictInfos.end(); ++itFontInfos, ++i)
outNewFontDictsIndexes.insert(FontDictInfoToByteMap::value_type(*itFontInfos, i));
}
bool CCFFWriter::WriteFDSelect(const std::vector<unsigned int>& inSubsetGlyphIDs, const FontDictInfoToByteMap& inNewFontDictsIndexes)
{
// always write format 3. cause at most cases the FD dicts count will be so low that it'd
// take a bloody mircale for no repeats to occur.
std::vector<unsigned int>::const_iterator itGlyphs = inSubsetGlyphIDs.begin();
mFDSelectPosition = mFontFileStream->Tell();
mPrimitivesWriter->WriteCard8(3);
long long rangesCountPosition = mFontFileStream->Tell();
mPrimitivesWriter->WriteCard16(1); // temporary. will get back to this later
unsigned short rangesCount = 1;
BYTE currentFD, newFD;
unsigned short glyphIndex = 1;
FontDictInfoToByteMap::const_iterator itNewIndex = inNewFontDictsIndexes.find(mOpenTypeInput.mCFF.mTopDictIndex[0].mFDSelect[*itGlyphs]);
// k. seems like i probably just imagine exceptions here. i guess there must
// be a proper FDSelect with FDs for all...so i'm defaulting to some 0
currentFD = (itNewIndex == inNewFontDictsIndexes.end() ? 0 : itNewIndex->second);
mPrimitivesWriter->WriteCard16(0);
mPrimitivesWriter->WriteCard8(currentFD);
++itGlyphs;
for (; itGlyphs != inSubsetGlyphIDs.end(); ++itGlyphs, ++glyphIndex)
{
itNewIndex = inNewFontDictsIndexes.find(mOpenTypeInput.mCFF.mTopDictIndex[0].mFDSelect[*itGlyphs]);
newFD = (itNewIndex == inNewFontDictsIndexes.end() ? 0 : itNewIndex->second);
if (newFD != currentFD)
{
currentFD = newFD;
mPrimitivesWriter->WriteCard16(glyphIndex);
mPrimitivesWriter->WriteCard8(currentFD);
++rangesCount;
}
}
mPrimitivesWriter->WriteCard16((unsigned short)inSubsetGlyphIDs.size());
// go back to ranges count if not equal to what's already written
if (rangesCount != 1)
{
long long currentPosition = mFontFileStream->Tell();
mFontFileStream->Seek(rangesCountPosition, SeekSet);
mPrimitivesWriter->WriteCard16(rangesCount);
mFontFileStream->Seek(currentPosition, SeekSet);
}
return true;
}
bool CCFFWriter::WriteCharStrings(const std::vector<unsigned int>& inSubsetGlyphIDs)
{
/*
1. build the charstrings data, looping the glyphs charstrings and writing a flattened
version of each charstring
2. write the charstring index based on offsets inside the data (size should be according to the max)
3. copy the data into the stream
*/
unsigned long* offsets = new unsigned long[inSubsetGlyphIDs.size() + 1];
// CMemoryStream charStringsData;
CMemoryStream charStringsDataWriteStream;
CharStringType2Flattener charStringFlattener;
std::vector<unsigned int>::const_iterator itGlyphs = inSubsetGlyphIDs.begin();
bool status = true;
unsigned short i = 0;
for (; itGlyphs != inSubsetGlyphIDs.end() && status; ++itGlyphs, ++i)
{
offsets[i] = (unsigned long)charStringsDataWriteStream.Tell();
status = charStringFlattener.WriteFlattenedGlyphProgram(0, *itGlyphs, &(mOpenTypeInput.mCFF), &charStringsDataWriteStream);
}
if (!status)
{
delete[] offsets;
return false;
}
offsets[i] = (unsigned long)charStringsDataWriteStream.Tell();
charStringsDataWriteStream.Seek(0, SeekSet);
// write index section
mCharStringPosition = mFontFileStream->Tell();
BYTE sizeOfOffset = GetMostCompressedOffsetSize(offsets[i] + 1);
mPrimitivesWriter->WriteCard16((unsigned short)inSubsetGlyphIDs.size());
mPrimitivesWriter->WriteOffSize(sizeOfOffset);
mPrimitivesWriter->SetOffSize(sizeOfOffset);
for (i = 0; i <= inSubsetGlyphIDs.size(); ++i)
mPrimitivesWriter->WriteOffset(offsets[i] + 1);
// Write data
mFontFileStream->WriteStream(&charStringsDataWriteStream, 0, NULL);
delete[] offsets;
return status;
}
bool CCFFWriter::WritePrivateDictionary()
{
WritePrivateDictionaryBody(mOpenTypeInput.mCFF.mPrivateDicts[0], mPrivateSize, mPrivatePosition);
return true;
}
void CCFFWriter::WritePrivateDictionaryBody(const PrivateDictInfo& inPrivateDictionary, long long& outWriteSize, long long& outWritePosition)
{
// just copy the private dict, without the subrs reference
if (inPrivateDictionary.mPrivateDictStart != 0)
{
UShortToDictOperandListMap::const_iterator it = inPrivateDictionary.mPrivateDict.begin();
outWritePosition = mFontFileStream->Tell();
for (; it != inPrivateDictionary.mPrivateDict.end(); ++it)
if (it->first != scSubrs) // should get me a nice little pattern for this some time..a filter thing
mPrimitivesWriter->WriteDictItems(it->first, it->second);
outWriteSize = mFontFileStream->Tell() - outWritePosition;
return;
}
outWritePosition = 0;
outWriteSize = 0;
}
bool CCFFWriter::WriteFDArray(const std::vector<unsigned int>&, const FontDictInfoToByteMap& inNewFontDictsIndexes)
{
// loop the glyphs IDs, for each get their respective dictionary. put them in a set.
// now itereate them, and write each private dictionary [no need for index]. save the private dictionary position.
// now write the FDArray. remember it's an index, so first write into a separate, maintain the offsets and only then write the actual buffer.
// save a mapping between the original pointer and a new index.
FontDictInfoToLongFilePositionTypePairMap privateDictionaries;
bool status = true;
unsigned long* offsets = NULL;
if (inNewFontDictsIndexes.size() == 0)
{
// if no valid font infos, write an empty index and finish
mFDArrayPosition = mFontFileStream->Tell();
mPrimitivesWriter->WriteCard16(0);
return true;
}
// loop the font infos, and write the private dictionaries
long long privatePosition, privateSize;
FontDictInfoToByteMap::const_iterator itFontInfos = inNewFontDictsIndexes.begin();
for (; itFontInfos != inNewFontDictsIndexes.end(); ++itFontInfos)
{
WritePrivateDictionaryBody(itFontInfos->first->mPrivateDict, privateSize, privatePosition);
privateDictionaries.insert(
FontDictInfoToLongFilePositionTypePairMap::value_type(itFontInfos->first, LongFilePositionTypePair(privateSize, privatePosition)));
}
// write FDArray segment
offsets = new unsigned long[inNewFontDictsIndexes.size() + 1];
CMemoryStream fontDictDataWriteStream;
CPrimitiveWriter fontDictPrimitiveWriter(&fontDictDataWriteStream);
BYTE i = 0;
for (itFontInfos = inNewFontDictsIndexes.begin(); itFontInfos != inNewFontDictsIndexes.end() && status; ++itFontInfos, ++i)
{
offsets[i] = (unsigned long)fontDictDataWriteStream.Tell();
UShortToDictOperandListMap::const_iterator itDict = itFontInfos->first->mFontDict.begin();
for (; itDict != itFontInfos->first->mFontDict.end() && status; ++itDict)
if (itDict->first != scPrivate) // should get me a nice little pattern for this some time..a filter thing
status = fontDictPrimitiveWriter.WriteDictItems(itDict->first, itDict->second);
// now add the private key
if (status && privateDictionaries[itFontInfos->first].first != 0)
{
fontDictPrimitiveWriter.WriteIntegerOperand(long(privateDictionaries[itFontInfos->first].first));
fontDictPrimitiveWriter.WriteIntegerOperand(long(privateDictionaries[itFontInfos->first].second));
fontDictPrimitiveWriter.WriteDictOperator(scPrivate);
}
}
if (!status)
{
delete[] offsets;
return false;
}
offsets[i] = (unsigned long)fontDictDataWriteStream.Tell();
fontDictDataWriteStream.Seek(0, SeekSet);
// write index section
mFDArrayPosition = mFontFileStream->Tell();
BYTE sizeOfOffset = GetMostCompressedOffsetSize(offsets[i] + 1);
mPrimitivesWriter->WriteCard16((unsigned short)inNewFontDictsIndexes.size());
mPrimitivesWriter->WriteOffSize(sizeOfOffset);
mPrimitivesWriter->SetOffSize(sizeOfOffset);
for (i = 0; i <= inNewFontDictsIndexes.size(); ++i)
mPrimitivesWriter->WriteOffset(offsets[i] + 1);
// Write data
mFontFileStream->WriteStream(&fontDictDataWriteStream, 0, NULL);
delete[] offsets;
return status;
}
bool CCFFWriter::UpdateIndexesAtTopDict()
{
mFontFileStream->Seek(mCharsetPlaceHolderPosition, SeekSet);
mPrimitivesWriter->Write5ByteDictInteger((long)mCharsetPosition);
mFontFileStream->Seek(mCharstringsPlaceHolderPosition, SeekSet);
mPrimitivesWriter->Write5ByteDictInteger((long)mCharStringPosition);
if (mOpenTypeInput.mCFF.mPrivateDicts[0].mPrivateDictStart != 0)
{
mFontFileStream->Seek(mPrivatePlaceHolderPosition, SeekSet);
mPrimitivesWriter->Write5ByteDictInteger((long)mPrivateSize);
mPrimitivesWriter->Write5ByteDictInteger((long)mPrivatePosition);
}
if (mIsCID)
{
mFontFileStream->Seek(mFDArrayPlaceHolderPosition, SeekSet);
mPrimitivesWriter->Write5ByteDictInteger((long)mFDArrayPosition);
mFontFileStream->Seek(mFDSelectPlaceHolderPosition, SeekSet);
mPrimitivesWriter->Write5ByteDictInteger((long)mFDSelectPosition);
}
else
{
mFontFileStream->Seek(mEncodingPlaceHolderPosition, SeekSet);
mPrimitivesWriter->Write5ByteDictInteger((long)mEncodingPosition);
}
return true;
}
//----------------------------------------------------------------------------------------
// CFontFileTrueType
//----------------------------------------------------------------------------------------
void CFontFileTrueType::WriteCIDFontType0C(CStream* pOutputStream, unsigned short* pCodeToGID, unsigned int unCodesCount)
{
if (!m_bOpenTypeCFF)
{
// If font is not OpenType CFF, exit
return;
}
CCFFWriter pWriter;
pWriter.CreateCFFSubset(m_sFile, m_nLen, m_unFontIndex, m_sName, pOutputStream, pCodeToGID, unCodesCount);
}
}