1 /****************************************************************************
2  *
3  * cffgload.c
4  *
5  *   OpenType Glyph Loader (body).
6  *
7  * Copyright (C) 1996-2019 by
8  * David Turner, Robert Wilhelm, and Werner Lemberg.
9  *
10  * This file is part of the FreeType project, and may only be used,
11  * modified, and distributed under the terms of the FreeType project
12  * license, LICENSE.TXT.  By continuing to use, modify, or distribute
13  * this file you indicate that you have read the license and
14  * understand and accept it fully.
15  *
16  */
17 
18 
19 #include <ft2build.h>
20 #include FT_INTERNAL_DEBUG_H
21 #include FT_INTERNAL_STREAM_H
22 #include FT_INTERNAL_SFNT_H
23 #include FT_INTERNAL_CALC_H
24 #include FT_INTERNAL_POSTSCRIPT_AUX_H
25 #include FT_OUTLINE_H
26 #include FT_DRIVER_H
27 
28 #include "cffload.h"
29 #include "cffgload.h"
30 
31 #include "cfferrs.h"
32 
33 
34   /**************************************************************************
35    *
36    * The macro FT_COMPONENT is used in trace mode.  It is an implicit
37    * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
38    * messages during execution.
39    */
40 #undef  FT_COMPONENT
41 #define FT_COMPONENT  cffgload
42 
43 
44   FT_LOCAL_DEF( FT_Error )
cff_get_glyph_data(TT_Face face,FT_UInt glyph_index,FT_Byte ** pointer,FT_ULong * length)45   cff_get_glyph_data( TT_Face    face,
46                       FT_UInt    glyph_index,
47                       FT_Byte**  pointer,
48                       FT_ULong*  length )
49   {
50 #ifdef FT_CONFIG_OPTION_INCREMENTAL
51     /* For incremental fonts get the character data using the */
52     /* callback function.                                     */
53     if ( face->root.internal->incremental_interface )
54     {
55       FT_Data   data;
56       FT_Error  error =
57                   face->root.internal->incremental_interface->funcs->get_glyph_data(
58                     face->root.internal->incremental_interface->object,
59                     glyph_index, &data );
60 
61 
62       *pointer = (FT_Byte*)data.pointer;
63       *length  = (FT_ULong)data.length;
64 
65       return error;
66     }
67     else
68 #endif /* FT_CONFIG_OPTION_INCREMENTAL */
69 
70     {
71       CFF_Font  cff = (CFF_Font)(face->extra.data);
72 
73 
74       return cff_index_access_element( &cff->charstrings_index, glyph_index,
75                                        pointer, length );
76     }
77   }
78 
79 
80   FT_LOCAL_DEF( void )
cff_free_glyph_data(TT_Face face,FT_Byte ** pointer,FT_ULong length)81   cff_free_glyph_data( TT_Face    face,
82                        FT_Byte**  pointer,
83                        FT_ULong   length )
84   {
85 #ifndef FT_CONFIG_OPTION_INCREMENTAL
86     FT_UNUSED( length );
87 #endif
88 
89 #ifdef FT_CONFIG_OPTION_INCREMENTAL
90     /* For incremental fonts get the character data using the */
91     /* callback function.                                     */
92     if ( face->root.internal->incremental_interface )
93     {
94       FT_Data  data;
95 
96 
97       data.pointer = *pointer;
98       data.length  = (FT_Int)length;
99 
100       face->root.internal->incremental_interface->funcs->free_glyph_data(
101         face->root.internal->incremental_interface->object, &data );
102     }
103     else
104 #endif /* FT_CONFIG_OPTION_INCREMENTAL */
105 
106     {
107       CFF_Font  cff = (CFF_Font)(face->extra.data);
108 
109 
110       cff_index_forget_element( &cff->charstrings_index, pointer );
111     }
112   }
113 
114 
115   /*************************************************************************/
116   /*************************************************************************/
117   /*************************************************************************/
118   /**********                                                      *********/
119   /**********                                                      *********/
120   /**********            COMPUTE THE MAXIMUM ADVANCE WIDTH         *********/
121   /**********                                                      *********/
122   /**********    The following code is in charge of computing      *********/
123   /**********    the maximum advance width of the font.  It        *********/
124   /**********    quickly processes each glyph charstring to        *********/
125   /**********    extract the value from either a `sbw' or `seac'   *********/
126   /**********    operator.                                         *********/
127   /**********                                                      *********/
128   /*************************************************************************/
129   /*************************************************************************/
130   /*************************************************************************/
131 
132 
133 #if 0 /* unused until we support pure CFF fonts */
134 
135 
136   FT_LOCAL_DEF( FT_Error )
137   cff_compute_max_advance( TT_Face  face,
138                            FT_Int*  max_advance )
139   {
140     FT_Error     error = FT_Err_Ok;
141     CFF_Decoder  decoder;
142     FT_Int       glyph_index;
143     CFF_Font     cff = (CFF_Font)face->other;
144 
145     PSAux_Service            psaux         = (PSAux_Service)face->psaux;
146     const CFF_Decoder_Funcs  decoder_funcs = psaux->cff_decoder_funcs;
147 
148 
149     *max_advance = 0;
150 
151     /* Initialize load decoder */
152     decoder_funcs->init( &decoder, face, 0, 0, 0, 0, 0, 0 );
153 
154     decoder.builder.metrics_only = 1;
155     decoder.builder.load_points  = 0;
156 
157     /* For each glyph, parse the glyph charstring and extract */
158     /* the advance width.                                     */
159     for ( glyph_index = 0; glyph_index < face->root.num_glyphs;
160           glyph_index++ )
161     {
162       FT_Byte*  charstring;
163       FT_ULong  charstring_len;
164 
165 
166       /* now get load the unscaled outline */
167       error = cff_get_glyph_data( face, glyph_index,
168                                   &charstring, &charstring_len );
169       if ( !error )
170       {
171         error = decoder_funcs->prepare( &decoder, size, glyph_index );
172         if ( !error )
173           error = decoder_funcs->parse_charstrings_old( &decoder,
174                                                         charstring,
175                                                         charstring_len,
176                                                         0 );
177 
178         cff_free_glyph_data( face, &charstring, &charstring_len );
179       }
180 
181       /* ignore the error if one has occurred -- skip to next glyph */
182       error = FT_Err_Ok;
183     }
184 
185     *max_advance = decoder.builder.advance.x;
186 
187     return FT_Err_Ok;
188   }
189 
190 
191 #endif /* 0 */
192 
193 
194   FT_LOCAL_DEF( FT_Error )
cff_slot_load(CFF_GlyphSlot glyph,CFF_Size size,FT_UInt glyph_index,FT_Int32 load_flags)195   cff_slot_load( CFF_GlyphSlot  glyph,
196                  CFF_Size       size,
197                  FT_UInt        glyph_index,
198                  FT_Int32       load_flags )
199   {
200     FT_Error     error;
201     CFF_Decoder  decoder;
202     PS_Decoder   psdecoder;
203     TT_Face      face = (TT_Face)glyph->root.face;
204     FT_Bool      hinting, scaled, force_scaling;
205     CFF_Font     cff  = (CFF_Font)face->extra.data;
206 
207     PSAux_Service            psaux         = (PSAux_Service)face->psaux;
208     const CFF_Decoder_Funcs  decoder_funcs = psaux->cff_decoder_funcs;
209 
210     FT_Matrix    font_matrix;
211     FT_Vector    font_offset;
212 
213 
214     force_scaling = FALSE;
215 
216     /* in a CID-keyed font, consider `glyph_index' as a CID and map */
217     /* it immediately to the real glyph_index -- if it isn't a      */
218     /* subsetted font, glyph_indices and CIDs are identical, though */
219     if ( cff->top_font.font_dict.cid_registry != 0xFFFFU &&
220          cff->charset.cids                               )
221     {
222       /* don't handle CID 0 (.notdef) which is directly mapped to GID 0 */
223       if ( glyph_index != 0 )
224       {
225         glyph_index = cff_charset_cid_to_gindex( &cff->charset,
226                                                  glyph_index );
227         if ( glyph_index == 0 )
228           return FT_THROW( Invalid_Argument );
229       }
230     }
231     else if ( glyph_index >= cff->num_glyphs )
232       return FT_THROW( Invalid_Argument );
233 
234     if ( load_flags & FT_LOAD_NO_RECURSE )
235       load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING;
236 
237     glyph->x_scale = 0x10000L;
238     glyph->y_scale = 0x10000L;
239     if ( size )
240     {
241       glyph->x_scale = size->root.metrics.x_scale;
242       glyph->y_scale = size->root.metrics.y_scale;
243     }
244 
245 #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
246 
247     /* try to load embedded bitmap if any              */
248     /*                                                 */
249     /* XXX: The convention should be emphasized in     */
250     /*      the documents because it can be confusing. */
251     if ( size )
252     {
253       CFF_Face      cff_face = (CFF_Face)size->root.face;
254       SFNT_Service  sfnt     = (SFNT_Service)cff_face->sfnt;
255       FT_Stream     stream   = cff_face->root.stream;
256 
257 
258       if ( size->strike_index != 0xFFFFFFFFUL      &&
259            sfnt->load_eblc                         &&
260            ( load_flags & FT_LOAD_NO_BITMAP ) == 0 )
261       {
262         TT_SBit_MetricsRec  metrics;
263 
264 
265         error = sfnt->load_sbit_image( face,
266                                        size->strike_index,
267                                        glyph_index,
268                                        (FT_UInt)load_flags,
269                                        stream,
270                                        &glyph->root.bitmap,
271                                        &metrics );
272 
273         if ( !error )
274         {
275           FT_Bool    has_vertical_info;
276           FT_UShort  advance;
277           FT_Short   dummy;
278 
279 
280           glyph->root.outline.n_points   = 0;
281           glyph->root.outline.n_contours = 0;
282 
283           glyph->root.metrics.width  = (FT_Pos)metrics.width  * 64;
284           glyph->root.metrics.height = (FT_Pos)metrics.height * 64;
285 
286           glyph->root.metrics.horiBearingX = (FT_Pos)metrics.horiBearingX * 64;
287           glyph->root.metrics.horiBearingY = (FT_Pos)metrics.horiBearingY * 64;
288           glyph->root.metrics.horiAdvance  = (FT_Pos)metrics.horiAdvance  * 64;
289 
290           glyph->root.metrics.vertBearingX = (FT_Pos)metrics.vertBearingX * 64;
291           glyph->root.metrics.vertBearingY = (FT_Pos)metrics.vertBearingY * 64;
292           glyph->root.metrics.vertAdvance  = (FT_Pos)metrics.vertAdvance  * 64;
293 
294           glyph->root.format = FT_GLYPH_FORMAT_BITMAP;
295 
296           if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
297           {
298             glyph->root.bitmap_left = metrics.vertBearingX;
299             glyph->root.bitmap_top  = metrics.vertBearingY;
300           }
301           else
302           {
303             glyph->root.bitmap_left = metrics.horiBearingX;
304             glyph->root.bitmap_top  = metrics.horiBearingY;
305           }
306 
307           /* compute linear advance widths */
308 
309           (void)( (SFNT_Service)face->sfnt )->get_metrics( face, 0,
310                                                            glyph_index,
311                                                            &dummy,
312                                                            &advance );
313           glyph->root.linearHoriAdvance = advance;
314 
315           has_vertical_info = FT_BOOL(
316                                 face->vertical_info                   &&
317                                 face->vertical.number_Of_VMetrics > 0 );
318 
319           /* get the vertical metrics from the vmtx table if we have one */
320           if ( has_vertical_info )
321           {
322             (void)( (SFNT_Service)face->sfnt )->get_metrics( face, 1,
323                                                              glyph_index,
324                                                              &dummy,
325                                                              &advance );
326             glyph->root.linearVertAdvance = advance;
327           }
328           else
329           {
330             /* make up vertical ones */
331             if ( face->os2.version != 0xFFFFU )
332               glyph->root.linearVertAdvance = (FT_Pos)
333                 ( face->os2.sTypoAscender - face->os2.sTypoDescender );
334             else
335               glyph->root.linearVertAdvance = (FT_Pos)
336                 ( face->horizontal.Ascender - face->horizontal.Descender );
337           }
338 
339           return error;
340         }
341       }
342     }
343 
344 #endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
345 
346     /* return immediately if we only want the embedded bitmaps */
347     if ( load_flags & FT_LOAD_SBITS_ONLY )
348       return FT_THROW( Invalid_Argument );
349 
350     /* if we have a CID subfont, use its matrix (which has already */
351     /* been multiplied with the root matrix)                       */
352 
353     /* this scaling is only relevant if the PS hinter isn't active */
354     if ( cff->num_subfonts )
355     {
356       FT_Long  top_upm, sub_upm;
357       FT_Byte  fd_index = cff_fd_select_get( &cff->fd_select,
358                                              glyph_index );
359 
360 
361       if ( fd_index >= cff->num_subfonts )
362         fd_index = (FT_Byte)( cff->num_subfonts - 1 );
363 
364       top_upm = (FT_Long)cff->top_font.font_dict.units_per_em;
365       sub_upm = (FT_Long)cff->subfonts[fd_index]->font_dict.units_per_em;
366 
367 
368       font_matrix = cff->subfonts[fd_index]->font_dict.font_matrix;
369       font_offset = cff->subfonts[fd_index]->font_dict.font_offset;
370 
371       if ( top_upm != sub_upm )
372       {
373         glyph->x_scale = FT_MulDiv( glyph->x_scale, top_upm, sub_upm );
374         glyph->y_scale = FT_MulDiv( glyph->y_scale, top_upm, sub_upm );
375 
376         force_scaling = TRUE;
377       }
378     }
379     else
380     {
381       font_matrix = cff->top_font.font_dict.font_matrix;
382       font_offset = cff->top_font.font_dict.font_offset;
383     }
384 
385     glyph->root.outline.n_points   = 0;
386     glyph->root.outline.n_contours = 0;
387 
388     /* top-level code ensures that FT_LOAD_NO_HINTING is set */
389     /* if FT_LOAD_NO_SCALE is active                         */
390     hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_HINTING ) == 0 );
391     scaled  = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE   ) == 0 );
392 
393     glyph->hint        = hinting;
394     glyph->scaled      = scaled;
395     glyph->root.format = FT_GLYPH_FORMAT_OUTLINE;  /* by default */
396 
397     {
398 #ifdef CFF_CONFIG_OPTION_OLD_ENGINE
399       PS_Driver  driver = (PS_Driver)FT_FACE_DRIVER( face );
400 #endif
401 
402 
403       FT_Byte*  charstring;
404       FT_ULong  charstring_len;
405 
406 
407       decoder_funcs->init( &decoder, face, size, glyph, hinting,
408                            FT_LOAD_TARGET_MODE( load_flags ),
409                            cff_get_glyph_data,
410                            cff_free_glyph_data );
411 
412       /* this is for pure CFFs */
413       if ( load_flags & FT_LOAD_ADVANCE_ONLY )
414         decoder.width_only = TRUE;
415 
416       decoder.builder.no_recurse =
417         FT_BOOL( load_flags & FT_LOAD_NO_RECURSE );
418 
419       /* now load the unscaled outline */
420       error = cff_get_glyph_data( face, glyph_index,
421                                   &charstring, &charstring_len );
422       if ( error )
423         goto Glyph_Build_Finished;
424 
425       error = decoder_funcs->prepare( &decoder, size, glyph_index );
426       if ( error )
427         goto Glyph_Build_Finished;
428 
429 #ifdef CFF_CONFIG_OPTION_OLD_ENGINE
430       /* choose which CFF renderer to use */
431       if ( driver->hinting_engine == FT_HINTING_FREETYPE )
432         error = decoder_funcs->parse_charstrings_old( &decoder,
433                                                       charstring,
434                                                       charstring_len,
435                                                       0 );
436       else
437 #endif
438       {
439         psaux->ps_decoder_init( &psdecoder, &decoder, FALSE );
440 
441         error = decoder_funcs->parse_charstrings( &psdecoder,
442                                                   charstring,
443                                                   charstring_len );
444 
445         /* Adobe's engine uses 16.16 numbers everywhere;              */
446         /* as a consequence, glyphs larger than 2000ppem get rejected */
447         if ( FT_ERR_EQ( error, Glyph_Too_Big ) )
448         {
449           /* this time, we retry unhinted and scale up the glyph later on */
450           /* (the engine uses and sets the hardcoded value 0x10000 / 64 = */
451           /* 0x400 for both `x_scale' and `y_scale' in this case)         */
452           hinting       = FALSE;
453           force_scaling = TRUE;
454           glyph->hint   = hinting;
455 
456           error = decoder_funcs->parse_charstrings( &psdecoder,
457                                                     charstring,
458                                                     charstring_len );
459         }
460       }
461 
462       cff_free_glyph_data( face, &charstring, charstring_len );
463 
464       if ( error )
465         goto Glyph_Build_Finished;
466 
467 #ifdef FT_CONFIG_OPTION_INCREMENTAL
468       /* Control data and length may not be available for incremental */
469       /* fonts.                                                       */
470       if ( face->root.internal->incremental_interface )
471       {
472         glyph->root.control_data = NULL;
473         glyph->root.control_len = 0;
474       }
475       else
476 #endif /* FT_CONFIG_OPTION_INCREMENTAL */
477 
478       /* We set control_data and control_len if charstrings is loaded. */
479       /* See how charstring loads at cff_index_access_element() in     */
480       /* cffload.c.                                                    */
481       {
482         CFF_Index  csindex = &cff->charstrings_index;
483 
484 
485         if ( csindex->offsets )
486         {
487           glyph->root.control_data = csindex->bytes +
488                                      csindex->offsets[glyph_index] - 1;
489           glyph->root.control_len  = (FT_Long)charstring_len;
490         }
491       }
492 
493   Glyph_Build_Finished:
494       /* save new glyph tables, if no error */
495       if ( !error )
496         decoder.builder.funcs.done( &decoder.builder );
497       /* XXX: anything to do for broken glyph entry? */
498     }
499 
500 #ifdef FT_CONFIG_OPTION_INCREMENTAL
501 
502     /* Incremental fonts can optionally override the metrics. */
503     if ( !error                                                               &&
504          face->root.internal->incremental_interface                           &&
505          face->root.internal->incremental_interface->funcs->get_glyph_metrics )
506     {
507       FT_Incremental_MetricsRec  metrics;
508 
509 
510       metrics.bearing_x = decoder.builder.left_bearing.x;
511       metrics.bearing_y = 0;
512       metrics.advance   = decoder.builder.advance.x;
513       metrics.advance_v = decoder.builder.advance.y;
514 
515       error = face->root.internal->incremental_interface->funcs->get_glyph_metrics(
516                 face->root.internal->incremental_interface->object,
517                 glyph_index, FALSE, &metrics );
518 
519       decoder.builder.left_bearing.x = metrics.bearing_x;
520       decoder.builder.advance.x      = metrics.advance;
521       decoder.builder.advance.y      = metrics.advance_v;
522     }
523 
524 #endif /* FT_CONFIG_OPTION_INCREMENTAL */
525 
526     if ( !error )
527     {
528       /* Now, set the metrics -- this is rather simple, as   */
529       /* the left side bearing is the xMin, and the top side */
530       /* bearing the yMax.                                   */
531 
532       /* For composite glyphs, return only left side bearing and */
533       /* advance width.                                          */
534       if ( load_flags & FT_LOAD_NO_RECURSE )
535       {
536         FT_Slot_Internal  internal = glyph->root.internal;
537 
538 
539         glyph->root.metrics.horiBearingX = decoder.builder.left_bearing.x;
540         glyph->root.metrics.horiAdvance  = decoder.glyph_width;
541         internal->glyph_matrix           = font_matrix;
542         internal->glyph_delta            = font_offset;
543         internal->glyph_transformed      = 1;
544       }
545       else
546       {
547         FT_BBox            cbox;
548         FT_Glyph_Metrics*  metrics = &glyph->root.metrics;
549         FT_Bool            has_vertical_info;
550 
551 
552         if ( face->horizontal.number_Of_HMetrics )
553         {
554           FT_Short   horiBearingX = 0;
555           FT_UShort  horiAdvance  = 0;
556 
557 
558           ( (SFNT_Service)face->sfnt )->get_metrics( face, 0,
559                                                      glyph_index,
560                                                      &horiBearingX,
561                                                      &horiAdvance );
562           metrics->horiAdvance          = horiAdvance;
563           metrics->horiBearingX         = horiBearingX;
564           glyph->root.linearHoriAdvance = horiAdvance;
565         }
566         else
567         {
568           /* copy the _unscaled_ advance width */
569           metrics->horiAdvance          = decoder.glyph_width;
570           glyph->root.linearHoriAdvance = decoder.glyph_width;
571         }
572 
573         glyph->root.internal->glyph_transformed = 0;
574 
575         has_vertical_info = FT_BOOL( face->vertical_info                   &&
576                                      face->vertical.number_Of_VMetrics > 0 );
577 
578         /* get the vertical metrics from the vmtx table if we have one */
579         if ( has_vertical_info )
580         {
581           FT_Short   vertBearingY = 0;
582           FT_UShort  vertAdvance  = 0;
583 
584 
585           ( (SFNT_Service)face->sfnt )->get_metrics( face, 1,
586                                                      glyph_index,
587                                                      &vertBearingY,
588                                                      &vertAdvance );
589           metrics->vertBearingY = vertBearingY;
590           metrics->vertAdvance  = vertAdvance;
591         }
592         else
593         {
594           /* make up vertical ones */
595           if ( face->os2.version != 0xFFFFU )
596             metrics->vertAdvance = (FT_Pos)( face->os2.sTypoAscender -
597                                              face->os2.sTypoDescender );
598           else
599             metrics->vertAdvance = (FT_Pos)( face->horizontal.Ascender -
600                                              face->horizontal.Descender );
601         }
602 
603         glyph->root.linearVertAdvance = metrics->vertAdvance;
604 
605         glyph->root.format = FT_GLYPH_FORMAT_OUTLINE;
606 
607         glyph->root.outline.flags = 0;
608         if ( size && size->root.metrics.y_ppem < 24 )
609           glyph->root.outline.flags |= FT_OUTLINE_HIGH_PRECISION;
610 
611         glyph->root.outline.flags |= FT_OUTLINE_REVERSE_FILL;
612 
613         /* apply the font matrix, if any */
614         if ( font_matrix.xx != 0x10000L || font_matrix.yy != 0x10000L ||
615              font_matrix.xy != 0        || font_matrix.yx != 0        )
616         {
617           FT_Outline_Transform( &glyph->root.outline, &font_matrix );
618 
619           metrics->horiAdvance = FT_MulFix( metrics->horiAdvance,
620                                             font_matrix.xx );
621           metrics->vertAdvance = FT_MulFix( metrics->vertAdvance,
622                                             font_matrix.yy );
623         }
624 
625         if ( font_offset.x || font_offset.y )
626         {
627           FT_Outline_Translate( &glyph->root.outline,
628                                 font_offset.x,
629                                 font_offset.y );
630 
631           metrics->horiAdvance += font_offset.x;
632           metrics->vertAdvance += font_offset.y;
633         }
634 
635         if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 || force_scaling )
636         {
637           /* scale the outline and the metrics */
638           FT_Int       n;
639           FT_Outline*  cur     = &glyph->root.outline;
640           FT_Vector*   vec     = cur->points;
641           FT_Fixed     x_scale = glyph->x_scale;
642           FT_Fixed     y_scale = glyph->y_scale;
643 
644 
645           /* First of all, scale the points */
646           if ( !hinting || !decoder.builder.hints_funcs )
647             for ( n = cur->n_points; n > 0; n--, vec++ )
648             {
649               vec->x = FT_MulFix( vec->x, x_scale );
650               vec->y = FT_MulFix( vec->y, y_scale );
651             }
652 
653           /* Then scale the metrics */
654           metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale );
655           metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale );
656         }
657 
658         /* compute the other metrics */
659         FT_Outline_Get_CBox( &glyph->root.outline, &cbox );
660 
661         metrics->width  = cbox.xMax - cbox.xMin;
662         metrics->height = cbox.yMax - cbox.yMin;
663 
664         metrics->horiBearingX = cbox.xMin;
665         metrics->horiBearingY = cbox.yMax;
666 
667         if ( has_vertical_info )
668           metrics->vertBearingX = metrics->horiBearingX -
669                                     metrics->horiAdvance / 2;
670         else
671         {
672           if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
673             ft_synthesize_vertical_metrics( metrics,
674                                             metrics->vertAdvance );
675         }
676       }
677     }
678 
679     return error;
680   }
681 
682 
683 /* END */
684