diff options
Diffstat (limited to 'backend/dvi/mdvi-lib/tt.c')
-rw-r--r-- | backend/dvi/mdvi-lib/tt.c | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/backend/dvi/mdvi-lib/tt.c b/backend/dvi/mdvi-lib/tt.c new file mode 100644 index 00000000..e85d8e70 --- /dev/null +++ b/backend/dvi/mdvi-lib/tt.c @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include "mdvi.h" + +#ifdef WITH_TRUETYPE_FONTS + +#include <string.h> +#include <freetype.h> +#include <ftxpost.h> +#include <ftxerr18.h> + +#include "private.h" + +static TT_Engine tt_handle; +static int initialized = 0; + +typedef struct ftinfo { + struct ftinfo *next; + struct ftinfo *prev; + char *fontname; + char *fmfname; + TT_Face face; + TT_Instance instance; + TT_Glyph glyph; + int hasmetrics; + int loaded; + int fmftype; + TFMInfo *tfminfo; + DviFontMapInfo mapinfo; + DviEncoding *encoding; +} FTInfo; + +static int tt_load_font __PROTO((DviParams *, DviFont *)); +static int tt_font_get_glyph __PROTO((DviParams *, DviFont *, int)); +static void tt_free_data __PROTO((DviFont *)); +static void tt_reset_font __PROTO((DviFont *)); +static void tt_shrink_glyph + __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *)); +static void tt_font_remove __PROTO((FTInfo *)); + +DviFontInfo tt_font_info = { + "TT", + 0, + tt_load_font, + tt_font_get_glyph, + tt_shrink_glyph, + mdvi_shrink_glyph_grey, + tt_free_data, /* free */ + tt_reset_font, /* reset */ + NULL, /* lookup */ + kpse_truetype_format, + NULL +}; + +#define FT_HASH_SIZE 31 + +static ListHead ttfonts = {NULL, NULL, 0}; + +static int init_freetype(void) +{ + TT_Error code; + + ASSERT(initialized == 0); + code = TT_Init_FreeType(&tt_handle); + if(code) { + DEBUG((DBG_TT, "(tt) Init_Freetype: error %d\n", code)); + return -1; + } + code = TT_Init_Post_Extension(tt_handle); + if(code) { + TT_Done_FreeType(tt_handle); + return -1; + } + /* we're on */ + initialized = 1; + return 0; +} + +static void tt_encode_font(DviFont *font, FTInfo *info) +{ + TT_Face_Properties prop; + int i; + + if(TT_Get_Face_Properties(info->face, &prop)) + return; + + for(i = 0; i < prop.num_Glyphs; i++) { + char *string; + int ndx; + + if(TT_Get_PS_Name(info->face, i, &string)) + continue; + ndx = mdvi_encode_glyph(info->encoding, string); + if(ndx < font->loc || ndx > font->hic) + continue; + font->chars[ndx - font->loc].code = i; + } +} + +static int tt_really_load_font(DviParams *params, DviFont *font, FTInfo *info) +{ + DviFontChar *ch; + TFMChar *ptr; + Int32 z, alpha, beta; + int i; + FTInfo *old; + TT_Error status; + double point_size; + static int warned = 0; + TT_CharMap cmap; + TT_Face_Properties props; + int map_found; + + DEBUG((DBG_TT, "(tt) really_load_font(%s)\n", info->fontname)); + + /* get the point size */ + point_size = (double)font->scale / (params->tfm_conv * 0x100000); + point_size = 72.0 * point_size / 72.27; + if(info->loaded) { + /* just reset the size info */ + TT_Set_Instance_Resolutions(info->instance, + params->dpi, params->vdpi); + TT_Set_Instance_CharSize(info->instance, FROUND(point_size * 64)); + /* FIXME: should extend/slant again */ + info->hasmetrics = 1; + return 0; + } + + /* load the face */ + DEBUG((DBG_TT, "(tt) loading new face `%s'\n", + info->fontname)); + status = TT_Open_Face(tt_handle, font->filename, &info->face); + if(status) { + mdvi_warning(_("(tt) %s: could not load face: %s\n"), + info->fontname, TT_ErrToString18(status)); + return -1; + } + + /* create a new instance of this face */ + status = TT_New_Instance(info->face, &info->instance); + if(status) { + mdvi_warning(_("(tt) %s: could not create face: %s\n"), + info->fontname, TT_ErrToString18(status)); + TT_Close_Face(info->face); + return -1; + } + + /* create a glyph */ + status = TT_New_Glyph(info->face, &info->glyph); + if(status) { + mdvi_warning(_("(tt) %s: could not create glyph: %s\n"), + info->fontname, TT_ErrToString18(status)); + goto tt_error; + } + + /* + * We'll try to find a Unicode charmap. It's not that important that we + * actually find one, especially if the fontmap files are installed + * properly, but it's good to have some predefined behaviour + */ + TT_Get_Face_Properties(info->face, &props); + + map_found = -1; + for(i = 0; map_found < 0 && i < props.num_CharMaps; i++) { + TT_UShort pid, eid; + + TT_Get_CharMap_ID(info->face, i, &pid, &eid); + switch(pid) { + case TT_PLATFORM_APPLE_UNICODE: + map_found = i; + break; + case TT_PLATFORM_ISO: + if(eid == TT_ISO_ID_7BIT_ASCII || + eid == TT_ISO_ID_8859_1) + map_found = 1; + break; + case TT_PLATFORM_MICROSOFT: + if(eid == TT_MS_ID_UNICODE_CS) + map_found = 1; + break; + } + } + if(map_found < 0) { + mdvi_warning(_("(tt) %s: no acceptable map found, using #0\n"), + info->fontname); + map_found = 0; + } + DEBUG((DBG_TT, "(tt) %s: using charmap #%d\n", + info->fontname, map_found)); + TT_Get_CharMap(info->face, map_found, &cmap); + + DEBUG((DBG_TT, "(tt) %s: Set_Char_Size(%.2f, %d, %d)\n", + font->fontname, point_size, font->hdpi, font->vdpi)); + status = TT_Set_Instance_Resolutions(info->instance, + params->dpi, params->vdpi); + if(status) { + error(_("(tt) %s: could not set resolution: %s\n"), + info->fontname, TT_ErrToString18(status)); + goto tt_error; + } + status = TT_Set_Instance_CharSize(info->instance, + FROUND(point_size * 64)); + if(status) { + error(_("(tt) %s: could not set point size: %s\n"), + info->fontname, TT_ErrToString18(status)); + goto tt_error; + } + + /* after this point we don't fail */ + + /* get information from the fontmap */ + status = mdvi_query_fontmap(&info->mapinfo, info->fontname); + if(!status && info->mapinfo.encoding) + info->encoding = mdvi_request_encoding(info->mapinfo.encoding); + else + info->encoding = NULL; + + if(info->encoding != NULL) { + TT_Post post; + + status = TT_Load_PS_Names(info->face, &post); + if(status) { + mdvi_warning(_("(tt) %s: could not load PS name table\n"), + info->fontname); + mdvi_release_encoding(info->encoding, 0); + info->encoding = NULL; + } + } + + /* get the metrics. If this fails, it's not fatal, but certainly bad */ + info->tfminfo = get_font_metrics(info->fontname, + info->fmftype, info->fmfname); + + if(info->tfminfo == NULL) { + mdvi_warning("(tt) %s: no metrics data, font ignored\n", + info->fontname); + goto tt_error; + } + /* fix this */ + font->design = info->tfminfo->design; + + /* get the scaled character metrics */ + get_tfm_chars(params, font, info->tfminfo, 0); + + if(info->encoding) + tt_encode_font(font, info); + else { + mdvi_warning(_("%s: no encoding vector found, expect bad output\n"), + info->fontname); + /* this is better than nothing */ + for(i = font->loc; i <= font->hic; i++) + font->chars[i - font->loc].code = TT_Char_Index(cmap, i); + } + + info->loaded = 1; + info->hasmetrics = 1; + return 0; + +tt_error: + tt_font_remove(info); + mdvi_free(font->chars); + font->chars = NULL; + font->loc = font->hic = 0; + return -1; +} + +static int tt_load_font(DviParams *params, DviFont *font) +{ + int i; + FTInfo *info; + + if(!initialized && init_freetype() < 0) + return -1; + + if(font->in != NULL) { + fclose(font->in); + font->in = NULL; + } + + info = xalloc(FTInfo); + + memzero(info, sizeof(FTInfo)); + info->fmftype = DviFontAny; /* any metrics type will do */ + info->fmfname = lookup_font_metrics(font->fontname, &info->fmftype); + info->fontname = font->fontname; + info->hasmetrics = 0; + info->loaded = 0; + + /* these will be obtained from the fontmaps */ + info->mapinfo.psname = NULL; + info->mapinfo.encoding = NULL; + info->mapinfo.fontfile = NULL; + info->mapinfo.extend = 0; + info->mapinfo.slant = 0; + + /* initialize these */ + font->chars = xnalloc(DviFontChar, 256); + font->loc = 0; + font->hic = 255; + for(i = 0; i < 256; i++) { + font->chars[i].offset = 1; + font->chars[i].glyph.data = NULL; + font->chars[i].shrunk.data = NULL; + font->chars[i].grey.data = NULL; + } + + if(info->fmfname == NULL) + mdvi_warning(_("(tt) %s: no font metric data\n"), font->fontname); + + listh_append(&ttfonts, LIST(info)); + font->private = info; + + return 0; +} + +static int tt_get_bitmap(DviParams *params, DviFont *font, + int code, double xscale, double yscale, DviGlyph *glyph) +{ + TT_Outline outline; + TT_Raster_Map raster; + TT_BBox bbox; + TT_Glyph_Metrics metrics; + TT_Matrix mat; + FTInfo *info; + int error; + int have_outline = 0; + int w, h; + + info = (FTInfo *)font->private; + if(info == NULL) + return -1; + + error = TT_Load_Glyph(info->instance, info->glyph, + code, TTLOAD_DEFAULT); + if(error) goto tt_error; + error = TT_Get_Glyph_Outline(info->glyph, &outline); + if(error) goto tt_error; + have_outline = 1; + mat.xx = FROUND(xscale * 65536); + mat.yy = FROUND(yscale * 65536); + mat.yx = 0; + mat.xy = 0; + TT_Transform_Outline(&outline, &mat); + error = TT_Get_Outline_BBox(&outline, &bbox); + if(error) goto tt_error; + bbox.xMin &= -64; + bbox.yMin &= -64; + bbox.xMax = (bbox.xMax + 63) & -64; + bbox.yMax = (bbox.yMax + 63) & -64; + w = (bbox.xMax - bbox.xMin) / 64; + h = (bbox.yMax - bbox.yMin) / 64; + + glyph->w = w; + glyph->h = h; + glyph->x = -bbox.xMin / 64; + glyph->y = bbox.yMax / 64; + if(!w || !h) + goto tt_error; + raster.rows = h; + raster.width = w; + raster.cols = ROUND(w, 8); + raster.size = h * raster.cols; + raster.flow = TT_Flow_Down; + raster.bitmap = mdvi_calloc(h, raster.cols); + + TT_Translate_Outline(&outline, -bbox.xMin, -bbox.yMin); + TT_Get_Outline_Bitmap(tt_handle, &outline, &raster); + glyph->data = bitmap_convert_msb8(raster.bitmap, w, h, ROUND(w, 8)); + TT_Done_Outline(&outline); + mdvi_free(raster.bitmap); + + return 0; +tt_error: + if(have_outline) + TT_Done_Outline(&outline); + return -1; +} + +static int tt_font_get_glyph(DviParams *params, DviFont *font, int code) +{ + FTInfo *info = (FTInfo *)font->private; + DviFontChar *ch; + int error; + double xs, ys; + int dpi; + + ASSERT(info != NULL); + if(!info->hasmetrics && tt_really_load_font(params, font, info) < 0) + return -1; + ch = FONTCHAR(font, code); + if(!ch || !glyph_present(ch)) + return -1; + ch->loaded = 1; + if(!ch->width || !ch->height) + goto blank; + if(ch->code == 0) { + ch->glyph.data = NULL; + goto missing; + } + /* get the glyph */ + dpi = Max(font->hdpi, font->vdpi); + error = tt_get_bitmap(params, font, ch->code, + (double)font->hdpi / dpi, + (double)font->vdpi / dpi, + &ch->glyph); + if(error) + goto missing; + ch->x = ch->glyph.x; + ch->y = ch->glyph.y; + + return 0; + +missing: + ch->glyph.data = MDVI_GLYPH_EMPTY; + ch->missing = 1; +blank: + ch->glyph.w = ch->width; + ch->glyph.h = ch->height; + ch->glyph.x = ch->x; + ch->glyph.y = ch->y; + return 0; +} + +static void tt_shrink_glyph(DviContext *dvi, DviFont *font, DviFontChar *ch, DviGlyph *dest) +{ + tt_get_bitmap(&dvi->params, font, + ch->code, + (double)font->hdpi / (dvi->params.dpi * dvi->params.hshrink), + (double)font->vdpi / (dvi->params.vdpi * dvi->params.vshrink), + dest); + /* transform the glyph for the current orientation */ + font_transform_glyph(dvi->params.orientation, dest); +} + +static void tt_reset_font(DviFont *font) +{ + FTInfo *info = (FTInfo *)font->private; + + if(info == NULL) + return; + info->hasmetrics = 0; +} + +static void tt_font_remove(FTInfo *info) +{ + FTInfo *old; + + if(info->loaded) { + /* all fonts in the hash table have called TT_Open_Face */ + TT_Done_Instance(info->instance); + TT_Close_Face(info->face); + } + listh_remove(&ttfonts, LIST(info)); + /* release our encodings */ + if(info->encoding) + mdvi_release_encoding(info->encoding, 1); + /* and destroy the font */ + if(info->tfminfo) + free_font_metrics(info->tfminfo); + if(info->fmfname) + mdvi_free(info->fmfname); + mdvi_free(info); +} + +static void tt_free_data(DviFont *font) +{ + if(font->private == NULL) + return; + + tt_font_remove((FTInfo *)font->private); + if(initialized && ttfonts.count == 0) { + DEBUG((DBG_TT, "(tt) last font removed -- closing FreeType\n")); + TT_Done_FreeType(tt_handle); + initialized = 0; + } +} + +#endif /* WITH_TRUETYPE_FONTS */ |