Spaces:
Running
Running
//======================================================================== | |
// | |
// FoFiTrueType.cc | |
// | |
// Copyright 1999-2003 Glyph & Cog, LLC | |
// | |
//======================================================================== | |
//======================================================================== | |
// | |
// Modified under the Poppler project - http://poppler.freedesktop.org | |
// | |
// All changes made under the Poppler project to this file are licensed | |
// under GPL version 2 or later | |
// | |
// Copyright (C) 2006 Takashi Iwai <[email protected]> | |
// Copyright (C) 2007 Koji Otani <[email protected]> | |
// Copyright (C) 2007 Carlos Garcia Campos <[email protected]> | |
// Copyright (C) 2008, 2009, 2012, 2014-2022 Albert Astals Cid <[email protected]> | |
// Copyright (C) 2008 Tomas Are Haavet <[email protected]> | |
// Copyright (C) 2012 Suzuki Toshiya <[email protected]> | |
// Copyright (C) 2012, 2017 Adrian Johnson <[email protected]> | |
// Copyright (C) 2014 Thomas Freitag <[email protected]> | |
// Copyright (C) 2015 Aleksei Volkov <Aleksei Volkov> | |
// Copyright (C) 2015, 2016 William Bader <[email protected]> | |
// Copyright (C) 2018 Adam Reichold <[email protected]> | |
// Copyright (C) 2022 Zachary Travis <[email protected]> | |
// Copyright (C) 2022 Oliver Sander <[email protected]> | |
// | |
// To see a description of the changes please see the Changelog file that | |
// came with your tarball or type make ChangeLog if you are building from git | |
// | |
//======================================================================== | |
// | |
// Terminology | |
// ----------- | |
// | |
// character code = number used as an element of a text string | |
// | |
// character name = glyph name = name for a particular glyph within a | |
// font | |
// | |
// glyph index = GID = position (within some internal table in the font) | |
// where the instructions to draw a particular glyph are | |
// stored | |
// | |
// Type 1 fonts | |
// ------------ | |
// | |
// Type 1 fonts contain: | |
// | |
// Encoding: array of glyph names, maps char codes to glyph names | |
// | |
// Encoding[charCode] = charName | |
// | |
// CharStrings: dictionary of instructions, keyed by character names, | |
// maps character name to glyph data | |
// | |
// CharStrings[charName] = glyphData | |
// | |
// TrueType fonts | |
// -------------- | |
// | |
// TrueType fonts contain: | |
// | |
// 'cmap' table: mapping from character code to glyph index; there may | |
// be multiple cmaps in a TrueType font | |
// | |
// cmap[charCode] = gid | |
// | |
// 'post' table: mapping from glyph index to glyph name | |
// | |
// post[gid] = glyphName | |
// | |
// Type 42 fonts | |
// ------------- | |
// | |
// Type 42 fonts contain: | |
// | |
// Encoding: array of glyph names, maps char codes to glyph names | |
// | |
// Encoding[charCode] = charName | |
// | |
// CharStrings: dictionary of glyph indexes, keyed by character names, | |
// maps character name to glyph index | |
// | |
// CharStrings[charName] = gid | |
// | |
//------------------------------------------------------------------------ | |
//------------------------------------------------------------------------ | |
struct TrueTypeTable | |
{ | |
unsigned int tag; | |
unsigned int checksum; | |
int offset; | |
int origOffset; | |
int len; | |
}; | |
struct TrueTypeCmap | |
{ | |
int platform; | |
int encoding; | |
int offset; | |
int len; | |
int fmt; | |
}; | |
struct TrueTypeLoca | |
{ | |
int idx; | |
int origOffset; | |
int newOffset; | |
int len; | |
}; | |
struct cmpTrueTypeLocaOffsetFunctor | |
{ | |
bool operator()(const TrueTypeLoca loca1, const TrueTypeLoca loca2) | |
{ | |
if (loca1.origOffset == loca2.origOffset) { | |
return loca1.idx < loca2.idx; | |
} | |
return loca1.origOffset < loca2.origOffset; | |
} | |
}; | |
struct cmpTrueTypeLocaIdxFunctor | |
{ | |
bool operator()(const TrueTypeLoca loca1, const TrueTypeLoca loca2) { return loca1.idx < loca2.idx; } | |
}; | |
struct cmpTrueTypeTableTagFunctor | |
{ | |
bool operator()(const TrueTypeTable &tab1, const TrueTypeTable &tab2) { return tab1.tag < tab2.tag; } | |
}; | |
//------------------------------------------------------------------------ | |
struct T42Table | |
{ | |
const char *tag; // 4-byte tag | |
bool required; // required by the TrueType spec? | |
}; | |
// TrueType tables to be embedded in Type 42 fonts. | |
// NB: the table names must be in alphabetical order here. | |
static const T42Table t42Tables[nT42Tables] = { { "cvt ", true }, { "fpgm", true }, { "glyf", true }, { "head", true }, { "hhea", true }, { "hmtx", true }, | |
{ "loca", true }, { "maxp", true }, { "prep", true }, { "vhea", false }, { "vmtx", false } }; | |
//------------------------------------------------------------------------ | |
// Glyph names in some arbitrary standard order that Apple uses for | |
// their TrueType fonts. | |
static const char *macGlyphNames[258] = { ".notdef", | |
"null", | |
"CR", | |
"space", | |
"exclam", | |
"quotedbl", | |
"numbersign", | |
"dollar", | |
"percent", | |
"ampersand", | |
"quotesingle", | |
"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", | |
"grave", | |
"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", | |
"Adieresis", | |
"Aring", | |
"Ccedilla", | |
"Eacute", | |
"Ntilde", | |
"Odieresis", | |
"Udieresis", | |
"aacute", | |
"agrave", | |
"acircumflex", | |
"adieresis", | |
"atilde", | |
"aring", | |
"ccedilla", | |
"eacute", | |
"egrave", | |
"ecircumflex", | |
"edieresis", | |
"iacute", | |
"igrave", | |
"icircumflex", | |
"idieresis", | |
"ntilde", | |
"oacute", | |
"ograve", | |
"ocircumflex", | |
"odieresis", | |
"otilde", | |
"uacute", | |
"ugrave", | |
"ucircumflex", | |
"udieresis", | |
"dagger", | |
"degree", | |
"cent", | |
"sterling", | |
"section", | |
"bullet", | |
"paragraph", | |
"germandbls", | |
"registered", | |
"copyright", | |
"trademark", | |
"acute", | |
"dieresis", | |
"notequal", | |
"AE", | |
"Oslash", | |
"infinity", | |
"plusminus", | |
"lessequal", | |
"greaterequal", | |
"yen", | |
"mu", | |
"partialdiff", | |
"summation", | |
"product", | |
"pi", | |
"integral", | |
"ordfeminine", | |
"ordmasculine", | |
"Omega", | |
"ae", | |
"oslash", | |
"questiondown", | |
"exclamdown", | |
"logicalnot", | |
"radical", | |
"florin", | |
"approxequal", | |
"increment", | |
"guillemotleft", | |
"guillemotright", | |
"ellipsis", | |
"nbspace", | |
"Agrave", | |
"Atilde", | |
"Otilde", | |
"OE", | |
"oe", | |
"endash", | |
"emdash", | |
"quotedblleft", | |
"quotedblright", | |
"quoteleft", | |
"quoteright", | |
"divide", | |
"lozenge", | |
"ydieresis", | |
"Ydieresis", | |
"fraction", | |
"currency", | |
"guilsinglleft", | |
"guilsinglright", | |
"fi", | |
"fl", | |
"daggerdbl", | |
"periodcentered", | |
"quotesinglbase", | |
"quotedblbase", | |
"perthousand", | |
"Acircumflex", | |
"Ecircumflex", | |
"Aacute", | |
"Edieresis", | |
"Egrave", | |
"Iacute", | |
"Icircumflex", | |
"Idieresis", | |
"Igrave", | |
"Oacute", | |
"Ocircumflex", | |
"applelogo", | |
"Ograve", | |
"Uacute", | |
"Ucircumflex", | |
"Ugrave", | |
"dotlessi", | |
"circumflex", | |
"tilde", | |
"overscore", | |
"breve", | |
"dotaccent", | |
"ring", | |
"cedilla", | |
"hungarumlaut", | |
"ogonek", | |
"caron", | |
"Lslash", | |
"lslash", | |
"Scaron", | |
"scaron", | |
"Zcaron", | |
"zcaron", | |
"brokenbar", | |
"Eth", | |
"eth", | |
"Yacute", | |
"yacute", | |
"Thorn", | |
"thorn", | |
"minus", | |
"multiply", | |
"onesuperior", | |
"twosuperior", | |
"threesuperior", | |
"onehalf", | |
"onequarter", | |
"threequarters", | |
"franc", | |
"Gbreve", | |
"gbreve", | |
"Idot", | |
"Scedilla", | |
"scedilla", | |
"Cacute", | |
"cacute", | |
"Ccaron", | |
"ccaron", | |
"dmacron" }; | |
//------------------------------------------------------------------------ | |
// FoFiTrueType | |
//------------------------------------------------------------------------ | |
std::unique_ptr<FoFiTrueType> FoFiTrueType::make(const unsigned char *fileA, int lenA, int faceIndexA) | |
{ | |
// Cannot use std::make_unique, because the constructor is private | |
auto ff = new FoFiTrueType(fileA, lenA, false, faceIndexA); | |
if (!ff->parsedOk) { | |
delete ff; | |
return nullptr; | |
} | |
return std::unique_ptr<FoFiTrueType>(ff); | |
} | |
std::unique_ptr<FoFiTrueType> FoFiTrueType::load(const char *fileName, int faceIndexA) | |
{ | |
char *fileA; | |
int lenA; | |
if (!(fileA = FoFiBase::readFile(fileName, &lenA))) { | |
return nullptr; | |
} | |
// Cannot use std::make_unique, because the constructor is private | |
auto ff = new FoFiTrueType((unsigned char *)fileA, lenA, true, faceIndexA); | |
if (!ff->parsedOk) { | |
delete ff; | |
return nullptr; | |
} | |
return std::unique_ptr<FoFiTrueType>(ff); | |
} | |
FoFiTrueType::FoFiTrueType(const unsigned char *fileA, int lenA, bool freeFileDataA, int faceIndexA) : FoFiBase(fileA, lenA, freeFileDataA) | |
{ | |
tables = nullptr; | |
nTables = 0; | |
cmaps = nullptr; | |
nCmaps = 0; | |
parsedOk = false; | |
faceIndex = faceIndexA; | |
gsubFeatureTable = 0; | |
gsubLookupList = 0; | |
parse(); | |
} | |
FoFiTrueType::~FoFiTrueType() | |
{ | |
gfree(tables); | |
gfree(cmaps); | |
} | |
int FoFiTrueType::getNumCmaps() const | |
{ | |
return nCmaps; | |
} | |
int FoFiTrueType::getCmapPlatform(int i) const | |
{ | |
return cmaps[i].platform; | |
} | |
int FoFiTrueType::getCmapEncoding(int i) const | |
{ | |
return cmaps[i].encoding; | |
} | |
int FoFiTrueType::findCmap(int platform, int encoding) const | |
{ | |
int i; | |
for (i = 0; i < nCmaps; ++i) { | |
if (cmaps[i].platform == platform && cmaps[i].encoding == encoding) { | |
return i; | |
} | |
} | |
return -1; | |
} | |
int FoFiTrueType::mapCodeToGID(int i, unsigned int c) const | |
{ | |
int gid; | |
unsigned int segCnt, segEnd, segStart, segDelta, segOffset; | |
unsigned int cmapFirst, cmapLen; | |
int pos, a, b, m; | |
unsigned int high, low, idx; | |
bool ok; | |
if (i < 0 || i >= nCmaps) { | |
return 0; | |
} | |
ok = true; | |
pos = cmaps[i].offset; | |
switch (cmaps[i].fmt) { | |
case 0: | |
if (c + 6 >= (unsigned int)cmaps[i].len) { | |
return 0; | |
} | |
gid = getU8(cmaps[i].offset + 6 + c, &ok); | |
break; | |
case 2: | |
high = c >> 8; | |
low = c & 0xFFU; | |
idx = getU16BE(pos + 6 + high * 2, &ok); | |
segStart = getU16BE(pos + 6 + 512 + idx, &ok); | |
segCnt = getU16BE(pos + 6 + 512 + idx + 2, &ok); | |
segDelta = getS16BE(pos + 6 + 512 + idx + 4, &ok); | |
segOffset = getU16BE(pos + 6 + 512 + idx + 6, &ok); | |
if (low < segStart || low >= segStart + segCnt) { | |
gid = 0; | |
} else { | |
int val = getU16BE(pos + 6 + 512 + idx + 6 + segOffset + (low - segStart) * 2, &ok); | |
gid = val == 0 ? 0 : (val + segDelta) & 0xFFFFU; | |
} | |
break; | |
case 4: | |
segCnt = getU16BE(pos + 6, &ok) / 2; | |
a = -1; | |
b = segCnt - 1; | |
segEnd = getU16BE(pos + 14 + 2 * b, &ok); | |
if (c > segEnd) { | |
// malformed font -- the TrueType spec requires the last segEnd | |
// to be 0xffff | |
return 0; | |
} | |
// invariant: seg[a].end < code <= seg[b].end | |
while (b - a > 1 && ok) { | |
m = (a + b) / 2; | |
segEnd = getU16BE(pos + 14 + 2 * m, &ok); | |
if (segEnd < c) { | |
a = m; | |
} else { | |
b = m; | |
} | |
} | |
segStart = getU16BE(pos + 16 + 2 * segCnt + 2 * b, &ok); | |
segDelta = getU16BE(pos + 16 + 4 * segCnt + 2 * b, &ok); | |
segOffset = getU16BE(pos + 16 + 6 * segCnt + 2 * b, &ok); | |
if (c < segStart) { | |
return 0; | |
} | |
if (segOffset == 0) { | |
gid = (c + segDelta) & 0xffff; | |
} else { | |
gid = getU16BE(pos + 16 + 6 * segCnt + 2 * b + segOffset + 2 * (c - segStart), &ok); | |
if (gid != 0) { | |
gid = (gid + segDelta) & 0xffff; | |
} | |
} | |
break; | |
case 6: | |
cmapFirst = getU16BE(pos + 6, &ok); | |
cmapLen = getU16BE(pos + 8, &ok); | |
if (c < cmapFirst || c >= cmapFirst + cmapLen) { | |
return 0; | |
} | |
gid = getU16BE(pos + 10 + 2 * (c - cmapFirst), &ok); | |
break; | |
case 12: | |
case 13: | |
segCnt = getU32BE(pos + 12, &ok); | |
a = -1; | |
b = segCnt - 1; | |
segEnd = getU32BE(pos + 16 + 12 * b + 4, &ok); | |
if (c > segEnd) { | |
return 0; | |
} | |
// invariant: seg[a].end < code <= seg[b].end | |
while (b - a > 1 && ok) { | |
m = (a + b) / 2; | |
segEnd = getU32BE(pos + 16 + 12 * m + 4, &ok); | |
if (segEnd < c) { | |
a = m; | |
} else { | |
b = m; | |
} | |
} | |
segStart = getU32BE(pos + 16 + 12 * b, &ok); | |
segDelta = getU32BE(pos + 16 + 12 * b + 8, &ok); | |
if (c < segStart) { | |
return 0; | |
} | |
// In format 12, the glyph codes increment through | |
// each segment; in format 13 the same glyph code is used | |
// for an entire segment. | |
gid = segDelta + (cmaps[i].fmt == 12 ? (c - segStart) : 0); | |
break; | |
default: | |
return 0; | |
} | |
if (!ok) { | |
return 0; | |
} | |
return gid; | |
} | |
int FoFiTrueType::mapNameToGID(const char *name) const | |
{ | |
const auto gid = nameToGID.find(name); | |
if (gid == nameToGID.end()) { | |
return 0; | |
} | |
return gid->second; | |
} | |
bool FoFiTrueType::getCFFBlock(char **start, int *length) const | |
{ | |
int i; | |
if (!openTypeCFF || !tables) { | |
return false; | |
} | |
i = seekTable("CFF "); | |
if (i < 0 || !checkRegion(tables[i].offset, tables[i].len)) { | |
return false; | |
} | |
*start = (char *)file + tables[i].offset; | |
*length = tables[i].len; | |
return true; | |
} | |
int *FoFiTrueType::getCIDToGIDMap(int *nCIDs) const | |
{ | |
char *start; | |
int length; | |
FoFiType1C *ff; | |
int *map; | |
*nCIDs = 0; | |
if (!getCFFBlock(&start, &length)) { | |
return nullptr; | |
} | |
if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { | |
return nullptr; | |
} | |
map = ff->getCIDToGIDMap(nCIDs); | |
delete ff; | |
return map; | |
} | |
int FoFiTrueType::getEmbeddingRights() const | |
{ | |
int i, fsType; | |
bool ok; | |
if ((i = seekTable("OS/2")) < 0) { | |
return 4; | |
} | |
ok = true; | |
fsType = getU16BE(tables[i].offset + 8, &ok); | |
if (!ok) { | |
return 4; | |
} | |
if (fsType & 0x0008) { | |
return 2; | |
} | |
if (fsType & 0x0004) { | |
return 1; | |
} | |
if (fsType & 0x0002) { | |
return 0; | |
} | |
return 3; | |
} | |
void FoFiTrueType::getFontMatrix(double *mat) const | |
{ | |
char *start; | |
int length; | |
FoFiType1C *ff; | |
if (!getCFFBlock(&start, &length)) { | |
return; | |
} | |
if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { | |
return; | |
} | |
ff->getFontMatrix(mat); | |
delete ff; | |
} | |
void FoFiTrueType::convertToType42(const char *psName, char **encoding, int *codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const | |
{ | |
int maxUsedGlyph; | |
bool ok; | |
if (openTypeCFF) { | |
return; | |
} | |
// write the header | |
ok = true; | |
std::unique_ptr<GooString> buf = GooString::format("%!PS-TrueTypeFont-{0:2g}\n", (double)getS32BE(0, &ok) / 65536.0); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
// begin the font dictionary | |
(*outputFunc)(outputStream, "10 dict begin\n", 14); | |
(*outputFunc)(outputStream, "/FontName /", 11); | |
(*outputFunc)(outputStream, psName, strlen(psName)); | |
(*outputFunc)(outputStream, " def\n", 5); | |
(*outputFunc)(outputStream, "/FontType 42 def\n", 17); | |
(*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); | |
buf = GooString::format("/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
(*outputFunc)(outputStream, "/PaintType 0 def\n", 17); | |
// write the guts of the dictionary | |
cvtEncoding(encoding, outputFunc, outputStream); | |
cvtCharStrings(encoding, codeToGID, outputFunc, outputStream); | |
cvtSfnts(outputFunc, outputStream, nullptr, false, &maxUsedGlyph); | |
// end the dictionary and define the font | |
(*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40); | |
} | |
void FoFiTrueType::convertToType1(const char *psName, const char **newEncoding, bool ascii, FoFiOutputFunc outputFunc, void *outputStream) const | |
{ | |
char *start; | |
int length; | |
FoFiType1C *ff; | |
if (!getCFFBlock(&start, &length)) { | |
return; | |
} | |
if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { | |
return; | |
} | |
ff->convertToType1(psName, newEncoding, ascii, outputFunc, outputStream); | |
delete ff; | |
} | |
void FoFiTrueType::convertToCIDType2(const char *psName, const int *cidMap, int nCIDs, bool needVerticalMetrics, FoFiOutputFunc outputFunc, void *outputStream) const | |
{ | |
int cid, maxUsedGlyph; | |
bool ok; | |
int i, j, k; | |
if (openTypeCFF) { | |
return; | |
} | |
// write the header | |
ok = true; | |
std::unique_ptr<GooString> buf = GooString::format("%!PS-TrueTypeFont-{0:2g}\n", (double)getS32BE(0, &ok) / 65536.0); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
// begin the font dictionary | |
(*outputFunc)(outputStream, "20 dict begin\n", 14); | |
(*outputFunc)(outputStream, "/CIDFontName /", 14); | |
(*outputFunc)(outputStream, psName, strlen(psName)); | |
(*outputFunc)(outputStream, " def\n", 5); | |
(*outputFunc)(outputStream, "/CIDFontType 2 def\n", 19); | |
(*outputFunc)(outputStream, "/FontType 42 def\n", 17); | |
(*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32); | |
(*outputFunc)(outputStream, " /Registry (Adobe) def\n", 24); | |
(*outputFunc)(outputStream, " /Ordering (Identity) def\n", 27); | |
(*outputFunc)(outputStream, " /Supplement 0 def\n", 20); | |
(*outputFunc)(outputStream, " end def\n", 10); | |
(*outputFunc)(outputStream, "/GDBytes 2 def\n", 15); | |
if (cidMap) { | |
buf = GooString::format("/CIDCount {0:d} def\n", nCIDs); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
if (nCIDs > 32767) { | |
(*outputFunc)(outputStream, "/CIDMap [", 9); | |
for (i = 0; i < nCIDs; i += 32768 - 16) { | |
(*outputFunc)(outputStream, "<\n", 2); | |
for (j = 0; j < 32768 - 16 && i + j < nCIDs; j += 16) { | |
(*outputFunc)(outputStream, " ", 2); | |
for (k = 0; k < 16 && i + j + k < nCIDs; ++k) { | |
cid = cidMap[i + j + k]; | |
buf = GooString::format("{0:02x}{1:02x}", (cid >> 8) & 0xff, cid & 0xff); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
} | |
(*outputFunc)(outputStream, "\n", 1); | |
} | |
(*outputFunc)(outputStream, " >", 3); | |
} | |
(*outputFunc)(outputStream, "\n", 1); | |
(*outputFunc)(outputStream, "] def\n", 6); | |
} else { | |
(*outputFunc)(outputStream, "/CIDMap <\n", 10); | |
for (i = 0; i < nCIDs; i += 16) { | |
(*outputFunc)(outputStream, " ", 2); | |
for (j = 0; j < 16 && i + j < nCIDs; ++j) { | |
cid = cidMap[i + j]; | |
buf = GooString::format("{0:02x}{1:02x}", (cid >> 8) & 0xff, cid & 0xff); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
} | |
(*outputFunc)(outputStream, "\n", 1); | |
} | |
(*outputFunc)(outputStream, "> def\n", 6); | |
} | |
} else { | |
// direct mapping - just fill the string(s) with s[i]=i | |
buf = GooString::format("/CIDCount {0:d} def\n", nGlyphs); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
if (nGlyphs > 32767) { | |
(*outputFunc)(outputStream, "/CIDMap [\n", 10); | |
for (i = 0; i < nGlyphs; i += 32767) { | |
j = nGlyphs - i < 32767 ? nGlyphs - i : 32767; | |
buf = GooString::format(" {0:d} string 0 1 {1:d} {{\n", 2 * j, j - 1); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
buf = GooString::format(" 2 copy dup 2 mul exch {0:d} add -8 bitshift put\n", i); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
buf = GooString::format(" 1 index exch dup 2 mul 1 add exch {0:d} add" | |
" 255 and put\n", | |
i); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
(*outputFunc)(outputStream, " } for\n", 8); | |
} | |
(*outputFunc)(outputStream, "] def\n", 6); | |
} else { | |
buf = GooString::format("/CIDMap {0:d} string\n", 2 * nGlyphs); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
buf = GooString::format(" 0 1 {0:d} {{\n", nGlyphs - 1); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
(*outputFunc)(outputStream, " 2 copy dup 2 mul exch -8 bitshift put\n", 42); | |
(*outputFunc)(outputStream, " 1 index exch dup 2 mul 1 add exch 255 and put\n", 50); | |
(*outputFunc)(outputStream, " } for\n", 8); | |
(*outputFunc)(outputStream, "def\n", 4); | |
} | |
} | |
(*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); | |
buf = GooString::format("/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
(*outputFunc)(outputStream, "/PaintType 0 def\n", 17); | |
(*outputFunc)(outputStream, "/Encoding [] readonly def\n", 26); | |
(*outputFunc)(outputStream, "/CharStrings 1 dict dup begin\n", 30); | |
(*outputFunc)(outputStream, " /.notdef 0 def\n", 17); | |
(*outputFunc)(outputStream, " end readonly def\n", 19); | |
// write the guts of the dictionary | |
cvtSfnts(outputFunc, outputStream, nullptr, needVerticalMetrics, &maxUsedGlyph); | |
// end the dictionary and define the font | |
(*outputFunc)(outputStream, "CIDFontName currentdict end /CIDFont defineresource pop\n", 56); | |
} | |
void FoFiTrueType::convertToCIDType0(const char *psName, int *cidMap, int nCIDs, FoFiOutputFunc outputFunc, void *outputStream) const | |
{ | |
char *start; | |
int length; | |
FoFiType1C *ff; | |
if (!getCFFBlock(&start, &length)) { | |
return; | |
} | |
if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { | |
return; | |
} | |
ff->convertToCIDType0(psName, cidMap, nCIDs, outputFunc, outputStream); | |
delete ff; | |
} | |
void FoFiTrueType::convertToType0(const char *psName, int *cidMap, int nCIDs, bool needVerticalMetrics, int *maxValidGlyph, FoFiOutputFunc outputFunc, void *outputStream) const | |
{ | |
GooString *sfntsName; | |
int maxUsedGlyph, n, i, j; | |
*maxValidGlyph = -1; | |
if (openTypeCFF) { | |
return; | |
} | |
// write the Type 42 sfnts array | |
sfntsName = (new GooString(psName))->append("_sfnts"); | |
cvtSfnts(outputFunc, outputStream, sfntsName, needVerticalMetrics, &maxUsedGlyph); | |
delete sfntsName; | |
// write the descendant Type 42 fonts | |
// (The following is a kludge: nGlyphs is the glyph count from the | |
// maxp table; maxUsedGlyph is the max glyph number that has a | |
// non-zero-length description, from the loca table. The problem is | |
// that some TrueType font subsets fail to change the glyph count, | |
// i.e., nGlyphs is much larger than maxUsedGlyph+1, which results | |
// in an unnecessarily huge Type 0 font. But some other PDF files | |
// have fonts with only zero or one used glyph, and a content stream | |
// that refers to one of the unused glyphs -- this results in PS | |
// errors if we simply use maxUsedGlyph+1 for the Type 0 font. So | |
// we compromise by always defining at least 256 glyphs.) | |
// Some fonts have a large nGlyphs but maxUsedGlyph of 0. | |
// These fonts might reference any glyph. | |
// Return the last written glyph number in maxValidGlyph. | |
// PSOutputDev::drawString() can use maxValidGlyph to avoid | |
// referencing zero-length glyphs that we trimmed. | |
// This allows pdftops to avoid writing huge files while still | |
// handling the rare PDF that uses a zero-length glyph. | |
if (cidMap) { | |
n = nCIDs; | |
} else if (nGlyphs > maxUsedGlyph + 256) { | |
if (maxUsedGlyph <= 255) { | |
n = 256; | |
} else { | |
n = maxUsedGlyph + 1; | |
} | |
} else { | |
n = nGlyphs; | |
} | |
*maxValidGlyph = n - 1; | |
for (i = 0; i < n; i += 256) { | |
(*outputFunc)(outputStream, "10 dict begin\n", 14); | |
(*outputFunc)(outputStream, "/FontName /", 11); | |
(*outputFunc)(outputStream, psName, strlen(psName)); | |
std::unique_ptr<GooString> buf = GooString::format("_{0:02x} def\n", i >> 8); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
(*outputFunc)(outputStream, "/FontType 42 def\n", 17); | |
(*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); | |
buf = GooString::format("/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
(*outputFunc)(outputStream, "/PaintType 0 def\n", 17); | |
(*outputFunc)(outputStream, "/sfnts ", 7); | |
(*outputFunc)(outputStream, psName, strlen(psName)); | |
(*outputFunc)(outputStream, "_sfnts def\n", 11); | |
(*outputFunc)(outputStream, "/Encoding 256 array\n", 20); | |
for (j = 0; j < 256 && i + j < n; ++j) { | |
buf = GooString::format("dup {0:d} /c{1:02x} put\n", j, j); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
} | |
(*outputFunc)(outputStream, "readonly def\n", 13); | |
(*outputFunc)(outputStream, "/CharStrings 257 dict dup begin\n", 32); | |
(*outputFunc)(outputStream, "/.notdef 0 def\n", 15); | |
for (j = 0; j < 256 && i + j < n; ++j) { | |
buf = GooString::format("/c{0:02x} {1:d} def\n", j, cidMap ? cidMap[i + j] : i + j); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
} | |
(*outputFunc)(outputStream, "end readonly def\n", 17); | |
(*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40); | |
} | |
// write the Type 0 parent font | |
(*outputFunc)(outputStream, "16 dict begin\n", 14); | |
(*outputFunc)(outputStream, "/FontName /", 11); | |
(*outputFunc)(outputStream, psName, strlen(psName)); | |
(*outputFunc)(outputStream, " def\n", 5); | |
(*outputFunc)(outputStream, "/FontType 0 def\n", 16); | |
(*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); | |
(*outputFunc)(outputStream, "/FMapType 2 def\n", 16); | |
(*outputFunc)(outputStream, "/Encoding [\n", 12); | |
for (i = 0; i < n; i += 256) { | |
const std::unique_ptr<GooString> buf = GooString::format("{0:d}\n", i >> 8); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
} | |
(*outputFunc)(outputStream, "] def\n", 6); | |
(*outputFunc)(outputStream, "/FDepVector [\n", 14); | |
for (i = 0; i < n; i += 256) { | |
(*outputFunc)(outputStream, "/", 1); | |
(*outputFunc)(outputStream, psName, strlen(psName)); | |
const std::unique_ptr<GooString> buf = GooString::format("_{0:02x} findfont\n", i >> 8); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
} | |
(*outputFunc)(outputStream, "] def\n", 6); | |
(*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40); | |
} | |
void FoFiTrueType::convertToType0(const char *psName, int *cidMap, int nCIDs, FoFiOutputFunc outputFunc, void *outputStream) const | |
{ | |
char *start; | |
int length; | |
FoFiType1C *ff; | |
if (!getCFFBlock(&start, &length)) { | |
return; | |
} | |
if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { | |
return; | |
} | |
ff->convertToType0(psName, cidMap, nCIDs, outputFunc, outputStream); | |
delete ff; | |
} | |
void FoFiTrueType::cvtEncoding(char **encoding, FoFiOutputFunc outputFunc, void *outputStream) const | |
{ | |
const char *name; | |
int i; | |
(*outputFunc)(outputStream, "/Encoding 256 array\n", 20); | |
if (encoding) { | |
for (i = 0; i < 256; ++i) { | |
if (!(name = encoding[i])) { | |
name = ".notdef"; | |
} | |
const std::unique_ptr<GooString> buf = GooString::format("dup {0:d} /", i); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
(*outputFunc)(outputStream, name, strlen(name)); | |
(*outputFunc)(outputStream, " put\n", 5); | |
} | |
} else { | |
for (i = 0; i < 256; ++i) { | |
const std::unique_ptr<GooString> buf = GooString::format("dup {0:d} /c{1:02x} put\n", i, i); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
} | |
} | |
(*outputFunc)(outputStream, "readonly def\n", 13); | |
} | |
void FoFiTrueType::cvtCharStrings(char **encoding, const int *codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const | |
{ | |
const char *name; | |
char buf2[16]; | |
int i, k; | |
// always define '.notdef' | |
(*outputFunc)(outputStream, "/CharStrings 256 dict dup begin\n", 32); | |
(*outputFunc)(outputStream, "/.notdef 0 def\n", 15); | |
// if there's no 'cmap' table, punt | |
if (nCmaps == 0) { | |
goto err; | |
} | |
// map char name to glyph index: | |
// 1. use encoding to map name to char code | |
// 2. use codeToGID to map char code to glyph index | |
// N.B. We do this in reverse order because font subsets can have | |
// weird encodings that use the same character name twice, and | |
// the first definition is probably the one we want. | |
k = 0; // make gcc happy | |
for (i = 255; i >= 0; --i) { | |
if (encoding) { | |
name = encoding[i]; | |
} else { | |
sprintf(buf2, "c%02x", i); | |
name = buf2; | |
} | |
if (name && strcmp(name, ".notdef")) { | |
k = codeToGID[i]; | |
// note: Distiller (maybe Adobe's PS interpreter in general) | |
// doesn't like TrueType fonts that have CharStrings entries | |
// which point to nonexistent glyphs, hence the (k < nGlyphs) | |
// test | |
if (k > 0 && k < nGlyphs) { | |
(*outputFunc)(outputStream, "/", 1); | |
(*outputFunc)(outputStream, name, strlen(name)); | |
const std::unique_ptr<GooString> buf = GooString::format(" {0:d} def\n", k); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
} | |
} | |
} | |
err: | |
(*outputFunc)(outputStream, "end readonly def\n", 17); | |
} | |
void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const GooString *name, bool needVerticalMetrics, int *maxUsedGlyph) const | |
{ | |
unsigned char headData[54]; | |
TrueTypeLoca *locaTable; | |
unsigned char *locaData; | |
TrueTypeTable newTables[nT42Tables]; | |
unsigned char tableDir[12 + nT42Tables * 16]; | |
bool ok; | |
unsigned int checksum; | |
int nNewTables; | |
int glyfTableLen, length, pos, glyfPos, i, j, k, vmtxTabLength; | |
unsigned char vheaTab[36] = { | |
0, 1, 0, 0, // table version number | |
0, 0, // ascent | |
0, 0, // descent | |
0, 0, // reserved | |
0, 0, // max advance height | |
0, 0, // min top side bearing | |
0, 0, // min bottom side bearing | |
0, 0, // y max extent | |
0, 0, // caret slope rise | |
0, 1, // caret slope run | |
0, 0, // caret offset | |
0, 0, // reserved | |
0, 0, // reserved | |
0, 0, // reserved | |
0, 0, // reserved | |
0, 0, // metric data format | |
0, 1 // number of advance heights in vmtx table | |
}; | |
unsigned char *vmtxTab; | |
bool needVhea, needVmtx; | |
int advance; | |
*maxUsedGlyph = -1; | |
// construct the 'head' table, zero out the font checksum | |
i = seekTable("head"); | |
if (i < 0 || i >= nTables) { | |
return; | |
} | |
pos = tables[i].offset; | |
if (!checkRegion(pos, 54)) { | |
return; | |
} | |
memcpy(headData, file + pos, 54); | |
headData[8] = headData[9] = headData[10] = headData[11] = (unsigned char)0; | |
// check for a bogus loca format field in the 'head' table | |
// (I've encountered fonts with loca format set to 0x0100 instead of 0x0001) | |
if (locaFmt != 0 && locaFmt != 1) { | |
headData[50] = 0; | |
headData[51] = 1; | |
} | |
// read the original 'loca' table, pad entries out to 4 bytes, and | |
// sort it into proper order -- some (non-compliant) fonts have | |
// out-of-order loca tables; in order to correctly handle the case | |
// where (compliant) fonts have empty entries in the middle of the | |
// table, cmpTrueTypeLocaOffset uses offset as its primary sort key, | |
// and idx as its secondary key (ensuring that adjacent entries with | |
// the same pos value remain in the same order) | |
locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca)); | |
i = seekTable("loca"); | |
pos = tables[i].offset; | |
i = seekTable("glyf"); | |
glyfTableLen = tables[i].len; | |
ok = true; | |
for (i = 0; i <= nGlyphs; ++i) { | |
locaTable[i].idx = i; | |
if (locaFmt) { | |
locaTable[i].origOffset = (int)getU32BE(pos + i * 4, &ok); | |
} else { | |
locaTable[i].origOffset = 2 * getU16BE(pos + i * 2, &ok); | |
} | |
if (locaTable[i].origOffset > glyfTableLen) { | |
locaTable[i].origOffset = glyfTableLen; | |
} | |
} | |
std::sort(locaTable, locaTable + nGlyphs + 1, cmpTrueTypeLocaOffsetFunctor()); | |
for (i = 0; i < nGlyphs; ++i) { | |
locaTable[i].len = locaTable[i + 1].origOffset - locaTable[i].origOffset; | |
} | |
locaTable[nGlyphs].len = 0; | |
std::sort(locaTable, locaTable + nGlyphs + 1, cmpTrueTypeLocaIdxFunctor()); | |
pos = 0; | |
for (i = 0; i <= nGlyphs; ++i) { | |
locaTable[i].newOffset = pos; | |
int newPos; | |
if (unlikely(checkedAdd(pos, locaTable[i].len, &newPos))) { | |
ok = false; | |
} else { | |
pos = newPos; | |
if (pos & 3) { | |
pos += 4 - (pos & 3); | |
} | |
} | |
if (locaTable[i].len > 0) { | |
*maxUsedGlyph = i; | |
} | |
} | |
// construct the new 'loca' table | |
locaData = (unsigned char *)gmallocn(nGlyphs + 1, (locaFmt ? 4 : 2)); | |
for (i = 0; i <= nGlyphs; ++i) { | |
pos = locaTable[i].newOffset; | |
if (locaFmt) { | |
locaData[4 * i] = (unsigned char)(pos >> 24); | |
locaData[4 * i + 1] = (unsigned char)(pos >> 16); | |
locaData[4 * i + 2] = (unsigned char)(pos >> 8); | |
locaData[4 * i + 3] = (unsigned char)pos; | |
} else { | |
locaData[2 * i] = (unsigned char)(pos >> 9); | |
locaData[2 * i + 1] = (unsigned char)(pos >> 1); | |
} | |
} | |
// count the number of tables | |
nNewTables = 0; | |
for (i = 0; i < nT42Tables; ++i) { | |
if (t42Tables[i].required || seekTable(t42Tables[i].tag) >= 0) { | |
++nNewTables; | |
} | |
} | |
vmtxTab = nullptr; // make gcc happy | |
vmtxTabLength = 0; | |
advance = 0; // make gcc happy | |
if (needVerticalMetrics) { | |
needVhea = seekTable("vhea") < 0; | |
needVmtx = seekTable("vmtx") < 0; | |
if (needVhea || needVmtx) { | |
i = seekTable("head"); | |
advance = getU16BE(tables[i].offset + 18, &ok); // units per em | |
if (needVhea) { | |
++nNewTables; | |
} | |
if (needVmtx) { | |
++nNewTables; | |
} | |
} | |
} | |
// construct the new table headers, including table checksums | |
// (pad each table out to a multiple of 4 bytes) | |
pos = 12 + nNewTables * 16; | |
k = 0; | |
for (i = 0; i < nT42Tables; ++i) { | |
length = -1; | |
checksum = 0; // make gcc happy | |
if (i == t42HeadTable) { | |
length = 54; | |
checksum = computeTableChecksum(headData, 54); | |
} else if (i == t42LocaTable) { | |
length = (nGlyphs + 1) * (locaFmt ? 4 : 2); | |
checksum = computeTableChecksum(locaData, length); | |
} else if (i == t42GlyfTable) { | |
length = 0; | |
checksum = 0; | |
glyfPos = tables[seekTable("glyf")].offset; | |
for (j = 0; j < nGlyphs; ++j) { | |
length += locaTable[j].len; | |
if (length & 3) { | |
length += 4 - (length & 3); | |
} | |
if (checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) { | |
checksum += computeTableChecksum(file + glyfPos + locaTable[j].origOffset, locaTable[j].len); | |
} | |
} | |
} else { | |
if ((j = seekTable(t42Tables[i].tag)) >= 0) { | |
length = tables[j].len; | |
if (checkRegion(tables[j].offset, length)) { | |
checksum = computeTableChecksum(file + tables[j].offset, length); | |
} | |
} else if (needVerticalMetrics && i == t42VheaTable) { | |
vheaTab[10] = advance / 256; // max advance height | |
vheaTab[11] = advance % 256; | |
length = sizeof(vheaTab); | |
checksum = computeTableChecksum(vheaTab, length); | |
} else if (needVerticalMetrics && i == t42VmtxTable) { | |
length = 4 + (nGlyphs - 1) * 2; | |
vmtxTabLength = length; | |
vmtxTab = (unsigned char *)gmalloc(length); | |
vmtxTab[0] = advance / 256; | |
vmtxTab[1] = advance % 256; | |
for (j = 2; j < length; j += 2) { | |
vmtxTab[j] = 0; | |
vmtxTab[j + 1] = 0; | |
} | |
checksum = computeTableChecksum(vmtxTab, length); | |
} else if (t42Tables[i].required) { | |
//~ error(-1, "Embedded TrueType font is missing a required table ('%s')", | |
//~ t42Tables[i].tag); | |
length = 0; | |
checksum = 0; | |
} | |
} | |
if (length >= 0) { | |
newTables[k].tag = ((t42Tables[i].tag[0] & 0xff) << 24) | ((t42Tables[i].tag[1] & 0xff) << 16) | ((t42Tables[i].tag[2] & 0xff) << 8) | (t42Tables[i].tag[3] & 0xff); | |
newTables[k].checksum = checksum; | |
newTables[k].offset = pos; | |
newTables[k].len = length; | |
int newPos; | |
if (unlikely(checkedAdd(pos, length, &newPos))) { | |
ok = false; | |
} else { | |
pos = newPos; | |
if (pos & 3) { | |
pos += 4 - (length & 3); | |
} | |
} | |
++k; | |
} | |
} | |
if (unlikely(k < nNewTables)) { | |
error(errSyntaxWarning, -1, "unexpected number of tables"); | |
nNewTables = k; | |
} | |
// construct the table directory | |
tableDir[0] = 0x00; // sfnt version | |
tableDir[1] = 0x01; | |
tableDir[2] = 0x00; | |
tableDir[3] = 0x00; | |
tableDir[4] = 0; // numTables | |
tableDir[5] = nNewTables; | |
tableDir[6] = 0; // searchRange | |
tableDir[7] = (unsigned char)128; | |
tableDir[8] = 0; // entrySelector | |
tableDir[9] = 3; | |
tableDir[10] = 0; // rangeShift | |
tableDir[11] = (unsigned char)(16 * nNewTables - 128); | |
pos = 12; | |
for (i = 0; i < nNewTables; ++i) { | |
tableDir[pos] = (unsigned char)(newTables[i].tag >> 24); | |
tableDir[pos + 1] = (unsigned char)(newTables[i].tag >> 16); | |
tableDir[pos + 2] = (unsigned char)(newTables[i].tag >> 8); | |
tableDir[pos + 3] = (unsigned char)newTables[i].tag; | |
tableDir[pos + 4] = (unsigned char)(newTables[i].checksum >> 24); | |
tableDir[pos + 5] = (unsigned char)(newTables[i].checksum >> 16); | |
tableDir[pos + 6] = (unsigned char)(newTables[i].checksum >> 8); | |
tableDir[pos + 7] = (unsigned char)newTables[i].checksum; | |
tableDir[pos + 8] = (unsigned char)(newTables[i].offset >> 24); | |
tableDir[pos + 9] = (unsigned char)(newTables[i].offset >> 16); | |
tableDir[pos + 10] = (unsigned char)(newTables[i].offset >> 8); | |
tableDir[pos + 11] = (unsigned char)newTables[i].offset; | |
tableDir[pos + 12] = (unsigned char)(newTables[i].len >> 24); | |
tableDir[pos + 13] = (unsigned char)(newTables[i].len >> 16); | |
tableDir[pos + 14] = (unsigned char)(newTables[i].len >> 8); | |
tableDir[pos + 15] = (unsigned char)newTables[i].len; | |
pos += 16; | |
} | |
// compute the font checksum and store it in the head table | |
checksum = computeTableChecksum(tableDir, 12 + nNewTables * 16); | |
for (i = 0; i < nNewTables; ++i) { | |
checksum += newTables[i].checksum; | |
} | |
checksum = 0xb1b0afba - checksum; // because the TrueType spec says so | |
headData[8] = (unsigned char)(checksum >> 24); | |
headData[9] = (unsigned char)(checksum >> 16); | |
headData[10] = (unsigned char)(checksum >> 8); | |
headData[11] = (unsigned char)checksum; | |
// start the sfnts array | |
if (name) { | |
(*outputFunc)(outputStream, "/", 1); | |
(*outputFunc)(outputStream, name->c_str(), name->getLength()); | |
(*outputFunc)(outputStream, " [\n", 3); | |
} else { | |
(*outputFunc)(outputStream, "/sfnts [\n", 9); | |
} | |
// write the table directory | |
dumpString(tableDir, 12 + nNewTables * 16, outputFunc, outputStream); | |
// write the tables | |
for (i = 0; i < nNewTables; ++i) { | |
if (i == t42HeadTable) { | |
dumpString(headData, 54, outputFunc, outputStream); | |
} else if (i == t42LocaTable) { | |
length = (nGlyphs + 1) * (locaFmt ? 4 : 2); | |
dumpString(locaData, length, outputFunc, outputStream); | |
} else if (i == t42GlyfTable) { | |
glyfPos = tables[seekTable("glyf")].offset; | |
for (j = 0; j < nGlyphs; ++j) { | |
if (locaTable[j].len > 0 && checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) { | |
dumpString(file + glyfPos + locaTable[j].origOffset, locaTable[j].len, outputFunc, outputStream); | |
} | |
} | |
} else { | |
// length == 0 means the table is missing and the error was | |
// already reported during the construction of the table | |
// headers | |
if ((length = newTables[i].len) > 0) { | |
if ((j = seekTable(t42Tables[i].tag)) >= 0 && checkRegion(tables[j].offset, tables[j].len)) { | |
dumpString(file + tables[j].offset, tables[j].len, outputFunc, outputStream); | |
} else if (needVerticalMetrics && i == t42VheaTable) { | |
if (unlikely(length > (int)sizeof(vheaTab))) { | |
error(errSyntaxWarning, -1, "length bigger than vheaTab size"); | |
length = sizeof(vheaTab); | |
} | |
dumpString(vheaTab, length, outputFunc, outputStream); | |
} else if (needVerticalMetrics && i == t42VmtxTable) { | |
if (unlikely(length > vmtxTabLength)) { | |
error(errSyntaxWarning, -1, "length bigger than vmtxTab size"); | |
length = vmtxTabLength; | |
} | |
dumpString(vmtxTab, length, outputFunc, outputStream); | |
} | |
} | |
} | |
} | |
// end the sfnts array | |
(*outputFunc)(outputStream, "] def\n", 6); | |
gfree(locaData); | |
gfree(locaTable); | |
if (vmtxTab) { | |
gfree(vmtxTab); | |
} | |
} | |
void FoFiTrueType::dumpString(const unsigned char *s, int length, FoFiOutputFunc outputFunc, void *outputStream) const | |
{ | |
int pad, i, j; | |
(*outputFunc)(outputStream, "<", 1); | |
for (i = 0; i < length; i += 32) { | |
for (j = 0; j < 32 && i + j < length; ++j) { | |
const std::unique_ptr<GooString> buf = GooString::format("{0:02x}", s[i + j] & 0xff); | |
(*outputFunc)(outputStream, buf->c_str(), buf->getLength()); | |
} | |
if (i % (65536 - 32) == 65536 - 64) { | |
(*outputFunc)(outputStream, ">\n<", 3); | |
} else if (i + 32 < length) { | |
(*outputFunc)(outputStream, "\n", 1); | |
} | |
} | |
if (length & 3) { | |
pad = 4 - (length & 3); | |
for (i = 0; i < pad; ++i) { | |
(*outputFunc)(outputStream, "00", 2); | |
} | |
} | |
// add an extra zero byte because the Adobe Type 42 spec says so | |
(*outputFunc)(outputStream, "00>\n", 4); | |
} | |
unsigned int FoFiTrueType::computeTableChecksum(const unsigned char *data, int length) const | |
{ | |
unsigned int checksum, word; | |
int i; | |
checksum = 0; | |
for (i = 0; i + 3 < length; i += 4) { | |
word = ((data[i] & 0xff) << 24) + ((data[i + 1] & 0xff) << 16) + ((data[i + 2] & 0xff) << 8) + (data[i + 3] & 0xff); | |
checksum += word; | |
} | |
if (length & 3) { | |
word = 0; | |
i = length & ~3; | |
switch (length & 3) { | |
case 3: | |
word |= (data[i + 2] & 0xff) << 8; | |
// fallthrough | |
case 2: | |
word |= (data[i + 1] & 0xff) << 16; | |
// fallthrough | |
case 1: | |
word |= (data[i] & 0xff) << 24; | |
break; | |
} | |
checksum += word; | |
} | |
return checksum; | |
} | |
void FoFiTrueType::parse() | |
{ | |
unsigned int topTag; | |
int pos, ver, i, j; | |
parsedOk = true; | |
// look for a collection (TTC) | |
topTag = getU32BE(0, &parsedOk); | |
if (!parsedOk) { | |
return; | |
} | |
if (topTag == ttcfTag) { | |
/* TTC font */ | |
int dircount; | |
dircount = getU32BE(8, &parsedOk); | |
if (!parsedOk) { | |
return; | |
} | |
if (!dircount) { | |
parsedOk = false; | |
return; | |
} | |
if (faceIndex >= dircount) { | |
faceIndex = 0; | |
} | |
pos = getU32BE(12 + faceIndex * 4, &parsedOk); | |
if (!parsedOk) { | |
return; | |
} | |
} else { | |
pos = 0; | |
} | |
// check the sfnt version | |
ver = getU32BE(pos, &parsedOk); | |
if (!parsedOk) { | |
return; | |
} | |
openTypeCFF = ver == 0x4f54544f; // 'OTTO' | |
// read the table directory | |
nTables = getU16BE(pos + 4, &parsedOk); | |
if (!parsedOk) { | |
return; | |
} | |
tables = (TrueTypeTable *)gmallocn(nTables, sizeof(TrueTypeTable)); | |
pos += 12; | |
j = 0; | |
for (i = 0; i < nTables; ++i) { | |
tables[j].tag = getU32BE(pos, &parsedOk); | |
tables[j].checksum = getU32BE(pos + 4, &parsedOk); | |
tables[j].offset = (int)getU32BE(pos + 8, &parsedOk); | |
tables[j].len = (int)getU32BE(pos + 12, &parsedOk); | |
if (unlikely((tables[j].offset < 0) || (tables[j].len < 0) || (tables[j].offset < INT_MAX - tables[j].len) || (tables[j].len > INT_MAX - tables[j].offset) | |
|| (tables[j].offset + tables[j].len >= tables[j].offset && tables[j].offset + tables[j].len <= len))) { | |
// ignore any bogus entries in the table directory | |
++j; | |
} | |
pos += 16; | |
} | |
if (nTables != j) { | |
nTables = j; | |
tables = (TrueTypeTable *)greallocn_checkoverflow(tables, nTables, sizeof(TrueTypeTable)); | |
} | |
if (!parsedOk || tables == nullptr) { | |
parsedOk = false; | |
return; | |
} | |
// check for tables that are required by both the TrueType spec and | |
// the Type 42 spec | |
if (seekTable("head") < 0 || seekTable("hhea") < 0 || seekTable("maxp") < 0 || (!openTypeCFF && seekTable("loca") < 0) || (!openTypeCFF && seekTable("glyf") < 0) || (openTypeCFF && (seekTable("CFF ") < 0 && seekTable("CFF2") < 0))) { | |
parsedOk = false; | |
return; | |
} | |
// read the cmaps | |
if ((i = seekTable("cmap")) >= 0) { | |
pos = tables[i].offset + 2; | |
nCmaps = getU16BE(pos, &parsedOk); | |
pos += 2; | |
if (!parsedOk) { | |
return; | |
} | |
cmaps = (TrueTypeCmap *)gmallocn(nCmaps, sizeof(TrueTypeCmap)); | |
for (j = 0; j < nCmaps; ++j) { | |
cmaps[j].platform = getU16BE(pos, &parsedOk); | |
cmaps[j].encoding = getU16BE(pos + 2, &parsedOk); | |
cmaps[j].offset = tables[i].offset + getU32BE(pos + 4, &parsedOk); | |
pos += 8; | |
cmaps[j].fmt = getU16BE(cmaps[j].offset, &parsedOk); | |
cmaps[j].len = getU16BE(cmaps[j].offset + 2, &parsedOk); | |
} | |
if (!parsedOk) { | |
return; | |
} | |
} else { | |
nCmaps = 0; | |
} | |
// get the number of glyphs from the maxp table | |
i = seekTable("maxp"); | |
nGlyphs = getU16BE(tables[i].offset + 4, &parsedOk); | |
if (!parsedOk) { | |
return; | |
} | |
// get the bbox and loca table format from the head table | |
i = seekTable("head"); | |
bbox[0] = getS16BE(tables[i].offset + 36, &parsedOk); | |
bbox[1] = getS16BE(tables[i].offset + 38, &parsedOk); | |
bbox[2] = getS16BE(tables[i].offset + 40, &parsedOk); | |
bbox[3] = getS16BE(tables[i].offset + 42, &parsedOk); | |
locaFmt = getS16BE(tables[i].offset + 50, &parsedOk); | |
if (!parsedOk) { | |
return; | |
} | |
// read the post table | |
readPostTable(); | |
} | |
void FoFiTrueType::readPostTable() | |
{ | |
std::string name; | |
int tablePos, postFmt, stringIdx, stringPos; | |
bool ok; | |
int i, j, n, m; | |
ok = true; | |
if ((i = seekTable("post")) < 0) { | |
return; | |
} | |
tablePos = tables[i].offset; | |
postFmt = getU32BE(tablePos, &ok); | |
if (!ok) { | |
goto err; | |
} | |
if (postFmt == 0x00010000) { | |
nameToGID.reserve(258); | |
for (i = 0; i < 258; ++i) { | |
nameToGID.emplace(macGlyphNames[i], i); | |
} | |
} else if (postFmt == 0x00020000) { | |
nameToGID.reserve(258); | |
n = getU16BE(tablePos + 32, &ok); | |
if (!ok) { | |
goto err; | |
} | |
if (n > nGlyphs) { | |
n = nGlyphs; | |
} | |
stringIdx = 0; | |
stringPos = tablePos + 34 + 2 * n; | |
for (i = 0; i < n; ++i) { | |
ok = true; | |
j = getU16BE(tablePos + 34 + 2 * i, &ok); | |
if (j < 258) { | |
nameToGID[macGlyphNames[j]] = i; | |
} else { | |
j -= 258; | |
if (j != stringIdx) { | |
for (stringIdx = 0, stringPos = tablePos + 34 + 2 * n; stringIdx < j; ++stringIdx, stringPos += 1 + getU8(stringPos, &ok)) { | |
; | |
} | |
if (!ok) { | |
continue; | |
} | |
} | |
m = getU8(stringPos, &ok); | |
if (!ok || !checkRegion(stringPos + 1, m)) { | |
continue; | |
} | |
name.assign((char *)&file[stringPos + 1], m); | |
nameToGID[name] = i; | |
++stringIdx; | |
stringPos += 1 + m; | |
} | |
} | |
} else if (postFmt == 0x00028000) { | |
nameToGID.reserve(258); | |
for (i = 0; i < nGlyphs; ++i) { | |
j = getU8(tablePos + 32 + i, &ok); | |
if (!ok) { | |
continue; | |
} | |
if (j < 258) { | |
nameToGID[macGlyphNames[j]] = i; | |
} | |
} | |
} | |
return; | |
err: | |
nameToGID.clear(); | |
} | |
int FoFiTrueType::seekTable(const char *tag) const | |
{ | |
unsigned int tagI; | |
int i; | |
tagI = ((tag[0] & 0xff) << 24) | ((tag[1] & 0xff) << 16) | ((tag[2] & 0xff) << 8) | (tag[3] & 0xff); | |
for (i = 0; i < nTables; ++i) { | |
if (tables[i].tag == tagI) { | |
return i; | |
} | |
} | |
return -1; | |
} | |
unsigned int FoFiTrueType::charToTag(const char *tagName) | |
{ | |
int n = strlen(tagName); | |
unsigned int tag = 0; | |
int i; | |
if (n > 4) { | |
n = 4; | |
} | |
for (i = 0; i < n; i++) { | |
tag <<= 8; | |
tag |= tagName[i] & 0xff; | |
} | |
for (; i < 4; i++) { | |
tag <<= 8; | |
tag |= ' '; | |
} | |
return tag; | |
} | |
/* | |
setup GSUB table data | |
Only supporting vertical text substitution. | |
*/ | |
int FoFiTrueType::setupGSUB(const char *scriptName) | |
{ | |
return setupGSUB(scriptName, nullptr); | |
} | |
/* | |
setup GSUB table data | |
Only supporting vertical text substitution. | |
*/ | |
int FoFiTrueType::setupGSUB(const char *scriptName, const char *languageName) | |
{ | |
unsigned int gsubTable; | |
unsigned int i; | |
unsigned int scriptList, featureList; | |
unsigned int scriptCount; | |
unsigned int tag; | |
unsigned int scriptTable = 0; | |
unsigned int langSys; | |
unsigned int featureCount; | |
unsigned int featureIndex; | |
unsigned int ftable = 0; | |
unsigned int llist; | |
unsigned int scriptTag; | |
int x; | |
unsigned int pos; | |
if (scriptName == nullptr) { | |
gsubFeatureTable = 0; | |
return 0; | |
} | |
scriptTag = charToTag(scriptName); | |
/* read GSUB Header */ | |
if ((x = seekTable("GSUB")) < 0) { | |
return 0; /* GSUB table not found */ | |
} | |
gsubTable = tables[x].offset; | |
pos = gsubTable + 4; | |
scriptList = getU16BE(pos, &parsedOk); | |
pos += 2; | |
featureList = getU16BE(pos, &parsedOk); | |
pos += 2; | |
llist = getU16BE(pos, &parsedOk); | |
gsubLookupList = llist + gsubTable; /* change to offset from top of file */ | |
/* read script list table */ | |
pos = gsubTable + scriptList; | |
scriptCount = getU16BE(pos, &parsedOk); | |
pos += 2; | |
/* find script */ | |
for (i = 0; i < scriptCount; i++) { | |
tag = getU32BE(pos, &parsedOk); | |
pos += 4; | |
scriptTable = getU16BE(pos, &parsedOk); | |
pos += 2; | |
if (tag == scriptTag) { | |
/* found */ | |
break; | |
} | |
} | |
if (i >= scriptCount) { | |
/* not found */ | |
return 0; | |
} | |
/* read script table */ | |
/* use default language system */ | |
pos = gsubTable + scriptList + scriptTable; | |
langSys = 0; | |
if (languageName) { | |
unsigned int langTag = charToTag(languageName); | |
unsigned int langCount = getU16BE(pos + 2, &parsedOk); | |
for (i = 0; i < langCount && langSys == 0; i++) { | |
tag = getU32BE(pos + 4 + i * (4 + 2), &parsedOk); | |
if (tag == langTag) { | |
langSys = getU16BE(pos + 4 + i * (4 + 2) + 4, &parsedOk); | |
} | |
} | |
} | |
if (langSys == 0) { | |
/* default language system */ | |
langSys = getU16BE(pos, &parsedOk); | |
} | |
/* read LangSys table */ | |
if (langSys == 0) { | |
/* no default LangSys */ | |
return 0; | |
} | |
pos = gsubTable + scriptList + scriptTable + langSys + 2; | |
featureIndex = getU16BE(pos, &parsedOk); /* ReqFeatureIndex */ | |
pos += 2; | |
if (featureIndex != 0xffff) { | |
unsigned int tpos; | |
/* read feature record */ | |
tpos = gsubTable + featureList; | |
featureCount = getU16BE(tpos, &parsedOk); | |
tpos = gsubTable + featureList + 2 + featureIndex * (4 + 2); | |
tag = getU32BE(tpos, &parsedOk); | |
tpos += 4; | |
if (tag == vrt2Tag) { | |
/* vrt2 is preferred, overwrite vert */ | |
ftable = getU16BE(tpos, &parsedOk); | |
/* convert to offset from file top */ | |
gsubFeatureTable = ftable + gsubTable + featureList; | |
return 0; | |
} else if (tag == vertTag) { | |
ftable = getU16BE(tpos, &parsedOk); | |
} | |
} | |
featureCount = getU16BE(pos, &parsedOk); | |
pos += 2; | |
/* find 'vrt2' or 'vert' feature */ | |
for (i = 0; i < featureCount; i++) { | |
unsigned int oldPos; | |
featureIndex = getU16BE(pos, &parsedOk); | |
pos += 2; | |
oldPos = pos; /* save position */ | |
/* read feature record */ | |
pos = gsubTable + featureList + 2 + featureIndex * (4 + 2); | |
tag = getU32BE(pos, &parsedOk); | |
pos += 4; | |
if (tag == vrt2Tag) { | |
/* vrt2 is preferred, overwrite vert */ | |
ftable = getU16BE(pos, &parsedOk); | |
break; | |
} else if (ftable == 0 && tag == vertTag) { | |
ftable = getU16BE(pos, &parsedOk); | |
} | |
pos = oldPos; /* restore old position */ | |
} | |
if (ftable == 0) { | |
/* vert nor vrt2 are not found */ | |
return 0; | |
} | |
/* convert to offset from file top */ | |
gsubFeatureTable = ftable + gsubTable + featureList; | |
return 0; | |
} | |
unsigned int FoFiTrueType::doMapToVertGID(unsigned int orgGID) | |
{ | |
unsigned int lookupCount; | |
unsigned int lookupListIndex; | |
unsigned int i; | |
unsigned int gid = 0; | |
unsigned int pos; | |
pos = gsubFeatureTable + 2; | |
lookupCount = getU16BE(pos, &parsedOk); | |
pos += 2; | |
for (i = 0; i < lookupCount; i++) { | |
lookupListIndex = getU16BE(pos, &parsedOk); | |
pos += 2; | |
if ((gid = scanLookupList(lookupListIndex, orgGID)) != 0) { | |
break; | |
} | |
} | |
return gid; | |
} | |
unsigned int FoFiTrueType::mapToVertGID(unsigned int orgGID) | |
{ | |
unsigned int mapped; | |
if (gsubFeatureTable == 0) { | |
return orgGID; | |
} | |
if ((mapped = doMapToVertGID(orgGID)) != 0) { | |
return mapped; | |
} | |
return orgGID; | |
} | |
unsigned int FoFiTrueType::scanLookupList(unsigned int listIndex, unsigned int orgGID) | |
{ | |
unsigned int lookupTable; | |
unsigned int subTableCount; | |
unsigned int subTable; | |
unsigned int i; | |
unsigned int gid = 0; | |
unsigned int pos; | |
if (gsubLookupList == 0) { | |
return 0; /* no lookup list */ | |
} | |
pos = gsubLookupList + 2 + listIndex * 2; | |
lookupTable = getU16BE(pos, &parsedOk); | |
/* read lookup table */ | |
pos = gsubLookupList + lookupTable + 4; | |
subTableCount = getU16BE(pos, &parsedOk); | |
pos += 2; | |
; | |
for (i = 0; i < subTableCount; i++) { | |
subTable = getU16BE(pos, &parsedOk); | |
pos += 2; | |
if ((gid = scanLookupSubTable(gsubLookupList + lookupTable + subTable, orgGID)) != 0) { | |
break; | |
} | |
} | |
return gid; | |
} | |
unsigned int FoFiTrueType::scanLookupSubTable(unsigned int subTable, unsigned int orgGID) | |
{ | |
unsigned int format; | |
unsigned int coverage; | |
int delta; | |
int glyphCount; | |
unsigned int substitute; | |
unsigned int gid = 0; | |
int coverageIndex; | |
int pos; | |
pos = subTable; | |
format = getU16BE(pos, &parsedOk); | |
pos += 2; | |
coverage = getU16BE(pos, &parsedOk); | |
pos += 2; | |
if ((coverageIndex = checkGIDInCoverage(subTable + coverage, orgGID)) >= 0) { | |
switch (format) { | |
case 1: | |
/* format 1 */ | |
delta = getS16BE(pos, &parsedOk); | |
pos += 2; | |
gid = orgGID + delta; | |
break; | |
case 2: | |
/* format 2 */ | |
glyphCount = getS16BE(pos, &parsedOk); | |
pos += 2; | |
if (glyphCount > coverageIndex) { | |
pos += coverageIndex * 2; | |
substitute = getU16BE(pos, &parsedOk); | |
gid = substitute; | |
} | |
break; | |
default: | |
/* unknown format */ | |
break; | |
} | |
} | |
return gid; | |
} | |
int FoFiTrueType::checkGIDInCoverage(unsigned int coverage, unsigned int orgGID) | |
{ | |
int index = -1; | |
unsigned int format; | |
unsigned int count; | |
unsigned int i; | |
unsigned int pos; | |
pos = coverage; | |
format = getU16BE(pos, &parsedOk); | |
pos += 2; | |
switch (format) { | |
case 1: | |
count = getU16BE(pos, &parsedOk); | |
pos += 2; | |
// In some poor CJK fonts, key GIDs are not sorted, | |
// thus we cannot finish checking even when the range | |
// including orgGID seems to have already passed. | |
for (i = 0; i < count; i++) { | |
unsigned int gid; | |
gid = getU16BE(pos, &parsedOk); | |
pos += 2; | |
if (gid == orgGID) { | |
/* found */ | |
index = i; | |
break; | |
} | |
} | |
break; | |
case 2: | |
count = getU16BE(pos, &parsedOk); | |
pos += 2; | |
for (i = 0; i < count; i++) { | |
unsigned int startGID, endGID; | |
unsigned int startIndex; | |
startGID = getU16BE(pos, &parsedOk); | |
pos += 2; | |
endGID = getU16BE(pos, &parsedOk); | |
pos += 2; | |
startIndex = getU16BE(pos, &parsedOk); | |
pos += 2; | |
// In some poor CJK fonts, key GIDs are not sorted, | |
// thus we cannot finish checking even when the range | |
// including orgGID seems to have already passed. | |
if (startGID <= orgGID && orgGID <= endGID) { | |
/* found */ | |
index = startIndex + orgGID - startGID; | |
break; | |
} | |
} | |
break; | |
default: | |
break; | |
} | |
return index; | |
} | |