Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7640 views
1
#include "mupdf/fitz.h"
2
3
#include <ft2build.h>
4
#include FT_FREETYPE_H
5
#include FT_ADVANCES_H
6
#include FT_STROKER_H
7
8
#define MAX_BBOX_TABLE_SIZE 4096
9
10
/* 20 degrees */
11
#define SHEAR 0.36397f
12
13
static void fz_drop_freetype(fz_context *ctx);
14
15
static fz_font *
16
fz_new_font(fz_context *ctx, const char *name, int use_glyph_bbox, int glyph_count)
17
{
18
fz_font *font;
19
int i;
20
21
font = fz_malloc_struct(ctx, fz_font);
22
font->refs = 1;
23
24
if (name)
25
fz_strlcpy(font->name, name, sizeof font->name);
26
else
27
fz_strlcpy(font->name, "(null)", sizeof font->name);
28
29
font->ft_face = NULL;
30
font->ft_substitute = 0;
31
font->ft_bold = 0;
32
font->ft_italic = 0;
33
font->ft_hint = 0;
34
35
font->ft_buffer = NULL;
36
font->ft_filepath = NULL;
37
38
font->t3matrix = fz_identity;
39
font->t3resources = NULL;
40
font->t3procs = NULL;
41
font->t3lists = NULL;
42
font->t3widths = NULL;
43
font->t3flags = NULL;
44
font->t3doc = NULL;
45
font->t3run = NULL;
46
47
font->bbox.x0 = 0;
48
font->bbox.y0 = 0;
49
font->bbox.x1 = 1;
50
font->bbox.y1 = 1;
51
52
font->use_glyph_bbox = use_glyph_bbox;
53
if (use_glyph_bbox && glyph_count <= MAX_BBOX_TABLE_SIZE)
54
{
55
font->bbox_count = glyph_count;
56
font->bbox_table = fz_malloc_array(ctx, glyph_count, sizeof(fz_rect));
57
for (i = 0; i < glyph_count; i++)
58
font->bbox_table[i] = fz_infinite_rect;
59
}
60
else
61
{
62
if (use_glyph_bbox)
63
fz_warn(ctx, "not building glyph bbox table for font '%s' with %d glyphs", font->name, glyph_count);
64
font->bbox_count = 0;
65
font->bbox_table = NULL;
66
}
67
68
font->width_count = 0;
69
font->width_table = NULL;
70
71
return font;
72
}
73
74
fz_font *
75
fz_keep_font(fz_context *ctx, fz_font *font)
76
{
77
return fz_keep_imp(ctx, font, &font->refs);
78
}
79
80
static void
81
free_resources(fz_context *ctx, fz_font *font)
82
{
83
int i;
84
85
if (font->t3resources)
86
{
87
font->t3freeres(ctx, font->t3doc, font->t3resources);
88
font->t3resources = NULL;
89
}
90
91
if (font->t3procs)
92
{
93
for (i = 0; i < 256; i++)
94
if (font->t3procs[i])
95
fz_drop_buffer(ctx, font->t3procs[i]);
96
}
97
fz_free(ctx, font->t3procs);
98
font->t3procs = NULL;
99
}
100
101
void fz_decouple_type3_font(fz_context *ctx, fz_font *font, void *t3doc)
102
{
103
if (!ctx || !font || !t3doc || font->t3doc == NULL)
104
return;
105
106
if (font->t3doc != t3doc)
107
fz_throw(ctx, FZ_ERROR_GENERIC, "can't decouple type3 font from a different doc");
108
109
font->t3doc = NULL;
110
free_resources(ctx, font);
111
}
112
113
void
114
fz_drop_font(fz_context *ctx, fz_font *font)
115
{
116
int fterr;
117
int i;
118
119
if (!fz_drop_imp(ctx, font, &font->refs))
120
return;
121
122
free_resources(ctx, font);
123
if (font->t3lists)
124
{
125
for (i = 0; i < 256; i++)
126
{
127
if (font->t3lists[i])
128
fz_drop_display_list(ctx, font->t3lists[i]);
129
}
130
fz_free(ctx, font->t3procs);
131
fz_free(ctx, font->t3lists);
132
fz_free(ctx, font->t3widths);
133
fz_free(ctx, font->t3flags);
134
}
135
136
if (font->ft_face)
137
{
138
fz_lock(ctx, FZ_LOCK_FREETYPE);
139
fterr = FT_Done_Face((FT_Face)font->ft_face);
140
fz_unlock(ctx, FZ_LOCK_FREETYPE);
141
if (fterr)
142
fz_warn(ctx, "freetype finalizing face: %s", ft_error_string(fterr));
143
fz_drop_freetype(ctx);
144
}
145
146
fz_drop_buffer(ctx, font->ft_buffer);
147
fz_free(ctx, font->ft_filepath);
148
fz_free(ctx, font->bbox_table);
149
fz_free(ctx, font->width_table);
150
fz_free(ctx, font);
151
}
152
153
void
154
fz_set_font_bbox(fz_context *ctx, fz_font *font, float xmin, float ymin, float xmax, float ymax)
155
{
156
if (xmin >= xmax || ymin >= ymax)
157
{
158
/* Invalid bbox supplied. It would be prohibitively slow to
159
* measure the true one, so make one up. */
160
font->bbox.x0 = -1;
161
font->bbox.y0 = -1;
162
font->bbox.x1 = 2;
163
font->bbox.y1 = 2;
164
}
165
else
166
{
167
font->bbox.x0 = xmin;
168
font->bbox.y0 = ymin;
169
font->bbox.x1 = xmax;
170
font->bbox.y1 = ymax;
171
}
172
}
173
174
/*
175
* Freetype hooks
176
*/
177
178
struct fz_font_context_s {
179
int ctx_refs;
180
FT_Library ftlib;
181
int ftlib_refs;
182
fz_load_system_font_func load_font;
183
fz_load_system_cjk_font_func load_cjk_font;
184
};
185
186
#undef __FTERRORS_H__
187
#define FT_ERRORDEF(e, v, s) { (e), (s) },
188
#define FT_ERROR_START_LIST
189
#define FT_ERROR_END_LIST { 0, NULL }
190
191
struct ft_error
192
{
193
int err;
194
char *str;
195
};
196
197
void fz_new_font_context(fz_context *ctx)
198
{
199
ctx->font = fz_malloc_struct(ctx, fz_font_context);
200
ctx->font->ctx_refs = 1;
201
ctx->font->ftlib = NULL;
202
ctx->font->ftlib_refs = 0;
203
ctx->font->load_font = NULL;
204
}
205
206
fz_font_context *
207
fz_keep_font_context(fz_context *ctx)
208
{
209
if (!ctx)
210
return NULL;
211
return fz_keep_imp(ctx, ctx->font, &ctx->font->ctx_refs);
212
}
213
214
void fz_drop_font_context(fz_context *ctx)
215
{
216
if (!ctx)
217
return;
218
if (fz_drop_imp(ctx, ctx->font, &ctx->font->ctx_refs))
219
fz_free(ctx, ctx->font);
220
}
221
222
void fz_install_load_system_font_funcs(fz_context *ctx, fz_load_system_font_func f, fz_load_system_cjk_font_func f_cjk)
223
{
224
ctx->font->load_font = f;
225
ctx->font->load_cjk_font = f_cjk;
226
}
227
228
fz_font *fz_load_system_font(fz_context *ctx, const char *name, int bold, int italic, int needs_exact_metrics)
229
{
230
fz_font *font = NULL;
231
232
if (ctx->font->load_font)
233
{
234
fz_try(ctx)
235
{
236
font = ctx->font->load_font(ctx, name, bold, italic, needs_exact_metrics);
237
}
238
fz_catch(ctx)
239
{
240
font = NULL;
241
}
242
}
243
244
return font;
245
}
246
247
fz_font *fz_load_system_cjk_font(fz_context *ctx, const char *name, int ros, int serif)
248
{
249
fz_font *font = NULL;
250
251
if (ctx->font->load_cjk_font)
252
{
253
fz_try(ctx)
254
{
255
font = ctx->font->load_cjk_font(ctx, name, ros, serif);
256
}
257
fz_catch(ctx)
258
{
259
font = NULL;
260
}
261
}
262
263
return font;
264
}
265
266
static const struct ft_error ft_errors[] =
267
{
268
#include FT_ERRORS_H
269
};
270
271
char *ft_error_string(int err)
272
{
273
const struct ft_error *e;
274
275
for (e = ft_errors; e->str; e++)
276
if (e->err == err)
277
return e->str;
278
279
return "Unknown error";
280
}
281
282
static void
283
fz_keep_freetype(fz_context *ctx)
284
{
285
int fterr;
286
int maj, min, pat;
287
fz_font_context *fct = ctx->font;
288
289
fz_lock(ctx, FZ_LOCK_FREETYPE);
290
if (fct->ftlib)
291
{
292
fct->ftlib_refs++;
293
fz_unlock(ctx, FZ_LOCK_FREETYPE);
294
return;
295
}
296
297
fterr = FT_Init_FreeType(&fct->ftlib);
298
if (fterr)
299
{
300
char *mess = ft_error_string(fterr);
301
fz_unlock(ctx, FZ_LOCK_FREETYPE);
302
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot init freetype: %s", mess);
303
}
304
305
FT_Library_Version(fct->ftlib, &maj, &min, &pat);
306
if (maj == 2 && min == 1 && pat < 7)
307
{
308
fterr = FT_Done_FreeType(fct->ftlib);
309
if (fterr)
310
fz_warn(ctx, "freetype finalizing: %s", ft_error_string(fterr));
311
fz_unlock(ctx, FZ_LOCK_FREETYPE);
312
fz_throw(ctx, FZ_ERROR_GENERIC, "freetype version too old: %d.%d.%d", maj, min, pat);
313
}
314
315
fct->ftlib_refs++;
316
fz_unlock(ctx, FZ_LOCK_FREETYPE);
317
}
318
319
static void
320
fz_drop_freetype(fz_context *ctx)
321
{
322
int fterr;
323
fz_font_context *fct = ctx->font;
324
325
fz_lock(ctx, FZ_LOCK_FREETYPE);
326
if (--fct->ftlib_refs == 0)
327
{
328
fterr = FT_Done_FreeType(fct->ftlib);
329
if (fterr)
330
fz_warn(ctx, "freetype finalizing: %s", ft_error_string(fterr));
331
fct->ftlib = NULL;
332
}
333
fz_unlock(ctx, FZ_LOCK_FREETYPE);
334
}
335
336
fz_font *
337
fz_new_font_from_file(fz_context *ctx, const char *name, const char *path, int index, int use_glyph_bbox)
338
{
339
FT_Face face;
340
fz_font *font;
341
int fterr;
342
343
fz_keep_freetype(ctx);
344
345
fz_lock(ctx, FZ_LOCK_FREETYPE);
346
fterr = FT_New_Face(ctx->font->ftlib, path, index, &face);
347
fz_unlock(ctx, FZ_LOCK_FREETYPE);
348
if (fterr)
349
{
350
fz_drop_freetype(ctx);
351
fz_throw(ctx, FZ_ERROR_GENERIC, "freetype: cannot load font: %s", ft_error_string(fterr));
352
}
353
354
if (!name)
355
name = face->family_name;
356
357
font = fz_new_font(ctx, name, use_glyph_bbox, face->num_glyphs);
358
font->ft_face = face;
359
fz_set_font_bbox(ctx, font,
360
(float) face->bbox.xMin / face->units_per_EM,
361
(float) face->bbox.yMin / face->units_per_EM,
362
(float) face->bbox.xMax / face->units_per_EM,
363
(float) face->bbox.yMax / face->units_per_EM);
364
font->ft_filepath = fz_strdup(ctx, path);
365
366
return font;
367
}
368
369
fz_font *
370
fz_new_font_from_memory(fz_context *ctx, const char *name, unsigned char *data, int len, int index, int use_glyph_bbox)
371
{
372
FT_Face face;
373
fz_font *font;
374
int fterr;
375
376
fz_keep_freetype(ctx);
377
378
fz_lock(ctx, FZ_LOCK_FREETYPE);
379
fterr = FT_New_Memory_Face(ctx->font->ftlib, data, len, index, &face);
380
fz_unlock(ctx, FZ_LOCK_FREETYPE);
381
if (fterr)
382
{
383
fz_drop_freetype(ctx);
384
fz_throw(ctx, FZ_ERROR_GENERIC, "freetype: cannot load font: %s", ft_error_string(fterr));
385
}
386
387
if (!name)
388
name = face->family_name;
389
390
font = fz_new_font(ctx, name, use_glyph_bbox, face->num_glyphs);
391
font->ft_face = face;
392
fz_set_font_bbox(ctx, font,
393
(float) face->bbox.xMin / face->units_per_EM,
394
(float) face->bbox.yMin / face->units_per_EM,
395
(float) face->bbox.xMax / face->units_per_EM,
396
(float) face->bbox.yMax / face->units_per_EM);
397
398
return font;
399
}
400
401
fz_font *
402
fz_new_font_from_buffer(fz_context *ctx, const char *name, fz_buffer *buffer, int index, int use_glyph_bbox)
403
{
404
fz_font *font = fz_new_font_from_memory(ctx, name, buffer->data, buffer->len, index, use_glyph_bbox);
405
font->ft_buffer = fz_keep_buffer(ctx, buffer); /* remember buffer so we can drop it when we free the font */
406
return font;
407
}
408
409
static fz_matrix *
410
fz_adjust_ft_glyph_width(fz_context *ctx, fz_font *font, int gid, fz_matrix *trm)
411
{
412
/* Fudge the font matrix to stretch the glyph if we've substituted the font. */
413
if (font->ft_substitute && font->width_table && gid < font->width_count /* && font->wmode == 0 */)
414
{
415
FT_Error fterr;
416
int subw;
417
int realw;
418
float scale;
419
420
fz_lock(ctx, FZ_LOCK_FREETYPE);
421
/* TODO: use FT_Get_Advance */
422
fterr = FT_Set_Char_Size(font->ft_face, 1000, 1000, 72, 72);
423
if (fterr)
424
fz_warn(ctx, "freetype setting character size: %s", ft_error_string(fterr));
425
426
fterr = FT_Load_Glyph(font->ft_face, gid,
427
FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM);
428
if (fterr)
429
fz_warn(ctx, "freetype failed to load glyph: %s", ft_error_string(fterr));
430
431
realw = ((FT_Face)font->ft_face)->glyph->metrics.horiAdvance;
432
fz_unlock(ctx, FZ_LOCK_FREETYPE);
433
subw = font->width_table[gid];
434
if (realw)
435
scale = (float) subw / realw;
436
else
437
scale = 1;
438
439
fz_pre_scale(trm, scale, 1);
440
}
441
442
return trm;
443
}
444
445
static fz_glyph *
446
glyph_from_ft_bitmap(fz_context *ctx, int left, int top, FT_Bitmap *bitmap)
447
{
448
if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO)
449
return fz_new_glyph_from_1bpp_data(ctx, left, top - bitmap->rows, bitmap->width, bitmap->rows, bitmap->buffer + (bitmap->rows-1)*bitmap->pitch, -bitmap->pitch);
450
else
451
return fz_new_glyph_from_8bpp_data(ctx, left, top - bitmap->rows, bitmap->width, bitmap->rows, bitmap->buffer + (bitmap->rows-1)*bitmap->pitch, -bitmap->pitch);
452
}
453
454
static fz_pixmap *
455
pixmap_from_ft_bitmap(fz_context *ctx, int left, int top, FT_Bitmap *bitmap)
456
{
457
if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO)
458
return fz_new_pixmap_from_1bpp_data(ctx, left, top - bitmap->rows, bitmap->width, bitmap->rows, bitmap->buffer + (bitmap->rows-1)*bitmap->pitch, -bitmap->pitch);
459
else
460
return fz_new_pixmap_from_8bpp_data(ctx, left, top - bitmap->rows, bitmap->width, bitmap->rows, bitmap->buffer + (bitmap->rows-1)*bitmap->pitch, -bitmap->pitch);
461
}
462
463
/* Takes the freetype lock, and returns with it held */
464
static FT_GlyphSlot
465
do_ft_render_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, int aa)
466
{
467
FT_Face face = font->ft_face;
468
FT_Matrix m;
469
FT_Vector v;
470
FT_Error fterr;
471
fz_matrix local_trm = *trm;
472
473
float strength = fz_matrix_expansion(trm) * 0.02f;
474
475
fz_adjust_ft_glyph_width(ctx, font, gid, &local_trm);
476
477
if (font->ft_italic)
478
fz_pre_shear(&local_trm, SHEAR, 0);
479
480
/*
481
Freetype mutilates complex glyphs if they are loaded
482
with FT_Set_Char_Size 1.0. it rounds the coordinates
483
before applying transformation. to get more precision in
484
freetype, we shift part of the scale in the matrix
485
into FT_Set_Char_Size instead
486
*/
487
488
m.xx = local_trm.a * 64; /* should be 65536 */
489
m.yx = local_trm.b * 64;
490
m.xy = local_trm.c * 64;
491
m.yy = local_trm.d * 64;
492
v.x = local_trm.e * 64;
493
v.y = local_trm.f * 64;
494
495
fz_lock(ctx, FZ_LOCK_FREETYPE);
496
fterr = FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */
497
if (fterr)
498
fz_warn(ctx, "freetype setting character size: %s", ft_error_string(fterr));
499
FT_Set_Transform(face, &m, &v);
500
501
if (aa == 0)
502
{
503
/* enable grid fitting for non-antialiased rendering */
504
float scale = fz_matrix_expansion(&local_trm);
505
m.xx = local_trm.a * 65536 / scale;
506
m.yx = local_trm.b * 65536 / scale;
507
m.xy = local_trm.c * 65536 / scale;
508
m.yy = local_trm.d * 65536 / scale;
509
v.x = 0;
510
v.y = 0;
511
512
fterr = FT_Set_Char_Size(face, 64 * scale, 64 * scale, 72, 72);
513
if (fterr)
514
fz_warn(ctx, "freetype setting character size: %s", ft_error_string(fterr));
515
FT_Set_Transform(face, &m, &v);
516
fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_TARGET_MONO);
517
if (fterr) {
518
fz_warn(ctx, "freetype load hinted glyph (gid %d): %s", gid, ft_error_string(fterr));
519
goto retry_unhinted;
520
}
521
}
522
else if (font->ft_hint)
523
{
524
/*
525
Enable hinting, but keep the huge char size so that
526
it is hinted for a character. This will in effect nullify
527
the effect of grid fitting. This form of hinting should
528
only be used for DynaLab and similar tricky TrueType fonts,
529
so that we get the correct outline shape.
530
*/
531
fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP);
532
if (fterr) {
533
fz_warn(ctx, "freetype load hinted glyph (gid %d): %s", gid, ft_error_string(fterr));
534
goto retry_unhinted;
535
}
536
}
537
else
538
{
539
retry_unhinted:
540
fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING);
541
if (fterr)
542
{
543
fz_warn(ctx, "freetype load glyph (gid %d): %s", gid, ft_error_string(fterr));
544
return NULL;
545
}
546
}
547
548
if (font->ft_bold)
549
{
550
FT_Outline_Embolden(&face->glyph->outline, strength * 64);
551
FT_Outline_Translate(&face->glyph->outline, -strength * 32, -strength * 32);
552
}
553
554
fterr = FT_Render_Glyph(face->glyph, fz_aa_level(ctx) > 0 ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
555
if (fterr)
556
{
557
fz_warn(ctx, "freetype render glyph (gid %d): %s", gid, ft_error_string(fterr));
558
return NULL;
559
}
560
return face->glyph;
561
}
562
563
fz_pixmap *
564
fz_render_ft_glyph_pixmap(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, int aa)
565
{
566
FT_GlyphSlot slot = do_ft_render_glyph(ctx, font, gid, trm, aa);
567
fz_pixmap *pixmap;
568
569
if (slot == NULL)
570
{
571
fz_unlock(ctx, FZ_LOCK_FREETYPE);
572
return NULL;
573
}
574
575
fz_try(ctx)
576
{
577
pixmap = pixmap_from_ft_bitmap(ctx, slot->bitmap_left, slot->bitmap_top, &slot->bitmap);
578
}
579
fz_always(ctx)
580
{
581
fz_unlock(ctx, FZ_LOCK_FREETYPE);
582
}
583
fz_catch(ctx)
584
{
585
fz_rethrow(ctx);
586
}
587
588
return pixmap;
589
}
590
591
/* The glyph cache lock is always taken when this is called. */
592
fz_glyph *
593
fz_render_ft_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, int aa)
594
{
595
FT_GlyphSlot slot = do_ft_render_glyph(ctx, font, gid, trm, aa);
596
fz_glyph *glyph;
597
598
if (slot == NULL)
599
{
600
fz_unlock(ctx, FZ_LOCK_FREETYPE);
601
return NULL;
602
}
603
604
fz_try(ctx)
605
{
606
glyph = glyph_from_ft_bitmap(ctx, slot->bitmap_left, slot->bitmap_top, &slot->bitmap);
607
}
608
fz_always(ctx)
609
{
610
fz_unlock(ctx, FZ_LOCK_FREETYPE);
611
}
612
fz_catch(ctx)
613
{
614
fz_rethrow(ctx);
615
}
616
617
return glyph;
618
}
619
620
/* Takes the freetype lock, and returns with it held */
621
static FT_Glyph
622
do_render_ft_stroked_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, const fz_matrix *ctm, fz_stroke_state *state)
623
{
624
FT_Face face = font->ft_face;
625
float expansion = fz_matrix_expansion(ctm);
626
int linewidth = state->linewidth * expansion * 64 / 2;
627
FT_Matrix m;
628
FT_Vector v;
629
FT_Error fterr;
630
FT_Stroker stroker;
631
FT_Glyph glyph;
632
FT_Stroker_LineJoin line_join;
633
fz_matrix local_trm = *trm;
634
635
fz_adjust_ft_glyph_width(ctx, font, gid, &local_trm);
636
637
if (font->ft_italic)
638
fz_pre_shear(&local_trm, SHEAR, 0);
639
640
m.xx = local_trm.a * 64; /* should be 65536 */
641
m.yx = local_trm.b * 64;
642
m.xy = local_trm.c * 64;
643
m.yy = local_trm.d * 64;
644
v.x = local_trm.e * 64;
645
v.y = local_trm.f * 64;
646
647
fz_lock(ctx, FZ_LOCK_FREETYPE);
648
fterr = FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */
649
if (fterr)
650
{
651
fz_warn(ctx, "FT_Set_Char_Size: %s", ft_error_string(fterr));
652
return NULL;
653
}
654
655
FT_Set_Transform(face, &m, &v);
656
657
fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING);
658
if (fterr)
659
{
660
fz_warn(ctx, "FT_Load_Glyph(gid %d): %s", gid, ft_error_string(fterr));
661
return NULL;
662
}
663
664
fterr = FT_Stroker_New(ctx->font->ftlib, &stroker);
665
if (fterr)
666
{
667
fz_warn(ctx, "FT_Stroker_New: %s", ft_error_string(fterr));
668
return NULL;
669
}
670
671
#if FREETYPE_MAJOR * 10000 + FREETYPE_MINOR * 100 + FREETYPE_PATCH > 20405
672
/* New freetype */
673
line_join =
674
state->linejoin == FZ_LINEJOIN_MITER ? FT_STROKER_LINEJOIN_MITER_FIXED :
675
state->linejoin == FZ_LINEJOIN_ROUND ? FT_STROKER_LINEJOIN_ROUND :
676
state->linejoin == FZ_LINEJOIN_BEVEL ? FT_STROKER_LINEJOIN_BEVEL :
677
FT_STROKER_LINEJOIN_MITER_VARIABLE;
678
#else
679
/* Old freetype */
680
line_join =
681
state->linejoin == FZ_LINEJOIN_MITER ? FT_STROKER_LINEJOIN_MITER :
682
state->linejoin == FZ_LINEJOIN_ROUND ? FT_STROKER_LINEJOIN_ROUND :
683
state->linejoin == FZ_LINEJOIN_BEVEL ? FT_STROKER_LINEJOIN_BEVEL :
684
FT_STROKER_LINEJOIN_MITER;
685
#endif
686
FT_Stroker_Set(stroker, linewidth, (FT_Stroker_LineCap)state->start_cap, line_join, state->miterlimit * 65536);
687
688
fterr = FT_Get_Glyph(face->glyph, &glyph);
689
if (fterr)
690
{
691
fz_warn(ctx, "FT_Get_Glyph: %s", ft_error_string(fterr));
692
FT_Stroker_Done(stroker);
693
return NULL;
694
}
695
696
fterr = FT_Glyph_Stroke(&glyph, stroker, 1);
697
if (fterr)
698
{
699
fz_warn(ctx, "FT_Glyph_Stroke: %s", ft_error_string(fterr));
700
FT_Done_Glyph(glyph);
701
FT_Stroker_Done(stroker);
702
return NULL;
703
}
704
705
FT_Stroker_Done(stroker);
706
707
fterr = FT_Glyph_To_Bitmap(&glyph, fz_aa_level(ctx) > 0 ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1);
708
if (fterr)
709
{
710
fz_warn(ctx, "FT_Glyph_To_Bitmap: %s", ft_error_string(fterr));
711
FT_Done_Glyph(glyph);
712
return NULL;
713
}
714
return glyph;
715
}
716
717
fz_pixmap *
718
fz_render_ft_stroked_glyph_pixmap(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, const fz_matrix *ctm, fz_stroke_state *state)
719
{
720
FT_Glyph glyph = do_render_ft_stroked_glyph(ctx, font, gid, trm, ctm, state);
721
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph;
722
fz_pixmap *pixmap;
723
724
if (bitmap == NULL)
725
{
726
fz_unlock(ctx, FZ_LOCK_FREETYPE);
727
return NULL;
728
}
729
730
fz_try(ctx)
731
{
732
pixmap = pixmap_from_ft_bitmap(ctx, bitmap->left, bitmap->top, &bitmap->bitmap);
733
}
734
fz_always(ctx)
735
{
736
FT_Done_Glyph(glyph);
737
fz_unlock(ctx, FZ_LOCK_FREETYPE);
738
}
739
fz_catch(ctx)
740
{
741
fz_rethrow(ctx);
742
}
743
744
return pixmap;
745
}
746
747
fz_glyph *
748
fz_render_ft_stroked_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, const fz_matrix *ctm, fz_stroke_state *state)
749
{
750
FT_Glyph glyph = do_render_ft_stroked_glyph(ctx, font, gid, trm, ctm, state);
751
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph;
752
fz_glyph *result;
753
754
if (bitmap == NULL)
755
{
756
fz_unlock(ctx, FZ_LOCK_FREETYPE);
757
return NULL;
758
}
759
760
fz_try(ctx)
761
{
762
result = glyph_from_ft_bitmap(ctx, bitmap->left, bitmap->top, &bitmap->bitmap);
763
}
764
fz_always(ctx)
765
{
766
FT_Done_Glyph(glyph);
767
fz_unlock(ctx, FZ_LOCK_FREETYPE);
768
}
769
fz_catch(ctx)
770
{
771
fz_rethrow(ctx);
772
}
773
774
return result;
775
}
776
777
static fz_rect *
778
fz_bound_ft_glyph(fz_context *ctx, fz_font *font, int gid, fz_rect *bounds)
779
{
780
FT_Face face = font->ft_face;
781
FT_Error fterr;
782
FT_BBox cbox;
783
FT_Matrix m;
784
FT_Vector v;
785
int ft_flags;
786
787
// TODO: refactor loading into fz_load_ft_glyph
788
// TODO: cache results
789
790
const int scale = face->units_per_EM;
791
const float recip = 1 / (float)scale;
792
const float strength = 0.02f;
793
fz_matrix local_trm = fz_identity;
794
795
fz_adjust_ft_glyph_width(ctx, font, gid, &local_trm);
796
797
if (font->ft_italic)
798
fz_pre_shear(&local_trm, SHEAR, 0);
799
800
m.xx = local_trm.a * 65536;
801
m.yx = local_trm.b * 65536;
802
m.xy = local_trm.c * 65536;
803
m.yy = local_trm.d * 65536;
804
v.x = local_trm.e * 65536;
805
v.y = local_trm.f * 65536;
806
807
if (font->ft_hint)
808
{
809
ft_flags = FT_LOAD_NO_BITMAP;
810
}
811
else
812
{
813
ft_flags = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING;
814
}
815
816
fz_lock(ctx, FZ_LOCK_FREETYPE);
817
/* Set the char size to scale=face->units_per_EM to effectively give
818
* us unscaled results. This avoids quantisation. We then apply the
819
* scale ourselves below. */
820
fterr = FT_Set_Char_Size(face, scale, scale, 72, 72);
821
if (fterr)
822
fz_warn(ctx, "freetype setting character size: %s", ft_error_string(fterr));
823
FT_Set_Transform(face, &m, &v);
824
825
fterr = FT_Load_Glyph(face, gid, ft_flags);
826
if (fterr)
827
{
828
fz_warn(ctx, "freetype load glyph (gid %d): %s", gid, ft_error_string(fterr));
829
fz_unlock(ctx, FZ_LOCK_FREETYPE);
830
bounds->x0 = bounds->x1 = local_trm.e;
831
bounds->y0 = bounds->y1 = local_trm.f;
832
return bounds;
833
}
834
835
if (font->ft_bold)
836
{
837
FT_Outline_Embolden(&face->glyph->outline, strength * scale);
838
FT_Outline_Translate(&face->glyph->outline, -strength * 0.5 * scale, -strength * 0.5 * scale);
839
}
840
841
FT_Outline_Get_CBox(&face->glyph->outline, &cbox);
842
fz_unlock(ctx, FZ_LOCK_FREETYPE);
843
bounds->x0 = cbox.xMin * recip;
844
bounds->y0 = cbox.yMin * recip;
845
bounds->x1 = cbox.xMax * recip;
846
bounds->y1 = cbox.yMax * recip;
847
848
if (fz_is_empty_rect(bounds))
849
{
850
bounds->x0 = bounds->x1 = local_trm.e;
851
bounds->y0 = bounds->y1 = local_trm.f;
852
}
853
854
return bounds;
855
}
856
857
/* Turn FT_Outline into a fz_path */
858
859
struct closure {
860
fz_context *ctx;
861
fz_path *path;
862
fz_matrix trm;
863
};
864
865
static int move_to(const FT_Vector *p, void *cc_)
866
{
867
struct closure *cc = (struct closure *)cc_;
868
fz_context *ctx = cc->ctx;
869
fz_path *path = cc->path;
870
fz_point pt;
871
872
fz_transform_point_xy(&pt, &cc->trm, p->x, p->y);
873
fz_moveto(ctx, path, pt.x, pt.y);
874
return 0;
875
}
876
877
static int line_to(const FT_Vector *p, void *cc_)
878
{
879
struct closure *cc = (struct closure *)cc_;
880
fz_context *ctx = cc->ctx;
881
fz_path *path = cc->path;
882
fz_point pt;
883
884
fz_transform_point_xy(&pt, &cc->trm, p->x, p->y);
885
fz_lineto(ctx, path, pt.x, pt.y);
886
return 0;
887
}
888
889
static int conic_to(const FT_Vector *c, const FT_Vector *p, void *cc_)
890
{
891
struct closure *cc = (struct closure *)cc_;
892
fz_context *ctx = cc->ctx;
893
fz_path *path = cc->path;
894
fz_point ct, pt;
895
896
fz_transform_point_xy(&ct, &cc->trm, c->x, c->y);
897
fz_transform_point_xy(&pt, &cc->trm, p->x, p->y);
898
899
fz_quadto(ctx, path, ct.x, ct.y, pt.x, pt.y);
900
return 0;
901
}
902
903
static int cubic_to(const FT_Vector *c1, const FT_Vector *c2, const FT_Vector *p, void *cc_)
904
{
905
struct closure *cc = (struct closure *)cc_;
906
fz_context *ctx = cc->ctx;
907
fz_path *path = cc->path;
908
fz_point c1t, c2t, pt;
909
910
fz_transform_point_xy(&c1t, &cc->trm, c1->x, c1->y);
911
fz_transform_point_xy(&c2t, &cc->trm, c2->x, c2->y);
912
fz_transform_point_xy(&pt, &cc->trm, p->x, p->y);
913
914
fz_curveto(ctx, path, c1t.x, c1t.y, c2t.x, c2t.y, pt.x, pt.y);
915
return 0;
916
}
917
918
static const FT_Outline_Funcs outline_funcs = {
919
move_to, line_to, conic_to, cubic_to, 0, 0
920
};
921
922
fz_path *
923
fz_outline_ft_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm)
924
{
925
struct closure cc;
926
FT_Face face = font->ft_face;
927
int fterr;
928
fz_matrix local_trm = *trm;
929
int ft_flags;
930
931
const int scale = face->units_per_EM;
932
const float recip = 1 / (float)scale;
933
const float strength = 0.02f;
934
935
fz_adjust_ft_glyph_width(ctx, font, gid, &local_trm);
936
937
if (font->ft_italic)
938
fz_pre_shear(&local_trm, SHEAR, 0);
939
940
fz_lock(ctx, FZ_LOCK_FREETYPE);
941
942
if (font->ft_hint)
943
{
944
ft_flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM;
945
fterr = FT_Set_Char_Size(face, scale, scale, 72, 72);
946
if (fterr)
947
fz_warn(ctx, "freetype setting character size: %s", ft_error_string(fterr));
948
}
949
else
950
{
951
ft_flags = FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM;
952
}
953
954
fterr = FT_Load_Glyph(face, gid, ft_flags);
955
if (fterr)
956
{
957
fz_warn(ctx, "freetype load glyph (gid %d): %s", gid, ft_error_string(fterr));
958
fz_unlock(ctx, FZ_LOCK_FREETYPE);
959
return NULL;
960
}
961
962
if (font->ft_bold)
963
{
964
FT_Outline_Embolden(&face->glyph->outline, strength * scale);
965
FT_Outline_Translate(&face->glyph->outline, -strength * 0.5 * scale, -strength * 0.5 * scale);
966
}
967
968
cc.path = NULL;
969
fz_try(ctx)
970
{
971
cc.ctx = ctx;
972
cc.path = fz_new_path(ctx);
973
fz_concat(&cc.trm, fz_scale(&cc.trm, recip, recip), &local_trm);
974
fz_moveto(ctx, cc.path, cc.trm.e, cc.trm.f);
975
FT_Outline_Decompose(&face->glyph->outline, &outline_funcs, &cc);
976
fz_closepath(ctx, cc.path);
977
}
978
fz_always(ctx)
979
{
980
fz_unlock(ctx, FZ_LOCK_FREETYPE);
981
}
982
fz_catch(ctx)
983
{
984
fz_warn(ctx, "freetype cannot decompose outline");
985
fz_free(ctx, cc.path);
986
return NULL;
987
}
988
989
return cc.path;
990
}
991
992
/*
993
* Type 3 fonts...
994
*/
995
996
fz_font *
997
fz_new_type3_font(fz_context *ctx, const char *name, const fz_matrix *matrix)
998
{
999
fz_font *font;
1000
int i;
1001
1002
font = fz_new_font(ctx, name, 1, 256);
1003
fz_try(ctx)
1004
{
1005
font->t3procs = fz_malloc_array(ctx, 256, sizeof(fz_buffer*));
1006
font->t3lists = fz_malloc_array(ctx, 256, sizeof(fz_display_list*));
1007
font->t3widths = fz_malloc_array(ctx, 256, sizeof(float));
1008
font->t3flags = fz_malloc_array(ctx, 256, sizeof(unsigned short));
1009
}
1010
fz_catch(ctx)
1011
{
1012
fz_drop_font(ctx, font);
1013
fz_rethrow(ctx);
1014
}
1015
1016
font->t3matrix = *matrix;
1017
for (i = 0; i < 256; i++)
1018
{
1019
font->t3procs[i] = NULL;
1020
font->t3lists[i] = NULL;
1021
font->t3widths[i] = 0;
1022
font->t3flags[i] = 0;
1023
}
1024
1025
return font;
1026
}
1027
1028
void
1029
fz_prepare_t3_glyph(fz_context *ctx, fz_font *font, int gid, int nested_depth)
1030
{
1031
fz_buffer *contents;
1032
fz_device *dev;
1033
1034
contents = font->t3procs[gid];
1035
if (!contents)
1036
return;
1037
1038
/* We've not already loaded this one! */
1039
assert(font->t3lists[gid] == NULL);
1040
1041
font->t3lists[gid] = fz_new_display_list(ctx);
1042
1043
dev = fz_new_list_device(ctx, font->t3lists[gid]);
1044
dev->flags = FZ_DEVFLAG_FILLCOLOR_UNDEFINED |
1045
FZ_DEVFLAG_STROKECOLOR_UNDEFINED |
1046
FZ_DEVFLAG_STARTCAP_UNDEFINED |
1047
FZ_DEVFLAG_DASHCAP_UNDEFINED |
1048
FZ_DEVFLAG_ENDCAP_UNDEFINED |
1049
FZ_DEVFLAG_LINEJOIN_UNDEFINED |
1050
FZ_DEVFLAG_MITERLIMIT_UNDEFINED |
1051
FZ_DEVFLAG_LINEWIDTH_UNDEFINED;
1052
font->t3run(ctx, font->t3doc, font->t3resources, contents, dev, &fz_identity, NULL, 0);
1053
font->t3flags[gid] = dev->flags;
1054
if (dev->flags & FZ_DEVFLAG_BBOX_DEFINED)
1055
{
1056
assert(font->bbox_table != NULL);
1057
assert(font->bbox_count > gid);
1058
font->bbox_table[gid] = dev->d1_rect;
1059
fz_transform_rect(&font->bbox_table[gid], &font->t3matrix);
1060
}
1061
fz_drop_device(ctx, dev);
1062
}
1063
1064
static fz_rect *
1065
fz_bound_t3_glyph(fz_context *ctx, fz_font *font, int gid, fz_rect *bounds)
1066
{
1067
fz_display_list *list;
1068
fz_device *dev;
1069
fz_rect big;
1070
float m;
1071
1072
list = font->t3lists[gid];
1073
if (!list)
1074
{
1075
*bounds = fz_empty_rect;
1076
return bounds;
1077
}
1078
1079
dev = fz_new_bbox_device(ctx, bounds);
1080
fz_try(ctx)
1081
{
1082
fz_run_display_list(ctx, list, dev, &font->t3matrix, &fz_infinite_rect, NULL);
1083
}
1084
fz_always(ctx)
1085
{
1086
fz_drop_device(ctx, dev);
1087
}
1088
fz_catch(ctx)
1089
{
1090
fz_rethrow(ctx);
1091
}
1092
1093
/* clip the bbox size to a reasonable maximum for degenerate glyphs */
1094
big = font->bbox;
1095
m = fz_max(fz_abs(big.x1 - big.x0), fz_abs(big.y1 - big.y0));
1096
fz_expand_rect(&big, fz_max(fz_matrix_expansion(&font->t3matrix) * 2, m));
1097
fz_intersect_rect(bounds, &big);
1098
1099
return bounds;
1100
}
1101
1102
void
1103
fz_run_t3_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, fz_device *dev)
1104
{
1105
fz_display_list *list;
1106
fz_matrix ctm;
1107
1108
list = font->t3lists[gid];
1109
if (!list)
1110
return;
1111
1112
fz_concat(&ctm, &font->t3matrix, trm);
1113
fz_run_display_list(ctx, list, dev, &ctm, &fz_infinite_rect, NULL);
1114
}
1115
1116
fz_pixmap *
1117
fz_render_t3_glyph_pixmap(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, fz_colorspace *model, const fz_irect *scissor)
1118
{
1119
fz_display_list *list;
1120
fz_rect bounds;
1121
fz_irect bbox;
1122
fz_device *dev;
1123
fz_pixmap *glyph;
1124
fz_pixmap *result;
1125
1126
if (gid < 0 || gid > 255)
1127
return NULL;
1128
1129
list = font->t3lists[gid];
1130
if (!list)
1131
return NULL;
1132
1133
if (font->t3flags[gid] & FZ_DEVFLAG_MASK)
1134
{
1135
if (font->t3flags[gid] & FZ_DEVFLAG_COLOR)
1136
fz_warn(ctx, "type3 glyph claims to be both masked and colored");
1137
model = NULL;
1138
}
1139
else if (font->t3flags[gid] & FZ_DEVFLAG_COLOR)
1140
{
1141
if (!model)
1142
fz_warn(ctx, "colored type3 glyph wanted in masked context");
1143
}
1144
else
1145
{
1146
fz_warn(ctx, "type3 glyph doesn't specify masked or colored");
1147
model = NULL; /* Treat as masked */
1148
}
1149
1150
fz_expand_rect(fz_bound_glyph(ctx, font, gid, trm, &bounds), 1);
1151
fz_irect_from_rect(&bbox, &bounds);
1152
fz_intersect_irect(&bbox, scissor);
1153
1154
glyph = fz_new_pixmap_with_bbox(ctx, model ? model : fz_device_gray(ctx), &bbox);
1155
fz_clear_pixmap(ctx, glyph);
1156
1157
dev = fz_new_draw_device_type3(ctx, glyph);
1158
fz_try(ctx)
1159
{
1160
fz_run_t3_glyph(ctx, font, gid, trm, dev);
1161
}
1162
fz_always(ctx)
1163
{
1164
fz_drop_device(ctx, dev);
1165
}
1166
fz_catch(ctx)
1167
{
1168
fz_rethrow(ctx);
1169
}
1170
1171
if (!model)
1172
{
1173
fz_try(ctx)
1174
{
1175
result = fz_alpha_from_gray(ctx, glyph, 0);
1176
}
1177
fz_always(ctx)
1178
{
1179
fz_drop_pixmap(ctx, glyph);
1180
}
1181
fz_catch(ctx)
1182
{
1183
fz_rethrow(ctx);
1184
}
1185
}
1186
else
1187
result = glyph;
1188
1189
return result;
1190
}
1191
1192
fz_glyph *
1193
fz_render_t3_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, fz_colorspace *model, const fz_irect *scissor)
1194
{
1195
fz_pixmap *pixmap = fz_render_t3_glyph_pixmap(ctx, font, gid, trm, model, scissor);
1196
return fz_new_glyph_from_pixmap(ctx, pixmap);
1197
}
1198
1199
void
1200
fz_render_t3_glyph_direct(fz_context *ctx, fz_device *dev, fz_font *font, int gid, const fz_matrix *trm, void *gstate, int nested_depth)
1201
{
1202
fz_matrix ctm;
1203
void *contents;
1204
1205
if (gid < 0 || gid > 255)
1206
return;
1207
1208
contents = font->t3procs[gid];
1209
if (!contents)
1210
return;
1211
1212
if (font->t3flags[gid] & FZ_DEVFLAG_MASK)
1213
{
1214
if (font->t3flags[gid] & FZ_DEVFLAG_COLOR)
1215
fz_warn(ctx, "type3 glyph claims to be both masked and colored");
1216
}
1217
else if (font->t3flags[gid] & FZ_DEVFLAG_COLOR)
1218
{
1219
}
1220
else
1221
{
1222
fz_warn(ctx, "type3 glyph doesn't specify masked or colored");
1223
}
1224
1225
fz_concat(&ctm, &font->t3matrix, trm);
1226
font->t3run(ctx, font->t3doc, font->t3resources, contents, dev, &ctm, gstate, nested_depth);
1227
}
1228
1229
#ifndef NDEBUG
1230
void
1231
fz_print_font(fz_context *ctx, FILE *out, fz_font *font)
1232
{
1233
fprintf(out, "font '%s' {\n", font->name);
1234
1235
if (font->ft_face)
1236
{
1237
fprintf(out, "\tfreetype face %p\n", font->ft_face);
1238
if (font->ft_substitute)
1239
fprintf(out, "\tsubstitute font\n");
1240
}
1241
1242
if (font->t3procs)
1243
{
1244
fprintf(out, "\ttype3 matrix [%g %g %g %g]\n",
1245
font->t3matrix.a, font->t3matrix.b,
1246
font->t3matrix.c, font->t3matrix.d);
1247
1248
fprintf(out, "\ttype3 bbox [%g %g %g %g]\n",
1249
font->bbox.x0, font->bbox.y0,
1250
font->bbox.x1, font->bbox.y1);
1251
}
1252
1253
fprintf(out, "}\n");
1254
}
1255
#endif
1256
1257
fz_rect *
1258
fz_bound_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, fz_rect *rect)
1259
{
1260
if (font->bbox_table && gid < font->bbox_count)
1261
{
1262
if (fz_is_infinite_rect(&font->bbox_table[gid]))
1263
{
1264
if (font->ft_face)
1265
fz_bound_ft_glyph(ctx, font, gid, &font->bbox_table[gid]);
1266
else if (font->t3lists)
1267
fz_bound_t3_glyph(ctx, font, gid, &font->bbox_table[gid]);
1268
else
1269
font->bbox_table[gid] = fz_empty_rect;
1270
}
1271
*rect = font->bbox_table[gid];
1272
}
1273
else
1274
{
1275
/* fall back to font bbox */
1276
*rect = font->bbox;
1277
}
1278
1279
return fz_transform_rect(rect, trm);
1280
}
1281
1282
fz_path *
1283
fz_outline_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *ctm)
1284
{
1285
if (!font->ft_face)
1286
return NULL;
1287
return fz_outline_ft_glyph(ctx, font, gid, ctm);
1288
}
1289
1290
int fz_glyph_cacheable(fz_context *ctx, fz_font *font, int gid)
1291
{
1292
if (!font->t3procs || !font->t3flags || gid < 0 || gid >= font->bbox_count)
1293
return 1;
1294
return (font->t3flags[gid] & FZ_DEVFLAG_UNCACHEABLE) == 0;
1295
}
1296
1297
static float
1298
fz_advance_ft_glyph(fz_context *ctx, fz_font *font, int gid)
1299
{
1300
FT_Fixed adv;
1301
int mask;
1302
1303
if (font->ft_substitute && font->width_table && gid < font->width_count)
1304
return font->width_table[gid];
1305
1306
mask = FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM;
1307
/* if (font->wmode)
1308
mask |= FT_LOAD_VERTICAL_LAYOUT; */
1309
fz_lock(ctx, FZ_LOCK_FREETYPE);
1310
FT_Get_Advance(font->ft_face, gid, mask, &adv);
1311
fz_unlock(ctx, FZ_LOCK_FREETYPE);
1312
return (float) adv / ((FT_Face)font->ft_face)->units_per_EM;
1313
}
1314
1315
static float
1316
fz_advance_t3_glyph(fz_context *ctx, fz_font *font, int gid)
1317
{
1318
if (gid < 0 || gid > 255)
1319
return 0;
1320
return font->t3widths[gid];
1321
}
1322
1323
float
1324
fz_advance_glyph(fz_context *ctx, fz_font *font, int gid)
1325
{
1326
if (font->ft_face)
1327
return fz_advance_ft_glyph(ctx, font, gid);
1328
if (font->t3procs)
1329
return fz_advance_t3_glyph(ctx, font, gid);
1330
return 0;
1331
}
1332
1333
static int
1334
fz_encode_ft_character(fz_context *ctx, fz_font *font, int ucs)
1335
{
1336
return FT_Get_Char_Index(font->ft_face, ucs);
1337
}
1338
1339
int
1340
fz_encode_character(fz_context *ctx, fz_font *font, int ucs)
1341
{
1342
if (font->ft_face)
1343
return fz_encode_ft_character(ctx, font, ucs);
1344
return ucs;
1345
}
1346
1347