//======================================================================== // // FontFile.cc // // Copyright 1999-2002 Glyph & Cog, LLC // //======================================================================== #ifdef __GNUC__ #pragma implementation #endif #include #include #include #include #include #include #include "gmem.h" #include "Error.h" #include "GlobalParams.h" #include "CharCodeToUnicode.h" #include "FontEncodingTables.h" #include "FontFile.h" #include "CompactFontTables.h" //------------------------------------------------------------------------ static inline char *nextLine(char *line, char *end) { while (line < end && *line != '\n' && *line != '\r') ++line; while (line < end && *line == '\n' || *line == '\r') ++line; return line; } static char hexChars[17] = "0123456789ABCDEF"; //------------------------------------------------------------------------ // FontFile //------------------------------------------------------------------------ FontFile::FontFile() { } FontFile::~FontFile() { } //------------------------------------------------------------------------ // Type1FontFile //------------------------------------------------------------------------ Type1FontFile::Type1FontFile(char *file, int len) { char *line, *line1, *p, *p2; GBool haveEncoding; char buf[256]; char c; int n, code, i, j; name = NULL; encoding = (char **)gmalloc(256 * sizeof(char *)); for (i = 0; i < 256; ++i) { encoding[i] = NULL; } haveEncoding = gFalse; for (i = 1, line = file; i <= 100 && line < file + len && !haveEncoding; ++i) { // get font name if (!strncmp(line, "/FontName", 9)) { strncpy(buf, line, 255); buf[255] = '\0'; if ((p = strchr(buf+9, '/')) && (p = strtok(p+1, " \t\n\r"))) { name = copyString(p); } line = nextLine(line, file + len); // get encoding } else if (!strncmp(line, "/Encoding StandardEncoding def", 30)) { for (j = 0; j < 256; ++j) { if (standardEncoding[j]) { encoding[j] = copyString(standardEncoding[j]); } } haveEncoding = gTrue; } else if (!strncmp(line, "/Encoding 256 array", 19)) { for (j = 0; j < 300; ++j) { line1 = nextLine(line, file + len); if ((n = line1 - line) > 255) { n = 255; } strncpy(buf, line, n); buf[n] = '\0'; for (p = buf; *p == ' ' || *p == '\t'; ++p) ; if (!strncmp(p, "dup", 3)) { for (p += 3; *p == ' ' || *p == '\t'; ++p) ; for (p2 = p; *p2 >= '0' && *p2 <= '9'; ++p2) ; if (*p2) { c = *p2; *p2 = '\0'; if ((code = atoi(p)) < 256) { *p2 = c; for (p = p2; *p == ' ' || *p == '\t'; ++p) ; if (*p == '/') { ++p; for (p2 = p; *p2 && *p2 != ' ' && *p2 != '\t'; ++p2) ; *p2 = '\0'; encoding[code] = copyString(p); } } } } else { if (strtok(buf, " \t") && (p = strtok(NULL, " \t\n\r")) && !strcmp(p, "def")) { break; } } line = line1; } //~ check for getinterval/putinterval junk haveEncoding = gTrue; } else { line = nextLine(line, file + len); } } } Type1FontFile::~Type1FontFile() { int i; if (name) { gfree(name); } for (i = 0; i < 256; ++i) { gfree(encoding[i]); } gfree(encoding); } //------------------------------------------------------------------------ // Type1CFontFile //------------------------------------------------------------------------ struct Type1CTopDict { int version; int notice; int copyright; int fullName; int familyName; int weight; int isFixedPitch; double italicAngle; double underlinePosition; double underlineThickness; int paintType; int charstringType; double fontMatrix[6]; int uniqueID; double fontBBox[4]; double strokeWidth; int charset; int encoding; int charStrings; int privateSize; int privateOffset; //----- CIDFont entries int registry; int ordering; int supplement; int fdArrayOffset; int fdSelectOffset; }; struct Type1CPrivateDict { GString *dictData; int subrsOffset; double defaultWidthX; GBool defaultWidthXFP; double nominalWidthX; GBool nominalWidthXFP; }; Type1CFontFile::Type1CFontFile(char *fileA, int lenA) { Guchar *nameIdxPtr, *idxPtr0, *idxPtr1; file = fileA; len = lenA; name = NULL; encoding = NULL; // some tools embed Type 1C fonts with an extra whitespace char at // the beginning if (file[0] != '\x01') { ++file; } // read header topOffSize = file[3] & 0xff; // read name index (first font only) nameIdxPtr = (Guchar *)file + (file[2] & 0xff); idxPtr0 = getIndexValPtr(nameIdxPtr, 0); idxPtr1 = getIndexValPtr(nameIdxPtr, 1); name = new GString((char *)idxPtr0, idxPtr1 - idxPtr0); topDictIdxPtr = getIndexEnd(nameIdxPtr); stringIdxPtr = getIndexEnd(topDictIdxPtr); gsubrIdxPtr = getIndexEnd(stringIdxPtr); } Type1CFontFile::~Type1CFontFile() { int i; delete name; if (encoding) { for (i = 0; i < 256; ++i) { gfree(encoding[i]); } gfree(encoding); } } char *Type1CFontFile::getName() { return name->getCString(); } char **Type1CFontFile::getEncoding() { if (!encoding) { readNameAndEncoding(); } return encoding; } void Type1CFontFile::readNameAndEncoding() { char buf[256]; Guchar *idxPtr0, *idxPtr1, *ptr; int nGlyphs; int nCodes, nRanges, nLeft, nSups; Gushort *glyphNames; int charset, enc, charstrings; int encFormat; int c, sid; double x; GBool isFP; int key; int i, j; encoding = (char **)gmalloc(256 * sizeof(char *)); for (i = 0; i < 256; ++i) { encoding[i] = NULL; } // read top dict (first font only) idxPtr0 = getIndexValPtr(topDictIdxPtr, 0); idxPtr1 = getIndexValPtr(topDictIdxPtr, 1); charset = enc = charstrings = 0; i = 0; ptr = idxPtr0; while (ptr < idxPtr1) { if (*ptr <= 27 || *ptr == 31) { key = *ptr++; if (key == 0x0c) { key = (key << 8) | *ptr++; } if (key == 0x0f) { // charset charset = (int)op[0]; } else if (key == 0x10) { // encoding enc = (int)op[0]; } else if (key == 0x11) { // charstrings charstrings = (int)op[0]; } i = 0; } else { x = getNum(&ptr, &isFP); if (i < 48) { op[i++] = x; } } } // get number of glyphs from charstrings index nGlyphs = getIndexLen((Guchar *)file + charstrings); // read charset (GID -> name mapping) glyphNames = readCharset(charset, nGlyphs); // read encoding (GID -> code mapping) if (enc == 0) { for (i = 0; i < 256; ++i) { if (standardEncoding[i]) { encoding[i] = copyString(standardEncoding[i]); } } } else if (enc == 1) { for (i = 0; i < 256; ++i) { if (expertEncoding[i]) { encoding[i] = copyString(expertEncoding[i]); } } } else { ptr = (Guchar *)file + enc; encFormat = *ptr++; if ((encFormat & 0x7f) == 0) { nCodes = 1 + *ptr++; if (nCodes > nGlyphs) { nCodes = nGlyphs; } for (i = 1; i < nCodes; ++i) { c = *ptr++; encoding[c] = copyString(getString(glyphNames[i], buf)); } } else if ((encFormat & 0x7f) == 1) { nRanges = *ptr++; nCodes = 1; for (i = 0; i < nRanges; ++i) { c = *ptr++; nLeft = *ptr++; for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) { encoding[c] = copyString(getString(glyphNames[nCodes], buf)); ++nCodes; ++c; } } } if (encFormat & 0x80) { nSups = *ptr++; for (i = 0; i < nSups; ++i) { c = *ptr++; sid = getWord(ptr, 2); ptr += 2; encoding[c] = copyString(getString(sid, buf)); } } } if (charset > 2) { gfree(glyphNames); } } void Type1CFontFile::convertToType1(FILE *outA) { Type1CTopDict dict; Type1CPrivateDict privateDict; char buf[256], eBuf[256]; Guchar *idxPtr0, *idxPtr1, *subrsIdxPtr, *charStringsIdxPtr, *ptr; int nGlyphs, nCodes, nRanges, nLeft, nSups; Gushort *glyphNames; int encFormat, nSubrs, nCharStrings; int c, sid; int i, j, n; out = outA; // read top dict (first font only) readTopDict(&dict); // get global subrs //~ ... global subrs are unimplemented // write header and font dictionary, up to encoding fprintf(out, "%%!FontType1-1.0: %s", name->getCString()); if (dict.version != 0) { fprintf(out, "%s", getString(dict.version, buf)); } fprintf(out, "\n"); fprintf(out, "11 dict begin\n"); fprintf(out, "/FontInfo 10 dict dup begin\n"); if (dict.version != 0) { fprintf(out, "/version (%s) readonly def\n", getString(dict.version, buf)); } if (dict.notice != 0) { fprintf(out, "/Notice (%s) readonly def\n", getString(dict.notice, buf)); } if (dict.copyright != 0) { fprintf(out, "/Copyright (%s) readonly def\n", getString(dict.copyright, buf)); } if (dict.fullName != 0) { fprintf(out, "/FullName (%s) readonly def\n", getString(dict.fullName, buf)); } if (dict.familyName != 0) { fprintf(out, "/FamilyName (%s) readonly def\n", getString(dict.familyName, buf)); } if (dict.weight != 0) { fprintf(out, "/Weight (%s) readonly def\n", getString(dict.weight, buf)); } fprintf(out, "/isFixedPitch %s def\n", dict.isFixedPitch ? "true" : "false"); fprintf(out, "/ItalicAngle %g def\n", dict.italicAngle); fprintf(out, "/UnderlinePosition %g def\n", dict.underlinePosition); fprintf(out, "/UnderlineThickness %g def\n", dict.underlineThickness); fprintf(out, "end readonly def\n"); fprintf(out, "/FontName /%s def\n", name->getCString()); fprintf(out, "/PaintType %d def\n", dict.paintType); fprintf(out, "/FontType 1 def\n"); fprintf(out, "/FontMatrix [%g %g %g %g %g %g] readonly def\n", dict.fontMatrix[0], dict.fontMatrix[1], dict.fontMatrix[2], dict.fontMatrix[3], dict.fontMatrix[4], dict.fontMatrix[5]); fprintf(out, "/FontBBox [%g %g %g %g] readonly def\n", dict.fontBBox[0], dict.fontBBox[1], dict.fontBBox[2], dict.fontBBox[3]); fprintf(out, "/StrokeWidth %g def\n", dict.strokeWidth); if (dict.uniqueID != 0) { fprintf(out, "/UniqueID %d def\n", dict.uniqueID); } // get number of glyphs from charstrings index nGlyphs = getIndexLen((Guchar *)file + dict.charStrings); // read charset glyphNames = readCharset(dict.charset, nGlyphs); // read encoding (glyph -> code mapping), write Type 1 encoding fprintf(out, "/Encoding "); if (dict.encoding == 0) { fprintf(out, "StandardEncoding def\n"); } else { fprintf(out, "256 array\n"); fprintf(out, "0 1 255 {1 index exch /.notdef put} for\n"); if (dict.encoding == 1) { for (i = 0; i < 256; ++i) { if (expertEncoding[i]) { fprintf(out, "dup %d /%s put\n", i, expertEncoding[i]); } } } else { ptr = (Guchar *)file + dict.encoding; encFormat = *ptr++; if ((encFormat & 0x7f) == 0) { nCodes = 1 + *ptr++; if (nCodes > nGlyphs) { nCodes = nGlyphs; } for (i = 1; i < nCodes; ++i) { c = *ptr++; fprintf(out, "dup %d /%s put\n", c, getString(glyphNames[i], buf)); } } else if ((encFormat & 0x7f) == 1) { nRanges = *ptr++; nCodes = 1; for (i = 0; i < nRanges; ++i) { c = *ptr++; nLeft = *ptr++; for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) { fprintf(out, "dup %d /%s put\n", c, getString(glyphNames[nCodes], buf)); ++nCodes; ++c; } } } if (encFormat & 0x80) { nSups = *ptr++; for (i = 0; i < nSups; ++i) { c = *ptr++; sid = getWord(ptr, 2); ptr += 2; fprintf(out, "dup %d /%s put\n", c, getString(sid, buf)); } } } fprintf(out, "readonly def\n"); } fprintf(out, "currentdict end\n"); // start the binary section fprintf(out, "currentfile eexec\n"); r1 = 55665; line = 0; // get private dictionary eexecWrite("\x83\xca\x73\xd5"); eexecWrite("dup /Private 32 dict dup begin\n"); eexecWrite("/RD {string currentfile exch readstring pop} executeonly def\n"); eexecWrite("/ND {noaccess def} executeonly def\n"); eexecWrite("/NP {noaccess put} executeonly def\n"); eexecWrite("/MinFeature {16 16} ND\n"); readPrivateDict(&privateDict, dict.privateOffset, dict.privateSize); eexecWrite(privateDict.dictData->getCString()); defaultWidthX = privateDict.defaultWidthX; defaultWidthXFP = privateDict.defaultWidthXFP; nominalWidthX = privateDict.nominalWidthX; nominalWidthXFP = privateDict.nominalWidthXFP; // get subrs if (privateDict.subrsOffset != 0) { subrsIdxPtr = (Guchar *)file + dict.privateOffset + privateDict.subrsOffset; nSubrs = getIndexLen(subrsIdxPtr); sprintf(eBuf, "/Subrs %d array\n", nSubrs); eexecWrite(eBuf); idxPtr1 = getIndexValPtr(subrsIdxPtr, 0); for (i = 0; i < nSubrs; ++i) { idxPtr0 = idxPtr1; idxPtr1 = getIndexValPtr(subrsIdxPtr, i+1); n = idxPtr1 - idxPtr0; #if 1 //~ Type 2 subrs are unimplemented error(-1, "Unimplemented Type 2 subrs"); #else sprintf(eBuf, "dup %d %d RD ", i, n); eexecWrite(eBuf); eexecCvtGlyph(idxPtr0, n); eexecWrite(" NP\n"); #endif } eexecWrite("ND\n"); } // get CharStrings charStringsIdxPtr = (Guchar *)file + dict.charStrings; nCharStrings = getIndexLen(charStringsIdxPtr); sprintf(eBuf, "2 index /CharStrings %d dict dup begin\n", nCharStrings); eexecWrite(eBuf); idxPtr1 = getIndexValPtr(charStringsIdxPtr, 0); for (i = 0; i < nCharStrings; ++i) { idxPtr0 = idxPtr1; idxPtr1 = getIndexValPtr(charStringsIdxPtr, i+1); n = idxPtr1 - idxPtr0; eexecCvtGlyph(getString(glyphNames[i], buf), idxPtr0, n); } eexecWrite("end\n"); eexecWrite("end\n"); eexecWrite("readonly put\n"); eexecWrite("noaccess put\n"); eexecWrite("dup /FontName get exch definefont pop\n"); eexecWrite("mark currentfile closefile\n"); // trailer if (line > 0) { fputc('\n', out); } for (i = 0; i < 8; ++i) { fprintf(out, "0000000000000000000000000000000000000000000000000000000000000000\n"); } fprintf(out, "cleartomark\n"); // clean up delete privateDict.dictData; if (dict.charset > 2) { gfree(glyphNames); } } void Type1CFontFile::convertToCIDType0(char *psName, FILE *outA) { Type1CTopDict dict; Type1CPrivateDict *privateDicts; GString *charStrings; int *charStringOffsets; Gushort *charset; int *cidMap; Guchar *fdSelect; Guchar *charStringsIdxPtr, *fdArrayIdx, *idxPtr0, *idxPtr1, *ptr; char buf[256]; int nGlyphs, nCIDs, gdBytes, nFDs; int fdSelectFmt, nRanges, gid0, gid1, fd, offset; int key; double x; GBool isFP; int i, j, k, n; out = outA; fprintf(out, "/CIDInit /ProcSet findresource begin\n"); // read top dict (first font only) readTopDict(&dict); // read the FDArray dictionaries and Private dictionaries if (dict.fdArrayOffset == 0) { nFDs = 1; privateDicts = (Type1CPrivateDict *) gmalloc(nFDs * sizeof(Type1CPrivateDict)); privateDicts[0].dictData = new GString(); privateDicts[0].subrsOffset = 0; privateDicts[0].defaultWidthX = 0; privateDicts[0].defaultWidthXFP = gFalse; privateDicts[0].nominalWidthX = 0; privateDicts[0].nominalWidthXFP = gFalse; } else { fdArrayIdx = (Guchar *)file + dict.fdArrayOffset; nFDs = getIndexLen(fdArrayIdx); privateDicts = (Type1CPrivateDict *) gmalloc(nFDs * sizeof(Type1CPrivateDict)); idxPtr1 = getIndexValPtr(fdArrayIdx, 0); for (i = 0; i < nFDs; ++i) { privateDicts[i].dictData = NULL; idxPtr0 = idxPtr1; idxPtr1 = getIndexValPtr(fdArrayIdx, i + 1); ptr = idxPtr0; j = 0; while (ptr < idxPtr1) { if (*ptr <= 27 || *ptr == 31) { key = *ptr++; if (key == 0x0c) { key = (key << 8) | *ptr++; } if (key == 0x0012) { readPrivateDict(&privateDicts[i], (int)op[1], (int)op[0]); } j = 0; } else { x = getNum(&ptr, &isFP); if (j < 48) { op[j] = x; fp[j++] = isFP; } } } if (!privateDicts[i].dictData) { privateDicts[i].dictData = new GString(); privateDicts[i].subrsOffset = 0; privateDicts[i].defaultWidthX = 0; privateDicts[i].defaultWidthXFP = gFalse; privateDicts[i].nominalWidthX = 0; privateDicts[i].nominalWidthXFP = gFalse; } } } // get the glyph count charStringsIdxPtr = (Guchar *)file + dict.charStrings; nGlyphs = getIndexLen(charStringsIdxPtr); // read the FDSelect table fdSelect = (Guchar *)gmalloc(nGlyphs); if (dict.fdSelectOffset == 0) { for (i = 0; i < nGlyphs; ++i) { fdSelect[i] = 0; } } else { ptr = (Guchar *)file + dict.fdSelectOffset; fdSelectFmt = *ptr++; if (fdSelectFmt == 0) { memcpy(fdSelect, ptr, nGlyphs); } else if (fdSelectFmt == 3) { nRanges = getWord(ptr, 2); ptr += 2; gid0 = getWord(ptr, 2); ptr += 2; for (i = 1; i <= nRanges; ++i) { fd = *ptr++; gid1 = getWord(ptr, 2); ptr += 2; for (j = gid0; j < gid1; ++j) { fdSelect[j] = fd; } gid0 = gid1; } } else { error(-1, "Unknown FDSelect table format in CID font"); for (i = 0; i < nGlyphs; ++i) { fdSelect[i] = 0; } } } // read the charset, compute the CID-to-GID mapping charset = readCharset(dict.charset, nGlyphs); nCIDs = 0; for (i = 0; i < nGlyphs; ++i) { if (charset[i] >= nCIDs) { nCIDs = charset[i] + 1; } } cidMap = (int *)gmalloc(nCIDs * sizeof(int)); for (i = 0; i < nCIDs; ++i) { cidMap[i] = -1; } for (i = 0; i < nGlyphs; ++i) { cidMap[charset[i]] = i; } // build the charstrings charStrings = new GString(); charStringOffsets = (int *)gmalloc((nCIDs + 1) * sizeof(int)); for (i = 0; i < nCIDs; ++i) { charStringOffsets[i] = charStrings->getLength(); if (cidMap[i] >= 0) { idxPtr0 = getIndexValPtr(charStringsIdxPtr, cidMap[i]); idxPtr1 = getIndexValPtr(charStringsIdxPtr, cidMap[i]+1); n = idxPtr1 - idxPtr0; j = fdSelect[cidMap[i]]; defaultWidthX = privateDicts[j].defaultWidthX; defaultWidthXFP = privateDicts[j].defaultWidthXFP; nominalWidthX = privateDicts[j].nominalWidthX; nominalWidthXFP = privateDicts[j].nominalWidthXFP; cvtGlyph(idxPtr0, n); charStrings->append(charBuf); delete charBuf; } } charStringOffsets[nCIDs] = charStrings->getLength(); // compute gdBytes = number of bytes needed for charstring offsets // (offset size needs to account for the charstring offset table, // with a worst case of five bytes per entry, plus the charstrings // themselves) i = (nCIDs + 1) * 5 + charStrings->getLength(); if (i < 0x100) { gdBytes = 1; } else if (i < 0x10000) { gdBytes = 2; } else if (i < 0x1000000) { gdBytes = 3; } else { gdBytes = 4; } // begin the font dictionary fprintf(out, "20 dict begin\n"); fprintf(out, "/CIDFontName /%s def\n", psName); fprintf(out, "/CIDFontType 0 def\n"); fprintf(out, "/CIDSystemInfo 3 dict dup begin\n"); if (dict.registry > 0 && dict.ordering > 0) { fprintf(out, " /Registry (%s) def\n", getString(dict.registry, buf)); fprintf(out, " /Ordering (%s) def\n", getString(dict.ordering, buf)); } else { fprintf(out, " /Registry (Adobe) def\n"); fprintf(out, " /Ordering (Identity) def\n"); } fprintf(out, " /Supplement %d def\n", dict.supplement); fprintf(out, "end def\n"); fprintf(out, "/FontMatrix [%g %g %g %g %g %g] def\n", dict.fontMatrix[0], dict.fontMatrix[1], dict.fontMatrix[2], dict.fontMatrix[3], dict.fontMatrix[4], dict.fontMatrix[5]); fprintf(out, "/FontBBox [%g %g %g %g] def\n", dict.fontBBox[0], dict.fontBBox[1], dict.fontBBox[2], dict.fontBBox[3]); fprintf(out, "/FontInfo 1 dict dup begin\n"); fprintf(out, " /FSType 8 def\n"); fprintf(out, "end def\n"); // CIDFont-specific entries fprintf(out, "/CIDCount %d def\n", nCIDs); fprintf(out, "/FDBytes 1 def\n"); fprintf(out, "/GDBytes %d def\n", gdBytes); fprintf(out, "/CIDMapOffset 0 def\n"); if (dict.paintType != 0) { fprintf(out, "/PaintType %d def\n", dict.paintType); fprintf(out, "/StrokeWidth %g def\n", dict.strokeWidth); } // FDArray entry fprintf(out, "/FDArray %d array\n", nFDs); for (i = 0; i < nFDs; ++i) { fprintf(out, "dup %d 10 dict begin\n", i); fprintf(out, "/FontType 1 def\n"); fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n"); fprintf(out, "/PaintType %d def\n", dict.paintType); fprintf(out, "/Private 32 dict begin\n"); fwrite(privateDicts[i].dictData->getCString(), 1, privateDicts[i].dictData->getLength(), out); fprintf(out, "currentdict end def\n"); fprintf(out, "currentdict end put\n"); } fprintf(out, "def\n"); //~ need to deal with subrs // start the binary section offset = (nCIDs + 1) * (1 + gdBytes); fprintf(out, "(Hex) %d StartData\n", offset + charStrings->getLength()); // write the charstring offset (CIDMap) table for (i = 0; i <= nCIDs; i += 6) { for (j = 0; j < 6 && i+j <= nCIDs; ++j) { if (cidMap[i+j] >= 0) { buf[0] = (char)fdSelect[cidMap[i+j]]; } else { buf[0] = (char)0; } n = offset + charStringOffsets[i+j]; for (k = gdBytes; k >= 1; --k) { buf[k] = (char)(n & 0xff); n >>= 8; } for (k = 0; k <= gdBytes; ++k) { fprintf(out, "%02x", buf[k] & 0xff); } } fputc('\n', out); } // write the charstring data n = charStrings->getLength(); for (i = 0; i < n; i += 32) { for (j = 0; j < 32 && i+j < n; ++j) { fprintf(out, "%02x", charStrings->getChar(i+j) & 0xff); } if (i + 32 >= n) { fputc('>', out); } fputc('\n', out); } for (i = 0; i < nFDs; ++i) { delete privateDicts[i].dictData; } gfree(privateDicts); gfree(cidMap); gfree(charset); gfree(charStringOffsets); delete charStrings; gfree(fdSelect); } void Type1CFontFile::convertToType0(char *psName, FILE *outA) { Type1CTopDict dict; Type1CPrivateDict *privateDicts; Gushort *charset; int *cidMap; Guchar *fdSelect; Guchar *charStringsIdxPtr, *fdArrayIdx, *idxPtr0, *idxPtr1, *ptr; char buf[256]; char eBuf[256]; int nGlyphs, nCIDs, nFDs; int fdSelectFmt, nRanges, gid0, gid1, fd; int key; double x; GBool isFP; int i, j, n; out = outA; // read top dict (first font only) readTopDict(&dict); // read the FDArray dictionaries and Private dictionaries if (dict.fdArrayOffset == 0) { nFDs = 1; privateDicts = (Type1CPrivateDict *) gmalloc(nFDs * sizeof(Type1CPrivateDict)); privateDicts[0].dictData = new GString(); privateDicts[0].subrsOffset = 0; privateDicts[0].defaultWidthX = 0; privateDicts[0].defaultWidthXFP = gFalse; privateDicts[0].nominalWidthX = 0; privateDicts[0].nominalWidthXFP = gFalse; } else { fdArrayIdx = (Guchar *)file + dict.fdArrayOffset; nFDs = getIndexLen(fdArrayIdx); privateDicts = (Type1CPrivateDict *) gmalloc(nFDs * sizeof(Type1CPrivateDict)); idxPtr1 = getIndexValPtr(fdArrayIdx, 0); for (i = 0; i < nFDs; ++i) { privateDicts[i].dictData = NULL; idxPtr0 = idxPtr1; idxPtr1 = getIndexValPtr(fdArrayIdx, i + 1); ptr = idxPtr0; j = 0; while (ptr < idxPtr1) { if (*ptr <= 27 || *ptr == 31) { key = *ptr++; if (key == 0x0c) { key = (key << 8) | *ptr++; } if (key == 0x0012) { readPrivateDict(&privateDicts[i], (int)op[1], (int)op[0]); } j = 0; } else { x = getNum(&ptr, &isFP); if (j < 48) { op[j] = x; fp[j++] = isFP; } } } if (!privateDicts[i].dictData) { privateDicts[i].dictData = new GString(); privateDicts[i].subrsOffset = 0; privateDicts[i].defaultWidthX = 0; privateDicts[i].defaultWidthXFP = gFalse; privateDicts[i].nominalWidthX = 0; privateDicts[i].nominalWidthXFP = gFalse; } } } // get the glyph count charStringsIdxPtr = (Guchar *)file + dict.charStrings; nGlyphs = getIndexLen(charStringsIdxPtr); // read the FDSelect table fdSelect = (Guchar *)gmalloc(nGlyphs); if (dict.fdSelectOffset == 0) { for (i = 0; i < nGlyphs; ++i) { fdSelect[i] = 0; } } else { ptr = (Guchar *)file + dict.fdSelectOffset; fdSelectFmt = *ptr++; if (fdSelectFmt == 0) { memcpy(fdSelect, ptr, nGlyphs); } else if (fdSelectFmt == 3) { nRanges = getWord(ptr, 2); ptr += 2; gid0 = getWord(ptr, 2); ptr += 2; for (i = 1; i <= nRanges; ++i) { fd = *ptr++; gid1 = getWord(ptr, 2); ptr += 2; for (j = gid0; j < gid1; ++j) { fdSelect[j] = fd; } gid0 = gid1; } } else { error(-1, "Unknown FDSelect table format in CID font"); for (i = 0; i < nGlyphs; ++i) { fdSelect[i] = 0; } } } // read the charset, compute the CID-to-GID mapping charset = readCharset(dict.charset, nGlyphs); nCIDs = 0; for (i = 0; i < nGlyphs; ++i) { if (charset[i] >= nCIDs) { nCIDs = charset[i] + 1; } } cidMap = (int *)gmalloc(nCIDs * sizeof(int)); for (i = 0; i < nCIDs; ++i) { cidMap[i] = -1; } for (i = 0; i < nGlyphs; ++i) { cidMap[charset[i]] = i; } // write the descendant Type 1 fonts for (i = 0; i < nCIDs; i += 256) { //~ this assumes that all CIDs in this block have the same FD -- //~ to handle multiple FDs correctly, need to somehow divide the //~ font up by FD fd = 0; for (j = 0; j < 256 && i+j < nCIDs; ++j) { if (cidMap[i+j] >= 0) { fd = fdSelect[cidMap[i+j]]; break; } } // font dictionary (unencrypted section) fprintf(out, "16 dict begin\n"); fprintf(out, "/FontName /%s_%02x def\n", psName, i >> 8); fprintf(out, "/FontType 1 def\n"); fprintf(out, "/FontMatrix [%g %g %g %g %g %g] def\n", dict.fontMatrix[0], dict.fontMatrix[1], dict.fontMatrix[2], dict.fontMatrix[3], dict.fontMatrix[4], dict.fontMatrix[5]); fprintf(out, "/FontBBox [%g %g %g %g] def\n", dict.fontBBox[0], dict.fontBBox[1], dict.fontBBox[2], dict.fontBBox[3]); fprintf(out, "/PaintType %d def\n", dict.paintType); if (dict.paintType != 0) { fprintf(out, "/StrokeWidth %g def\n", dict.strokeWidth); } fprintf(out, "/Encoding 256 array\n"); for (j = 0; j < 256 && i+j < nCIDs; ++j) { fprintf(out, "dup %d /c%02x put\n", j, j); } fprintf(out, "readonly def\n"); fprintf(out, "currentdict end\n"); // start the binary section fprintf(out, "currentfile eexec\n"); r1 = 55665; line = 0; // start the private dictionary eexecWrite("\x83\xca\x73\xd5"); eexecWrite("dup /Private 32 dict dup begin\n"); eexecWrite("/RD {string currentfile exch readstring pop} executeonly def\n"); eexecWrite("/ND {noaccess def} executeonly def\n"); eexecWrite("/NP {noaccess put} executeonly def\n"); eexecWrite("/MinFeature {16 16} ND\n"); eexecWrite(privateDicts[fd].dictData->getCString()); defaultWidthX = privateDicts[fd].defaultWidthX; defaultWidthXFP = privateDicts[fd].defaultWidthXFP; nominalWidthX = privateDicts[fd].nominalWidthX; nominalWidthXFP = privateDicts[fd].nominalWidthXFP; // start the CharStrings sprintf(eBuf, "2 index /CharStrings 256 dict dup begin\n"); eexecWrite(eBuf); // write the .notdef CharString idxPtr0 = getIndexValPtr(charStringsIdxPtr, 0); idxPtr1 = getIndexValPtr(charStringsIdxPtr, 1); n = idxPtr1 - idxPtr0; eexecCvtGlyph(".notdef", idxPtr0, n); // write the CharStrings for (j = 0; j < 256 && i+j < nCIDs; ++j) { if (cidMap[i+j] >= 0) { idxPtr0 = getIndexValPtr(charStringsIdxPtr, cidMap[i+j]); idxPtr1 = getIndexValPtr(charStringsIdxPtr, cidMap[i+j]+1); n = idxPtr1 - idxPtr0; sprintf(buf, "c%02x", j); eexecCvtGlyph(buf, idxPtr0, n); } } eexecWrite("end\n"); eexecWrite("end\n"); eexecWrite("readonly put\n"); eexecWrite("noaccess put\n"); eexecWrite("dup /FontName get exch definefont pop\n"); eexecWrite("mark currentfile closefile\n"); // trailer if (line > 0) { fputc('\n', out); } for (j = 0; j < 8; ++j) { fprintf(out, "0000000000000000000000000000000000000000000000000000000000000000\n"); } fprintf(out, "cleartomark\n"); } // write the Type 0 parent font fprintf(out, "16 dict begin\n"); fprintf(out, "/FontName /%s def\n", psName); fprintf(out, "/FontType 0 def\n"); fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n"); fprintf(out, "/FMapType 2 def\n"); fprintf(out, "/Encoding [\n"); for (i = 0; i < nCIDs; i += 256) { fprintf(out, "%d\n", i >> 8); } fprintf(out, "] def\n"); fprintf(out, "/FDepVector [\n"); for (i = 0; i < nCIDs; i += 256) { fprintf(out, "/%s_%02x findfont\n", psName, i >> 8); } fprintf(out, "] def\n"); fprintf(out, "FontName currentdict end definefont pop\n"); // clean up for (i = 0; i < nFDs; ++i) { delete privateDicts[i].dictData; } gfree(privateDicts); gfree(cidMap); gfree(charset); gfree(fdSelect); } void Type1CFontFile::readTopDict(Type1CTopDict *dict) { Guchar *idxPtr0, *idxPtr1, *ptr; double x; GBool isFP; int key; int i; idxPtr0 = getIndexValPtr(topDictIdxPtr, 0); idxPtr1 = getIndexValPtr(topDictIdxPtr, 1); dict->version = 0; dict->notice = 0; dict->copyright = 0; dict->fullName = 0; dict->familyName = 0; dict->weight = 0; dict->isFixedPitch = 0; dict->italicAngle = 0; dict->underlinePosition = -100; dict->underlineThickness = 50; dict->paintType = 0; dict->charstringType = 2; dict->fontMatrix[0] = 0.001; dict->fontMatrix[1] = 0; dict->fontMatrix[2] = 0; dict->fontMatrix[3] = 0.001; dict->fontMatrix[4] = 0; dict->fontMatrix[5] = 0; dict->uniqueID = 0; dict->fontBBox[0] = 0; dict->fontBBox[1] = 0; dict->fontBBox[2] = 0; dict->fontBBox[3] = 0; dict->strokeWidth = 0; dict->charset = 0; dict->encoding = 0; dict->charStrings = 0; dict->privateSize = 0; dict->privateOffset = 0; dict->registry = 0; dict->ordering = 0; dict->supplement = 0; dict->fdArrayOffset = 0; dict->fdSelectOffset = 0; i = 0; ptr = idxPtr0; while (ptr < idxPtr1) { if (*ptr <= 27 || *ptr == 31) { key = *ptr++; if (key == 0x0c) { key = (key << 8) | *ptr++; } switch (key) { case 0x0000: dict->version = (int)op[0]; break; case 0x0001: dict->notice = (int)op[0]; break; case 0x0c00: dict->copyright = (int)op[0]; break; case 0x0002: dict->fullName = (int)op[0]; break; case 0x0003: dict->familyName = (int)op[0]; break; case 0x0004: dict->weight = (int)op[0]; break; case 0x0c01: dict->isFixedPitch = (int)op[0]; break; case 0x0c02: dict->italicAngle = op[0]; break; case 0x0c03: dict->underlinePosition = op[0]; break; case 0x0c04: dict->underlineThickness = op[0]; break; case 0x0c05: dict->paintType = (int)op[0]; break; case 0x0c06: dict->charstringType = (int)op[0]; break; case 0x0c07: dict->fontMatrix[0] = op[0]; dict->fontMatrix[1] = op[1]; dict->fontMatrix[2] = op[2]; dict->fontMatrix[3] = op[3]; dict->fontMatrix[4] = op[4]; dict->fontMatrix[5] = op[5]; break; case 0x000d: dict->uniqueID = (int)op[0]; break; case 0x0005: dict->fontBBox[0] = op[0]; dict->fontBBox[1] = op[1]; dict->fontBBox[2] = op[2]; dict->fontBBox[3] = op[3]; break; case 0x0c08: dict->strokeWidth = op[0]; break; case 0x000f: dict->charset = (int)op[0]; break; case 0x0010: dict->encoding = (int)op[0]; break; case 0x0011: dict->charStrings = (int)op[0]; break; case 0x0012: dict->privateSize = (int)op[0]; dict->privateOffset = (int)op[1]; break; case 0x0c1e: dict->registry = (int)op[0]; dict->ordering = (int)op[1]; dict->supplement = (int)op[2]; break; case 0x0c24: dict->fdArrayOffset = (int)op[0]; break; case 0x0c25: dict->fdSelectOffset = (int)op[0]; break; } i = 0; } else { x = getNum(&ptr, &isFP); if (i < 48) { op[i] = x; fp[i++] = isFP; } } } } void Type1CFontFile::readPrivateDict(Type1CPrivateDict *privateDict, int offset, int size) { Guchar *idxPtr0, *idxPtr1, *ptr; char eBuf[256]; int key; double x; GBool isFP; int i; privateDict->dictData = new GString(); privateDict->subrsOffset = 0; privateDict->defaultWidthX = 0; privateDict->defaultWidthXFP = gFalse; privateDict->nominalWidthX = 0; privateDict->nominalWidthXFP = gFalse; idxPtr0 = (Guchar *)file + offset; idxPtr1 = idxPtr0 + size; ptr = idxPtr0; i = 0; while (ptr < idxPtr1) { if (*ptr <= 27 || *ptr == 31) { key = *ptr++; if (key == 0x0c) { key = (key << 8) | *ptr++; } switch (key) { case 0x0006: getDeltaInt(eBuf, "BlueValues", op, i); privateDict->dictData->append(eBuf); break; case 0x0007: getDeltaInt(eBuf, "OtherBlues", op, i); privateDict->dictData->append(eBuf); break; case 0x0008: getDeltaInt(eBuf, "FamilyBlues", op, i); privateDict->dictData->append(eBuf); break; case 0x0009: getDeltaInt(eBuf, "FamilyOtherBlues", op, i); privateDict->dictData->append(eBuf); break; case 0x0c09: sprintf(eBuf, "/BlueScale %g def\n", op[0]); privateDict->dictData->append(eBuf); break; case 0x0c0a: sprintf(eBuf, "/BlueShift %d def\n", (int)op[0]); privateDict->dictData->append(eBuf); break; case 0x0c0b: sprintf(eBuf, "/BlueFuzz %d def\n", (int)op[0]); privateDict->dictData->append(eBuf); break; case 0x000a: sprintf(eBuf, "/StdHW [%g] def\n", op[0]); privateDict->dictData->append(eBuf); break; case 0x000b: sprintf(eBuf, "/StdVW [%g] def\n", op[0]); privateDict->dictData->append(eBuf); break; case 0x0c0c: getDeltaReal(eBuf, "StemSnapH", op, i); privateDict->dictData->append(eBuf); break; case 0x0c0d: getDeltaReal(eBuf, "StemSnapV", op, i); privateDict->dictData->append(eBuf); break; case 0x0c0e: sprintf(eBuf, "/ForceBold %s def\n", op[0] ? "true" : "false"); privateDict->dictData->append(eBuf); break; case 0x0c0f: sprintf(eBuf, "/ForceBoldThreshold %g def\n", op[0]); privateDict->dictData->append(eBuf); break; case 0x0c11: sprintf(eBuf, "/LanguageGroup %d def\n", (int)op[0]); privateDict->dictData->append(eBuf); break; case 0x0c12: sprintf(eBuf, "/ExpansionFactor %g def\n", op[0]); privateDict->dictData->append(eBuf); break; case 0x0c13: error(-1, "Got Type 1C InitialRandomSeed"); break; case 0x0013: privateDict->subrsOffset = (int)op[0]; break; case 0x0014: privateDict->defaultWidthX = op[0]; privateDict->defaultWidthXFP = fp[0]; break; case 0x0015: privateDict->nominalWidthX = op[0]; privateDict->nominalWidthXFP = fp[0]; break; default: error(-1, "Unknown Type 1C private dict entry %04x", key); break; } i = 0; } else { x = getNum(&ptr, &isFP); if (i < 48) { op[i] = x; fp[i++] = isFP; } } } } Gushort *Type1CFontFile::readCharset(int charset, int nGlyphs) { Gushort *glyphNames; Guchar *ptr; int charsetFormat, c; int nLeft, i, j; if (charset == 0) { glyphNames = type1CISOAdobeCharset; } else if (charset == 1) { glyphNames = type1CExpertCharset; } else if (charset == 2) { glyphNames = type1CExpertSubsetCharset; } else { glyphNames = (Gushort *)gmalloc(nGlyphs * sizeof(Gushort)); glyphNames[0] = 0; ptr = (Guchar *)file + charset; charsetFormat = *ptr++; if (charsetFormat == 0) { for (i = 1; i < nGlyphs; ++i) { glyphNames[i] = getWord(ptr, 2); ptr += 2; } } else if (charsetFormat == 1) { i = 1; while (i < nGlyphs) { c = getWord(ptr, 2); ptr += 2; nLeft = *ptr++; for (j = 0; j <= nLeft && i < nGlyphs; ++j) { glyphNames[i++] = c++; } } } else if (charsetFormat == 2) { i = 1; while (i < nGlyphs) { c = getWord(ptr, 2); ptr += 2; nLeft = getWord(ptr, 2); ptr += 2; for (j = 0; j <= nLeft && i < nGlyphs; ++j) { glyphNames[i++] = c++; } } } } return glyphNames; } void Type1CFontFile::eexecWrite(char *s) { Guchar *p; Guchar x; for (p = (Guchar *)s; *p; ++p) { x = *p ^ (r1 >> 8); r1 = (x + r1) * 52845 + 22719; fputc(hexChars[x >> 4], out); fputc(hexChars[x & 0x0f], out); line += 2; if (line == 64) { fputc('\n', out); line = 0; } } } void Type1CFontFile::eexecCvtGlyph(char *glyphName, Guchar *s, int n) { char eBuf[256]; cvtGlyph(s, n); sprintf(eBuf, "/%s %d RD ", glyphName, charBuf->getLength()); eexecWrite(eBuf); eexecWriteCharstring((Guchar *)charBuf->getCString(), charBuf->getLength()); eexecWrite(" ND\n"); delete charBuf; } void Type1CFontFile::cvtGlyph(Guchar *s, int n) { int nHints; int x; GBool first = gTrue; double d, dx, dy; GBool dFP; Gushort r2; Guchar byte; int i, k; charBuf = new GString(); charBuf->append((char)73); charBuf->append((char)58); charBuf->append((char)147); charBuf->append((char)134); i = 0; nOps = 0; nHints = 0; while (i < n) { if (s[i] == 12) { switch (s[i+1]) { case 0: // dotsection (should be Type 1 only?) // ignored break; case 34: // hflex if (nOps != 7) { error(-1, "Wrong number of args (%d) to Type 2 hflex", nOps); } eexecDumpNum(op[0], fp[0]); eexecDumpNum(0, gFalse); eexecDumpNum(op[1], fp[1]); eexecDumpNum(op[2], fp[2]); eexecDumpNum(op[3], fp[3]); eexecDumpNum(0, gFalse); eexecDumpOp1(8); eexecDumpNum(op[4], fp[4]); eexecDumpNum(0, gFalse); eexecDumpNum(op[5], fp[5]); eexecDumpNum(-op[2], fp[2]); eexecDumpNum(op[6], fp[6]); eexecDumpNum(0, gFalse); eexecDumpOp1(8); break; case 35: // flex if (nOps != 13) { error(-1, "Wrong number of args (%d) to Type 2 flex", nOps); } eexecDumpNum(op[0], fp[0]); eexecDumpNum(op[1], fp[1]); eexecDumpNum(op[2], fp[2]); eexecDumpNum(op[3], fp[3]); eexecDumpNum(op[4], fp[4]); eexecDumpNum(op[5], fp[5]); eexecDumpOp1(8); eexecDumpNum(op[6], fp[6]); eexecDumpNum(op[7], fp[7]); eexecDumpNum(op[8], fp[8]); eexecDumpNum(op[9], fp[9]); eexecDumpNum(op[10], fp[10]); eexecDumpNum(op[11], fp[11]); eexecDumpOp1(8); break; case 36: // hflex1 if (nOps != 9) { error(-1, "Wrong number of args (%d) to Type 2 hflex1", nOps); } eexecDumpNum(op[0], fp[0]); eexecDumpNum(op[1], fp[1]); eexecDumpNum(op[2], fp[2]); eexecDumpNum(op[3], fp[3]); eexecDumpNum(op[4], fp[4]); eexecDumpNum(0, gFalse); eexecDumpOp1(8); eexecDumpNum(op[5], fp[5]); eexecDumpNum(0, gFalse); eexecDumpNum(op[6], fp[6]); eexecDumpNum(op[7], fp[7]); eexecDumpNum(op[8], fp[8]); eexecDumpNum(-(op[1] + op[3] + op[7]), fp[1] | fp[3] | fp[7]); eexecDumpOp1(8); break; case 37: // flex1 if (nOps != 11) { error(-1, "Wrong number of args (%d) to Type 2 flex1", nOps); } eexecDumpNum(op[0], fp[0]); eexecDumpNum(op[1], fp[1]); eexecDumpNum(op[2], fp[2]); eexecDumpNum(op[3], fp[3]); eexecDumpNum(op[4], fp[4]); eexecDumpNum(op[5], fp[5]); eexecDumpOp1(8); eexecDumpNum(op[6], fp[6]); eexecDumpNum(op[7], fp[7]); eexecDumpNum(op[8], fp[8]); eexecDumpNum(op[9], fp[9]); dx = op[0] + op[2] + op[4] + op[6] + op[8]; dy = op[1] + op[3] + op[5] + op[7] + op[9]; if (fabs(dx) > fabs(dy)) { eexecDumpNum(op[10], fp[10]); eexecDumpNum(-dy, fp[1] | fp[3] | fp[5] | fp[7] | fp[9]); } else { eexecDumpNum(-dx, fp[0] | fp[2] | fp[4] | fp[6] | fp[8]); eexecDumpNum(op[10], fp[10]); } eexecDumpOp1(8); break; case 3: // and case 4: // or case 5: // not case 8: // store case 9: // abs case 10: // add case 11: // sub case 12: // div case 13: // load case 14: // neg case 15: // eq case 18: // drop case 20: // put case 21: // get case 22: // ifelse case 23: // random case 24: // mul case 26: // sqrt case 27: // dup case 28: // exch case 29: // index case 30: // roll error(-1, "Unimplemented Type 2 charstring op: 12.%d", s[i+1]); break; default: error(-1, "Illegal Type 2 charstring op: 12.%d", s[i+1]); break; } i += 2; nOps = 0; } else if (s[i] == 19) { // hintmask // ignored if (first) { cvtGlyphWidth(nOps == 1); first = gFalse; } if (nOps > 0) { if (nOps & 1) { error(-1, "Wrong number of args (%d) to Type 2 hintmask/vstemhm", nOps); } nHints += nOps / 2; } i += 1 + ((nHints + 7) >> 3); nOps = 0; } else if (s[i] == 20) { // cntrmask // ignored if (first) { cvtGlyphWidth(nOps == 1); first = gFalse; } if (nOps > 0) { if (nOps & 1) { error(-1, "Wrong number of args (%d) to Type 2 cntrmask/vstemhm", nOps); } nHints += nOps / 2; } i += 1 + ((nHints + 7) >> 3); nOps = 0; } else if (s[i] == 28) { x = (s[i+1] << 8) + s[i+2]; if (x & 0x8000) { x |= -1 << 15; } if (nOps < 48) { fp[nOps] = gFalse; op[nOps++] = x; } i += 3; } else if (s[i] <= 31) { switch (s[i]) { case 4: // vmoveto if (first) { cvtGlyphWidth(nOps == 2); first = gFalse; } if (nOps != 1) { error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps); } eexecDumpNum(op[0], fp[0]); eexecDumpOp1(4); break; case 5: // rlineto if (nOps < 2 || nOps % 2 != 0) { error(-1, "Wrong number of args (%d) to Type 2 rlineto", nOps); } for (k = 0; k < nOps; k += 2) { eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpOp1(5); } break; case 6: // hlineto if (nOps < 1) { error(-1, "Wrong number of args (%d) to Type 2 hlineto", nOps); } for (k = 0; k < nOps; ++k) { eexecDumpNum(op[k], fp[k]); eexecDumpOp1((k & 1) ? 7 : 6); } break; case 7: // vlineto if (nOps < 1) { error(-1, "Wrong number of args (%d) to Type 2 vlineto", nOps); } for (k = 0; k < nOps; ++k) { eexecDumpNum(op[k], fp[k]); eexecDumpOp1((k & 1) ? 6 : 7); } break; case 8: // rrcurveto if (nOps < 6 || nOps % 6 != 0) { error(-1, "Wrong number of args (%d) to Type 2 rrcurveto", nOps); } for (k = 0; k < nOps; k += 6) { eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(op[k+3], fp[k+3]); eexecDumpNum(op[k+4], fp[k+4]); eexecDumpNum(op[k+5], fp[k+5]); eexecDumpOp1(8); } break; case 14: // endchar / seac if (first) { cvtGlyphWidth(nOps == 1 || nOps == 5); first = gFalse; } if (nOps == 4) { eexecDumpNum(0, 0); eexecDumpNum(op[0], fp[0]); eexecDumpNum(op[1], fp[1]); eexecDumpNum(op[2], fp[2]); eexecDumpNum(op[3], fp[3]); eexecDumpOp2(6); } else if (nOps == 0) { eexecDumpOp1(14); } else { error(-1, "Wrong number of args (%d) to Type 2 endchar", nOps); } break; case 21: // rmoveto if (first) { cvtGlyphWidth(nOps == 3); first = gFalse; } if (nOps != 2) { error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps); } eexecDumpNum(op[0], fp[0]); eexecDumpNum(op[1], fp[1]); eexecDumpOp1(21); break; case 22: // hmoveto if (first) { cvtGlyphWidth(nOps == 2); first = gFalse; } if (nOps != 1) { error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps); } eexecDumpNum(op[0], fp[0]); eexecDumpOp1(22); break; case 24: // rcurveline if (nOps < 8 || (nOps - 2) % 6 != 0) { error(-1, "Wrong number of args (%d) to Type 2 rcurveline", nOps); } for (k = 0; k < nOps - 2; k += 6) { eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(op[k+3], fp[k+3]); eexecDumpNum(op[k+4], fp[k+4]); eexecDumpNum(op[k+5], fp[k+5]); eexecDumpOp1(8); } eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k]); eexecDumpOp1(5); break; case 25: // rlinecurve if (nOps < 8 || (nOps - 6) % 2 != 0) { error(-1, "Wrong number of args (%d) to Type 2 rlinecurve", nOps); } for (k = 0; k < nOps - 6; k += 2) { eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k]); eexecDumpOp1(5); } eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(op[k+3], fp[k+3]); eexecDumpNum(op[k+4], fp[k+4]); eexecDumpNum(op[k+5], fp[k+5]); eexecDumpOp1(8); break; case 26: // vvcurveto if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) { error(-1, "Wrong number of args (%d) to Type 2 vvcurveto", nOps); } if (nOps % 2 == 1) { eexecDumpNum(op[0], fp[0]); eexecDumpNum(op[1], fp[1]); eexecDumpNum(op[2], fp[2]); eexecDumpNum(op[3], fp[3]); eexecDumpNum(0, gFalse); eexecDumpNum(op[4], fp[4]); eexecDumpOp1(8); k = 5; } else { k = 0; } for (; k < nOps; k += 4) { eexecDumpNum(0, gFalse); eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(0, gFalse); eexecDumpNum(op[k+3], fp[k+3]); eexecDumpOp1(8); } break; case 27: // hhcurveto if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) { error(-1, "Wrong number of args (%d) to Type 2 hhcurveto", nOps); } if (nOps % 2 == 1) { eexecDumpNum(op[1], fp[1]); eexecDumpNum(op[0], fp[0]); eexecDumpNum(op[2], fp[2]); eexecDumpNum(op[3], fp[3]); eexecDumpNum(op[4], fp[4]); eexecDumpNum(0, gFalse); eexecDumpOp1(8); k = 5; } else { k = 0; } for (; k < nOps; k += 4) { eexecDumpNum(op[k], fp[k]); eexecDumpNum(0, gFalse); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(op[k+3], fp[k+3]); eexecDumpNum(0, gFalse); eexecDumpOp1(8); } break; case 30: // vhcurveto if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) { error(-1, "Wrong number of args (%d) to Type 2 vhcurveto", nOps); } for (k = 0; k < nOps && k != nOps-5; k += 4) { if (k % 8 == 0) { eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(op[k+3], fp[k+3]); eexecDumpOp1(30); } else { eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(op[k+3], fp[k+3]); eexecDumpOp1(31); } } if (k == nOps-5) { if (k % 8 == 0) { eexecDumpNum(0, gFalse); eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(op[k+3], fp[k+3]); eexecDumpNum(op[k+4], fp[k+4]); } else { eexecDumpNum(op[k], fp[k]); eexecDumpNum(0, gFalse); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(op[k+4], fp[k+4]); eexecDumpNum(op[k+3], fp[k+3]); } eexecDumpOp1(8); } break; case 31: // hvcurveto if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) { error(-1, "Wrong number of args (%d) to Type 2 hvcurveto", nOps); } for (k = 0; k < nOps && k != nOps-5; k += 4) { if (k % 8 == 0) { eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(op[k+3], fp[k+3]); eexecDumpOp1(31); } else { eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(op[k+3], fp[k+3]); eexecDumpOp1(30); } } if (k == nOps-5) { if (k % 8 == 0) { eexecDumpNum(op[k], fp[k]); eexecDumpNum(0, gFalse); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(op[k+4], fp[k+4]); eexecDumpNum(op[k+3], fp[k+3]); } else { eexecDumpNum(0, gFalse); eexecDumpNum(op[k], fp[k]); eexecDumpNum(op[k+1], fp[k+1]); eexecDumpNum(op[k+2], fp[k+2]); eexecDumpNum(op[k+3], fp[k+3]); eexecDumpNum(op[k+4], fp[k+4]); } eexecDumpOp1(8); } break; case 1: // hstem if (first) { cvtGlyphWidth(nOps & 1); first = gFalse; } if (nOps & 1) { error(-1, "Wrong number of args (%d) to Type 2 hstem", nOps); } d = 0; dFP = gFalse; for (k = 0; k < nOps; k += 2) { if (op[k+1] < 0) { d += op[k] + op[k+1]; dFP |= fp[k] | fp[k+1]; eexecDumpNum(d, dFP); eexecDumpNum(-op[k+1], fp[k+1]); } else { d += op[k]; dFP |= fp[k]; eexecDumpNum(d, dFP); eexecDumpNum(op[k+1], fp[k+1]); d += op[k+1]; dFP |= fp[k+1]; } eexecDumpOp1(1); } nHints += nOps / 2; break; case 3: // vstem if (first) { cvtGlyphWidth(nOps & 1); first = gFalse; } if (nOps & 1) { error(-1, "Wrong number of args (%d) to Type 2 vstem", nOps); } d = 0; dFP = gFalse; for (k = 0; k < nOps; k += 2) { if (op[k+1] < 0) { d += op[k] + op[k+1]; dFP |= fp[k] | fp[k+1]; eexecDumpNum(d, dFP); eexecDumpNum(-op[k+1], fp[k+1]); } else { d += op[k]; dFP |= fp[k]; eexecDumpNum(d, dFP); eexecDumpNum(op[k+1], fp[k+1]); d += op[k+1]; dFP |= fp[k+1]; } eexecDumpOp1(3); } nHints += nOps / 2; break; case 18: // hstemhm // ignored if (first) { cvtGlyphWidth(nOps & 1); first = gFalse; } if (nOps & 1) { error(-1, "Wrong number of args (%d) to Type 2 hstemhm", nOps); } nHints += nOps / 2; break; case 23: // vstemhm // ignored if (first) { cvtGlyphWidth(nOps & 1); first = gFalse; } if (nOps & 1) { error(-1, "Wrong number of args (%d) to Type 2 vstemhm", nOps); } nHints += nOps / 2; break; case 10: // callsubr case 11: // return case 16: // blend case 29: // callgsubr error(-1, "Unimplemented Type 2 charstring op: %d", s[i]); break; default: error(-1, "Illegal Type 2 charstring op: %d", s[i]); break; } ++i; nOps = 0; } else if (s[i] <= 246) { if (nOps < 48) { fp[nOps] = gFalse; op[nOps++] = (int)s[i] - 139; } ++i; } else if (s[i] <= 250) { if (nOps < 48) { fp[nOps] = gFalse; op[nOps++] = (((int)s[i] - 247) << 8) + (int)s[i+1] + 108; } i += 2; } else if (s[i] <= 254) { if (nOps < 48) { fp[nOps] = gFalse; op[nOps++] = -(((int)s[i] - 251) << 8) - (int)s[i+1] - 108; } i += 2; } else { x = (s[i+1] << 24) | (s[i+2] << 16) | (s[i+3] << 8) | s[i+4]; if (x & 0x80000000) x |= -1 << 31; if (nOps < 48) { fp[nOps] = gTrue; op[nOps++] = (double)x / 65536.0; } i += 5; } } // charstring encryption r2 = 4330; for (i = 0; i < charBuf->getLength(); ++i) { byte = charBuf->getChar(i) ^ (r2 >> 8); charBuf->setChar(i, byte); r2 = (byte + r2) * 52845 + 22719; } } void Type1CFontFile::cvtGlyphWidth(GBool useOp) { double w; GBool wFP; int i; if (useOp) { w = nominalWidthX + op[0]; wFP = nominalWidthXFP | fp[0]; for (i = 1; i < nOps; ++i) { op[i-1] = op[i]; fp[i-1] = fp[i]; } --nOps; } else { w = defaultWidthX; wFP = defaultWidthXFP; } eexecDumpNum(0, gFalse); eexecDumpNum(w, wFP); eexecDumpOp1(13); } void Type1CFontFile::eexecDumpNum(double x, GBool fpA) { Guchar buf[12]; int y, n; n = 0; if (fpA) { if (x >= -32768 && x < 32768) { y = (int)(x * 256.0); buf[0] = 255; buf[1] = (Guchar)(y >> 24); buf[2] = (Guchar)(y >> 16); buf[3] = (Guchar)(y >> 8); buf[4] = (Guchar)y; buf[5] = 255; buf[6] = 0; buf[7] = 0; buf[8] = 1; buf[9] = 0; buf[10] = 12; buf[11] = 12; n = 12; } else { error(-1, "Type 2 fixed point constant out of range"); } } else { y = (int)x; if (y >= -107 && y <= 107) { buf[0] = (Guchar)(y + 139); n = 1; } else if (y > 107 && y <= 1131) { y -= 108; buf[0] = (Guchar)((y >> 8) + 247); buf[1] = (Guchar)(y & 0xff); n = 2; } else if (y < -107 && y >= -1131) { y = -y - 108; buf[0] = (Guchar)((y >> 8) + 251); buf[1] = (Guchar)(y & 0xff); n = 2; } else { buf[0] = 255; buf[1] = (Guchar)(y >> 24); buf[2] = (Guchar)(y >> 16); buf[3] = (Guchar)(y >> 8); buf[4] = (Guchar)y; n = 5; } } charBuf->append((char *)buf, n); } void Type1CFontFile::eexecDumpOp1(int opA) { charBuf->append((char)opA); } void Type1CFontFile::eexecDumpOp2(int opA) { charBuf->append((char)12); charBuf->append((char)opA); } void Type1CFontFile::eexecWriteCharstring(Guchar *s, int n) { Guchar x; int i; // eexec encryption for (i = 0; i < n; ++i) { x = s[i] ^ (r1 >> 8); r1 = (x + r1) * 52845 + 22719; fputc(hexChars[x >> 4], out); fputc(hexChars[x & 0x0f], out); line += 2; if (line == 64) { fputc('\n', out); line = 0; } } } void Type1CFontFile::getDeltaInt(char *buf, char *key, double *opA, int n) { int x, i; sprintf(buf, "/%s [", key); buf += strlen(buf); x = 0; for (i = 0; i < n; ++i) { x += (int)opA[i]; sprintf(buf, "%s%d", i > 0 ? " " : "", x); buf += strlen(buf); } sprintf(buf, "] def\n"); } void Type1CFontFile::getDeltaReal(char *buf, char *key, double *opA, int n) { double x; int i; sprintf(buf, "/%s [", key); buf += strlen(buf); x = 0; for (i = 0; i < n; ++i) { x += opA[i]; sprintf(buf, "%s%g", i > 0 ? " " : "", x); buf += strlen(buf); } sprintf(buf, "] def\n"); } int Type1CFontFile::getIndexLen(Guchar *indexPtr) { return (int)getWord(indexPtr, 2); } Guchar *Type1CFontFile::getIndexValPtr(Guchar *indexPtr, int i) { int n, offSize; Guchar *idxStartPtr; n = (int)getWord(indexPtr, 2); offSize = indexPtr[2]; idxStartPtr = indexPtr + 3 + (n + 1) * offSize - 1; return idxStartPtr + getWord(indexPtr + 3 + i * offSize, offSize); } Guchar *Type1CFontFile::getIndexEnd(Guchar *indexPtr) { int n, offSize; Guchar *idxStartPtr; n = (int)getWord(indexPtr, 2); offSize = indexPtr[2]; idxStartPtr = indexPtr + 3 + (n + 1) * offSize - 1; return idxStartPtr + getWord(indexPtr + 3 + n * offSize, offSize); } Guint Type1CFontFile::getWord(Guchar *ptr, int size) { Guint x; int i; x = 0; for (i = 0; i < size; ++i) { x = (x << 8) + *ptr++; } return x; } double Type1CFontFile::getNum(Guchar **ptr, GBool *isFP) { static char nybChars[16] = "0123456789.ee -"; int b0, b, nyb0, nyb1; double x; char buf[65]; int i; x = 0; *isFP = gFalse; b0 = (*ptr)[0]; if (b0 < 28) { x = 0; } else if (b0 == 28) { x = ((*ptr)[1] << 8) + (*ptr)[2]; *ptr += 3; } else if (b0 == 29) { x = ((*ptr)[1] << 24) + ((*ptr)[2] << 16) + ((*ptr)[3] << 8) + (*ptr)[4]; *ptr += 5; } else if (b0 == 30) { *ptr += 1; i = 0; do { b = *(*ptr)++; nyb0 = b >> 4; nyb1 = b & 0x0f; if (nyb0 == 0xf) { break; } buf[i++] = nybChars[nyb0]; if (i == 64) { break; } if (nyb0 == 0xc) { buf[i++] = '-'; } if (i == 64) { break; } if (nyb1 == 0xf) { break; } buf[i++] = nybChars[nyb1]; if (i == 64) { break; } if (nyb1 == 0xc) { buf[i++] = '-'; } } while (i < 64); buf[i] = '\0'; x = atof(buf); *isFP = gTrue; } else if (b0 == 31) { x = 0; } else if (b0 < 247) { x = b0 - 139; *ptr += 1; } else if (b0 < 251) { x = ((b0 - 247) << 8) + (*ptr)[1] + 108; *ptr += 2; } else { x = -((b0 - 251) << 8) - (*ptr)[1] - 108; *ptr += 2; } return x; } char *Type1CFontFile::getString(int sid, char *buf) { Guchar *idxPtr0, *idxPtr1; int n; if (sid < 391) { strcpy(buf, type1CStdStrings[sid]); } else { sid -= 391; idxPtr0 = getIndexValPtr(stringIdxPtr, sid); idxPtr1 = getIndexValPtr(stringIdxPtr, sid + 1); if ((n = idxPtr1 - idxPtr0) > 255) { n = 255; } strncpy(buf, (char *)idxPtr0, n); buf[n] = '\0'; } return buf; } //------------------------------------------------------------------------ // TrueTypeFontFile //------------------------------------------------------------------------ // // 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 = 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] = glyphIdx // // 'post' table: mapping from glyph index to glyph name // // post[glyphIdx] = 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] = glyphIdx // struct TTFontTableHdr { char tag[4]; Guint checksum; Guint offset; Guint length; }; struct T42Table { char *tag; // 4-byte tag GBool 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 ", gTrue }, { "fpgm", gTrue }, { "glyf", gTrue }, { "head", gTrue }, { "hhea", gTrue }, { "hmtx", gTrue }, { "loca", gTrue }, { "maxp", gTrue }, { "prep", gTrue }, { "vhea", gFalse }, { "vmtx", gFalse } }; #define t42HeadTable 3 #define t42LocaTable 6 #define t42GlyfTable 2 // Glyph names in some arbitrary standard that Apple uses for their // TrueType fonts. static 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", "mu1", "partialdiff", "summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Ohm", "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" }; enum T42FontIndexMode { t42FontModeUnicode, t42FontModeCharCode, t42FontModeCharCodeOffset, t42FontModeMacRoman }; TrueTypeFontFile::TrueTypeFontFile(char *fileA, int lenA) { int pos, i; file = fileA; len = lenA; encoding = NULL; // read table directory nTables = getUShort(4); tableHdrs = (TTFontTableHdr *)gmalloc(nTables * sizeof(TTFontTableHdr)); pos = 12; for (i = 0; i < nTables; ++i) { tableHdrs[i].tag[0] = getByte(pos+0); tableHdrs[i].tag[1] = getByte(pos+1); tableHdrs[i].tag[2] = getByte(pos+2); tableHdrs[i].tag[3] = getByte(pos+3); tableHdrs[i].checksum = getULong(pos+4); tableHdrs[i].offset = getULong(pos+8); tableHdrs[i].length = getULong(pos+12); pos += 16; } // check for tables that are required by both the TrueType spec // and the Type 42 spec if (seekTable("head") < 0 || seekTable("hhea") < 0 || seekTable("loca") < 0 || seekTable("maxp") < 0 || seekTable("glyf") < 0 || seekTable("hmtx") < 0) { error(-1, "TrueType font file is missing a required table"); return; } // read the 'head' table pos = seekTable("head"); bbox[0] = getShort(pos + 36); bbox[1] = getShort(pos + 38); bbox[2] = getShort(pos + 40); bbox[3] = getShort(pos + 42); locaFmt = getShort(pos + 50); // read the 'maxp' table pos = seekTable("maxp"); nGlyphs = getUShort(pos + 4); } TrueTypeFontFile::~TrueTypeFontFile() { int i; if (encoding) { for (i = 0; i < 256; ++i) { gfree(encoding[i]); } gfree(encoding); } gfree(tableHdrs); } char *TrueTypeFontFile::getName() { return NULL; } char **TrueTypeFontFile::getEncoding() { int cmap[256]; int nCmaps, cmapPlatform, cmapEncoding, cmapFmt; int cmapLen, cmapOffset, cmapFirst; int segCnt, segStart, segEnd, segDelta, segOffset; int pos, i, j, k; Guint fmt; GString *s; int stringIdx, stringPos, n; if (encoding) { return encoding; } //----- construct the (char code) -> (glyph idx) mapping // map everything to the missing glyph for (i = 0; i < 256; ++i) { cmap[i] = 0; } // look for the 'cmap' table if ((pos = seekTable("cmap")) >= 0) { nCmaps = getUShort(pos+2); // if the font has a Windows-symbol cmap, use it; // otherwise, use the first cmap in the table for (i = 0; i < nCmaps; ++i) { cmapPlatform = getUShort(pos + 4 + 8*i); cmapEncoding = getUShort(pos + 4 + 8*i + 2); if (cmapPlatform == 3 && cmapEncoding == 0) { break; } } if (i >= nCmaps) { i = 0; cmapPlatform = getUShort(pos + 4); cmapEncoding = getUShort(pos + 4 + 2); } pos += getULong(pos + 4 + 8*i + 4); // read the cmap cmapFmt = getUShort(pos); switch (cmapFmt) { case 0: // byte encoding table (Apple standard) cmapLen = getUShort(pos + 2); for (i = 0; i < cmapLen && i < 256; ++i) { cmap[i] = getByte(pos + 6 + i); } break; case 4: // segment mapping to delta values (Microsoft standard) if (cmapPlatform == 3 && cmapEncoding == 0) { // Windows-symbol uses char codes 0xf000 - 0xf0ff cmapOffset = 0xf000; } else { cmapOffset = 0; } segCnt = getUShort(pos + 6) / 2; for (i = 0; i < segCnt; ++i) { segEnd = getUShort(pos + 14 + 2*i); segStart = getUShort(pos + 16 + 2*segCnt + 2*i); segDelta = getUShort(pos + 16 + 4*segCnt + 2*i); segOffset = getUShort(pos + 16 + 6*segCnt + 2*i); if (segStart - cmapOffset <= 0xff && segEnd - cmapOffset >= 0) { for (j = (segStart - cmapOffset >= 0) ? segStart : cmapOffset; j <= segEnd && j - cmapOffset <= 0xff; ++j) { if (segOffset == 0) { k = (j + segDelta) & 0xffff; } else { k = getUShort(pos + 16 + 6*segCnt + 2*i + segOffset + 2 * (j - segStart)); if (k != 0) { k = (k + segDelta) & 0xffff; } } cmap[j - cmapOffset] = k; } } } break; case 6: // trimmed table mapping cmapFirst = getUShort(pos + 6); cmapLen = getUShort(pos + 8); for (i = cmapFirst; i < 256 && i < cmapFirst + cmapLen; ++i) { cmap[i] = getUShort(pos + 10 + 2*i); } break; default: error(-1, "Unimplemented cmap format (%d) in TrueType font file", cmapFmt); break; } } //----- construct the (glyph idx) -> (glyph name) mapping //----- and compute the (char code) -> (glyph name) mapping encoding = (char **)gmalloc(256 * sizeof(char *)); for (i = 0; i < 256; ++i) { encoding[i] = NULL; } if ((pos = seekTable("post")) >= 0) { fmt = getULong(pos); // Apple font if (fmt == 0x00010000) { for (i = 0; i < 256; ++i) { j = (cmap[i] < 258) ? cmap[i] : 0; encoding[i] = copyString(macGlyphNames[j]); } // Microsoft font } else if (fmt == 0x00020000) { stringIdx = 0; stringPos = pos + 34 + 2*nGlyphs; for (i = 0; i < 256; ++i) { if (cmap[i] < nGlyphs) { j = getUShort(pos + 34 + 2 * cmap[i]); if (j < 258) { encoding[i] = copyString(macGlyphNames[j]); } else { j -= 258; if (j != stringIdx) { for (stringIdx = 0, stringPos = pos + 34 + 2*nGlyphs; stringIdx < j; ++stringIdx, stringPos += 1 + getByte(stringPos)) ; } n = getByte(stringPos); s = new GString(file + stringPos + 1, n); encoding[i] = copyString(s->getCString()); delete s; ++stringIdx; stringPos += 1 + n; } } else { encoding[i] = copyString(macGlyphNames[0]); } } // Apple subset } else if (fmt == 0x000280000) { for (i = 0; i < 256; ++i) { if (cmap[i] < nGlyphs) { j = i + getChar(pos + 32 + cmap[i]); } else { j = 0; } encoding[i] = copyString(macGlyphNames[j]); } // Ugh, just assume the Apple glyph set } else { for (i = 0; i < 256; ++i) { j = (cmap[i] < 258) ? cmap[i] : 0; encoding[i] = copyString(macGlyphNames[j]); } } // no "post" table: assume the Apple glyph set } else { for (i = 0; i < 256; ++i) { j = (cmap[i] < 258) ? cmap[i] : 0; encoding[i] = copyString(macGlyphNames[j]); } } return encoding; } void TrueTypeFontFile::convertToType42(char *name, char **encodingA, CharCodeToUnicode *toUnicode, GBool pdfFontHasEncoding, FILE *out) { // write the header fprintf(out, "%%!PS-TrueTypeFont-%g\n", getFixed(0)); // begin the font dictionary fprintf(out, "10 dict begin\n"); fprintf(out, "/FontName /%s def\n", name); fprintf(out, "/FontType 42 def\n"); fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n"); fprintf(out, "/FontBBox [%d %d %d %d] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); fprintf(out, "/PaintType 0 def\n"); // write the guts of the dictionary cvtEncoding(encodingA, out); cvtCharStrings(encodingA, toUnicode, pdfFontHasEncoding, out); cvtSfnts(out, NULL); // end the dictionary and define the font fprintf(out, "FontName currentdict end definefont pop\n"); } void TrueTypeFontFile::convertToCIDType2(char *name, Gushort *cidMap, int nCIDs, FILE *out) { Gushort cid; int i, j, k; // write the header fprintf(out, "%%!PS-TrueTypeFont-%g\n", getFixed(0)); // begin the font dictionary fprintf(out, "20 dict begin\n"); fprintf(out, "/CIDFontName /%s def\n", name); fprintf(out, "/CIDFontType 2 def\n"); fprintf(out, "/FontType 42 def\n"); fprintf(out, "/CIDSystemInfo 3 dict dup begin\n"); fprintf(out, " /Registry (Adobe) def\n"); fprintf(out, " /Ordering (Identity) def\n"); fprintf(out, " /Supplement 0 def\n"); fprintf(out, " end def\n"); fprintf(out, "/GDBytes 2 def\n"); if (cidMap) { fprintf(out, "/CIDCount %d def\n", nCIDs); if (nCIDs > 32767) { fprintf(out, "/CIDMap ["); for (i = 0; i < nCIDs; i += 32768 - 16) { fprintf(out, "<\n"); for (j = 0; j < 32768 - 16 && i+j < nCIDs; j += 16) { fprintf(out, " "); for (k = 0; k < 16 && i+j+k < nCIDs; ++k) { cid = cidMap[i+j+k]; fprintf(out, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff); } fprintf(out, "\n"); } fprintf(out, " >"); } fprintf(out, "\n"); fprintf(out, "] def\n"); } else { fprintf(out, "/CIDMap <\n"); for (i = 0; i < nCIDs; i += 16) { fprintf(out, " "); for (j = 0; j < 16 && i+j < nCIDs; ++j) { cid = cidMap[i+j]; fprintf(out, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff); } fprintf(out, "\n"); } fprintf(out, "> def\n"); } } else { // direct mapping - just fill the string(s) with s[i]=i fprintf(out, "/CIDCount %d def\n", nGlyphs); if (nGlyphs > 32767) { fprintf(out, "/CIDMap [\n"); for (i = 0; i < nGlyphs; i += 32767) { j = nGlyphs - i < 32767 ? nGlyphs - i : 32767; fprintf(out, " %d string 0 1 %d {\n", 2 * j, j - 1); fprintf(out, " 2 copy dup 2 mul exch %d add -8 bitshift put\n", i); fprintf(out, " 1 index exch dup 2 mul 1 add exch %d add" " 255 and put\n", i); fprintf(out, " } for\n"); } fprintf(out, "] def\n"); } else { fprintf(out, "/CIDMap %d string\n", 2 * nGlyphs); fprintf(out, " 0 1 %d {\n", nGlyphs - 1); fprintf(out, " 2 copy dup 2 mul exch -8 bitshift put\n"); fprintf(out, " 1 index exch dup 2 mul 1 add exch 255 and put\n"); fprintf(out, " } for\n"); fprintf(out, "def\n"); } } fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n"); fprintf(out, "/FontBBox [%d %d %d %d] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); fprintf(out, "/PaintType 0 def\n"); fprintf(out, "/Encoding [] readonly def\n"); fprintf(out, "/CharStrings 1 dict dup begin\n"); fprintf(out, " /.notdef 0 def\n"); fprintf(out, " end readonly def\n"); // write the guts of the dictionary cvtSfnts(out, NULL); // end the dictionary and define the font fprintf(out, "CIDFontName currentdict end /CIDFont defineresource pop\n"); } void TrueTypeFontFile::convertToType0(char *name, Gushort *cidMap, int nCIDs, FILE *out) { GString *sfntsName; int n, i, j; // write the Type 42 sfnts array sfntsName = (new GString(name))->append("_sfnts"); cvtSfnts(out, sfntsName); delete sfntsName; // write the descendant Type 42 fonts n = cidMap ? nCIDs : nGlyphs; for (i = 0; i < n; i += 256) { fprintf(out, "10 dict begin\n"); fprintf(out, "/FontName /%s_%02x def\n", name, i >> 8); fprintf(out, "/FontType 42 def\n"); fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n"); fprintf(out, "/FontBBox [%d %d %d %d] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); fprintf(out, "/PaintType 0 def\n"); fprintf(out, "/sfnts %s_sfnts def\n", name); fprintf(out, "/Encoding 256 array\n"); for (j = 0; j < 256 && i+j < n; ++j) { fprintf(out, "dup %d /c%02x put\n", j, j); } fprintf(out, "readonly def\n"); fprintf(out, "/CharStrings 257 dict dup begin\n"); fprintf(out, "/.notdef 0 def\n"); for (j = 0; j < 256 && i+j < n; ++j) { fprintf(out, "/c%02x %d def\n", j, cidMap ? cidMap[i+j] : i+j); } fprintf(out, "end readonly def\n"); fprintf(out, "FontName currentdict end definefont pop\n"); } // write the Type 0 parent font fprintf(out, "16 dict begin\n"); fprintf(out, "/FontName /%s def\n", name); fprintf(out, "/FontType 0 def\n"); fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n"); fprintf(out, "/FMapType 2 def\n"); fprintf(out, "/Encoding [\n"); for (i = 0; i < n; i += 256) { fprintf(out, "%d\n", i >> 8); } fprintf(out, "] def\n"); fprintf(out, "/FDepVector [\n"); for (i = 0; i < n; i += 256) { fprintf(out, "/%s_%02x findfont\n", name, i >> 8); } fprintf(out, "] def\n"); fprintf(out, "FontName currentdict end definefont pop\n"); } int TrueTypeFontFile::getByte(int pos) { if (pos < 0 || pos >= len) { return 0; } return file[pos] & 0xff; } int TrueTypeFontFile::getChar(int pos) { int x; if (pos < 0 || pos >= len) { return 0; } x = file[pos] & 0xff; if (x & 0x80) x |= 0xffffff00; return x; } int TrueTypeFontFile::getUShort(int pos) { int x; if (pos < 0 || pos+1 >= len) { return 0; } x = file[pos] & 0xff; x = (x << 8) + (file[pos+1] & 0xff); return x; } int TrueTypeFontFile::getShort(int pos) { int x; if (pos < 0 || pos+1 >= len) { return 0; } x = file[pos] & 0xff; x = (x << 8) + (file[pos+1] & 0xff); if (x & 0x8000) x |= 0xffff0000; return x; } Guint TrueTypeFontFile::getULong(int pos) { int x; if (pos < 0 || pos+3 >= len) { return 0; } x = file[pos] & 0xff; x = (x << 8) + (file[pos+1] & 0xff); x = (x << 8) + (file[pos+2] & 0xff); x = (x << 8) + (file[pos+3] & 0xff); return x; } double TrueTypeFontFile::getFixed(int pos) { int x, y; x = getShort(pos); y = getUShort(pos+2); return (double)x + (double)y / 65536; } int TrueTypeFontFile::seekTable(char *tag) { int i; for (i = 0; i < nTables; ++i) { if (!strncmp(tableHdrs[i].tag, tag, 4)) { return tableHdrs[i].offset; } } return -1; } int TrueTypeFontFile::seekTableIdx(char *tag) { int i; for (i = 0; i < nTables; ++i) { if (!strncmp(tableHdrs[i].tag, tag, 4)) { return i; } } return -1; } void TrueTypeFontFile::cvtEncoding(char **encodingA, FILE *out) { char *name; int i; fprintf(out, "/Encoding 256 array\n"); for (i = 0; i < 256; ++i) { if (!(name = encodingA[i])) { name = ".notdef"; } fprintf(out, "dup %d /%s put\n", i, name); } fprintf(out, "readonly def\n"); } void TrueTypeFontFile::cvtCharStrings(char **encodingA, CharCodeToUnicode *toUnicode, GBool pdfFontHasEncoding, FILE *out) { int unicodeCmap, macRomanCmap, msSymbolCmap; int nCmaps, cmapPlatform, cmapEncoding, cmapFmt, cmapOffset; T42FontIndexMode mode; char *name; Unicode u; int pos, i, j, k; // always define '.notdef' fprintf(out, "/CharStrings 256 dict dup begin\n"); fprintf(out, "/.notdef 0 def\n"); // if there's no 'cmap' table, punt if ((pos = seekTable("cmap")) < 0) { goto err; } // To match up with the Adobe-defined behaviour, we choose a cmap // like this: // 1. If the PDF font has an encoding: // 1a. If the TrueType font has a Microsoft Unicode cmap, use it, // and use the Unicode indexes, not the char codes. // 1b. If the TrueType font has a Macintosh Roman cmap, use it, // and reverse map the char names through MacRomanEncoding to // get char codes. // 2. If the PDF font does not have an encoding: // 2a. If the TrueType font has a Macintosh Roman cmap, use it, // and use char codes directly. // 2b. If the TrueType font has a Microsoft Symbol cmap, use it, // and use (0xf000 + char code). // 3. If none of these rules apply, use the first cmap and hope for // the best (this shouldn't happen). nCmaps = getUShort(pos+2); unicodeCmap = macRomanCmap = msSymbolCmap = -1; cmapOffset = 0; for (i = 0; i < nCmaps; ++i) { cmapPlatform = getUShort(pos + 4 + 8*i); cmapEncoding = getUShort(pos + 4 + 8*i + 2); if (cmapPlatform == 3 && cmapEncoding == 1) { unicodeCmap = i; } else if (cmapPlatform == 1 && cmapEncoding == 0) { macRomanCmap = i; } else if (cmapPlatform == 3 && cmapEncoding == 0) { msSymbolCmap = i; } } i = 0; mode = t42FontModeCharCode; if (pdfFontHasEncoding) { if (unicodeCmap >= 0) { i = unicodeCmap; mode = t42FontModeUnicode; } else if (macRomanCmap >= 0) { i = macRomanCmap; mode = t42FontModeMacRoman; } } else { if (macRomanCmap >= 0) { i = macRomanCmap; mode = t42FontModeCharCode; } else if (msSymbolCmap >= 0) { i = msSymbolCmap; mode = t42FontModeCharCodeOffset; cmapOffset = 0xf000; } } cmapPlatform = getUShort(pos + 4 + 8*i); cmapEncoding = getUShort(pos + 4 + 8*i + 2); pos += getULong(pos + 4 + 8*i + 4); cmapFmt = getUShort(pos); if (cmapFmt != 0 && cmapFmt != 4 && cmapFmt != 6) { error(-1, "Unimplemented cmap format (%d) in TrueType font file", cmapFmt); goto err; } // map char name to glyph index: // 1. use encoding to map name to char code // 2. use cmap to map char code to glyph index j = 0; // make gcc happy for (i = 0; i < 256; ++i) { name = encodingA[i]; if (name && strcmp(name, ".notdef")) { switch (mode) { case t42FontModeUnicode: toUnicode->mapToUnicode((CharCode)i, &u, 1); j = (int)u; break; case t42FontModeCharCode: j = i; break; case t42FontModeCharCodeOffset: j = cmapOffset + i; break; case t42FontModeMacRoman: j = globalParams->getMacRomanCharCode(name); break; } // 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 = getCmapEntry(cmapFmt, pos, j)) > 0 && k < nGlyphs) { fprintf(out, "/%s %d def\n", name, k); } } } err: fprintf(out, "end readonly def\n"); } int TrueTypeFontFile::getCmapEntry(int cmapFmt, int pos, int code) { int cmapLen, cmapFirst; int segCnt, segEnd, segStart, segDelta, segOffset; int a, b, m, i; switch (cmapFmt) { case 0: // byte encoding table (Apple standard) cmapLen = getUShort(pos + 2); if (code >= cmapLen) { return 0; } return getByte(pos + 6 + code); case 4: // segment mapping to delta values (Microsoft standard) segCnt = getUShort(pos + 6) / 2; a = -1; b = segCnt - 1; segEnd = getUShort(pos + 14 + 2*b); if (code > 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) { m = (a + b) / 2; segEnd = getUShort(pos + 14 + 2*m); if (segEnd < code) { a = m; } else { b = m; } } segStart = getUShort(pos + 16 + 2*segCnt + 2*b); segDelta = getUShort(pos + 16 + 4*segCnt + 2*b); segOffset = getUShort(pos + 16 + 6*segCnt + 2*b); if (segOffset == 0) { i = (code + segDelta) & 0xffff; } else { i = getUShort(pos + 16 + 6*segCnt + 2*b + segOffset + 2 * (code - segStart)); if (i != 0) { i = (i + segDelta) & 0xffff; } } return i; case 6: // trimmed table mapping cmapFirst = getUShort(pos + 6); cmapLen = getUShort(pos + 8); if (code < cmapFirst || code >= cmapFirst + cmapLen) { return 0; } return getUShort(pos + 10 + 2*(code - cmapFirst)); default: // shouldn't happen - this is checked earlier break; } return 0; } void TrueTypeFontFile::cvtSfnts(FILE *out, GString *name) { TTFontTableHdr newTableHdrs[nT42Tables]; char tableDir[12 + nT42Tables*16]; char headTable[54]; int *origLocaTable; char *locaTable; int nNewTables; Guint checksum; int pos, glyfPos, length, glyphLength, pad; int i, j, k; // construct the 'head' table, zero out the font checksum memcpy(headTable, file + seekTable("head"), 54); headTable[8] = headTable[9] = headTable[10] = headTable[11] = (char)0; // read the original 'loca' table and construct the new one // (pad each glyph out to a multiple of 4 bytes) origLocaTable = (int *)gmalloc((nGlyphs + 1) * sizeof(int)); pos = seekTable("loca"); for (i = 0; i <= nGlyphs; ++i) { if (locaFmt) { origLocaTable[i] = getULong(pos + 4*i); } else { origLocaTable[i] = 2 * getUShort(pos + 2*i); } } locaTable = (char *)gmalloc((nGlyphs + 1) * (locaFmt ? 4 : 2)); if (locaFmt) { locaTable[0] = locaTable[1] = locaTable[2] = locaTable[3] = 0; } else { locaTable[0] = locaTable[1] = 0; } pos = 0; for (i = 1; i <= nGlyphs; ++i) { length = origLocaTable[i] - origLocaTable[i-1]; if (length & 3) { length += 4 - (length & 3); } pos += length; if (locaFmt) { locaTable[4*i ] = (char)(pos >> 24); locaTable[4*i+1] = (char)(pos >> 16); locaTable[4*i+2] = (char)(pos >> 8); locaTable[4*i+3] = (char) pos; } else { locaTable[2*i ] = (char)(pos >> 9); locaTable[2*i+1] = (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; } } // 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(headTable, 54); } else if (i == t42LocaTable) { length = (nGlyphs + 1) * (locaFmt ? 4 : 2); checksum = computeTableChecksum(locaTable, length); } else if (i == t42GlyfTable) { length = 0; checksum = 0; glyfPos = seekTable("glyf"); for (j = 0; j < nGlyphs; ++j) { glyphLength = origLocaTable[j+1] - origLocaTable[j]; pad = (glyphLength & 3) ? 4 - (glyphLength & 3) : 0; length += glyphLength + pad; checksum += computeTableChecksum(file + glyfPos + origLocaTable[j], glyphLength); } } else { if ((j = seekTableIdx(t42Tables[i].tag)) >= 0) { length = tableHdrs[j].length; checksum = computeTableChecksum(file + tableHdrs[j].offset, 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) { strncpy(newTableHdrs[k].tag, t42Tables[i].tag, 4); newTableHdrs[k].checksum = checksum; newTableHdrs[k].offset = pos; newTableHdrs[k].length = length; pad = (length & 3) ? 4 - (length & 3) : 0; pos += length + pad; ++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] = (char)128; tableDir[8] = 0; // entrySelector tableDir[9] = 3; tableDir[10] = 0; // rangeShift tableDir[11] = (char)(16 * nNewTables - 128); pos = 12; for (i = 0; i < nNewTables; ++i) { tableDir[pos ] = newTableHdrs[i].tag[0]; tableDir[pos+ 1] = newTableHdrs[i].tag[1]; tableDir[pos+ 2] = newTableHdrs[i].tag[2]; tableDir[pos+ 3] = newTableHdrs[i].tag[3]; tableDir[pos+ 4] = (char)(newTableHdrs[i].checksum >> 24); tableDir[pos+ 5] = (char)(newTableHdrs[i].checksum >> 16); tableDir[pos+ 6] = (char)(newTableHdrs[i].checksum >> 8); tableDir[pos+ 7] = (char) newTableHdrs[i].checksum; tableDir[pos+ 8] = (char)(newTableHdrs[i].offset >> 24); tableDir[pos+ 9] = (char)(newTableHdrs[i].offset >> 16); tableDir[pos+10] = (char)(newTableHdrs[i].offset >> 8); tableDir[pos+11] = (char) newTableHdrs[i].offset; tableDir[pos+12] = (char)(newTableHdrs[i].length >> 24); tableDir[pos+13] = (char)(newTableHdrs[i].length >> 16); tableDir[pos+14] = (char)(newTableHdrs[i].length >> 8); tableDir[pos+15] = (char) newTableHdrs[i].length; 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 += newTableHdrs[i].checksum; } checksum = 0xb1b0afba - checksum; // because the TrueType spec says so headTable[ 8] = (char)(checksum >> 24); headTable[ 9] = (char)(checksum >> 16); headTable[10] = (char)(checksum >> 8); headTable[11] = (char) checksum; // start the sfnts array if (name) { fprintf(out, "/%s [\n", name->getCString()); } else { fprintf(out, "/sfnts [\n"); } // write the table directory dumpString(tableDir, 12 + nNewTables*16, out); // write the tables for (i = 0; i < nNewTables; ++i) { if (i == t42HeadTable) { dumpString(headTable, 54, out); } else if (i == t42LocaTable) { length = (nGlyphs + 1) * (locaFmt ? 4 : 2); dumpString(locaTable, length, out); } else if (i == t42GlyfTable) { glyfPos = seekTable("glyf"); for (j = 0; j < nGlyphs; ++j) { length = origLocaTable[j+1] - origLocaTable[j]; if (length > 0) { dumpString(file + glyfPos + origLocaTable[j], length, out); } } } else { // length == 0 means the table is missing and the error was // already reported during the construction of the table // headers if ((length = newTableHdrs[i].length) > 0) { dumpString(file + seekTable(t42Tables[i].tag), length, out); } } } // end the sfnts array fprintf(out, "] def\n"); gfree(origLocaTable); gfree(locaTable); } void TrueTypeFontFile::dumpString(char *s, int length, FILE *out) { int pad, i, j; fprintf(out, "<"); for (i = 0; i < length; i += 32) { for (j = 0; j < 32 && i+j < length; ++j) { fprintf(out, "%02X", s[i+j] & 0xff); } if (i % (65536 - 32) == 65536 - 64) { fprintf(out, ">\n<"); } else if (i+32 < length) { fprintf(out, "\n"); } } if (length & 3) { pad = 4 - (length & 3); for (i = 0; i < pad; ++i) { fprintf(out, "00"); } } // add an extra zero byte because the Adobe Type 42 spec says so fprintf(out, "00>\n"); } Guint TrueTypeFontFile::computeTableChecksum(char *data, int length) { Guint 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; case 2: word |= (data[i+1] & 0xff) << 16; case 1: word |= (data[i ] & 0xff) << 24; break; } checksum += word; } return checksum; } void TrueTypeFontFile::writeTTF(FILE *out) { static char cmapTab[20] = { 0, 0, // table version number 0, 1, // number of encoding tables 0, 1, // platform ID 0, 0, // encoding ID 0, 0, 0, 12, // offset of subtable 0, 0, // subtable format 0, 1, // subtable length 0, 1, // subtable version 0, // map char 0 -> glyph 0 0 // pad to multiple of four bytes }; static char nameTab[8] = { 0, 0, // format 0, 0, // number of name records 0, 6, // offset to start of string storage 0, 0 // pad to multiple of four bytes }; static char postTab[32] = { 0, 1, 0, 0, // format 0, 0, 0, 0, // italic angle 0, 0, // underline position 0, 0, // underline thickness 0, 0, 0, 0, // fixed pitch 0, 0, 0, 0, // min Type 42 memory 0, 0, 0, 0, // max Type 42 memory 0, 0, 0, 0, // min Type 1 memory 0, 0, 0, 0 // max Type 1 memory }; GBool haveCmap, haveName, havePost; GBool dirCmap, dirName, dirPost; int nNewTables, nAllTables, pad; char *tableDir; Guint t, pos; int i, j; // check for missing tables haveCmap = seekTable("cmap") >= 0; haveName = seekTable("name") >= 0; havePost = seekTable("post") >= 0; nNewTables = (haveCmap ? 0 : 1) + (haveName ? 0 : 1) + (havePost ? 0 : 1); if (!nNewTables) { // none are missing - write the TTF file as is fwrite(file, 1, len, out); return; } // construct the new table directory nAllTables = nTables + nNewTables; tableDir = (char *)gmalloc(12 + nAllTables * 16); memcpy(tableDir, file, 12 + nTables * 16); tableDir[4] = (char)((nAllTables >> 8) & 0xff); tableDir[5] = (char)(nAllTables & 0xff); for (i = -1, t = (Guint)nAllTables; t; ++i, t >>= 1) ; t = 1 << (4 + i); tableDir[6] = (char)((t >> 8) & 0xff); tableDir[7] = (char)(t & 0xff); tableDir[8] = (char)((i >> 8) & 0xff); tableDir[9] = (char)(i & 0xff); t = nAllTables * 16 - t; tableDir[10] = (char)((t >> 8) & 0xff); tableDir[11] = (char)(t & 0xff); dirCmap = haveCmap; dirName = haveName; dirPost = havePost; j = 0; pad = (len & 3) ? 4 - (len & 3) : 0; pos = len + pad + 16 * nNewTables; for (i = 0; i < nTables; ++i) { if (!dirCmap && strncmp(tableHdrs[i].tag, "cmap", 4) > 0) { tableDir[12 + 16*j ] = 'c'; tableDir[12 + 16*j + 1] = 'm'; tableDir[12 + 16*j + 2] = 'a'; tableDir[12 + 16*j + 3] = 'p'; tableDir[12 + 16*j + 4] = (char)0; //~ should compute the checksum tableDir[12 + 16*j + 5] = (char)0; tableDir[12 + 16*j + 6] = (char)0; tableDir[12 + 16*j + 7] = (char)0; tableDir[12 + 16*j + 8] = (char)((pos >> 24) & 0xff); tableDir[12 + 16*j + 9] = (char)((pos >> 16) & 0xff); tableDir[12 + 16*j + 10] = (char)((pos >> 8) & 0xff); tableDir[12 + 16*j + 11] = (char)( pos & 0xff); tableDir[12 + 16*j + 12] = (char)((sizeof(cmapTab) >> 24) & 0xff); tableDir[12 + 16*j + 13] = (char)((sizeof(cmapTab) >> 16) & 0xff); tableDir[12 + 16*j + 14] = (char)((sizeof(cmapTab) >> 8) & 0xff); tableDir[12 + 16*j + 15] = (char)( sizeof(cmapTab) & 0xff); pos += sizeof(cmapTab); ++j; dirCmap = gTrue; } if (!dirName && strncmp(tableHdrs[i].tag, "name", 4) > 0) { tableDir[12 + 16*j ] = 'n'; tableDir[12 + 16*j + 1] = 'a'; tableDir[12 + 16*j + 2] = 'm'; tableDir[12 + 16*j + 3] = 'e'; tableDir[12 + 16*j + 4] = (char)0; //~ should compute the checksum tableDir[12 + 16*j + 5] = (char)0; tableDir[12 + 16*j + 6] = (char)0; tableDir[12 + 16*j + 7] = (char)0; tableDir[12 + 16*j + 8] = (char)((pos >> 24) & 0xff); tableDir[12 + 16*j + 9] = (char)((pos >> 16) & 0xff); tableDir[12 + 16*j + 10] = (char)((pos >> 8) & 0xff); tableDir[12 + 16*j + 11] = (char)( pos & 0xff); tableDir[12 + 16*j + 12] = (char)((sizeof(nameTab) >> 24) & 0xff); tableDir[12 + 16*j + 13] = (char)((sizeof(nameTab) >> 16) & 0xff); tableDir[12 + 16*j + 14] = (char)((sizeof(nameTab) >> 8) & 0xff); tableDir[12 + 16*j + 15] = (char)( sizeof(nameTab) & 0xff); pos += sizeof(nameTab); ++j; dirName = gTrue; } if (!dirName && strncmp(tableHdrs[i].tag, "post", 4) > 0) { tableDir[12 + 16*j ] = 'p'; tableDir[12 + 16*j + 1] = 'o'; tableDir[12 + 16*j + 2] = 's'; tableDir[12 + 16*j + 3] = 't'; tableDir[12 + 16*j + 4] = (char)0; //~ should compute the checksum tableDir[12 + 16*j + 5] = (char)0; tableDir[12 + 16*j + 6] = (char)0; tableDir[12 + 16*j + 7] = (char)0; tableDir[12 + 16*j + 8] = (char)((pos >> 24) & 0xff); tableDir[12 + 16*j + 9] = (char)((pos >> 16) & 0xff); tableDir[12 + 16*j + 10] = (char)((pos >> 8) & 0xff); tableDir[12 + 16*j + 11] = (char)( pos & 0xff); tableDir[12 + 16*j + 12] = (char)((sizeof(postTab) >> 24) & 0xff); tableDir[12 + 16*j + 13] = (char)((sizeof(postTab) >> 16) & 0xff); tableDir[12 + 16*j + 14] = (char)((sizeof(postTab) >> 8) & 0xff); tableDir[12 + 16*j + 15] = (char)( sizeof(postTab) & 0xff); pos += sizeof(postTab); ++j; dirPost = gTrue; } memcpy(&tableDir[12 + 16*j], file + 12 + 16*i, 16); t = tableHdrs[i].offset + nNewTables * 16; tableDir[12 + 16*j + 8] = (char)((t >> 24) & 0xff); tableDir[12 + 16*j + 9] = (char)((t >> 16) & 0xff); tableDir[12 + 16*j + 10] = (char)((t >> 8) & 0xff); tableDir[12 + 16*j + 11] = (char)( t & 0xff); ++j; } if (!dirCmap) { tableDir[12 + 16*j ] = 'c'; tableDir[12 + 16*j + 1] = 'm'; tableDir[12 + 16*j + 2] = 'a'; tableDir[12 + 16*j + 3] = 'p'; tableDir[12 + 16*j + 4] = (char)0; //~ should compute the checksum tableDir[12 + 16*j + 5] = (char)0; tableDir[12 + 16*j + 6] = (char)0; tableDir[12 + 16*j + 7] = (char)0; tableDir[12 + 16*j + 8] = (char)((pos >> 24) & 0xff); tableDir[12 + 16*j + 9] = (char)((pos >> 16) & 0xff); tableDir[12 + 16*j + 10] = (char)((pos >> 8) & 0xff); tableDir[12 + 16*j + 11] = (char)( pos & 0xff); tableDir[12 + 16*j + 12] = (char)((sizeof(cmapTab) >> 24) & 0xff); tableDir[12 + 16*j + 13] = (char)((sizeof(cmapTab) >> 16) & 0xff); tableDir[12 + 16*j + 14] = (char)((sizeof(cmapTab) >> 8) & 0xff); tableDir[12 + 16*j + 15] = (char)( sizeof(cmapTab) & 0xff); pos += sizeof(cmapTab); ++j; dirCmap = gTrue; } if (!dirName) { tableDir[12 + 16*j ] = 'n'; tableDir[12 + 16*j + 1] = 'a'; tableDir[12 + 16*j + 2] = 'm'; tableDir[12 + 16*j + 3] = 'e'; tableDir[12 + 16*j + 4] = (char)0; //~ should compute the checksum tableDir[12 + 16*j + 5] = (char)0; tableDir[12 + 16*j + 6] = (char)0; tableDir[12 + 16*j + 7] = (char)0; tableDir[12 + 16*j + 8] = (char)((pos >> 24) & 0xff); tableDir[12 + 16*j + 9] = (char)((pos >> 16) & 0xff); tableDir[12 + 16*j + 10] = (char)((pos >> 8) & 0xff); tableDir[12 + 16*j + 11] = (char)( pos & 0xff); tableDir[12 + 16*j + 12] = (char)((sizeof(nameTab) >> 24) & 0xff); tableDir[12 + 16*j + 13] = (char)((sizeof(nameTab) >> 16) & 0xff); tableDir[12 + 16*j + 14] = (char)((sizeof(nameTab) >> 8) & 0xff); tableDir[12 + 16*j + 15] = (char)( sizeof(nameTab) & 0xff); pos += sizeof(nameTab); ++j; dirName = gTrue; } if (!dirPost) { tableDir[12 + 16*j ] = 'p'; tableDir[12 + 16*j + 1] = 'o'; tableDir[12 + 16*j + 2] = 's'; tableDir[12 + 16*j + 3] = 't'; tableDir[12 + 16*j + 4] = (char)0; //~ should compute the checksum tableDir[12 + 16*j + 5] = (char)0; tableDir[12 + 16*j + 6] = (char)0; tableDir[12 + 16*j + 7] = (char)0; tableDir[12 + 16*j + 8] = (char)((pos >> 24) & 0xff); tableDir[12 + 16*j + 9] = (char)((pos >> 16) & 0xff); tableDir[12 + 16*j + 10] = (char)((pos >> 8) & 0xff); tableDir[12 + 16*j + 11] = (char)( pos & 0xff); tableDir[12 + 16*j + 12] = (char)((sizeof(postTab) >> 24) & 0xff); tableDir[12 + 16*j + 13] = (char)((sizeof(postTab) >> 16) & 0xff); tableDir[12 + 16*j + 14] = (char)((sizeof(postTab) >> 8) & 0xff); tableDir[12 + 16*j + 15] = (char)( sizeof(postTab) & 0xff); pos += sizeof(postTab); ++j; dirPost = gTrue; } // write the table directory fwrite(tableDir, 1, 12 + 16 * nAllTables, out); // write the original tables fwrite(file + 12 + 16*nTables, 1, len - (12 + 16*nTables), out); // write the new tables for (i = 0; i < pad; ++i) { fputc((char)0, out); } if (!haveCmap) { fwrite(cmapTab, 1, sizeof(cmapTab), out); } if (!haveName) { fwrite(nameTab, 1, sizeof(nameTab), out); } if (!havePost) { fwrite(postTab, 1, sizeof(postTab), out); } gfree(tableDir); }