]> www.fi.muni.cz Git - evince.git/blob - backend/dvi/mdvi-lib/tt.c
Include config.h. Bug #504721.
[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 <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 
55         __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *));
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,
64         mdvi_shrink_glyph_grey,
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                 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                 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                 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                 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                         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                 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                 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                 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);
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:
431         ch->glyph.data = MDVI_GLYPH_EMPTY;
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 */
449         font_transform_glyph(dvi->params.orientation, dest);
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 */