//======================================================================== // // WebFont.cc // // Copyright 2019 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "gmem.h" #include "gmempp.h" #include "GHash.h" #include "FoFiTrueType.h" #include "FoFiType1C.h" #include "CharCodeToUnicode.h" #include "WebFont.h" WebFont::WebFont(GfxFont *gfxFontA, XRef *xref) { GfxFontType type; Ref id; gfxFont = gfxFontA; fontBuf = NULL; ffTrueType = NULL; ffType1C = NULL; isOpenType = gFalse; if (gfxFont->getEmbeddedFontID(&id)) { type = gfxFont->getType(); if (type == fontTrueType || type == fontTrueTypeOT || type == fontCIDType2 || type == fontCIDType2OT) { if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) { ffTrueType = FoFiTrueType::make(fontBuf, fontLength, 0); } } else if (type == fontType1C || type == fontCIDType0C) { if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) { ffType1C = FoFiType1C::make(fontBuf, fontLength); } } else if (type == fontType1COT || type == fontCIDType0COT) { if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) { isOpenType = gTrue; } } } } WebFont::~WebFont() { delete ffTrueType; delete ffType1C; gfree(fontBuf); } GBool WebFont::canWriteTTF() { return ffTrueType != NULL; } GBool WebFont::canWriteOTF() { return ffType1C || isOpenType; } static void writeToFile(void *stream, const char *data, int len) { fwrite(data, 1, len, (FILE *)stream); } static void writeToString(void *stream, const char *data, int len) { ((GString *)stream)->append(data, len); } GBool WebFont::generateTTF(FoFiOutputFunc outFunc, void *stream) { int *codeToGID; Guchar *cmapTable; GBool freeCodeToGID; int nCodes, cmapTableLength; if (!ffTrueType) { return gFalse; } if (gfxFont->isCIDFont()) { codeToGID = ((GfxCIDFont *)gfxFont)->getCIDToGID(); nCodes = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); if (!codeToGID) { nCodes = ffTrueType->getNumGlyphs(); } freeCodeToGID = gFalse; } else { codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffTrueType); nCodes = 256; freeCodeToGID = gTrue; } cmapTable = makeUnicodeCmapTable(codeToGID, nCodes, &cmapTableLength); if (freeCodeToGID) { gfree(codeToGID); } if (!cmapTable) { return gFalse; } ffTrueType->writeTTF(outFunc, stream, NULL, NULL, cmapTable, cmapTableLength); gfree(cmapTable); return gTrue; } GBool WebFont::writeTTF(const char *fontFilePath) { FILE *out = fopen(fontFilePath, "wb"); if (!out) { return gFalse; } GBool ret = generateTTF(writeToFile, out); fclose(out); return ret; } GString *WebFont::getTTFData() { GString *s = new GString(); if (!generateTTF(writeToString, s)) { delete s; return NULL; } return s; } GBool WebFont::generateOTF(FoFiOutputFunc outFunc, void *stream) { int *codeToGID; Gushort *widths; Guchar *cmapTable; int nCodes, nWidths, cmapTableLength; if (ffType1C) { if (gfxFont->getType() == fontType1C) { codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffType1C); if (!(cmapTable = makeUnicodeCmapTable(codeToGID, 256, &cmapTableLength))) { gfree(codeToGID); return gFalse; } widths = makeType1CWidths(codeToGID, 256, &nWidths); gfree(codeToGID); } else { // fontCIDType0C codeToGID = ffType1C->getCIDToGIDMap(&nCodes); if (!(cmapTable = makeUnicodeCmapTable(codeToGID, nCodes, &cmapTableLength))) { gfree(codeToGID); return gFalse; } widths = makeCIDType0CWidths(codeToGID, nCodes, &nWidths); gfree(codeToGID); } ffType1C->convertToOpenType(outFunc, stream, nWidths, widths, cmapTable, cmapTableLength); gfree(cmapTable); gfree(widths); } else if (isOpenType) { outFunc(stream, fontBuf, fontLength); } else { return gFalse; } return gTrue; } GBool WebFont::writeOTF(const char *fontFilePath) { FILE *out = fopen(fontFilePath, "wb"); if (!out) { return gFalse; } GBool ret = generateOTF(writeToFile, out); fclose(out); return ret; } GString *WebFont::getOTFData() { GString *s = new GString(); if (!generateOTF(writeToString, s)) { delete s; return NULL; } return s; } Gushort *WebFont::makeType1CWidths(int *codeToGID, int nCodes, int *nWidths) { Gushort *widths; Gushort width; int widthsLen, gid, i; widthsLen = ffType1C->getNumGlyphs(); widths = (Gushort *)gmallocn(widthsLen, sizeof(Gushort)); for (i = 0; i < widthsLen; ++i) { widths[i] = 0; } for (i = 0; i < nCodes; ++i) { gid = codeToGID[i]; if (gid < 0 || gid >= widthsLen) { continue; } width = (Gushort)(((Gfx8BitFont *)gfxFont)->getWidth((Guchar)i) * 1000 + 0.5); if (width == 0) { continue; } widths[gid] = width; } *nWidths = widthsLen; return widths; } Gushort *WebFont::makeCIDType0CWidths(int *codeToGID, int nCodes, int *nWidths) { Gushort *widths; Gushort width; int widthsLen, gid, i; widthsLen = ffType1C->getNumGlyphs(); widths = (Gushort *)gmallocn(widthsLen, sizeof(Gushort)); for (i = 0 ; i < widthsLen; ++i) { widths[i] = 0; } for (i = 0; i < nCodes; ++i) { gid = codeToGID[i]; if (gid < 0 || gid >= widthsLen) { continue; } width = (Gushort)(((GfxCIDFont *)gfxFont)->getWidth((CID)i) * 1000 + 0.5); if (width == 0) { continue; } widths[gid] = width; } *nWidths = widthsLen; return widths; } Guchar *WebFont::makeUnicodeCmapTable(int *codeToGID, int nCodes, int *unicodeCmapLength) { int *unicodeToGID; Guchar *cmapTable; int unicodeToGIDLength, nMappings, len; int nSegs, searchRange, entrySelector, rangeShift; int glyphIdOffset, idRangeOffset; int start, end, c, i; if (!(unicodeToGID = makeUnicodeToGID(codeToGID, nCodes, &unicodeToGIDLength))) { return NULL; } // count the valid code-to-glyph mappings, and the sequences of // consecutive valid mappings // (note: char code 65535 is used to mark the end of table) nMappings = 0; nSegs = 1; // count the last segment, mapping 65535 for (c = 0; c < unicodeToGIDLength && c <= 65534; ++c) { if (unicodeToGID[c]) { ++nMappings; if (c == 0 || !unicodeToGID[c-1]) { ++nSegs; } } } i = 1; entrySelector = 0; while (2 * i <= nSegs) { i *= 2; ++entrySelector; } searchRange = 1 << (entrySelector + 1); rangeShift = 2 * nSegs - searchRange; len = 28 + nSegs * 8 + nMappings * 2; cmapTable = (Guchar *)gmalloc(len); // header cmapTable[ 0] = 0x00; // table version cmapTable[ 1] = 0x00; cmapTable[ 2] = 0x00; // number of cmaps cmapTable[ 3] = 0x01; cmapTable[ 4] = 0x00; // platform[0] cmapTable[ 5] = 0x03; cmapTable[ 6] = 0x00; // encoding[0] cmapTable[ 7] = 0x01; cmapTable[ 8] = 0x00; // offset[0] cmapTable[ 9] = 0x00; cmapTable[10] = 0x00; cmapTable[11] = 0x0c; // table info cmapTable[12] = 0x00; // cmap format cmapTable[13] = 0x04; cmapTable[14] = (Guchar)((len - 12) >> 8); // cmap length cmapTable[15] = (Guchar)(len - 12); cmapTable[16] = 0x00; // cmap version cmapTable[17] = 0x00; cmapTable[18] = (Guchar)(nSegs >> 7); // segCountX2 cmapTable[19] = (Guchar)(nSegs << 1); cmapTable[20] = (Guchar)(searchRange >> 8); // searchRange cmapTable[21] = (Guchar)searchRange; cmapTable[22] = (Guchar)(entrySelector >> 8); // entrySelector cmapTable[23] = (Guchar)entrySelector; cmapTable[24] = (Guchar)(rangeShift >> 8); // rangeShift cmapTable[25] = (Guchar)rangeShift; cmapTable[26 + nSegs*2 ] = 0; // reservedPad cmapTable[26 + nSegs*2 + 1] = 0; i = 0; glyphIdOffset = 28 + nSegs*8; for (c = 0; c < unicodeToGIDLength && c <= 65534; ++c) { if (unicodeToGID[c]) { if (c == 0 || !unicodeToGID[c-1]) { start = c; cmapTable[28 + nSegs*2 + i*2 ] = (Guchar)(start >> 8); cmapTable[28 + nSegs*2 + i*2 + 1] = (Guchar)start; cmapTable[28 + nSegs*4 + i*2 ] = (Guchar)0; // idDelta cmapTable[28 + nSegs*4 + i*2 + 1] = (Guchar)0; idRangeOffset = glyphIdOffset - (28 + nSegs*6 + i*2); cmapTable[28 + nSegs*6 + i*2 ] = (Guchar)(idRangeOffset >> 8); cmapTable[28 + nSegs*6 + i*2 + 1] = (Guchar)idRangeOffset; } if (c == 65534 || !unicodeToGID[c+1]) { end = c; cmapTable[26 + i*2 ] = (Guchar)(end >> 8); cmapTable[26 + i*2 + 1] = (Guchar)end; ++i; } cmapTable[glyphIdOffset++] = (Guchar)(unicodeToGID[c] >> 8); cmapTable[glyphIdOffset++] = (Guchar)unicodeToGID[c]; } } // last segment maps code 65535 to GID 0 cmapTable[26 + i*2 ] = (Guchar)0xff; // end cmapTable[26 + i*2 + 1] = (Guchar)0xff; cmapTable[28 + nSegs*2 + i*2 ] = (Guchar)0xff; // start cmapTable[28 + nSegs*2 + i*2 + 1] = (Guchar)0xff; cmapTable[28 + nSegs*4 + i*2 ] = (Guchar)0; // idDelta cmapTable[28 + nSegs*4 + i*2 + 1] = (Guchar)1; cmapTable[28 + nSegs*6 + i*2 ] = (Guchar)0; // idRangeOffset cmapTable[28 + nSegs*6 + i*2 + 1] = (Guchar)0; gfree(unicodeToGID); *unicodeCmapLength = len; return cmapTable; } int *WebFont::makeUnicodeToGID(int *codeToGID, int nCodes, int *unicodeToGIDLength) { int *unicodeToGID; CharCodeToUnicode *ctu; Unicode u[2]; int len, size, newSize, uLen, c, gid; if (gfxFont->isCIDFont()) { if (!(ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) { return NULL; } } else { ctu = ((Gfx8BitFont *)gfxFont)->getToUnicode(); } len = 0; size = 256; unicodeToGID = (int *)gmallocn(size, sizeof(int)); memset(unicodeToGID, 0, size * sizeof(int)); for (c = 0; c < nCodes; ++c) { gid = codeToGID ? codeToGID[c] : c; if (gid < 0 || gid >= 65536) { continue; } uLen = ctu->mapToUnicode(c, u, 2); if (uLen != 1) { continue; } if (u[0] >= 65536) { // sanity check continue; } if ((int)u[0] >= size) { newSize = 2 * size; while ((int)u[0] >= newSize) { newSize *= 2; } unicodeToGID = (int *)greallocn(unicodeToGID, newSize, sizeof(int)); memset(unicodeToGID + size, 0, (newSize - size) * sizeof(int)); size = newSize; } unicodeToGID[u[0]] = gid; if ((int)u[0] >= len) { len = u[0] + 1; } } ctu->decRefCnt(); *unicodeToGIDLength = len; return unicodeToGID; }