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