/** * Andrew Weeks was kind anough * to contribute this code to us. * Picked up by Gaspar Sinai on December 1997. */ #include #include #include #include #include #include #include #include #include "ttf.h" typedef struct glyph { int char_no; /* Encoding of glyph */ int unicode; /* Unicode value of glyph */ char *name; /* Postscript name of glyph */ int xMin, yMin, xMax, yMax; int width, lsb; } GLYPH; static GLYPH *glyph_list; static short encoding[256]; /* inverse of glyph[].char_no */ static FILE *pfa_file, *afm_file; static TTF_DIRECTORY *directory; static TTF_DIR_ENTRY *dir_entry ; static char *filebuffer; static TTF_NAME *name_table = NULL; static TTF_NAME_REC *name_record; static TTF_HEAD *head_table = NULL; static TTF_HHEA *hhea_table = NULL; static TTF_KERN *kern_table = NULL; static TTF_CMAP *cmap_table = NULL; static LONGHORMETRIC *hmtx_table = NULL; static TTF_GLYF *glyf_table; static BYTE *glyf_start = NULL; static TTF_MAXP *maxp_table = NULL; static TTF_POST_HEAD *post_table = NULL; static USHORT *short_loca_table = NULL; static ULONG *long_loca_table = NULL; static int ttf_file, numglyphs, long_offsets, ncurves; static short cmap_n_segs; static USHORT *cmap_seg_start, *cmap_seg_end; static short *cmap_idDelta, *cmap_idRangeOffset; static int ps_fmt_3 = 0, unicode = 0; static float scale_factor; static char *Unknown_glyph = "UNKN"; static char name_buffer[2000]; static char *name_fields[8]; static char *ISOLatin1Encoding[256] = { ".null", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", "CR", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", ".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "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", ".notdef", ".notdef", ".notdef", "quotesinglbase", "florin", "quotedblbase", "ellipsis", "dagger", "daggerdbl", "circumflex", "perthousand", "Scaron", "guilsinglleft", "OE", ".notdef", ".notdef", ".notdef", ".notdef", "quoteleft", "quoteright", "quotedblleft", "quotedblright", "bullet", "endash", "emdash", "tilde", "trademark", "scaron", "guilsinglright", "oe", ".notdef", ".notdef", "Ydieresis", "nbspace", "exclamdown", "cent", "sterling", "currency", "yen", "brokenbar", "section", "dieresis", "copyright", "ordfeminine", "guillemotleft", "logicalnot", "sfthyphen", "registered", "macron", "degree", "plusminus", "twosuperior", "threesuperior", "acute", "mu", "paragraph", "periodcentered", "cedilla", "onesuperior", "ordmasculine", "guillemotright", "onequarter", "onehalf", "threequarters", "questiondown", "Agrave", "Aacute", "Acircumflex", "Atilde", "Adieresis", "Aring", "AE", "Ccedilla", "Egrave", "Eacute", "Ecircumflex", "Edieresis", "Igrave", "Iacute", "Icircumflex", "Idieresis", "Eth", "Ntilde", "Ograve", "Oacute", "Ocircumflex", "Otilde", "Odieresis", "multiply", "Oslash", "Ugrave", "Uacute", "Ucircumflex", "Udieresis", "Yacute", "Thorn", "germandbls", "agrave", "aacute", "acircumflex", "atilde", "adieresis", "aring", "ae", "ccedilla", "egrave", "eacute", "ecircumflex", "edieresis", "igrave", "iacute", "icircumflex", "idieresis", "eth", "ntilde", "ograve", "oacute", "ocircumflex", "otilde", "odieresis", "divide", "oslash", "ugrave", "uacute", "ucircumflex", "udieresis", "yacute", "thorn", "ydieresis"}; static char *adobe_StandardEncoding[256] = { ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", "space","exclam","quotedbl","numbersign", "dollar","percent","ampersand","quoteright", "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",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef","exclamdown","cent","sterling", "fraction","yen","florin","section", "currency","quotesingle","quotedblleft","guillemotleft", "guilsinglleft","guilsinglright","fi","fl", ".notdef","endash","dagger","daggerdbl", "periodcentered",".notdef","paragraph","bullet", "quotesinglbase","quotedblbase","quotedblright","guillemotright", "ellipsis","perthousand",".notdef","questiondown", ".notdef","grave","acute","circumflex", "tilde","macron","breve","dotaccent", "dieresis",".notdef","ring","cedilla", ".notdef","hungarumlaut","ogonek","caron", "emdash",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef",".notdef",".notdef",".notdef", ".notdef","AE",".notdef","ordfeminine", ".notdef",".notdef",".notdef",".notdef", "Lslash","Oslash","OE","ordmasculine", ".notdef",".notdef",".notdef",".notdef", ".notdef","ae",".notdef",".notdef", ".notdef","dotlessi",".notdef",".notdef", "lslash","oslash","oe","germandbls", ".notdef",".notdef",".notdef",".notdef"}; static char *mac_glyph_names[258] = { ".notdef", ".null", "CR", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde", "aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling", "section", "bullet", "paragraph", "germandbls", "registered", "copyright", "trademark", "acute", "dieresis", "notequal", "AE", "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff", "summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", "ae", "oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin", "approxequal", "increment", "guillemotleft", "guillemotright", "ellipsis", "nbspace", "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction", "currency", "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", "applelogo", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "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"}; static int unicode_to_win31 (int unival) { if (unival <= 0x0081) { return unival; } else if (unival >= 0x00a0 && unival <= 0x00ff) { return unival; } else { switch (unival) { case 0x008d: return 0x8d; case 0x008e: return 0x8e; case 0x008f: return 0x8f; case 0x0090: return 0x90; case 0x009d: return 0x9d; case 0x009e: return 0x9e; case 0x0152: return 0x8c; case 0x0153: return 0x9c; case 0x0160: return 0x8a; case 0x0161: return 0x9a; case 0x0178: return 0x9f; case 0x0192: return 0x83; case 0x02c6: return 0x88; case 0x02dc: return 0x98; case 0x2013: return 0x96; case 0x2014: return 0x97; case 0x2018: return 0x91; case 0x2019: return 0x92; case 0x201a: return 0x82; case 0x201c: return 0x93; case 0x201d: return 0x94; case 0x201e: return 0x84; case 0x2020: return 0x86; case 0x2021: return 0x87; case 0x2022: return 0x95; case 0x2026: return 0x85; case 0x2030: return 0x89; case 0x2039: return 0x8b; case 0x203a: return 0x9b; case 0x2122: return 0x99; default: return 0xffff; } } } static void handle_name () { int j, k, lang, len, platform; char *p, *ptr, *string_area; char *nbp = name_buffer; int found3 = 0; string_area = (char *)name_table + ntohs(name_table->offset); name_record = &(name_table->nameRecords); for (j=0; j<8; j++) { name_fields[j] = NULL; } for (j=0; j < ntohs(name_table->numberOfNameRecords); j++) { platform = ntohs(name_record->platformID); if (platform == 3) { found3 = 1; lang = ntohs(name_record->languageID) & 0xff; len = ntohs(name_record->stringLength); if (lang == 0 || lang == 9) { k = ntohs(name_record->nameID); if (k < 8) { name_fields[k] = nbp; p = string_area+ntohs(name_record->stringOffset); for ( k = 0; k < len; k++) { if (p[k] != '\0') { if (p[k] == '(') { *nbp = '['; } else if (p[k] == ')') { *nbp = ']'; } else { *nbp = p[k]; } nbp ++; } } *nbp = '\0'; nbp ++; } } } name_record++; } string_area = (char *)name_table + ntohs(name_table->offset); name_record = &(name_table->nameRecords); if (!found3) { for (j=0; j < ntohs(name_table->numberOfNameRecords); j++) { platform = ntohs(name_record->platformID); if (platform == 1) { found3 = 1; lang = ntohs(name_record->languageID) & 0xff; len = ntohs(name_record->stringLength); if (lang == 0 || lang == 9) { k = ntohs(name_record->nameID); if (k < 8) { name_fields[k] = nbp; p = string_area+ntohs(name_record->stringOffset); for ( k = 0; k < len; k++) { if (p[k] != '\0') { if (p[k] == '(') { *nbp = '['; } else if (p[k] == ')') { *nbp = ']'; } else { *nbp = p[k]; } nbp ++; } } *nbp = '\0'; nbp ++; } } } name_record++; } } if (!found3) { fprintf(stderr, "**** Cannot decode font name fields ****\n"); exit(-1); } if (name_fields[6] == NULL) { name_fields[6] = name_fields[4]; } p = name_fields[6]; while (*p != '\0') { if (!isalnum(*p)) { *p = '_'; } p++; } } static void handle_cmap () { int num_tables = ntohs(cmap_table->numberOfEncodingTables); BYTE *ptr; int i, j, k, kk, size, format, offset, seg_c2, found, set_ok; int platform, encoding_id; TTF_CMAP_ENTRY *table_entry; TTF_CMAP_FMT0 *encoding0; TTF_CMAP_FMT4 *encoding4; USHORT start, end, ro; short delta, n; found = 0; for (i=0; i<256; i++) { encoding[i] = 0; } for (i=0; i < num_tables && !found; i++) { table_entry = &(cmap_table->encodingTable[i]); offset = ntohl(table_entry->offset); encoding4 = (TTF_CMAP_FMT4 *) ((BYTE *)cmap_table + offset); format = ntohs(encoding4->format); platform = ntohs(table_entry->platformID); encoding_id = ntohs(table_entry->encodingID); if (platform == 3 && format == 4) { switch (encoding_id) { case 0: fputs("Found Symbol Encoding\n", stderr); break; case 1: fputs("Found Unicode Encoding\n", stderr); unicode = 1; break; default: fprintf(stderr, "****MS Encoding ID %d not supported****\n", encoding_id); exit(-1); break; } found = 1; seg_c2 = ntohs(encoding4->segCountX2); cmap_n_segs = seg_c2 >> 1; ptr = (BYTE *)encoding4 + 14; cmap_seg_end = (USHORT *) ptr; cmap_seg_start = (USHORT *) (ptr + seg_c2 + 2); cmap_idDelta = (short *) (ptr + (seg_c2 * 2 )+ 2); cmap_idRangeOffset = (short *) (ptr + (seg_c2 * 3) + 2); for (j=0; j < cmap_n_segs-1; j++) { start = ntohs(cmap_seg_start[j]); end = ntohs(cmap_seg_end[j]); delta = ntohs(cmap_idDelta[j]); ro = ntohs(cmap_idRangeOffset[j]); for (k = start; k <= end; k++) { if (delta != 0) { n = k + delta; } else { n = ntohs(*( (ro >> 1) + (k - start) + &(cmap_idRangeOffset[j]))); } if (glyph_list[n].unicode != -1) { if (strcmp(glyph_list[n].name, ".notdef") != 0) { fprintf(stderr, "Glyph %s has >= two encodings (A), %4.4x & %4.4x\n", glyph_list[n].name, glyph_list[n].unicode, k); } set_ok = 0; } else { set_ok = 1; } if (unicode) { kk = unicode_to_win31 (k); if (set_ok) { glyph_list[n].unicode = k; glyph_list[n].char_no = kk; } if (kk <= 0xff) encoding[kk] = n; } else { if ((k & 0xff00) == 0xf000) { encoding[k & 0x00ff] = n; if (set_ok) { glyph_list[n].char_no = k & 0x00ff; glyph_list[n].unicode = k; } } else { if (set_ok) { glyph_list[n].char_no = k; glyph_list[n].unicode = k; } fprintf(stderr, "Glyph %s has non-symbol encoding %4.4x\n", glyph_list[n].name, k & 0xffff); } } } } } } if (!found) { fputs ("No Microsoft encoding, looking for MAC encoding\n", stderr); for (i=0; i < num_tables && !found; i++) { table_entry = &(cmap_table->encodingTable[i]); offset = ntohl(table_entry->offset); encoding0 = (TTF_CMAP_FMT0 *) ((BYTE *)cmap_table + offset); format = ntohs(encoding0->format); platform = ntohs(table_entry->platformID); encoding_id = ntohs(table_entry->encodingID); if (format == 0) { found = 1; size = ntohs(encoding0->length) - 6; for (j=0; jglyphIdArray[j]; if (glyph_list[n].char_no != -1) { fprintf(stderr, "Glyph %s has >= two encodings (B), %4.4x & %4.4x\n", glyph_list[n].name, glyph_list[n].char_no, j); } else { glyph_list[n].char_no = j; if (j < 256) { encoding[j] = n; } } } } } } if (!found) { fprintf(stderr, "**** No Recognised Encoding Table ****\n"); exit(-1); } } static void handle_head () { long_offsets = ntohs(head_table->indexToLocFormat); if (long_offsets != 0 && long_offsets != 1) { fprintf(stderr, "**** indexToLocFormat wrong ****\n"); exit(-1); } } static void draw_glyf(int glyphno, int parent) { int i, j, k, k1, len, first, cs, ce; int finished, nguide, contour_start, contour_end; short ncontours, n_inst, last_point; USHORT *contour_end_pt; BYTE *ptr; short xcoord[2000], ycoord[2000], xrel[2000], yrel[2000]; BYTE flags[2000]; if (long_offsets) { glyf_table = (TTF_GLYF *) (glyf_start + ntohl(long_loca_table[glyphno])); len = ntohl(long_loca_table[glyphno+1]) - ntohl(long_loca_table[glyphno]); } else { glyf_table = (TTF_GLYF *) (glyf_start + (ntohs(short_loca_table[glyphno]) << 1)); len = (ntohs(short_loca_table[glyphno+1]) - ntohs(short_loca_table[glyphno])) << 1; } if (len <= 0) { fprintf(stderr, "**** Composite glyph %s refers to non-existent glyph %s ****\n", glyph_list[parent].name, glyph_list[glyphno].name); fprintf(pfa_file, "\n%%**** Composite glyph %s refers to non-existent glyph %s ****\n", glyph_list[parent].name, glyph_list[glyphno].name); return; } ncontours = ntohs(glyf_table->numberOfContours); if (ncontours <= 0) { fprintf(stderr, "**** Composite glyph %s refers to composite glyph %s ****\n", glyph_list[parent].name, glyph_list[glyphno].name); fprintf(pfa_file, "\n%%**** Composite glyph %s refers to composite glyph %s ****\n", glyph_list[parent].name, glyph_list[glyphno].name); return; } contour_end_pt = (USHORT *) ((char *)glyf_table + sizeof(TTF_GLYF)); last_point = ntohs(contour_end_pt[ncontours-1]); n_inst = ntohs(contour_end_pt[ncontours]); ptr = ((BYTE *)contour_end_pt) + (ncontours << 1) + n_inst + 2; j = k = 0; while (k <= last_point) { flags[k] = ptr[j]; if (ptr[j] & REPEAT) { for (k1=0; k1 < ptr[j+1]; k1++) { k++; flags[k] = ptr[j]; } j++; } j++; k++; } for (k=0; k <= last_point; k++) { if (flags[k] & XSHORT) { if (flags[k] & XSAME) { xrel[k] = ptr[j]; } else { xrel[k] = - ptr[j]; } j++; } else if (flags[k] & XSAME) { xrel[k] = 0; } else { xrel[k] = ptr[j] * 256 + ptr[j+1]; j += 2; } if (k==0) { xcoord[k] = xrel[k]; } else { xcoord[k] = xrel[k] + xcoord[k-1]; } } for (k=0; k <= last_point; k++) { if (flags[k] & YSHORT) { if (flags[k] & YSAME) { yrel[k] = ptr[j]; } else { yrel[k] = - ptr[j]; } j++; } else if (flags[k] & YSAME) { yrel[k] = 0; } else { yrel[k] = ptr[j] * 256 + ptr[j+1]; j += 2; } if (k==0) { ycoord[k] = yrel[k]; } else { ycoord[k] = yrel[k] + ycoord[k-1]; } } i = j = 0; first = 1; while (i <= ntohs(contour_end_pt[ncontours-1])) { contour_end = ntohs(contour_end_pt[j]); if (first) { fprintf(pfa_file, "%d %d moveto\n", xcoord[i], ycoord[i]); ncurves ++; contour_start = i; first = 0; } else if (flags[i] & ONOROFF) { fprintf(pfa_file, "%d %d lineto\n", xcoord[i], ycoord[i]); ncurves ++; } else { cs = i-1; finished = nguide = 0; while (!finished) { if (i == contour_end+1) { ce = contour_start; finished = 1; } else if (flags[i] & ONOROFF) { ce = i; finished = 1; } else { i++; nguide++; } } switch (nguide) { case 0: fprintf( pfa_file,"%d %d lineto\n", xcoord[ce], ycoord[ce]); ncurves ++; break; case 1: fprintf( pfa_file, "%d %d %d %d %d %d curveto\n", (xcoord[cs]+2*xcoord[cs+1])/3, (ycoord[cs]+2*ycoord[cs+1])/3, (2*xcoord[cs+1]+xcoord[ce])/3, (2*ycoord[cs+1]+ycoord[ce])/3, xcoord[ce], ycoord[ce]); ncurves ++; break; case 2: fprintf( pfa_file, "%d %d %d %d %d %d curveto\n", (-xcoord[cs]+4*xcoord[cs+1])/3, (-ycoord[cs]+4*ycoord[cs+1])/3, (4*xcoord[cs+2]-xcoord[ce])/3, (4*ycoord[cs+2]-ycoord[ce])/3, xcoord[ce], ycoord[ce]); ncurves ++; break; case 3: fprintf( pfa_file, "%d %d %d %d %d %d curveto\n", (xcoord[cs]+2*xcoord[cs+1])/3, (ycoord[cs]+2*ycoord[cs+1])/3, (5*xcoord[cs+1]+xcoord[cs+2])/6, (5*ycoord[cs+1]+ycoord[cs+2])/6, (xcoord[cs+1]+xcoord[cs+2])/2, (ycoord[cs+1]+ycoord[cs+2])/2); fprintf( pfa_file, "%d %d %d %d %d %d curveto\n", (xcoord[cs+1]+5*xcoord[cs+2])/6, (ycoord[cs+1]+5*ycoord[cs+2])/6, (5*xcoord[cs+2]+xcoord[cs+3])/6, (5*ycoord[cs+2]+ycoord[cs+3])/6, (xcoord[cs+3]+xcoord[cs+2])/2, (ycoord[cs+3]+ycoord[cs+2])/2); fprintf( pfa_file, "%d %d %d %d %d %d curveto\n", (xcoord[cs+2]+5*xcoord[cs+3])/6, (ycoord[cs+2]+5*ycoord[cs+3])/6, (2*xcoord[cs+3]+xcoord[ce])/3, (2*ycoord[cs+3]+ycoord[ce])/3, xcoord[ce], ycoord[ce]); ncurves += 3; break; default:k1 = cs + nguide; fprintf( pfa_file, "%d %d %d %d %d %d curveto\n", (xcoord[cs]+2*xcoord[cs+1])/3, (ycoord[cs]+2*ycoord[cs+1])/3, (5*xcoord[cs+1]+xcoord[cs+2])/6, (5*ycoord[cs+1]+ycoord[cs+2])/6, (xcoord[cs+1]+xcoord[cs+2])/2, (ycoord[cs+1]+ycoord[cs+2])/2); for (k = cs+2; k <= k1-1; k++) { fprintf( pfa_file, "%d %d %d %d %d %d curveto\n", (xcoord[k-1]+5*xcoord[k])/6, (ycoord[k-1]+5*ycoord[k])/6, (5*xcoord[k]+xcoord[k+1])/6, (5*ycoord[k]+ycoord[k+1])/6, (xcoord[k]+xcoord[k+1])/2, (ycoord[k]+ycoord[k+1])/2); } fprintf( pfa_file, "%d %d %d %d %d %d curveto\n", (xcoord[k1-1]+5*xcoord[k1])/6, (ycoord[k1-1]+5*ycoord[k1])/6, (2*xcoord[k1]+xcoord[ce])/3, (2*ycoord[k1]+ycoord[ce])/3, xcoord[ce], ycoord[ce]); ncurves += nguide; break; } } if (i >= contour_end) { fprintf(pfa_file, " closepath "); first = 1; i = contour_end + 1; j++; } else { i++; } } } static float f2dot14 (short x) { short y = ntohs(x); return (y >> 14) + ((y & 0x3fff) / 16384.0); } static void do_glyf(int glyphno) { int len, c; short ncontours; USHORT flagbyte, glyphindex, xscale, yscale, scale01, scale10; SHORT arg1, arg2; BYTE *ptr; char *bptr; SHORT *sptr; float matrix[4]; ncurves = 0; if (long_offsets) { glyf_table = (TTF_GLYF *) (glyf_start + ntohl(long_loca_table[glyphno])); len = ntohl(long_loca_table[glyphno+1]) - ntohl(long_loca_table[glyphno]); } else { glyf_table = (TTF_GLYF *) (glyf_start + (ntohs(short_loca_table[glyphno]) << 1)); len = (ntohs(short_loca_table[glyphno+1]) - ntohs(short_loca_table[glyphno])) << 1; } if (unicode) { fprintf(pfa_file, "/%s { %% %d - U+%4.4x\n", glyph_list[glyphno].name, glyphno, (unsigned short)glyph_list[glyphno].unicode); } else { fprintf(pfa_file, "/%s { %% %d - 0x%2.2x\n", glyph_list[glyphno].name, glyphno, (unsigned short)glyph_list[glyphno].char_no); } c = glyph_list[glyphno].char_no; fprintf(afm_file, "C %d ; WX %.0f ; N %s ; B %.0f %.0f %.0f %.0f ;\n", c < 256 ? c : -1, scale_factor * glyph_list[glyphno].width, glyph_list[glyphno].name, scale_factor * (short)ntohs(glyf_table->xMin), scale_factor * (short)ntohs(glyf_table->yMin), scale_factor * (short)ntohs(glyf_table->xMax), scale_factor * (short)ntohs(glyf_table->yMax)); fprintf(pfa_file, "%d 0 %hd %hd %hd %hd setcachedevice\n", glyph_list[glyphno].width, (short)ntohs(glyf_table->xMin), (short)ntohs(glyf_table->yMin), (short)ntohs(glyf_table->xMax), (short)ntohs(glyf_table->yMax)); if (len != 0) { ncontours = ntohs(glyf_table->numberOfContours); if (ncontours <= 0) { ptr = ((BYTE *)glyf_table + sizeof(TTF_GLYF)); sptr = (SHORT *) ptr; do { flagbyte = ntohs(*sptr); sptr ++; glyphindex = ntohs(*sptr); sptr ++; fprintf(pfa_file, "%% flags %x glyph %s\n", flagbyte, glyph_list[glyphindex].name); if (flagbyte & ARG_1_AND_2_ARE_WORDS) { arg1 = ntohs(*sptr); sptr++; arg2 = ntohs(*sptr); sptr++; } else { bptr = (char *)sptr; arg1 = (signed char)bptr[0]; arg2 = (signed char)bptr[1]; sptr ++; } matrix[1] = matrix[2] = 0.0; if (flagbyte & WE_HAVE_A_SCALE) { matrix[0] = matrix[3] = f2dot14(*sptr); sptr ++; } else if (flagbyte & WE_HAVE_AN_X_AND_Y_SCALE) { matrix[0] = f2dot14(*sptr); sptr ++; matrix[3] = f2dot14(*sptr); sptr ++; } else if (flagbyte & WE_HAVE_A_TWO_BY_TWO) { matrix[0] = f2dot14(*sptr); sptr ++; matrix[1] = f2dot14(*sptr); sptr ++; matrix[2] = f2dot14(*sptr); sptr ++; matrix[3] = f2dot14(*sptr); sptr ++; } else { matrix[0] = matrix[3] = 1.0; } fprintf(pfa_file, "matrix currentmatrix\n[ %9.7f %9.7f %9.7f %9.7f %hd %hd ] concat\n", matrix[0], matrix[1], matrix[2], matrix[3], arg1, arg2); draw_glyf(glyphindex, glyphno); /*fputs("setmatrix closepath\n", pfa_file);*/ fputs("setmatrix\n", pfa_file); } while (flagbyte & MORE_COMPONENTS); } else { draw_glyf(glyphno, glyphno); /*fprintf( pfa_file, "closepath ");*/ } if (ncurves > 100) { fprintf(stderr, "**Glyf %s is too long, may have to be removed**\n", glyph_list[glyphno].name); fprintf(pfa_file, "\n%%**Glyf %s is too long, may have to be removed**\n", glyph_list[glyphno].name); } } fprintf(pfa_file, "fill } bind def\n"); } static void handle_hmtx() { int i; int n_hmetrics = ntohs(hhea_table->numberOfHMetrics); GLYPH *g; LONGHORMETRIC *hmtx_entry = hmtx_table; FWORD *lsblist; for (i = 0; i < n_hmetrics; i++) { g = &(glyph_list[i]); g->width = ntohs(hmtx_entry->advanceWidth); g->lsb = ntohs(hmtx_entry->lsb); hmtx_entry++; } lsblist = (FWORD *)hmtx_entry; hmtx_entry--; for (i = n_hmetrics; i < numglyphs; i++) { g = &(glyph_list[i]); g->width = ntohs(hmtx_entry->advanceWidth); g->lsb = ntohs(lsblist [i-n_hmetrics]); } } static void handle_post() { int i, len, n, found; unsigned int format; USHORT *name_index; char *ptr; char **ps_name_ptr = (char **) malloc (numglyphs * sizeof (char *)); int *ps_name_len = (int *) malloc (numglyphs * sizeof (int)); int n_ps_names; format = ntohl(post_table->formatType); if (format == 0x00010000) { for (i=0; i<258; i++) { glyph_list[i].name = mac_glyph_names[i]; } } else if (format == 0x00020000) { n = ntohs(post_table->numGlyphs); if (numglyphs != n) { fprintf(stderr, "**** Postscript table size mismatch %d/%d ****\n", n, numglyphs); exit(-1); } ptr = (char *)post_table + 34 + (numglyphs << 1); n_ps_names = 0; while (*ptr > 0) { len = ps_name_len[n_ps_names] = *ptr; ps_name_ptr[n_ps_names] = ptr+1; *ptr = '\0'; n_ps_names ++; ptr += len + 1; } *ptr = '\0'; /* for (i=0; iglyphNameIndex); for (i=0; inumGlyphs); for (i=0; inTables); int npairs; char *ptr = (char *)kern_table + 4; for (i=0; i < ntables; i++) { subtable = (TTF_KERN_SUB *)ptr; if ((ntohs(subtable->coverage) & 0xff00) == 0) { npairs = (short)ntohs(subtable->nPairs); fprintf(afm_file, "StartKernPairs %hd\n", npairs); kern_entry = (TTF_KERN_ENTRY *)(ptr + sizeof(TTF_KERN_SUB)); for (j=0; jleft)].name, glyph_list[ntohs(kern_entry->right)].name, scale_factor * (short) ntohs(kern_entry->value)); kern_entry ++; } fprintf(afm_file, "EndKernPairs\n"); } ptr += subtable->length; } } main (int argc, char **argv) { int i; time_t now; float italic_angle; struct stat statbuf; char filename[100]; if (argc != 3) { fputs("ttf2pfa \n", stderr); exit(-1); } if (stat(argv[1], &statbuf) == -1) { fprintf(stderr, "**** Cannot access %s ****\n", argv[1]); exit(-1); } if ((filebuffer = malloc (statbuf.st_size)) == NULL) { fprintf(stderr, "**** Cannot malloc space for file ****\n"); exit(-1); } if ((ttf_file=open(argv[1], O_RDONLY, 0)) == -1) { fprintf(stderr, "**** Cannot open %s ****\n", argv[1]); exit(-1); } else { fprintf(stderr, "Processing file %s\n", argv[1]); } if (read (ttf_file, filebuffer, statbuf.st_size) != statbuf.st_size) { fprintf(stderr, "**** Could not read whole file ****\n"); exit(-1); } directory = (TTF_DIRECTORY *) filebuffer; if (ntohl(directory->sfntVersion) != 0x00010000) { fprintf(stderr, "****Unknown File Version number [%x], or not a TrueType file****\n", directory->sfntVersion); exit(-1); } if(argv[2][0]=='-' && argv[2][1]==0) { pfa_file=stdout; if ((afm_file = fopen("/dev/null", "w+")) == NULL) { fprintf(stderr, "**** Cannot create %s ****\n", filename); exit(-1); } } else { sprintf(filename, "%s.pfa", argv[2]) ; if ((pfa_file = fopen(filename, "w+")) == NULL) { fprintf(stderr, "**** Cannot create %s ****\n", filename); exit(-1); } else { fprintf(stderr, "Creating file %s\n", filename); } sprintf(filename, "%s.afm", argv[2]) ; if ((afm_file = fopen(filename, "w+")) == NULL) { fprintf(stderr, "**** Cannot create %s ****\n", filename); exit(-1); } } dir_entry = &(directory->list); for (i=0; i < ntohs(directory->numTables); i++) { if (memcmp(dir_entry->tag, "name", 4) == 0) { name_table = (TTF_NAME *) (filebuffer+ntohl(dir_entry->offset)); } else if (memcmp(dir_entry->tag, "head", 4) == 0) { head_table = (TTF_HEAD *) (filebuffer+ntohl(dir_entry->offset)); } else if (memcmp(dir_entry->tag, "hhea", 4) == 0) { hhea_table = (TTF_HHEA *) (filebuffer+ntohl(dir_entry->offset)); } else if (memcmp(dir_entry->tag, "post", 4) == 0) { post_table = (TTF_POST_HEAD *) (filebuffer+ntohl(dir_entry->offset)); } else if (memcmp(dir_entry->tag, "glyf", 4) == 0) { glyf_start = (BYTE *) (filebuffer+ntohl(dir_entry->offset)); } else if (memcmp(dir_entry->tag, "cmap", 4) == 0) { cmap_table = (TTF_CMAP *) (filebuffer+ntohl(dir_entry->offset)); } else if (memcmp(dir_entry->tag, "kern", 4) == 0) { kern_table = (TTF_KERN *) (filebuffer+ntohl(dir_entry->offset)); } else if (memcmp(dir_entry->tag, "maxp", 4) == 0) { maxp_table = (TTF_MAXP *) (filebuffer+ntohl(dir_entry->offset)); } else if (memcmp(dir_entry->tag, "hmtx", 4) == 0) { hmtx_table = (LONGHORMETRIC *) (filebuffer+ntohl(dir_entry->offset)); } else if (memcmp(dir_entry->tag, "loca", 4) == 0) { long_loca_table = (ULONG *) (filebuffer+ntohl(dir_entry->offset)); short_loca_table = (USHORT *) long_loca_table; } else if (memcmp(dir_entry->tag, "EBDT", 4) == 0 || memcmp(dir_entry->tag, "EBLC", 4) == 0 || memcmp(dir_entry->tag, "EBSC", 4) == 0) { fprintf (stderr, "Font contains bitmaps\n"); } dir_entry++; } handle_name(); handle_head(); numglyphs = ntohs(maxp_table->numGlyphs); fprintf(stderr, "numglyphs = %d\n", numglyphs); glyph_list = (GLYPH *) malloc (numglyphs * sizeof (GLYPH)); for (i=0; iitalicAngle.upper)) + (ntohs(post_table->italicAngle.lower) / 65536.0); fprintf(pfa_file, "/italicAngle %f def\n", italic_angle); fprintf(afm_file, "ItalicAngle %f\n", italic_angle); scale_factor = 1000.0 / ntohs(head_table->unitsPerEm); fprintf(afm_file, "Ascender %.0f\n", scale_factor * (short)ntohs(hhea_table->ascender)); fprintf(afm_file, "Descender %.0f\n", scale_factor * (short)ntohs(hhea_table->descender)); fprintf(pfa_file, "/underlineThickness %hd def\n", (short)ntohs(post_table->underlineThickness)); fprintf(afm_file, "UnderlineThickness %.0f\n", scale_factor * (short)ntohs(post_table->underlineThickness)); fprintf(pfa_file, "/underlinePosition %hd def\n", (short)ntohs(post_table->underlinePosition)); fprintf(afm_file, "UnderlinePosition %.0f\n", scale_factor * (short)ntohs(post_table->underlinePosition)); fprintf(pfa_file, "/isFixedPitch %s def end def\n", ntohl(post_table->isFixedPitch) ? "true" : "false" ); fprintf(afm_file, "IsFixedPitch %s\n", ntohl(post_table->isFixedPitch) ? "true" : "false" ); fprintf(pfa_file, "/FontMatrix [%9.7f 0 0 %9.7f 0 0] def\n", scale_factor/1000.0, scale_factor/1000.0); fprintf(pfa_file, "/FontBBox [%hd %hd %hd %hd] def\n", (short)ntohs(head_table->xMin), (short)ntohs(head_table->yMin), (short)ntohs(head_table->xMax), (short)ntohs(head_table->yMax)); fprintf(afm_file, "FontBBox %.0f %.0f %.0f %.0f\n", scale_factor * (short)ntohs(head_table->xMin), scale_factor * (short)ntohs(head_table->yMin), scale_factor * (short)ntohs(head_table->xMax), scale_factor * (short)ntohs(head_table->yMax)); fprintf(pfa_file, "/Encoding [\n"); for (i=0; i<256; i++) { fprintf(pfa_file, "/%s ", glyph_list[encoding[i]].name); if (i%4 == 3) { fprintf(pfa_file, "%% 0x%x\n", i-3); } } fprintf(pfa_file, "] def\n/CharProcs %d dict def CharProcs begin\n", numglyphs+1); fprintf(afm_file, "StartCharMetrics %d\n", numglyphs); for (i=0; i