5049 lines
162 KiB
C++
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; }
|
||
|
||
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; // внешний, освобождать не надо
|
||
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);
|
||
|
||
// Добавить зависимые глифы
|
||
// Они есть в m_vCodeToGid из pCodeToGID. В pUseGlyfs они тоже есть из m_mGlyphs, но только в m_mGlyphs, они имеют 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 = std::min((BYTE)(inSubsetGlyphIDs.size() - 1), encodingInfo->mEncodingsCount);
|
||
|
||
mPrimitivesWriter->WriteCard8(encodingGlyphsCount);
|
||
for (BYTE i = 0; i < encodingGlyphsCount; ++i)
|
||
{
|
||
if (inSubsetGlyphIDs[i + 1] < encodingInfo->mEncodingsCount)
|
||
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 ? (*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)
|
||
{
|
||
// Если шрифт не является OpenType CFF, завершаем
|
||
return;
|
||
}
|
||
|
||
CCFFWriter pWriter;
|
||
pWriter.CreateCFFSubset(m_sFile, m_nLen, m_unFontIndex, m_sName, pOutputStream, pCodeToGID, unCodesCount);
|
||
}
|
||
}
|