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
tt.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 #include <config.h>
20 #include "mdvi.h"
21 
22 #ifdef WITH_TRUETYPE_FONTS
23 
24 #include <string.h>
25 #include <freetype.h>
26 #include <ftxpost.h>
27 #include <ftxerr18.h>
28 
29 #include "private.h"
30 
31 static TT_Engine tt_handle;
32 static int initialized = 0;
33 
34 typedef struct ftinfo {
35  struct ftinfo *next;
36  struct ftinfo *prev;
37  char *fontname;
38  char *fmfname;
39  TT_Face face;
40  TT_Instance instance;
41  TT_Glyph glyph;
42  int hasmetrics;
43  int loaded;
44  int fmftype;
45  TFMInfo *tfminfo;
46  DviFontMapInfo mapinfo;
47  DviEncoding *encoding;
48 } FTInfo;
49 
50 static int tt_load_font __PROTO((DviParams *, DviFont *));
51 static int tt_font_get_glyph __PROTO((DviParams *, DviFont *, int));
52 static void tt_free_data __PROTO((DviFont *));
53 static void tt_reset_font __PROTO((DviFont *));
54 static void tt_shrink_glyph
56 static void tt_font_remove __PROTO((FTInfo *));
57 
58 DviFontInfo tt_font_info = {
59  "TT",
60  0,
61  tt_load_font,
62  tt_font_get_glyph,
63  tt_shrink_glyph,
65  tt_free_data, /* free */
66  tt_reset_font, /* reset */
67  NULL, /* lookup */
68  kpse_truetype_format,
69  NULL
70 };
71 
72 #define FT_HASH_SIZE 31
73 
74 static ListHead ttfonts = {NULL, NULL, 0};
75 
76 static int init_freetype(void)
77 {
78  TT_Error code;
79 
80  ASSERT(initialized == 0);
81  code = TT_Init_FreeType(&tt_handle);
82  if(code) {
83  DEBUG((DBG_TT, "(tt) Init_Freetype: error %d\n", code));
84  return -1;
85  }
86  code = TT_Init_Post_Extension(tt_handle);
87  if(code) {
88  TT_Done_FreeType(tt_handle);
89  return -1;
90  }
91  /* we're on */
92  initialized = 1;
93  return 0;
94 }
95 
96 static void tt_encode_font(DviFont *font, FTInfo *info)
97 {
98  TT_Face_Properties prop;
99  int i;
100 
101  if(TT_Get_Face_Properties(info->face, &prop))
102  return;
103 
104  for(i = 0; i < prop.num_Glyphs; i++) {
105  char *string;
106  int ndx;
107 
108  if(TT_Get_PS_Name(info->face, i, &string))
109  continue;
110  ndx = mdvi_encode_glyph(info->encoding, string);
111  if(ndx < font->loc || ndx > font->hic)
112  continue;
113  font->chars[ndx - font->loc].code = i;
114  }
115 }
116 
117 static int tt_really_load_font(DviParams *params, DviFont *font, FTInfo *info)
118 {
119  DviFontChar *ch;
120  TFMChar *ptr;
121  Int32 z, alpha, beta;
122  int i;
123  FTInfo *old;
124  TT_Error status;
125  double point_size;
126  static int warned = 0;
127  TT_CharMap cmap;
128  TT_Face_Properties props;
129  int map_found;
130 
131  DEBUG((DBG_TT, "(tt) really_load_font(%s)\n", info->fontname));
132 
133  /* get the point size */
134  point_size = (double)font->scale / (params->tfm_conv * 0x100000);
135  point_size = 72.0 * point_size / 72.27;
136  if(info->loaded) {
137  /* just reset the size info */
138  TT_Set_Instance_Resolutions(info->instance,
139  params->dpi, params->vdpi);
140  TT_Set_Instance_CharSize(info->instance, FROUND(point_size * 64));
141  /* FIXME: should extend/slant again */
142  info->hasmetrics = 1;
143  return 0;
144  }
145 
146  /* load the face */
147  DEBUG((DBG_TT, "(tt) loading new face `%s'\n",
148  info->fontname));
149  status = TT_Open_Face(tt_handle, font->filename, &info->face);
150  if(status) {
151  mdvi_warning(_("(tt) %s: could not load face: %s\n"),
152  info->fontname, TT_ErrToString18(status));
153  return -1;
154  }
155 
156  /* create a new instance of this face */
157  status = TT_New_Instance(info->face, &info->instance);
158  if(status) {
159  mdvi_warning(_("(tt) %s: could not create face: %s\n"),
160  info->fontname, TT_ErrToString18(status));
161  TT_Close_Face(info->face);
162  return -1;
163  }
164 
165  /* create a glyph */
166  status = TT_New_Glyph(info->face, &info->glyph);
167  if(status) {
168  mdvi_warning(_("(tt) %s: could not create glyph: %s\n"),
169  info->fontname, TT_ErrToString18(status));
170  goto tt_error;
171  }
172 
173  /*
174  * We'll try to find a Unicode charmap. It's not that important that we
175  * actually find one, especially if the fontmap files are installed
176  * properly, but it's good to have some predefined behaviour
177  */
178  TT_Get_Face_Properties(info->face, &props);
179 
180  map_found = -1;
181  for(i = 0; map_found < 0 && i < props.num_CharMaps; i++) {
182  TT_UShort pid, eid;
183 
184  TT_Get_CharMap_ID(info->face, i, &pid, &eid);
185  switch(pid) {
186  case TT_PLATFORM_APPLE_UNICODE:
187  map_found = i;
188  break;
189  case TT_PLATFORM_ISO:
190  if(eid == TT_ISO_ID_7BIT_ASCII ||
191  eid == TT_ISO_ID_8859_1)
192  map_found = 1;
193  break;
194  case TT_PLATFORM_MICROSOFT:
195  if(eid == TT_MS_ID_UNICODE_CS)
196  map_found = 1;
197  break;
198  }
199  }
200  if(map_found < 0) {
201  mdvi_warning(_("(tt) %s: no acceptable map found, using #0\n"),
202  info->fontname);
203  map_found = 0;
204  }
205  DEBUG((DBG_TT, "(tt) %s: using charmap #%d\n",
206  info->fontname, map_found));
207  TT_Get_CharMap(info->face, map_found, &cmap);
208 
209  DEBUG((DBG_TT, "(tt) %s: Set_Char_Size(%.2f, %d, %d)\n",
210  font->fontname, point_size, font->hdpi, font->vdpi));
211  status = TT_Set_Instance_Resolutions(info->instance,
212  params->dpi, params->vdpi);
213  if(status) {
214  error(_("(tt) %s: could not set resolution: %s\n"),
215  info->fontname, TT_ErrToString18(status));
216  goto tt_error;
217  }
218  status = TT_Set_Instance_CharSize(info->instance,
219  FROUND(point_size * 64));
220  if(status) {
221  error(_("(tt) %s: could not set point size: %s\n"),
222  info->fontname, TT_ErrToString18(status));
223  goto tt_error;
224  }
225 
226  /* after this point we don't fail */
227 
228  /* get information from the fontmap */
229  status = mdvi_query_fontmap(&info->mapinfo, info->fontname);
230  if(!status && info->mapinfo.encoding)
231  info->encoding = mdvi_request_encoding(info->mapinfo.encoding);
232  else
233  info->encoding = NULL;
234 
235  if(info->encoding != NULL) {
236  TT_Post post;
237 
238  status = TT_Load_PS_Names(info->face, &post);
239  if(status) {
240  mdvi_warning(_("(tt) %s: could not load PS name table\n"),
241  info->fontname);
242  mdvi_release_encoding(info->encoding, 0);
243  info->encoding = NULL;
244  }
245  }
246 
247  /* get the metrics. If this fails, it's not fatal, but certainly bad */
248  info->tfminfo = get_font_metrics(info->fontname,
249  info->fmftype, info->fmfname);
250 
251  if(info->tfminfo == NULL) {
252  mdvi_warning("(tt) %s: no metrics data, font ignored\n",
253  info->fontname);
254  goto tt_error;
255  }
256  /* fix this */
257  font->design = info->tfminfo->design;
258 
259  /* get the scaled character metrics */
260  get_tfm_chars(params, font, info->tfminfo, 0);
261 
262  if(info->encoding)
263  tt_encode_font(font, info);
264  else {
265  mdvi_warning(_("%s: no encoding vector found, expect bad output\n"),
266  info->fontname);
267  /* this is better than nothing */
268  for(i = font->loc; i <= font->hic; i++)
269  font->chars[i - font->loc].code = TT_Char_Index(cmap, i);
270  }
271 
272  info->loaded = 1;
273  info->hasmetrics = 1;
274  return 0;
275 
276 tt_error:
277  tt_font_remove(info);
278  mdvi_free(font->chars);
279  font->chars = NULL;
280  font->loc = font->hic = 0;
281  return -1;
282 }
283 
284 static int tt_load_font(DviParams *params, DviFont *font)
285 {
286  int i;
287  FTInfo *info;
288 
289  if(!initialized && init_freetype() < 0)
290  return -1;
291 
292  if(font->in != NULL) {
293  fclose(font->in);
294  font->in = NULL;
295  }
296 
297  info = xalloc(FTInfo);
298 
299  memzero(info, sizeof(FTInfo));
300  info->fmftype = DviFontAny; /* any metrics type will do */
301  info->fmfname = lookup_font_metrics(font->fontname, &info->fmftype);
302  info->fontname = font->fontname;
303  info->hasmetrics = 0;
304  info->loaded = 0;
305 
306  /* these will be obtained from the fontmaps */
307  info->mapinfo.psname = NULL;
308  info->mapinfo.encoding = NULL;
309  info->mapinfo.fontfile = NULL;
310  info->mapinfo.extend = 0;
311  info->mapinfo.slant = 0;
312 
313  /* initialize these */
314  font->chars = xnalloc(DviFontChar, 256);
315  font->loc = 0;
316  font->hic = 255;
317  for(i = 0; i < 256; i++) {
318  font->chars[i].offset = 1;
319  font->chars[i].glyph.data = NULL;
320  font->chars[i].shrunk.data = NULL;
321  font->chars[i].grey.data = NULL;
322  }
323 
324  if(info->fmfname == NULL)
325  mdvi_warning(_("(tt) %s: no font metric data\n"), font->fontname);
326 
327  listh_append(&ttfonts, LIST(info));
328  font->private = info;
329 
330  return 0;
331 }
332 
333 static int tt_get_bitmap(DviParams *params, DviFont *font,
334  int code, double xscale, double yscale, DviGlyph *glyph)
335 {
336  TT_Outline outline;
337  TT_Raster_Map raster;
338  TT_BBox bbox;
339  TT_Glyph_Metrics metrics;
340  TT_Matrix mat;
341  FTInfo *info;
342  int error;
343  int have_outline = 0;
344  int w, h;
345 
346  info = (FTInfo *)font->private;
347  if(info == NULL)
348  return -1;
349 
350  error = TT_Load_Glyph(info->instance, info->glyph,
351  code, TTLOAD_DEFAULT);
352  if(error) goto tt_error;
353  error = TT_Get_Glyph_Outline(info->glyph, &outline);
354  if(error) goto tt_error;
355  have_outline = 1;
356  mat.xx = FROUND(xscale * 65536);
357  mat.yy = FROUND(yscale * 65536);
358  mat.yx = 0;
359  mat.xy = 0;
360  TT_Transform_Outline(&outline, &mat);
361  error = TT_Get_Outline_BBox(&outline, &bbox);
362  if(error) goto tt_error;
363  bbox.xMin &= -64;
364  bbox.yMin &= -64;
365  bbox.xMax = (bbox.xMax + 63) & -64;
366  bbox.yMax = (bbox.yMax + 63) & -64;
367  w = (bbox.xMax - bbox.xMin) / 64;
368  h = (bbox.yMax - bbox.yMin) / 64;
369 
370  glyph->w = w;
371  glyph->h = h;
372  glyph->x = -bbox.xMin / 64;
373  glyph->y = bbox.yMax / 64;
374  if(!w || !h)
375  goto tt_error;
376  raster.rows = h;
377  raster.width = w;
378  raster.cols = ROUND(w, 8);
379  raster.size = h * raster.cols;
380  raster.flow = TT_Flow_Down;
381  raster.bitmap = mdvi_calloc(h, raster.cols);
382 
383  TT_Translate_Outline(&outline, -bbox.xMin, -bbox.yMin);
384  TT_Get_Outline_Bitmap(tt_handle, &outline, &raster);
385  glyph->data = bitmap_convert_msb8(raster.bitmap, w, h, ROUND(w, 8));
386  TT_Done_Outline(&outline);
387  mdvi_free(raster.bitmap);
388 
389  return 0;
390 tt_error:
391  if(have_outline)
392  TT_Done_Outline(&outline);
393  return -1;
394 }
395 
396 static int tt_font_get_glyph(DviParams *params, DviFont *font, int code)
397 {
398  FTInfo *info = (FTInfo *)font->private;
399  DviFontChar *ch;
400  int error;
401  double xs, ys;
402  int dpi;
403 
404  ASSERT(info != NULL);
405  if(!info->hasmetrics && tt_really_load_font(params, font, info) < 0)
406  return -1;
407  ch = FONTCHAR(font, code);
408  if(!ch || !glyph_present(ch))
409  return -1;
410  ch->loaded = 1;
411  if(!ch->width || !ch->height)
412  goto blank;
413  if(ch->code == 0) {
414  ch->glyph.data = NULL;
415  goto missing;
416  }
417  /* get the glyph */
418  dpi = Max(font->hdpi, font->vdpi);
419  error = tt_get_bitmap(params, font, ch->code,
420  (double)font->hdpi / dpi,
421  (double)font->vdpi / dpi,
422  &ch->glyph);
423  if(error)
424  goto missing;
425  ch->x = ch->glyph.x;
426  ch->y = ch->glyph.y;
427 
428  return 0;
429 
430 missing:
432  ch->missing = 1;
433 blank:
434  ch->glyph.w = ch->width;
435  ch->glyph.h = ch->height;
436  ch->glyph.x = ch->x;
437  ch->glyph.y = ch->y;
438  return 0;
439 }
440 
441 static void tt_shrink_glyph(DviContext *dvi, DviFont *font, DviFontChar *ch, DviGlyph *dest)
442 {
443  tt_get_bitmap(&dvi->params, font,
444  ch->code,
445  (double)font->hdpi / (dvi->params.dpi * dvi->params.hshrink),
446  (double)font->vdpi / (dvi->params.vdpi * dvi->params.vshrink),
447  dest);
448  /* transform the glyph for the current orientation */
450 }
451 
452 static void tt_reset_font(DviFont *font)
453 {
454  FTInfo *info = (FTInfo *)font->private;
455 
456  if(info == NULL)
457  return;
458  info->hasmetrics = 0;
459 }
460 
461 static void tt_font_remove(FTInfo *info)
462 {
463  FTInfo *old;
464 
465  if(info->loaded) {
466  /* all fonts in the hash table have called TT_Open_Face */
467  TT_Done_Instance(info->instance);
468  TT_Close_Face(info->face);
469  }
470  listh_remove(&ttfonts, LIST(info));
471  /* release our encodings */
472  if(info->encoding)
473  mdvi_release_encoding(info->encoding, 1);
474  /* and destroy the font */
475  if(info->tfminfo)
476  free_font_metrics(info->tfminfo);
477  if(info->fmfname)
478  mdvi_free(info->fmfname);
479  mdvi_free(info);
480 }
481 
482 static void tt_free_data(DviFont *font)
483 {
484  if(font->private == NULL)
485  return;
486 
487  tt_font_remove((FTInfo *)font->private);
488  if(initialized && ttfonts.count == 0) {
489  DEBUG((DBG_TT, "(tt) last font removed -- closing FreeType\n"));
490  TT_Done_FreeType(tt_handle);
491  initialized = 0;
492  }
493 }
494 
495 #endif /* WITH_TRUETYPE_FONTS */