/* * Copyright 1996, 1997, 1998, 1999 Computing Research Labs, * New Mexico State University * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef lint #ifdef __GNUC__ static char rcsid[] __attribute__ ((unused)) = "Id: ttf2bdf.c,v 1.25 1999/10/21 16:31:54 mleisher Exp $"; #else static char rcsid[] = "Id: ttf2bdf.c,v 1.25 1999/10/21 16:31:54 mleisher Exp $"; #endif #endif #include #ifdef WIN32 #include #else #include #include #endif #include #include "freetype.h" /* * Include the remapping support. */ #include "remap.h" /************************************************************************** * * Macros. * **************************************************************************/ /* * The version of ttf2bdf. */ #define TTF2BDF_VERSION "2.8" /* * Set the default values used to generate a BDF font. */ #ifndef DEFAULT_PLATFORM_ID #define DEFAULT_PLATFORM_ID 3 #endif #ifndef DEFAULT_ENCODING_ID #define DEFAULT_ENCODING_ID 1 #endif #ifndef DEFAULT_POINT_SIZE #define DEFAULT_POINT_SIZE 12 #endif #ifndef DEFAULT_RESOLUTION #define DEFAULT_RESOLUTION 100 #endif /* * Used as a fallback for XLFD names where the character set/encoding can not * be determined. */ #ifndef DEFAULT_XLFD_CSET #define DEFAULT_XLFD_CSET "-FontSpecific-0" #endif /* * nameID macros for getting strings from the TT font. */ #define TTF_COPYRIGHT 0 #define TTF_TYPEFACE 1 #define TTF_PSNAME 6 #ifndef MAX #define MAX(h,i) ((h) > (i) ? (h) : (i)) #endif #ifndef MIN #define MIN(l,o) ((l) < (o) ? (l) : (o)) #endif /************************************************************************** * * General globals set from command line. * **************************************************************************/ /* * The program name. */ static char *prog; /* * The flag indicating whether messages should be printed or not. */ static int verbose = 0; /* * Flags used when loading glyphs. */ static int load_flags = TTLOAD_SCALE_GLYPH | TTLOAD_HINT_GLYPH; /* * The default platform and encoding ID's. */ static int pid = DEFAULT_PLATFORM_ID; static int eid = DEFAULT_ENCODING_ID; /* * Default point size and resolutions. */ static int point_size = DEFAULT_POINT_SIZE; static int hres = DEFAULT_RESOLUTION; static int vres = DEFAULT_RESOLUTION; /* * The user supplied foundry name to use in the XLFD name. */ static char *foundry_name = 0; /* * The user supplied typeface name to use in the XLFD name. */ static char *face_name = 0; /* * The user supplied weight name to use in the XLFD name. */ static char *weight_name = 0; /* * The user supplied slant name to use in the XLFD name. */ static char *slant_name = 0; /* * The user supplied width name to use in the XLFD name. */ static char *width_name = 0; /* * The user supplied additional style name to use in the XLFD name. */ static char *style_name = 0; /* * The user supplied spacing (p = proportional, c = character cell, * m = monospace). */ static int spacing = 0; /* * The dash character to use in the names retrieved from the font. Default is * the space. */ static int dashchar = ' '; /* * Flag, bitmask, and max code for generating a subset of the glyphs in a font. */ static int do_subset = 0; static unsigned short maxcode; static unsigned long subset[2048]; /* * The flag that indicates the remapping table should be used to * reencode the font. */ static int do_remap = 0; /************************************************************************** * * Internal globals. * **************************************************************************/ /* * Structure used for calculating the font bounding box as the glyphs are * generated. */ typedef struct { short minlb; short maxlb; short maxrb; short maxas; short maxds; short rbearing; } bbx_t; static bbx_t bbx; /* * The buffer used to transfer the temporary file to the actual output file. */ #define TTF2BDF_IOBUFSIZ 8192 static char iobuf[TTF2BDF_IOBUFSIZ]; /* * The Units Per Em value used in numerous places. */ static TT_UShort upm; /* * A flag indicating if a CMap was found or not. */ static TT_UShort nocmap; /* * The scaling factor needed to compute the SWIDTH (scalable width) value * for BDF glyphs. */ static double swscale; /* * Mac encoding names used when creating the BDF XLFD font name. */ static char *mac_encodings[] = { "-MacRoman-0", "-MacJapanese-0", "-MacChinese-0", "-MacKorean-0", "-MacArabic-0", "-MacHebrew-0", "-MacGreek-0", "-MacRussian-0", "-MacRSymbol-0", "-MacDevanagari-0", "-MacGurmukhi-0", "-MacGujarati-0", "-MacOriya-0", "-MacBengali-0", "-MacTamil-0", "-MacTelugu-0", "-MacKannada-0", "-MacMalayalam-0", "-MacSinhalese-0", "-MacBurmese-0", "-MacKhmer-0", "-MacThai-0", "-MacLaotian-0", "-MacGeorgian-0", "-MacArmenian-0", "-MacMaldivian-0", "-MacTibetan-0", "-MacMongolian-0", "-MacGeez-0", "-MacSlavic-0", "-MacVietnamese-0","-MacSindhi-0", "-MacUninterp-0" }; static int num_mac_encodings = sizeof(mac_encodings) / sizeof(mac_encodings[0]); /* * ISO encoding names used when creating the BDF XLFD font name. */ static char *iso_encodings[] = { "-ASCII-0", "-ISO10646-0", "-ISO8859-1" }; static int num_iso_encodings = sizeof(iso_encodings) / sizeof(iso_encodings[0]); /* * Microsoft encoding names used when creating the BDF XLFD font name. */ static char *ms_encodings[] = { "-Symbol-0", "-ISO10646-1", "-ShiftJIS-0", "-GB2312.1980-0", "-Big5-0", "-KSC5601.1987-0", "-KSC5601.1992-0" }; static int num_ms_encodings = sizeof(ms_encodings) / sizeof(ms_encodings[0]); /* * The propery names for all the XLFD properties. */ static char *xlfd_props[] = { "FOUNDRY", "FAMILY_NAME", "WEIGHT_NAME", "SLANT", "SETWIDTH_NAME", "ADD_STYLE_NAME", "PIXEL_SIZE", "POINT_SIZE", "RESOLUTION_X", "RESOLUTION_Y", "SPACING", "AVERAGE_WIDTH", "CHARSET_REGISTRY", "CHARSET_ENCODING", }; /************************************************************************** * * Freetype globals. * **************************************************************************/ static TT_Engine engine; static TT_Face face; static TT_Face_Properties properties; static TT_Instance instance; static TT_Glyph glyph; static TT_Glyph_Metrics metrics; static TT_Instance_Metrics imetrics; static TT_Raster_Map raster; static TT_CharMap cmap; /************************************************************************** * * Freetype related code. * **************************************************************************/ /* * A generic routine to get a name from the TT name table. This routine * always looks for English language names and checks three possibilities: * 1. English names with the MS Unicode encoding ID. * 2. English names with the MS unknown encoding ID. * 3. English names with the Apple Unicode encoding ID. * * The particular name ID mut be provided (e.g. nameID = 0 for copyright * string, nameID = 6 for Postscript name, nameID = 1 for typeface name. * * If the `dash' flag is non-zero, all dashes (-) in the name will be replaced * with the character passed. * * Returns the number of bytes added. */ static int #ifdef __STDC__ ttf_get_english_name(char *name, int nameID, int dash) #else ttf_get_english_name(name, nameID, dash) char *name; int nameID, dash; #endif { TT_UShort slen; int i, j, encid, nrec; unsigned short nrPlatformID, nrEncodingID, nrLanguageID, nrNameID; char *s; nrec = TT_Get_Name_Count(face); for (encid = 1, j = 0; j < 2; j++, encid--) { /* * Locate one of the MS English font names. */ for (i = 0; i < nrec; i++) { TT_Get_Name_ID(face, i, &nrPlatformID, &nrEncodingID, &nrLanguageID, &nrNameID); if (nrPlatformID == 3 && nrEncodingID == encid && nrNameID == nameID && (nrLanguageID == 0x0409 || nrLanguageID == 0x0809 || nrLanguageID == 0x0c09 || nrLanguageID == 0x1009 || nrLanguageID == 0x1409 || nrLanguageID == 0x1809)) { TT_Get_Name_String(face, i, &s, &slen); break; } } if (i < nrec) { /* * Found one of the MS English font names. The name is by * definition encoded in Unicode, so copy every second byte into * the `name' parameter, assuming there is enough space. */ for (i = 1; s != 0 && i < slen; i += 2) { if (dash) *name++ = (s[i] == '-' || s[i] == ' ') ? dash : s[i]; else if (s[i] == '\r' || s[i] == '\n') { if (s[i] == '\r' && i + 2 < slen && s[i + 2] == '\n') i += 2; *name++ = ' '; *name++ = ' '; } else *name++ = s[i]; } *name = 0; return (slen >> 1); } } /* * No MS English name found, attempt to find an Apple Unicode English * name. */ for (i = 0; i < nrec; i++) { TT_Get_Name_ID(face, i, &nrPlatformID, &nrEncodingID, &nrLanguageID, &nrNameID); if (nrPlatformID == 0 && nrLanguageID == 0 && nrNameID == nameID) { TT_Get_Name_String(face, i, &s, &slen); break; } } if (i < nrec) { /* * Found the Apple Unicode English name. The name is by definition * encoded in Unicode, so copy every second byte into the `name' * parameter, assuming there is enough space. */ for (i = 1; s != 0 && i < slen; i += 2) { if (dash) *name++ = (s[i] == '-' || s[i] == ' ') ? dash : s[i]; else if (s[i] == '\r' || s[i] == '\n') { if (s[i] == '\r' && i + 2 < slen && s[i + 2] == '\n') i += 2; *name++ = ' '; *name++ = ' '; } else *name++ = s[i]; } *name = 0; return (slen >> 1); } return 0; } /************************************************************************** * * General code. * **************************************************************************/ /* * Create an XLFD name. Assumes there is enough space in the string passed * to fit a reasonably long XLFD name into, up to the 256 byte maximum. */ static void #ifdef __STDC__ make_xlfd_name(char *name, TT_Long awidth, int ismono) #else make_xlfd_name(name, awidth, ismono) char *name; TT_Long awidth; int ismono; #endif { TT_Long i; TT_ULong val; char *r, *e; double dr, dp; /* * Default the foundry name to "FreeType" in honor of the project and * because the foundry name is too difficult to automatically determine * from the names in TT fonts. But the user can provide his own. */ if (foundry_name == 0) { (void) strcpy(name, "-FreeType"); name += 9; } else { *(name++)='-'; strcpy(name,foundry_name); name+=strlen(foundry_name); } /* * Add the typeface name from the font. The fallback default will be * "Unknown". */ *name++ = '-'; if (face_name == 0) { if((i = ttf_get_english_name(name, TTF_TYPEFACE, dashchar))) name += i; else { (void) strcpy(name, "Unknown"); name += 7; } } else { (void) strcpy(name, face_name); name += strlen(face_name); } /* * Add the weight name. The default will be "Medium". */ if (weight_name != 0) { sprintf(name, "-%s", weight_name); name += strlen(weight_name) + 1; } else { if (properties.os2->fsSelection & 0x20) { (void) strcpy(name, "-Bold"); name += 5; } else { (void) strcpy(name, "-Medium"); name += 7; } } /* * Add the slant name. The default will be 'R'. */ if (slant_name) { sprintf(name, "-%s", slant_name); name += strlen(slant_name) + 1; } else { *name++ = '-'; if (properties.os2->fsSelection & 0x01) *name++ = 'I'; else *name++ = 'R'; } /* * Default the setwidth name to "Normal" but user can specify one. */ if (width_name == 0) { (void) strcpy(name, "-Normal"); name += 7; } else { *(name++)='-'; strcpy(name,width_name); name+=strlen(width_name); } /* * Default the additional style name to NULL but user can specify one. */ *name++ = '-'; if (style_name != 0) { strcpy(name,style_name); name+=strlen(style_name); } /* * Determine the pixel size from the point size and resolution. */ dr = (double) vres; dp = (double) (point_size * 10); val = (unsigned long) (((dp * dr) / 722.7) + 0.5); /* * Set the pixel size, point size, and resolution. */ sprintf(name, "-%ld-%d-%d-%d", val, point_size * 10, hres, vres); name += strlen(name); switch (spacing) { case 'p': case 'P': spacing = 'P'; break; case 'm': case 'M': spacing = 'M'; break; case 'c': case 'C': spacing = 'C'; break; default: spacing = 0; break; } /* * Set the spacing. */ if (!spacing) spacing = (ismono) ? 'M' : 'P'; *name++ = '-'; *name++ = spacing; /* * Add the average width. */ sprintf(name, "-%ld", awidth); name += strlen(name); /* * Check to see if the remapping table specified a registry and encoding * and use those if they both exist. */ ttf2bdf_remap_charset(&r, &e); if (r != 0 && e != 0) { sprintf(name, "-%s-%s", r, e); return; } /* * If the cmap for the platform and encoding id was not found, or the * platform id is unknown, assume the character set registry and encoding * are the XLFD default. */ if (nocmap || pid > 3) (void) strcpy(name, DEFAULT_XLFD_CSET); else { /* * Finally, determine the character set registry and encoding from the * platform and encoding ID. */ switch (pid) { case 0: /* * Apple Unicode platform, so "Unicode-2.0" is the default. */ (void) strcpy(name, "-Unicode-2.0"); break; case 1: /* * Macintosh platform, so choose from the Macintosh encoding * strings. */ if (eid < 0 || eid >= num_mac_encodings) (void) strcpy(name, DEFAULT_XLFD_CSET); else (void) strcpy(name, mac_encodings[eid]); break; case 2: /* * ISO platform, so choose from the ISO encoding strings. */ if (eid < 0 || eid >= num_iso_encodings) (void) strcpy(name, DEFAULT_XLFD_CSET); else (void) strcpy(name, iso_encodings[eid]); break; case 3: /* * Microsoft platform, so choose from the MS encoding strings. */ if (eid < 0 || eid >= num_ms_encodings) (void) strcpy(name, DEFAULT_XLFD_CSET); else (void) strcpy(name, ms_encodings[eid]); break; } } } static int #ifdef __STDC__ generate_font(FILE *out, char *iname, char *oname) #else generate_font(out, iname, oname) FILE *out; char *iname, *oname; #endif { int eof, ismono, i; FILE *tmp; TT_Short maxx, maxy, minx, miny, xoff, yoff, dwidth, swidth; TT_Short y_off, x_off; TT_UShort sx, sy, ex, ey, wd, ht; TT_Long code, idx, ng, aw; TT_UShort remapped_code; unsigned char *bmap; double dw; char *xp, xlfd[256]; char *tmpdir, tmpfile[BUFSIZ]; /* * Open a temporary file to store the bitmaps in until the exact number * of bitmaps are known. */ if ((tmpdir = getenv("TMPDIR")) == 0) tmpdir = "/tmp"; sprintf(tmpfile, "%s/ttf2bdf%ld", tmpdir, (long) getpid()); if ((tmp = fopen(tmpfile, "w")) == 0) { fprintf(stderr, "%s: unable to open temporary file '%s'.\n", prog, tmpfile); return -1; } /* * Calculate the scale factor for the SWIDTH field. */ swscale = ((double) vres) * ((double) point_size); /* * Calculate the font bounding box again so enough storage for the largest * bitmap can be allocated. */ minx = (properties.header->xMin * imetrics.x_ppem) / upm; miny = (properties.header->yMin * imetrics.y_ppem) / upm; maxx = (properties.header->xMax * imetrics.x_ppem) / upm; maxy = (properties.header->yMax * imetrics.y_ppem) / upm; maxx -= minx; ++maxx; maxy -= miny; ++maxy; /* * Initialize the flag that tracks if the font is monowidth or not and * initialize the glyph width variable that is used for testing for a * monowidth font. */ wd = 0xffff; ismono = 1; /* * Use the upward flow because the version of FreeType being used when * this was written did not support TT_Flow_Down. This insures that this * routine will not mess up if TT_Flow_Down is implemented at some point. */ raster.flow = TT_Flow_Up; raster.width = maxx; raster.rows = maxy; raster.cols = (maxx + 7) >> 3; raster.size = raster.cols * raster.rows; raster.bitmap = (void *) malloc(raster.size); for (ng = code = 0, eof = 0, aw = 0; eof != EOF && code < 0xffff; code++) { /* * If a remap is indicated, attempt to remap the code. If a remapped * code is not found, then skip generating the glyph. */ remapped_code = (TT_UShort) code; if (do_remap && !ttf2bdf_remap(&remapped_code)) continue; /* * If a subset is being generated and the code is greater than the max * code of the subset, break out of the loop to avoid doing any more * work. */ if (do_subset && remapped_code > maxcode) break; /* * If a subset is being generated and the index is not in the subset * bitmap, just continue. */ if (do_subset && !(subset[remapped_code >> 5] & (1 << (remapped_code & 31)))) continue; if (nocmap) { if (code >= properties.num_Glyphs) /* * At this point, all the glyphs are done. */ break; idx = code; } else idx = TT_Char_Index(cmap, code); /* * If the glyph could not be loaded for some reason, or a subset is * being generated and the index is not in the subset bitmap, just * continue. */ if (idx <= 0 || TT_Load_Glyph(instance, glyph, idx, load_flags)) continue; (void) TT_Get_Glyph_Metrics(glyph, &metrics); /* * Clear the raster bitmap. */ (void) memset((char *) raster.bitmap, 0, raster.size); /* * Grid fit to determine the x and y offsets that will force the * bitmap to fit into the storage provided. */ xoff = (63 - metrics.bbox.xMin) & -64; yoff = (63 - metrics.bbox.yMin) & -64; /* * If the bitmap cannot be generated, simply continue. */ if (TT_Get_Glyph_Bitmap(glyph, &raster, xoff, yoff)) continue; /* * Determine the DWIDTH (device width, or advance width in TT terms) * and the SWIDTH (scalable width) values. */ dwidth = metrics.advance >> 6; dw = (double) dwidth; swidth = (TT_Short) ((dw * 72000.0) / swscale); /* * Determine the actual bounding box of the glyph bitmap. Do not * forget that the glyph is rendered upside down! */ sx = ey = 0xffff; sy = ex = 0; bmap = (unsigned char *) raster.bitmap; for (miny = 0; miny < raster.rows; miny++) { for (minx = 0; minx < raster.width; minx++) { if (bmap[(miny * raster.cols) + (minx >> 3)] & (0x80 >> (minx & 7))) { if (minx < sx) sx = minx; if (minx > ex) ex = minx; if (miny > sy) sy = miny; if (miny < ey) ey = miny; } } } /* * If the glyph is actually an empty bitmap, set the size to 0 all * around. */ if (sx == 0xffff && ey == 0xffff && sy == 0 && ex == 0) sx = ex = sy = ey = 0; /* * Increment the number of glyphs generated. */ ng++; /* * Test to see if the font is going to be monowidth or not by * comparing the current glyph width against the last one. */ if (ismono && (ex - sx) + 1 != wd) ismono = 0; /* * Adjust the font bounding box. */ wd = (ex - sx) + 1; ht = (sy - ey) + 1; x_off = sx - (xoff >> 6); y_off = ey - (yoff >> 6); bbx.maxas = MAX(bbx.maxas, ht + y_off); bbx.maxds = MAX(bbx.maxds, -y_off); bbx.rbearing = wd + x_off; bbx.maxrb = MAX(bbx.maxrb, bbx.rbearing); bbx.minlb = MIN(bbx.minlb, x_off); bbx.maxlb = MAX(bbx.maxlb, x_off); /* * Add to the average width accumulator. */ aw += wd; /* * Print the bitmap header. */ fprintf(tmp, "STARTCHAR %04lX\nENCODING %ld\n", code, (long) remapped_code); fprintf(tmp, "SWIDTH %hd 0\n", swidth); fprintf(tmp, "DWIDTH %hd 0\n", dwidth); fprintf(tmp, "BBX %hd %hd %hd %hd\n", wd, ht, x_off, y_off); /* * Check for an error return here in case the temporary file system * fills up or the file is deleted while it is being used. */ eof = fprintf(tmp, "BITMAP\n"); /* * Now collect the bits so they can be printed. */ for (miny = sy; eof != EOF && miny >= ey; miny--) { for (idx = 0, minx = sx; eof != EOF && minx <= ex; minx++) { if (minx > sx && ((minx - sx) & 7) == 0) { /* * Print the next byte. */ eof = fprintf(tmp, "%02lX", idx & 0xff); idx = 0; } if (bmap[(miny * raster.cols) + (minx >> 3)] & (0x80 >> (minx & 7))) idx |= 0x80 >> ((minx - sx) & 7); } if (eof != EOF) /* * Because of the structure of the loop, the last byte should * always be printed. */ fprintf(tmp, "%02lX\n", idx & 0xff); } if (eof != EOF) fprintf(tmp, "ENDCHAR\n"); } fclose(tmp); /* * If a write error occured, delete the temporary file and issue an error * message. */ if (eof == EOF) { (void) unlink(tmpfile); fprintf(stderr, "%s: problem writing to temporary file '%s'.\n", prog, tmpfile); if (raster.size > 0) free((char *) raster.bitmap); return -1; } /* * If no characters were generated, just unlink the temp file and issue a * warning. */ if (ng == 0) { (void) unlink(tmpfile); fprintf(stderr, "%s: no glyphs generated from '%s'.\n", prog, iname); if (raster.size > 0) free((char *) raster.bitmap); return -1; } /* * Reopen the temporary file so it can be copied to the actual output * file. */ if ((tmp = fopen(tmpfile, "r")) == 0) { /* * Unable to open the file for read, so attempt to delete it and issue * an error message. */ (void) unlink(tmpfile); fprintf(stderr, "%s: unable to open temporary file '%s' for read.\n", prog, tmpfile); if (raster.size > 0) free((char *) raster.bitmap); return -1; } /* * Free up the raster storage. */ if (raster.size > 0) free((char *) raster.bitmap); /* * Calculate the average width. */ aw = (TT_Long) ((((double) aw / (double) ng) + 0.5) * 10.0); /* * Generate the XLFD font name. */ make_xlfd_name(xlfd, aw, ismono); /* * Start writing the font out. */ fprintf(out, "STARTFONT 2.1\n"); /* * Add the vanity comments. */ fprintf(out, "COMMENT\n"); fprintf(out, "COMMENT Converted from TrueType font \"%s\" by \"%s %s\".\n", iname, prog, TTF2BDF_VERSION); fprintf(out, "COMMENT\n"); fprintf(out, "FONT %s\n", xlfd); fprintf(out, "SIZE %d %d %d\n", point_size, hres, vres); /* * Generate the font bounding box. */ fprintf(out, "FONTBOUNDINGBOX %hd %hd %hd %hd\n", bbx.maxrb - bbx.minlb, bbx.maxas + bbx.maxds, bbx.minlb, -bbx.maxds); /* * Print the properties. */ fprintf(out, "STARTPROPERTIES %hd\n", 19); /* * Print the font properties from the XLFD name. */ for (i = 0, xp = xlfd; i < 14; i++) { /* * Print the XLFD property name. */ fprintf(out, "%s ", xlfd_props[i]); /* * Make sure the ATOM properties are wrapped in double quotes. */ if (i < 6 || i == 10 || i > 11) putc('"', out); /* * Skip the leading '-' in the XLFD name. */ xp++; /* * Skip until the next '-' or NULL. */ for (; *xp && *xp != '-'; xp++) putc(*xp, out); /* * Make sure the ATOM properties are wrapped in double quotes. */ if (i < 6 || i == 10 || i > 11) putc('"', out); putc('\n', out); } /* * Make sure to add the FONT_ASCENT and FONT_DESCENT properties * because X11 can not live without them. */ fprintf(out, "FONT_ASCENT %hd\nFONT_DESCENT %hd\n", (properties.horizontal->Ascender * imetrics.y_ppem) / upm, -((properties.horizontal->Descender * imetrics.y_ppem) / upm)); /* * Get the copyright string from the font. */ (void) ttf_get_english_name(xlfd, TTF_COPYRIGHT, 0); fprintf(out, "COPYRIGHT \"%s\"\n", xlfd); /* * Last, print the two user-defined properties _TTF_FONTFILE and * _TTF_PSNAME. _TTF_FONTFILE provides a reference to the original TT * font file which some systems can take advantage of, and _TTF_PSNAME * provides the Postscript name of the font if it exists. */ (void) ttf_get_english_name(xlfd, TTF_PSNAME, 0); fprintf(out, "_TTF_FONTFILE \"%s\"\n_TTF_PSNAME \"%s\"\n", iname, xlfd); fprintf(out, "ENDPROPERTIES\n"); /* * Print the actual number of glyphs to the output file. */ eof = fprintf(out, "CHARS %ld\n", ng); /* * Copy the temporary file to the output file. */ while (eof != EOF && (ng = fread(iobuf, 1, TTF2BDF_IOBUFSIZ, tmp))) { if (fwrite(iobuf, 1, ng, out) == 0) eof = EOF; } /* * Close the temporary file and delete it. */ fclose(tmp); (void) unlink(tmpfile); /* * If an error occured when writing to the output file, issue a warning * and return. */ if (eof == EOF) { fprintf(stderr, "%s: problem writing to output file '%s'.\n", prog, oname); if (raster.size > 0) free((char *) raster.bitmap); return -1; } /* * End the font and do memory cleanup on the glyph and raster structures. */ eof = fprintf(out, "ENDFONT\n"); return eof; } static int #ifdef __STDC__ generate_bdf(FILE *out, char *iname, char *oname) #else generate_bdf(out, iname, oname) FILE *out; char *iname, *oname; #endif { TT_Long i; TT_UShort p, e; /* * Get the requested cmap. */ for (i = 0; i < TT_Get_CharMap_Count(face); i++) { if (!TT_Get_CharMap_ID(face, i, &p, &e) && p == pid && e == eid) break; } if (i == TT_Get_CharMap_Count(face) && pid == 3 && eid == 1) { /* * Make a special case when this fails with pid == 3 and eid == 1. * Change to eid == 0 and try again. This captures the two possible * cases for MS fonts. Some other method should be used to cycle * through all the alternatives later. */ for (i = 0; i < TT_Get_CharMap_Count(face); i++) { if (!TT_Get_CharMap_ID(face, i, &p, &e) && p == pid && e == 0) break; } if (i < TT_Get_CharMap_Count(face)) { if (!TT_Get_CharMap(face, i, &cmap)) eid = 0; else nocmap = 1; } } else { /* * A CMap was found for the platform and encoding IDs. */ if (i < TT_Get_CharMap_Count(face) && TT_Get_CharMap(face, i, &cmap)) nocmap = 1; else nocmap = 0; } if (nocmap && verbose) { fprintf(stderr, "%s: no character map for platform %d encoding %d. ", prog, pid, eid); fprintf(stderr, "Generating all glyphs.\n"); } /* * Now go through and generate the glyph bitmaps themselves. */ return generate_font(out, iname, oname); } #define isdig(cc) ((cc) >= '0' && (cc) <= '9') /* * Routine to parse a subset specification supplied on the command line. * The syntax for this specification is the same as the syntax used for * the XLFD font names (XLFD documentation, page 9). * * Example: * * "60 70 80_90" means the glyphs at codes 60, 70, and between 80 and * 90 inclusive. */ static void #ifdef __STDC__ parse_subset(char *s) #else parse_subset(s) char *s; #endif { long l, r; /* * Make sure to clear the flag and bitmap in case more than one subset is * specified on the command line. */ maxcode = 0; do_subset = 0; (void) memset((char *) subset, 0, sizeof(unsigned long) * 2048); while (*s) { /* * Collect the next code value. */ for (l = r = 0; *s && isdig(*s); s++) l = (l * 10) + (*s - '0'); /* * If the next character is an '_', advance and collect the end of the * specified range. */ if (*s == '_') { s++; for (; *s && isdig(*s); s++) r = (r * 10) + (*s - '0'); } else r = l; /* * Add the range just collected to the subset bitmap and set the flag * that indicates a subset is wanted. */ for (; l <= r; l++) { do_subset = 1; subset[l >> 5] |= (1 << (l & 31)); if (l > maxcode) maxcode = l; } /* * Skip all non-digit characters. */ while (*s && !isdig(*s)) s++; } } static void #ifdef __STDC__ usage(int eval) #else usage(eval) int eval; #endif { fprintf(stderr, "Usage: %s [options below] font.ttf\n", prog); fprintf(stderr, "-h\t\tThis message.\n"); fprintf(stderr, "-v\t\tPrint warning messages during conversion.\n"); fprintf(stderr, "-l \"subset\"\tSpecify a subset of glyphs to generate.\n"); fprintf(stderr, "-m mapfile\tGlyph reencoding file.\n"); fprintf(stderr, "-n\t\tTurn off glyph hinting.\n"); fprintf(stderr, "-c c\t\tSet the character spacing (default: from font).\n"); fprintf(stderr, "-f name\t\tSet the foundry name (default: freetype).\n"); fprintf(stderr, "-t name\t\tSet the typeface name (default: from font).\n"); fprintf(stderr, "-w name\t\tSet the weight name (default: Medium).\n"); fprintf(stderr, "-s name\t\tSet the slant name (default: R).\n"); fprintf(stderr, "-k name\t\tSet the width name (default: Normal).\n"); fprintf(stderr, "-d name\t\tSet the additional style name (default: empty).\n"); fprintf(stderr, "-u char\t\tSet the character to replace '-' in names "); fprintf(stderr, "(default: space).\n"); fprintf(stderr, "-pid id\t\tSet the platform ID for encoding (default: %d).\n", DEFAULT_PLATFORM_ID); fprintf(stderr, "-eid id\t\tSet the encoding ID for encoding (default: %d).\n", DEFAULT_ENCODING_ID); fprintf(stderr, "-p n\t\tSet the point size (default: %dpt).\n", DEFAULT_POINT_SIZE); fprintf(stderr, "-r n\t\tSet the horizontal and vertical resolution "); fprintf(stderr, "(default: %ddpi).\n", DEFAULT_RESOLUTION); fprintf(stderr, "-rh n\t\tSet the horizontal resolution "); fprintf(stderr, "(default: %ddpi)\n", DEFAULT_RESOLUTION); fprintf(stderr, "-rv n\t\tSet the vertical resolution "); fprintf(stderr, "(default: %ddpi)\n", DEFAULT_RESOLUTION); fprintf(stderr, "-o outfile\tSet the output filename (default: stdout).\n"); exit(eval); } int #ifdef __STDC__ main(int argc, char *argv[]) #else main(argc, argv) int argc; char *argv[]; #endif { int res; char *infile, *outfile, *iname, *oname; FILE *out, *mapin; if ((prog = strrchr(argv[0], '/'))) prog++; else prog = argv[0]; out = stdout; infile = outfile = 0; argc--; argv++; while (argc > 0) { if (argv[0][0] == '-') { switch (argv[0][1]) { case 'v': case 'V': verbose = 1; break; case 'l': case 'L': argc--; argv++; parse_subset(argv[0]); break; case 'n': case 'N': load_flags &= ~TTLOAD_HINT_GLYPH; break; case 'c': case 'C': argc--; argv++; spacing = argv[0][0]; break; case 't': case 'T': argc--; argv++; face_name = argv[0]; break; case 'w': case 'W': argc--; argv++; weight_name = argv[0]; break; case 's': case 'S': argc--; argv++; slant_name = argv[0]; break; case 'k': case 'K': argc--; argv++; width_name = argv[0]; break; case 'd': case 'D': argc--; argv++; style_name = argv[0]; break; case 'f': case 'F': argc--; argv++; foundry_name = argv[0]; break; case 'u': case 'U': argc--; argv++; dashchar = argv[0][0]; break; case 'p': case 'P': res = argv[0][2]; argc--; argv++; if (res == 'i' || res == 'I') /* * Set the platform ID. */ pid = atoi(argv[0]); else /* * Set the point size. */ point_size = atoi(argv[0]); break; case 'e': case 'E': /* * Set the encoding ID. */ argc--; argv++; eid = atoi(argv[0]); break; case 'r': /* * Set the horizontal and vertical resolutions. */ if (argv[0][2] == 'h') hres = atoi(argv[1]); else if (argv[0][2] == 'v') vres = atoi(argv[1]); else hres = vres = atoi(argv[1]); argc--; argv++; break; case 'm': case 'M': /* * Try to load a remap table. */ argc--; argv++; /* * Always reset the `do_remap' variable here in case more than * one map file appears on the command line. */ do_remap = 0; if ((mapin = fopen(argv[0], "r")) == 0) fprintf(stderr, "%s: unable to open the remap table '%s'.\n", prog, argv[0]); else { if (ttf2bdf_load_map(mapin) < 0) { fprintf(stderr, "%s: problem loading remap table '%s'.\n", prog, argv[0]); do_remap = 0; } else do_remap = 1; fclose(mapin); } break; case 'o': case 'O': /* * Set the output file name. */ argc--; argv++; outfile = argv[0]; break; default: usage(1); } } else /* * Set the input file name. */ infile = argv[0]; argc--; argv++; } /* * Validate the values passed on the command line. */ if (infile == 0) { fprintf(stderr, "%s: no input file provided.\n", prog); usage(1); } /* * Set the input filename that will be passed to the generator * routine. */ if ((iname = strrchr(infile, '/'))) iname++; else iname = infile; /* * Check the platform and encoding IDs. */ if (pid < 0 || pid > 255) { fprintf(stderr, "%s: invalid platform ID '%d'.\n", prog, pid); exit(1); } if (eid < 0 || eid > 65535) { fprintf(stderr, "%s: invalid encoding ID '%d'.\n", prog, eid); exit(1); } /* * Arbitrarily limit the point size to a minimum of 2pt and maximum of * 256pt. */ if (point_size < 2 || point_size > 256) { fprintf(stderr, "%s: invalid point size '%dpt'.\n", prog, point_size); exit(1); } /* * Arbitrarily limit the resolutions to a minimum of 10dpi and a maximum * of 1200dpi. */ if (hres < 10 || hres > 1200) { fprintf(stderr, "%s: invalid horizontal resolution '%ddpi'.\n", prog, hres); exit(1); } if (vres < 10 || vres > 1200) { fprintf(stderr, "%s: invalid vertical resolution '%ddpi'.\n", prog, vres); exit(1); } /* * Open the output file if specified. */ if (outfile != 0) { /* * Attempt to open the output file. */ if ((out = fopen(outfile, "w")) == 0) { fprintf(stderr, "%s: unable to open the output file '%s'.\n", prog, outfile); exit(1); } /* * Set the output filename to be passed to the generator routine. */ if ((oname = strrchr(outfile, '/'))) oname++; else oname = outfile; } else /* * Set the default output file name to . */ oname = ""; /* * Intialize Freetype. */ if ((res = TT_Init_FreeType(&engine))) { /* * Close the output file. */ if (out != stdout) { fclose(out); (void) unlink(outfile); } fprintf(stderr, "%s[%d]: unable to initialize renderer.\n", prog, res); exit(1); } /* * Open the input file. */ if ((res = TT_Open_Face(engine, infile, &face))) { if (out != stdout) { fclose(out); (void) unlink(outfile); } fprintf(stderr, "%s[%d]: unable to open input file '%s'.\n", prog, res, infile); exit(1); } /* * Create a new instance. */ if ((res = TT_New_Instance(face, &instance))) { (void) TT_Close_Face(face); if (out != stdout) { fclose(out); (void) unlink(outfile); } fprintf(stderr, "%s[%d]: unable to create instance.\n", prog, res); exit(1); } /* * Set the instance resolution and point size and the relevant * metrics. */ (void) TT_Set_Instance_Resolutions(instance, hres, vres); (void) TT_Set_Instance_CharSize(instance, point_size*64); (void) TT_Get_Instance_Metrics(instance, &imetrics); /* * Get the face properties and set the global units per em value for * convenience. */ (void) TT_Get_Face_Properties(face, &properties); upm = properties.header->Units_Per_EM; /* * Create a new glyph container. */ if ((res = TT_New_Glyph(face, &glyph))) { (void) TT_Done_Instance(instance); (void) TT_Close_Face(face); if (out != stdout) { fclose(out); (void) unlink(outfile); } fprintf(stderr, "%s[%d]: unable to create glyph.\n", prog, res); exit(1); } /* * Generate the BDF font from the TrueType font. */ res = generate_bdf(out, iname, oname); /* * Free up the mapping table if one was loaded. */ ttf2bdf_free_map(); /* * Close the input and output files. */ (void) TT_Close_Face(face); if (out != stdout) { fclose(out); if (res < 0) /* * An error occured when generating the font, so delete the * output file. */ (void) unlink(outfile); } /* * Shut down the renderer. */ (void) TT_Done_FreeType(engine); exit(res); return 0; }