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