//======================================================================== // // FontFile.cc // // Copyright 1999 Derek B. Noonburg // //======================================================================== #ifdef __GNUC__ #pragma implementation #endif #include #include #include #include #include #include "gmem.h" #include "Error.h" #include "FontFile.h" #include "StdFontInfo.h" #include "CompactFontInfo.h" //------------------------------------------------------------------------ static Guint getWord(Guchar *ptr, int size); static double getNum(Guchar **ptr, GBool *fp); static char *getString(int sid, Guchar *stringIdxPtr, Guchar *stringStartPtr, int stringOffSize, char *buf); //------------------------------------------------------------------------ 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; char buf[256]; char c; int n, code, i; name = NULL; encoding = NULL; freeEnc = gTrue; for (i = 1, line = file; i <= 100 && line < file + len && !encoding; ++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)) { encoding = type1StdEncoding.copy(); } else if (!strncmp(line, "/Encoding 256 array", 19)) { encoding = new FontEncoding(); for (i = 0; i < 300; ++i) { 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->addChar(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 } else { line = nextLine(line, file + len); } } } Type1FontFile::~Type1FontFile() { if (name) gfree(name); if (encoding && freeEnc) delete encoding; } FontEncoding *Type1FontFile::getEncoding(GBool taken) { if (taken) freeEnc = gFalse; return encoding; } //------------------------------------------------------------------------ // Type1CFontFile //------------------------------------------------------------------------ Type1CFontFile::Type1CFontFile(char *file, int len) { char buf[256]; Guchar *topPtr, *idxStartPtr, *idxPtr0, *idxPtr1; Guchar *stringIdxPtr, *stringStartPtr; int topOffSize, idxOffSize, stringOffSize; int nFonts, nStrings, nGlyphs; int nCodes, nRanges, nLeft, nSups; Gushort *glyphNames; int charset, enc, charstrings; int charsetFormat, encFormat; int c, sid; double op[48]; double x; GBool isFP; int key; int i, j, n; name = NULL; encoding = NULL; freeEnc = gTrue; // read header topPtr = (Guchar *)file + (file[2] & 0xff); topOffSize = file[3] & 0xff; // read name index (first font only) nFonts = getWord(topPtr, 2); idxOffSize = topPtr[2]; topPtr += 3; idxStartPtr = topPtr + (nFonts + 1) * idxOffSize - 1; idxPtr0 = idxStartPtr + getWord(topPtr, idxOffSize); idxPtr1 = idxStartPtr + getWord(topPtr + idxOffSize, idxOffSize); if ((n = idxPtr1 - idxPtr0) > 255) n = 255; strncpy(buf, (char *)idxPtr0, n); buf[n] = '\0'; name = copyString(buf); topPtr = idxStartPtr + getWord(topPtr + nFonts * idxOffSize, idxOffSize); // read top dict index (first font only) nFonts = getWord(topPtr, 2); idxOffSize = topPtr[2]; topPtr += 3; idxStartPtr = topPtr + (nFonts + 1) * idxOffSize - 1; idxPtr0 = idxStartPtr + getWord(topPtr, idxOffSize); idxPtr1 = idxStartPtr + getWord(topPtr + idxOffSize, idxOffSize); charset = 0; enc = 0; charstrings = 0; i = 0; while (idxPtr0 < idxPtr1) { if (*idxPtr0 <= 27 || *idxPtr0 == 31) { key = *idxPtr0++; if (key == 0x0c) key = (key << 8) | *idxPtr0++; 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(&idxPtr0, &isFP); if (i < 48) op[i++] = x; } } topPtr = idxStartPtr + getWord(topPtr + nFonts * idxOffSize, idxOffSize); // read string index nStrings = getWord(topPtr, 2); stringOffSize = topPtr[2]; topPtr += 3; stringIdxPtr = topPtr; stringStartPtr = topPtr + (nStrings + 1) * stringOffSize - 1; topPtr = stringStartPtr + getWord(topPtr + nStrings * stringOffSize, stringOffSize); // get number of glyphs from charstrings index topPtr = (Guchar *)file + charstrings; nGlyphs = getWord(topPtr, 2); // read charset 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; topPtr = (Guchar *)file + charset; charsetFormat = *topPtr++; if (charsetFormat == 0) { for (i = 1; i < nGlyphs; ++i) { glyphNames[i] = getWord(topPtr, 2); topPtr += 2; } } else if (charsetFormat == 1) { i = 1; while (i < nGlyphs) { c = getWord(topPtr, 2); topPtr += 2; nLeft = *topPtr++; for (j = 0; j <= nLeft; ++j) glyphNames[i++] = c++; } } else if (charsetFormat == 2) { i = 1; while (i < nGlyphs) { c = getWord(topPtr, 2); topPtr += 2; nLeft = getWord(topPtr, 2); topPtr += 2; for (j = 0; j <= nLeft; ++j) glyphNames[i++] = c++; } } } // read encoding (glyph -> code mapping) if (enc == 0) { encoding = type1StdEncoding.copy(); } else if (enc == 1) { encoding = type1ExpertEncoding.copy(); } else { encoding = new FontEncoding(); topPtr = (Guchar *)file + enc; encFormat = *topPtr++; if ((encFormat & 0x7f) == 0) { nCodes = 1 + *topPtr++; if (nCodes > nGlyphs) { nCodes = nGlyphs; } for (i = 1; i < nCodes; ++i) { c = *topPtr++; getString(glyphNames[i], stringIdxPtr, stringStartPtr, stringOffSize, buf); encoding->addChar(c, copyString(buf)); } } else if ((encFormat & 0x7f) == 1) { nRanges = *topPtr++; nCodes = 1; for (i = 0; i < nRanges; ++i) { c = *topPtr++; nLeft = *topPtr++; for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) { getString(glyphNames[nCodes], stringIdxPtr, stringStartPtr, stringOffSize, buf); encoding->addChar(c, copyString(buf)); ++nCodes; ++c; } } } if (encFormat & 0x80) { nSups = *topPtr++; for (i = 0; i < nSups; ++i) { c = *topPtr++; sid = getWord(topPtr, 2); topPtr += 2; getString(sid, stringIdxPtr, stringStartPtr, stringOffSize, buf); encoding->addChar(c, copyString(buf)); } } } if (charset > 2) gfree(glyphNames); } Type1CFontFile::~Type1CFontFile() { if (name) gfree(name); if (encoding && freeEnc) delete encoding; } FontEncoding *Type1CFontFile::getEncoding(GBool taken) { if (taken) freeEnc = gFalse; return encoding; } static Guint getWord(Guchar *ptr, int size) { Guint x; int i; x = 0; for (i = 0; i < size; ++i) x = (x << 8) + *ptr++; return x; } static double getNum(Guchar **ptr, GBool *fp) { static char nybChars[16] = "0123456789.ee -"; int b0, b, nyb0, nyb1; double x; char buf[65]; int i; x = 0; *fp = 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); *fp = 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; } static char *getString(int sid, Guchar *stringIdxPtr, Guchar *stringStartPtr, int stringOffSize, char *buf) { Guchar *idxPtr0, *idxPtr1; int len; if (sid < 391) { strcpy(buf, type1CStdStrings[sid]); } else { sid -= 391; idxPtr0 = stringStartPtr + getWord(stringIdxPtr + sid * stringOffSize, stringOffSize); idxPtr1 = stringStartPtr + getWord(stringIdxPtr + (sid+1) * stringOffSize, stringOffSize); if ((len = idxPtr1 - idxPtr0) > 255) len = 255; strncpy(buf, (char *)idxPtr0, len); buf[len] = '\0'; } return buf; } //------------------------------------------------------------------------ // Type1CFontConverter //------------------------------------------------------------------------ Type1CFontConverter::Type1CFontConverter(char *fileA, int lenA, FILE *outA) { file = fileA; len = lenA; out = outA; r1 = 55665; line = 0; } Type1CFontConverter::~Type1CFontConverter() { } void Type1CFontConverter::convert() { char *fontName; struct { 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; } dict; char buf[256], eBuf[256]; Guchar *topPtr, *idxStartPtr, *idxPtr0, *idxPtr1; Guchar *stringIdxPtr, *stringStartPtr; int topOffSize, idxOffSize, stringOffSize; int nFonts, nStrings, nGlyphs; int nCodes, nRanges, nLeft, nSups; Gushort *glyphNames; int charsetFormat, encFormat; int subrsOffset, nSubrs; int nCharStrings; int c, sid; double x; GBool isFP; int key; int i, j, n; // read header topPtr = (Guchar *)file + (file[2] & 0xff); topOffSize = file[3] & 0xff; // read name (first font only) nFonts = getWord(topPtr, 2); idxOffSize = topPtr[2]; topPtr += 3; idxStartPtr = topPtr + (nFonts + 1) * idxOffSize - 1; idxPtr0 = idxStartPtr + getWord(topPtr, idxOffSize); idxPtr1 = idxStartPtr + getWord(topPtr + idxOffSize, idxOffSize); if ((n = idxPtr1 - idxPtr0) > 255) n = 255; strncpy(buf, (char *)idxPtr0, n); buf[n] = '\0'; fontName = copyString(buf); topPtr = idxStartPtr + getWord(topPtr + nFonts * idxOffSize, idxOffSize); // read top dict (first font only) nFonts = getWord(topPtr, 2); idxOffSize = topPtr[2]; topPtr += 3; idxStartPtr = topPtr + (nFonts + 1) * idxOffSize - 1; idxPtr0 = idxStartPtr + getWord(topPtr, idxOffSize); idxPtr1 = idxStartPtr + getWord(topPtr + idxOffSize, idxOffSize); 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; i = 0; while (idxPtr0 < idxPtr1) { if (*idxPtr0 <= 27 || *idxPtr0 == 31) { key = *idxPtr0++; if (key == 0x0c) key = (key << 8) | *idxPtr0++; 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; } i = 0; } else { x = getNum(&idxPtr0, &isFP); if (i < 48) { op[i] = x; fp[i++] = isFP; } } } topPtr = idxStartPtr + getWord(topPtr + nFonts * idxOffSize, idxOffSize); // read string index nStrings = getWord(topPtr, 2); stringOffSize = topPtr[2]; topPtr += 3; stringIdxPtr = topPtr; stringStartPtr = topPtr + (nStrings + 1) * stringOffSize - 1; topPtr = stringStartPtr + getWord(topPtr + nStrings * stringOffSize, stringOffSize); #if 1 //~ // get global subrs int nGSubrs; int gSubrOffSize; nGSubrs = getWord(topPtr, 2); gSubrOffSize = topPtr[2]; topPtr += 3; #endif // write header and font dictionary, up to encoding fprintf(out, "%%!FontType1-1.0: %s", fontName); if (dict.version != 0) { fprintf(out, "%s", getString(dict.version, stringIdxPtr, stringStartPtr, stringOffSize, 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, stringIdxPtr, stringStartPtr, stringOffSize, buf)); } if (dict.notice != 0) { fprintf(out, "/Notice (%s) readonly def\n", getString(dict.notice, stringIdxPtr, stringStartPtr, stringOffSize, buf)); } if (dict.copyright != 0) { fprintf(out, "/Copyright (%s) readonly def\n", getString(dict.copyright, stringIdxPtr, stringStartPtr, stringOffSize, buf)); } if (dict.fullName != 0) { fprintf(out, "/FullName (%s) readonly def\n", getString(dict.fullName, stringIdxPtr, stringStartPtr, stringOffSize, buf)); } if (dict.familyName != 0) { fprintf(out, "/FamilyName (%s) readonly def\n", getString(dict.familyName, stringIdxPtr, stringStartPtr, stringOffSize, buf)); } if (dict.weight != 0) { fprintf(out, "/Weight (%s) readonly def\n", getString(dict.weight, stringIdxPtr, stringStartPtr, stringOffSize, 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", fontName); 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]); if (dict.uniqueID != 0) { fprintf(out, "/UniqueID %d def\n", dict.uniqueID); } // get number of glyphs from charstrings index topPtr = (Guchar *)file + dict.charStrings; nGlyphs = getWord(topPtr, 2); // read charset if (dict.charset == 0) { glyphNames = type1CISOAdobeCharset; } else if (dict.charset == 1) { glyphNames = type1CExpertCharset; } else if (dict.charset == 2) { glyphNames = type1CExpertSubsetCharset; } else { glyphNames = (Gushort *)gmalloc(nGlyphs * sizeof(Gushort)); glyphNames[0] = 0; topPtr = (Guchar *)file + dict.charset; charsetFormat = *topPtr++; if (charsetFormat == 0) { for (i = 1; i < nGlyphs; ++i) { glyphNames[i] = getWord(topPtr, 2); topPtr += 2; } } else if (charsetFormat == 1) { i = 1; while (i < nGlyphs) { c = getWord(topPtr, 2); topPtr += 2; nLeft = *topPtr++; for (j = 0; j <= nLeft; ++j) glyphNames[i++] = c++; } } else if (charsetFormat == 2) { i = 1; while (i < nGlyphs) { c = getWord(topPtr, 2); topPtr += 2; nLeft = getWord(topPtr, 2); topPtr += 2; for (j = 0; j <= nLeft; ++j) glyphNames[i++] = c++; } } } // 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 (type1ExpertEncodingNames[i]) fprintf(out, "dup %d /%s put\n", i, type1ExpertEncodingNames[i]); } } else { topPtr = (Guchar *)file + dict.encoding; encFormat = *topPtr++; if ((encFormat & 0x7f) == 0) { nCodes = 1 + *topPtr++; if (nCodes > nGlyphs) { nCodes = nGlyphs; } for (i = 1; i < nCodes; ++i) { c = *topPtr++; fprintf(out, "dup %d /%s put\n", c, getString(glyphNames[i], stringIdxPtr, stringStartPtr, stringOffSize, buf)); } } else if ((encFormat & 0x7f) == 1) { nRanges = *topPtr++; nCodes = 1; for (i = 0; i < nRanges; ++i) { c = *topPtr++; nLeft = *topPtr++; for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) { fprintf(out, "dup %d /%s put\n", c, getString(glyphNames[nCodes], stringIdxPtr, stringStartPtr, stringOffSize, buf)); ++nCodes; ++c; } } } if (encFormat & 0x80) { nSups = *topPtr++; for (i = 0; i < nSups; ++i) { c = *topPtr++; sid = getWord(topPtr, 2); topPtr += 2; fprintf(out, "dup %d /%s put\n", c, getString(sid, stringIdxPtr, stringStartPtr, stringOffSize, buf)); } } } fprintf(out, "readonly def\n"); } fprintf(out, "currentdict end\n"); fprintf(out, "currentfile eexec\n"); // 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"); eexecWrite("/password 5839 def\n"); subrsOffset = 0; defaultWidthX = 0; nominalWidthX = 0; topPtr = (Guchar *)file + dict.privateOffset; idxPtr0 = topPtr; idxPtr1 = idxPtr0 + dict.privateSize; i = 0; while (idxPtr0 < idxPtr1) { if (*idxPtr0 <= 27 || *idxPtr0 == 31) { key = *idxPtr0++; if (key == 0x0c) key = (key << 8) | *idxPtr0++; switch (key) { case 0x0006: getDeltaInt(eBuf, "BlueValues", op, i); eexecWrite(eBuf); break; case 0x0007: getDeltaInt(eBuf, "OtherBlues", op, i); eexecWrite(eBuf); break; case 0x0008: getDeltaInt(eBuf, "FamilyBlues", op, i); eexecWrite(eBuf); break; case 0x0009: getDeltaInt(eBuf, "FamilyOtherBlues", op, i); eexecWrite(eBuf); break; case 0x0c09: sprintf(eBuf, "/BlueScale %g def\n", op[0]); eexecWrite(eBuf); break; case 0x0c0a: sprintf(eBuf, "/BlueShift %d def\n", (int)op[0]); eexecWrite(eBuf); break; case 0x0c0b: sprintf(eBuf, "/BlueFuzz %d def\n", (int)op[0]); eexecWrite(eBuf); break; case 0x000a: sprintf(eBuf, "/StdHW [%g] def\n", op[0]); eexecWrite(eBuf); break; case 0x000b: sprintf(eBuf, "/StdVW [%g] def\n", op[0]); eexecWrite(eBuf); break; case 0x0c0c: getDeltaReal(eBuf, "StemSnapH", op, i); eexecWrite(eBuf); break; case 0x0c0d: getDeltaReal(eBuf, "StemSnapV", op, i); eexecWrite(eBuf); break; case 0x0c0e: sprintf(eBuf, "/ForceBold %s def\n", op[0] ? "true" : "false"); eexecWrite(eBuf); break; case 0x0c0f: sprintf(eBuf, "/ForceBoldThreshold %g def\n", op[0]); eexecWrite(eBuf); break; case 0x0c11: sprintf(eBuf, "/LanguageGroup %d def\n", (int)op[0]); eexecWrite(eBuf); break; case 0x0c12: sprintf(eBuf, "/ExpansionFactor %g def\n", op[0]); eexecWrite(eBuf); break; case 0x0c13: error(-1, "Got Type 1C InitialRandomSeed"); break; case 0x0013: subrsOffset = (int)op[0]; break; case 0x0014: defaultWidthX = op[0]; defaultWidthXFP = fp[0]; break; case 0x0015: nominalWidthX = op[0]; nominalWidthXFP = fp[0]; break; default: error(-1, "Unknown Type 1C private dict entry %04x", key); break; } i = 0; } else { x = getNum(&idxPtr0, &isFP); if (i < 48) { op[i] = x; fp[i++] = isFP; } } } // get subrs if (subrsOffset != 0) { topPtr += subrsOffset; nSubrs = getWord(topPtr, 2); idxOffSize = topPtr[2]; topPtr += 3; sprintf(eBuf, "/Subrs %d array\n", nSubrs); eexecWrite(eBuf); idxStartPtr = topPtr + (nSubrs + 1) * idxOffSize - 1; idxPtr1 = idxStartPtr + getWord(topPtr, idxOffSize); for (i = 0; i < nSubrs; ++i) { idxPtr0 = idxPtr1; idxPtr1 = idxStartPtr + getWord(topPtr + (i+1)*idxOffSize, idxOffSize); n = idxPtr1 - idxPtr0; #if 1 //~ error(-1, "Unimplemented Type 2 subrs"); #else sprintf(eBuf, "dup %d %d RD ", i, n); eexecWrite(eBuf); cvtGlyph(idxPtr0, n); eexecWrite(" NP\n"); #endif } eexecWrite("ND\n"); } // get CharStrings topPtr = (Guchar *)file + dict.charStrings; nCharStrings = getWord(topPtr, 2); idxOffSize = topPtr[2]; topPtr += 3; sprintf(eBuf, "2 index /CharStrings %d dict dup begin\n", nCharStrings); eexecWrite(eBuf); idxStartPtr = topPtr + (nCharStrings + 1) * idxOffSize - 1; idxPtr1 = idxStartPtr + getWord(topPtr, idxOffSize); for (i = 0; i < nCharStrings; ++i) { idxPtr0 = idxPtr1; idxPtr1 = idxStartPtr + getWord(topPtr + (i+1)*idxOffSize, idxOffSize); n = idxPtr1 - idxPtr0; cvtGlyph(getString(glyphNames[i], stringIdxPtr, stringStartPtr, stringOffSize, 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 if (dict.charset > 2) gfree(glyphNames); gfree(fontName); } void Type1CFontConverter::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 Type1CFontConverter::cvtGlyph(char *name, Guchar *s, int n) { int nHints; int x; GBool first = gTrue; char eBuf[256]; double d, dx, dy; GBool dFP; 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; } } sprintf(eBuf, "/%s %d RD ", name, charBuf->getLength()); eexecWrite(eBuf); eexecWriteCharstring((Guchar *)charBuf->getCString(), charBuf->getLength()); eexecWrite(" ND\n"); delete charBuf; } void Type1CFontConverter::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 Type1CFontConverter::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 Type1CFontConverter::eexecDumpOp1(int opA) { charBuf->append((char)opA); } void Type1CFontConverter::eexecDumpOp2(int opA) { charBuf->append((char)12); charBuf->append((char)opA); } void Type1CFontConverter::eexecWriteCharstring(Guchar *s, int n) { Gushort r2; Guchar x; int i; r2 = 4330; for (i = 0; i < n; ++i) { // charstring encryption x = s[i]; x ^= (r2 >> 8); r2 = (x + r2) * 52845 + 22719; // eexec encryption x ^= (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 Type1CFontConverter::getDeltaInt(char *buf, char *name, double *opA, int n) { int x, i; sprintf(buf, "/%s [", name); 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 Type1CFontConverter::getDeltaReal(char *buf, char *name, double *opA, int n) { double x; int i; sprintf(buf, "/%s [", name); 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"); } //------------------------------------------------------------------------ // 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; }; // TrueType tables required by the Type 42 spec. static char *t42ReqTables[9] = { "head", "hhea", "loca", "maxp", "cvt ", "prep", "glyf", "hmtx", "fpgm" }; // 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" }; TrueTypeFontFile::TrueTypeFontFile(char *fileA, int lenA) { int pos, i; file = fileA; len = lenA; encoding = NULL; freeEnc = gTrue; // 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() { if (encoding && freeEnc) { delete encoding; } gfree(tableHdrs); } char *TrueTypeFontFile::getName() { return NULL; } FontEncoding *TrueTypeFontFile::getEncoding(GBool taken) { int cmap[256]; int nCmaps, cmapPlatform, cmapEncoding, cmapFmt, cmapLen, cmapOffset; int segCnt, segStart, segEnd, segDelta, segOffset; int pos, i, j, k; Guint fmt; GString *s; int stringIdx, stringPos, n; //----- 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; default: error(-1, "Unimplemented cmap type (%d) in TrueType font file", cmapFmt); break; } } //----- construct the (glyph idx) -> (glyph name) mapping //----- and compute the (char code) -> (glyph name) mapping encoding = new FontEncoding(); 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->addChar(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->addChar(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->addChar(i, copyString(s->getCString())); delete s; ++stringIdx; stringPos += 1 + n; } } else { encoding->addChar(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->addChar(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->addChar(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->addChar(i, copyString(macGlyphNames[j])); } } if (taken) { freeEnc = gFalse; } return encoding; } void TrueTypeFontFile::convertToType42(char *name, FontEncoding *encodingA, 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, out); cvtSfnts(out); // end the dictionary and define the font fprintf(out, "FontName currentdict end definefont pop\n"); } int TrueTypeFontFile::getByte(int pos) { return file[pos] & 0xff; } int TrueTypeFontFile::getChar(int pos) { int x; x = file[pos] & 0xff; if (x & 0x80) x |= 0xffffff00; return x; } int TrueTypeFontFile::getUShort(int pos) { int x; x = file[pos] & 0xff; x = (x << 8) + (file[pos+1] & 0xff); return x; } int TrueTypeFontFile::getShort(int pos) { int x; 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; 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; } void TrueTypeFontFile::cvtEncoding(FontEncoding *encodingA, FILE *out) { char *name; int i; fprintf(out, "/Encoding 256 array\n"); for (i = 0; i < 256; ++i) { if (!(name = encodingA->getCharName(i))) { name = ".notdef"; } fprintf(out, "dup %d /%s put\n", i, name); } fprintf(out, "readonly def\n"); } void TrueTypeFontFile::cvtCharStrings(FontEncoding *encodingA, FILE *out) { int cmap[256]; int nCmaps, cmapPlatform, cmapEncoding, cmapFmt, cmapLen, cmapOffset; int segCnt, segStart, segEnd, segDelta, segOffset; char *name; int pos, i, j, k; //----- read the cmap: 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; default: error(-1, "Unimplemented cmap type (%d) in TrueType font file", cmapFmt); break; } } //----- map char code to glyph index // 1. use encoding to map name to char code // 2. use cmap to map char code to glyph index fprintf(out, "/CharStrings 256 dict dup begin\n"); fprintf(out, "/.notdef 0 def\n"); // kludge: this loop goes backward because the WinAnsi and MacRoman // encodings define certain chars multiple times (space, hyphen, // etc.), and we want the lowest-numbered definition to "stick" // (because the higher-numbered defn(s) may not have valid cmap // entries) i = encodingA->getSize(); if (i > 255) { i = 255; } for (; i >= 0; --i) { name = encodingA->getCharName(i); if (name && strcmp(name, ".notdef")) { fprintf(out, "/%s %d def\n", name, cmap[i]); } } fprintf(out, "end readonly def\n"); } void TrueTypeFontFile::cvtSfnts(FILE *out) { char tableDir[12 + 9*16]; int *list; int nTablesOut, pos, destPos, i, j, k1, k2; fprintf(out, "/sfnts [\n"); // count tables nTablesOut = 0; for (i = 0; i < 9; ++i) { for (j = 0; j < nTables; ++j) { if (!strncmp(t42ReqTables[i], tableHdrs[j].tag, 4)) { ++nTablesOut; break; } } } // header tableDir[0] = 0x00; // sfnt version tableDir[1] = 0x01; tableDir[2] = 0x00; tableDir[3] = 0x00; tableDir[4] = (nTablesOut >> 8) & 0xff; // numTables tableDir[5] = nTablesOut & 0xff; tableDir[6] = 0; // searchRange tableDir[7] = 128; tableDir[8] = 0; // entrySelector tableDir[9] = 3; tableDir[10] = 0; // rangeShift tableDir[11] = 16; // table directory pos = 12; destPos = 12 + 16 * nTablesOut; for (i = 0; i < 9; ++i) { for (j = 0; j < nTables; ++j) { if (!strncmp(t42ReqTables[i], tableHdrs[j].tag, 4)) { break; } } if (j < nTables) { memcpy(&tableDir[pos], t42ReqTables[i], 4); tableDir[pos+4] = (tableHdrs[j].checksum >> 24) & 0xff; tableDir[pos+5] = (tableHdrs[j].checksum >> 16) & 0xff; tableDir[pos+6] = (tableHdrs[j].checksum >> 8) & 0xff; tableDir[pos+7] = tableHdrs[j].checksum & 0xff; tableDir[pos+8] = (destPos >> 24) & 0xff; tableDir[pos+9] = (destPos >> 16) & 0xff; tableDir[pos+10] = (destPos >> 8) & 0xff; tableDir[pos+11] = destPos & 0xff; tableDir[pos+12] = (tableHdrs[j].length >> 24) & 0xff; tableDir[pos+13] = (tableHdrs[j].length >> 16) & 0xff; tableDir[pos+14] = (tableHdrs[j].length >> 8) & 0xff; tableDir[pos+15] = tableHdrs[j].length & 0xff; pos += 16; destPos += tableHdrs[j].length; if (tableHdrs[j].length & 3) { destPos += 4 - (tableHdrs[j].length & 3); } } } dumpString(tableDir, 12 + 16 * nTablesOut, out); for (i = 0; i < 9; ++i) { for (j = 0; j < nTables; ++j) { if (!strncmp(t42ReqTables[i], tableHdrs[j].tag, 4)) { break; } } if (j < nTables) { if (!strcmp(t42ReqTables[i], "glyf") && tableHdrs[j].length > 65532) { // the 'glyf' table won't fit in a single string, and we're only // allowed to break at glyph boundaries list = (int *)gmalloc((nGlyphs + 1) * sizeof(int)); pos = seekTable("loca"); for (k1 = 0; k1 <= nGlyphs; ++k1) { if (locaFmt) { list[k1] = getULong(pos + 4*k1); } else { list[k1] = 2 * getUShort(pos + 2*k1); } } k1 = 0; while (k1 < nGlyphs) { for (k2 = k1 + 1; k2 < nGlyphs && list[k2+1] - list[k1] <= 65532; ++k2) ; // ghostscript is unhappy if we break at anything other // than a multiple of four bytes while (((list[k2] - list[k1]) & 3) && k2 > k1 + 1) { --k2; } dumpString(file + tableHdrs[j].offset + list[k1], list[k2] - list[k1], out); k1 = k2; } gfree(list); } else { dumpString(file + tableHdrs[j].offset, tableHdrs[j].length, out); } } } fprintf(out, "] def\n"); } void TrueTypeFontFile::dumpString(char *s, int n, FILE *out) { int i, j; fprintf(out, "<"); for (i = 0; i < n; i += 32) { for (j = 0; j < 32 && i+j < n; ++j) { fprintf(out, "%02X", s[i+j] & 0xff); } if (i+32 < n) { fprintf(out, "\n"); } } if (n & 3) { for (i = 0; i < 4 - (n & 3); ++i) { fprintf(out, "00"); } } // append an extra mystery zero byte because the Type 42 spec says so fprintf(out, "00>\n"); }