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