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