tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

afshaper.c (23309B)


      1 /****************************************************************************
      2 *
      3 * afshaper.c
      4 *
      5 *   HarfBuzz interface for accessing OpenType features (body).
      6 *
      7 * Copyright (C) 2013-2025 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 
     26 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
     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  FT_LOCAL_ARRAY_DEF( hb_script_t )
     93  af_hb_scripts[] =
     94  {
     95 #include "afscript.h"
     96  };
     97 
     98 
     99  static FT_Error
    100  af_shaper_get_coverage_hb( AF_FaceGlobals  globals,
    101                             AF_StyleClass   style_class,
    102                             FT_UShort*      gstyles,
    103                             FT_Bool         default_script )
    104  {
    105    hb_face_t*  face;
    106 
    107    hb_set_t*  gsub_lookups = NULL; /* GSUB lookups for a given script */
    108    hb_set_t*  gsub_glyphs  = NULL; /* glyphs covered by GSUB lookups  */
    109    hb_set_t*  gpos_lookups = NULL; /* GPOS lookups for a given script */
    110    hb_set_t*  gpos_glyphs  = NULL; /* glyphs covered by GPOS lookups  */
    111 
    112    hb_script_t      script;
    113    const hb_tag_t*  coverage_tags;
    114    hb_tag_t         script_tags[] = { HB_TAG_NONE,
    115                                       HB_TAG_NONE,
    116                                       HB_TAG_NONE,
    117                                       HB_TAG_NONE };
    118 
    119    hb_codepoint_t  idx;
    120 #ifdef FT_DEBUG_LEVEL_TRACE
    121    int             count;
    122 #endif
    123 
    124 
    125    if ( !globals || !style_class || !gstyles )
    126      return FT_THROW( Invalid_Argument );
    127 
    128    face = hb( font_get_face )( globals->hb_font );
    129 
    130    coverage_tags = coverages[style_class->coverage];
    131    script        = af_hb_scripts[style_class->script];
    132 
    133    /* Convert a HarfBuzz script tag into the corresponding OpenType */
    134    /* tag or tags -- some Indic scripts like Devanagari have an old */
    135    /* and a new set of features.                                    */
    136    {
    137      unsigned int  tags_count = 3;
    138      hb_tag_t      tags[3];
    139 
    140 
    141      hb( ot_tags_from_script_and_language )( script,
    142                                              HB_LANGUAGE_INVALID,
    143                                              &tags_count,
    144                                              tags,
    145                                              NULL,
    146                                              NULL );
    147      script_tags[0] = tags_count > 0 ? tags[0] : HB_TAG_NONE;
    148      script_tags[1] = tags_count > 1 ? tags[1] : HB_TAG_NONE;
    149      script_tags[2] = tags_count > 2 ? tags[2] : HB_TAG_NONE;
    150    }
    151 
    152    /* If the second tag is HB_OT_TAG_DEFAULT_SCRIPT, change that to */
    153    /* HB_TAG_NONE except for the default script.                    */
    154    if ( default_script )
    155    {
    156      if ( script_tags[0] == HB_TAG_NONE )
    157        script_tags[0] = HB_OT_TAG_DEFAULT_SCRIPT;
    158      else
    159      {
    160        if ( script_tags[1] == HB_TAG_NONE )
    161          script_tags[1] = HB_OT_TAG_DEFAULT_SCRIPT;
    162        else if ( script_tags[1] != HB_OT_TAG_DEFAULT_SCRIPT )
    163          script_tags[2] = HB_OT_TAG_DEFAULT_SCRIPT;
    164      }
    165    }
    166    else
    167    {
    168      /* we use non-standard tags like `khms' for special purposes;       */
    169      /* HarfBuzz maps them to `DFLT', which we don't want to handle here */
    170      if ( script_tags[0] == HB_OT_TAG_DEFAULT_SCRIPT )
    171        goto Exit;
    172    }
    173 
    174    gsub_lookups = hb( set_create )();
    175    hb( ot_layout_collect_lookups )( face,
    176                                     HB_OT_TAG_GSUB,
    177                                     script_tags,
    178                                     NULL,
    179                                     coverage_tags,
    180                                     gsub_lookups );
    181 
    182    if ( hb( set_is_empty )( gsub_lookups ) )
    183      goto Exit; /* nothing to do */
    184 
    185    FT_TRACE4(( "GSUB lookups (style `%s'):\n",
    186                af_style_names[style_class->style] ));
    187    FT_TRACE4(( " " ));
    188 
    189 #ifdef FT_DEBUG_LEVEL_TRACE
    190    count = 0;
    191 #endif
    192 
    193    gsub_glyphs = hb( set_create )();
    194    for ( idx = HB_SET_VALUE_INVALID; hb( set_next )( gsub_lookups, &idx ); )
    195    {
    196 #ifdef FT_DEBUG_LEVEL_TRACE
    197      FT_TRACE4(( " %u", idx ));
    198      count++;
    199 #endif
    200 
    201      /* get output coverage of GSUB feature */
    202      hb( ot_layout_lookup_collect_glyphs )( face,
    203                                             HB_OT_TAG_GSUB,
    204                                             idx,
    205                                             NULL,
    206                                             NULL,
    207                                             NULL,
    208                                             gsub_glyphs );
    209    }
    210 
    211 #ifdef FT_DEBUG_LEVEL_TRACE
    212    if ( !count )
    213      FT_TRACE4(( " (none)" ));
    214    FT_TRACE4(( "\n" ));
    215    FT_TRACE4(( "\n" ));
    216 #endif
    217 
    218    FT_TRACE4(( "GPOS lookups (style `%s'):\n",
    219                af_style_names[style_class->style] ));
    220    FT_TRACE4(( " " ));
    221 
    222    gpos_lookups = hb( set_create )();
    223    hb( ot_layout_collect_lookups )( face,
    224                                     HB_OT_TAG_GPOS,
    225                                     script_tags,
    226                                     NULL,
    227                                     coverage_tags,
    228                                     gpos_lookups );
    229 
    230 #ifdef FT_DEBUG_LEVEL_TRACE
    231    count = 0;
    232 #endif
    233 
    234    gpos_glyphs = hb( set_create )();
    235    for ( idx = HB_SET_VALUE_INVALID; hb( set_next )( gpos_lookups, &idx ); )
    236    {
    237 #ifdef FT_DEBUG_LEVEL_TRACE
    238      FT_TRACE4(( " %u", idx ));
    239      count++;
    240 #endif
    241 
    242      /* get input coverage of GPOS feature */
    243      hb( ot_layout_lookup_collect_glyphs )( face,
    244                                             HB_OT_TAG_GPOS,
    245                                             idx,
    246                                             NULL,
    247                                             gpos_glyphs,
    248                                             NULL,
    249                                             NULL );
    250    }
    251 
    252 #ifdef FT_DEBUG_LEVEL_TRACE
    253    if ( !count )
    254      FT_TRACE4(( " (none)" ));
    255    FT_TRACE4(( "\n" ));
    256    FT_TRACE4(( "\n" ));
    257 #endif
    258 
    259    /*
    260     * We now check whether we can construct blue zones, using glyphs
    261     * covered by the feature only.  In case there is not a single zone
    262     * (that is, not a single character is covered), we skip this coverage.
    263     *
    264     */
    265    if ( style_class->coverage != AF_COVERAGE_DEFAULT )
    266    {
    267      AF_Blue_Stringset         bss = style_class->blue_stringset;
    268      const AF_Blue_StringRec*  bs  = &af_blue_stringsets[bss];
    269 
    270      FT_Bool  found = 0;
    271 
    272 
    273      for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
    274      {
    275        const char*  p = &af_blue_strings[bs->string];
    276 
    277 
    278        while ( *p )
    279        {
    280          hb_codepoint_t  ch;
    281 
    282 
    283          GET_UTF8_CHAR( ch, p );
    284 
    285          for ( idx = HB_SET_VALUE_INVALID; hb( set_next )( gsub_lookups,
    286                                                            &idx ); )
    287          {
    288            hb_codepoint_t  gidx = FT_Get_Char_Index( globals->face, ch );
    289 
    290 
    291            if ( hb( ot_layout_lookup_would_substitute )( face, idx,
    292                                                          &gidx, 1, 1 ) )
    293            {
    294              found = 1;
    295              break;
    296            }
    297          }
    298        }
    299      }
    300 
    301      if ( !found )
    302      {
    303        FT_TRACE4(( "  no blue characters found; style skipped\n" ));
    304        goto Exit;
    305      }
    306    }
    307 
    308    /*
    309     * Various OpenType features might use the same glyphs at different
    310     * vertical positions; for example, superscript and subscript glyphs
    311     * could be the same.  However, the auto-hinter is completely
    312     * agnostic of OpenType features after the feature analysis has been
    313     * completed: The engine then simply receives a glyph index and returns a
    314     * hinted and usually rendered glyph.
    315     *
    316     * Consider the superscript feature of font `pala.ttf': Some of the
    317     * glyphs are `real', that is, they have a zero vertical offset, but
    318     * most of them are small caps glyphs shifted up to the superscript
    319     * position (that is, the `sups' feature is present in both the GSUB and
    320     * GPOS tables).  The code for blue zones computation actually uses a
    321     * feature's y offset so that the `real' glyphs get correct hints.  But
    322     * later on it is impossible to decide whether a glyph index belongs to,
    323     * say, the small caps or superscript feature.
    324     *
    325     * For this reason, we don't assign a style to a glyph if the current
    326     * feature covers the glyph in both the GSUB and the GPOS tables.  This
    327     * is quite a broad condition, assuming that
    328     *
    329     *   (a) glyphs that get used in multiple features are present in a
    330     *       feature without vertical shift,
    331     *
    332     * and
    333     *
    334     *   (b) a feature's GPOS data really moves the glyph vertically.
    335     *
    336     * Not fulfilling condition (a) makes a font larger; it would also
    337     * reduce the number of glyphs that could be addressed directly without
    338     * using OpenType features, so this assumption is rather strong.
    339     *
    340     * Condition (b) is much weaker, and there might be glyphs which get
    341     * missed.  However, the OpenType features we are going to handle are
    342     * primarily located in GSUB, and HarfBuzz doesn't provide an API to
    343     * directly get the necessary information from the GPOS table.  A
    344     * possible solution might be to directly parse the GPOS table to find
    345     * out whether a glyph gets shifted vertically, but this is something I
    346     * would like to avoid if not really necessary.
    347     *
    348     * Note that we don't follow this logic for the default coverage.
    349     * Complex scripts like Devanagari have mandatory GPOS features to
    350     * position many glyph elements, using mark-to-base or mark-to-ligature
    351     * tables; the number of glyphs missed due to condition (b) would be far
    352     * too large.
    353     *
    354     */
    355    if ( style_class->coverage != AF_COVERAGE_DEFAULT )
    356      hb( set_subtract )( gsub_glyphs, gpos_glyphs );
    357 
    358 #ifdef FT_DEBUG_LEVEL_TRACE
    359    FT_TRACE4(( "  glyphs without GPOS data (`*' means already assigned)" ));
    360    count = 0;
    361 #endif
    362 
    363    for ( idx = HB_SET_VALUE_INVALID; hb( set_next )( gsub_glyphs, &idx ); )
    364    {
    365 #ifdef FT_DEBUG_LEVEL_TRACE
    366      if ( !( count % 10 ) )
    367      {
    368        FT_TRACE4(( "\n" ));
    369        FT_TRACE4(( "   " ));
    370      }
    371 
    372      FT_TRACE4(( " %u", idx ));
    373      count++;
    374 #endif
    375 
    376      /* glyph indices returned by `hb_ot_layout_lookup_collect_glyphs' */
    377      /* can be arbitrary: some fonts use fake indices for processing   */
    378      /* internal to GSUB or GPOS, which is fully valid                 */
    379      if ( idx >= (hb_codepoint_t)globals->glyph_count )
    380        continue;
    381 
    382      if ( gstyles[idx] == AF_STYLE_UNASSIGNED )
    383        gstyles[idx] = (FT_UShort)style_class->style;
    384 #ifdef FT_DEBUG_LEVEL_TRACE
    385      else
    386        FT_TRACE4(( "*" ));
    387 #endif
    388    }
    389 
    390 #ifdef FT_DEBUG_LEVEL_TRACE
    391    if ( !count )
    392    {
    393      FT_TRACE4(( "\n" ));
    394      FT_TRACE4(( "    (none)" ));
    395    }
    396    FT_TRACE4(( "\n" ));
    397    FT_TRACE4(( "\n" ));
    398 #endif
    399 
    400  Exit:
    401    hb( set_destroy )( gsub_lookups );
    402    hb( set_destroy )( gsub_glyphs  );
    403    hb( set_destroy )( gpos_lookups );
    404    hb( set_destroy )( gpos_glyphs  );
    405 
    406    return FT_Err_Ok;
    407  }
    408 
    409 
    410  /* construct HarfBuzz features */
    411 #undef  COVERAGE
    412 #define COVERAGE( name, NAME, description,                \
    413                  tag1, tag2, tag3, tag4 )                \
    414          static const hb_feature_t  name ## _feature[] = \
    415          {                                               \
    416            {                                             \
    417              HB_TAG( tag1, tag2, tag3, tag4 ),           \
    418              1, 0, (unsigned int)-1                      \
    419            }                                             \
    420          };
    421 
    422 
    423 #include "afcover.h"
    424 
    425 
    426  /* define mapping between HarfBuzz features and AF_Coverage */
    427 #undef  COVERAGE
    428 #define COVERAGE( name, NAME, description, \
    429                  tag1, tag2, tag3, tag4 ) \
    430          name ## _feature,
    431 
    432 
    433  static const hb_feature_t*  features[] =
    434  {
    435 #include "afcover.h"
    436 
    437    NULL /* AF_COVERAGE_DEFAULT */
    438  };
    439 
    440 
    441  static void*
    442  af_shaper_buf_create_hb( AF_FaceGlobals  globals )
    443  {
    444    FT_UNUSED( globals );
    445 
    446    return (void*)hb( buffer_create )();
    447  }
    448 
    449 
    450  static void
    451  af_shaper_buf_destroy_hb( AF_FaceGlobals  globals,
    452                            void*           buf )
    453  {
    454    FT_UNUSED( globals );
    455 
    456    hb( buffer_destroy )( (hb_buffer_t*)buf );
    457  }
    458 
    459 
    460  static const char*
    461  af_shaper_get_cluster_hb( const char*      p,
    462                            AF_StyleMetrics  metrics,
    463                            void*            buf_,
    464                            unsigned int*    count )
    465  {
    466    AF_FaceGlobals  globals = metrics->globals;
    467 
    468    AF_StyleClass        style_class;
    469    const hb_feature_t*  feature;
    470    FT_Int               upem;
    471    const char*          q;
    472    int                  len;
    473 
    474    hb_buffer_t*    buf = (hb_buffer_t*)buf_;
    475    hb_font_t*      font;
    476    hb_codepoint_t  dummy;
    477 
    478    FT_UNUSED( globals );
    479 
    480 
    481    upem        = (FT_Int)metrics->globals->face->units_per_EM;
    482    style_class = metrics->style_class;
    483    feature     = features[style_class->coverage];
    484 
    485    font = metrics->globals->hb_font;
    486 
    487    /* we shape at a size of units per EM; this means font units */
    488    hb( font_set_scale )( font, upem, upem );
    489 
    490    while ( *p == ' ' )
    491      p++;
    492 
    493    /* count bytes up to next space (or end of buffer) */
    494    q = p;
    495    while ( !( *q == ' ' || *q == '\0' ) )
    496      GET_UTF8_CHAR( dummy, q );
    497    len = (int)( q - p );
    498 
    499    /* feed character(s) to the HarfBuzz buffer */
    500    hb( buffer_clear_contents )( buf );
    501    hb( buffer_add_utf8 )( buf, p, len, 0, len );
    502 
    503    /* we let HarfBuzz guess the script and writing direction */
    504    hb( buffer_guess_segment_properties )( buf );
    505 
    506    /* shape buffer, which means conversion from character codes to */
    507    /* glyph indices, possibly applying a feature                   */
    508    hb( shape )( font, buf, feature, feature ? 1 : 0 );
    509 
    510    if ( feature )
    511    {
    512      hb_buffer_t*  hb_buf = metrics->globals->hb_buf;
    513 
    514      unsigned int      gcount;
    515      hb_glyph_info_t*  ginfo;
    516 
    517      unsigned int      hb_gcount;
    518      hb_glyph_info_t*  hb_ginfo;
    519 
    520 
    521      /* we have to check whether applying a feature does actually change */
    522      /* glyph indices; otherwise the affected glyph or glyphs aren't     */
    523      /* available at all in the feature                                  */
    524 
    525      hb( buffer_clear_contents )( hb_buf );
    526      hb( buffer_add_utf8 )( hb_buf, p, len, 0, len );
    527      hb( buffer_guess_segment_properties )( hb_buf );
    528      hb( shape )( font, hb_buf, NULL, 0 );
    529 
    530      ginfo    = hb( buffer_get_glyph_infos )( buf, &gcount );
    531      hb_ginfo = hb( buffer_get_glyph_infos )( hb_buf, &hb_gcount );
    532 
    533      if ( gcount == hb_gcount )
    534      {
    535        unsigned int  i;
    536 
    537 
    538        for (i = 0; i < gcount; i++ )
    539          if ( ginfo[i].codepoint != hb_ginfo[i].codepoint )
    540            break;
    541 
    542        if ( i == gcount )
    543        {
    544          /* both buffers have identical glyph indices */
    545          hb( buffer_clear_contents )( buf );
    546        }
    547      }
    548    }
    549 
    550    *count = hb( buffer_get_length )( buf );
    551 
    552 #ifdef FT_DEBUG_LEVEL_TRACE
    553    if ( feature && *count > 1 )
    554      FT_TRACE1(( "af_shaper_get_cluster:"
    555                  " input character mapped to multiple glyphs\n" ));
    556 #endif
    557 
    558    return q;
    559  }
    560 
    561 
    562  static FT_ULong
    563  af_shaper_get_elem_hb( AF_StyleMetrics  metrics,
    564                         void*            buf_,
    565                         unsigned int     idx,
    566                         FT_Long*         advance,
    567                         FT_Long*         y_offset )
    568  {
    569    AF_FaceGlobals  globals = metrics->globals;
    570 
    571    hb_buffer_t*          buf = (hb_buffer_t*)buf_;
    572    hb_glyph_info_t*      ginfo;
    573    hb_glyph_position_t*  gpos;
    574    unsigned int          gcount;
    575 
    576    FT_UNUSED( globals );
    577 
    578 
    579    ginfo = hb( buffer_get_glyph_infos )( buf, &gcount );
    580    gpos  = hb( buffer_get_glyph_positions )( buf, &gcount );
    581 
    582    if ( idx >= gcount )
    583      return 0;
    584 
    585    if ( advance )
    586      *advance = gpos[idx].x_advance;
    587    if ( y_offset )
    588      *y_offset = gpos[idx].y_offset;
    589 
    590    return ginfo[idx].codepoint;
    591  }
    592 
    593 
    594 #endif /* FT_CONFIG_OPTION_USE_HARFBUZZ */
    595 
    596 
    597  static FT_Error
    598  af_shaper_get_coverage_nohb( AF_FaceGlobals  globals,
    599                               AF_StyleClass   style_class,
    600                               FT_UShort*      gstyles,
    601                               FT_Bool         default_script )
    602  {
    603    FT_UNUSED( globals );
    604    FT_UNUSED( style_class );
    605    FT_UNUSED( gstyles );
    606    FT_UNUSED( default_script );
    607 
    608    return FT_Err_Ok;
    609  }
    610 
    611 
    612  static void*
    613  af_shaper_buf_create_nohb( AF_FaceGlobals  globals )
    614  {
    615    FT_UNUSED( globals );
    616 
    617    return NULL;
    618  }
    619 
    620 
    621  static void
    622  af_shaper_buf_destroy_nohb( AF_FaceGlobals  globals,
    623                              void*    buf )
    624  {
    625    FT_UNUSED( globals );
    626    FT_UNUSED( buf );
    627  }
    628 
    629 
    630  static const char*
    631  af_shaper_get_cluster_nohb( const char*      p,
    632                              AF_StyleMetrics  metrics,
    633                              void*            buf_,
    634                              unsigned int*    count )
    635  {
    636    FT_Face    face      = metrics->globals->face;
    637    FT_ULong   ch, dummy = 0;
    638    FT_ULong*  buf       = (FT_ULong*)buf_;
    639 
    640 
    641    while ( *p == ' ' )
    642      p++;
    643 
    644    GET_UTF8_CHAR( ch, p );
    645 
    646    /* since we don't have an engine to handle clusters, */
    647    /* we scan the characters but return zero            */
    648    while ( !( *p == ' ' || *p == '\0' ) )
    649      GET_UTF8_CHAR( dummy, p );
    650 
    651    if ( dummy )
    652    {
    653      *buf   = 0;
    654      *count = 0;
    655    }
    656    else
    657    {
    658      *buf   = FT_Get_Char_Index( face, ch );
    659      *count = 1;
    660    }
    661 
    662    return p;
    663  }
    664 
    665 
    666  static FT_ULong
    667  af_shaper_get_elem_nohb( AF_StyleMetrics  metrics,
    668                           void*            buf_,
    669                           unsigned int     idx,
    670                           FT_Long*         advance,
    671                           FT_Long*         y_offset )
    672  {
    673    FT_Face   face        = metrics->globals->face;
    674    FT_ULong  glyph_index = *(FT_ULong*)buf_;
    675 
    676    FT_UNUSED( idx );
    677 
    678 
    679    if ( advance )
    680      FT_Get_Advance( face,
    681                      glyph_index,
    682                      FT_LOAD_NO_SCALE         |
    683                      FT_LOAD_NO_HINTING       |
    684                      FT_LOAD_IGNORE_TRANSFORM,
    685                      advance );
    686 
    687    if ( y_offset )
    688      *y_offset = 0;
    689 
    690    return glyph_index;
    691  }
    692 
    693 
    694  /********************************************************************/
    695 
    696  FT_Error
    697  af_shaper_get_coverage( AF_FaceGlobals  globals,
    698                          AF_StyleClass   style_class,
    699                          FT_UShort*      gstyles,
    700                          FT_Bool         default_script )
    701  {
    702 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
    703    if ( ft_hb_enabled( globals ) )
    704      return af_shaper_get_coverage_hb( globals,
    705                                        style_class,
    706                                        gstyles,
    707                                        default_script );
    708    else
    709 #endif
    710      return af_shaper_get_coverage_nohb( globals,
    711                                          style_class,
    712                                          gstyles,
    713                                          default_script );
    714  }
    715 
    716 
    717  void*
    718  af_shaper_buf_create( AF_FaceGlobals  globals )
    719  {
    720 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
    721    if ( ft_hb_enabled( globals ) )
    722      return af_shaper_buf_create_hb( globals );
    723    else
    724 #endif
    725      return af_shaper_buf_create_nohb( globals );
    726  }
    727 
    728 
    729  void
    730  af_shaper_buf_destroy( AF_FaceGlobals  globals,
    731                         void*           buf )
    732  {
    733 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
    734    if ( ft_hb_enabled( globals ) )
    735      af_shaper_buf_destroy_hb( globals, buf );
    736    else
    737 #endif
    738      af_shaper_buf_destroy_nohb( globals, buf );
    739  }
    740 
    741 
    742  const char*
    743  af_shaper_get_cluster( const char*      p,
    744                         AF_StyleMetrics  metrics,
    745                         void*            buf_,
    746                         unsigned int*    count )
    747  {
    748 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
    749    if ( ft_hb_enabled( metrics->globals ) )
    750      return af_shaper_get_cluster_hb( p, metrics, buf_, count );
    751    else
    752 #endif
    753      return af_shaper_get_cluster_nohb( p, metrics, buf_, count );
    754  }
    755 
    756 
    757  FT_ULong
    758  af_shaper_get_elem( AF_StyleMetrics  metrics,
    759                      void*            buf_,
    760                      unsigned int     idx,
    761                      FT_Long*         advance,
    762                      FT_Long*         y_offset )
    763  {
    764 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
    765    if ( ft_hb_enabled( metrics->globals ) )
    766      return af_shaper_get_elem_hb( metrics,
    767                                    buf_,
    768                                    idx,
    769                                    advance,
    770                                    y_offset );
    771 #endif
    772      return af_shaper_get_elem_nohb( metrics,
    773                                      buf_,
    774                                      idx,
    775                                      advance,
    776                                      y_offset );
    777  }
    778 
    779 
    780 /* END */