diff options
Diffstat (limited to 'fitz/res_font.c')
-rw-r--r-- | fitz/res_font.c | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/fitz/res_font.c b/fitz/res_font.c new file mode 100644 index 00000000..7238cc3c --- /dev/null +++ b/fitz/res_font.c @@ -0,0 +1,408 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" /* for type3 font rendering */ + +#include <ft2build.h> +#include FT_FREETYPE_H + +static fz_font * +fz_newfont(void) +{ + fz_font *font; + + font = fz_malloc(sizeof(fz_font)); + if (!font) + return nil; + + font->refs = 1; + strcpy(font->name, "<unknown>"); + + font->ftface = nil; + font->ftsubstitute = 0; + font->fthint = 0; + + font->t3matrix = fz_identity(); + font->t3procs = nil; + + font->bbox.x0 = 0; + font->bbox.y0 = 0; + font->bbox.x1 = 1000; + font->bbox.y1 = 1000; + + return font; +} + +fz_font * +fz_keepfont(fz_font *font) +{ + font->refs ++; + return font; +} + +void +fz_dropfont(fz_font *font) +{ + int i; + + if (font && --font->refs == 0) + { + if (font->t3procs) + { + for (i = 0; i < 256; i++) + if (font->t3procs[i]) + fz_droptree(font->t3procs[i]); + fz_free(font->t3procs); + } + + if (font->ftface) + { + FT_Done_Face((FT_Face)font->ftface); + } + + fz_free(font); + } +} + +void +fz_setfontbbox(fz_font *font, int xmin, int ymin, int xmax, int ymax) +{ + font->bbox.x0 = xmin; + font->bbox.y0 = ymin; + font->bbox.x1 = xmax; + font->bbox.y1 = ymax; +} + +/* + * Freetype hooks + */ + +static FT_Library fz_ftlib = nil; + +#undef __FTERRORS_H__ +#define FT_ERRORDEF(e, v, s) { (e), (s) }, +#define FT_ERROR_START_LIST +#define FT_ERROR_END_LIST { 0, NULL } + +struct ft_error +{ + int err; + char *str; +}; + +const struct ft_error ft_errors[] = +{ +#include FT_ERRORS_H +}; + +char *ft_errorstring(int err) +{ + const struct ft_error *e; + + for (e = ft_errors; e->str != NULL; e++) + if (e->err == err) + return e->str; + + return "Unknown error"; +} + +static fz_error * +fz_initfreetype(void) +{ + int code; + int maj, min, pat; + + if (fz_ftlib) + return fz_okay; + + code = FT_Init_FreeType(&fz_ftlib); + if (code) + return fz_throw("cannot init freetype: %s", ft_errorstring(code)); + + FT_Library_Version(fz_ftlib, &maj, &min, &pat); + if (maj == 2 && min == 1 && pat < 7) + return fz_throw("freetype version too old: %d.%d.%d", maj, min, pat); + + return fz_okay; +} + +fz_error * +fz_newfontfromfile(fz_font **fontp, char *path, int index) +{ + fz_error *error; + fz_font *font; + int code; + + error = fz_initfreetype(); + if (error) + return fz_rethrow(error, "cannot init freetype library"); + + font = fz_newfont(); + if (!font) + return fz_throw("outofmem: font struct"); + + code = FT_New_Face(fz_ftlib, path, index, (FT_Face*)&font->ftface); + if (code) + { + fz_free(font); + return fz_throw("freetype: cannot load font: %s", ft_errorstring(code)); + } + + *fontp = font; + return fz_okay; +} + +fz_error * +fz_newfontfrombuffer(fz_font **fontp, unsigned char *data, int len, int index) +{ + fz_error *error; + fz_font *font; + int code; + + error = fz_initfreetype(); + if (error) + return fz_rethrow(error, "cannot init freetype library"); + + font = fz_newfont(); + if (!font) + return fz_throw("outofmem: font struct"); + + code = FT_New_Memory_Face(fz_ftlib, data, len, index, (FT_Face*)&font->ftface); + if (code) + { + fz_free(font); + return fz_throw("freetype: cannot load font: %s", ft_errorstring(code)); + } + + *fontp = font; + return fz_okay; +} + +fz_error * +fz_renderftglyph(fz_glyph *glyph, fz_font *font, int gid, fz_matrix trm) +{ + FT_Face face = font->ftface; + FT_Matrix m; + FT_Vector v; + FT_Error fterr; + int x, y; + +#if 0 + /* We lost this feature in refactoring. + * We can't access pdf_fontdesc metrics from fz_font. + * The pdf_fontdesc metrics are character based (cid), + * where the glyph being rendered is given by glyph (gid). + */ + if (font->ftsubstitute && font->wmode == 0) + { + fz_hmtx subw; + int realw; + float scale; + + FT_Set_Char_Size(face, 1000, 1000, 72, 72); + + fterr = FT_Load_Glyph(font->ftface, gid, + FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM); + if (fterr) + return fz_throw("freetype failed to load glyph: %s", ft_errorstring(fterr)); + + realw = ((FT_Face)font->ftface)->glyph->advance.x; + subw = fz_gethmtx(font, cid); // <-- this is the offender + if (realw) + scale = (float) subw.w / realw; + else + scale = 1.0; + + trm = fz_concat(fz_scale(scale, 1.0), trm); + } +#endif + + glyph->w = 0; + glyph->h = 0; + glyph->x = 0; + glyph->y = 0; + glyph->samples = nil; + + /* freetype mutilates complex glyphs if they are loaded + * with FT_Set_Char_Size 1.0. it rounds the coordinates + * before applying transformation. to get more precision in + * freetype, we shift part of the scale in the matrix + * into FT_Set_Char_Size instead + */ + + m.xx = trm.a * 64; /* should be 65536 */ + m.yx = trm.b * 64; + m.xy = trm.c * 64; + m.yy = trm.d * 64; + v.x = trm.e * 64; + v.y = trm.f * 64; + + FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */ + FT_Set_Transform(face, &m, &v); + + if (font->fthint) + { + /* Enable hinting, but keep the huge char size so that + * it is hinted for a character. This will in effect nullify + * the effect of grid fitting. This form of hinting should + * only be used for DynaLab and similar tricky TrueType fonts, + * so that we get the correct outline shape. + */ +#ifdef USE_HINTING + /* If you really want grid fitting, enable this code. */ + float scale = fz_matrixexpansion(trm); + m.xx = trm.a * 65536 / scale; + m.xy = trm.b * 65536 / scale; + m.yx = trm.c * 65536 / scale; + m.yy = trm.d * 65536 / scale; + v.x = 0; + v.y = 0; + FT_Set_Char_Size(face, 64 * scale, 64 * scale, 72, 72); + FT_Set_Transform(face, &m, &v); +#endif + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP); + if (fterr) + fz_warn("freetype load glyph: %s", ft_errorstring(fterr)); + } + else + { + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); + if (fterr) + fz_warn("freetype load glyph: %s", ft_errorstring(fterr)); + } + + fterr = FT_Render_Glyph(face->glyph, ft_render_mode_normal); + if (fterr) + fz_warn("freetype render glyph: %s", ft_errorstring(fterr)); + + glyph->w = face->glyph->bitmap.width; + glyph->h = face->glyph->bitmap.rows; + glyph->x = face->glyph->bitmap_left; + glyph->y = face->glyph->bitmap_top - glyph->h; + glyph->samples = face->glyph->bitmap.buffer; + + for (y = 0; y < glyph->h / 2; y++) + { + for (x = 0; x < glyph->w; x++) + { + unsigned char a = glyph->samples[y * glyph->w + x ]; + unsigned char b = glyph->samples[(glyph->h - y - 1) * glyph->w + x]; + glyph->samples[y * glyph->w + x ] = b; + glyph->samples[(glyph->h - y - 1) * glyph->w + x] = a; + } + } + + return fz_okay; +} + + +/* + * Type 3 fonts... + */ + +fz_error * +fz_newtype3font(fz_font **fontp, char *name, fz_matrix matrix, void **procs0) +{ + fz_font *font; + fz_tree **procs = (fz_tree**)procs0; + int i; + + font = fz_newfont(); + if (!font) + return fz_throw("outofmem: font struct"); + + font->t3procs = fz_malloc(sizeof(fz_tree*) * 256); + if (!font->t3procs) + { + fz_free(font); + return fz_throw("outofmem: type3 font charproc array"); + } + + font->t3matrix = matrix; + for (i = 0; i < 256; i++) + font->t3procs[i] = procs[i]; + + strlcpy(font->name, name, sizeof(font->name)); + + *fontp = font; + return fz_okay; +} + +fz_error * +fz_rendert3glyph(fz_glyph *glyph, fz_font *font, int gid, fz_matrix trm) +{ + fz_error *error; + fz_renderer *gc; + fz_tree *tree; + fz_matrix ctm; + fz_irect bbox; + + /* TODO: make it reentrant */ + static fz_pixmap *pixmap = nil; + if (pixmap) + { + fz_droppixmap(pixmap); + pixmap = nil; + } + + if (gid < 0 || gid > 255) + return fz_throw("assert: glyph out of range"); + + tree = font->t3procs[gid]; + if (!tree) + { + glyph->w = 0; + glyph->h = 0; + return fz_okay; + } + + /* XXX UGLY HACK XXX */ + extern fz_colorspace *pdf_devicegray; + + ctm = fz_concat(font->t3matrix, trm); + bbox = fz_roundrect(fz_boundtree(tree, ctm)); + + error = fz_newrenderer(&gc, pdf_devicegray, 1, 4096); + if (error) + return fz_rethrow(error, "cannot create renderer"); + error = fz_rendertree(&pixmap, gc, tree, ctm, bbox, 0); + fz_droprenderer(gc); + if (error) + return fz_rethrow(error, "cannot render glyph"); + + assert(pixmap->n == 1); + + glyph->x = pixmap->x; + glyph->y = pixmap->y; + glyph->w = pixmap->w; + glyph->h = pixmap->h; + glyph->samples = pixmap->samples; + + return fz_okay; +} + +void +fz_debugfont(fz_font *font) +{ + printf("font '%s' {\n", font->name); + + if (font->ftface) + { + printf(" freetype face %p\n", font->ftface); + if (font->ftsubstitute) + printf(" substitute font\n"); + } + + if (font->t3procs) + { + printf(" type3 matrix [%g %g %g %g]\n", + font->t3matrix.a, font->t3matrix.b, + font->t3matrix.c, font->t3matrix.d); + } + + printf(" bbox [%d %d %d %d]\n", + font->bbox.x0, font->bbox.y0, + font->bbox.x1, font->bbox.y1); + + printf("}\n"); +} + |