mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-04-10 19:15:13 +00:00
2019 lines
70 KiB
Plaintext
2019 lines
70 KiB
Plaintext
//========================================================================
|
|
//
|
|
// 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 <tiwai@suse.de>
|
|
// Copyright (C) 2007 Koji Otani <sho@bbr.jp>
|
|
// Copyright (C) 2007 Carlos Garcia Campos <carlosgc@gnome.org>
|
|
// Copyright (C) 2008, 2009, 2012, 2014-2022 Albert Astals Cid <aacid@kde.org>
|
|
// Copyright (C) 2008 Tomas Are Haavet <tomasare@gmail.com>
|
|
// Copyright (C) 2012 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
|
|
// Copyright (C) 2012, 2017 Adrian Johnson <ajohnson@redneon.com>
|
|
// Copyright (C) 2014 Thomas Freitag <Thomas.Freitag@alfa.de>
|
|
// Copyright (C) 2015 Aleksei Volkov <Aleksei Volkov>
|
|
// Copyright (C) 2015, 2016 William Bader <williambader@hotmail.com>
|
|
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
|
|
// Copyright (C) 2022 Zachary Travis <ztravis@everlaw.com>
|
|
// Copyright (C) 2022 Oliver Sander <oliver.sander@tu-dresden.de>
|
|
//
|
|
// 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
|
|
//
|
|
//========================================================================
|
|
|
|
#include <config.h>
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <climits>
|
|
#include <algorithm>
|
|
#include "goo/gmem.h"
|
|
#include "goo/GooLikely.h"
|
|
#include "goo/GooString.h"
|
|
#include "FoFiType1C.h"
|
|
#include "FoFiTrueType.h"
|
|
#include "poppler/Error.h"
|
|
|
|
//
|
|
// 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
|
|
//
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
#define ttcfTag 0x74746366
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
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;
|
|
};
|
|
|
|
#define cmapTag 0x636d6170
|
|
#define glyfTag 0x676c7966
|
|
#define headTag 0x68656164
|
|
#define hheaTag 0x68686561
|
|
#define hmtxTag 0x686d7478
|
|
#define locaTag 0x6c6f6361
|
|
#define nameTag 0x6e616d65
|
|
#define os2Tag 0x4f532f32
|
|
#define postTag 0x706f7374
|
|
#define vrt2Tag 0x76727432
|
|
#define vertTag 0x76657274
|
|
|
|
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.
|
|
#define nT42Tables 11
|
|
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 } };
|
|
#define t42HeadTable 3
|
|
#define t42LocaTable 6
|
|
#define t42GlyfTable 2
|
|
#define t42VheaTable 9
|
|
#define t42VmtxTable 10
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
// 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;
|
|
}
|