/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Type1 font support for MDVI * * We use T1lib only as a rasterizer, not to draw glyphs. */ #include "mdvi.h" #ifdef WITH_TYPE1_FONTS #include #include #include "private.h" static int t1lib_initialized = 0; typedef struct t1info { struct t1info *next; struct t1info *prev; char *fontname; /* (short) name of this font */ int t1id; /* T1lib's id for this font */ int hasmetrics; /* have we processed this font? */ TFMInfo *tfminfo; /* TFM data is shared */ DviFontMapInfo mapinfo; DviEncoding *encoding; } T1Info; static void t1_font_remove __PROTO((T1Info *)); static int t1_load_font __PROTO((DviParams *, DviFont *)); static int t1_font_get_glyph __PROTO((DviParams *, DviFont *, int)); static void t1_font_shrink_glyph __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *)); static void t1_free_data __PROTO((DviFont *)); static void t1_reset_font __PROTO((DviFont *)); static char *t1_lookup_font __PROTO((const char *, Ushort *, Ushort *)); /* only symbol exported by this file */ DviFontInfo t1_font_info = { "Type1", 1, /* scaling supported by format */ t1_load_font, t1_font_get_glyph, t1_font_shrink_glyph, mdvi_shrink_glyph_grey, t1_free_data, t1_reset_font, t1_lookup_font, /* lookup */ kpse_type1_format, NULL }; /* this seems good enough for most DVI files */ #define T1_HASH_SIZE 31 /* If these parameters change, we must delete all size information * in all fonts, and reset the device resolutions in T1lib */ static int t1lib_xdpi = -1; static int t1lib_ydpi = -1; static ListHead t1fonts = {NULL, NULL, 0}; static DviHashTable t1hash; /* Type1 fonts need their own `lookup' function. Here is how it works: * First we try to find the font by its given name. If that fails, we * query the font maps. A typical font map entry may contain the line * * ptmr8rn Times-Roman ".82 ExtendFont TeXBase1Encoding ReEncodeFont" <8r.enc private; if(info == NULL) return; DEBUG((DBG_FONTS, "(t1) resetting font `%s'\n", font->fontname)); /* just mark the font as not having metric info. It will be reset * automatically later */ info->hasmetrics = 0; } static void t1_transform_font(T1Info *info) { if(!info->hasmetrics && info->encoding != NULL) { DEBUG((DBG_TYPE1, "(t1) %s: encoding with vector `%s'\n", info->fontname, info->encoding->name)); T1_DeleteAllSizes(info->t1id); if(T1_ReencodeFont(info->t1id, info->encoding->vector) < 0) warning(_("%s: could not encode font\n"), info->fontname); } if(info->mapinfo.slant) { DEBUG((DBG_TYPE1, "(t1) %s: slanting by %.3f\n", info->fontname, MDVI_FMAP_SLANT(&info->mapinfo))); T1_SlantFont(info->t1id, MDVI_FMAP_SLANT(&info->mapinfo)); } if(info->mapinfo.extend) { DEBUG((DBG_TYPE1, "(t1) %s: extending by %.3f\n", info->fontname, MDVI_FMAP_EXTEND(&info->mapinfo))); T1_ExtendFont(info->t1id, MDVI_FMAP_EXTEND(&info->mapinfo)); } } /* if this function is called, we really need this font */ static int t1_really_load_font(DviParams *params, DviFont *font, T1Info *info) { int i; T1Info *old; int t1id; int copied; int status; DEBUG((DBG_TYPE1, "(t1) really_load_font(%s)\n", info->fontname)); /* if the parameters changed, reset T1lib */ if(t1lib_xdpi != params->dpi || t1lib_ydpi != params->vdpi) t1_reset_resolution(params->dpi, params->vdpi); /* if we already have a T1lib id, do nothing */ if(info->t1id != -1) { info->hasmetrics = 1; /* apply slant and extend again */ t1_transform_font(info); return 0; } /* before we even attempt to load the font, make sure we have metric * data for it */ info->tfminfo = mdvi_ps_get_metrics(info->fontname); if(info->tfminfo == NULL) { DEBUG((DBG_FONTS, "(t1) %s: no metric data, font ignored\n", info->fontname)); goto t1_error; } /* fix this */ font->design = info->tfminfo->design; /* check if we have a font with this name (maybe at a different size) */ old = (T1Info *)mdvi_hash_lookup(&t1hash, info->fontname); if(old == info) { /* let's avoid confusion */ old = NULL; } if(old && old->t1id != -1) { /* let's take advantage of T1lib's font sharing */ t1id = T1_CopyFont(old->t1id); DEBUG((DBG_TYPE1, "(t1) %s -> %d (CopyFont)\n", info->fontname, t1id)); copied = 1; } else { t1id = T1_AddFont(font->filename); DEBUG((DBG_TYPE1, "(t1) %s -> %d (AddFont)\n", info->fontname, t1id)); copied = 0; } if(t1id < 0) goto t1_error; info->t1id = t1id; /* * a minor optimization: If the old font in the hash table has * not been loaded yet, replace it by this one, so we can use * CopyFont later. */ if(old && old->t1id == -1) { DEBUG((DBG_TYPE1, "(t1) font `%s' exchanged in hash table\n", info->fontname)); mdvi_hash_remove(&t1hash, old->fontname); mdvi_hash_add(&t1hash, info->fontname, info, MDVI_HASH_UNCHECKED); } /* now let T1lib load it */ if(!copied && T1_LoadFont(info->t1id) < 0) { DEBUG((DBG_TYPE1, "(t1) T1_LoadFont(%d) failed with error %d\n", info->t1id, T1_errno)); goto t1_error; } DEBUG((DBG_TYPE1, "(t1) T1_LoadFont(%d) -> Ok\n", info->t1id)); /* 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); t1_transform_font(info); i = info->tfminfo->hic - info->tfminfo->loc + 1; if(i != font->hic - font->loc + 1) { /* reset to optimal size */ font->chars = mdvi_realloc(font->chars, i * sizeof(DviFontChar)); } /* get the scaled characters metrics */ get_tfm_chars(params, font, info->tfminfo, 0); info->hasmetrics = 1; DEBUG((DBG_TYPE1, "(t1) font `%s' really-loaded\n", info->fontname)); return 0; t1_error: /* some error does not allows us to use this font. We need to reset * the font structure, so the font system can try to read this * font in a different class */ /* first destroy the private data */ t1_font_remove(info); /* now reset all chars -- this is the important part */ mdvi_free(font->chars); font->chars = NULL; font->loc = font->hic = 0; return -1; } static int init_t1lib(DviParams *params) { int t1flags; #ifdef WORD_LITTLE_ENDIAN /* try making T1lib use bitmaps in our format, but if this * fails we'll convert the bitmap ourselves */ T1_SetBitmapPad(BITMAP_BITS); #endif T1_SetDeviceResolutions((float)params->dpi, (float)params->vdpi); t1flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE|T1_NO_AFM; if(DEBUGGING(TYPE1)) t1flags |= LOGFILE; if(T1_InitLib(t1flags) == NULL) return (t1lib_initialized = -1); if(DEBUGGING(TYPE1)) { DEBUG((DBG_TYPE1, "T1lib debugging output saved in t1lib.log\n")); T1_SetLogLevel(T1LOG_DEBUG); } /* initialize our hash table, but don't allocate memory for it * until we use it */ mdvi_hash_init(&t1hash); DEBUG((DBG_TYPE1, "(t1) t1lib %s initialized -- resolution is (%d, %d), pad is %d bits\n", T1_GetLibIdent(), params->dpi, params->vdpi, T1_GetBitmapPad())); t1lib_initialized = 1; t1lib_xdpi = params->dpi; t1lib_ydpi = params->vdpi; return 0; } static int t1_load_font(DviParams *params, DviFont *font) { T1Info *info; int i; if(t1lib_initialized < 0) return -1; else if(t1lib_initialized == 0 && init_t1lib(params) < 0) return -1; if(font->in != NULL) { /* we don't need this */ fclose(font->in); font->in = NULL; } info = xalloc(T1Info); /* * mark the font as `unregistered' with T1lib. It will * be added when we actually use it */ info->t1id = -1; /* add the font to our list */ info->fontname = font->fontname; info->hasmetrics = 0; info->encoding = NULL; info->mapinfo.psname = NULL; info->mapinfo.encoding = NULL; info->mapinfo.fontfile = NULL; info->mapinfo.extend = 0; info->mapinfo.slant = 0; info->encoding = NULL; /* create the hash table if we have not done so yet */ if(t1hash.nbucks == 0) mdvi_hash_create(&t1hash, T1_HASH_SIZE); mdvi_hash_add(&t1hash, info->fontname, info, MDVI_HASH_UNIQUE); listh_append(&t1fonts, LIST(info)); font->private = info; /* reset everything */ font->chars = xnalloc(DviFontChar, 256); font->loc = 0; font->hic = 255; for(i = 0; i < 256; i++) { font->chars[i].code = i; font->chars[i].offset = 1; font->chars[i].loaded = 0; font->chars[i].glyph.data = NULL; font->chars[i].shrunk.data = NULL; font->chars[i].grey.data = NULL; } return 0; } #define GLYPH_WIDTH(g) \ ((g)->metrics.rightSideBearing - (g)->metrics.leftSideBearing) #define GLYPH_HEIGHT(g) \ ((g)->metrics.ascent - (g)->metrics.descent) static inline BITMAP *t1_glyph_bitmap(GLYPH *glyph) { BITMAP *bm; int w, h; w = GLYPH_WIDTH(glyph); h = GLYPH_HEIGHT(glyph); if(!w || !h) return MDVI_GLYPH_EMPTY; switch(glyph->bpp << 3) { case 8: bm = bitmap_convert_lsb8(glyph->bits, w, h); break; default: warning(_("(t1) unsupported bitmap pad size %d\n"), glyph->bpp); bm = MDVI_GLYPH_EMPTY; break; } return bm; } static void t1_font_shrink_glyph(DviContext *dvi, DviFont *font, DviFontChar *ch, DviGlyph *dest) { double size; GLYPH *glyph; T1Info *info; T1_TMATRIX matrix; info = (T1Info *)font->private; ASSERT(info != NULL); DEBUG((DBG_TYPE1, "(t1) shrinking glyph for character %d in `%s' (%d,%d)\n", ch->code, font->fontname, ch->width, ch->height)); size = (double)font->scale / (dvi->params.tfm_conv * 0x100000); size = 72.0 * size / 72.27; matrix.cxx = 1.0/(double)dvi->params.hshrink; matrix.cyy = 1.0/(double)dvi->params.vshrink; matrix.cxy = 0.0; matrix.cyx = 0.0; glyph = T1_SetChar(info->t1id, ch->code, (float)size, &matrix); dest->data = t1_glyph_bitmap(glyph); dest->x = -glyph->metrics.leftSideBearing; dest->y = glyph->metrics.ascent; dest->w = GLYPH_WIDTH(glyph); dest->h = GLYPH_HEIGHT(glyph); #ifndef NODEBUG if(DEBUGGING(BITMAP_DATA)) { DEBUG((DBG_BITMAP_DATA, "(t1) %s: t1_shrink_glyph(%d): (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n", ch->glyph.w, ch->glyph.h, ch->glyph.x, ch->glyph.y, dest->w, dest->h, dest->x, dest->y)); bitmap_print(stderr, (BITMAP *)dest->data); } #endif /* transform the glyph - we could do this with t1lib, but we do * it ourselves for now */ font_transform_glyph(dvi->params.orientation, dest); } static int t1_font_get_glyph(DviParams *params, DviFont *font, int code) { T1Info *info = (T1Info *)font->private; GLYPH *glyph; DviFontChar *ch; double size; T1_TMATRIX matrix; int dpi; ASSERT(info != NULL); if(!info->hasmetrics && t1_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) { ch->glyph.x = ch->x; ch->glyph.y = ch->y; ch->glyph.w = ch->width; ch->glyph.h = ch->height; ch->glyph.data = NULL; return 0; } /* load the glyph with T1lib (this is done only once for each glyph) */ /* get size in TeX points (tfm_conv includes dpi and magnification) */ size = (double)font->scale / (params->tfm_conv * 0x100000); /* and transform into PostScript points */ size = 72.0 * size / 72.27; dpi = Max(font->hdpi, font->vdpi); /* we don't want the glyph to be cached twice (once by us, another by * T1lib), so we use an identity matrix to tell T1lib not to keep the * glyph around */ matrix.cxx = (double)font->hdpi / dpi; matrix.cyy = (double)font->vdpi / dpi; matrix.cxy = matrix.cyx = 0.0; glyph = T1_SetChar(info->t1id, ch->code, (float)size, &matrix); if(glyph == NULL) { ch->glyph.x = ch->x; ch->glyph.y = ch->y; ch->glyph.w = ch->width; ch->glyph.h = ch->height; ch->glyph.data = NULL; ch->missing = 1; return 0; } /* and make it a bitmap */ ch->glyph.data = t1_glyph_bitmap(glyph); ch->glyph.x = -glyph->metrics.leftSideBearing; ch->glyph.y = glyph->metrics.ascent; ch->glyph.w = GLYPH_WIDTH(glyph); ch->glyph.h = GLYPH_HEIGHT(glyph); /* let's also fix the glyph's origin * (which is not contained in the TFM) */ ch->x = ch->glyph.x; ch->y = ch->glyph.y; /* let's fix these too */ ch->width = ch->glyph.w; ch->height = ch->glyph.h; return 0; } static void t1_font_remove(T1Info *info) { T1Info *old; /* first remove it from our list */ listh_remove(&t1fonts, LIST(info)); /* it it's in the hash table, we may need to replace this by another font */ old = (T1Info *)mdvi_hash_lookup(&t1hash, info->fontname); if(old == info) { mdvi_hash_remove(&t1hash, info->fontname); /* go through the list and see if there is another * font with this name */ for(old = (T1Info *)t1fonts.head; old; old = old->next) if(STREQ(old->fontname, info->fontname)) break; if(old != NULL) mdvi_hash_add(&t1hash, old->fontname, old, MDVI_HASH_UNCHECKED); } /* release our encoding vector */ if(info->encoding) { DEBUG((DBG_TYPE1, "(t1) %s: releasing vector `%s'\n", info->fontname, info->encoding->name)); mdvi_release_encoding(info->encoding, 1); } /* now get rid of it */ if(info->t1id != -1) { DEBUG((DBG_TYPE1, "(t1) %s: T1_DeleteFont(%d)\n", info->fontname, info->t1id)); T1_DeleteFont(info->t1id); } else DEBUG((DBG_TYPE1, "(t1) %s: not loaded yet, DeleteFont skipped\n", info->fontname)); if(info->tfminfo) free_font_metrics(info->tfminfo); /*mdvi_free(info->fontname);*/ mdvi_free(info); } static void t1_free_data(DviFont *font) { /* called after all the glyphs are destroyed */ if(font->private == NULL) { /* this is perfectly normal, it just means the font has * not been requested by MDVI yet */ return; } /* destroy this data */ t1_font_remove((T1Info *)font->private); font->private = NULL; /* * if this is the last T1 font, reset the T1 library * It is important that we do this, because this is will be called * when the resolution or the magnification changes. */ if(t1fonts.count == 0) { DEBUG((DBG_TYPE1, "(t1) last font removed -- closing T1lib\n")); T1_CloseLib(); t1lib_initialized = 0; t1lib_xdpi = -1; t1lib_ydpi = -1; } } #endif /* WITH_TYPE1_FONTS */