2 * Copyright (C) 2000, Matias Atria
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Type1 font support for MDVI
22 * We use T1lib only as a rasterizer, not to draw glyphs.
27 #ifdef WITH_TYPE1_FONTS
33 static int t1lib_initialized = 0;
35 typedef struct t1info {
38 char *fontname; /* (short) name of this font */
39 int t1id; /* T1lib's id for this font */
40 int hasmetrics; /* have we processed this font? */
41 TFMInfo *tfminfo; /* TFM data is shared */
42 DviFontMapInfo mapinfo;
43 DviEncoding *encoding;
46 static void t1_font_remove __PROTO((T1Info *));
47 static int t1_load_font __PROTO((DviParams *, DviFont *));
48 static int t1_font_get_glyph __PROTO((DviParams *, DviFont *, int));
49 static void t1_font_shrink_glyph
50 __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *));
51 static void t1_free_data __PROTO((DviFont *));
52 static void t1_reset_font __PROTO((DviFont *));
53 static char *t1_lookup_font __PROTO((const char *, Ushort *, Ushort *));
55 /* only symbol exported by this file */
56 DviFontInfo t1_font_info = {
58 1, /* scaling supported by format */
62 mdvi_shrink_glyph_grey,
65 t1_lookup_font, /* lookup */
70 /* this seems good enough for most DVI files */
71 #define T1_HASH_SIZE 31
73 /* If these parameters change, we must delete all size information
74 * in all fonts, and reset the device resolutions in T1lib */
75 static int t1lib_xdpi = -1;
76 static int t1lib_ydpi = -1;
78 static ListHead t1fonts = {NULL, NULL, 0};
79 static DviHashTable t1hash;
81 /* Type1 fonts need their own `lookup' function. Here is how it works:
82 * First we try to find the font by its given name. If that fails, we
83 * query the font maps. A typical font map entry may contain the line
85 * ptmr8rn Times-Roman ".82 ExtendFont TeXBase1Encoding ReEncodeFont" <8r.enc <ptmr
87 * which means: If you're looking for the font `ptmr8rn' load `Times-Roman'
88 * which is in `ptmr' instead, and extend it by 0.82 points, then reencode
89 * it with the vector TeXBase1Encoding from the file `8r.enc'. This will
90 * fail if the entry looks like this:
92 * ptmr8rn Times-Roman ".82 ExtendFont TeXBase1Encoding ReEncodeFont" <8r.enc
94 * because to deal with this we would need to be able to locate the font file
95 * for the `Times-Roman' font ourselves, and that's beyond the scope of mdvi.
96 * But hey, we tried hard.
98 char *t1_lookup_font(const char *name, Ushort *hdpi, Ushort *vdpi)
105 DEBUG((DBG_TYPE1, "(t1) looking for `%s'\n", name));
107 /* first let's try the font we were asked for */
108 filename = kpse_find_file(name, kpse_type1_format, 1);
109 if(filename != NULL) {
114 DEBUG((DBG_TYPE1, "(t1) %s: not found, querying font maps\n", name));
115 /* now query the fontmap */
116 if(mdvi_query_fontmap(&info, name) < 0) {
117 /* it's not there either */
121 /* check what we got */
123 DEBUG((DBG_TYPE1, "(t1) %s: found `%s' (cached)\n",
124 name, info.fullfile));
125 /* this is a cached lookup */
126 return xstrdup(info.fullfile);
129 /* no file associated to this font? */
130 if(info.fontfile == NULL)
131 return info.psname ? mdvi_ps_find_font(info.psname) : NULL;
133 /* let's extract the extension */
134 ext = file_extension(info.fontfile);
135 if(ext && !STREQ(ext, "pfa") && !STREQ(ext, "pfb")) {
137 "(t1) %s: associated name `%s' is not Type1\n",
138 name, info.fontfile));
139 /* it's not a Type1 font */
143 /* get the `base' name */
145 newname = xstrdup(name);
146 newname[ext - info.fontfile - 1] = 0;
148 newname = (char *)name; /* we don't modify this */
151 DEBUG((DBG_TYPE1, "(t1) looking for `%s' on behalf of `%s'\n",
153 filename = kpse_find_file(newname, kpse_type1_format, 1);
155 /* we don't need this anymore */
158 if(filename == NULL) {
159 DEBUG((DBG_TYPE1, "(t1) %s: not found\n", name));
163 DEBUG((DBG_TYPE1, "(t1) %s: found as `%s'\n", name, filename));
164 /* got it! let's remember this */
165 mdvi_add_fontmap_file(name, filename);
169 static void t1_reset_resolution(int xdpi, int ydpi)
174 DEBUG((DBG_TYPE1, "(t1) resetting device resolution (current: (%d,%d))\n",
175 t1lib_xdpi, t1lib_ydpi));
177 nfonts = T1_Get_no_fonts();
178 for(i = 0; i < nfonts; i++)
179 T1_DeleteAllSizes(i);
180 /* reset device resolutions */
181 if(T1_SetDeviceResolutions((float)xdpi, (float)ydpi) < 0)
182 warning(_("(t1) failed to reset device resolution\n"));
185 "(t1) reset successful, new resolution is (%d, %d)\n",
191 static void t1_reset_font(DviFont *font)
193 T1Info *info = (T1Info *)font->private;
197 DEBUG((DBG_FONTS, "(t1) resetting font `%s'\n", font->fontname));
198 /* just mark the font as not having metric info. It will be reset
199 * automatically later */
200 info->hasmetrics = 0;
203 static void t1_transform_font(T1Info *info)
205 if(!info->hasmetrics && info->encoding != NULL) {
206 DEBUG((DBG_TYPE1, "(t1) %s: encoding with vector `%s'\n",
207 info->fontname, info->encoding->name));
208 T1_DeleteAllSizes(info->t1id);
209 if(T1_ReencodeFont(info->t1id, info->encoding->vector) < 0)
210 warning(_("%s: could not encode font\n"), info->fontname);
212 if(info->mapinfo.slant) {
213 DEBUG((DBG_TYPE1, "(t1) %s: slanting by %.3f\n",
215 MDVI_FMAP_SLANT(&info->mapinfo)));
216 T1_SlantFont(info->t1id,
217 MDVI_FMAP_SLANT(&info->mapinfo));
219 if(info->mapinfo.extend) {
220 DEBUG((DBG_TYPE1, "(t1) %s: extending by %.3f\n",
222 MDVI_FMAP_EXTEND(&info->mapinfo)));
223 T1_ExtendFont(info->t1id,
224 MDVI_FMAP_EXTEND(&info->mapinfo));
228 /* if this function is called, we really need this font */
229 static int t1_really_load_font(DviParams *params, DviFont *font, T1Info *info)
237 DEBUG((DBG_TYPE1, "(t1) really_load_font(%s)\n", info->fontname));
239 /* if the parameters changed, reset T1lib */
240 if(t1lib_xdpi != params->dpi || t1lib_ydpi != params->vdpi)
241 t1_reset_resolution(params->dpi, params->vdpi);
243 /* if we already have a T1lib id, do nothing */
244 if(info->t1id != -1) {
245 info->hasmetrics = 1;
246 /* apply slant and extend again */
247 t1_transform_font(info);
251 /* before we even attempt to load the font, make sure we have metric
253 info->tfminfo = mdvi_ps_get_metrics(info->fontname);
254 if(info->tfminfo == NULL) {
256 "(t1) %s: no metric data, font ignored\n",
261 font->design = info->tfminfo->design;
263 /* check if we have a font with this name (maybe at a different size) */
264 old = (T1Info *)mdvi_hash_lookup(&t1hash, info->fontname);
266 /* let's avoid confusion */
269 if(old && old->t1id != -1) {
270 /* let's take advantage of T1lib's font sharing */
271 t1id = T1_CopyFont(old->t1id);
272 DEBUG((DBG_TYPE1, "(t1) %s -> %d (CopyFont)\n",
273 info->fontname, t1id));
276 t1id = T1_AddFont(font->filename);
277 DEBUG((DBG_TYPE1, "(t1) %s -> %d (AddFont)\n",
278 info->fontname, t1id));
286 * a minor optimization: If the old font in the hash table has
287 * not been loaded yet, replace it by this one, so we can use
290 if(old && old->t1id == -1) {
291 DEBUG((DBG_TYPE1, "(t1) font `%s' exchanged in hash table\n",
293 mdvi_hash_remove(&t1hash, old->fontname);
294 mdvi_hash_add(&t1hash, info->fontname,
295 info, MDVI_HASH_UNCHECKED);
298 /* now let T1lib load it */
299 if(!copied && T1_LoadFont(info->t1id) < 0) {
300 DEBUG((DBG_TYPE1, "(t1) T1_LoadFont(%d) failed with error %d\n",
301 info->t1id, T1_errno));
304 DEBUG((DBG_TYPE1, "(t1) T1_LoadFont(%d) -> Ok\n", info->t1id));
306 /* get information from the fontmap */
307 status = mdvi_query_fontmap(&info->mapinfo, info->fontname);
308 if(!status && info->mapinfo.encoding)
309 info->encoding = mdvi_request_encoding(info->mapinfo.encoding);
310 t1_transform_font(info);
312 i = info->tfminfo->hic - info->tfminfo->loc + 1;
313 if(i != font->hic - font->loc + 1) {
314 /* reset to optimal size */
315 font->chars = xrealloc(font->chars, i * sizeof(DviFontChar));
318 /* get the scaled characters metrics */
319 get_tfm_chars(params, font, info->tfminfo, 0);
320 info->hasmetrics = 1;
322 DEBUG((DBG_TYPE1, "(t1) font `%s' really-loaded\n", info->fontname));
326 /* some error does not allows us to use this font. We need to reset
327 * the font structure, so the font system can try to read this
328 * font in a different class */
330 /* first destroy the private data */
331 t1_font_remove(info);
332 /* now reset all chars -- this is the important part */
335 font->loc = font->hic = 0;
339 static int init_t1lib(DviParams *params)
343 #ifdef WORD_LITTLE_ENDIAN
344 /* try making T1lib use bitmaps in our format, but if this
345 * fails we'll convert the bitmap ourselves */
346 T1_SetBitmapPad(BITMAP_BITS);
348 T1_SetDeviceResolutions((float)params->dpi, (float)params->vdpi);
349 t1flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE|T1_NO_AFM;
352 if(T1_InitLib(t1flags) == NULL)
353 return (t1lib_initialized = -1);
354 if(DEBUGGING(TYPE1)) {
355 DEBUG((DBG_TYPE1, "T1lib debugging output saved in t1lib.log\n"));
356 T1_SetLogLevel(T1LOG_DEBUG);
358 /* initialize our hash table, but don't allocate memory for it
360 mdvi_hash_init(&t1hash);
361 DEBUG((DBG_TYPE1, "(t1) t1lib %s initialized -- resolution is (%d, %d), pad is %d bits\n",
362 T1_GetLibIdent(), params->dpi, params->vdpi, T1_GetBitmapPad()));
363 t1lib_initialized = 1;
364 t1lib_xdpi = params->dpi;
365 t1lib_ydpi = params->vdpi;
369 static int t1_load_font(DviParams *params, DviFont *font)
374 if(t1lib_initialized < 0)
376 else if(t1lib_initialized == 0 && init_t1lib(params) < 0)
379 if(font->in != NULL) {
380 /* we don't need this */
385 info = xalloc(T1Info);
388 * mark the font as `unregistered' with T1lib. It will
389 * be added when we actually use it
393 /* add the font to our list */
394 info->fontname = font->fontname;
395 info->hasmetrics = 0;
396 info->encoding = NULL;
397 info->mapinfo.psname = NULL;
398 info->mapinfo.encoding = NULL;
399 info->mapinfo.fontfile = NULL;
400 info->mapinfo.extend = 0;
401 info->mapinfo.slant = 0;
402 info->encoding = NULL;
404 /* create the hash table if we have not done so yet */
405 if(t1hash.nbucks == 0)
406 mdvi_hash_create(&t1hash, T1_HASH_SIZE);
407 mdvi_hash_add(&t1hash, info->fontname, info, MDVI_HASH_UNIQUE);
408 listh_append(&t1fonts, LIST(info));
410 font->private = info;
412 /* reset everything */
413 font->chars = xnalloc(DviFontChar, 256);
416 for(i = 0; i < 256; i++) {
417 font->chars[i].code = i;
418 font->chars[i].offset = 1;
419 font->chars[i].loaded = 0;
420 font->chars[i].glyph.data = NULL;
421 font->chars[i].shrunk.data = NULL;
422 font->chars[i].grey.data = NULL;
428 #define GLYPH_WIDTH(g) \
429 ((g)->metrics.rightSideBearing - (g)->metrics.leftSideBearing)
430 #define GLYPH_HEIGHT(g) \
431 ((g)->metrics.ascent - (g)->metrics.descent)
433 static inline BITMAP *t1_glyph_bitmap(GLYPH *glyph)
438 w = GLYPH_WIDTH(glyph);
439 h = GLYPH_HEIGHT(glyph);
442 return MDVI_GLYPH_EMPTY;
443 switch(glyph->bpp << 3) {
445 bm = bitmap_convert_lsb8(glyph->bits, w, h);
448 warning(_("(t1) unsupported bitmap pad size %d\n"),
450 bm = MDVI_GLYPH_EMPTY;
456 static void t1_font_shrink_glyph(DviContext *dvi, DviFont *font, DviFontChar *ch, DviGlyph *dest)
463 info = (T1Info *)font->private;
464 ASSERT(info != NULL);
466 DEBUG((DBG_TYPE1, "(t1) shrinking glyph for character %d in `%s' (%d,%d)\n",
467 ch->code, font->fontname, ch->width, ch->height));
468 size = (double)font->scale / (dvi->params.tfm_conv * 0x100000);
469 size = 72.0 * size / 72.27;
470 matrix.cxx = 1.0/(double)dvi->params.hshrink;
471 matrix.cyy = 1.0/(double)dvi->params.vshrink;
474 glyph = T1_SetChar(info->t1id, ch->code, (float)size, &matrix);
476 dest->data = t1_glyph_bitmap(glyph);
477 dest->x = -glyph->metrics.leftSideBearing;
478 dest->y = glyph->metrics.ascent;
479 dest->w = GLYPH_WIDTH(glyph);
480 dest->h = GLYPH_HEIGHT(glyph);
483 if(DEBUGGING(BITMAP_DATA)) {
484 DEBUG((DBG_BITMAP_DATA,
485 "(t1) %s: t1_shrink_glyph(%d): (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n",
486 ch->glyph.w, ch->glyph.h, ch->glyph.x, ch->glyph.y,
487 dest->w, dest->h, dest->x, dest->y));
488 bitmap_print(stderr, (BITMAP *)dest->data);
491 /* transform the glyph - we could do this with t1lib, but we do
492 * it ourselves for now */
493 font_transform_glyph(dvi->params.orientation, dest);
496 static int t1_font_get_glyph(DviParams *params, DviFont *font, int code)
498 T1Info *info = (T1Info *)font->private;
505 ASSERT(info != NULL);
506 if(!info->hasmetrics && t1_really_load_font(params, font, info) < 0)
508 ch = FONTCHAR(font, code);
509 if(!ch || !glyph_present(ch))
512 if(!ch->width || !ch->height) {
515 ch->glyph.w = ch->width;
516 ch->glyph.h = ch->height;
517 ch->glyph.data = NULL;
521 /* load the glyph with T1lib (this is done only once for each glyph) */
523 /* get size in TeX points (tfm_conv includes dpi and magnification) */
524 size = (double)font->scale / (params->tfm_conv * 0x100000);
525 /* and transform into PostScript points */
526 size = 72.0 * size / 72.27;
528 dpi = Max(font->hdpi, font->vdpi);
529 /* we don't want the glyph to be cached twice (once by us, another by
530 * T1lib), so we use an identity matrix to tell T1lib not to keep the
532 matrix.cxx = (double)font->hdpi / dpi;
533 matrix.cyy = (double)font->vdpi / dpi;
534 matrix.cxy = matrix.cyx = 0.0;
535 glyph = T1_SetChar(info->t1id, ch->code, (float)size, &matrix);
539 ch->glyph.w = ch->width;
540 ch->glyph.h = ch->height;
541 ch->glyph.data = NULL;
545 /* and make it a bitmap */
546 ch->glyph.data = t1_glyph_bitmap(glyph);
547 ch->glyph.x = -glyph->metrics.leftSideBearing;
548 ch->glyph.y = glyph->metrics.ascent;
549 ch->glyph.w = GLYPH_WIDTH(glyph);
550 ch->glyph.h = GLYPH_HEIGHT(glyph);
552 /* let's also fix the glyph's origin
553 * (which is not contained in the TFM) */
556 /* let's fix these too */
557 ch->width = ch->glyph.w;
558 ch->height = ch->glyph.h;
563 static void t1_font_remove(T1Info *info)
567 /* first remove it from our list */
568 listh_remove(&t1fonts, LIST(info));
570 /* it it's in the hash table, we may need to replace this by another font */
571 old = (T1Info *)mdvi_hash_lookup(&t1hash, info->fontname);
573 mdvi_hash_remove(&t1hash, info->fontname);
574 /* go through the list and see if there is another
575 * font with this name */
576 for(old = (T1Info *)t1fonts.head; old; old = old->next)
577 if(STREQ(old->fontname, info->fontname))
580 mdvi_hash_add(&t1hash, old->fontname, old,
581 MDVI_HASH_UNCHECKED);
583 /* release our encoding vector */
585 DEBUG((DBG_TYPE1, "(t1) %s: releasing vector `%s'\n",
586 info->fontname, info->encoding->name));
587 mdvi_release_encoding(info->encoding, 1);
590 /* now get rid of it */
591 if(info->t1id != -1) {
592 DEBUG((DBG_TYPE1, "(t1) %s: T1_DeleteFont(%d)\n",
593 info->fontname, info->t1id));
594 T1_DeleteFont(info->t1id);
596 DEBUG((DBG_TYPE1, "(t1) %s: not loaded yet, DeleteFont skipped\n",
600 free_font_metrics(info->tfminfo);
601 /*xfree(info->fontname);*/
605 static void t1_free_data(DviFont *font)
607 /* called after all the glyphs are destroyed */
609 if(font->private == NULL) {
610 /* this is perfectly normal, it just means the font has
611 * not been requested by MDVI yet */
615 /* destroy this data */
617 t1_font_remove((T1Info *)font->private);
618 font->private = NULL;
621 * if this is the last T1 font, reset the T1 library
622 * It is important that we do this, because this is will be called
623 * when the resolution or the magnification changes.
625 if(t1fonts.count == 0) {
626 DEBUG((DBG_TYPE1, "(t1) last font removed -- closing T1lib\n"));
628 t1lib_initialized = 0;
634 #endif /* WITH_TYPE1_FONTS */