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