1 | /* |
2 | * Copyright © 2009 Red Hat, Inc. |
3 | * Copyright © 2009 Keith Stribley |
4 | * Copyright © 2015 Google, Inc. |
5 | * |
6 | * This is part of HarfBuzz, a text shaping library. |
7 | * |
8 | * Permission is hereby granted, without written agreement and without |
9 | * license or royalty fees, to use, copy, modify, and distribute this |
10 | * software and its documentation for any purpose, provided that the |
11 | * above copyright notice and the following two paragraphs appear in |
12 | * all copies of this software. |
13 | * |
14 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
15 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
16 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
17 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
18 | * DAMAGE. |
19 | * |
20 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
21 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
22 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
23 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
24 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
25 | * |
26 | * Red Hat Author(s): Behdad Esfahbod |
27 | * Google Author(s): Behdad Esfahbod |
28 | */ |
29 | |
30 | #include "hb.hh" |
31 | |
32 | #ifdef HAVE_FREETYPE |
33 | |
34 | #include "hb-ft.h" |
35 | |
36 | #include "hb-font.hh" |
37 | #include "hb-machinery.hh" |
38 | #include "hb-cache.hh" |
39 | |
40 | #include FT_ADVANCES_H |
41 | #include FT_MULTIPLE_MASTERS_H |
42 | #include FT_TRUETYPE_TABLES_H |
43 | |
44 | |
45 | /** |
46 | * SECTION:hb-ft |
47 | * @title: hb-ft |
48 | * @short_description: FreeType integration |
49 | * @include: hb-ft.h |
50 | * |
51 | * Functions for using HarfBuzz with the FreeType library to provide face and |
52 | * font data. |
53 | **/ |
54 | |
55 | |
56 | /* TODO: |
57 | * |
58 | * In general, this file does a fine job of what it's supposed to do. |
59 | * There are, however, things that need more work: |
60 | * |
61 | * - FreeType works in 26.6 mode. Clients can decide to use that mode, and everything |
62 | * would work fine. However, we also abuse this API for performing in font-space, |
63 | * but don't pass the correct flags to FreeType. We just abuse the no-hinting mode |
64 | * for that, such that no rounding etc happens. As such, we don't set ppem, and |
65 | * pass NO_HINTING as load_flags. Would be much better to use NO_SCALE, and scale |
66 | * ourselves. |
67 | * |
68 | * - We don't handle / allow for emboldening / obliqueing. |
69 | * |
70 | * - In the future, we should add constructors to create fonts in font space? |
71 | */ |
72 | |
73 | |
74 | struct hb_ft_font_t |
75 | { |
76 | mutable hb_mutex_t lock; |
77 | FT_Face ft_face; |
78 | int load_flags; |
79 | bool symbol; /* Whether selected cmap is symbol cmap. */ |
80 | bool unref; /* Whether to destroy ft_face when done. */ |
81 | |
82 | mutable hb_atomic_int_t cached_x_scale; |
83 | mutable hb_advance_cache_t advance_cache; |
84 | }; |
85 | |
86 | static hb_ft_font_t * |
87 | _hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) |
88 | { |
89 | hb_ft_font_t *ft_font = (hb_ft_font_t *) calloc (1, sizeof (hb_ft_font_t)); |
90 | |
91 | if (unlikely (!ft_font)) |
92 | return nullptr; |
93 | |
94 | ft_font->lock.init (); |
95 | ft_font->ft_face = ft_face; |
96 | ft_font->symbol = symbol; |
97 | ft_font->unref = unref; |
98 | |
99 | ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; |
100 | |
101 | ft_font->cached_x_scale.set_relaxed (0); |
102 | ft_font->advance_cache.init (); |
103 | |
104 | return ft_font; |
105 | } |
106 | |
107 | static void |
108 | _hb_ft_face_destroy (void *data) |
109 | { |
110 | FT_Done_Face ((FT_Face) data); |
111 | } |
112 | |
113 | static void |
114 | _hb_ft_font_destroy (void *data) |
115 | { |
116 | hb_ft_font_t *ft_font = (hb_ft_font_t *) data; |
117 | |
118 | ft_font->advance_cache.fini (); |
119 | |
120 | if (ft_font->unref) |
121 | _hb_ft_face_destroy (ft_font->ft_face); |
122 | |
123 | ft_font->lock.fini (); |
124 | |
125 | free (ft_font); |
126 | } |
127 | |
128 | /** |
129 | * hb_ft_font_set_load_flags: |
130 | * @font: |
131 | * @load_flags: |
132 | * |
133 | * |
134 | * |
135 | * Since: 1.0.5 |
136 | **/ |
137 | void |
138 | hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) |
139 | { |
140 | if (hb_object_is_immutable (font)) |
141 | return; |
142 | |
143 | if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) |
144 | return; |
145 | |
146 | hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; |
147 | |
148 | ft_font->load_flags = load_flags; |
149 | } |
150 | |
151 | /** |
152 | * hb_ft_font_get_load_flags: |
153 | * @font: |
154 | * |
155 | * |
156 | * |
157 | * Return value: |
158 | * Since: 1.0.5 |
159 | **/ |
160 | int |
161 | hb_ft_font_get_load_flags (hb_font_t *font) |
162 | { |
163 | if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) |
164 | return 0; |
165 | |
166 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; |
167 | |
168 | return ft_font->load_flags; |
169 | } |
170 | |
171 | FT_Face |
172 | hb_ft_font_get_face (hb_font_t *font) |
173 | { |
174 | if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) |
175 | return nullptr; |
176 | |
177 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; |
178 | |
179 | return ft_font->ft_face; |
180 | } |
181 | |
182 | |
183 | |
184 | static hb_bool_t |
185 | hb_ft_get_nominal_glyph (hb_font_t *font HB_UNUSED, |
186 | void *font_data, |
187 | hb_codepoint_t unicode, |
188 | hb_codepoint_t *glyph, |
189 | void *user_data HB_UNUSED) |
190 | { |
191 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; |
192 | hb_lock_t lock (ft_font->lock); |
193 | unsigned int g = FT_Get_Char_Index (ft_font->ft_face, unicode); |
194 | |
195 | if (unlikely (!g)) |
196 | { |
197 | if (unlikely (ft_font->symbol) && unicode <= 0x00FFu) |
198 | { |
199 | /* For symbol-encoded OpenType fonts, we duplicate the |
200 | * U+F000..F0FF range at U+0000..U+00FF. That's what |
201 | * Windows seems to do, and that's hinted about at: |
202 | * https://docs.microsoft.com/en-us/typography/opentype/spec/recom |
203 | * under "Non-Standard (Symbol) Fonts". */ |
204 | g = FT_Get_Char_Index (ft_font->ft_face, 0xF000u + unicode); |
205 | if (!g) |
206 | return false; |
207 | } |
208 | else |
209 | return false; |
210 | } |
211 | |
212 | *glyph = g; |
213 | return true; |
214 | } |
215 | |
216 | static unsigned int |
217 | hb_ft_get_nominal_glyphs (hb_font_t *font HB_UNUSED, |
218 | void *font_data, |
219 | unsigned int count, |
220 | const hb_codepoint_t *first_unicode, |
221 | unsigned int unicode_stride, |
222 | hb_codepoint_t *first_glyph, |
223 | unsigned int glyph_stride, |
224 | void *user_data HB_UNUSED) |
225 | { |
226 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; |
227 | hb_lock_t lock (ft_font->lock); |
228 | unsigned int done; |
229 | for (done = 0; |
230 | done < count && (*first_glyph = FT_Get_Char_Index (ft_font->ft_face, *first_unicode)); |
231 | done++) |
232 | { |
233 | first_unicode = &StructAtOffsetUnaligned<hb_codepoint_t> (first_unicode, unicode_stride); |
234 | first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride); |
235 | } |
236 | /* We don't need to do ft_font->symbol dance here, since HB calls the singular |
237 | * nominal_glyph() for what we don't handle here. */ |
238 | return done; |
239 | } |
240 | |
241 | |
242 | static hb_bool_t |
243 | hb_ft_get_variation_glyph (hb_font_t *font HB_UNUSED, |
244 | void *font_data, |
245 | hb_codepoint_t unicode, |
246 | hb_codepoint_t variation_selector, |
247 | hb_codepoint_t *glyph, |
248 | void *user_data HB_UNUSED) |
249 | { |
250 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; |
251 | hb_lock_t lock (ft_font->lock); |
252 | unsigned int g = FT_Face_GetCharVariantIndex (ft_font->ft_face, unicode, variation_selector); |
253 | |
254 | if (unlikely (!g)) |
255 | return false; |
256 | |
257 | *glyph = g; |
258 | return true; |
259 | } |
260 | |
261 | static void |
262 | hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, |
263 | unsigned count, |
264 | const hb_codepoint_t *first_glyph, |
265 | unsigned glyph_stride, |
266 | hb_position_t *first_advance, |
267 | unsigned advance_stride, |
268 | void *user_data HB_UNUSED) |
269 | { |
270 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; |
271 | hb_lock_t lock (ft_font->lock); |
272 | FT_Face ft_face = ft_font->ft_face; |
273 | int load_flags = ft_font->load_flags; |
274 | int mult = font->x_scale < 0 ? -1 : +1; |
275 | |
276 | if (font->x_scale != ft_font->cached_x_scale.get ()) |
277 | { |
278 | ft_font->advance_cache.clear (); |
279 | ft_font->cached_x_scale.set (font->x_scale); |
280 | } |
281 | |
282 | for (unsigned int i = 0; i < count; i++) |
283 | { |
284 | FT_Fixed v = 0; |
285 | hb_codepoint_t glyph = *first_glyph; |
286 | |
287 | unsigned int cv; |
288 | if (ft_font->advance_cache.get (glyph, &cv)) |
289 | v = cv; |
290 | else |
291 | { |
292 | FT_Get_Advance (ft_face, glyph, load_flags, &v); |
293 | ft_font->advance_cache.set (glyph, v); |
294 | } |
295 | |
296 | *first_advance = (v * mult + (1<<9)) >> 10; |
297 | first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride); |
298 | first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride); |
299 | } |
300 | } |
301 | |
302 | static hb_position_t |
303 | hb_ft_get_glyph_v_advance (hb_font_t *font, |
304 | void *font_data, |
305 | hb_codepoint_t glyph, |
306 | void *user_data HB_UNUSED) |
307 | { |
308 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; |
309 | hb_lock_t lock (ft_font->lock); |
310 | FT_Fixed v; |
311 | |
312 | if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v))) |
313 | return 0; |
314 | |
315 | if (font->y_scale < 0) |
316 | v = -v; |
317 | |
318 | /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates |
319 | * have a Y growing upward. Hence the extra negation. */ |
320 | return (-v + (1<<9)) >> 10; |
321 | } |
322 | |
323 | static hb_bool_t |
324 | hb_ft_get_glyph_v_origin (hb_font_t *font, |
325 | void *font_data, |
326 | hb_codepoint_t glyph, |
327 | hb_position_t *x, |
328 | hb_position_t *y, |
329 | void *user_data HB_UNUSED) |
330 | { |
331 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; |
332 | hb_lock_t lock (ft_font->lock); |
333 | FT_Face ft_face = ft_font->ft_face; |
334 | |
335 | if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) |
336 | return false; |
337 | |
338 | /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates |
339 | * have a Y growing upward. Hence the extra negation. */ |
340 | *x = ft_face->glyph->metrics.horiBearingX - ft_face->glyph->metrics.vertBearingX; |
341 | *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY); |
342 | |
343 | if (font->x_scale < 0) |
344 | *x = -*x; |
345 | if (font->y_scale < 0) |
346 | *y = -*y; |
347 | |
348 | return true; |
349 | } |
350 | |
351 | #ifndef HB_NO_OT_SHAPE_FALLBACK |
352 | static hb_position_t |
353 | hb_ft_get_glyph_h_kerning (hb_font_t *font, |
354 | void *font_data, |
355 | hb_codepoint_t left_glyph, |
356 | hb_codepoint_t right_glyph, |
357 | void *user_data HB_UNUSED) |
358 | { |
359 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; |
360 | FT_Vector kerningv; |
361 | |
362 | FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED; |
363 | if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv)) |
364 | return 0; |
365 | |
366 | return kerningv.x; |
367 | } |
368 | #endif |
369 | |
370 | static hb_bool_t |
371 | hb_ft_get_glyph_extents (hb_font_t *font, |
372 | void *font_data, |
373 | hb_codepoint_t glyph, |
374 | hb_glyph_extents_t *extents, |
375 | void *user_data HB_UNUSED) |
376 | { |
377 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; |
378 | hb_lock_t lock (ft_font->lock); |
379 | FT_Face ft_face = ft_font->ft_face; |
380 | |
381 | if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) |
382 | return false; |
383 | |
384 | extents->x_bearing = ft_face->glyph->metrics.horiBearingX; |
385 | extents->y_bearing = ft_face->glyph->metrics.horiBearingY; |
386 | extents->width = ft_face->glyph->metrics.width; |
387 | extents->height = -ft_face->glyph->metrics.height; |
388 | if (font->x_scale < 0) |
389 | { |
390 | extents->x_bearing = -extents->x_bearing; |
391 | extents->width = -extents->width; |
392 | } |
393 | if (font->y_scale < 0) |
394 | { |
395 | extents->y_bearing = -extents->y_bearing; |
396 | extents->height = -extents->height; |
397 | } |
398 | return true; |
399 | } |
400 | |
401 | static hb_bool_t |
402 | hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, |
403 | void *font_data, |
404 | hb_codepoint_t glyph, |
405 | unsigned int point_index, |
406 | hb_position_t *x, |
407 | hb_position_t *y, |
408 | void *user_data HB_UNUSED) |
409 | { |
410 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; |
411 | hb_lock_t lock (ft_font->lock); |
412 | FT_Face ft_face = ft_font->ft_face; |
413 | |
414 | if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) |
415 | return false; |
416 | |
417 | if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)) |
418 | return false; |
419 | |
420 | if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points)) |
421 | return false; |
422 | |
423 | *x = ft_face->glyph->outline.points[point_index].x; |
424 | *y = ft_face->glyph->outline.points[point_index].y; |
425 | |
426 | return true; |
427 | } |
428 | |
429 | static hb_bool_t |
430 | hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED, |
431 | void *font_data, |
432 | hb_codepoint_t glyph, |
433 | char *name, unsigned int size, |
434 | void *user_data HB_UNUSED) |
435 | { |
436 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; |
437 | hb_lock_t lock (ft_font->lock); |
438 | FT_Face ft_face = ft_font->ft_face; |
439 | |
440 | hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size); |
441 | if (ret && (size && !*name)) |
442 | ret = false; |
443 | |
444 | return ret; |
445 | } |
446 | |
447 | static hb_bool_t |
448 | hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED, |
449 | void *font_data, |
450 | const char *name, int len, /* -1 means nul-terminated */ |
451 | hb_codepoint_t *glyph, |
452 | void *user_data HB_UNUSED) |
453 | { |
454 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; |
455 | hb_lock_t lock (ft_font->lock); |
456 | FT_Face ft_face = ft_font->ft_face; |
457 | |
458 | if (len < 0) |
459 | *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name); |
460 | else { |
461 | /* Make a nul-terminated version. */ |
462 | char buf[128]; |
463 | len = hb_min (len, (int) sizeof (buf) - 1); |
464 | strncpy (buf, name, len); |
465 | buf[len] = '\0'; |
466 | *glyph = FT_Get_Name_Index (ft_face, buf); |
467 | } |
468 | |
469 | if (*glyph == 0) |
470 | { |
471 | /* Check whether the given name was actually the name of glyph 0. */ |
472 | char buf[128]; |
473 | if (!FT_Get_Glyph_Name(ft_face, 0, buf, sizeof (buf)) && |
474 | len < 0 ? !strcmp (buf, name) : !strncmp (buf, name, len)) |
475 | return true; |
476 | } |
477 | |
478 | return *glyph != 0; |
479 | } |
480 | |
481 | static hb_bool_t |
482 | hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, |
483 | void *font_data, |
484 | hb_font_extents_t *metrics, |
485 | void *user_data HB_UNUSED) |
486 | { |
487 | const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; |
488 | hb_lock_t lock (ft_font->lock); |
489 | FT_Face ft_face = ft_font->ft_face; |
490 | metrics->ascender = FT_MulFix(ft_face->ascender, ft_face->size->metrics.y_scale); |
491 | metrics->descender = FT_MulFix(ft_face->descender, ft_face->size->metrics.y_scale); |
492 | metrics->line_gap = FT_MulFix( ft_face->height, ft_face->size->metrics.y_scale ) - (metrics->ascender - metrics->descender); |
493 | if (font->y_scale < 0) |
494 | { |
495 | metrics->ascender = -metrics->ascender; |
496 | metrics->descender = -metrics->descender; |
497 | metrics->line_gap = -metrics->line_gap; |
498 | } |
499 | return true; |
500 | } |
501 | |
502 | #if HB_USE_ATEXIT |
503 | static void free_static_ft_funcs (); |
504 | #endif |
505 | |
506 | static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ft_font_funcs_lazy_loader_t> |
507 | { |
508 | static hb_font_funcs_t *create () |
509 | { |
510 | hb_font_funcs_t *funcs = hb_font_funcs_create (); |
511 | |
512 | hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, nullptr, nullptr); |
513 | //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, nullptr, nullptr); |
514 | hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, nullptr, nullptr); |
515 | hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ft_get_nominal_glyphs, nullptr, nullptr); |
516 | hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, nullptr, nullptr); |
517 | hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ft_get_glyph_h_advances, nullptr, nullptr); |
518 | hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, nullptr, nullptr); |
519 | //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, nullptr, nullptr); |
520 | hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, nullptr, nullptr); |
521 | #ifndef HB_NO_OT_SHAPE_FALLBACK |
522 | hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, nullptr, nullptr); |
523 | #endif |
524 | //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ft_get_glyph_v_kerning, nullptr, nullptr); |
525 | hb_font_funcs_set_glyph_extents_func (funcs, hb_ft_get_glyph_extents, nullptr, nullptr); |
526 | hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ft_get_glyph_contour_point, nullptr, nullptr); |
527 | hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, nullptr, nullptr); |
528 | hb_font_funcs_set_glyph_from_name_func (funcs, hb_ft_get_glyph_from_name, nullptr, nullptr); |
529 | |
530 | hb_font_funcs_make_immutable (funcs); |
531 | |
532 | #if HB_USE_ATEXIT |
533 | atexit (free_static_ft_funcs); |
534 | #endif |
535 | |
536 | return funcs; |
537 | } |
538 | } static_ft_funcs; |
539 | |
540 | #if HB_USE_ATEXIT |
541 | static |
542 | void free_static_ft_funcs () |
543 | { |
544 | static_ft_funcs.free_instance (); |
545 | } |
546 | #endif |
547 | |
548 | static hb_font_funcs_t * |
549 | _hb_ft_get_font_funcs () |
550 | { |
551 | return static_ft_funcs.get_unconst (); |
552 | } |
553 | |
554 | static void |
555 | _hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref) |
556 | { |
557 | bool symbol = ft_face->charmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL; |
558 | |
559 | hb_font_set_funcs (font, |
560 | _hb_ft_get_font_funcs (), |
561 | _hb_ft_font_create (ft_face, symbol, unref), |
562 | _hb_ft_font_destroy); |
563 | } |
564 | |
565 | |
566 | static hb_blob_t * |
567 | _hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) |
568 | { |
569 | FT_Face ft_face = (FT_Face) user_data; |
570 | FT_Byte *buffer; |
571 | FT_ULong length = 0; |
572 | FT_Error error; |
573 | |
574 | /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */ |
575 | |
576 | error = FT_Load_Sfnt_Table (ft_face, tag, 0, nullptr, &length); |
577 | if (error) |
578 | return nullptr; |
579 | |
580 | buffer = (FT_Byte *) malloc (length); |
581 | if (!buffer) |
582 | return nullptr; |
583 | |
584 | error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length); |
585 | if (error) |
586 | { |
587 | free (buffer); |
588 | return nullptr; |
589 | } |
590 | |
591 | return hb_blob_create ((const char *) buffer, length, |
592 | HB_MEMORY_MODE_WRITABLE, |
593 | buffer, free); |
594 | } |
595 | |
596 | /** |
597 | * hb_ft_face_create: |
598 | * @ft_face: (destroy destroy) (scope notified): |
599 | * @destroy: |
600 | * |
601 | * |
602 | * |
603 | * Return value: (transfer full): |
604 | * Since: 0.9.2 |
605 | **/ |
606 | hb_face_t * |
607 | hb_ft_face_create (FT_Face ft_face, |
608 | hb_destroy_func_t destroy) |
609 | { |
610 | hb_face_t *face; |
611 | |
612 | if (!ft_face->stream->read) { |
613 | hb_blob_t *blob; |
614 | |
615 | blob = hb_blob_create ((const char *) ft_face->stream->base, |
616 | (unsigned int) ft_face->stream->size, |
617 | HB_MEMORY_MODE_READONLY, |
618 | ft_face, destroy); |
619 | face = hb_face_create (blob, ft_face->face_index); |
620 | hb_blob_destroy (blob); |
621 | } else { |
622 | face = hb_face_create_for_tables (_hb_ft_reference_table, ft_face, destroy); |
623 | } |
624 | |
625 | hb_face_set_index (face, ft_face->face_index); |
626 | hb_face_set_upem (face, ft_face->units_per_EM); |
627 | |
628 | return face; |
629 | } |
630 | |
631 | /** |
632 | * hb_ft_face_create_referenced: |
633 | * @ft_face: |
634 | * |
635 | * |
636 | * |
637 | * Return value: (transfer full): |
638 | * Since: 0.9.38 |
639 | **/ |
640 | hb_face_t * |
641 | hb_ft_face_create_referenced (FT_Face ft_face) |
642 | { |
643 | FT_Reference_Face (ft_face); |
644 | return hb_ft_face_create (ft_face, _hb_ft_face_destroy); |
645 | } |
646 | |
647 | static void |
648 | hb_ft_face_finalize (FT_Face ft_face) |
649 | { |
650 | hb_face_destroy ((hb_face_t *) ft_face->generic.data); |
651 | } |
652 | |
653 | /** |
654 | * hb_ft_face_create_cached: |
655 | * @ft_face: |
656 | * |
657 | * |
658 | * |
659 | * Return value: (transfer full): |
660 | * Since: 0.9.2 |
661 | **/ |
662 | hb_face_t * |
663 | hb_ft_face_create_cached (FT_Face ft_face) |
664 | { |
665 | if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize)) |
666 | { |
667 | if (ft_face->generic.finalizer) |
668 | ft_face->generic.finalizer (ft_face); |
669 | |
670 | ft_face->generic.data = hb_ft_face_create (ft_face, nullptr); |
671 | ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize; |
672 | } |
673 | |
674 | return hb_face_reference ((hb_face_t *) ft_face->generic.data); |
675 | } |
676 | |
677 | |
678 | /** |
679 | * hb_ft_font_create: |
680 | * @ft_face: (destroy destroy) (scope notified): |
681 | * @destroy: |
682 | * |
683 | * |
684 | * |
685 | * Return value: (transfer full): |
686 | * Since: 0.9.2 |
687 | **/ |
688 | hb_font_t * |
689 | hb_ft_font_create (FT_Face ft_face, |
690 | hb_destroy_func_t destroy) |
691 | { |
692 | hb_font_t *font; |
693 | hb_face_t *face; |
694 | |
695 | face = hb_ft_face_create (ft_face, destroy); |
696 | font = hb_font_create (face); |
697 | hb_face_destroy (face); |
698 | _hb_ft_font_set_funcs (font, ft_face, false); |
699 | hb_ft_font_changed (font); |
700 | return font; |
701 | } |
702 | |
703 | void |
704 | hb_ft_font_changed (hb_font_t *font) |
705 | { |
706 | if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) |
707 | return; |
708 | |
709 | hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; |
710 | FT_Face ft_face = ft_font->ft_face; |
711 | |
712 | hb_font_set_scale (font, |
713 | (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16), |
714 | (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16)); |
715 | #if 0 /* hb-ft works in no-hinting model */ |
716 | hb_font_set_ppem (font, |
717 | ft_face->size->metrics.x_ppem, |
718 | ft_face->size->metrics.y_ppem); |
719 | #endif |
720 | |
721 | #ifdef HAVE_FT_GET_VAR_BLEND_COORDINATES |
722 | FT_MM_Var *mm_var = nullptr; |
723 | if (!FT_Get_MM_Var (ft_face, &mm_var)) |
724 | { |
725 | FT_Fixed *ft_coords = (FT_Fixed *) calloc (mm_var->num_axis, sizeof (FT_Fixed)); |
726 | int *coords = (int *) calloc (mm_var->num_axis, sizeof (int)); |
727 | if (coords && ft_coords) |
728 | { |
729 | if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, ft_coords)) |
730 | { |
731 | bool nonzero = false; |
732 | |
733 | for (unsigned int i = 0; i < mm_var->num_axis; ++i) |
734 | { |
735 | coords[i] = ft_coords[i] >>= 2; |
736 | nonzero = nonzero || coords[i]; |
737 | } |
738 | |
739 | if (nonzero) |
740 | hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis); |
741 | else |
742 | hb_font_set_var_coords_normalized (font, nullptr, 0); |
743 | } |
744 | } |
745 | free (coords); |
746 | free (ft_coords); |
747 | #ifdef HAVE_FT_DONE_MM_VAR |
748 | FT_Done_MM_Var (ft_face->glyph->library, mm_var); |
749 | #else |
750 | free (mm_var); |
751 | #endif |
752 | } |
753 | #endif |
754 | } |
755 | |
756 | /** |
757 | * hb_ft_font_create_referenced: |
758 | * @ft_face: |
759 | * |
760 | * |
761 | * |
762 | * Return value: (transfer full): |
763 | * Since: 0.9.38 |
764 | **/ |
765 | hb_font_t * |
766 | hb_ft_font_create_referenced (FT_Face ft_face) |
767 | { |
768 | FT_Reference_Face (ft_face); |
769 | return hb_ft_font_create (ft_face, _hb_ft_face_destroy); |
770 | } |
771 | |
772 | #if HB_USE_ATEXIT |
773 | static void free_static_ft_library (); |
774 | #endif |
775 | |
776 | static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<FT_Library>, |
777 | hb_ft_library_lazy_loader_t> |
778 | { |
779 | static FT_Library create () |
780 | { |
781 | FT_Library l; |
782 | if (FT_Init_FreeType (&l)) |
783 | return nullptr; |
784 | |
785 | #if HB_USE_ATEXIT |
786 | atexit (free_static_ft_library); |
787 | #endif |
788 | |
789 | return l; |
790 | } |
791 | static void destroy (FT_Library l) |
792 | { |
793 | FT_Done_FreeType (l); |
794 | } |
795 | static FT_Library get_null () |
796 | { |
797 | return nullptr; |
798 | } |
799 | } static_ft_library; |
800 | |
801 | #if HB_USE_ATEXIT |
802 | static |
803 | void free_static_ft_library () |
804 | { |
805 | static_ft_library.free_instance (); |
806 | } |
807 | #endif |
808 | |
809 | static FT_Library |
810 | get_ft_library () |
811 | { |
812 | return static_ft_library.get_unconst (); |
813 | } |
814 | |
815 | static void |
816 | _release_blob (FT_Face ft_face) |
817 | { |
818 | hb_blob_destroy ((hb_blob_t *) ft_face->generic.data); |
819 | } |
820 | |
821 | void |
822 | hb_ft_font_set_funcs (hb_font_t *font) |
823 | { |
824 | hb_blob_t *blob = hb_face_reference_blob (font->face); |
825 | unsigned int blob_length; |
826 | const char *blob_data = hb_blob_get_data (blob, &blob_length); |
827 | if (unlikely (!blob_length)) |
828 | DEBUG_MSG (FT, font, "Font face has empty blob" ); |
829 | |
830 | FT_Face ft_face = nullptr; |
831 | FT_Error err = FT_New_Memory_Face (get_ft_library (), |
832 | (const FT_Byte *) blob_data, |
833 | blob_length, |
834 | hb_face_get_index (font->face), |
835 | &ft_face); |
836 | |
837 | if (unlikely (err)) { |
838 | hb_blob_destroy (blob); |
839 | DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed" ); |
840 | return; |
841 | } |
842 | |
843 | if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL)) |
844 | FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); |
845 | |
846 | FT_Set_Char_Size (ft_face, |
847 | abs (font->x_scale), abs (font->y_scale), |
848 | 0, 0); |
849 | #if 0 |
850 | font->x_ppem * 72 * 64 / font->x_scale, |
851 | font->y_ppem * 72 * 64 / font->y_scale); |
852 | #endif |
853 | if (font->x_scale < 0 || font->y_scale < 0) |
854 | { |
855 | FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0, |
856 | 0, font->y_scale < 0 ? -1 : +1}; |
857 | FT_Set_Transform (ft_face, &matrix, nullptr); |
858 | } |
859 | |
860 | #ifdef HAVE_FT_SET_VAR_BLEND_COORDINATES |
861 | unsigned int num_coords; |
862 | const int *coords = hb_font_get_var_coords_normalized (font, &num_coords); |
863 | if (num_coords) |
864 | { |
865 | FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed)); |
866 | if (ft_coords) |
867 | { |
868 | for (unsigned int i = 0; i < num_coords; i++) |
869 | ft_coords[i] = coords[i] * 4; |
870 | FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords); |
871 | free (ft_coords); |
872 | } |
873 | } |
874 | #endif |
875 | |
876 | ft_face->generic.data = blob; |
877 | ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob; |
878 | |
879 | _hb_ft_font_set_funcs (font, ft_face, true); |
880 | hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); |
881 | } |
882 | |
883 | |
884 | #endif |
885 | |