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
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 
init_freetype(void)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 
tt_encode_font(DviFont * font,FTInfo * info)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 
tt_really_load_font(DviParams * params,DviFont * font,FTInfo * info)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 
tt_load_font(DviParams * params,DviFont * font)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 
tt_get_bitmap(DviParams * params,DviFont * font,int code,double xscale,double yscale,DviGlyph * glyph)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 
tt_font_get_glyph(DviParams * params,DviFont * font,int code)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 
tt_shrink_glyph(DviContext * dvi,DviFont * font,DviFontChar * ch,DviGlyph * dest)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 
tt_reset_font(DviFont * font)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 
tt_font_remove(FTInfo * info)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 
tt_free_data(DviFont * font)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 */
496