Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4680 right-hear 1
#include "fitz.h"
2
 
3
#include 
4
#include FT_FREETYPE_H
5
#include FT_STROKER_H
6
 
7
static void fz_finalize_freetype(void);
8
 
9
static fz_font *
10
fz_new_font(char *name)
11
{
12
	fz_font *font;
13
 
14
	font = fz_malloc(sizeof(fz_font));
15
	font->refs = 1;
16
 
17
	if (name)
18
		fz_strlcpy(font->name, name, sizeof font->name);
19
	else
20
		fz_strlcpy(font->name, "(null)", sizeof font->name);
21
 
22
	font->ft_face = NULL;
23
	font->ft_substitute = 0;
24
	font->ft_bold = 0;
25
	font->ft_italic = 0;
26
	font->ft_hint = 0;
27
 
28
	font->ft_file = NULL;
29
	font->ft_data = NULL;
30
	font->ft_size = 0;
31
 
32
	font->t3matrix = fz_identity;
33
	font->t3resources = NULL;
34
	font->t3procs = NULL;
35
	font->t3widths = NULL;
36
	font->t3xref = NULL;
37
	font->t3run = NULL;
38
 
39
	font->bbox.x0 = 0;
40
	font->bbox.y0 = 0;
41
	font->bbox.x1 = 1000;
42
	font->bbox.y1 = 1000;
43
 
44
	font->width_count = 0;
45
	font->width_table = NULL;
46
 
47
	return font;
48
}
49
 
50
fz_font *
51
fz_keep_font(fz_font *font)
52
{
53
	font->refs ++;
54
	return font;
55
}
56
 
57
void
58
fz_drop_font(fz_font *font)
59
{
60
	int fterr;
61
	int i;
62
 
63
	if (font && --font->refs == 0)
64
	{
65
		if (font->t3procs)
66
		{
67
			if (font->t3resources)
68
				fz_drop_obj(font->t3resources);
69
			for (i = 0; i < 256; i++)
70
				if (font->t3procs[i])
71
					fz_drop_buffer(font->t3procs[i]);
72
			fz_free(font->t3procs);
73
			fz_free(font->t3widths);
74
		}
75
 
76
		if (font->ft_face)
77
		{
78
			fterr = FT_Done_Face((FT_Face)font->ft_face);
79
			if (fterr)
80
				fz_warn("freetype finalizing face: %s", ft_error_string(fterr));
81
			fz_finalize_freetype();
82
		}
83
 
84
		if (font->ft_file)
85
			fz_free(font->ft_file);
86
		if (font->ft_data)
87
			fz_free(font->ft_data);
88
 
89
		if (font->width_table)
90
			fz_free(font->width_table);
91
 
92
		fz_free(font);
93
	}
94
}
95
 
96
void
97
fz_set_font_bbox(fz_font *font, float xmin, float ymin, float xmax, float ymax)
98
{
99
	font->bbox.x0 = xmin;
100
	font->bbox.y0 = ymin;
101
	font->bbox.x1 = xmax;
102
	font->bbox.y1 = ymax;
103
}
104
 
105
/*
106
 * Freetype hooks
107
 */
108
 
109
static FT_Library fz_ftlib = NULL;
110
static int fz_ftlib_refs = 0;
111
 
112
#undef __FTERRORS_H__
113
#define FT_ERRORDEF(e, v, s)	{ (e), (s) },
114
#define FT_ERROR_START_LIST
115
#define FT_ERROR_END_LIST	{ 0, NULL }
116
 
117
struct ft_error
118
{
119
	int err;
120
	char *str;
121
};
122
 
123
static const struct ft_error ft_errors[] =
124
{
125
#include FT_ERRORS_H
126
};
127
 
128
char *ft_error_string(int err)
129
{
130
	const struct ft_error *e;
131
 
132
	for (e = ft_errors; e->str != NULL; e++)
133
		if (e->err == err)
134
			return e->str;
135
 
136
	return "Unknown error";
137
}
138
 
139
static fz_error
140
fz_init_freetype(void)
141
{
142
	int fterr;
143
	int maj, min, pat;
144
 
145
	if (fz_ftlib)
146
	{
147
		fz_ftlib_refs++;
148
		return fz_okay;
149
	}
150
 
151
	fterr = FT_Init_FreeType(&fz_ftlib);
152
	if (fterr)
153
		return fz_throw("cannot init freetype: %s", ft_error_string(fterr));
154
 
155
	FT_Library_Version(fz_ftlib, &maj, &min, &pat);
156
	if (maj == 2 && min == 1 && pat < 7)
157
	{
158
		fterr = FT_Done_FreeType(fz_ftlib);
159
		if (fterr)
160
			fz_warn("freetype finalizing: %s", ft_error_string(fterr));
161
		return fz_throw("freetype version too old: %d.%d.%d", maj, min, pat);
162
	}
163
 
164
	fz_ftlib_refs++;
165
	return fz_okay;
166
}
167
 
168
static void
169
fz_finalize_freetype(void)
170
{
171
	int fterr;
172
 
173
	if (--fz_ftlib_refs == 0)
174
	{
175
		fterr = FT_Done_FreeType(fz_ftlib);
176
		if (fterr)
177
			fz_warn("freetype finalizing: %s", ft_error_string(fterr));
178
		fz_ftlib = NULL;
179
	}
180
}
181
 
182
fz_error
183
fz_new_font_from_file(fz_font **fontp, char *path, int index)
184
{
185
	FT_Face face;
186
	fz_error error;
187
	fz_font *font;
188
	int fterr;
189
 
190
	error = fz_init_freetype();
191
	if (error)
192
		return fz_rethrow(error, "cannot init freetype library");
193
 
194
	fterr = FT_New_Face(fz_ftlib, path, index, &face);
195
	if (fterr)
196
		return fz_throw("freetype: cannot load font: %s", ft_error_string(fterr));
197
 
198
	font = fz_new_font(face->family_name);
199
	font->ft_face = face;
200
	font->bbox.x0 = face->bbox.xMin * 1000 / face->units_per_EM;
201
	font->bbox.y0 = face->bbox.yMin * 1000 / face->units_per_EM;
202
	font->bbox.x1 = face->bbox.xMax * 1000 / face->units_per_EM;
203
	font->bbox.y1 = face->bbox.yMax * 1000 / face->units_per_EM;
204
 
205
	*fontp = font;
206
	return fz_okay;
207
}
208
 
209
fz_error
210
fz_new_font_from_memory(fz_font **fontp, unsigned char *data, int len, int index)
211
{
212
	FT_Face face;
213
	fz_error error;
214
	fz_font *font;
215
	int fterr;
216
 
217
	error = fz_init_freetype();
218
	if (error)
219
		return fz_rethrow(error, "cannot init freetype library");
220
 
221
	fterr = FT_New_Memory_Face(fz_ftlib, data, len, index, &face);
222
	if (fterr)
223
		return fz_throw("freetype: cannot load font: %s", ft_error_string(fterr));
224
 
225
	font = fz_new_font(face->family_name);
226
	font->ft_face = face;
227
	font->bbox.x0 = face->bbox.xMin * 1000 / face->units_per_EM;
228
	font->bbox.y0 = face->bbox.yMin * 1000 / face->units_per_EM;
229
	font->bbox.x1 = face->bbox.xMax * 1000 / face->units_per_EM;
230
	font->bbox.y1 = face->bbox.yMax * 1000 / face->units_per_EM;
231
 
232
	*fontp = font;
233
	return fz_okay;
234
}
235
 
236
static fz_matrix
237
fz_adjust_ft_glyph_width(fz_font *font, int gid, fz_matrix trm)
238
{
239
	/* Fudge the font matrix to stretch the glyph if we've substituted the font. */
240
	if (font->ft_substitute && gid < font->width_count)
241
	{
242
		FT_Error fterr;
243
		int subw;
244
		int realw;
245
		float scale;
246
 
247
		/* TODO: use FT_Get_Advance */
248
		fterr = FT_Set_Char_Size(font->ft_face, 1000, 1000, 72, 72);
249
		if (fterr)
250
			fz_warn("freetype setting character size: %s", ft_error_string(fterr));
251
 
252
		fterr = FT_Load_Glyph(font->ft_face, gid,
253
			FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM);
254
		if (fterr)
255
			fz_warn("freetype failed to load glyph: %s", ft_error_string(fterr));
256
 
257
		realw = ((FT_Face)font->ft_face)->glyph->metrics.horiAdvance;
258
		subw = font->width_table[gid];
259
		if (realw)
260
			scale = (float) subw / realw;
261
		else
262
			scale = 1;
263
 
264
		return fz_concat(fz_scale(scale, 1), trm);
265
	}
266
 
267
	return trm;
268
}
269
 
270
static fz_pixmap *
271
fz_copy_ft_bitmap(int left, int top, FT_Bitmap *bitmap)
272
{
273
	fz_pixmap *pixmap;
274
	int y;
275
 
276
	pixmap = fz_new_pixmap(NULL, bitmap->width, bitmap->rows);
277
	pixmap->x = left;
278
	pixmap->y = top - bitmap->rows;
279
 
280
	if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO)
281
	{
282
		for (y = 0; y < pixmap->h; y++)
283
		{
284
			unsigned char *out = pixmap->samples + y * pixmap->w;
285
			unsigned char *in = bitmap->buffer + (pixmap->h - y - 1) * bitmap->pitch;
286
			unsigned char bit = 0x80;
287
			int w = pixmap->w;
288
			while (w--)
289
			{
290
				*out++ = (*in & bit) ? 255 : 0;
291
				bit >>= 1;
292
				if (bit == 0)
293
				{
294
					bit = 0x80;
295
					in++;
296
				}
297
			}
298
		}
299
	}
300
	else
301
	{
302
		for (y = 0; y < pixmap->h; y++)
303
		{
304
			memcpy(pixmap->samples + y * pixmap->w,
305
				bitmap->buffer + (pixmap->h - y - 1) * bitmap->pitch,
306
				pixmap->w);
307
		}
308
	}
309
 
310
	return pixmap;
311
}
312
 
313
fz_pixmap *
314
fz_render_ft_glyph(fz_font *font, int gid, fz_matrix trm)
315
{
316
	FT_Face face = font->ft_face;
317
	FT_Matrix m;
318
	FT_Vector v;
319
	FT_Error fterr;
320
 
321
	trm = fz_adjust_ft_glyph_width(font, gid, trm);
322
 
323
	if (font->ft_italic)
324
		trm = fz_concat(fz_shear(0.3f, 0), trm);
325
 
326
	/*
327
	Freetype mutilates complex glyphs if they are loaded
328
	with FT_Set_Char_Size 1.0. it rounds the coordinates
329
	before applying transformation. to get more precision in
330
	freetype, we shift part of the scale in the matrix
331
	into FT_Set_Char_Size instead
332
	*/
333
 
334
	m.xx = trm.a * 64; /* should be 65536 */
335
	m.yx = trm.b * 64;
336
	m.xy = trm.c * 64;
337
	m.yy = trm.d * 64;
338
	v.x = trm.e * 64;
339
	v.y = trm.f * 64;
340
 
341
	fterr = FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */
342
	if (fterr)
343
		fz_warn("freetype setting character size: %s", ft_error_string(fterr));
344
	FT_Set_Transform(face, &m, &v);
345
 
346
	if (fz_get_aa_level() == 0)
347
	{
348
		/* If you really want grid fitting, enable this code. */
349
		float scale = fz_matrix_expansion(trm);
350
		m.xx = trm.a * 65536 / scale;
351
		m.xy = trm.b * 65536 / scale;
352
		m.yx = trm.c * 65536 / scale;
353
		m.yy = trm.d * 65536 / scale;
354
		v.x = 0;
355
		v.y = 0;
356
 
357
		fterr = FT_Set_Char_Size(face, 64 * scale, 64 * scale, 72, 72);
358
		if (fterr)
359
			fz_warn("freetype setting character size: %s", ft_error_string(fterr));
360
		FT_Set_Transform(face, &m, &v);
361
		fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_TARGET_MONO);
362
		if (fterr)
363
			fz_warn("freetype load glyph (gid %d): %s", gid, ft_error_string(fterr));
364
	}
365
	else if (font->ft_hint)
366
	{
367
		/*
368
		Enable hinting, but keep the huge char size so that
369
		it is hinted for a character. This will in effect nullify
370
		the effect of grid fitting. This form of hinting should
371
		only be used for DynaLab and similar tricky TrueType fonts,
372
		so that we get the correct outline shape.
373
		*/
374
		fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP);
375
		if (fterr)
376
			fz_warn("freetype load glyph (gid %d): %s", gid, ft_error_string(fterr));
377
	}
378
	else
379
	{
380
		fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING);
381
		if (fterr)
382
		{
383
			fz_warn("freetype load glyph (gid %d): %s", gid, ft_error_string(fterr));
384
			return NULL;
385
		}
386
	}
387
 
388
	if (font->ft_bold)
389
	{
390
		float strength = fz_matrix_expansion(trm) * 0.04f;
391
		FT_Outline_Embolden(&face->glyph->outline, strength * 64);
392
		FT_Outline_Translate(&face->glyph->outline, -strength * 32, -strength * 32);
393
	}
394
 
395
	fterr = FT_Render_Glyph(face->glyph, fz_get_aa_level() > 0 ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
396
	if (fterr)
397
	{
398
		fz_warn("freetype render glyph (gid %d): %s", gid, ft_error_string(fterr));
399
		return NULL;
400
	}
401
 
402
	return fz_copy_ft_bitmap(face->glyph->bitmap_left, face->glyph->bitmap_top, &face->glyph->bitmap);
403
}
404
 
405
fz_pixmap *
406
fz_render_ft_stroked_glyph(fz_font *font, int gid, fz_matrix trm, fz_matrix ctm, fz_stroke_state *state)
407
{
408
	FT_Face face = font->ft_face;
409
	float expansion = fz_matrix_expansion(ctm);
410
	int linewidth = state->linewidth * expansion * 64 / 2;
411
	FT_Matrix m;
412
	FT_Vector v;
413
	FT_Error fterr;
414
	FT_Stroker stroker;
415
	FT_Glyph glyph;
416
	FT_BitmapGlyph bitmap;
417
	fz_pixmap *pixmap;
418
 
419
	trm = fz_adjust_ft_glyph_width(font, gid, trm);
420
 
421
	if (font->ft_italic)
422
		trm = fz_concat(fz_shear(0.3f, 0), trm);
423
 
424
	m.xx = trm.a * 64; /* should be 65536 */
425
	m.yx = trm.b * 64;
426
	m.xy = trm.c * 64;
427
	m.yy = trm.d * 64;
428
	v.x = trm.e * 64;
429
	v.y = trm.f * 64;
430
 
431
	fterr = FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */
432
	if (fterr)
433
	{
434
		fz_warn("FT_Set_Char_Size: %s", ft_error_string(fterr));
435
		return NULL;
436
	}
437
 
438
	FT_Set_Transform(face, &m, &v);
439
 
440
	fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING);
441
	if (fterr)
442
	{
443
		fz_warn("FT_Load_Glyph(gid %d): %s", gid, ft_error_string(fterr));
444
		return NULL;
445
	}
446
 
447
	fterr = FT_Stroker_New(fz_ftlib, &stroker);
448
	if (fterr)
449
	{
450
		fz_warn("FT_Stroker_New: %s", ft_error_string(fterr));
451
		return NULL;
452
	}
453
 
454
	FT_Stroker_Set(stroker, linewidth, state->start_cap, state->linejoin, state->miterlimit * 65536);
455
 
456
	fterr = FT_Get_Glyph(face->glyph, &glyph);
457
	if (fterr)
458
	{
459
		fz_warn("FT_Get_Glyph: %s", ft_error_string(fterr));
460
		FT_Stroker_Done(stroker);
461
		return NULL;
462
	}
463
 
464
	fterr = FT_Glyph_Stroke(&glyph, stroker, 1);
465
	if (fterr)
466
	{
467
		fz_warn("FT_Glyph_Stroke: %s", ft_error_string(fterr));
468
		FT_Done_Glyph(glyph);
469
		FT_Stroker_Done(stroker);
470
		return NULL;
471
	}
472
 
473
	FT_Stroker_Done(stroker);
474
 
475
	fterr = FT_Glyph_To_Bitmap(&glyph, fz_get_aa_level() > 0 ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1);
476
	if (fterr)
477
	{
478
		fz_warn("FT_Glyph_To_Bitmap: %s", ft_error_string(fterr));
479
		FT_Done_Glyph(glyph);
480
		return NULL;
481
	}
482
 
483
	bitmap = (FT_BitmapGlyph)glyph;
484
	pixmap = fz_copy_ft_bitmap(bitmap->left, bitmap->top, &bitmap->bitmap);
485
	FT_Done_Glyph(glyph);
486
 
487
	return pixmap;
488
}
489
 
490
/*
491
 * Type 3 fonts...
492
 */
493
 
494
fz_font *
495
fz_new_type3_font(char *name, fz_matrix matrix)
496
{
497
	fz_font *font;
498
	int i;
499
 
500
	font = fz_new_font(name);
501
	font->t3procs = fz_calloc(256, sizeof(fz_buffer*));
502
	font->t3widths = fz_calloc(256, sizeof(float));
503
 
504
	font->t3matrix = matrix;
505
	for (i = 0; i < 256; i++)
506
	{
507
		font->t3procs[i] = NULL;
508
		font->t3widths[i] = 0;
509
	}
510
 
511
	return font;
512
}
513
 
514
fz_pixmap *
515
fz_render_t3_glyph(fz_font *font, int gid, fz_matrix trm, fz_colorspace *model)
516
{
517
	fz_error error;
518
	fz_matrix ctm;
519
	fz_buffer *contents;
520
	fz_bbox bbox;
521
	fz_device *dev;
522
	fz_glyph_cache *cache;
523
	fz_pixmap *glyph;
524
	fz_pixmap *result;
525
 
526
	if (gid < 0 || gid > 255)
527
		return NULL;
528
 
529
	contents = font->t3procs[gid];
530
	if (!contents)
531
		return NULL;
532
 
533
	ctm = fz_concat(font->t3matrix, trm);
534
	dev = fz_new_bbox_device(&bbox);
535
	error = font->t3run(font->t3xref, font->t3resources, contents, dev, ctm);
536
	if (error)
537
		fz_catch(error, "cannot draw type3 glyph");
538
 
539
	if (dev->flags & FZ_CHARPROC_MASK)
540
	{
541
		if (dev->flags & FZ_CHARPROC_COLOR)
542
			fz_warn("type3 glyph claims to be both masked and colored");
543
		model = NULL;
544
	}
545
	else if (dev->flags & FZ_CHARPROC_COLOR)
546
	{
547
		if (model == NULL)
548
			fz_warn("colored type3 glyph wanted in masked context");
549
	}
550
	else
551
	{
552
		fz_warn("type3 glyph doesn't specify masked or colored");
553
		model = NULL; /* Treat as masked */
554
	}
555
 
556
	fz_free_device(dev);
557
 
558
	bbox.x0--;
559
	bbox.y0--;
560
	bbox.x1++;
561
	bbox.y1++;
562
 
563
	glyph = fz_new_pixmap_with_rect(model ? model : fz_device_gray, bbox);
564
	fz_clear_pixmap(glyph);
565
 
566
	cache = fz_new_glyph_cache();
567
	dev = fz_new_draw_device_type3(cache, glyph);
568
	error = font->t3run(font->t3xref, font->t3resources, contents, dev, ctm);
569
	if (error)
570
		fz_catch(error, "cannot draw type3 glyph");
571
	fz_free_device(dev);
572
	fz_free_glyph_cache(cache);
573
 
574
	if (model == NULL)
575
	{
576
		result = fz_alpha_from_gray(glyph, 0);
577
		fz_drop_pixmap(glyph);
578
	}
579
	else
580
		result = glyph;
581
 
582
	return result;
583
}
584
 
585
void
586
fz_debug_font(fz_font *font)
587
{
588
	printf("font '%s' {\n", font->name);
589
 
590
	if (font->ft_face)
591
	{
592
		printf("\tfreetype face %p\n", font->ft_face);
593
		if (font->ft_substitute)
594
			printf("\tsubstitute font\n");
595
	}
596
 
597
	if (font->t3procs)
598
	{
599
		printf("\ttype3 matrix [%g %g %g %g]\n",
600
			font->t3matrix.a, font->t3matrix.b,
601
			font->t3matrix.c, font->t3matrix.d);
602
	}
603
 
604
	printf("\tbbox [%g %g %g %g]\n",
605
		font->bbox.x0, font->bbox.y0,
606
		font->bbox.x1, font->bbox.y1);
607
 
608
	printf("}\n");
609
}