Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
t1.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2000, Matias Atria
3  *
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.
8  *
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.
13  *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 /*
20  * Type1 font support for MDVI
21  *
22  * We use T1lib only as a rasterizer, not to draw glyphs.
23  */
24 
25 #include <config.h>
26 #include "mdvi.h"
27 
28 #ifdef WITH_TYPE1_FONTS
29 
30 #include <stdio.h>
31 #include <t1lib.h>
32 #include "private.h"
33 
34 static int t1lib_initialized = 0;
35 
36 typedef struct t1info {
37  struct t1info *next;
38  struct t1info *prev;
39  char *fontname; /* (short) name of this font */
40  int t1id; /* T1lib's id for this font */
41  int hasmetrics; /* have we processed this font? */
42  TFMInfo *tfminfo; /* TFM data is shared */
43  DviFontMapInfo mapinfo;
44  DviEncoding *encoding;
45 } T1Info;
46 
47 static void t1_font_remove __PROTO((T1Info *));
48 static int t1_load_font __PROTO((DviParams *, DviFont *));
49 static int t1_font_get_glyph __PROTO((DviParams *, DviFont *, int));
50 static void t1_font_shrink_glyph
52 static void t1_free_data __PROTO((DviFont *));
53 static void t1_reset_font __PROTO((DviFont *));
54 static char *t1_lookup_font __PROTO((const char *, Ushort *, Ushort *));
55 
56 /* only symbol exported by this file */
57 DviFontInfo t1_font_info = {
58  "Type1",
59  1, /* scaling supported by format */
60  t1_load_font,
61  t1_font_get_glyph,
62  t1_font_shrink_glyph,
64  t1_free_data,
65  t1_reset_font,
66  t1_lookup_font, /* lookup */
67  kpse_type1_format,
68  NULL
69 };
70 
71 /* this seems good enough for most DVI files */
72 #define T1_HASH_SIZE 31
73 
74 /* If these parameters change, we must delete all size information
75  * in all fonts, and reset the device resolutions in T1lib */
76 static int t1lib_xdpi = -1;
77 static int t1lib_ydpi = -1;
78 
79 static ListHead t1fonts = {NULL, NULL, 0};
80 static DviHashTable t1hash;
81 
82 /* Type1 fonts need their own `lookup' function. Here is how it works:
83  * First we try to find the font by its given name. If that fails, we
84  * query the font maps. A typical font map entry may contain the line
85  *
86  * ptmr8rn Times-Roman ".82 ExtendFont TeXBase1Encoding ReEncodeFont" <8r.enc <ptmr
87  *
88  * which means: If you're looking for the font `ptmr8rn' load `Times-Roman'
89  * which is in `ptmr' instead, and extend it by 0.82 points, then reencode
90  * it with the vector TeXBase1Encoding from the file `8r.enc'. This will
91  * fail if the entry looks like this:
92  *
93  * ptmr8rn Times-Roman ".82 ExtendFont TeXBase1Encoding ReEncodeFont" <8r.enc
94  *
95  * because to deal with this we would need to be able to locate the font file
96  * for the `Times-Roman' font ourselves, and that's beyond the scope of mdvi.
97  * But hey, we tried hard.
98  */
99 char *t1_lookup_font(const char *name, Ushort *hdpi, Ushort *vdpi)
100 {
101  char *filename;
102  char *newname;
103  const char *ext;
104  DviFontMapInfo info;
105 
106  DEBUG((DBG_TYPE1, "(t1) looking for `%s'\n", name));
107 
108  /* first let's try the font we were asked for */
109  filename = kpse_find_file(name, kpse_type1_format, 1);
110  if(filename != NULL) {
111  /* we got it */
112  return filename;
113  }
114 
115  DEBUG((DBG_TYPE1, "(t1) %s: not found, querying font maps\n", name));
116  /* now query the fontmap */
117  if(mdvi_query_fontmap(&info, name) < 0) {
118  /* it's not there either */
119  return NULL;
120  }
121 
122  /* check what we got */
123  if(info.fullfile) {
124  DEBUG((DBG_TYPE1, "(t1) %s: found `%s' (cached)\n",
125  name, info.fullfile));
126  /* this is a cached lookup */
127  return mdvi_strdup(info.fullfile);
128  }
129 
130  /* no file associated to this font? */
131  if(info.fontfile == NULL)
132  return info.psname ? mdvi_ps_find_font(info.psname) : NULL;
133 
134  /* let's extract the extension */
135  ext = file_extension(info.fontfile);
136  if(ext && !STREQ(ext, "pfa") && !STREQ(ext, "pfb")) {
137  DEBUG((DBG_TYPE1,
138  "(t1) %s: associated name `%s' is not Type1\n",
139  name, info.fontfile));
140  /* it's not a Type1 font */
141  return NULL;
142  }
143 
144  /* get the `base' name */
145  if(ext) {
146  newname = mdvi_strdup(info.fontfile);
147  newname[ext - info.fontfile - 1] = 0;
148  } else
149  newname = (char *)name; /* we don't modify this */
150 
151  /* look it up */
152  DEBUG((DBG_TYPE1, "(t1) looking for `%s' on behalf of `%s'\n",
153  newname, name));
154  filename = kpse_find_file(newname, kpse_type1_format, 1);
155 
156  /* we don't need this anymore */
157  if(newname != name)
158  mdvi_free(newname);
159  if(filename == NULL) {
160  DEBUG((DBG_TYPE1, "(t1) %s: not found\n", name));
161  return NULL;
162  }
163 
164  DEBUG((DBG_TYPE1, "(t1) %s: found as `%s'\n", name, filename));
165  /* got it! let's remember this */
166  mdvi_add_fontmap_file(name, filename);
167  return filename;
168 }
169 
170 static void t1_reset_resolution(int xdpi, int ydpi)
171 {
172  int i;
173  int nfonts;
174 
175  DEBUG((DBG_TYPE1, "(t1) resetting device resolution (current: (%d,%d))\n",
176  t1lib_xdpi, t1lib_ydpi));
177 #if T1LIB_VERSION < 5
178  nfonts = T1_Get_no_fonts();
179 #else
180  nfonts = T1_GetNoFonts();
181 #endif
182 
183  for(i = 0; i < nfonts; i++)
184  T1_DeleteAllSizes(i);
185  /* reset device resolutions */
186  if(T1_SetDeviceResolutions((float)xdpi, (float)ydpi) < 0)
187  mdvi_warning(_("(t1) failed to reset device resolution\n"));
188  else
189  DEBUG((DBG_TYPE1,
190  "(t1) reset successful, new resolution is (%d, %d)\n",
191  xdpi, ydpi));
192  t1lib_xdpi = xdpi;
193  t1lib_ydpi = ydpi;
194 }
195 
196 static void t1_reset_font(DviFont *font)
197 {
198  T1Info *info = (T1Info *)font->private;
199 
200  if(info == NULL)
201  return;
202  DEBUG((DBG_FONTS, "(t1) resetting font `%s'\n", font->fontname));
203  /* just mark the font as not having metric info. It will be reset
204  * automatically later */
205  info->hasmetrics = 0;
206 }
207 
208 static void t1_transform_font(T1Info *info)
209 {
210  if(!info->hasmetrics && info->encoding != NULL) {
211  DEBUG((DBG_TYPE1, "(t1) %s: encoding with vector `%s'\n",
212  info->fontname, info->encoding->name));
213  T1_DeleteAllSizes(info->t1id);
214  if(T1_ReencodeFont(info->t1id, info->encoding->vector) < 0)
215  mdvi_warning(_("%s: could not encode font\n"), info->fontname);
216  }
217  if(info->mapinfo.slant) {
218  DEBUG((DBG_TYPE1, "(t1) %s: slanting by %.3f\n",
219  info->fontname,
220  MDVI_FMAP_SLANT(&info->mapinfo)));
221  T1_SlantFont(info->t1id,
222  MDVI_FMAP_SLANT(&info->mapinfo));
223  }
224  if(info->mapinfo.extend) {
225  DEBUG((DBG_TYPE1, "(t1) %s: extending by %.3f\n",
226  info->fontname,
227  MDVI_FMAP_EXTEND(&info->mapinfo)));
228  T1_ExtendFont(info->t1id,
229  MDVI_FMAP_EXTEND(&info->mapinfo));
230  }
231 }
232 
233 /* if this function is called, we really need this font */
234 static int t1_really_load_font(DviParams *params, DviFont *font, T1Info *info)
235 {
236  int i;
237  T1Info *old;
238  int t1id;
239  int copied;
240  int status;
241 
242  DEBUG((DBG_TYPE1, "(t1) really_load_font(%s)\n", info->fontname));
243 
244  /* if the parameters changed, reset T1lib */
245  if(t1lib_xdpi != params->dpi || t1lib_ydpi != params->vdpi)
246  t1_reset_resolution(params->dpi, params->vdpi);
247 
248  /* if we already have a T1lib id, do nothing */
249  if(info->t1id != -1) {
250  info->hasmetrics = 1;
251  /* apply slant and extend again */
252  t1_transform_font(info);
253  return 0;
254  }
255 
256  /* before we even attempt to load the font, make sure we have metric
257  * data for it */
258  info->tfminfo = mdvi_ps_get_metrics(info->fontname);
259  if(info->tfminfo == NULL) {
260  DEBUG((DBG_FONTS,
261  "(t1) %s: no metric data, font ignored\n",
262  info->fontname));
263  goto t1_error;
264  }
265  /* fix this */
266  font->design = info->tfminfo->design;
267 
268  /* check if we have a font with this name (maybe at a different size) */
269  old = (T1Info *)mdvi_hash_lookup(&t1hash, (unsigned char *)info->fontname);
270  if(old == info) {
271  /* let's avoid confusion */
272  old = NULL;
273  }
274  if(old && old->t1id != -1) {
275  /* let's take advantage of T1lib's font sharing */
276  t1id = T1_CopyFont(old->t1id);
277  DEBUG((DBG_TYPE1, "(t1) %s -> %d (CopyFont)\n",
278  info->fontname, t1id));
279  copied = 1;
280  } else {
281  t1id = T1_AddFont(font->filename);
282  DEBUG((DBG_TYPE1, "(t1) %s -> %d (AddFont)\n",
283  info->fontname, t1id));
284  copied = 0;
285  }
286  if(t1id < 0)
287  goto t1_error;
288  info->t1id = t1id;
289 
290  /*
291  * a minor optimization: If the old font in the hash table has
292  * not been loaded yet, replace it by this one, so we can use
293  * CopyFont later.
294  */
295  if(old && old->t1id == -1) {
296  DEBUG((DBG_TYPE1, "(t1) font `%s' exchanged in hash table\n",
297  info->fontname));
298  mdvi_hash_remove(&t1hash, (unsigned char *)old->fontname);
299  mdvi_hash_add(&t1hash, (unsigned char *)info->fontname,
300  info, MDVI_HASH_UNCHECKED);
301  }
302 
303  /* now let T1lib load it */
304  if(!copied && T1_LoadFont(info->t1id) < 0) {
305  DEBUG((DBG_TYPE1, "(t1) T1_LoadFont(%d) failed with error %d\n",
306  info->t1id, T1_errno));
307  goto t1_error;
308  }
309  DEBUG((DBG_TYPE1, "(t1) T1_LoadFont(%d) -> Ok\n", info->t1id));
310 
311  /* get information from the fontmap */
312  status = mdvi_query_fontmap(&info->mapinfo, info->fontname);
313  if(!status && info->mapinfo.encoding)
314  info->encoding = mdvi_request_encoding(info->mapinfo.encoding);
315  t1_transform_font(info);
316 
317  i = info->tfminfo->hic - info->tfminfo->loc + 1;
318  if(i != font->hic - font->loc + 1) {
319  /* reset to optimal size */
320  font->chars = mdvi_realloc(font->chars, i * sizeof(DviFontChar));
321  }
322 
323  /* get the scaled characters metrics */
324  get_tfm_chars(params, font, info->tfminfo, 0);
325  info->hasmetrics = 1;
326 
327  DEBUG((DBG_TYPE1, "(t1) font `%s' really-loaded\n", info->fontname));
328  return 0;
329 
330 t1_error:
331  /* some error does not allows us to use this font. We need to reset
332  * the font structure, so the font system can try to read this
333  * font in a different class */
334 
335  /* first destroy the private data */
336  t1_font_remove(info);
337  /* now reset all chars -- this is the important part */
338  mdvi_free(font->chars);
339  font->chars = NULL;
340  font->loc = font->hic = 0;
341  return -1;
342 }
343 
344 static int init_t1lib(DviParams *params)
345 {
346  int t1flags;
347 
348 #ifdef WORD_LITTLE_ENDIAN
349  /* try making T1lib use bitmaps in our format, but if this
350  * fails we'll convert the bitmap ourselves */
351  T1_SetBitmapPad(BITMAP_BITS);
352 #endif
353  T1_SetDeviceResolutions((float)params->dpi, (float)params->vdpi);
354  t1flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE|T1_NO_AFM;
355  if(DEBUGGING(TYPE1))
356  t1flags |= LOGFILE;
357  if(T1_InitLib(t1flags) == NULL)
358  return (t1lib_initialized = -1);
359  if(DEBUGGING(TYPE1)) {
360  DEBUG((DBG_TYPE1, "T1lib debugging output saved in t1lib.log\n"));
361  T1_SetLogLevel(T1LOG_DEBUG);
362  }
363  /* initialize our hash table, but don't allocate memory for it
364  * until we use it */
365  mdvi_hash_init(&t1hash);
366  DEBUG((DBG_TYPE1, "(t1) t1lib %s initialized -- resolution is (%d, %d), pad is %d bits\n",
367  T1_GetLibIdent(), params->dpi, params->vdpi, T1_GetBitmapPad()));
368  t1lib_initialized = 1;
369  t1lib_xdpi = params->dpi;
370  t1lib_ydpi = params->vdpi;
371  return 0;
372 }
373 
374 static int t1_load_font(DviParams *params, DviFont *font)
375 {
376  T1Info *info;
377  int i;
378 
379  if(t1lib_initialized < 0)
380  return -1;
381  else if(t1lib_initialized == 0 && init_t1lib(params) < 0)
382  return -1;
383 
384  if(font->in != NULL) {
385  /* we don't need this */
386  fclose(font->in);
387  font->in = NULL;
388  }
389 
390  info = xalloc(T1Info);
391 
392  /*
393  * mark the font as `unregistered' with T1lib. It will
394  * be added when we actually use it
395  */
396  info->t1id = -1;
397 
398  /* add the font to our list */
399  info->fontname = font->fontname;
400  info->hasmetrics = 0;
401  info->encoding = NULL;
402  info->mapinfo.psname = NULL;
403  info->mapinfo.encoding = NULL;
404  info->mapinfo.fontfile = NULL;
405  info->mapinfo.extend = 0;
406  info->mapinfo.slant = 0;
407  info->encoding = NULL;
408 
409  /* create the hash table if we have not done so yet */
410  if(t1hash.nbucks == 0)
411  mdvi_hash_create(&t1hash, T1_HASH_SIZE);
412  mdvi_hash_add(&t1hash, (unsigned char *) info->fontname, info, MDVI_HASH_UNIQUE);
413  listh_append(&t1fonts, LIST(info));
414 
415  font->private = info;
416 
417  /* reset everything */
418  font->chars = xnalloc(DviFontChar, 256);
419  font->loc = 0;
420  font->hic = 255;
421  for(i = 0; i < 256; i++) {
422  font->chars[i].code = i;
423  font->chars[i].offset = 1;
424  font->chars[i].loaded = 0;
425  font->chars[i].glyph.data = NULL;
426  font->chars[i].shrunk.data = NULL;
427  font->chars[i].grey.data = NULL;
428  }
429 
430  return 0;
431 }
432 
433 #define GLYPH_WIDTH(g) \
434  ((g)->metrics.rightSideBearing - (g)->metrics.leftSideBearing)
435 #define GLYPH_HEIGHT(g) \
436  ((g)->metrics.ascent - (g)->metrics.descent)
437 
438 static inline BITMAP *t1_glyph_bitmap(GLYPH *glyph)
439 {
440  int w, h, pad;
441 
442  w = GLYPH_WIDTH(glyph);
443  h = GLYPH_HEIGHT(glyph);
444 
445  if(!w || !h)
446  return MDVI_GLYPH_EMPTY;
447 
448  pad = T1_GetBitmapPad();
449  return bitmap_convert_lsb8((unsigned char *)glyph->bits, w, h, ROUND(w, pad) * (pad >> 3));
450 }
451 
452 static void t1_font_shrink_glyph(DviContext *dvi, DviFont *font, DviFontChar *ch, DviGlyph *dest)
453 {
454  double size;
455  GLYPH *glyph;
456  T1Info *info;
457  T1_TMATRIX matrix;
458 
459  info = (T1Info *)font->private;
460  ASSERT(info != NULL);
461 
462  DEBUG((DBG_TYPE1, "(t1) shrinking glyph for character %d in `%s' (%d,%d)\n",
463  ch->code, font->fontname, ch->width, ch->height));
464  size = (double)font->scale / (dvi->params.tfm_conv * 0x100000);
465  size = 72.0 * size / 72.27;
466  matrix.cxx = 1.0/(double)dvi->params.hshrink;
467  matrix.cyy = 1.0/(double)dvi->params.vshrink;
468  matrix.cxy = 0.0;
469  matrix.cyx = 0.0;
470  glyph = T1_SetChar(info->t1id, ch->code, (float)size, &matrix);
471 
472  dest->data = t1_glyph_bitmap(glyph);
473  dest->x = -glyph->metrics.leftSideBearing;
474  dest->y = glyph->metrics.ascent;
475  dest->w = GLYPH_WIDTH(glyph);
476  dest->h = GLYPH_HEIGHT(glyph);
477 
478 #ifndef NODEBUG
479  if(DEBUGGING(BITMAP_DATA)) {
481  "(t1) %s: t1_shrink_glyph(%d): (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n",
482  ch->glyph.w, ch->glyph.h, ch->glyph.x, ch->glyph.y,
483  dest->w, dest->h, dest->x, dest->y));
484  bitmap_print(stderr, (BITMAP *)dest->data);
485  }
486 #endif
487  /* transform the glyph - we could do this with t1lib, but we do
488  * it ourselves for now */
490 }
491 
492 static int t1_font_get_glyph(DviParams *params, DviFont *font, int code)
493 {
494  T1Info *info = (T1Info *)font->private;
495  GLYPH *glyph;
496  DviFontChar *ch;
497  double size;
498  T1_TMATRIX matrix;
499  int dpi;
500 
501  ASSERT(info != NULL);
502  if(!info->hasmetrics && t1_really_load_font(params, font, info) < 0)
503  return -1;
504  ch = FONTCHAR(font, code);
505  if(!ch || !glyph_present(ch))
506  return -1;
507  ch->loaded = 1;
508  if(!ch->width || !ch->height) {
509  ch->glyph.x = ch->x;
510  ch->glyph.y = ch->y;
511  ch->glyph.w = ch->width;
512  ch->glyph.h = ch->height;
513  ch->glyph.data = NULL;
514  return 0;
515  }
516 
517  /* load the glyph with T1lib (this is done only once for each glyph) */
518 
519  /* get size in TeX points (tfm_conv includes dpi and magnification) */
520  size = (double)font->scale / (params->tfm_conv * 0x100000);
521  /* and transform into PostScript points */
522  size = 72.0 * size / 72.27;
523 
524  dpi = Max(font->hdpi, font->vdpi);
525  /* we don't want the glyph to be cached twice (once by us, another by
526  * T1lib), so we use an identity matrix to tell T1lib not to keep the
527  * glyph around */
528  matrix.cxx = (double)font->hdpi / dpi;
529  matrix.cyy = (double)font->vdpi / dpi;
530  matrix.cxy = matrix.cyx = 0.0;
531  glyph = T1_SetChar(info->t1id, ch->code, (float)size, &matrix);
532  if(glyph == NULL) {
533  ch->glyph.x = ch->x;
534  ch->glyph.y = ch->y;
535  ch->glyph.w = ch->width;
536  ch->glyph.h = ch->height;
537  ch->glyph.data = NULL;
538  ch->missing = 1;
539  return 0;
540  }
541  /* and make it a bitmap */
542  ch->glyph.data = t1_glyph_bitmap(glyph);
543  ch->glyph.x = -glyph->metrics.leftSideBearing;
544  ch->glyph.y = glyph->metrics.ascent;
545  ch->glyph.w = GLYPH_WIDTH(glyph);
546  ch->glyph.h = GLYPH_HEIGHT(glyph);
547 
548  /* let's also fix the glyph's origin
549  * (which is not contained in the TFM) */
550  ch->x = ch->glyph.x;
551  ch->y = ch->glyph.y;
552  /* let's fix these too */
553  ch->width = ch->glyph.w;
554  ch->height = ch->glyph.h;
555 
556  return 0;
557 }
558 
559 static void t1_font_remove(T1Info *info)
560 {
561  T1Info *old;
562 
563  /* first remove it from our list */
564  listh_remove(&t1fonts, LIST(info));
565 
566  /* it it's in the hash table, we may need to replace this by another font */
567  old = (T1Info *)mdvi_hash_lookup(&t1hash, (unsigned char *)info->fontname);
568  if(old == info) {
569  mdvi_hash_remove(&t1hash, (unsigned char *) info->fontname);
570  /* go through the list and see if there is another
571  * font with this name */
572  for(old = (T1Info *)t1fonts.head; old; old = old->next)
573  if(STREQ(old->fontname, info->fontname))
574  break;
575  if(old != NULL)
576  mdvi_hash_add(&t1hash, (unsigned char *) old->fontname, old,
578  }
579  /* release our encoding vector */
580  if(info->encoding) {
581  DEBUG((DBG_TYPE1, "(t1) %s: releasing vector `%s'\n",
582  info->fontname, info->encoding->name));
583  mdvi_release_encoding(info->encoding, 1);
584  }
585 
586  /* now get rid of it */
587  if(info->t1id != -1) {
588  DEBUG((DBG_TYPE1, "(t1) %s: T1_DeleteFont(%d)\n",
589  info->fontname, info->t1id));
590  T1_DeleteFont(info->t1id);
591  } else
592  DEBUG((DBG_TYPE1, "(t1) %s: not loaded yet, DeleteFont skipped\n",
593  info->fontname));
594 
595  if(info->tfminfo)
596  free_font_metrics(info->tfminfo);
597  /*mdvi_free(info->fontname);*/
598  mdvi_free(info);
599 }
600 
601 static void t1_free_data(DviFont *font)
602 {
603  /* called after all the glyphs are destroyed */
604 
605  if(font->private == NULL) {
606  /* this is perfectly normal, it just means the font has
607  * not been requested by MDVI yet */
608  return;
609  }
610 
611  /* destroy this data */
612 
613  t1_font_remove((T1Info *)font->private);
614  font->private = NULL;
615 
616  /*
617  * if this is the last T1 font, reset the T1 library
618  * It is important that we do this, because this is will be called
619  * when the resolution or the magnification changes.
620  */
621  if(t1fonts.count == 0) {
622  DEBUG((DBG_TYPE1, "(t1) last font removed -- closing T1lib\n"));
623  T1_CloseLib();
624  t1lib_initialized = 0;
625  t1lib_xdpi = -1;
626  t1lib_ydpi = -1;
627  }
628 }
629 
630 #endif /* WITH_TYPE1_FONTS */