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-private.hh"
  31 #include "hb-debug.hh"
  32 
  33 #include "hb-ft.h"
  34 
  35 #include "hb-font-private.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 static hb_font_funcs_t *static_ft_funcs = nullptr;
 421 
 422 #ifdef HB_USE_ATEXIT
 423 static
 424 void free_static_ft_funcs (void)
 425 {
 426 retry:
 427   hb_font_funcs_t *ft_funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ft_funcs);
 428   if (!hb_atomic_ptr_cmpexch (&static_ft_funcs, ft_funcs, nullptr))
 429     goto retry;
 430 
 431   hb_font_funcs_destroy (ft_funcs);
 432 }
 433 #endif
 434 
 435 static void
 436 _hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref)
 437 {
 438 retry:
 439   hb_font_funcs_t *funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ft_funcs);
 440 
 441   if (unlikely (!funcs))
 442   {
 443     funcs = hb_font_funcs_create ();
 444 
 445     hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, nullptr, nullptr);
 446     //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, nullptr, nullptr);
 447     hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, nullptr, nullptr);
 448     hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, nullptr, nullptr);
 449     hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ft_get_glyph_h_advance, nullptr, nullptr);
 450     hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, nullptr, nullptr);
 451     //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, nullptr, nullptr);
 452     hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, nullptr, nullptr);
 453     hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, nullptr, nullptr);
 454     //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ft_get_glyph_v_kerning, nullptr, nullptr);
 455     hb_font_funcs_set_glyph_extents_func (funcs, hb_ft_get_glyph_extents, nullptr, nullptr);
 456     hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ft_get_glyph_contour_point, nullptr, nullptr);
 457     hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, nullptr, nullptr);
 458     hb_font_funcs_set_glyph_from_name_func (funcs, hb_ft_get_glyph_from_name, nullptr, nullptr);
 459 
 460     hb_font_funcs_make_immutable (funcs);
 461 
 462     if (!hb_atomic_ptr_cmpexch (&static_ft_funcs, nullptr, funcs)) {
 463       hb_font_funcs_destroy (funcs);
 464       goto retry;
 465     }
 466 
 467 #ifdef HB_USE_ATEXIT
 468     atexit (free_static_ft_funcs); /* First person registers atexit() callback. */
 469 #endif
 470   };
 471 
 472   bool symbol = ft_face->charmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL;
 473 
 474   hb_font_set_funcs (font,
 475                      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     return nullptr;
 502 
 503   return hb_blob_create ((const char *) buffer, length,
 504                          HB_MEMORY_MODE_WRITABLE,
 505                          buffer, free);
 506 }
 507 
 508 /**
 509  * hb_ft_face_create:
 510  * @ft_face: (destroy destroy) (scope notified):
 511  * @destroy:
 512  *
 513  *
 514  *
 515  * Return value: (transfer full):
 516  * Since: 0.9.2
 517  **/
 518 hb_face_t *
 519 hb_ft_face_create (FT_Face           ft_face,
 520                    hb_destroy_func_t destroy)
 521 {
 522   hb_face_t *face;
 523 
 524   if (!ft_face->stream->read) {
 525     hb_blob_t *blob;
 526 
 527     blob = hb_blob_create ((const char *) ft_face->stream->base,
 528                            (unsigned int) ft_face->stream->size,
 529                            HB_MEMORY_MODE_READONLY,
 530                            ft_face, destroy);
 531     face = hb_face_create (blob, ft_face->face_index);
 532     hb_blob_destroy (blob);
 533   } else {
 534     face = hb_face_create_for_tables (reference_table, ft_face, destroy);
 535   }
 536 
 537   hb_face_set_index (face, ft_face->face_index);
 538   hb_face_set_upem (face, ft_face->units_per_EM);
 539 
 540   return face;
 541 }
 542 
 543 /**
 544  * hb_ft_face_create_referenced:
 545  * @ft_face:
 546  *
 547  *
 548  *
 549  * Return value: (transfer full):
 550  * Since: 0.9.38
 551  **/
 552 hb_face_t *
 553 hb_ft_face_create_referenced (FT_Face ft_face)
 554 {
 555   FT_Reference_Face (ft_face);
 556   return hb_ft_face_create (ft_face, _hb_ft_face_destroy);
 557 }
 558 
 559 static void
 560 hb_ft_face_finalize (FT_Face ft_face)
 561 {
 562   hb_face_destroy ((hb_face_t *) ft_face->generic.data);
 563 }
 564 
 565 /**
 566  * hb_ft_face_create_cached:
 567  * @ft_face:
 568  *
 569  *
 570  *
 571  * Return value: (transfer full):
 572  * Since: 0.9.2
 573  **/
 574 hb_face_t *
 575 hb_ft_face_create_cached (FT_Face ft_face)
 576 {
 577   if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize))
 578   {
 579     if (ft_face->generic.finalizer)
 580       ft_face->generic.finalizer (ft_face);
 581 
 582     ft_face->generic.data = hb_ft_face_create (ft_face, nullptr);
 583     ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize;
 584   }
 585 
 586   return hb_face_reference ((hb_face_t *) ft_face->generic.data);
 587 }
 588 
 589 
 590 /**
 591  * hb_ft_font_create:
 592  * @ft_face: (destroy destroy) (scope notified):
 593  * @destroy:
 594  *
 595  *
 596  *
 597  * Return value: (transfer full):
 598  * Since: 0.9.2
 599  **/
 600 hb_font_t *
 601 hb_ft_font_create (FT_Face           ft_face,
 602                    hb_destroy_func_t destroy)
 603 {
 604   hb_font_t *font;
 605   hb_face_t *face;
 606 
 607   face = hb_ft_face_create (ft_face, destroy);
 608   font = hb_font_create (face);
 609   hb_face_destroy (face);
 610   _hb_ft_font_set_funcs (font, ft_face, false);
 611   hb_ft_font_changed (font);
 612   return font;
 613 }
 614 
 615 void
 616 hb_ft_font_changed (hb_font_t *font)
 617 {
 618   if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
 619     return;
 620 
 621   hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
 622   FT_Face ft_face = ft_font->ft_face;
 623 
 624   hb_font_set_scale (font,
 625                      (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16),
 626                      (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16));
 627 #if 0 /* hb-ft works in no-hinting model */
 628   hb_font_set_ppem (font,
 629                     ft_face->size->metrics.x_ppem,
 630                     ft_face->size->metrics.y_ppem);
 631 #endif
 632 
 633 #ifdef HAVE_FT_GET_VAR_BLEND_COORDINATES
 634   FT_MM_Var *mm_var = nullptr;
 635   if (!FT_Get_MM_Var (ft_face, &mm_var))
 636   {
 637     FT_Fixed *ft_coords = (FT_Fixed *) calloc (mm_var->num_axis, sizeof (FT_Fixed));
 638     int *coords = (int *) calloc (mm_var->num_axis, sizeof (int));
 639     if (coords && ft_coords)
 640     {
 641       if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, ft_coords))
 642       {
 643         bool nonzero = false;
 644 
 645         for (unsigned int i = 0; i < mm_var->num_axis; ++i)
 646          {
 647           coords[i] = ft_coords[i] >>= 2;
 648           nonzero = nonzero || coords[i];
 649          }
 650 
 651         if (nonzero)
 652           hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis);
 653         else
 654           hb_font_set_var_coords_normalized (font, nullptr, 0);
 655       }
 656     }
 657     free (coords);
 658     free (ft_coords);
 659 #ifdef HAVE_FT_DONE_MM_VAR
 660     FT_Done_MM_Var (ft_face->glyph->library, mm_var);
 661 #else
 662     free (mm_var);
 663 #endif
 664   }
 665 #endif
 666 }
 667 
 668 /**
 669  * hb_ft_font_create_referenced:
 670  * @ft_face:
 671  *
 672  *
 673  *
 674  * Return value: (transfer full):
 675  * Since: 0.9.38
 676  **/
 677 hb_font_t *
 678 hb_ft_font_create_referenced (FT_Face ft_face)
 679 {
 680   FT_Reference_Face (ft_face);
 681   return hb_ft_font_create (ft_face, _hb_ft_face_destroy);
 682 }
 683 
 684 
 685 /* Thread-safe, lock-free, FT_Library */
 686 
 687 static FT_Library ft_library;
 688 
 689 #ifdef HB_USE_ATEXIT
 690 static
 691 void free_ft_library (void)
 692 {
 693 retry:
 694   FT_Library library = (FT_Library) hb_atomic_ptr_get (&ft_library);
 695   if (!hb_atomic_ptr_cmpexch (&ft_library, library, nullptr))
 696     goto retry;
 697 
 698   FT_Done_FreeType (library);
 699 }
 700 #endif
 701 
 702 static FT_Library
 703 get_ft_library (void)
 704 {
 705 retry:
 706   FT_Library library = (FT_Library) hb_atomic_ptr_get (&ft_library);
 707 
 708   if (unlikely (!library))
 709   {
 710     /* Not found; allocate one. */
 711     if (FT_Init_FreeType (&library))
 712       return nullptr;
 713 
 714     if (!hb_atomic_ptr_cmpexch (&ft_library, nullptr, library)) {
 715       FT_Done_FreeType (library);
 716       goto retry;
 717     }
 718 
 719 #ifdef HB_USE_ATEXIT
 720     atexit (free_ft_library); /* First person registers atexit() callback. */
 721 #endif
 722   }
 723 
 724   return library;
 725 }
 726 
 727 static void
 728 _release_blob (FT_Face ft_face)
 729 {
 730   hb_blob_destroy ((hb_blob_t *) ft_face->generic.data);
 731 }
 732 
 733 void
 734 hb_ft_font_set_funcs (hb_font_t *font)
 735 {
 736   hb_blob_t *blob = hb_face_reference_blob (font->face);
 737   unsigned int blob_length;
 738   const char *blob_data = hb_blob_get_data (blob, &blob_length);
 739   if (unlikely (!blob_length))
 740     DEBUG_MSG (FT, font, "Font face has empty blob");
 741 
 742   FT_Face ft_face = nullptr;
 743   FT_Error err = FT_New_Memory_Face (get_ft_library (),
 744                                      (const FT_Byte *) blob_data,
 745                                      blob_length,
 746                                      hb_face_get_index (font->face),
 747                                      &ft_face);
 748 
 749   if (unlikely (err)) {
 750     hb_blob_destroy (blob);
 751     DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed");
 752     return;
 753   }
 754 
 755   if (FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE))
 756     FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL);
 757 
 758   FT_Set_Char_Size (ft_face,
 759                     abs (font->x_scale), abs (font->y_scale),
 760                     0, 0);
 761 #if 0
 762                     font->x_ppem * 72 * 64 / font->x_scale,
 763                     font->y_ppem * 72 * 64 / font->y_scale);
 764 #endif
 765   if (font->x_scale < 0 || font->y_scale < 0)
 766   {
 767     FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0,
 768                           0, font->y_scale < 0 ? -1 : +1};
 769     FT_Set_Transform (ft_face, &matrix, nullptr);
 770   }
 771 
 772 #ifdef HAVE_FT_SET_VAR_BLEND_COORDINATES
 773   unsigned int num_coords;
 774   const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
 775   if (num_coords)
 776   {
 777     FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed));
 778     if (ft_coords)
 779     {
 780       for (unsigned int i = 0; i < num_coords; i++)
 781         ft_coords[i] = coords[i] << 2;
 782       FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords);
 783       free (ft_coords);
 784     }
 785   }
 786 #endif
 787 
 788   ft_face->generic.data = blob;
 789   ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob;
 790 
 791   _hb_ft_font_set_funcs (font, ft_face, true);
 792   hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING);
 793 }