/* * The font parser using the FreeType library version 2. * * see COPYRIGHT * */ #ifdef USE_FREETYPE #include #include #include #include #include #include #include #include #include #include #include "pt1.h" #include "global.h" /* prototypes of call entries */ static void openfont(char *fname, char *arg); static void closefont( void); static int getnglyphs ( void); static int glnames( GLYPH *glyph_list); static void glmetrics( GLYPH *glyph_list); static int glenc( GLYPH *glyph_list, int *encoding, int *unimap); static void fnmetrics( struct font_metrics *fm); static void glpath( int glyphno, GLYPH *glyph_list); static void kerning( GLYPH *glyph_list); /* globals */ /* front-end descriptor */ struct frontsw freetype_sw = { /*name*/ "ft", /*descr*/ "based on the FreeType library", /*suffix*/ { "ttf", "otf", "pfa", "pfb" }, /*open*/ openfont, /*close*/ closefont, /*nglyphs*/ getnglyphs, /*glnames*/ glnames, /*glmetrics*/ glmetrics, /*glenc*/ glenc, /*fnmetrics*/ fnmetrics, /*glpath*/ glpath, /*kerning*/ kerning, }; /* statics */ static char * dupcnstring( char *s, int len); static FT_Library library; static FT_Face face; static int enc_type, enc_found; /* SFNT functions do not seem to be included by default in FT2beta8 */ #define ENABLE_SFNT /* * Open font and prepare to return information to the main driver. * May print error and warning messages. * Exit on error. */ static void openfont( char *fname, char *arg /* unused now */ ) { FT_Error error; if( FT_Init_FreeType( &library ) ) { fprintf(stderr, "** FreeType initialization failed\n"); exit(1); } if( error = FT_New_Face( library, fname, 0, &face ) ) { if ( error == FT_Err_Unknown_File_Format ) fprintf(stderr, "**** %s has format unknown to FreeType\n", fname); else fprintf(stderr, "**** Cannot access %s ****\n", fname); exit(1); } if(FT_HAS_FIXED_SIZES(face)) { WARNING_1 fprintf(stderr, "Font contains bitmaps\n"); } if(FT_HAS_MULTIPLE_MASTERS(face)) { WARNING_1 fprintf(stderr, "Font contains multiple masters, using default\n"); } if(ISDBG(FT)) fprintf(stderr," %d units per EM\n", face->units_per_EM); enc_found = 0; } /* * Close font. * Exit on error. */ static void closefont( void ) { if( FT_Done_Face(face) ) { WARNING_1 fprintf(stderr, "Errors when closing the font file, ignored\n"); } if( FT_Done_FreeType(library) ) { WARNING_1 fprintf(stderr, "Errors when stopping FreeType, ignored\n"); } } /* * Get the number of glyphs in font. */ static int getnglyphs ( void ) { if(ISDBG(FT)) fprintf(stderr, "%d glyphs in font\n", face->num_glyphs); return (int)face->num_glyphs; } /* * Get the names of the glyphs. * Returns 0 if the names were assigned, non-zero if the font * provides no glyph names. */ static int glnames( GLYPH *glyph_list ) { #define MAX_NAMELEN 1024 unsigned char bf[1024]; int i; if( ! FT_HAS_GLYPH_NAMES(face) ) { WARNING_1 fprintf(stderr, "Font has no glyph names\n"); return 1; } for(i=0; i < face->num_glyphs; i++) { if( FT_Get_Glyph_Name(face, i, bf, MAX_NAMELEN) || bf[0]==0 ) { sprintf(bf, "_%d", i); WARNING_2 fprintf(stderr, "**** Glyph No. %d has no postscript name, becomes %s ****\n", i, bf); } glyph_list[i].name = strdup(bf); if(ISDBG(FT)) fprintf(stderr, "%d has name %s\n", i, bf); if (glyph_list[i].name == NULL) { fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); exit(255); } } return 0; } /* * Get the metrics of the glyphs. */ static void glmetrics( GLYPH *glyph_list ) { GLYPH *g; int i; FT_Glyph_Metrics *met; FT_BBox bbox; FT_Glyph gly; for(i=0; i < face->num_glyphs; i++) { g = &(glyph_list[i]); if( FT_Load_Glyph(face, i, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE) ) { fprintf(stderr, "Can't load glyph %s, skipped\n", g->name); continue; } met = &face->glyph->metrics; if(FT_HAS_HORIZONTAL(face)) { g->width = met->horiAdvance; g->lsb = met->horiBearingX; } else { WARNING_2 fprintf(stderr, "Glyph %s has no horizontal metrics, guessed them\n", g->name); g->width = met->width; g->lsb = 0; } if( FT_Get_Glyph(face->glyph, &gly) ) { fprintf(stderr, "Can't access glyph %s bbox, skipped\n", g->name); continue; } FT_Glyph_Get_CBox(gly, ft_glyph_bbox_unscaled, &bbox); g->xMin = bbox.xMin; g->yMin = bbox.yMin; g->xMax = bbox.xMax; g->yMax = bbox.yMax; g->ttf_pathlen = face->glyph->outline.n_points; } } /* * Get the original encoding of the font. * Returns 1 for if the original encoding is Unicode, 2 if the * original encoding is other 16-bit, 0 if 8-bit. */ static int glenc( GLYPH *glyph_list, int *encoding, int *unimap ) { int i, e; unsigned code; if(ISDBG(FT)) for(e=0; e < face->num_charmaps; e++) { fprintf(stderr, "found encoding pid=%d eid=%d\n", face->charmaps[e]->platform_id, face->charmaps[e]->encoding_id); } if(enc_found) goto populate_map; enc_type = 0; /* first check for an explicit PID/EID */ if(force_pid != -1) { for(e=0; e < face->num_charmaps; e++) { if(face->charmaps[e]->platform_id == force_pid && face->charmaps[e]->encoding_id == force_eid) { WARNING_1 fprintf(stderr, "Found Encoding PID=%d/EID=%d\n", force_pid, force_eid); if( FT_Set_Charmap(face, face->charmaps[e]) ) { fprintf(stderr, "**** Cannot set charmap in FreeType ****\n"); exit(1); } enc_type = 1; goto populate_map; } } fprintf(stderr, "*** TTF encoding table PID=%d/EID=%d not found\n", force_pid, force_eid); exit(1); } /* next check for a direct Adobe mapping */ if(!forceunicode) { for(e=0; e < face->num_charmaps; e++) { if(face->charmaps[e]->encoding == ft_encoding_adobe_custom) { WARNING_1 fputs("Found Adobe Custom Encoding\n", stderr); if( FT_Set_Charmap(face, face->charmaps[e]) ) { fprintf(stderr, "**** Cannot set charmap in FreeType ****\n"); exit(1); } goto populate_map; } } } for(e=0; e < face->num_charmaps; e++) { if(face->charmaps[e]->platform_id == 3) { switch(face->charmaps[e]->encoding_id) { case 0: WARNING_1 fputs("Found Symbol Encoding\n", stderr); break; case 1: WARNING_1 fputs("Found Unicode Encoding\n", stderr); enc_type = 1; break; default: WARNING_1 { fprintf(stderr, "****MS Encoding ID %d not supported****\n", face->charmaps[e]->encoding_id); fputs("Treating it like Symbol encoding\n", stderr); } break; } break; } } if(e >= face->num_charmaps) { WARNING_1 fputs("No Microsoft encoding, using first encoding available\n", stderr); e = 0; } if(forceunicode) { WARNING_1 fputs("Forcing Unicode Encoding\n", stderr); } if( FT_Set_Charmap(face, face->charmaps[e]) ) { fprintf(stderr, "**** Cannot set charmap in FreeType ****\n"); exit(1); } populate_map: enc_found = 1; for(i=0; i<256; i++) { if(encoding[i] != -1) continue; if(enc_type == 1 || forceunicode) { code = unimap[i]; if(code == (unsigned) -1) continue; } else code = i; code = FT_Get_Char_Index(face, code); if(0 && ISDBG(FT)) fprintf(stderr, "code of %3d is %3d\n", i, code); if(code == 0) continue; /* .notdef */ encoding[i] = code; } return enc_type; } /* duplicate a string with counter to a 0-terminated string */ static char * dupcnstring( char *s, int len ) { char *res; if(( res = malloc(len+1) )==NULL) { fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); exit(255); } memcpy(res, s, len); res[len] = 0; return res; } /* * Get the font metrics */ static void fnmetrics( struct font_metrics *fm ) { char *str; static char *fieldstocheck[3]; #ifdef ENABLE_SFNT FT_SfntName sn; #endif /* ENABLE_SFNT */ int i; fm->italic_angle = 0.0; /* FreeType hides the angle */ fm->underline_position = face->underline_position; fm->underline_thickness = face->underline_thickness; fm->is_fixed_pitch = FT_IS_FIXED_WIDTH(face); fm->ascender = face->ascender; fm->descender = face->descender; fm->units_per_em = face->units_per_EM; fm->bbox[0] = face->bbox.xMin; fm->bbox[1] = face->bbox.yMin; fm->bbox[2] = face->bbox.xMax; fm->bbox[3] = face->bbox.yMax; #ifdef ENABLE_SFNT if( FT_Get_Sfnt_Name(face, TT_NAME_ID_COPYRIGHT, &sn) ) #endif /* ENABLE_SFNT */ fm->name_copyright = ""; #ifdef ENABLE_SFNT else fm->name_copyright = dupcnstring(sn.string, sn.string_len); #endif /* ENABLE_SFNT */ fm->name_family = face->family_name; fm->name_style = face->style_name; if(fm->name_style == NULL) fm->name_style = ""; #ifdef ENABLE_SFNT if( FT_Get_Sfnt_Name(face, TT_NAME_ID_FULL_NAME, &sn) ) #endif /* ENABLE_SFNT */ { int len; len = strlen(fm->name_family) + strlen(fm->name_style) + 2; if(( fm->name_full = malloc(len) )==NULL) { fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); exit(255); } strcpy(fm->name_full, fm->name_family); if(strlen(fm->name_style) != 0) { strcat(fm->name_full, " "); strcat(fm->name_full, fm->name_style); } } #ifdef ENABLE_SFNT else fm->name_full = dupcnstring(sn.string, sn.string_len); #endif /* ENABLE_SFNT */ #ifdef ENABLE_SFNT if( FT_Get_Sfnt_Name(face, TT_NAME_ID_VERSION_STRING, &sn) ) #endif /* ENABLE_SFNT */ fm->name_version = "1.0"; #ifdef ENABLE_SFNT else fm->name_version = dupcnstring(sn.string, sn.string_len); #endif /* ENABLE_SFNT */ #ifdef ENABLE_SFNT if( FT_Get_Sfnt_Name(face, TT_NAME_ID_PS_NAME , &sn) ) { #endif /* ENABLE_SFNT */ if(( fm->name_ps = strdup(fm->name_full) )==NULL) { fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); exit(255); } #ifdef ENABLE_SFNT } else fm->name_ps = dupcnstring(sn.string, sn.string_len); #endif /* ENABLE_SFNT */ /* guess the boldness from the font names */ fm->force_bold=0; fieldstocheck[0] = fm->name_style; fieldstocheck[1] = fm->name_full; fieldstocheck[2] = fm->name_ps; for(i=0; !fm->force_bold && iforce_bold=1; break; } } } } /* * Functions to decompose the outlines */ static GLYPH *curg; static double lastx, lasty; static int outl_moveto( FT_Vector *to, void *unused ) { double tox, toy; tox = fscale((double)to->x); toy = fscale((double)to->y); /* FreeType does not do explicit closepath() */ if(curg->lastentry) { g_closepath(curg); } fg_rmoveto(curg, tox, toy); lastx = tox; lasty = toy; return 0; } static int outl_lineto( FT_Vector *to, void *unused ) { double tox, toy; tox = fscale((double)to->x); toy = fscale((double)to->y); fg_rlineto(curg, tox, toy); lastx = tox; lasty = toy; return 0; } static int outl_conicto( FT_Vector *control1, FT_Vector *to, void *unused ) { double c1x, c1y, tox, toy; c1x = fscale((double)control1->x); c1y = fscale((double)control1->y); tox = fscale((double)to->x); toy = fscale((double)to->y); fg_rrcurveto(curg, (lastx + 2.0 * c1x) / 3.0, (lasty + 2.0 * c1y) / 3.0, (2.0 * c1x + tox) / 3.0, (2.0 * c1y + toy) / 3.0, tox, toy ); lastx = tox; lasty = toy; return 0; } static int outl_cubicto( FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *unused ) { double c1x, c1y, c2x, c2y, tox, toy; c1x = fscale((double)control1->x); c1y = fscale((double)control1->y); c2x = fscale((double)control2->x); c2y = fscale((double)control2->y); tox = fscale((double)to->x); toy = fscale((double)to->y); fg_rrcurveto(curg, c1x, c1y, c2x, c2y, tox, toy); lastx = tox; lasty = toy; return 0; } static FT_Outline_Funcs ft_outl_funcs = { outl_moveto, outl_lineto, outl_conicto, outl_cubicto, 0, 0 }; /* * Get the path of contrours for a glyph. */ static void glpath( int glyphno, GLYPH *glyf_list ) { FT_Outline *ol; curg = &glyf_list[glyphno]; if( FT_Load_Glyph(face, glyphno, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE|FT_LOAD_NO_HINTING) || face->glyph->format != ft_glyph_format_outline ) { fprintf(stderr, "Can't load glyph %s, skipped\n", curg->name); return; } ol = &face->glyph->outline; lastx = 0.0; lasty = 0.0; if( FT_Outline_Decompose(ol, &ft_outl_funcs, NULL) ) { fprintf(stderr, "Can't decompose outline of glyph %s, skipped\n", curg->name); return; } /* FreeType does not do explicit closepath() */ if(curg->lastentry) { g_closepath(curg); } if(ol->flags & ft_outline_reverse_fill) { assertpath(curg->entries, __FILE__, __LINE__, curg->name); reversepaths(curg); } } /* * Get the kerning data. */ static void kerning( GLYPH *glyph_list ) { int i, j, n; int nglyphs = face->num_glyphs; FT_Vector k; GLYPH *gl; if( nglyphs == 0 || !FT_HAS_KERNING(face) ) { WARNING_1 fputs("No Kerning data\n", stderr); return; } for(i=0; i