diff options
Diffstat (limited to 'src/ft.c')
-rw-r--r-- | src/ft.c | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/src/ft.c b/src/ft.c new file mode 100644 index 0000000..b8f548b --- /dev/null +++ b/src/ft.c @@ -0,0 +1,569 @@ +/* + * The font parser using the FreeType library version 2. + * + * based in part upon the ft.c source file in TTF2PT1 + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include "ft2build.h" +#include FT_FREETYPE_H +#include "freetype/freetype.h" +#include "freetype/ftglyph.h" +#include "freetype/ftsnames.h" +#include "freetype/ttnameid.h" +#include "freetype/ftoutln.h" +#include "freetype/tttables.h" + +#include "ft.h" +#include "fm.h" +#include "glyph.h" +#include "glyphs.h" +#include "utils.h" + +/* statics */ + +static FT_Library library; +static FT_Face face; + +void ft_init(void) +{ + if (FT_Init_FreeType(&library)) { + fprintf(stderr, "** FreeType initialization failed\n"); + exit(1); + } +} + +void ft_fini(void) +{ + if (face) + close_font(); + + if (FT_Done_FreeType(library)) { + fprintf(stderr, "Errors when stopping FreeType, ignored\n"); + } +} + +/* + * Open font and prepare to return information to the main driver. + * May print error and warning messages. + */ + +int open_font(char *fname) +{ + FT_Error error; + + if ((error = FT_New_Face(library, fname, 0, &face)) != 0) { + 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); + return 1; + } + + return 0; +} + +/* + * Close font. + * Exit on error. + */ + +void close_font(void) +{ + if (FT_Done_Face(face)) { + fprintf(stderr, "Errors when closing the font file, ignored\n"); + } + + face = 0; +} + +/* + * Get the number of glyphs in font. + */ + +int count_glyphs(void) +{ + return (int)face->num_glyphs; +} + +/* + * Get the names of the glyphs. + * Returns 0 if the names were assigned, non-zero on error + */ + +int glnames(struct glyph *glyph_list) +{ +#define MAX_NAMELEN 1024 +// unsigned char bf[1024]; + int i; + +// if (!FT_HAS_GLYPH_NAMES(face)) { +// fprintf(stderr, "Font has no glyph names\n"); +// return 0; +// } + +// for (i = 0; i < face->num_glyphs; i++) { +// if (FT_Get_Glyph_Name(face, i, bf, MAX_NAMELEN) || bf[0]==0) { +// sprintf((char*)bf, "_g_%d", i); +// fprintf(stderr, +// "Glyph No. %d has no postscript name, becomes %s\n", i, bf); +// } + +// /* don't copy .notdef across when not necessary */ +// if (i == 0 || strcmp((char*)bf, ".notdef") != 0) { +// glyph_list[i].name = strdup((char*)bf); + +// if (glyph_list[i].name == NULL) { +// fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); +// return 1; +// } +// } +// } + + for (i = 0; i != face->num_glyphs; i++) { + ttf2f_poll(1); + glyph_list[i].name = glyph_name(glyph_list[i].code); + } + + return 0; +} + +/* + * Get the metrics of the glyphs. + */ + +void glmetrics(struct glyph *glyph_list, void (*callback)(int progress)) +{ + struct 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]); + + callback(i * 100 / face->num_glyphs); + ttf2f_poll(1); + + 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 = convert_units(met->horiAdvance, + face->units_per_EM); + g->lsb = convert_units(met->horiBearingX, + face->units_per_EM); + } else { + fprintf(stderr, "Glyph %s has no horizontal metrics, guessed them\n", g->name); + g->width = convert_units(met->width, + face->units_per_EM); + 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 = convert_units(bbox.xMin, face->units_per_EM); + g->yMin = convert_units(bbox.yMin, face->units_per_EM); + g->xMax = convert_units(bbox.xMax, face->units_per_EM); + g->yMax = convert_units(bbox.yMax, face->units_per_EM); + + g->ttf_pathlen = face->glyph->outline.n_points; + + FT_Done_Glyph(gly); + } +} + +/* + * Map charcodes to glyph ids using the unicode encoding + */ + +int glenc(struct glyph *glyph_list) +{ + unsigned charcode, glyphid; + + if (!face->charmaps || FT_Select_Charmap(face, FT_ENCODING_UNICODE)) { + fprintf(stderr, "**** Cannot set charmap in FreeType ****\n"); + return 1; + } + + charcode = FT_Get_First_Char(face, &glyphid); + while (glyphid != 0) { + ttf2f_poll(1); + glyph_list[glyphid].code = charcode; + charcode = FT_Get_Next_Char(face, charcode, &glyphid); + } + + return 0; +} + +/* + * Get the font metrics + */ +int fnmetrics(struct font_metrics *fm) +{ + char *str; + static char *fieldstocheck[3]; + FT_SfntName sn; + TT_Postscript *post; + int i, j, len; + + fm->underline_position = convert_units(face->underline_position, + face->units_per_EM); + fm->underline_thickness = convert_units(face->underline_thickness, + face->units_per_EM); + fm->is_fixed_pitch = FT_IS_FIXED_WIDTH(face); + + fm->ascender = convert_units(face->ascender, face->units_per_EM); + fm->descender = convert_units(face->descender, face->units_per_EM); + + fm->units_per_em = face->units_per_EM; + + fm->bbox[0] = convert_units(face->bbox.xMin, face->units_per_EM); + fm->bbox[1] = convert_units(face->bbox.yMin, face->units_per_EM); + fm->bbox[2] = convert_units(face->bbox.xMax, face->units_per_EM); + fm->bbox[3] = convert_units(face->bbox.yMax, face->units_per_EM); + + if ((post = (TT_Postscript*)FT_Get_Sfnt_Table(face, ft_sfnt_post)) != NULL) + fm->italic_angle = post->italicAngle; + else { + fprintf(stderr, "hidden"); + fm->italic_angle = 0.0; /* FreeType hides the angle */ + } + + if (FT_Get_Sfnt_Name(face, TT_NAME_ID_COPYRIGHT, &sn)) + fm->name_copyright = (char *) ""; + else + fm->name_copyright = strndup((const char*)sn.string, sn.string_len); + + fm->name_family = face->family_name; + + fm->name_style = face->style_name; + if (fm->name_style == NULL) + fm->name_style = (char *) ""; + + if (FT_Get_Sfnt_Name(face, TT_NAME_ID_FULL_NAME, &sn)) { + 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__); + return 1; + } + strcpy(fm->name_full, fm->name_family); + if (strlen(fm->name_style) != 0) { + strcat(fm->name_full, " "); + strcat(fm->name_full, fm->name_style); + } + } + else + fm->name_full = strndup((const char*)sn.string, sn.string_len); + + if (FT_Get_Sfnt_Name(face, TT_NAME_ID_VERSION_STRING, &sn)) + fm->name_version = (char *) "1.0"; + else + fm->name_version = strndup((const char*)sn.string, sn.string_len); + + if (FT_Get_Sfnt_Name(face, TT_NAME_ID_PS_NAME , &sn)) { + if ((fm->name_ps = strdup(fm->name_full)) == NULL) { + fprintf(stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + return 1; + } + } else + fm->name_ps = strndup((const char*)sn.string, sn.string_len); + for (i = 0; fm->name_ps[i]!=0; i++) + if (fm->name_ps[i] == ' ') + fm->name_ps[i] = '_'; /* no spaces in the Postscript name */ + + /* 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 && i < (int) (sizeof fieldstocheck / sizeof(fieldstocheck[0])); i++) { + str=fieldstocheck[i]; + len = strlen(str); + for (j = 0; j < len; j++) { + if ((str[j]=='B' + || str[j]=='b') + && (j==0 || !isalpha(str[j-1])) + && !strncmp("old",&str[j+1],3) + && (j+4 >= len || !islower(str[j+4]))) { + fm->force_bold=1; + break; + } + } + } + + return 0; +} + +/* + * Functions to decompose the outlines + */ + +static struct glyph *curg; +static struct outline *cur_outline_entry; +static long lastx, lasty; + +static int outl_moveto(const FT_Vector *to, void *unused) +{ + struct outline *o; + + UNUSED(unused); + + o = calloc(1, sizeof(struct outline)); + if (!o) { + fprintf(stderr, "malloc failed\n"); + return 1; + } + + o->type = MOVE_TO; + o->data.move_to.x = convert_units(to->x, face->units_per_EM); + o->data.move_to.y = convert_units(to->y, face->units_per_EM); + + if (cur_outline_entry) + cur_outline_entry->next = o; + else + curg->outline = o; + cur_outline_entry = o; + + lastx = convert_units(to->x, face->units_per_EM); + lasty = convert_units(to->y, face->units_per_EM); + +/* fprintf(stderr, "\tmoving to: (%x, %x)(%x, %x)\n", o->data.move_to.x, o->data.move_to.y, to->x, to->y); +*/ + return 0; +} + +static int outl_lineto(const FT_Vector *to, void *unused) +{ + struct outline *o; + + UNUSED(unused); + + o = calloc(1, sizeof(struct outline)); + if (!o) { + fprintf(stderr, "malloc failed\n"); + return 1; + } + + o->type = LINE_TO; + o->data.line_to.x = convert_units(to->x, face->units_per_EM); + o->data.line_to.y = convert_units(to->y, face->units_per_EM); + + if (cur_outline_entry) + cur_outline_entry->next = o; + else + curg->outline = o; + cur_outline_entry = o; + + lastx = convert_units(to->x, face->units_per_EM); + lasty = convert_units(to->y, face->units_per_EM); + +/* fprintf(stderr, "\tdrawing line to: (%x, %x)(%x, %x)\n", o->data.line_to.x, o->data.line_to.y, to->x, to->y); +*/ + return 0; +} + +static int outl_conicto(const FT_Vector *control1, const FT_Vector *to, + void *unused) +{ + struct outline *o; + double c1x, c1y; + + UNUSED(unused); + + o = calloc(1, sizeof(struct outline)); + if (!o) { + fprintf(stderr, "malloc failed\n"); + return 1; + } + + c1x = (double)lastx + 2.0 * + ((double)convert_units(control1->x, face->units_per_EM) - + (double)lastx) / 3.0; + c1y = (double)lasty + 2.0 * + ((double)convert_units(control1->y, face->units_per_EM) - + (double)lasty) / 3.0; + + o->type = CURVE; + o->data.curve.x1 = (int)c1x; + o->data.curve.y1 = (int)c1y; + o->data.curve.x2 = (int)(c1x + + ((double)convert_units(to->x, face->units_per_EM) - + (double)lastx) / 3.0); + o->data.curve.y2 = (int)(c1y + + ((double)convert_units(to->y, face->units_per_EM) - + (double)lasty) / 3.0); + o->data.curve.x3 = convert_units(to->x, face->units_per_EM); + o->data.curve.y3 = convert_units(to->y, face->units_per_EM); + + if (cur_outline_entry) + cur_outline_entry->next = o; + else + curg->outline = o; + cur_outline_entry = o; + +/* fprintf(stderr, "\tdrawing conic curve to: (%x, %x)(%x, %x) ctrl1: (%x, %x)(%f, %f) ctrl2: (%x, %x)(%f, %f) qctrl: (%x, %x)\n", + o->data.curve.x3, o->data.curve.y3, to->x, to->y, + o->data.curve.x1, o->data.curve.y1, c1x, c1y, + o->data.curve.x2, o->data.curve.y2, + (c1x + (double)(to->x - lastx) / 3.0), + (c1y + (double)(to->y - lasty) / 3.0), + control1->x, control1->y); +*/ + lastx = convert_units(to->x, face->units_per_EM); + lasty = convert_units(to->y, face->units_per_EM); + + return 0; +} + +static int outl_cubicto(const FT_Vector *control1, const FT_Vector *control2, + const FT_Vector *to, void *unused) +{ + struct outline *o; + + UNUSED(unused); + + o = calloc(1, sizeof(struct outline)); + if (!o) { + fprintf(stderr, "malloc failed\n"); + return 1; + } + + o->type = CURVE; + o->data.curve.x1 = convert_units(control1->x, face->units_per_EM); + o->data.curve.y1 = convert_units(control1->y, face->units_per_EM); + o->data.curve.x2 = convert_units(control2->x, face->units_per_EM); + o->data.curve.y2 = convert_units(control2->y, face->units_per_EM); + o->data.curve.x3 = convert_units(to->x, face->units_per_EM); + o->data.curve.y3 = convert_units(to->y, face->units_per_EM); + + if (cur_outline_entry) + cur_outline_entry->next = o; + else + curg->outline = o; + cur_outline_entry = o; + + lastx = convert_units(to->x, face->units_per_EM); + lasty = convert_units(to->y, face->units_per_EM); + +/* fprintf(stderr, "\tdrawing cubic curve to: (%x, %x)(%x, %x) ctrl1: (%x, %x)(%x, %x) ctrl2: (%x, %x)(%x, %x)\n", o->data.curve.x3, o->data.curve.y3, to->x, to->y, o->data.curve.x1, o->data.curve.y1, control1->x, control1->y, o->data.curve.x2, o->data.curve.y2, control2->x, control2->y); +*/ + 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. + */ + +void glpath(int glyphno, struct glyph *glyf_list) +{ + FT_Outline *ol; + FT_Glyph gly; + struct outline *o; + + curg = &glyf_list[glyphno]; + cur_outline_entry = 0; + + 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; lasty = 0; + +/* fprintf(stderr, "Decomposing outline of %s (%d):\n", curg->name, + glyphno); +*/ + if (FT_Outline_Decompose(ol, &ft_outl_funcs, NULL)) { + fprintf(stderr, "Can't decompose outline of glyph %s, skipped\n", curg->name); + return; + } + + o = calloc(1, sizeof(struct outline)); + if (!o) { + fprintf(stderr, "malloc failed\n"); + return; + } + + o->type = TERMINATE; + /* todo - handle composite glyphs */ + o->data.terminate.composite = 0; + + if (cur_outline_entry) + cur_outline_entry->next = o; + else + curg->outline = o; + cur_outline_entry = o; + + if (FT_Get_Glyph(face->glyph, &gly)) { + fprintf(stderr, "Can't access glyph %s bbox, skipped\n", curg->name); + return; + } + + FT_Done_Glyph(gly); + +// fprintf(stderr, "done\n\n"); +} + +#if 0 +/* + * Get the kerning data. + */ + +void kerning(struct glyph *glyph_list) +{ + int i, j, n; + int nglyphs = face->num_glyphs; + FT_Vector k; + struct glyph *gl; + + if (nglyphs == 0 || !FT_HAS_KERNING(face)) { + fputs("No Kerning data\n", stderr); + return; + } + + for (i = 0; i < nglyphs; i++) { + if ((glyph_list[i].flags & GF_USED) == 0) + continue; + for (j = 0; j < nglyphs; j++) { + if ((glyph_list[j].flags & GF_USED) == 0) + continue; + if (FT_Get_Kerning(face, i, j, ft_kerning_unscaled, &k)) + continue; + if (k.x == 0) + continue; + + addkernpair(i, j, k.x); + } + } +} + +#endif |