"Fossies" - the Fresh Open Source Software Archive

Member "SDL2_ttf-2.20.2/external/freetype/src/autofit/afshaper.c" (25 May 2022, 20127 Bytes) of package /linux/misc/SDL2_ttf-2.20.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "afshaper.c" see the Fossies "Dox" file reference documentation.

    1 /****************************************************************************
    2  *
    3  * afshaper.c
    4  *
    5  *   HarfBuzz interface for accessing OpenType features (body).
    6  *
    7  * Copyright (C) 2013-2022 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 <freetype/freetype.h>
   20 #include <freetype/ftadvanc.h>
   21 #include "afglobal.h"
   22 #include "aftypes.h"
   23 #include "afshaper.h"
   24 
   25 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
   26 
   27 
   28   /**************************************************************************
   29    *
   30    * The macro FT_COMPONENT is used in trace mode.  It is an implicit
   31    * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
   32    * messages during execution.
   33    */
   34 #undef  FT_COMPONENT
   35 #define FT_COMPONENT  afshaper
   36 
   37 
   38   /*
   39    * We use `sets' (in the HarfBuzz sense, which comes quite near to the
   40    * usual mathematical meaning) to manage both lookups and glyph indices.
   41    *
   42    * 1. For each coverage, collect lookup IDs in a set.  Note that an
   43    *    auto-hinter `coverage' is represented by one `feature', and a
   44    *    feature consists of an arbitrary number of (font specific) `lookup's
   45    *    that actually do the mapping job.  Please check the OpenType
   46    *    specification for more details on features and lookups.
   47    *
   48    * 2. Create glyph ID sets from the corresponding lookup sets.
   49    *
   50    * 3. The glyph set corresponding to AF_COVERAGE_DEFAULT is computed
   51    *    with all lookups specific to the OpenType script activated.  It
   52    *    relies on the order of AF_DEFINE_STYLE_CLASS entries so that
   53    *    special coverages (like `oldstyle figures') don't get overwritten.
   54    *
   55    */
   56 
   57 
   58   /* load coverage tags */
   59 #undef  COVERAGE
   60 #define COVERAGE( name, NAME, description,             \
   61                   tag1, tag2, tag3, tag4 )             \
   62           static const hb_tag_t  name ## _coverage[] = \
   63           {                                            \
   64             HB_TAG( tag1, tag2, tag3, tag4 ),          \
   65             HB_TAG_NONE                                \
   66           };
   67 
   68 
   69 #include "afcover.h"
   70 
   71 
   72   /* define mapping between coverage tags and AF_Coverage */
   73 #undef  COVERAGE
   74 #define COVERAGE( name, NAME, description, \
   75                   tag1, tag2, tag3, tag4 ) \
   76           name ## _coverage,
   77 
   78 
   79   static const hb_tag_t*  coverages[] =
   80   {
   81 #include "afcover.h"
   82 
   83     NULL /* AF_COVERAGE_DEFAULT */
   84   };
   85 
   86 
   87   /* load HarfBuzz script tags */
   88 #undef  SCRIPT
   89 #define SCRIPT( s, S, d, h, H, ss )  h,
   90 
   91 
   92   static const hb_script_t  scripts[] =
   93   {
   94 #include "afscript.h"
   95   };
   96 
   97 
   98   FT_Error
   99   af_shaper_get_coverage( AF_FaceGlobals  globals,
  100                           AF_StyleClass   style_class,
  101                           FT_UShort*      gstyles,
  102                           FT_Bool         default_script )
  103   {
  104     hb_face_t*  face;
  105 
  106     hb_set_t*  gsub_lookups = NULL; /* GSUB lookups for a given script */
  107     hb_set_t*  gsub_glyphs  = NULL; /* glyphs covered by GSUB lookups  */
  108     hb_set_t*  gpos_lookups = NULL; /* GPOS lookups for a given script */
  109     hb_set_t*  gpos_glyphs  = NULL; /* glyphs covered by GPOS lookups  */
  110 
  111     hb_script_t      script;
  112     const hb_tag_t*  coverage_tags;
  113     hb_tag_t         script_tags[] = { HB_TAG_NONE,
  114                                        HB_TAG_NONE,
  115                                        HB_TAG_NONE,
  116                                        HB_TAG_NONE };
  117 
  118     hb_codepoint_t  idx;
  119 #ifdef FT_DEBUG_LEVEL_TRACE
  120     int             count;
  121 #endif
  122 
  123 
  124     if ( !globals || !style_class || !gstyles )
  125       return FT_THROW( Invalid_Argument );
  126 
  127     face = hb_font_get_face( globals->hb_font );
  128 
  129     coverage_tags = coverages[style_class->coverage];
  130     script        = scripts[style_class->script];
  131 
  132     /* Convert a HarfBuzz script tag into the corresponding OpenType */
  133     /* tag or tags -- some Indic scripts like Devanagari have an old */
  134     /* and a new set of features.                                    */
  135     {
  136       unsigned int  tags_count = 3;
  137       hb_tag_t      tags[3];
  138 
  139 
  140       hb_ot_tags_from_script_and_language( script,
  141                                            HB_LANGUAGE_INVALID,
  142                                            &tags_count,
  143                                            tags,
  144                                            NULL,
  145                                            NULL );
  146       script_tags[0] = tags_count > 0 ? tags[0] : HB_TAG_NONE;
  147       script_tags[1] = tags_count > 1 ? tags[1] : HB_TAG_NONE;
  148       script_tags[2] = tags_count > 2 ? tags[2] : HB_TAG_NONE;
  149     }
  150 
  151     /* If the second tag is HB_OT_TAG_DEFAULT_SCRIPT, change that to     */
  152     /* HB_TAG_NONE except for the default script.                        */
  153     if ( default_script )
  154     {
  155       if ( script_tags[0] == HB_TAG_NONE )
  156         script_tags[0] = HB_OT_TAG_DEFAULT_SCRIPT;
  157       else
  158       {
  159         if ( script_tags[1] == HB_TAG_NONE )
  160           script_tags[1] = HB_OT_TAG_DEFAULT_SCRIPT;
  161         else if ( script_tags[1] != HB_OT_TAG_DEFAULT_SCRIPT )
  162           script_tags[2] = HB_OT_TAG_DEFAULT_SCRIPT;
  163       }
  164     }
  165     else
  166     {
  167       /* we use non-standard tags like `khms' for special purposes;       */
  168       /* HarfBuzz maps them to `DFLT', which we don't want to handle here */
  169       if ( script_tags[0] == HB_OT_TAG_DEFAULT_SCRIPT )
  170         goto Exit;
  171     }
  172 
  173     gsub_lookups = hb_set_create();
  174     hb_ot_layout_collect_lookups( face,
  175                                   HB_OT_TAG_GSUB,
  176                                   script_tags,
  177                                   NULL,
  178                                   coverage_tags,
  179                                   gsub_lookups );
  180 
  181     if ( hb_set_is_empty( gsub_lookups ) )
  182       goto Exit; /* nothing to do */
  183 
  184     FT_TRACE4(( "GSUB lookups (style `%s'):\n",
  185                 af_style_names[style_class->style] ));
  186     FT_TRACE4(( " " ));
  187 
  188 #ifdef FT_DEBUG_LEVEL_TRACE
  189     count = 0;
  190 #endif
  191 
  192     gsub_glyphs = hb_set_create();
  193     for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups, &idx ); )
  194     {
  195 #ifdef FT_DEBUG_LEVEL_TRACE
  196       FT_TRACE4(( " %d", idx ));
  197       count++;
  198 #endif
  199 
  200       /* get output coverage of GSUB feature */
  201       hb_ot_layout_lookup_collect_glyphs( face,
  202                                           HB_OT_TAG_GSUB,
  203                                           idx,
  204                                           NULL,
  205                                           NULL,
  206                                           NULL,
  207                                           gsub_glyphs );
  208     }
  209 
  210 #ifdef FT_DEBUG_LEVEL_TRACE
  211     if ( !count )
  212       FT_TRACE4(( " (none)" ));
  213     FT_TRACE4(( "\n" ));
  214     FT_TRACE4(( "\n" ));
  215 #endif
  216 
  217     FT_TRACE4(( "GPOS lookups (style `%s'):\n",
  218                 af_style_names[style_class->style] ));
  219     FT_TRACE4(( " " ));
  220 
  221     gpos_lookups = hb_set_create();
  222     hb_ot_layout_collect_lookups( face,
  223                                   HB_OT_TAG_GPOS,
  224                                   script_tags,
  225                                   NULL,
  226                                   coverage_tags,
  227                                   gpos_lookups );
  228 
  229 #ifdef FT_DEBUG_LEVEL_TRACE
  230     count = 0;
  231 #endif
  232 
  233     gpos_glyphs = hb_set_create();
  234     for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gpos_lookups, &idx ); )
  235     {
  236 #ifdef FT_DEBUG_LEVEL_TRACE
  237       FT_TRACE4(( " %d", idx ));
  238       count++;
  239 #endif
  240 
  241       /* get input coverage of GPOS feature */
  242       hb_ot_layout_lookup_collect_glyphs( face,
  243                                           HB_OT_TAG_GPOS,
  244                                           idx,
  245                                           NULL,
  246                                           gpos_glyphs,
  247                                           NULL,
  248                                           NULL );
  249     }
  250 
  251 #ifdef FT_DEBUG_LEVEL_TRACE
  252     if ( !count )
  253       FT_TRACE4(( " (none)" ));
  254     FT_TRACE4(( "\n" ));
  255     FT_TRACE4(( "\n" ));
  256 #endif
  257 
  258     /*
  259      * We now check whether we can construct blue zones, using glyphs
  260      * covered by the feature only.  In case there is not a single zone
  261      * (this is, not a single character is covered), we skip this coverage.
  262      *
  263      */
  264     if ( style_class->coverage != AF_COVERAGE_DEFAULT )
  265     {
  266       AF_Blue_Stringset         bss = style_class->blue_stringset;
  267       const AF_Blue_StringRec*  bs  = &af_blue_stringsets[bss];
  268 
  269       FT_Bool  found = 0;
  270 
  271 
  272       for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
  273       {
  274         const char*  p = &af_blue_strings[bs->string];
  275 
  276 
  277         while ( *p )
  278         {
  279           hb_codepoint_t  ch;
  280 
  281 
  282           GET_UTF8_CHAR( ch, p );
  283 
  284           for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups,
  285                                                          &idx ); )
  286           {
  287             hb_codepoint_t  gidx = FT_Get_Char_Index( globals->face, ch );
  288 
  289 
  290             if ( hb_ot_layout_lookup_would_substitute( face, idx,
  291                                                        &gidx, 1, 1 ) )
  292             {
  293               found = 1;
  294               break;
  295             }
  296           }
  297         }
  298       }
  299 
  300       if ( !found )
  301       {
  302         FT_TRACE4(( "  no blue characters found; style skipped\n" ));
  303         goto Exit;
  304       }
  305     }
  306 
  307     /*
  308      * Various OpenType features might use the same glyphs at different
  309      * vertical positions; for example, superscript and subscript glyphs
  310      * could be the same.  However, the auto-hinter is completely
  311      * agnostic of OpenType features after the feature analysis has been
  312      * completed: The engine then simply receives a glyph index and returns a
  313      * hinted and usually rendered glyph.
  314      *
  315      * Consider the superscript feature of font `pala.ttf': Some of the
  316      * glyphs are `real', this is, they have a zero vertical offset, but
  317      * most of them are small caps glyphs shifted up to the superscript
  318      * position (this is, the `sups' feature is present in both the GSUB and
  319      * GPOS tables).  The code for blue zones computation actually uses a
  320      * feature's y offset so that the `real' glyphs get correct hints.  But
  321      * later on it is impossible to decide whether a glyph index belongs to,
  322      * say, the small caps or superscript feature.
  323      *
  324      * For this reason, we don't assign a style to a glyph if the current
  325      * feature covers the glyph in both the GSUB and the GPOS tables.  This
  326      * is quite a broad condition, assuming that
  327      *
  328      *   (a) glyphs that get used in multiple features are present in a
  329      *       feature without vertical shift,
  330      *
  331      * and
  332      *
  333      *   (b) a feature's GPOS data really moves the glyph vertically.
  334      *
  335      * Not fulfilling condition (a) makes a font larger; it would also
  336      * reduce the number of glyphs that could be addressed directly without
  337      * using OpenType features, so this assumption is rather strong.
  338      *
  339      * Condition (b) is much weaker, and there might be glyphs which get
  340      * missed.  However, the OpenType features we are going to handle are
  341      * primarily located in GSUB, and HarfBuzz doesn't provide an API to
  342      * directly get the necessary information from the GPOS table.  A
  343      * possible solution might be to directly parse the GPOS table to find
  344      * out whether a glyph gets shifted vertically, but this is something I
  345      * would like to avoid if not really necessary.
  346      *
  347      * Note that we don't follow this logic for the default coverage.
  348      * Complex scripts like Devanagari have mandatory GPOS features to
  349      * position many glyph elements, using mark-to-base or mark-to-ligature
  350      * tables; the number of glyphs missed due to condition (b) would be far
  351      * too large.
  352      *
  353      */
  354     if ( style_class->coverage != AF_COVERAGE_DEFAULT )
  355       hb_set_subtract( gsub_glyphs, gpos_glyphs );
  356 
  357 #ifdef FT_DEBUG_LEVEL_TRACE
  358     FT_TRACE4(( "  glyphs without GPOS data (`*' means already assigned)" ));
  359     count = 0;
  360 #endif
  361 
  362     for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_glyphs, &idx ); )
  363     {
  364 #ifdef FT_DEBUG_LEVEL_TRACE
  365       if ( !( count % 10 ) )
  366       {
  367         FT_TRACE4(( "\n" ));
  368         FT_TRACE4(( "   " ));
  369       }
  370 
  371       FT_TRACE4(( " %d", idx ));
  372       count++;
  373 #endif
  374 
  375       /* glyph indices returned by `hb_ot_layout_lookup_collect_glyphs' */
  376       /* can be arbitrary: some fonts use fake indices for processing   */
  377       /* internal to GSUB or GPOS, which is fully valid                 */
  378       if ( idx >= (hb_codepoint_t)globals->glyph_count )
  379         continue;
  380 
  381       if ( gstyles[idx] == AF_STYLE_UNASSIGNED )
  382         gstyles[idx] = (FT_UShort)style_class->style;
  383 #ifdef FT_DEBUG_LEVEL_TRACE
  384       else
  385         FT_TRACE4(( "*" ));
  386 #endif
  387     }
  388 
  389 #ifdef FT_DEBUG_LEVEL_TRACE
  390     if ( !count )
  391     {
  392       FT_TRACE4(( "\n" ));
  393       FT_TRACE4(( "    (none)" ));
  394     }
  395     FT_TRACE4(( "\n" ));
  396     FT_TRACE4(( "\n" ));
  397 #endif
  398 
  399   Exit:
  400     hb_set_destroy( gsub_lookups );
  401     hb_set_destroy( gsub_glyphs  );
  402     hb_set_destroy( gpos_lookups );
  403     hb_set_destroy( gpos_glyphs  );
  404 
  405     return FT_Err_Ok;
  406   }
  407 
  408 
  409   /* construct HarfBuzz features */
  410 #undef  COVERAGE
  411 #define COVERAGE( name, NAME, description,                \
  412                   tag1, tag2, tag3, tag4 )                \
  413           static const hb_feature_t  name ## _feature[] = \
  414           {                                               \
  415             {                                             \
  416               HB_TAG( tag1, tag2, tag3, tag4 ),           \
  417               1, 0, (unsigned int)-1                      \
  418             }                                             \
  419           };
  420 
  421 
  422 #include "afcover.h"
  423 
  424 
  425   /* define mapping between HarfBuzz features and AF_Coverage */
  426 #undef  COVERAGE
  427 #define COVERAGE( name, NAME, description, \
  428                   tag1, tag2, tag3, tag4 ) \
  429           name ## _feature,
  430 
  431 
  432   static const hb_feature_t*  features[] =
  433   {
  434 #include "afcover.h"
  435 
  436     NULL /* AF_COVERAGE_DEFAULT */
  437   };
  438 
  439 
  440   void*
  441   af_shaper_buf_create( FT_Face  face )
  442   {
  443     FT_UNUSED( face );
  444 
  445     return (void*)hb_buffer_create();
  446   }
  447 
  448 
  449   void
  450   af_shaper_buf_destroy( FT_Face  face,
  451                          void*    buf )
  452   {
  453     FT_UNUSED( face );
  454 
  455     hb_buffer_destroy( (hb_buffer_t*)buf );
  456   }
  457 
  458 
  459   const char*
  460   af_shaper_get_cluster( const char*      p,
  461                          AF_StyleMetrics  metrics,
  462                          void*            buf_,
  463                          unsigned int*    count )
  464   {
  465     AF_StyleClass        style_class;
  466     const hb_feature_t*  feature;
  467     FT_Int               upem;
  468     const char*          q;
  469     int                  len;
  470 
  471     hb_buffer_t*    buf = (hb_buffer_t*)buf_;
  472     hb_font_t*      font;
  473     hb_codepoint_t  dummy;
  474 
  475 
  476     upem        = (FT_Int)metrics->globals->face->units_per_EM;
  477     style_class = metrics->style_class;
  478     feature     = features[style_class->coverage];
  479 
  480     font = metrics->globals->hb_font;
  481 
  482     /* we shape at a size of units per EM; this means font units */
  483     hb_font_set_scale( font, upem, upem );
  484 
  485     while ( *p == ' ' )
  486       p++;
  487 
  488     /* count bytes up to next space (or end of buffer) */
  489     q = p;
  490     while ( !( *q == ' ' || *q == '\0' ) )
  491       GET_UTF8_CHAR( dummy, q );
  492     len = (int)( q - p );
  493 
  494     /* feed character(s) to the HarfBuzz buffer */
  495     hb_buffer_clear_contents( buf );
  496     hb_buffer_add_utf8( buf, p, len, 0, len );
  497 
  498     /* we let HarfBuzz guess the script and writing direction */
  499     hb_buffer_guess_segment_properties( buf );
  500 
  501     /* shape buffer, which means conversion from character codes to */
  502     /* glyph indices, possibly applying a feature                   */
  503     hb_shape( font, buf, feature, feature ? 1 : 0 );
  504 
  505     if ( feature )
  506     {
  507       hb_buffer_t*  hb_buf = metrics->globals->hb_buf;
  508 
  509       unsigned int      gcount;
  510       hb_glyph_info_t*  ginfo;
  511 
  512       unsigned int      hb_gcount;
  513       hb_glyph_info_t*  hb_ginfo;
  514 
  515 
  516       /* we have to check whether applying a feature does actually change */
  517       /* glyph indices; otherwise the affected glyph or glyphs aren't     */
  518       /* available at all in the feature                                  */
  519 
  520       hb_buffer_clear_contents( hb_buf );
  521       hb_buffer_add_utf8( hb_buf, p, len, 0, len );
  522       hb_buffer_guess_segment_properties( hb_buf );
  523       hb_shape( font, hb_buf, NULL, 0 );
  524 
  525       ginfo    = hb_buffer_get_glyph_infos( buf, &gcount );
  526       hb_ginfo = hb_buffer_get_glyph_infos( hb_buf, &hb_gcount );
  527 
  528       if ( gcount == hb_gcount )
  529       {
  530         unsigned int  i;
  531 
  532 
  533         for (i = 0; i < gcount; i++ )
  534           if ( ginfo[i].codepoint != hb_ginfo[i].codepoint )
  535             break;
  536 
  537         if ( i == gcount )
  538         {
  539           /* both buffers have identical glyph indices */
  540           hb_buffer_clear_contents( buf );
  541         }
  542       }
  543     }
  544 
  545     *count = hb_buffer_get_length( buf );
  546 
  547 #ifdef FT_DEBUG_LEVEL_TRACE
  548     if ( feature && *count > 1 )
  549       FT_TRACE1(( "af_shaper_get_cluster:"
  550                   " input character mapped to multiple glyphs\n" ));
  551 #endif
  552 
  553     return q;
  554   }
  555 
  556 
  557   FT_ULong
  558   af_shaper_get_elem( AF_StyleMetrics  metrics,
  559                       void*            buf_,
  560                       unsigned int     idx,
  561                       FT_Long*         advance,
  562                       FT_Long*         y_offset )
  563   {
  564     hb_buffer_t*          buf = (hb_buffer_t*)buf_;
  565     hb_glyph_info_t*      ginfo;
  566     hb_glyph_position_t*  gpos;
  567     unsigned int          gcount;
  568 
  569     FT_UNUSED( metrics );
  570 
  571 
  572     ginfo = hb_buffer_get_glyph_infos( buf, &gcount );
  573     gpos  = hb_buffer_get_glyph_positions( buf, &gcount );
  574 
  575     if ( idx >= gcount )
  576       return 0;
  577 
  578     if ( advance )
  579       *advance = gpos[idx].x_advance;
  580     if ( y_offset )
  581       *y_offset = gpos[idx].y_offset;
  582 
  583     return ginfo[idx].codepoint;
  584   }
  585 
  586 
  587 #else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
  588 
  589 
  590   FT_Error
  591   af_shaper_get_coverage( AF_FaceGlobals  globals,
  592                           AF_StyleClass   style_class,
  593                           FT_UShort*      gstyles,
  594                           FT_Bool         default_script )
  595   {
  596     FT_UNUSED( globals );
  597     FT_UNUSED( style_class );
  598     FT_UNUSED( gstyles );
  599     FT_UNUSED( default_script );
  600 
  601     return FT_Err_Ok;
  602   }
  603 
  604 
  605   void*
  606   af_shaper_buf_create( FT_Face  face )
  607   {
  608     FT_UNUSED( face );
  609 
  610     return NULL;
  611   }
  612 
  613 
  614   void
  615   af_shaper_buf_destroy( FT_Face  face,
  616                          void*    buf )
  617   {
  618     FT_UNUSED( face );
  619     FT_UNUSED( buf );
  620   }
  621 
  622 
  623   const char*
  624   af_shaper_get_cluster( const char*      p,
  625                          AF_StyleMetrics  metrics,
  626                          void*            buf_,
  627                          unsigned int*    count )
  628   {
  629     FT_Face    face      = metrics->globals->face;
  630     FT_ULong   ch, dummy = 0;
  631     FT_ULong*  buf       = (FT_ULong*)buf_;
  632 
  633 
  634     while ( *p == ' ' )
  635       p++;
  636 
  637     GET_UTF8_CHAR( ch, p );
  638 
  639     /* since we don't have an engine to handle clusters, */
  640     /* we scan the characters but return zero            */
  641     while ( !( *p == ' ' || *p == '\0' ) )
  642       GET_UTF8_CHAR( dummy, p );
  643 
  644     if ( dummy )
  645     {
  646       *buf   = 0;
  647       *count = 0;
  648     }
  649     else
  650     {
  651       *buf   = FT_Get_Char_Index( face, ch );
  652       *count = 1;
  653     }
  654 
  655     return p;
  656   }
  657 
  658 
  659   FT_ULong
  660   af_shaper_get_elem( AF_StyleMetrics  metrics,
  661                       void*            buf_,
  662                       unsigned int     idx,
  663                       FT_Long*         advance,
  664                       FT_Long*         y_offset )
  665   {
  666     FT_Face   face        = metrics->globals->face;
  667     FT_ULong  glyph_index = *(FT_ULong*)buf_;
  668 
  669     FT_UNUSED( idx );
  670 
  671 
  672     if ( advance )
  673       FT_Get_Advance( face,
  674                       glyph_index,
  675                       FT_LOAD_NO_SCALE         |
  676                       FT_LOAD_NO_HINTING       |
  677                       FT_LOAD_IGNORE_TRANSFORM,
  678                       advance );
  679 
  680     if ( y_offset )
  681       *y_offset = 0;
  682 
  683     return glyph_index;
  684   }
  685 
  686 
  687 #endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
  688 
  689 
  690 /* END */