/* * ttf2pfb.c -- TrueType to PostScript Type 1 Font converter. * * Author: Chun-Yu Lee * Maintainer: Werner Lemberg * * The generated output is in a raw Type 1 Font format. An encoder * (e.g. t1asm or t1binary) is needed to convert it into PFA or PFB format. * * This program was adapted from the ntu2cjk package (which was part of the * LaTeX2e CJK package (by Werner Lemberg )). * * * Modified by Joachim H. Kaiser to include real glyph * names and other font infos (see PS_Head function). * The test programs of the FreeType distribution have been heavily used as * templates. * */ /* * Requirements: * - the FreeType library. * - t1asm or a similar converter if PFA or PFB format is required. * - getafm or a similar program if AFM font metrics are required. * - afm2tfm or a similar program if TFM font metrics are required. * - for compact fonts: the CJK package for typesetting LaTeX documents. * - dvips 5.66 or higher if self-contained PostScript document * outputs with partially downloaded fonts are required. Note that * the partial download code in dvips is still buggy, causing strange * error messages during loading of the created PS fonts. * - Ghostscript 3.33 or newer (this is optional). */ #include #include #include #include #include #include #ifdef TM_IN_SYS_TIME #include #endif #include #include "freetype.h" #include "extend/ftxpost.h" /* we are in the FreeType package tree */ char rcsid[] = "Id: ttf2pfb.c,v 1.13 1999/08/20 13:14:26 werner Exp $"; #define PID_UNICODE 3 #define EID_UNICODE 1 #define PID_SJIS 3 #define EID_SJIS 2 #define PID_GB 3 #define EID_GB 3 #define PID_BIG5 3 #define EID_BIG5 4 #define PID_KS 3 /* KS is also called Wansung */ #define EID_KS 5 #define PID_JOHAB 3 #define EID_JOHAB 6 /* Set default values */ #ifndef DEFAULT_PLATFORM_ID #define DEFAULT_PLATFORM_ID PID_UNICODE /* MS platform */ #endif #ifndef DEFAULT_ENCODING_ID #define DEFAULT_ENCODING_ID EID_UNICODE #endif /* The possible values for the `force_enc' variable. */ typedef enum enc_type_ { GB = 1, Big5, JIS, KS, Johab, SJIS, X } enc_type; /* A variable to enforce a certain font encoding (if > 0). */ enc_type force_enc = 0; #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef FAILURE #define FAILURE -1 #endif #define LINELEN 40 /* max # of columns of code range file */ #define NAMELEN 80 /* max length of name used from LookUp */ /* * Flags and globals */ int verbose = FALSE; /* messages should be printed or not. */ int compact = FALSE; /* generate compact font? */ int keep = FALSE; /* keep output font in case of error? */ int mapping = FALSE; /* use encoding file as mapping? */ float fontShift = 0; #ifndef UShort typedef unsigned short UShort; #endif #ifndef UChar typedef unsigned char UChar; #endif #define TT_Flag_On_Curve 1 /* default platform and encoding ID's. */ int pid = DEFAULT_PLATFORM_ID; int eid = DEFAULT_ENCODING_ID; char* prog; /* program name */ /* for orthogonality with fprintf */ #define Fputs(_string_) fprintf(out, "%s\n", _string_) /* Postscript font related defines and functions */ TT_F26Dot6 lastpsx, lastpsy; #define Coord(x) (int)(x) #define PS_LastPt(x, y) lastpsx = x; lastpsy = y #define PS_Moveto(x, y) \ fprintf(out, "%d %d rmoveto\n", \ Coord(x - lastpsx), Coord(y - lastpsy)); \ PS_LastPt(x, y) #define PS_Lineto(x, y) \ fprintf(out, "%d %d rlineto\n", \ Coord(x - lastpsx), Coord(y - lastpsy)); \ PS_LastPt(x, y) /* * Freetype globals. */ TT_Engine engine; TT_Face face; TT_Instance instance; TT_Glyph glyph; TT_CharMap cmap; TT_Error error; TT_Post post; TT_Outline outline; TT_Glyph_Metrics metrics; TT_Face_Properties properties; /* * Data structures defined for encoding vectors */ /* * A code range file for the encoding vector of a font contains code * range pairs, each pair a line. The values for the begin and end of the * ranges are separated by ` - '. Note that the spaces before and after the * minus sign are significant. The possible syntax is as follows: * * (Note that code ranges must appear in ascending order.) * * 1. Absolute range, i.e., the code is at least a two-byte number, e.g.: * * 0xA140 - 0xA17E * 41280 - 41342 * 0xE00000 - 0xE000FF * * The first two lines represent the same range. * * 2. Relative range, i.e., the code is a one-byte number. If the line ends * with a colon `:', it designates the high byte(s) range, otherwise the * low byte range. If there is no high byte(s) range declared before the low * byte range, the last defined high byte(s) range or `0x00 - 0x00:' will be * used. e.g.: * * 0xA1 - 0xFE: * 0x40 - 0x7E * 0xA1 - 0xFE * * which is Big-5 Encoding. * * 3. Single code. Similar to absolute or relative range but the second * number of the range is the same as the first number. E.g.: * * 0xA141 == 0xA141 - 0xA141 * 0xA1: == 0xA1 - 0xA1: * 0xA1 == 0xA1 - 0xA1 * * 4. If the high byte range is declared and there is no low byte range * declared consecutively, the assumed low byte range is `0x00 - 0xFF'. * * 5. Comment line. A line starting with a hash mark `#' followed by any * characters up to the end of the line is ignored. Blank lines are also * discarded. */ typedef struct _EVHigh { UShort start, end; } EVHigh; typedef struct _EVLow { UChar start, end; } EVLow; typedef struct _EVcRange { EVHigh high; UShort numLowRanges; EVLow* low; } EVcRange; typedef struct _EncVec { UShort numCodeRanges; EVcRange* codeRange; } EncVec; /* Select encoding vector with respect to pid and eid */ EncVec* eVecMap[5][10]; /* Select encoding vector with respect to force_enc */ EncVec* eVecMap_force[10]; /*************/ /* Functions */ /*************/ void mesg(const char *msg, ...) { va_list ap; va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); } /* * Usage. */ void Usage(int eval) { #ifdef DEBUG mesg("Usage: %s [-h][-v][-c][-k][-m][-d charcode][-l][-ng][-nc]\n", prog); #else mesg("Usage: %s [-h][-v][-c][-k][-m]\n", prog); #endif mesg("\t[-pid id] [-eid id] [-force enc] [-enc file]\n"); mesg("\t[-plane pl] [-f fname] [-uid id] [-s shift]\n"); mesg("\t[-o output] [-ttf font.ttf | font.ttf]\n"); mesg("-h\t\tThis message.\n"); mesg("-v\t\tPrint messages during conversion.\n"); mesg("-c\t\tCompact font"); mesg(" (256 chars per font; useful for the CJK package).\n"); mesg("-k\t\tKeep output file in case of error.\n"); mesg("-m\t\tUse encoding file as mapping file.\n"); #ifdef DEBUG mesg("-d charcode\tDebug CharString for the given character code.\n"); mesg("-l\t\tDisplay point labels.\n"); mesg("-ng\t\tDo not show glyph outline.\n"); mesg("-nc\t\tDo not show control paths.\n"); #endif mesg("-pid id\t\tSet the platform ID [%d].\n", DEFAULT_PLATFORM_ID); mesg("-eid id\t\tSet the encoding ID [%d].\n", DEFAULT_ENCODING_ID); mesg("\t\t(Use a strange pid,eid pair to list all possible pid,eid)\n"); mesg("-force enc\tForce a certain encoding [none].\n"); mesg("\t\t(Possible values are `GB', `JIS', `KS', `SJIS', and `X').\n"); mesg("-enc file\tFile contains code ranges [none].\n"); mesg("-plane pl\tA single font plane [0x0].\n"); mesg("-f fname\tFont name [UNKNOWN].\n"); mesg("-uid id\t\tUnique font ID, for private fonts 4000000-4999999 [4999999].\n"); mesg("-s shift\tY-offset factor [%5.3f].\n", fontShift); mesg("-o outfile\tSet the output filename [stdout].\n"); mesg("-ttf ttfpath\tThe TTF font pathname.\n"); exit(eval); } void fatal(const char *msg, ...) { va_list ap; va_start(ap, msg); fprintf(stderr, "%s: ", prog); vfprintf(stderr, msg, ap); fprintf(stderr, "\n"); va_end(ap); exit(FAILURE); } void fatal_error(const char *msg, ...) { va_list ap; va_start(ap, msg); fprintf(stderr, "%s: Error code 0x%04lx: ", prog, error); vfprintf(stderr, msg, ap); fprintf(stderr, "\n"); va_end(ap); exit(FAILURE); } /* * Reallocate a pointer. */ void * re_alloc(void* ptr, size_t size, char* sub) { register void* value = realloc(ptr, size); if (value == NULL) fatal("%s: Virtual memory exhausted", sub); return value; } /* * We have to introduce the `dummy' integer to assure correct handling of * the stack. Using `UShort' as the first parameter may fail in case * this type is promoted to a different type (as happens e.g. under * emx for DOS). */ EncVec* Alloc_EncVec(int dummy, ...) { va_list vp; EncVec* encVec = NULL; EVcRange* cRange = NULL; EVLow* evLow = NULL; UShort numCR, numLows; int i, j; va_start(vp, dummy); numCR = va_arg(vp, UShort); encVec = re_alloc(encVec, 1 * sizeof (EncVec), "Alloc_EncVec"); encVec->numCodeRanges = numCR; cRange = re_alloc(cRange, numCR * sizeof (EVcRange), "Alloc_EncVec"); for (i = 0; i < numCR; i++) { (cRange + i)->high.start = va_arg(vp, UShort); (cRange + i)->high.end = va_arg(vp, UShort); (cRange + i)->numLowRanges = numLows = va_arg(vp, UShort); evLow = NULL; evLow = re_alloc(evLow, numLows * sizeof (EVLow), "Alloc_EncVec"); for (j = 0; j < numLows; j++) { (evLow + j)->start = va_arg(vp, UChar); (evLow + j)->end = va_arg(vp, UChar); } (cRange + i)->low = evLow; } encVec->codeRange = cRange; va_end(vp); return encVec; } void Known_Encodings(void) { EncVec* encVec; /* Big-5 encoding */ encVec = Alloc_EncVec(1, 1, 0xA1, 0xFE, 2, 0x40, 0x7E, 0xA1, 0xFE); eVecMap[PID_BIG5][EID_BIG5] = encVec; eVecMap_force[Big5] = encVec; /* GB encoding */ encVec = Alloc_EncVec(2, 1, 0xA1, 0xF7, 1, 0xA1, 0xFE); eVecMap[PID_GB][EID_GB] = encVec; eVecMap_force[GB] = encVec; /* KS encoding */ encVec = Alloc_EncVec(3, 1, 0xA1, 0xFE, 1, 0xA1, 0xFE); eVecMap[PID_KS][EID_KS] = encVec; eVecMap_force[KS] = encVec; /* Johab encoding */ encVec = Alloc_EncVec(4, 3, 0x84, 0xD3, 2, 0x41, 0x7E, 0x81, 0xFE, 0xD8, 0xDE, 2, 0x31, 0x7E, 0x91, 0xFE, 0xE0, 0xF9, 2, 0x31, 0x7E, 0x91, 0xFE); eVecMap[PID_JOHAB][EID_JOHAB] = encVec; eVecMap_force[Johab] = encVec; /* JIS encoding */ encVec = Alloc_EncVec(5, 1, 0xA1, 0xF4, 1, 0xA1, 0xFE); eVecMap_force[JIS] = encVec; eVecMap_force[X] = encVec; /* will be internally translated to SJIS */ /* Unicode encoding */ encVec = Alloc_EncVec(6, 1, 0x00, 0xFF, 1, 0x00, 0xFF); eVecMap[PID_UNICODE][EID_UNICODE] = encVec; /* SJIS encoding */ encVec = Alloc_EncVec(7, 3, 0x0, 0x0, 1, 0xA1, 0xDF, 0x81, 0x9F, 2, 0x40, 0x7E, 0x80, 0xFC, 0xE0, 0xEF, 2, 0x40, 0x7E, 0x80, 0xFC); eVecMap[PID_SJIS][EID_SJIS] = encVec; eVecMap_force[SJIS] = encVec; } /* * Convert JIS to SJIS encoding. */ UShort JIS_to_SJIS(UShort code) { UShort index; UShort cc0 = (code >> 8) & 0xFF; UShort cc1 = code & 0xFF; index = (cc0 - 0xa1) * (0xfe - 0xa1 + 1) + (cc1 - 0xa1); cc0 = index / ((0x7e - 0x40 + 1) + (0xfc - 0x80 + 1)); cc1 = index % ((0x7e - 0x40 + 1) + (0xfc - 0x80 + 1)); if (cc0 < (0x9f - 0x81 + 1)) cc0 += 0x81; else cc0 += 0xe0 - (0x9f - 0x81 + 1); if (cc1 < (0x7e - 0x40 + 1)) cc1 += 0x40; else cc1 += 0x80 - (0x7E - 0x40 + 1); return (cc0 << 8) + cc1; } /* * Open TTF file and select cmap. */ int Init_Font_Engine(char* infile) { UShort cmapindex, platformID, encodingID, num_cmap; if (verbose) printf("Initializing TrueType font engine...\n"); /* initialization of the FreeType engine */ error = TT_Init_FreeType(&engine); if (error) fatal_error("Couldn't initialize FreeType engine"); /* initialization of the post extension */ error = TT_Init_Post_Extension(engine); if (error) fatal_error("Couldn't initialize the post extension"); /* open the input file */ error = TT_Open_Face(engine, infile, &face); if (error) fatal_error("Unable to open input file `%s'", infile); /* load full post table */ error = TT_Load_PS_Names(face, &post); if (error) fatal_error("Unable to load post table"); /* get face properties */ TT_Get_Face_Properties(face, &properties); /* Load the instance. */ error = TT_New_Instance(face, &instance); if (error) fatal_error("Couldn't create instance"); error = TT_Set_Instance_Resolutions(instance, 600, 600); if (error) fatal_error("Error setting resolutions"); error = TT_Set_Instance_CharSize(instance, 120 * 64); if (error) fatal_error("Error setting character size"); error = TT_New_Glyph(face, &glyph); if (error) fatal_error("Couldn't create new glyph"); /* Get the requested cmap. */ num_cmap = TT_Get_CharMap_Count(face); for (cmapindex = 0; cmapindex < num_cmap; cmapindex++) { TT_Get_CharMap_ID(face, cmapindex, &platformID, &encodingID); if (platformID == pid && encodingID == eid) break; } if (cmapindex == num_cmap) { mesg("Possible platform and encoding ID pairs:"); for (cmapindex = 0; cmapindex < num_cmap; cmapindex++) { TT_Get_CharMap_ID(face, cmapindex, &platformID, &encodingID); mesg(" (%d, %d)", platformID, encodingID); } mesg ("\n"); fatal("No character map for given platform %d, encoding %d", pid, eid); } /* malloc for glyph data */ error = TT_Get_CharMap(face, cmapindex, &cmap); if (error) fatal_error("Cannot load cmap"); return TRUE; } /* * Get font infos: name, version, copyright. */ char* LookUp_Name(int index) { UShort platform, encoding, language, id; char* string; UShort string_len; UShort i, n; n = properties.num_Names; for (i = 0; i < n; i++) { TT_Get_Name_ID(face, i, &platform, &encoding, &language, &id); TT_Get_Name_String(face, i, &string, &string_len); if (id == index) break; } i = (string_len > NAMELEN) ? NAMELEN : string_len; string[i] = '\0'; return string; } /* * Load a glyph's outline and metrics. */ int LoadTrueTypeChar(int idx) { TT_Matrix scale = {(1 << 16) / 64, 0, 0, (1 << 16) / 64}; error = TT_Load_Glyph(instance, glyph, idx, TTLOAD_DEFAULT); if (error) fatal_error("Load glyph"); error = TT_Get_Glyph_Outline(glyph, &outline); if (error) fatal_error("Get glyph outlines"); TT_Transform_Outline(&outline, &scale); error = TT_Get_Glyph_Metrics(glyph, &metrics); if (error) fatal_error("Get glyph_metrics"); return TRUE; } /* * Get PS name of a glyph. */ char* PS_GlyphName(UShort idx, UShort code) { char *glyphname = ".notdef"; static char CJK_glyphname[8]; if (compact) { sprintf(CJK_glyphname, "cjk%04X", code); glyphname = CJK_glyphname; } else { if (idx) TT_Get_PS_Name(face, idx, &glyphname); } return glyphname; } /* * Header of Type 1 font. */ void PS_Head(FILE *out, int plane, EncVec* planeEV, char* font, int UID) { EVcRange* cRange = planeEV->codeRange; UShort numCR = planeEV->numCodeRanges; int cjk = 0, nGlyph = 0, irange; EVLow* pLow = cRange->low; UShort nLow = cRange->numLowRanges; int ipl, ilow, ich; int idx; UShort code; time_t curtime; struct tm *loctime; char text[NAMELEN]; char fam_name[NAMELEN]; char* version; char fullname[NAMELEN]; char copyright[NAMELEN]; /* Get the current time with local representation */ curtime = time(NULL); loctime = localtime(&curtime); /* Get font infos: family name, version, notice */ strcpy(fullname, LookUp_Name(6)); strcpy(fam_name, LookUp_Name(1)); strcpy(text, LookUp_Name(5)); version = &text[strcspn(text, "1234567890.")]; version[strspn(version, "1234567890.")] = '\0'; strcpy(copyright, LookUp_Name(0)); fprintf(out, "%%!FontType1-1.0: %s %s\n", font, version); fprintf(out, "%%%%Creator: %s, ", prog); fprintf(out, "%s\n", rcsid); fprintf(out, "%%%%CreationDate: %s", asctime(loctime)); Fputs("%%VMusage: 030000 030000"); Fputs("11 dict begin"); Fputs("/FontInfo 8 dict dup begin"); fprintf(out, "/version (%s) readonly def\n", version); fprintf(out, "/Copyright (%s) readonly def\n", copyright); fprintf(out, "/Notice (Plane %d) readonly def\n", plane); fprintf(out, "/FullName (%s) readonly def\n", fullname); fprintf(out, "/FamilyName (%s) readonly def\n", fam_name); Fputs("/Weight (Regular) readonly def"); Fputs("/ItalicAngle 0 def"); Fputs("/isFixedPitch false def"); /* Fputs("/UnderlineThickness 50 def"); */ Fputs("end readonly def"); fprintf(out, "/FontName /%s def\n", font); Fputs("/PaintType 0 def"); Fputs("/FontType 1 def"); if (fontShift == 0) Fputs("/FontMatrix [0.001 0 0 0.001 0 0] readonly def"); else fprintf(out, "/FontMatrix [0.001 0 0 0.001 0 %5.3f] readonly def\n", fontShift); Fputs("/Encoding 256 array"); Fputs("0 1 255 {1 index exch /.notdef put} for"); /* encoding vector */ for (irange = 0; irange < numCR; irange++, cRange++) { pLow = cRange->low; nLow = cRange->numLowRanges; for (ipl = cRange->high.start; ipl <= cRange->high.end; ipl++) { if (nLow == 0) { nGlyph = 0x100; for (ich = 0; ich <= 0xff; ich++) { code = ipl<<8 | ich; idx = TT_Char_Index(cmap, code); fprintf(out, "dup %d /%s put\n", ich, PS_GlyphName(idx, code)); } } else { for (ilow = 0; ilow < nLow; ilow++, pLow++) { if (!compact && !mapping) cjk = pLow->start; nGlyph += pLow->end - pLow->start + 1; for (ich = pLow->start; ich <= pLow->end; ich++, cjk++) { code = ipl<<8 | ich; idx = TT_Char_Index(cmap, code); fprintf(out, "dup %d /%s put\n", cjk, PS_GlyphName(idx, code)); if (mapping && cjk == 0xFF) goto done; } } } } } done: Fputs("readonly def"); Fputs("/FontBBox [0 -300 1000 1000] readonly def"); fprintf(out, "/UniqueID %d def\n",UID); Fputs("currentdict end"); Fputs("currentfile eexec"); Fputs("dup /Private 8 dict dup begin"); Fputs("/-| { string currentfile exch readstring pop } executeonly def"); Fputs("/|- { noaccess def } executeonly def"); Fputs("/| { noaccess put } executeonly def"); Fputs("/BlueValues [ ] |-"); Fputs("/ForceBold true def"); Fputs("/LanguageGroup 1 def"); Fputs("/RndStemUp false def"); Fputs("/MinFeature{16 16} |-"); /* Fputs("/password 5839 def"); */ fprintf(out, "/UniqueID %d def\n",UID); Fputs("/Subrs 4 array"); Fputs("dup 0 { 3 0 callothersubr pop pop setcurrentpoint return } |"); Fputs("dup 1 { 0 1 callothersubr return } |"); Fputs("dup 2 { 0 2 callothersubr return } |"); Fputs("dup 3 { return } |"); Fputs("|-"); fprintf(out, "2 index /CharStrings %d dict dup begin\n", nGlyph + 1); } /* * Tail of Type 1 font. */ void PS_Tail(FILE *out) { Fputs("/.notdef { 0 250 hsbw endchar } |-"); Fputs("end end readonly put noaccess put"); Fputs("dup /FontName get exch definefont pop"); Fputs("mark currentfile closefile"); } /* * Use the `rrcurveto' command on more than one `off' points. */ void PS_Curveto(FILE *out, TT_F26Dot6 x, TT_F26Dot6 y, int s, int e) { int N, i; TT_F26Dot6 sx[3], sy[3], cx[4], cy[4]; N = e - s + 1; cx[0] = lastpsx; cy[0] = lastpsy; if (s == e) { cx[1] = (2 * outline.points[s].x + outline.points[s - 1].x) / 3; cy[1] = (2 * outline.points[s].y + outline.points[s - 1].y) / 3; cx[2] = (2 * outline.points[s].x + x) / 3; cy[2] = (2 * outline.points[s].y + y) / 3; cx[3] = x; cy[3] = y; fprintf(out, "%d %d %d %d %d %d rrcurveto\n", Coord(cx[1] - cx[0]), Coord(cy[1] - cy[0]), Coord(cx[2] - cx[1]), Coord(cy[2] - cy[1]), Coord(cx[3] - cx[2]), Coord(cy[3] - cy[2])); } else { for(i = 0; i < N; i++) { sx[0] = (i == 0) ? outline.points[s - 1].x : (outline.points[i + s].x + outline.points[i + s - 1].x) / 2; sy[0] = (i == 0) ? outline.points[s - 1].y : (outline.points[i + s].y + outline.points[i + s - 1].y) / 2; sx[1] = outline.points[s + i].x; sy[1] = outline.points[s + i].y; sx[2] = (i == N - 1) ? x : (outline.points[s + i].x + outline.points[s + i + 1].x) / 2; sy[2] = (i == N - 1) ? y : (outline.points[s + i].y + outline.points[s + i + 1].y) / 2; cx[1] = (2 * sx[1] + sx[0]) / 3; cy[1] = (2 * sy[1] + sy[0]) / 3; cx[2] = (2 * sx[1] + sx[2]) / 3; cy[2] = (2 * sy[1] + sy[2]) / 3; cx[3] = sx[2]; cy[3] = sy[2]; fprintf(out, "%d %d %d %d %d %d rrcurveto\n", Coord(cx[1] - cx[0]), Coord(cy[1] - cy[0]), Coord(cx[2] - cx[1]), Coord(cy[2] - cy[1]), Coord(cx[3] - cx[2]), Coord(cy[3] - cy[2])); cx[0] = cx[3]; cy[0] = cy[3]; } } PS_LastPt(x, y); } #ifdef DEBUG int debug_Char_Code = 0xFFFF; FILE* tmpout; int showlabel = FALSE; int no_glyph = FALSE; int no_control= FALSE; #define Fputps(_msg_) fprintf(tmpout, "%s\n", _msg_) void tmp_out(FILE* tmpout) { int i, j; Fputps("%!PS"); Fputps("%%% CharString debugging program."); Fputps("%%% Generated by: ttf2pfb Revision: 1.13 $"); Fputps("%%% plot char-string (pathes defined in /cjkxxxx)"); Fputps(""); Fputps("%%% user-defined parameter"); Fputps("/scalefactor .6 def"); Fputps("%% 0 black, 1 white"); Fputps("/glyph-outline-gray 0 def"); Fputps("/control-point-gray 0.7 def"); Fputps(""); Fputps("%%% calculate shifts and scale factor"); Fputps("currentpagedevice /PageSize get dup"); Fputps("0 get /pagewidth exch def"); Fputps("1 get /pageheight exch def"); Fputps(""); fprintf(tmpout, "/llx %d.0 def /lly %d.0 def /urx %d.0 def /ury %d.0 def\n", Coord(metrics.bbox.xMin / 64), Coord(metrics.bbox.yMin / 64), Coord(metrics.bbox.xMax / 64), Coord(metrics.bbox.yMax / 64)); Fputps("/olwidth urx llx sub def"); Fputps("/olheight ury lly sub def"); Fputps(""); Fputps("/scale scalefactor pagewidth mul olwidth div def"); Fputps("/xshift pagewidth 1 scalefactor sub mul 2 div def"); Fputps("/yshift pageheight olheight scale mul sub 2 div def"); Fputps(""); Fputps("%% save old gray-scale value"); Fputps("/oldgray currentgray def"); Fputps(""); Fputps("%%% for point sequence label"); Fputps("/TimesRoman 8 selectfont"); Fputps("/i++ {i /i i 1 add def} def"); Fputps("/itos {4 string cvs} def"); Fputps("/point {2 copy i++ 3 1 roll 5 3 roll} def"); Fputps("/drawlabel"); Fputps(" {{moveto dup 0 eq {exit}"); Fputps(" {itos show} ifelse} loop pop} def"); Fputps("/nodrawlabel {clear} def"); Fputps("/i 0 def"); Fputps(""); Fputps("%%% for drawing glyph paths, redefine commands used in CharString"); Fputps("%% scaled to proper size"); Fputps("/addr {scale mul 3 -1 roll add 3 1 roll"); Fputps(" scale mul add exch 2 copy} def"); if (no_glyph) { Fputps("/rmoveto {addr pop pop point} def"); Fputps("/rlineto {addr pop pop point} def"); Fputps("/rrcurveto {8 4 roll addr 8 -2 roll addr 8 -2 roll addr"); Fputps(" 8 2 roll 6 {pop} repeat point} def"); } else { Fputps("/rmoveto {addr moveto point} def"); Fputps("/rlineto {addr lineto point} def"); Fputps("/rrcurveto {8 4 roll addr 8 -2 roll addr 8 -2 roll addr"); Fputps(" 8 2 roll curveto point} def"); } Fputps("/hsbw {pop pop"); Fputps(" xshift llx scale mul sub"); Fputps(" yshift lly scale mul sub} def"); Fputps("/endchar {stroke pop pop} def"); Fputps(""); Fputps("%%% for drawing control paths"); Fputps("/T {pop lly sub scale mul yshift add exch"); Fputps(" llx sub scale mul xshift add exch } def"); Fputps("/mt {T 2 copy moveto} def"); if (no_control) Fputps("/lt {T} def"); else Fputps("/lt {T 2 copy lineto} def"); Fputps(""); Fputps("1 setlinecap 1 setlinejoin"); Fputps("%%% draw control points and paths"); Fputps("control-point-gray setgray"); for (i = 0, j = 0; i < outline.n_contours; i++) { Fputps(""); fprintf(tmpout, "%d %d %d %d mt\n", j, Coord(outline.points[j].x), Coord(outline.points[j].y), outline.flags[j]); j++; for (; j <= outline.contours[i]; j++) fprintf(tmpout, "%d %d %d %d lt\n", j, Coord(outline.points[j].x), Coord(outline.points[j].y), outline.flags[j]); Fputps("closepath"); } Fputps("stroke"); if (showlabel && !no_control) Fputps("drawlabel"); else Fputps("nodrawlabel"); Fputps(""); Fputps("%%% draw glyph outlines"); Fputps("glyph-outline-gray setgray"); Fputps(""); } #endif /* * Construct CharString of a glyph. */ short PS_CharString(FILE *out, UShort char_Code) { int idx, i, j; UShort start_offpt, end_offpt = 0, fst; #if DEBUG FILE* oldout = out; int loop = 1; #endif if (force_enc == X) char_Code = JIS_to_SJIS(char_Code); idx = TT_Char_Index(cmap, char_Code); if (idx == 0) return FALSE; if (!LoadTrueTypeChar(idx)) fatal("Couldn't load character with index %d (code %d)", idx, char_Code); if (verbose) printf("0x%04x (%05d): %s\n", char_Code, idx, PS_GlyphName(idx, char_Code)); /* Begin string */ fprintf(out, "/%s {\n", PS_GlyphName(idx, char_Code)); #ifdef DEBUG if (char_Code == debug_Char_Code) { tmp_out(tmpout); out = tmpout; loop = 0; } for (; loop < 2; loop++) { #endif /* coordinates are all relative to (0,0) in FreeType */ fprintf(out, "0 %d hsbw\n", (int)(metrics.advance / 64)); /* Initialize ending contour point, relative coordinates */ lastpsx = lastpsy = 0; for (i = 0, j = 0; i < outline.n_contours; i++) { fst = j; PS_Moveto(outline.points[j].x, outline.points[j].y); j++; start_offpt = 0; /* start at least 1 */ /* * data pts for all contours stored in one array. * each round j init at last j + 1 */ /* * start_offpt means start of off points. * 0 means no off points in record. * N means the position of the off point. * end_offpt means the ending off point. * lastx, lasty is the last ON point from which Curve and Line * shall start. */ /* * start with j=0. into loop, j=1. * if pt[1] off, if start_offpt == 0, toggle start_offpt * next j=2. if on, now start_off != 0, run Curveto. * if pt[1] on, start_off == 0, will run Lineto. */ for (; j <= outline.contours[i]; j++) { if (!(outline.flags[j] & TT_Flag_On_Curve)) { if (!start_offpt) start_offpt = end_offpt = j; else end_offpt++; } else { /* On Curve */ if (start_offpt) { /* * start_offpt stuck at j, end_offpt++. * end_offpt - start_offpt gives no of off pts. * start_offpt gives start of sequence. * why need outline.xCoord[j] outline.yCoord[j]? */ PS_Curveto(out, outline.points[j].x, outline.points[j].y, start_offpt, end_offpt); start_offpt = 0; /* * also use start_offpt as indicator to save one variable!! * after curveto, reset condition. */ } else PS_Lineto(outline.points[j].x, outline.points[j].y); } } /* * looks like closepath fst = first, i.e. go back to first */ if (start_offpt) PS_Curveto(out, outline.points[fst].x, outline.points[fst].y, start_offpt, end_offpt); else Fputs("closepath"); } Fputs("endchar"); #if DEBUG out = oldout; } if (char_Code == debug_Char_Code) { if (showlabel && !no_glyph) Fputps("drawlabel"); else Fputps("nodrawlabel"); Fputps(""); Fputps("%%% end of drawing"); Fputps("oldgray setgray"); Fputps("showpage"); fclose(tmpout); } #endif Fputs(" } |-"); return TRUE; } /* * Get code ranges of an encoding scheme either from * the eVecMap or a code range file. */ EncVec* Get_EncVec(FILE *enc) { EncVec* encVec = NULL; EVcRange* cRange = NULL; EVLow* lByte = NULL; UShort numCR = 0, numLow = 0; int start, end; int buflen = LINELEN, numAssigned; char buf[LINELEN]; if (force_enc != 0) return eVecMap_force[force_enc]; if (enc == NULL && eVecMap[pid][eid] != NULL) return eVecMap[pid][eid]; /* parse each code range line */ while (fgets(buf, buflen, enc) != NULL) { if (buf[0] != '#' && buf[0] != '\n') { if (strrchr(buf,':') != NULL) { /* if there is no high value declared before low value */ if (lByte != NULL) { if (cRange == NULL) { /* default code range `0x00-0x00:' */ cRange = re_alloc(cRange, ++numCR * sizeof (EVcRange), "Get_EncVec"); cRange->high.start = cRange->high.end = 0; } /* Assign the last low value */ (cRange + numCR - 1)->low = lByte; (cRange + numCR - 1)->numLowRanges = numLow; } /* New high byte range */ cRange = re_alloc(cRange, ++numCR * sizeof (EVcRange), "Get_EncVec"); (cRange + numCR - 1)->numLowRanges = numLow = 0; lByte = NULL; /* Parse code range */ numAssigned = sscanf(buf, "%i %*40s %i", &start, &end); if (numAssigned <= 0 || numAssigned > 2) { mesg("%s: Get_EncVec: Invalid high code range.\n", prog); return NULL; } else { (cRange + numCR - 1)->high.start = start; if (numAssigned == 1) (cRange + numCR - 1)->high.end = start; else (cRange + numCR - 1)->high.end = end; } } else { lByte = re_alloc(lByte, ++numLow * sizeof (EVLow), "Get_EncVec"); numAssigned = sscanf(buf, "%i %*40s %i", &start, &end); if (numAssigned <= 0 || numAssigned > 2) { mesg("%s: Get_EncVec: Invalid long code range.\n", prog); return NULL; } else { (lByte + numLow - 1)->start = start; if (numAssigned == 1) (lByte + numLow - 1)->end = start; else (lByte + numLow - 1)->end = end; } } } } if (cRange == NULL) { cRange = re_alloc(cRange, ++numCR * sizeof (EVcRange), "Get_EncVec"); cRange->high.start = cRange->high.end = 0; cRange->numLowRanges = 0; } if (lByte != NULL) { (cRange + numCR - 1)->low = lByte; (cRange + numCR - 1)->numLowRanges = numLow; } encVec = re_alloc(encVec, 1 * sizeof (EncVec), "Get_EncVec"); encVec->numCodeRanges = numCR; encVec->codeRange = cRange; return encVec; } /* * Match code ranges by a font plane. */ EncVec* Get_PlaneEV(EncVec* encVec, int plane) { UShort numCR = encVec->numCodeRanges; EVcRange* cRange = encVec->codeRange; EncVec* encV = NULL; EVcRange* planeCR = NULL; EVLow* planeLow = NULL; UShort nCR = 0, nLow = 0; int icr; if (compact) { int iChar = 0; /* summed # of chars */ int nChar = (plane-1) * 256; /* the first char code ranges recorded */ int recording = 0; /* if compact, plane starts from 1 to be */ /* compatible with the CJK package */ if (plane < 1 || plane > 99) fatal("Get_PlaneEV: Given plane out of range"); for (icr = 0; icr < numCR; icr++, cRange++) { UShort numLow = cRange->numLowRanges; int ipl; for (ipl = cRange->high.start; ipl <= cRange->high.end; ipl++) { EVLow* pLow = cRange->low; int ilow; if (recording) { /* if we have made a hit */ if (planeLow != NULL) { /* if low byte range has not been saved */ (planeCR + nCR - 1)->low = planeLow; (planeCR + nCR - 1)->numLowRanges = nLow; planeLow = NULL; } /* each new plane starts a EVcRange if */ /* iChar is still less than nChar */ if (iChar <= nChar) { planeCR = re_alloc(planeCR, ++nCR * sizeof (EVcRange), "Get_PlaneEV"); (planeCR + nCR - 1)->high.start = (planeCR + nCR - 1)->high.end = ipl; (planeCR + nCR - 1)->numLowRanges = nLow = 0; } } /* scan each low byte range */ for (ilow = 0; ilow < (numLow == 0 ? 1 : numLow); ilow++, pLow++) { int start, end, nLowChar; if (numLow == 0) { /* default range */ start = 0x0; end = 0xff; } else { start = pLow->start; end = pLow->end; } nLowChar = end - start + 1; if (iChar + nLowChar > nChar) { /* a hit! */ int bchar = start + nChar - iChar; if (planeCR == NULL) { /* the first time code range is recorded */ planeCR = re_alloc(planeCR, ++nCR * sizeof (EVcRange), "Get_PlaneEV"); (planeCR + nCR - 1)->high.start = ipl; (planeCR + nCR - 1)->high.end = ipl; } /* adjust range boundary */ if (recording == 0) start = bchar; else end = bchar; nChar += 0xff; /* recording starts */ recording++; } iChar += nLowChar; /* next range */ if (recording) { /* a new low range */ if (iChar <= nChar) { planeLow = re_alloc(planeLow, ++nLow * sizeof (EVLow), "Get_PlaneEV"); (planeLow + nLow - 1)->start = start; (planeLow + nLow - 1)->end = end; } if (recording > 1 || iChar > nChar) { /* beyond recording range */ (planeCR + nCR - 1)->numLowRanges = nLow; (planeCR + nCR - 1)->low = planeLow; encV = re_alloc(encV, 1 * sizeof (EncVec), "Get_PlaneEV"); encV->numCodeRanges = nCR; encV->codeRange = planeCR; return encV; } } } } } /* we must finalize the ranges */ if (recording) { (planeCR + nCR - 1)->numLowRanges = nLow; (planeCR + nCR - 1)->low = planeLow; encV = re_alloc(encV, 1 * sizeof (EncVec), "Get_PlaneEV"); encV->numCodeRanges = nCR; encV->codeRange = planeCR; return encV; } } else { for (icr = 0; icr < numCR; icr++, cRange++) { if (plane >= cRange->high.start && plane <= cRange->high.end) { encV = re_alloc(encV, 1 * sizeof (EncVec), "Get_PlaneEV"); planeCR = re_alloc(planeCR, 1 * sizeof (EVcRange), "Get_PlaneEV"); planeCR->high.start = planeCR->high.end = plane; planeCR->numLowRanges = cRange->numLowRanges; planeCR->low = cRange->low; encV->numCodeRanges = 1; encV->codeRange = planeCR; return encV; } } } return NULL; } /* * The main subroutine for generating Type 1 fonts. * One subfont per call. */ short Generate_Font(FILE *out, int plane, FILE *enc, char *fname, int UID) { EncVec* encVec = Get_EncVec(enc); EncVec* planeEncVec; EVcRange* cRange; UShort numCR; UShort code; int ilow, iplan, ichar, irange; if (verbose) printf("Generating fonts...\n\n"); if (encVec == NULL) return FALSE; if (mapping) planeEncVec = encVec; else if ((planeEncVec = Get_PlaneEV(encVec, plane)) == NULL) { mesg("%s: Can't find encoding vector for the font plane 0x%X.\n", prog, plane); return FALSE; } /* Header of Type1 font */ PS_Head(out, plane, planeEncVec, fname, UID); numCR = planeEncVec->numCodeRanges; cRange = planeEncVec->codeRange; for (irange = 0; irange < numCR; irange++, cRange++) { EVLow* pLow = cRange->low; UShort nLow = cRange->numLowRanges; for (iplan = cRange->high.start; iplan <= cRange->high.end; iplan++) { if (nLow == 0) { for (ichar = 0; ichar <= 0xff; ichar++) { code = iplan << 8 | ichar; PS_CharString(out, code); } } else { for (ilow = 0; ilow < nLow; ilow++, pLow++) { for (ichar = pLow->start; ichar <= pLow->end; ichar++) { code = iplan << 8 | ichar; PS_CharString(out, code); } } } } } PS_Tail(out); return TRUE; } /* * Main: process options, file I/O, etc. */ int main(int argc, char *argv[]) { char *infile, *outfile, *encFile, *fname = "UNKNOWN"; FILE *out, *enc; int result, plane = 0, UID = 4999999; if ((prog = strrchr(argv[0], '/'))) prog++; else prog = argv[0]; /* set up known encoding vectors */ Known_Encodings(); out = stdout; enc = NULL; infile = outfile = encFile = NULL; argc--; argv++; while (argc > 0) { if (argv[0][0] == '-') { switch (argv[0][1]) { case 'v': case 'V': verbose = TRUE; break; case 'c': case 'C': compact = TRUE; break; case 'k': case 'K': keep = TRUE; break; case 'm': case 'M': mapping = TRUE; break; case 'p': case 'P': result = argv[0][2]; argc--; argv++; if (result == 'i' || result == 'I') { /* Set the platform ID. Assumed upper bound is 64 */ if ((pid = atoi(argv[0])) < 0 || pid > 64) /* Check the platform and encoding IDs. */ fatal("Invalid platform ID %d", pid); } else if (result == 'l' || result == 'L') { result = 0; while (argv[0][result] == '0' && toupper(argv[0][result + 1]) != 'X') result++; /* no octal number */ sscanf(&argv[0][result], "%i", &plane); } break; case 'e': case 'E': result = argv[0][2]; argc--; argv++; if (result == 'i' || result == 'I') { /* Set the encoding ID. */ if ((eid = atoi(argv[0])) < 0 || eid > 64) fatal("Invalid encoding ID %d", eid); } else if (result == 'n' || result == 'N') encFile = argv[0]; break; case 'u': case 'U': argc--; argv++; UID = atoi(argv[0]); break; case 'f': case 'F': result = argv[0][2]; argc--; argv++; if (result == '\0') fname = argv[0]; else if (result == 'o' || result == 'O') { switch (argv[0][0]) { case 'g': case 'G': force_enc = GB; break; case 'k': case 'K': force_enc = KS; break; case 'b': case 'B': force_enc = Big5; break; case 'j': case 'J': result = argv[0][1]; if (result == 'o' || result == 'O') force_enc = Johab; else force_enc = JIS; break; case 's': case 'S': force_enc = SJIS; break; case 'x': case 'X': force_enc = X; } } break; case 't': /* Get the TTF file name. */ case 'T': argc--; argv++; infile = argv[0]; break; case 'o': /* Set the output file name. */ case 'O': argc--; argv++; outfile = argv[0]; break; case 's': /* shift font bbox up or down */ case 'S': argc--; argv++; sscanf(argv[0], "%f", &fontShift); break; #ifdef DEBUG case 'd': /* character code for debugging */ case 'D': argc--; argv++; sscanf(argv[0], "%i", &debug_Char_Code); tmpout = fopen("ch-debug.ps", "wt"); mesg("You have specified the character code 0x%04x for debugging.\n", debug_Char_Code); mesg("A PostScript program named `ch-debug.ps' will be created.\n"); break; case 'l': case 'L': showlabel = TRUE; break; case 'n': case 'N': result = argv[0][2]; if (result == 'g' || result == 'G') no_glyph = TRUE; else if (result == 'c' || result == 'C') no_control = TRUE; break; #endif default: Usage(1); } } else /* Set the input file name. */ infile = argv[0]; argc--; argv++; } /* Open the output file if specified. */ if (outfile != NULL) /* Attempt to open the output file. */ if ((out = fopen(outfile, "wt")) == 0) { fatal("Unable to open the output file `%s'", outfile); exit(FAILURE); } /* Validate the values passed on the command line. */ if (infile == NULL) { mesg("%s: No input TTF file provided\n", prog); Usage(1); } if (encFile != NULL) { if ((enc = fopen(encFile, "rt")) == 0) fatal("No input code range file"); } /* Initialize font engine */ if (!Init_Font_Engine(infile)) { if (out != stdout) { fclose(out); if (!keep) (void)unlink(outfile); } exit(FAILURE); } /* Generate the disassembled PFB font from the TrueType font */ if (Generate_Font(out, plane, enc, fname, UID)) result = 0; else result = 2; if (out != stdout) { fclose(out); if (result != 0) { mesg("%s: An error occurred while generating the font", prog); if (!keep) (void)unlink(outfile); } } TT_Close_Face(face); TT_Done_FreeType(engine); exit(result); return 0; /* never reached */ } /* end of ttf2pfb.c */