Coverage Report

Created: 2026-08-31 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/hb-ft.cc
Line
Count
Source
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-cache.hh"
37
#include "hb-draw.hh"
38
#include "hb-font.hh"
39
#include "hb-machinery.hh"
40
#include "hb-ot-os2-table.hh"
41
#include "hb-ot-shaper-arabic-pua.hh"
42
#include "hb-paint.hh"
43
44
#include FT_MODULE_H
45
#include FT_ADVANCES_H
46
#include FT_MULTIPLE_MASTERS_H
47
#include FT_OUTLINE_H
48
#include FT_TRUETYPE_TABLES_H
49
#include FT_SYNTHESIS_H
50
#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300
51
#include FT_COLOR_H
52
#endif
53
54
55
/**
56
 * SECTION:hb-ft
57
 * @title: hb-ft
58
 * @short_description: FreeType integration
59
 * @include: hb-ft.h
60
 *
61
 * Functions for using HarfBuzz with the FreeType library.
62
 *
63
 * HarfBuzz supports using FreeType to provide face and
64
 * font data.
65
 *
66
 * <note>Note that FreeType is not thread-safe, therefore these
67
 * functions are not thread-safe either.</note>
68
 **/
69
70
71
/* TODO:
72
 *
73
 * In general, this file does a fine job of what it's supposed to do.
74
 * There are, however, things that need more work:
75
 *
76
 *   - FreeType works in 26.6 mode.  Clients can decide to use that mode, and everything
77
 *     would work fine.  However, we also abuse this API for performing in font-space,
78
 *     but don't pass the correct flags to FreeType.  We just abuse the no-hinting mode
79
 *     for that, such that no rounding etc happens.  As such, we don't set ppem, and
80
 *     pass NO_HINTING as load_flags.  Would be much better to use NO_SCALE, and scale
81
 *     ourselves.
82
 *
83
 *   - We don't handle / allow for emboldening / obliqueing.
84
 *
85
 *   - In the future, we should add constructors to create fonts in font space?
86
 */
87
88
89
using hb_ft_advance_cache_t = hb_cache_t<16, 24, 8, false>;
90
91
struct hb_ft_font_t
92
{
93
  int load_flags;
94
  bool symbol; /* Whether selected cmap is symbol cmap. */
95
  bool unref; /* Whether to destroy ft_face when done. */
96
  bool transform; /* Whether to apply FT_Face's transform. */
97
98
  mutable hb_mutex_t lock; /* Protects members below. */
99
  FT_Face ft_face;
100
  mutable hb_atomic_t<unsigned> cached_serial;
101
  mutable hb_ft_advance_cache_t advance_cache;
102
};
103
104
static hb_ft_font_t *
105
_hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref)
106
0
{
107
0
  hb_ft_font_t *ft_font = (hb_ft_font_t *) hb_calloc (1, sizeof (hb_ft_font_t));
108
0
  if (unlikely (!ft_font)) return nullptr;
109
110
0
  ft_font->lock.init ();
111
0
  ft_font->ft_face = ft_face;
112
0
  ft_font->symbol = symbol;
113
0
  ft_font->unref = unref;
114
115
0
  ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
116
117
0
  ft_font->cached_serial = UINT_MAX;
118
0
  new (&ft_font->advance_cache) hb_ft_advance_cache_t;
119
120
0
  return ft_font;
121
0
}
122
123
static void
124
_hb_ft_face_destroy (void *data)
125
0
{
126
0
  FT_Done_Face ((FT_Face) data);
127
0
}
128
129
static void
130
_hb_ft_font_destroy (void *data)
131
0
{
132
0
  hb_ft_font_t *ft_font = (hb_ft_font_t *) data;
133
134
0
  if (ft_font->unref)
135
0
    _hb_ft_face_destroy (ft_font->ft_face);
136
137
0
  ft_font->lock.fini ();
138
139
0
  hb_free (ft_font);
140
0
}
141
142
143
/* hb_font changed, update FT_Face. */
144
static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face)
145
0
{
146
0
  if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
147
0
    return;
148
149
0
  hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
150
151
0
  float x_mult = 1.f, y_mult = 1.f;
152
153
0
  if (font->x_scale < 0) x_mult = -x_mult;
154
0
  if (font->y_scale < 0) y_mult = -y_mult;
155
156
0
  if (FT_Set_Char_Size (ft_face,
157
0
      abs (font->x_scale), abs (font->y_scale),
158
0
      0, 0
159
#if 0
160
        font->x_ppem * 72 * 64 / font->x_scale,
161
        font->y_ppem * 72 * 64 / font->y_scale
162
#endif
163
0
     ) && ft_face->num_fixed_sizes)
164
0
  {
165
#ifdef HAVE_FT_GET_TRANSFORM
166
    /* Bitmap font, eg. bitmap color emoji. */
167
    /* Pick largest size? */
168
    int x_scale  = ft_face->available_sizes[ft_face->num_fixed_sizes - 1].x_ppem;
169
    int y_scale = ft_face->available_sizes[ft_face->num_fixed_sizes - 1].y_ppem;
170
    FT_Set_Char_Size (ft_face,
171
          x_scale, y_scale,
172
          0, 0);
173
174
    /* This contains the sign that was previously in x_mult/y_mult. */
175
    x_mult = (float) font->x_scale / x_scale;
176
    y_mult = (float) font->y_scale / y_scale;
177
#endif
178
0
  }
179
0
  else
180
0
  { /* Shrug */ }
181
182
183
0
  if (x_mult != 1.f || y_mult != 1.f)
184
0
  {
185
0
    FT_Matrix matrix = { (int) roundf (x_mult * (1<<16)), 0,
186
0
        0, (int) roundf (y_mult * (1<<16))};
187
0
    FT_Set_Transform (ft_face, &matrix, nullptr);
188
0
    ft_font->transform = true;
189
0
  }
190
0
  else
191
0
    FT_Set_Transform (ft_face, nullptr, nullptr);
192
193
0
#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR)
194
0
  if (font->has_nonzero_coords)
195
0
  {
196
0
    unsigned int num_coords;
197
0
    const float *coords = hb_font_get_var_coords_design (font, &num_coords);
198
0
    FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (num_coords, sizeof (FT_Fixed));
199
0
    if (ft_coords)
200
0
    {
201
0
      for (unsigned int i = 0; i < num_coords; i++)
202
0
    ft_coords[i] = coords[i] * 65536.f;
203
0
      FT_Set_Var_Design_Coordinates (ft_face, num_coords, ft_coords);
204
0
      hb_free (ft_coords);
205
0
    }
206
0
  }
207
0
  else if (font->num_coords)
208
0
  {
209
    // Some old versions of FreeType crash if we
210
    // call this function on non-variable fonts.
211
0
    FT_Set_Var_Design_Coordinates (ft_face, 0, nullptr);
212
0
  }
213
0
#endif
214
0
}
215
216
/* Check if hb_font changed, update FT_Face. */
217
static inline bool
218
_hb_ft_hb_font_check_changed (hb_font_t *font,
219
            const hb_ft_font_t *ft_font)
220
0
{
221
0
  if (font->serial != ft_font->cached_serial)
222
0
  {
223
0
    hb_lock_t lock (ft_font->lock);
224
0
    _hb_ft_hb_font_changed (font, ft_font->ft_face);
225
0
    ft_font->advance_cache.clear ();
226
0
    ft_font->cached_serial.set_release (font->serial.get_acquire ());
227
0
    return true;
228
0
  }
229
0
  return false;
230
0
}
231
232
233
/**
234
 * hb_ft_font_set_load_flags:
235
 * @font: #hb_font_t to work upon
236
 * @load_flags: The FreeType load flags to set
237
 *
238
 * Sets the FT_Load_Glyph load flags for the specified #hb_font_t.
239
 *
240
 * For more information, see
241
 * <https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_load_xxx>
242
 *
243
 * This function works with #hb_font_t objects created by
244
 * hb_ft_font_create() or hb_ft_font_create_referenced().
245
 *
246
 * Since: 1.0.5
247
 **/
248
void
249
hb_ft_font_set_load_flags (hb_font_t *font, int load_flags)
250
0
{
251
0
  if (hb_object_is_immutable (font))
252
0
    return;
253
254
0
  if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
255
0
    return;
256
257
0
  hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
258
259
0
  ft_font->load_flags = load_flags;
260
0
}
261
262
/**
263
 * hb_ft_font_get_load_flags:
264
 * @font: #hb_font_t to work upon
265
 *
266
 * Fetches the FT_Load_Glyph load flags of the specified #hb_font_t.
267
 *
268
 * For more information, see
269
 * <https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_load_xxx>
270
 *
271
 * This function works with #hb_font_t objects created by
272
 * hb_ft_font_create() or hb_ft_font_create_referenced().
273
 *
274
 * Return value: FT_Load_Glyph flags found, or 0
275
 *
276
 * Since: 1.0.5
277
 **/
278
int
279
hb_ft_font_get_load_flags (hb_font_t *font)
280
0
{
281
0
  if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
282
0
    return 0;
283
284
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
285
286
0
  return ft_font->load_flags;
287
0
}
288
289
/**
290
 * hb_ft_font_get_ft_face: (skip)
291
 * @font: #hb_font_t to work upon
292
 *
293
 * Fetches the FT_Face associated with the specified #hb_font_t
294
 * font object.
295
 *
296
 * This function works with #hb_font_t objects created by
297
 * hb_ft_font_create() or hb_ft_font_create_referenced().
298
 *
299
 * Return value: (nullable): the FT_Face found or `NULL`
300
 *
301
 * Since: 10.4.0
302
 **/
303
FT_Face
304
hb_ft_font_get_ft_face (hb_font_t *font)
305
0
{
306
0
  if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
307
0
    return nullptr;
308
309
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
310
311
0
  return ft_font->ft_face;
312
0
}
313
314
#ifndef HB_DISABLE_DEPRECATED
315
316
/**
317
 * hb_ft_font_get_face: (skip)
318
 * @font: #hb_font_t to work upon
319
 *
320
 * Fetches the FT_Face associated with the specified #hb_font_t
321
 * font object.
322
 *
323
 * This function works with #hb_font_t objects created by
324
 * hb_ft_font_create() or hb_ft_font_create_referenced().
325
 *
326
 * Return value: (nullable): the FT_Face found or `NULL`
327
 *
328
 * Since: 0.9.2
329
 * Deprecated: 10.4.0: Use hb_ft_font_get_ft_face() instead.
330
 **/
331
FT_Face
332
hb_ft_font_get_face (hb_font_t *font)
333
0
{
334
0
  return hb_ft_font_get_ft_face (font);
335
0
}
336
337
#endif
338
339
/**
340
 * hb_ft_font_lock_face: (skip)
341
 * @font: #hb_font_t to work upon
342
 *
343
 * Gets the FT_Face associated with @font.
344
 *
345
 * This face will be kept around and access to the FT_Face object
346
 * from other HarfBuzz API wil be blocked until you call hb_ft_font_unlock_face().
347
 *
348
 * This function works with #hb_font_t objects created by
349
 * hb_ft_font_create() or hb_ft_font_create_referenced().
350
 *
351
 * Return value: (nullable) (transfer none): the FT_Face associated with @font or `NULL`
352
 * Since: 2.6.5
353
 **/
354
FT_Face
355
hb_ft_font_lock_face (hb_font_t *font)
356
0
{
357
0
  if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
358
0
    return nullptr;
359
360
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
361
362
0
  ft_font->lock.lock ();
363
364
0
  return ft_font->ft_face;
365
0
}
366
367
/**
368
 * hb_ft_font_unlock_face: (skip)
369
 * @font: #hb_font_t to work upon
370
 *
371
 * Releases an FT_Face previously obtained with hb_ft_font_lock_face().
372
 *
373
 * Since: 2.6.5
374
 **/
375
void
376
hb_ft_font_unlock_face (hb_font_t *font)
377
0
{
378
0
  if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
379
0
    return;
380
381
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
382
383
0
  ft_font->lock.unlock ();
384
0
}
385
386
387
static hb_bool_t
388
hb_ft_get_nominal_glyph (hb_font_t *font,
389
       void *font_data,
390
       hb_codepoint_t unicode,
391
       hb_codepoint_t *glyph,
392
       void *user_data HB_UNUSED)
393
0
{
394
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
395
0
  hb_lock_t lock (ft_font->lock);
396
0
  unsigned int g = FT_Get_Char_Index (ft_font->ft_face, unicode);
397
398
0
  if (unlikely (!g))
399
0
  {
400
0
    if (unlikely (ft_font->symbol))
401
0
    {
402
0
      switch ((unsigned) font->face->table.OS2->get_font_page ()) {
403
0
      case OT::OS2::font_page_t::FONT_PAGE_NONE:
404
0
  if (unicode <= 0x00FFu)
405
    /* For symbol-encoded OpenType fonts, we duplicate the
406
     * U+F000..F0FF range at U+0000..U+00FF.  That's what
407
     * Windows seems to do, and that's hinted about at:
408
     * https://docs.microsoft.com/en-us/typography/opentype/spec/recom
409
     * under "Non-Standard (Symbol) Fonts". */
410
0
    g = FT_Get_Char_Index (ft_font->ft_face, 0xF000u + unicode);
411
0
  break;
412
0
#ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK
413
0
      case OT::OS2::font_page_t::FONT_PAGE_SIMP_ARABIC:
414
0
  g = FT_Get_Char_Index (ft_font->ft_face, _hb_arabic_pua_simp_map (unicode));
415
0
  break;
416
0
      case OT::OS2::font_page_t::FONT_PAGE_TRAD_ARABIC:
417
0
  g = FT_Get_Char_Index (ft_font->ft_face, _hb_arabic_pua_trad_map (unicode));
418
0
  break;
419
0
#endif
420
0
      default:
421
0
  break;
422
0
      }
423
0
      if (!g)
424
0
  return false;
425
0
    }
426
0
    else
427
0
      return false;
428
0
  }
429
430
0
  *glyph = g;
431
0
  return true;
432
0
}
433
434
static unsigned int
435
hb_ft_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
436
        void *font_data,
437
        unsigned int count,
438
        const hb_codepoint_t *first_unicode,
439
        unsigned int unicode_stride,
440
        hb_codepoint_t *first_glyph,
441
        unsigned int glyph_stride,
442
        void *user_data HB_UNUSED)
443
0
{
444
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
445
0
  hb_lock_t lock (ft_font->lock);
446
0
  unsigned int done;
447
0
  for (done = 0;
448
0
       done < count && (*first_glyph = FT_Get_Char_Index (ft_font->ft_face, *first_unicode));
449
0
       done++)
450
0
  {
451
0
    first_unicode = &StructAtOffsetUnaligned<hb_codepoint_t> (first_unicode, unicode_stride);
452
0
    first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
453
0
  }
454
  /* We don't need to do ft_font->symbol dance here, since HB calls the singular
455
   * nominal_glyph() for what we don't handle here. */
456
0
  return done;
457
0
}
458
459
460
static hb_bool_t
461
hb_ft_get_variation_glyph (hb_font_t *font HB_UNUSED,
462
         void *font_data,
463
         hb_codepoint_t unicode,
464
         hb_codepoint_t variation_selector,
465
         hb_codepoint_t *glyph,
466
         void *user_data HB_UNUSED)
467
0
{
468
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
469
0
  hb_lock_t lock (ft_font->lock);
470
0
  unsigned int g = FT_Face_GetCharVariantIndex (ft_font->ft_face, unicode, variation_selector);
471
472
0
  if (unlikely (!g))
473
0
    return false;
474
475
0
  *glyph = g;
476
0
  return true;
477
0
}
478
479
static void
480
hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data,
481
          unsigned count,
482
          const hb_codepoint_t *first_glyph,
483
          unsigned glyph_stride,
484
          hb_position_t *first_advance,
485
          unsigned advance_stride,
486
          void *user_data HB_UNUSED)
487
0
{
488
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
489
0
  _hb_ft_hb_font_check_changed (font, ft_font);
490
491
0
  hb_lock_t lock (ft_font->lock);
492
0
  FT_Face ft_face = ft_font->ft_face;
493
0
  int load_flags = ft_font->load_flags;
494
0
  float x_mult;
495
#ifdef HAVE_FT_GET_TRANSFORM
496
  if (ft_font->transform)
497
  {
498
    FT_Matrix matrix;
499
    FT_Get_Transform (ft_face, &matrix, nullptr);
500
    x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f;
501
    x_mult *= font->x_scale < 0 ? -1 : +1;
502
  }
503
  else
504
#endif
505
0
  {
506
0
    x_mult = font->x_scale < 0 ? -1 : +1;
507
0
  }
508
509
0
  for (unsigned int i = 0; i < count; i++)
510
0
  {
511
0
    hb_position_t advance;
512
0
    hb_codepoint_t glyph = *first_glyph;
513
514
0
    unsigned int cv;
515
0
    if (ft_font->advance_cache.get (glyph, &cv))
516
0
      advance = (hb_position_t) cv;
517
0
    else
518
0
    {
519
0
      FT_Fixed v = 0;
520
0
      FT_Get_Advance (ft_face, glyph, load_flags, &v);
521
      /* Work around bug that FreeType seems to return negative advance
522
       * for variable-set fonts if x_scale is negative! */
523
0
      double scaled = trunc (fabs ((double) v) * (double) x_mult + (1<<9)) / (1<<10);
524
0
      advance = hb_clamp_to<hb_position_t> (floor (scaled));
525
0
      ft_font->advance_cache.set (glyph, advance);
526
0
    }
527
528
0
    *first_advance = advance;
529
0
    first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
530
0
    first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
531
0
  }
532
0
}
533
534
#ifndef HB_NO_VERTICAL
535
static hb_position_t
536
hb_ft_get_glyph_v_advance (hb_font_t *font,
537
         void *font_data,
538
         hb_codepoint_t glyph,
539
         void *user_data HB_UNUSED)
540
0
{
541
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
542
0
  _hb_ft_hb_font_check_changed (font, ft_font);
543
544
0
  hb_lock_t lock (ft_font->lock);
545
0
  FT_Fixed v;
546
0
  float y_mult;
547
#ifdef HAVE_FT_GET_TRANSFORM
548
  if (ft_font->transform)
549
  {
550
    FT_Matrix matrix;
551
    FT_Get_Transform (ft_font->ft_face, &matrix, nullptr);
552
    y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f;
553
    y_mult *= font->y_scale < 0 ? -1 : +1;
554
  }
555
  else
556
#endif
557
0
  {
558
0
    y_mult = font->y_scale < 0 ? -1 : +1;
559
0
  }
560
561
0
  if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v)))
562
0
    return 0;
563
564
  /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates
565
   * have a Y growing upward.  Hence the extra negation. */
566
0
  double advance = floor ((-(double) v + (1<<9)) / (1<<10));
567
568
0
  return hb_clamp_to<hb_position_t> ((double) y_mult * advance);
569
0
}
570
#endif
571
572
#ifndef HB_NO_VERTICAL
573
static hb_bool_t
574
hb_ft_get_glyph_v_origin (hb_font_t *font,
575
        void *font_data,
576
        hb_codepoint_t glyph,
577
        hb_position_t *x,
578
        hb_position_t *y,
579
        void *user_data HB_UNUSED)
580
0
{
581
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
582
0
  _hb_ft_hb_font_check_changed (font, ft_font);
583
584
0
  hb_lock_t lock (ft_font->lock);
585
0
  FT_Face ft_face = ft_font->ft_face;
586
0
  float x_mult, y_mult;
587
#ifdef HAVE_FT_GET_TRANSFORM
588
  if (ft_font->transform)
589
  {
590
    FT_Matrix matrix;
591
    FT_Get_Transform (ft_face, &matrix, nullptr);
592
    x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f;
593
    x_mult *= font->x_scale < 0 ? -1 : +1;
594
    y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f;
595
    y_mult *= font->y_scale < 0 ? -1 : +1;
596
  }
597
  else
598
#endif
599
0
  {
600
0
    x_mult = font->x_scale < 0 ? -1 : +1;
601
0
    y_mult = font->y_scale < 0 ? -1 : +1;
602
0
  }
603
604
0
  if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
605
0
    return false;
606
607
  /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates
608
   * have a Y growing upward.  Hence the extra negation. */
609
0
  *x = hb_clamp_to<hb_position_t> ((double) x_mult *
610
0
           ((double) ft_face->glyph->metrics.horiBearingX -
611
0
            ft_face->glyph->metrics.vertBearingX));
612
0
  *y = hb_clamp_to<hb_position_t> ((double) y_mult *
613
0
           ((double) ft_face->glyph->metrics.horiBearingY +
614
0
            ft_face->glyph->metrics.vertBearingY));
615
616
0
  return true;
617
0
}
618
#endif
619
620
#ifndef HB_NO_OT_SHAPE_FALLBACK
621
static hb_position_t
622
hb_ft_get_glyph_h_kerning (hb_font_t *font,
623
         void *font_data,
624
         hb_codepoint_t left_glyph,
625
         hb_codepoint_t right_glyph,
626
         void *user_data HB_UNUSED)
627
0
{
628
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
629
0
  _hb_ft_hb_font_check_changed (font, ft_font);
630
631
0
  hb_lock_t lock (ft_font->lock);
632
0
  FT_Vector kerningv;
633
634
0
  FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED;
635
0
  if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv))
636
0
    return 0;
637
638
0
  return hb_clamp_to<hb_position_t> ((int64_t) kerningv.x);
639
0
}
640
#endif
641
642
static bool
643
hb_ft_is_colr_glyph (hb_font_t *font,
644
         void *font_data,
645
         hb_codepoint_t gid)
646
0
{
647
0
#ifndef HB_NO_PAINT
648
#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300
649
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
650
  FT_Face ft_face = ft_font->ft_face;
651
652
653
  /* COLRv1 */
654
  FT_OpaquePaint paint = {0};
655
  if (FT_Get_Color_Glyph_Paint (ft_face, gid,
656
              FT_COLOR_NO_ROOT_TRANSFORM,
657
              &paint))
658
    return true;
659
660
  /* COLRv0 */
661
  FT_LayerIterator  iterator;
662
  FT_UInt  layer_glyph_index;
663
  FT_UInt  layer_color_index;
664
  iterator.p  = NULL;
665
  if (FT_Get_Color_Glyph_Layer (ft_face,
666
        gid,
667
        &layer_glyph_index,
668
        &layer_color_index,
669
        &iterator))
670
    return true;
671
#endif
672
0
#endif
673
674
0
  return false;
675
0
}
676
677
static hb_bool_t
678
hb_ft_get_glyph_extents (hb_font_t *font,
679
       void *font_data,
680
       hb_codepoint_t glyph,
681
       hb_glyph_extents_t *extents,
682
       void *user_data HB_UNUSED)
683
0
{
684
  // FreeType doesn't return COLR glyph extents.
685
0
  if (hb_ft_is_colr_glyph (font, font_data, glyph))
686
0
    return false;
687
688
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
689
0
  _hb_ft_hb_font_check_changed (font, ft_font);
690
691
0
  hb_lock_t lock (ft_font->lock);
692
0
  FT_Face ft_face = ft_font->ft_face;
693
0
  float x_mult, y_mult;
694
695
#ifdef HAVE_FT_GET_TRANSFORM
696
  if (ft_font->transform)
697
  {
698
    FT_Matrix matrix;
699
    FT_Get_Transform (ft_face, &matrix, nullptr);
700
    x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f;
701
    x_mult *= font->x_scale < 0 ? -1 : +1;
702
    y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f;
703
    y_mult *= font->y_scale < 0 ? -1 : +1;
704
  }
705
  else
706
#endif
707
0
  {
708
0
    x_mult = font->x_scale < 0 ? -1 : +1;
709
0
    y_mult = font->y_scale < 0 ? -1 : +1;
710
0
  }
711
712
0
  if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
713
0
    return false;
714
715
  /* Copied from hb_font_t::scale_glyph_extents. */
716
717
0
  float x1 = x_mult * ft_face->glyph->metrics.horiBearingX;
718
0
  float y1 = y_mult * ft_face->glyph->metrics.horiBearingY;
719
0
  float x2 = x1 + x_mult *  ft_face->glyph->metrics.width;
720
0
  float y2 = y1 + y_mult * -ft_face->glyph->metrics.height;
721
722
0
  double rx1 = (double) roundf (x1);
723
0
  double ry1 = (double) roundf (y1);
724
0
  double rx2 = (double) roundf (x2);
725
0
  double ry2 = (double) roundf (y2);
726
727
0
  extents->x_bearing = hb_clamp_to<hb_position_t> (rx1);
728
0
  extents->y_bearing = hb_clamp_to<hb_position_t> (ry1);
729
0
  extents->width = hb_clamp_to<hb_position_t> (rx2 - rx1);
730
0
  extents->height = hb_clamp_to<hb_position_t> (ry2 - ry1);
731
732
0
  return true;
733
0
}
734
735
static hb_bool_t
736
hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED,
737
             void *font_data,
738
             hb_codepoint_t glyph,
739
             unsigned int point_index,
740
             hb_position_t *x,
741
             hb_position_t *y,
742
             void *user_data HB_UNUSED)
743
0
{
744
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
745
0
  _hb_ft_hb_font_check_changed (font, ft_font);
746
747
0
  hb_lock_t lock (ft_font->lock);
748
0
  FT_Face ft_face = ft_font->ft_face;
749
750
0
  if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
751
0
      return false;
752
753
0
  if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE))
754
0
      return false;
755
756
0
  if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points))
757
0
      return false;
758
759
0
  *x = ft_face->glyph->outline.points[point_index].x;
760
0
  *y = ft_face->glyph->outline.points[point_index].y;
761
762
0
  return true;
763
0
}
764
765
static hb_bool_t
766
hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED,
767
          void *font_data,
768
          hb_codepoint_t glyph,
769
          char *name, unsigned int size,
770
          void *user_data HB_UNUSED)
771
0
{
772
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
773
0
  hb_lock_t lock (ft_font->lock);
774
0
  FT_Face ft_face = ft_font->ft_face;
775
776
0
  if (!size)
777
0
  {
778
0
    char buf[128];
779
0
    return !FT_Get_Glyph_Name (ft_face, glyph, buf, sizeof (buf)) && *buf;
780
0
  }
781
782
0
  hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size);
783
0
  if (ret && (size && !*name))
784
0
    ret = false;
785
786
0
  return ret;
787
0
}
788
789
static hb_bool_t
790
hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED,
791
         void *font_data,
792
         const char *name, int len, /* -1 means nul-terminated */
793
         hb_codepoint_t *glyph,
794
         void *user_data HB_UNUSED)
795
0
{
796
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
797
0
  hb_lock_t lock (ft_font->lock);
798
0
  FT_Face ft_face = ft_font->ft_face;
799
800
0
  if (len < 0)
801
0
    *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name);
802
0
  else {
803
    /* Make a nul-terminated version. */
804
0
    char buf[128];
805
0
    len = hb_min (len, (int) sizeof (buf) - 1);
806
0
    strncpy (buf, name, len);
807
0
    buf[len] = '\0';
808
0
    *glyph = FT_Get_Name_Index (ft_face, buf);
809
0
  }
810
811
0
  if (*glyph == 0)
812
0
  {
813
    /* Check whether the given name was actually the name of glyph 0. */
814
0
    char buf[128];
815
0
    if (!FT_Get_Glyph_Name(ft_face, 0, buf, sizeof (buf)) &&
816
0
  len < 0 ? !strcmp (buf, name) : !strncmp (buf, name, len))
817
0
      return true;
818
0
  }
819
820
0
  return *glyph != 0;
821
0
}
822
823
static hb_bool_t
824
hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED,
825
        void *font_data,
826
        hb_font_extents_t *metrics,
827
        void *user_data HB_UNUSED)
828
0
{
829
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
830
0
  _hb_ft_hb_font_check_changed (font, ft_font);
831
832
0
  hb_lock_t lock (ft_font->lock);
833
0
  FT_Face ft_face = ft_font->ft_face;
834
0
  float y_mult;
835
#ifdef HAVE_FT_GET_TRANSFORM
836
  if (ft_font->transform)
837
  {
838
    FT_Matrix matrix;
839
    FT_Get_Transform (ft_face, &matrix, nullptr);
840
    y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f;
841
    y_mult *= font->y_scale < 0 ? -1 : +1;
842
  }
843
  else
844
#endif
845
0
  {
846
0
    y_mult = font->y_scale < 0 ? -1 : +1;
847
0
  }
848
849
0
  if (ft_face->units_per_EM != 0)
850
0
  {
851
0
    double ascender = FT_MulFix(ft_face->ascender, ft_face->size->metrics.y_scale);
852
0
    double descender = FT_MulFix(ft_face->descender, ft_face->size->metrics.y_scale);
853
0
    double height = FT_MulFix( ft_face->height, ft_face->size->metrics.y_scale );
854
0
    metrics->ascender = hb_clamp_to<hb_position_t> ((double) y_mult * ascender);
855
0
    metrics->descender = hb_clamp_to<hb_position_t> ((double) y_mult * descender);
856
0
    metrics->line_gap = hb_clamp_to<hb_position_t> ((double) y_mult * (height - (ascender - descender)));
857
0
  }
858
0
  else
859
0
  {
860
    /* Bitmap-only font, eg. color bitmap font. */
861
0
    double ascender = ft_face->size->metrics.ascender;
862
0
    double descender = ft_face->size->metrics.descender;
863
0
    double height = ft_face->size->metrics.height;
864
0
    metrics->ascender = hb_clamp_to<hb_position_t> ((double) y_mult * ascender);
865
0
    metrics->descender = hb_clamp_to<hb_position_t> ((double) y_mult * descender);
866
0
    metrics->line_gap = hb_clamp_to<hb_position_t> ((double) y_mult * (height - (ascender - descender)));
867
0
  }
868
869
0
  return true;
870
0
}
871
872
#ifndef HB_NO_DRAW
873
874
static int
875
_hb_ft_move_to (const FT_Vector *to,
876
    void *arg)
877
0
{
878
0
  hb_draw_session_t *drawing = (hb_draw_session_t *) arg;
879
0
  drawing->move_to (to->x, to->y);
880
0
  return FT_Err_Ok;
881
0
}
882
883
static int
884
_hb_ft_line_to (const FT_Vector *to,
885
    void *arg)
886
0
{
887
0
  hb_draw_session_t *drawing = (hb_draw_session_t *) arg;
888
0
  drawing->line_to (to->x, to->y);
889
0
  return FT_Err_Ok;
890
0
}
891
892
static int
893
_hb_ft_conic_to (const FT_Vector *control,
894
     const FT_Vector *to,
895
     void *arg)
896
0
{
897
0
  hb_draw_session_t *drawing = (hb_draw_session_t *) arg;
898
0
  drawing->quadratic_to (control->x, control->y,
899
0
       to->x, to->y);
900
0
  return FT_Err_Ok;
901
0
}
902
903
static int
904
_hb_ft_cubic_to (const FT_Vector *control1,
905
     const FT_Vector *control2,
906
     const FT_Vector *to,
907
     void *arg)
908
0
{
909
0
  hb_draw_session_t *drawing = (hb_draw_session_t *) arg;
910
0
  drawing->cubic_to (control1->x, control1->y,
911
0
         control2->x, control2->y,
912
0
         to->x, to->y);
913
0
  return FT_Err_Ok;
914
0
}
915
916
static hb_bool_t
917
hb_ft_draw_glyph_or_fail (hb_font_t *font,
918
        void *font_data,
919
        hb_codepoint_t glyph,
920
        hb_draw_funcs_t *draw_funcs, void *draw_data,
921
        void *user_data HB_UNUSED)
922
0
{
923
0
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
924
0
  _hb_ft_hb_font_check_changed (font, ft_font);
925
926
0
  hb_lock_t lock (ft_font->lock);
927
0
  FT_Face ft_face = ft_font->ft_face;
928
929
0
  if (unlikely (FT_Load_Glyph (ft_face, glyph,
930
0
             FT_LOAD_NO_BITMAP | ft_font->load_flags)))
931
0
    return false;
932
933
0
  if (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
934
0
    return false;
935
936
0
  const FT_Outline_Funcs outline_funcs = {
937
0
    _hb_ft_move_to,
938
0
    _hb_ft_line_to,
939
0
    _hb_ft_conic_to,
940
0
    _hb_ft_cubic_to,
941
0
    0, /* shift */
942
0
    0, /* delta */
943
0
  };
944
945
0
  hb_draw_session_t draw_session {draw_funcs, draw_data};
946
947
0
  FT_Outline_Decompose (&ft_face->glyph->outline,
948
0
      &outline_funcs,
949
0
      &draw_session);
950
951
0
  return true;
952
0
}
953
#endif
954
955
#ifndef HB_NO_PAINT
956
#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300
957
958
#include "hb-ft-colr.hh"
959
960
static hb_bool_t
961
hb_ft_paint_glyph_or_fail (hb_font_t *font,
962
         void *font_data,
963
         hb_codepoint_t gid,
964
         hb_paint_funcs_t *paint_funcs, void *paint_data,
965
         unsigned int palette_index,
966
         hb_color_t foreground,
967
         void *user_data)
968
{
969
  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
970
  _hb_ft_hb_font_check_changed (font, ft_font);
971
972
  hb_lock_t lock (ft_font->lock);
973
  FT_Face ft_face = ft_font->ft_face;
974
975
  FT_Long load_flags = ft_font->load_flags | FT_LOAD_COLOR;
976
#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21301
977
  load_flags |= FT_LOAD_NO_SVG;
978
#endif
979
980
  /* We release the lock before calling into glyph callbacks, such that
981
   * eg. draw API can call back into the face.*/
982
983
  if (unlikely (FT_Load_Glyph (ft_face, gid, load_flags)))
984
    return false;
985
986
  if (ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
987
  {
988
    if (hb_ft_paint_glyph_colr (font, font_data, gid,
989
        paint_funcs, paint_data,
990
        palette_index, foreground,
991
        user_data))
992
      return true;
993
994
    // Outline glyph
995
    return false;
996
  }
997
998
  auto *glyph = ft_face->glyph;
999
  if (glyph->format == FT_GLYPH_FORMAT_BITMAP)
1000
  {
1001
    bool ret = false;
1002
    auto &bitmap = glyph->bitmap;
1003
    if (bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
1004
    {
1005
      if (bitmap.pitch != (signed) bitmap.width * 4)
1006
        return ret;
1007
1008
      ft_font->lock.unlock ();
1009
1010
      hb_blob_t *blob = hb_blob_create ((const char *) bitmap.buffer,
1011
          bitmap.pitch * bitmap.rows,
1012
          HB_MEMORY_MODE_DUPLICATE,
1013
          nullptr, nullptr);
1014
1015
      hb_glyph_extents_t extents;
1016
      if (!font->get_glyph_extents (gid, &extents, false))
1017
  goto out;
1018
1019
      if (paint_funcs->image (paint_data,
1020
            blob,
1021
            bitmap.width,
1022
            bitmap.rows,
1023
            HB_PAINT_IMAGE_FORMAT_BGRA,
1024
            0.f,
1025
            &extents))
1026
        ret = true;
1027
1028
    out:
1029
      hb_blob_destroy (blob);
1030
      ft_font->lock.lock ();
1031
    }
1032
1033
    return ret;
1034
  }
1035
  return false;
1036
}
1037
#endif
1038
#endif
1039
1040
1041
static inline void free_static_ft_funcs ();
1042
1043
static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ft_font_funcs_lazy_loader_t>
1044
{
1045
  static hb_font_funcs_t *create ()
1046
0
  {
1047
0
    hb_font_funcs_t *funcs = hb_font_funcs_create ();
1048
1049
0
    hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, nullptr, nullptr);
1050
0
    hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ft_get_nominal_glyphs, nullptr, nullptr);
1051
0
    hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, nullptr, nullptr);
1052
1053
0
    hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, nullptr, nullptr);
1054
0
    hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ft_get_glyph_h_advances, nullptr, nullptr);
1055
1056
0
#ifndef HB_NO_VERTICAL
1057
0
    hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, nullptr, nullptr);
1058
0
    hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, nullptr, nullptr);
1059
0
#endif
1060
1061
0
#ifndef HB_NO_OT_SHAPE_FALLBACK
1062
0
    hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, nullptr, nullptr);
1063
0
#endif
1064
0
    hb_font_funcs_set_glyph_extents_func (funcs, hb_ft_get_glyph_extents, nullptr, nullptr);
1065
0
    hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ft_get_glyph_contour_point, nullptr, nullptr);
1066
0
    hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, nullptr, nullptr);
1067
0
    hb_font_funcs_set_glyph_from_name_func (funcs, hb_ft_get_glyph_from_name, nullptr, nullptr);
1068
1069
0
#ifndef HB_NO_DRAW
1070
0
    hb_font_funcs_set_draw_glyph_or_fail_func (funcs, hb_ft_draw_glyph_or_fail, nullptr, nullptr);
1071
0
#endif
1072
1073
0
#ifndef HB_NO_PAINT
1074
#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300
1075
    hb_font_funcs_set_paint_glyph_or_fail_func (funcs, hb_ft_paint_glyph_or_fail, nullptr, nullptr);
1076
#endif
1077
0
#endif
1078
1079
0
    hb_font_funcs_make_immutable (funcs);
1080
1081
0
    hb_atexit (free_static_ft_funcs);
1082
1083
0
    return funcs;
1084
0
  }
1085
} static_ft_funcs;
1086
1087
static inline
1088
void free_static_ft_funcs ()
1089
0
{
1090
0
  static_ft_funcs.free_instance ();
1091
0
}
1092
1093
static hb_font_funcs_t *
1094
_hb_ft_get_font_funcs ()
1095
0
{
1096
0
  return static_ft_funcs.get_unconst ();
1097
0
}
1098
1099
static void
1100
_hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref)
1101
0
{
1102
0
  bool symbol = ft_face->charmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL;
1103
1104
0
  hb_ft_font_t *ft_font = _hb_ft_font_create (ft_face, symbol, unref);
1105
0
  if (unlikely (!ft_font)) return;
1106
1107
0
  hb_font_set_funcs (font,
1108
0
         _hb_ft_get_font_funcs (),
1109
0
         ft_font,
1110
0
         _hb_ft_font_destroy);
1111
0
}
1112
1113
1114
static hb_blob_t *
1115
_hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
1116
0
{
1117
0
  FT_Face ft_face = (FT_Face) user_data;
1118
0
  FT_Byte *buffer;
1119
0
  FT_ULong  length = 0;
1120
0
  FT_Error error;
1121
1122
  /* In new FreeType, a tag value of 1 loads the SFNT table directory. Reject it. */
1123
0
  if (tag == 1)
1124
0
    return nullptr;
1125
1126
  /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */
1127
1128
0
  error = FT_Load_Sfnt_Table (ft_face, tag, 0, nullptr, &length);
1129
0
  if (error)
1130
0
    return nullptr;
1131
1132
0
  buffer = (FT_Byte *) hb_malloc (length);
1133
0
  if (!buffer)
1134
0
    return nullptr;
1135
0
  auto buffer_guard = hb_make_scope_guard ([&]() { hb_free (buffer); });
1136
1137
0
  error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length);
1138
0
  if (error)
1139
0
    return nullptr;
1140
1141
0
  buffer_guard.release ();
1142
0
  return hb_blob_create ((const char *) buffer, length,
1143
0
       HB_MEMORY_MODE_WRITABLE,
1144
0
       buffer, hb_free);
1145
0
}
1146
1147
static unsigned
1148
_hb_ft_get_table_tags (const hb_face_t *face HB_UNUSED,
1149
           unsigned int start_offset,
1150
           unsigned int *table_count,
1151
           hb_tag_t *table_tags,
1152
           void *user_data)
1153
0
{
1154
0
  FT_Face ft_face = (FT_Face) user_data;
1155
1156
0
  FT_ULong population = 0;
1157
0
  FT_Sfnt_Table_Info (ft_face,
1158
0
          0, // table_index; ignored
1159
0
          nullptr,
1160
0
                      &population);
1161
1162
0
  if (!table_count)
1163
0
    return population;
1164
1165
0
  if (unlikely (start_offset >= population))
1166
0
  {
1167
0
    *table_count = 0;
1168
0
    return population;
1169
0
  }
1170
1171
0
  unsigned end_offset = start_offset + *table_count;
1172
0
  if (unlikely (end_offset < start_offset)) /* Overflow. */
1173
0
  {
1174
0
    *table_count = 0;
1175
0
    return population;
1176
0
  }
1177
0
  end_offset = hb_min (end_offset, (unsigned) population);
1178
1179
0
  *table_count = end_offset - start_offset;
1180
0
  if (table_tags)
1181
0
    for (unsigned i = start_offset; i < end_offset; i++)
1182
0
    {
1183
0
      FT_ULong tag = 0, length;
1184
0
      FT_Sfnt_Table_Info (ft_face, i, &tag, &length);
1185
0
      table_tags[i - start_offset] = tag;
1186
0
    }
1187
1188
0
  return population;
1189
0
}
1190
1191
1192
/**
1193
 * hb_ft_face_create:
1194
 * @ft_face: FT_Face to work upon
1195
 * @destroy: (nullable) (scope async): A callback to call when the face object is not needed anymore
1196
 *
1197
 * Creates an #hb_face_t face object from the specified FT_Face.
1198
 *
1199
 * Note that this is using the FT_Face object just to get at the underlying
1200
 * font data, and fonts created from the returned #hb_face_t will use the native
1201
 * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them.
1202
 *
1203
 * This variant of the function does not provide any life-cycle management.
1204
 *
1205
 * Most client programs should use hb_ft_face_create_referenced()
1206
 * (or, perhaps, hb_ft_face_create_cached()) instead.
1207
 *
1208
 * If you know you have valid reasons not to use hb_ft_face_create_referenced(),
1209
 * then it is the client program's responsibility to destroy @ft_face
1210
 * after the #hb_face_t face object has been destroyed.
1211
 *
1212
 * Return value: (transfer full): the new #hb_face_t face object
1213
 *
1214
 * Since: 0.9.2
1215
 **/
1216
hb_face_t *
1217
hb_ft_face_create (FT_Face           ft_face,
1218
       hb_destroy_func_t destroy)
1219
0
{
1220
0
  hb_face_t *face;
1221
1222
0
  if (!ft_face->stream->read) {
1223
0
    hb_blob_t *blob;
1224
1225
0
    blob = hb_blob_create ((const char *) ft_face->stream->base,
1226
0
         (unsigned int) ft_face->stream->size,
1227
0
         HB_MEMORY_MODE_READONLY,
1228
0
         ft_face, destroy);
1229
0
    face = hb_face_create (blob, ft_face->face_index);
1230
0
    hb_blob_destroy (blob);
1231
0
  } else {
1232
0
    face = hb_face_create_for_tables (_hb_ft_reference_table, ft_face, destroy);
1233
0
    hb_face_set_get_table_tags_func (face, _hb_ft_get_table_tags, ft_face, nullptr);
1234
0
  }
1235
1236
0
  hb_face_set_index (face, ft_face->face_index);
1237
0
  hb_face_set_upem (face, ft_face->units_per_EM);
1238
1239
0
  return face;
1240
0
}
1241
1242
/**
1243
 * hb_ft_face_create_referenced:
1244
 * @ft_face: FT_Face to work upon
1245
 *
1246
 * Creates an #hb_face_t face object from the specified FT_Face.
1247
 *
1248
 * Note that this is using the FT_Face object just to get at the underlying
1249
 * font data, and fonts created from the returned #hb_face_t will use the native
1250
 * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them.
1251
 *
1252
 * This is the preferred variant of the hb_ft_face_create*
1253
 * function family, because it calls FT_Reference_Face() on @ft_face,
1254
 * ensuring that @ft_face remains alive as long as the resulting
1255
 * #hb_face_t face object remains alive. Also calls FT_Done_Face()
1256
 * when the #hb_face_t face object is destroyed.
1257
 *
1258
 * Use this version unless you know you have good reasons not to.
1259
 *
1260
 * Return value: (transfer full): the new #hb_face_t face object
1261
 *
1262
 * Since: 0.9.38
1263
 **/
1264
hb_face_t *
1265
hb_ft_face_create_referenced (FT_Face ft_face)
1266
0
{
1267
0
  FT_Reference_Face (ft_face);
1268
0
  return hb_ft_face_create (ft_face, _hb_ft_face_destroy);
1269
0
}
1270
1271
static void
1272
hb_ft_face_finalize (void *arg)
1273
0
{
1274
0
  FT_Face ft_face = (FT_Face) arg;
1275
0
  hb_face_destroy ((hb_face_t *) ft_face->generic.data);
1276
0
}
1277
1278
/**
1279
 * hb_ft_face_create_cached:
1280
 * @ft_face: FT_Face to work upon
1281
 *
1282
 * Creates an #hb_face_t face object from the specified FT_Face.
1283
 *
1284
 * Note that this is using the FT_Face object just to get at the underlying
1285
 * font data, and fonts created from the returned #hb_face_t will use the native
1286
 * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them.
1287
 *
1288
 * This variant of the function caches the newly created #hb_face_t
1289
 * face object, using the @generic pointer of @ft_face. Subsequent function
1290
 * calls that are passed the same @ft_face parameter will have the same
1291
 * #hb_face_t returned to them, and that #hb_face_t will be correctly
1292
 * reference counted.
1293
 *
1294
 * However, client programs are still responsible for destroying
1295
 * @ft_face after the last #hb_face_t face object has been destroyed.
1296
 *
1297
 * Return value: (transfer full): the new #hb_face_t face object
1298
 *
1299
 * Since: 0.9.2
1300
 **/
1301
hb_face_t *
1302
hb_ft_face_create_cached (FT_Face ft_face)
1303
0
{
1304
0
  if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != hb_ft_face_finalize))
1305
0
  {
1306
0
    if (ft_face->generic.finalizer)
1307
0
      ft_face->generic.finalizer (ft_face);
1308
1309
0
    ft_face->generic.data = hb_ft_face_create (ft_face, nullptr);
1310
0
    ft_face->generic.finalizer = hb_ft_face_finalize;
1311
0
  }
1312
1313
0
  return hb_face_reference ((hb_face_t *) ft_face->generic.data);
1314
0
}
1315
1316
/**
1317
 * hb_ft_font_create:
1318
 * @ft_face: FT_Face to work upon
1319
 * @destroy: (nullable) (scope async): A callback to call when the font object is not needed anymore
1320
 *
1321
 * Creates an #hb_font_t font object from the specified FT_Face.
1322
 *
1323
 * <note>Note: You must set the face size on @ft_face before calling
1324
 * hb_ft_font_create() on it. HarfBuzz assumes size is always set and will
1325
 * access `size` member of FT_Face unconditionally.</note>
1326
 *
1327
 * This variant of the function does not provide any life-cycle management.
1328
 *
1329
 * Most client programs should use hb_ft_font_create_referenced()
1330
 * instead.
1331
 *
1332
 * If you know you have valid reasons not to use hb_ft_font_create_referenced(),
1333
 * then it is the client program's responsibility to destroy @ft_face
1334
 * only after the #hb_font_t font object has been destroyed.
1335
 *
1336
 * HarfBuzz will use the @destroy callback on the #hb_font_t font object
1337
 * if it is supplied when you use this function. However, even if @destroy
1338
 * is provided, it is the client program's responsibility to destroy @ft_face,
1339
 * and it is the client program's responsibility to ensure that @ft_face is
1340
 * destroyed only after the #hb_font_t font object has been destroyed.
1341
 *
1342
 * Return value: (transfer full): the new #hb_font_t font object
1343
 *
1344
 * Since: 0.9.2
1345
 **/
1346
hb_font_t *
1347
hb_ft_font_create (FT_Face           ft_face,
1348
       hb_destroy_func_t destroy)
1349
0
{
1350
0
  hb_font_t *font;
1351
0
  hb_face_t *face;
1352
1353
0
  face = hb_ft_face_create (ft_face, destroy);
1354
0
  font = hb_font_create (face);
1355
0
  hb_face_destroy (face);
1356
0
  _hb_ft_font_set_funcs (font, ft_face, false);
1357
0
  hb_ft_font_changed (font);
1358
0
  return font;
1359
0
}
1360
1361
/**
1362
 * hb_ft_font_changed:
1363
 * @font: #hb_font_t to work upon
1364
 *
1365
 * Refreshes the state of @font when the underlying FT_Face has changed.
1366
 * This function should be called after changing the size or
1367
 * variation-axis settings on the FT_Face.
1368
 *
1369
 * Since: 1.0.5
1370
 **/
1371
void
1372
hb_ft_font_changed (hb_font_t *font)
1373
0
{
1374
0
  if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
1375
0
    return;
1376
1377
0
  hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
1378
1379
0
  FT_Face ft_face = ft_font->ft_face;
1380
1381
0
  hb_font_set_scale (font,
1382
0
         (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16),
1383
0
         (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16));
1384
#if 0 /* hb-ft works in no-hinting model */
1385
  hb_font_set_ppem (font,
1386
        ft_face->size->metrics.x_ppem,
1387
        ft_face->size->metrics.y_ppem);
1388
#endif
1389
1390
0
#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR)
1391
0
  FT_MM_Var *mm_var = nullptr;
1392
0
  if (!FT_Get_MM_Var (ft_face, &mm_var))
1393
0
  {
1394
0
    FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (mm_var->num_axis, sizeof (FT_Fixed));
1395
0
    int *coords = (int *) hb_calloc (mm_var->num_axis, sizeof (int));
1396
0
    if (coords && ft_coords)
1397
0
    {
1398
0
      if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, ft_coords))
1399
0
      {
1400
0
  bool nonzero = false;
1401
1402
0
  for (unsigned int i = 0; i < mm_var->num_axis; ++i)
1403
0
   {
1404
0
    coords[i] = (ft_coords[i] + 2) >> 2;
1405
0
    nonzero = nonzero || coords[i];
1406
0
   }
1407
1408
0
  if (nonzero)
1409
0
    hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis);
1410
0
  else
1411
0
    hb_font_set_var_coords_normalized (font, nullptr, 0);
1412
0
      }
1413
0
    }
1414
0
    hb_free (coords);
1415
0
    hb_free (ft_coords);
1416
0
#ifdef HAVE_FT_DONE_MM_VAR
1417
0
    FT_Done_MM_Var (ft_face->glyph->library, mm_var);
1418
#else
1419
    hb_free (mm_var);
1420
#endif
1421
0
  }
1422
0
#endif
1423
1424
0
  ft_font->advance_cache.clear ();
1425
0
  ft_font->cached_serial = font->serial;
1426
0
}
1427
1428
/**
1429
 * hb_ft_hb_font_changed:
1430
 * @font: #hb_font_t to work upon
1431
 *
1432
 * Refreshes the state of the underlying FT_Face of @font when the hb_font_t
1433
 * @font has changed.
1434
 * This function should be called after changing the size or
1435
 * variation-axis settings on the @font.
1436
 * This call is fast if nothing has changed on @font.
1437
 *
1438
 * Note that as of version 11.0.0, calling this function is not necessary,
1439
 * as HarfBuzz will automatically detect changes to the font and update
1440
 * the underlying FT_Face as needed.
1441
 *
1442
 * Return value: true if changed, false otherwise
1443
 *
1444
 * Since: 4.4.0
1445
 **/
1446
hb_bool_t
1447
hb_ft_hb_font_changed (hb_font_t *font)
1448
0
{
1449
0
  if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
1450
0
    return false;
1451
1452
0
  hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
1453
1454
0
  return _hb_ft_hb_font_check_changed (font, ft_font);
1455
0
}
1456
1457
/**
1458
 * hb_ft_font_create_referenced:
1459
 * @ft_face: FT_Face to work upon
1460
 *
1461
 * Creates an #hb_font_t font object from the specified FT_Face.
1462
 *
1463
 * <note>Note: You must set the face size on @ft_face before calling
1464
 * hb_ft_font_create_referenced() on it. HarfBuzz assumes size is always set
1465
 * and will access `size` member of FT_Face unconditionally.</note>
1466
 *
1467
 * This is the preferred variant of the hb_ft_font_create*
1468
 * function family, because it calls FT_Reference_Face() on @ft_face,
1469
 * ensuring that @ft_face remains alive as long as the resulting
1470
 * #hb_font_t font object remains alive.
1471
 *
1472
 * Use this version unless you know you have good reasons not to.
1473
 *
1474
 * Return value: (transfer full): the new #hb_font_t font object
1475
 *
1476
 * Since: 0.9.38
1477
 **/
1478
hb_font_t *
1479
hb_ft_font_create_referenced (FT_Face ft_face)
1480
0
{
1481
0
  FT_Reference_Face (ft_face);
1482
0
  return hb_ft_font_create (ft_face, _hb_ft_face_destroy);
1483
0
}
1484
1485
1486
static void * _hb_ft_alloc (FT_Memory memory, long size)
1487
0
{ return hb_malloc (size); }
1488
1489
static void _hb_ft_free (FT_Memory memory, void *block)
1490
0
{ hb_free (block); }
1491
1492
static void * _hb_ft_realloc (FT_Memory memory, long cur_size, long new_size, void *block)
1493
0
{ return hb_realloc (block, new_size); }
1494
1495
static FT_MemoryRec_ m =
1496
{
1497
  nullptr,
1498
  _hb_ft_alloc,
1499
  _hb_ft_free,
1500
  _hb_ft_realloc
1501
};
1502
1503
static inline void free_static_ft_library ();
1504
1505
static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<FT_Library>,
1506
                   hb_ft_library_lazy_loader_t>
1507
{
1508
  static FT_Library create ()
1509
0
  {
1510
0
    FT_Library l;
1511
0
    if (FT_New_Library (&m, &l))
1512
0
      return nullptr;
1513
1514
0
    FT_Add_Default_Modules (l);
1515
0
    FT_Set_Default_Properties (l);
1516
1517
0
    hb_atexit (free_static_ft_library);
1518
1519
0
    return l;
1520
0
  }
1521
  static void destroy (FT_Library l)
1522
0
  {
1523
0
    FT_Done_Library (l);
1524
0
  }
1525
  static FT_Library get_null ()
1526
0
  {
1527
0
    return nullptr;
1528
0
  }
1529
} static_ft_library;
1530
1531
static inline
1532
void free_static_ft_library ()
1533
0
{
1534
0
  static_ft_library.free_instance ();
1535
0
}
1536
1537
static FT_Library
1538
reference_ft_library ()
1539
0
{
1540
0
  FT_Library l = static_ft_library.get_unconst ();
1541
0
  if (unlikely (FT_Reference_Library (l)))
1542
0
  {
1543
0
    DEBUG_MSG (FT, l, "FT_Reference_Library() failed");
1544
0
    return nullptr;
1545
0
  }
1546
0
  return l;
1547
0
}
1548
1549
static hb_user_data_key_t ft_library_key = {0};
1550
1551
static void
1552
finalize_ft_library (void *arg)
1553
0
{
1554
0
  FT_Face ft_face = (FT_Face) arg;
1555
0
  FT_Done_Library ((FT_Library) ft_face->generic.data);
1556
0
}
1557
1558
static void
1559
destroy_ft_library (void *arg)
1560
0
{
1561
0
  FT_Done_Library ((FT_Library) arg);
1562
0
}
1563
1564
/**
1565
 * hb_ft_face_create_from_file_or_fail:
1566
 * @file_name: A font filename
1567
 * @index: The index of the face within the file
1568
 *
1569
 * Creates an #hb_face_t face object from the specified
1570
 * font file and face index.
1571
 *
1572
 * This is similar in functionality to hb_face_create_from_file_or_fail(),
1573
 * but uses the FreeType library for loading the font file. This can
1574
 * be useful, for example, to load WOFF and WOFF2 font data.
1575
 *
1576
 * Return value: (transfer full): The new face object, or `NULL` if
1577
 * no face is found at the specified index or the file cannot be read.
1578
 *
1579
 * Since: 10.1.0
1580
 */
1581
hb_face_t *
1582
hb_ft_face_create_from_file_or_fail (const char   *file_name,
1583
             unsigned int  index)
1584
0
{
1585
0
  FT_Library ft_library = reference_ft_library ();
1586
0
  if (unlikely (!ft_library))
1587
0
  {
1588
0
    DEBUG_MSG (FT, ft_library, "reference_ft_library failed");
1589
0
    return nullptr;
1590
0
  }
1591
1592
0
  FT_Face ft_face;
1593
0
  if (unlikely (FT_New_Face (ft_library,
1594
0
           file_name,
1595
0
           index,
1596
0
           &ft_face)))
1597
0
    return nullptr;
1598
1599
0
  hb_face_t *face = hb_ft_face_create_referenced (ft_face);
1600
0
  FT_Done_Face (ft_face);
1601
1602
0
  ft_face->generic.data = ft_library;
1603
0
  ft_face->generic.finalizer = finalize_ft_library;
1604
1605
0
  if (hb_face_is_immutable (face))
1606
0
    return nullptr;
1607
1608
0
  return face;
1609
0
}
1610
1611
static hb_user_data_key_t ft_blob_key = {0};
1612
1613
static void
1614
_destroy_blob (void *p)
1615
0
{
1616
0
  hb_blob_destroy ((hb_blob_t *) p);
1617
0
}
1618
1619
/**
1620
 * hb_ft_face_create_from_blob_or_fail:
1621
 * @blob: A blob
1622
 * @index: The index of the face within the blob
1623
 *
1624
 * Creates an #hb_face_t face object from the specified
1625
 * font blob and face index.
1626
 *
1627
 * This is similar in functionality to hb_face_create_or_fail(),
1628
 * but uses the FreeType library for loading the font blob. This can
1629
 * be useful, for example, to load WOFF and WOFF2 font data.
1630
 *
1631
 * Return value: (transfer full): The new face object, or `NULL` if
1632
 * loading fails (eg. blob does not contain valid font data).
1633
 *
1634
 * Since: 11.0.0
1635
 */
1636
hb_face_t *
1637
hb_ft_face_create_from_blob_or_fail (hb_blob_t    *blob,
1638
             unsigned int  index)
1639
0
{
1640
0
  FT_Library ft_library = reference_ft_library ();
1641
0
  if (unlikely (!ft_library))
1642
0
  {
1643
0
    DEBUG_MSG (FT, ft_library, "reference_ft_library failed");
1644
0
    return nullptr;
1645
0
  }
1646
1647
0
  hb_blob_make_immutable (blob);
1648
0
  unsigned blob_size;
1649
0
  const char *blob_data = hb_blob_get_data (blob, &blob_size);
1650
1651
0
  FT_Face ft_face;
1652
0
  if (unlikely (FT_New_Memory_Face (ft_library,
1653
0
            (const FT_Byte *) blob_data,
1654
0
            blob_size,
1655
0
            index,
1656
0
            &ft_face)))
1657
0
    return nullptr;
1658
1659
0
  hb_face_t *face = hb_ft_face_create_referenced (ft_face);
1660
0
  FT_Done_Face (ft_face);
1661
1662
0
  ft_face->generic.data = ft_library;
1663
0
  ft_face->generic.finalizer = finalize_ft_library;
1664
1665
0
  if (hb_face_is_immutable (face))
1666
0
    return nullptr;
1667
1668
  // Hook the blob to the hb_face_t, since FT_Face still needs it.
1669
0
  hb_blob_reference (blob);
1670
0
  if (!hb_face_set_user_data (face, &ft_blob_key, blob, _destroy_blob, true))
1671
0
  {
1672
0
    hb_blob_destroy (blob);
1673
0
    hb_face_destroy (face);
1674
0
    return nullptr;
1675
0
  }
1676
1677
0
  return face;
1678
0
}
1679
1680
static void
1681
_release_blob (void *arg)
1682
0
{
1683
0
  FT_Face ft_face = (FT_Face) arg;
1684
0
  hb_blob_destroy ((hb_blob_t *) ft_face->generic.data);
1685
0
}
1686
1687
/**
1688
 * hb_ft_font_set_funcs:
1689
 * @font: #hb_font_t to work upon
1690
 *
1691
 * Configures the font-functions structure of the specified
1692
 * #hb_font_t font object to use FreeType font functions.
1693
 *
1694
 * In particular, you can use this function to configure an
1695
 * existing #hb_face_t face object for use with FreeType font
1696
 * functions even if that #hb_face_t face object was initially
1697
 * created with hb_face_create(), and therefore was not
1698
 * initially configured to use FreeType font functions.
1699
 *
1700
 * An #hb_font_t object created with hb_ft_font_create()
1701
 * is preconfigured for FreeType font functions and does not
1702
 * require this function to be used.
1703
 *
1704
 * Note that if you modify the underlying #hb_font_t after
1705
 * calling this function, you need to call hb_ft_hb_font_changed()
1706
 * to update the underlying FT_Face.
1707
 *
1708
 * <note>Note: Internally, this function creates an FT_Face.
1709
* </note>
1710
 *
1711
 * Since: 1.0.5
1712
 **/
1713
void
1714
hb_ft_font_set_funcs (hb_font_t *font)
1715
0
{
1716
0
  int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
1717
0
  if (font->destroy == (hb_destroy_func_t) _hb_ft_font_destroy && font->user_data)
1718
0
  {
1719
0
    const hb_ft_font_t *existing_ft_font = (const hb_ft_font_t *) font->user_data;
1720
0
    load_flags = existing_ft_font->load_flags;
1721
0
  }
1722
1723
  // In case of failure...
1724
0
  hb_font_set_funcs (font,
1725
0
         hb_font_funcs_get_empty (),
1726
0
         nullptr, nullptr);
1727
1728
0
  hb_blob_t *blob = hb_face_reference_blob (font->face);
1729
0
  unsigned int blob_length;
1730
0
  const char *blob_data = hb_blob_get_data (blob, &blob_length);
1731
0
  if (unlikely (!blob_length))
1732
0
    DEBUG_MSG (FT, font, "Font face has empty blob");
1733
1734
0
  FT_Library ft_library = reference_ft_library ();
1735
0
  if (unlikely (!ft_library))
1736
0
  {
1737
0
    hb_blob_destroy (blob);
1738
0
    DEBUG_MSG (FT, font, "reference_ft_library failed");
1739
0
    return;
1740
0
  }
1741
1742
0
  FT_Face ft_face = nullptr;
1743
0
  if (unlikely (FT_New_Memory_Face (ft_library,
1744
0
            (const FT_Byte *) blob_data,
1745
0
            blob_length,
1746
0
            hb_face_get_index (font->face),
1747
0
            &ft_face)))
1748
0
  {
1749
0
    hb_blob_destroy (blob);
1750
0
    DEBUG_MSG (FT, font, "FT_New_Memory_Face() failed");
1751
0
    return;
1752
0
  }
1753
1754
0
  if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL))
1755
0
    FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE);
1756
1757
  // Hook the blob to the FT_Face
1758
0
  ft_face->generic.data = blob;
1759
0
  ft_face->generic.finalizer = _release_blob;
1760
1761
  // And the FT_Library to the blob
1762
0
  if (unlikely (!hb_blob_set_user_data (blob, &ft_library_key, ft_library, destroy_ft_library, true)))
1763
0
  {
1764
0
    DEBUG_MSG (FT, font, "hb_blob_set_user_data() failed");
1765
0
    FT_Done_Face (ft_face);
1766
0
    return;
1767
0
  }
1768
1769
0
  _hb_ft_font_set_funcs (font, ft_face, true);
1770
0
  hb_ft_font_set_load_flags (font, load_flags);
1771
1772
0
  _hb_ft_hb_font_changed (font, ft_face);
1773
0
}
1774
1775
#endif