| //======================================================================== |
| // |
| // 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-2018 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> |
| // |
| // 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 <stdlib.h> |
| #include <string.h> |
| #include <limits.h> |
| #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 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 |
| //------------------------------------------------------------------------ |
| |
| FoFiTrueType *FoFiTrueType::make(const char *fileA, int lenA, int faceIndexA) { |
| FoFiTrueType *ff; |
| |
| ff = new FoFiTrueType(fileA, lenA, false, faceIndexA); |
| if (!ff->parsedOk) { |
| delete ff; |
| return nullptr; |
| } |
| return ff; |
| } |
| |
| FoFiTrueType *FoFiTrueType::load(const char *fileName, int faceIndexA) { |
| FoFiTrueType *ff; |
| char *fileA; |
| int lenA; |
| |
| if (!(fileA = FoFiBase::readFile(fileName, &lenA))) { |
| return nullptr; |
| } |
| ff = new FoFiTrueType(fileA, lenA, true, faceIndexA); |
| if (!ff->parsedOk) { |
| delete ff; |
| return nullptr; |
| } |
| return ff; |
| } |
| |
| FoFiTrueType::FoFiTrueType(const 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; |
| 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 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: |
| 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; |
| } |
| gid = segDelta + (c-segStart); |
| 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 (!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(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(start, length))) { |
| return; |
| } |
| ff->getFontMatrix(mat); |
| delete ff; |
| } |
| |
| void FoFiTrueType::convertToType42(const char *psName, char **encoding, |
| int *codeToGID, |
| FoFiOutputFunc outputFunc, |
| void *outputStream) const { |
| GooString *buf; |
| int maxUsedGlyph; |
| bool ok; |
| |
| if (openTypeCFF) { |
| return; |
| } |
| |
| // write the header |
| ok = true; |
| buf = GooString::format("%!PS-TrueTypeFont-{0:2g}\n", |
| (double)getS32BE(0, &ok) / 65536.0); |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| |
| // 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()); |
| delete buf; |
| (*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(start, length))) { |
| return; |
| } |
| ff->convertToType1(psName, newEncoding, ascii, outputFunc, outputStream); |
| delete ff; |
| } |
| |
| void FoFiTrueType::convertToCIDType2(const char *psName, |
| int *cidMap, int nCIDs, |
| bool needVerticalMetrics, |
| FoFiOutputFunc outputFunc, |
| void *outputStream) const { |
| GooString *buf; |
| int cid, maxUsedGlyph; |
| bool ok; |
| int i, j, k; |
| |
| if (openTypeCFF) { |
| return; |
| } |
| |
| // write the header |
| ok = true; |
| buf = GooString::format("%!PS-TrueTypeFont-{0:2g}\n", |
| (double)getS32BE(0, &ok) / 65536.0); |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| |
| // 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()); |
| delete buf; |
| 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()); |
| delete buf; |
| } |
| (*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()); |
| delete buf; |
| } |
| (*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()); |
| delete buf; |
| 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()); |
| delete buf; |
| buf = GooString::format(" 2 copy dup 2 mul exch {0:d} add -8 bitshift put\n", |
| i); |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| 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()); |
| delete buf; |
| (*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()); |
| delete buf; |
| buf = GooString::format(" 0 1 {0:d} {{\n", nGlyphs - 1); |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| (*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()); |
| delete buf; |
| (*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(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 *buf; |
| 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)); |
| buf = GooString::format("_{0:02x} def\n", i >> 8); |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| (*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()); |
| delete buf; |
| (*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()); |
| delete buf; |
| } |
| (*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()); |
| delete buf; |
| } |
| (*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) { |
| buf = GooString::format("{0:d}\n", i >> 8); |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| } |
| (*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)); |
| buf = GooString::format("_{0:02x} findfont\n", i >> 8); |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| } |
| (*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(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; |
| GooString *buf; |
| int i; |
| |
| (*outputFunc)(outputStream, "/Encoding 256 array\n", 20); |
| if (encoding) { |
| for (i = 0; i < 256; ++i) { |
| if (!(name = encoding[i])) { |
| name = ".notdef"; |
| } |
| buf = GooString::format("dup {0:d} /", i); |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| (*outputFunc)(outputStream, name, strlen(name)); |
| (*outputFunc)(outputStream, " put\n", 5); |
| } |
| } else { |
| for (i = 0; i < 256; ++i) { |
| buf = GooString::format("dup {0:d} /c{1:02x} put\n", i, i); |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| } |
| } |
| (*outputFunc)(outputStream, "readonly def\n", 13); |
| } |
| |
| void FoFiTrueType::cvtCharStrings(char **encoding, |
| int *codeToGID, |
| FoFiOutputFunc outputFunc, |
| void *outputStream) const { |
| const char *name; |
| GooString *buf; |
| 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)); |
| buf = GooString::format(" {0:d} def\n", k); |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| } |
| } |
| } |
| |
| err: |
| (*outputFunc)(outputStream, "end readonly def\n", 17); |
| } |
| |
| void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, |
| void *outputStream, 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; |
| |
| // 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; |
| *maxUsedGlyph = -1; |
| for (i = 0; i <= nGlyphs; ++i) { |
| locaTable[i].newOffset = pos; |
| pos += locaTable[i].len; |
| 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; |
| pos += length; |
| if (pos & 3) { |
| pos += 4 - (length & 3); |
| } |
| ++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 { |
| GooString *buf; |
| int pad, i, j; |
| |
| (*outputFunc)(outputStream, "<", 1); |
| for (i = 0; i < length; i += 32) { |
| for (j = 0; j < 32 && i+j < length; ++j) { |
| buf = GooString::format("{0:02x}", s[i+j] & 0xff); |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| } |
| 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) { |
| 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)) { |
| 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; |
| } |
| |