diff options
Diffstat (limited to 'backend/dvi/mdvi-lib/t1.c')
-rw-r--r-- | backend/dvi/mdvi-lib/t1.c | 630 |
1 files changed, 630 insertions, 0 deletions
diff --git a/backend/dvi/mdvi-lib/t1.c b/backend/dvi/mdvi-lib/t1.c new file mode 100644 index 00000000..bc910ba6 --- /dev/null +++ b/backend/dvi/mdvi-lib/t1.c @@ -0,0 +1,630 @@ +/* + * 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. + */ + +/* + * Type1 font support for MDVI + * + * We use T1lib only as a rasterizer, not to draw glyphs. + */ + +#include <config.h> +#include "mdvi.h" + +#ifdef WITH_TYPE1_FONTS + +#include <stdio.h> +#include <t1lib.h> +#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 <ptmr + * + * which means: If you're looking for the font `ptmr8rn' load `Times-Roman' + * which is in `ptmr' instead, and extend it by 0.82 points, then reencode + * it with the vector TeXBase1Encoding from the file `8r.enc'. This will + * fail if the entry looks like this: + * + * ptmr8rn Times-Roman ".82 ExtendFont TeXBase1Encoding ReEncodeFont" <8r.enc + * + * because to deal with this we would need to be able to locate the font file + * for the `Times-Roman' font ourselves, and that's beyond the scope of mdvi. + * But hey, we tried hard. + */ +char *t1_lookup_font(const char *name, Ushort *hdpi, Ushort *vdpi) +{ + char *filename; + char *newname; + const char *ext; + DviFontMapInfo info; + + DEBUG((DBG_TYPE1, "(t1) looking for `%s'\n", name)); + + /* first let's try the font we were asked for */ + filename = kpse_find_file(name, kpse_type1_format, 1); + if(filename != NULL) { + /* we got it */ + return filename; + } + + DEBUG((DBG_TYPE1, "(t1) %s: not found, querying font maps\n", name)); + /* now query the fontmap */ + if(mdvi_query_fontmap(&info, name) < 0) { + /* it's not there either */ + return NULL; + } + + /* check what we got */ + if(info.fullfile) { + DEBUG((DBG_TYPE1, "(t1) %s: found `%s' (cached)\n", + name, info.fullfile)); + /* this is a cached lookup */ + return mdvi_strdup(info.fullfile); + } + + /* no file associated to this font? */ + if(info.fontfile == NULL) + return info.psname ? mdvi_ps_find_font(info.psname) : NULL; + + /* let's extract the extension */ + ext = file_extension(info.fontfile); + if(ext && !STREQ(ext, "pfa") && !STREQ(ext, "pfb")) { + DEBUG((DBG_TYPE1, + "(t1) %s: associated name `%s' is not Type1\n", + name, info.fontfile)); + /* it's not a Type1 font */ + return NULL; + } + + /* get the `base' name */ + if(ext) { + newname = mdvi_strdup(name); + newname[ext - info.fontfile - 1] = 0; + } else + newname = (char *)name; /* we don't modify this */ + + /* look it up */ + DEBUG((DBG_TYPE1, "(t1) looking for `%s' on behalf of `%s'\n", + newname, name)); + filename = kpse_find_file(newname, kpse_type1_format, 1); + + /* we don't need this anymore */ + if(newname != name) + mdvi_free(newname); + if(filename == NULL) { + DEBUG((DBG_TYPE1, "(t1) %s: not found\n", name)); + return NULL; + } + + DEBUG((DBG_TYPE1, "(t1) %s: found as `%s'\n", name, filename)); + /* got it! let's remember this */ + mdvi_add_fontmap_file(name, filename); + return filename; +} + +static void t1_reset_resolution(int xdpi, int ydpi) +{ + int i; + int nfonts; + + DEBUG((DBG_TYPE1, "(t1) resetting device resolution (current: (%d,%d))\n", + t1lib_xdpi, t1lib_ydpi)); +#if T1LIB_VERSION < 5 + nfonts = T1_Get_no_fonts(); +#else + nfonts = T1_GetNoFonts(); +#endif + + for(i = 0; i < nfonts; i++) + T1_DeleteAllSizes(i); + /* reset device resolutions */ + if(T1_SetDeviceResolutions((float)xdpi, (float)ydpi) < 0) + mdvi_warning(_("(t1) failed to reset device resolution\n")); + else + DEBUG((DBG_TYPE1, + "(t1) reset successful, new resolution is (%d, %d)\n", + xdpi, ydpi)); + t1lib_xdpi = xdpi; + t1lib_ydpi = ydpi; +} + +static void t1_reset_font(DviFont *font) +{ + T1Info *info = (T1Info *)font->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) + mdvi_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, (unsigned char *)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, (unsigned char *)old->fontname); + mdvi_hash_add(&t1hash, (unsigned char *)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, (unsigned char *) 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) +{ + int w, h, pad; + + w = GLYPH_WIDTH(glyph); + h = GLYPH_HEIGHT(glyph); + + if(!w || !h) + return MDVI_GLYPH_EMPTY; + + pad = T1_GetBitmapPad(); + return bitmap_convert_lsb8((unsigned char *)glyph->bits, w, h, ROUND(w, pad) * (pad >> 3)); +} + +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, (unsigned char *)info->fontname); + if(old == info) { + mdvi_hash_remove(&t1hash, (unsigned char *) 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, (unsigned char *) 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 */ |