Home | History | Annotate | Download | only in autofit
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  hbshim.c                                                               */
      4 /*                                                                         */
      5 /*    HarfBuzz interface for accessing OpenType features (body).           */
      6 /*                                                                         */
      7 /*  Copyright 2013, 2014 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 "afglobal.h"
     22 #include "aftypes.h"
     23 #include "hbshim.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  trace_afharfbuzz
     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, sc1, sc2, sc3 )  h,
     90 
     91 
     92   static const hb_script_t  scripts[] =
     93   {
     94 #include "afscript.h"
     95   };
     96 
     97 
     98   FT_Error
     99   af_get_coverage( AF_FaceGlobals  globals,
    100                    AF_StyleClass   style_class,
    101                    FT_Byte*        gstyles )
    102   {
    103     hb_face_t*  face;
    104 
    105     hb_set_t*  gsub_lookups;  /* GSUB lookups for a given script */
    106     hb_set_t*  gsub_glyphs;   /* glyphs covered by GSUB lookups  */
    107     hb_set_t*  gpos_lookups;  /* GPOS lookups for a given script */
    108     hb_set_t*  gpos_glyphs;   /* glyphs covered by GPOS lookups  */
    109 
    110     hb_script_t      script;
    111     const hb_tag_t*  coverage_tags;
    112     hb_tag_t         script_tags[] = { HB_TAG_NONE,
    113                                        HB_TAG_NONE,
    114                                        HB_TAG_NONE,
    115                                        HB_TAG_NONE };
    116 
    117     hb_codepoint_t  idx;
    118 #ifdef FT_DEBUG_LEVEL_TRACE
    119     int             count;
    120 #endif
    121 
    122 
    123     if ( !globals || !style_class || !gstyles )
    124       return FT_THROW( Invalid_Argument );
    125 
    126     face = hb_font_get_face( globals->hb_font );
    127 
    128     gsub_lookups = hb_set_create();
    129     gsub_glyphs  = hb_set_create();
    130     gpos_lookups = hb_set_create();
    131     gpos_glyphs  = hb_set_create();
    132 
    133     coverage_tags = coverages[style_class->coverage];
    134     script        = scripts[style_class->script];
    135 
    136     /* Convert a HarfBuzz script tag into the corresponding OpenType */
    137     /* tag or tags -- some Indic scripts like Devanagari have an old */
    138     /* and a new set of features.                                    */
    139     hb_ot_tags_from_script( script,
    140                             &script_tags[0],
    141                             &script_tags[1] );
    142 
    143     /* `hb_ot_tags_from_script' usually returns HB_OT_TAG_DEFAULT_SCRIPT */
    144     /* as the second tag.  We change that to HB_TAG_NONE except for the  */
    145     /* default script.                                                   */
    146     if ( style_class->script == globals->module->default_script &&
    147          style_class->coverage == AF_COVERAGE_DEFAULT           )
    148     {
    149       if ( script_tags[0] == HB_TAG_NONE )
    150         script_tags[0] = HB_OT_TAG_DEFAULT_SCRIPT;
    151       else
    152       {
    153         if ( script_tags[1] == HB_TAG_NONE )
    154           script_tags[1] = HB_OT_TAG_DEFAULT_SCRIPT;
    155         else if ( script_tags[1] != HB_OT_TAG_DEFAULT_SCRIPT )
    156           script_tags[2] = HB_OT_TAG_DEFAULT_SCRIPT;
    157       }
    158     }
    159     else
    160     {
    161       if ( script_tags[1] == HB_OT_TAG_DEFAULT_SCRIPT )
    162         script_tags[1] = HB_TAG_NONE;
    163     }
    164 
    165     hb_ot_layout_collect_lookups( face,
    166                                   HB_OT_TAG_GSUB,
    167                                   script_tags,
    168                                   NULL,
    169                                   coverage_tags,
    170                                   gsub_lookups );
    171 
    172     if ( hb_set_is_empty( gsub_lookups ) )
    173       goto Exit; /* nothing to do */
    174 
    175     hb_ot_layout_collect_lookups( face,
    176                                   HB_OT_TAG_GPOS,
    177                                   script_tags,
    178                                   NULL,
    179                                   coverage_tags,
    180                                   gpos_lookups );
    181 
    182     FT_TRACE4(( "GSUB lookups (style `%s'):\n"
    183                 " ",
    184                 af_style_names[style_class->style] ));
    185 
    186 #ifdef FT_DEBUG_LEVEL_TRACE
    187     count = 0;
    188 #endif
    189 
    190     for ( idx = -1; hb_set_next( gsub_lookups, &idx ); )
    191     {
    192 #ifdef FT_DEBUG_LEVEL_TRACE
    193       FT_TRACE4(( " %d", idx ));
    194       count++;
    195 #endif
    196 
    197       /* get output coverage of GSUB feature */
    198       hb_ot_layout_lookup_collect_glyphs( face,
    199                                           HB_OT_TAG_GSUB,
    200                                           idx,
    201                                           NULL,
    202                                           NULL,
    203                                           NULL,
    204                                           gsub_glyphs );
    205     }
    206 
    207 #ifdef FT_DEBUG_LEVEL_TRACE
    208     if ( !count )
    209       FT_TRACE4(( " (none)" ));
    210     FT_TRACE4(( "\n\n" ));
    211 #endif
    212 
    213     FT_TRACE4(( "GPOS lookups (style `%s'):\n"
    214                 " ",
    215                 af_style_names[style_class->style] ));
    216 
    217 #ifdef FT_DEBUG_LEVEL_TRACE
    218     count = 0;
    219 #endif
    220 
    221     for ( idx = -1; hb_set_next( gpos_lookups, &idx ); )
    222     {
    223 #ifdef FT_DEBUG_LEVEL_TRACE
    224       FT_TRACE4(( " %d", idx ));
    225       count++;
    226 #endif
    227 
    228       /* get input coverage of GPOS feature */
    229       hb_ot_layout_lookup_collect_glyphs( face,
    230                                           HB_OT_TAG_GPOS,
    231                                           idx,
    232                                           NULL,
    233                                           gpos_glyphs,
    234                                           NULL,
    235                                           NULL );
    236     }
    237 
    238 #ifdef FT_DEBUG_LEVEL_TRACE
    239     if ( !count )
    240       FT_TRACE4(( " (none)" ));
    241     FT_TRACE4(( "\n\n" ));
    242 #endif
    243 
    244     /*
    245      * We now check whether we can construct blue zones, using glyphs
    246      * covered by the feature only.  In case there is not a single zone
    247      * (this is, not a single character is covered), we skip this coverage.
    248      *
    249      */
    250     if ( style_class->coverage != AF_COVERAGE_DEFAULT )
    251     {
    252       AF_Blue_Stringset         bss = style_class->blue_stringset;
    253       const AF_Blue_StringRec*  bs  = &af_blue_stringsets[bss];
    254 
    255       FT_Bool  found = 0;
    256 
    257 
    258       for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
    259       {
    260         const char*  p = &af_blue_strings[bs->string];
    261 
    262 
    263         while ( *p )
    264         {
    265           hb_codepoint_t  ch;
    266 
    267 
    268           GET_UTF8_CHAR( ch, p );
    269 
    270           for ( idx = -1; hb_set_next( gsub_lookups, &idx ); )
    271           {
    272             hb_codepoint_t  gidx = FT_Get_Char_Index( globals->face, ch );
    273 
    274 
    275             if ( hb_ot_layout_lookup_would_substitute( face, idx,
    276                                                        &gidx, 1, 1 ) )
    277             {
    278               found = 1;
    279               break;
    280             }
    281           }
    282         }
    283       }
    284 
    285       if ( !found )
    286       {
    287         FT_TRACE4(( "  no blue characters found; style skipped\n" ));
    288         goto Exit;
    289       }
    290     }
    291 
    292     /*
    293      * Various OpenType features might use the same glyphs at different
    294      * vertical positions; for example, superscript and subscript glyphs
    295      * could be the same.  However, the auto-hinter is completely
    296      * agnostic of OpenType features after the feature analysis has been
    297      * completed: The engine then simply receives a glyph index and returns a
    298      * hinted and usually rendered glyph.
    299      *
    300      * Consider the superscript feature of font `pala.ttf': Some of the
    301      * glyphs are `real', this is, they have a zero vertical offset, but
    302      * most of them are small caps glyphs shifted up to the superscript
    303      * position (this is, the `sups' feature is present in both the GSUB and
    304      * GPOS tables).  The code for blue zones computation actually uses a
    305      * feature's y offset so that the `real' glyphs get correct hints.  But
    306      * later on it is impossible to decide whether a glyph index belongs to,
    307      * say, the small caps or superscript feature.
    308      *
    309      * For this reason, we don't assign a style to a glyph if the current
    310      * feature covers the glyph in both the GSUB and the GPOS tables.  This
    311      * is quite a broad condition, assuming that
    312      *
    313      *   (a) glyphs that get used in multiple features are present in a
    314      *       feature without vertical shift,
    315      *
    316      * and
    317      *
    318      *   (b) a feature's GPOS data really moves the glyph vertically.
    319      *
    320      * Not fulfilling condition (a) makes a font larger; it would also
    321      * reduce the number of glyphs that could be addressed directly without
    322      * using OpenType features, so this assumption is rather strong.
    323      *
    324      * Condition (b) is much weaker, and there might be glyphs which get
    325      * missed.  However, the OpenType features we are going to handle are
    326      * primarily located in GSUB, and HarfBuzz doesn't provide an API to
    327      * directly get the necessary information from the GPOS table.  A
    328      * possible solution might be to directly parse the GPOS table to find
    329      * out whether a glyph gets shifted vertically, but this is something I
    330      * would like to avoid if not really necessary.
    331      *
    332      * Note that we don't follow this logic for the default coverage.
    333      * Complex scripts like Devanagari have mandatory GPOS features to
    334      * position many glyph elements, using mark-to-base or mark-to-ligature
    335      * tables; the number of glyphs missed due to condition (b) would be far
    336      * too large.
    337      *
    338      */
    339     if ( style_class->coverage != AF_COVERAGE_DEFAULT )
    340       hb_set_subtract( gsub_glyphs, gpos_glyphs );
    341 
    342 #ifdef FT_DEBUG_LEVEL_TRACE
    343     FT_TRACE4(( "  glyphs without GPOS data (`*' means already assigned)" ));
    344     count = 0;
    345 #endif
    346 
    347     for ( idx = -1; hb_set_next( gsub_glyphs, &idx ); )
    348     {
    349 #ifdef FT_DEBUG_LEVEL_TRACE
    350       if ( !( count % 10 ) )
    351         FT_TRACE4(( "\n"
    352                     "   " ));
    353 
    354       FT_TRACE4(( " %d", idx ));
    355       count++;
    356 #endif
    357 
    358       /* glyph indices returned by `hb_ot_layout_lookup_collect_glyphs' */
    359       /* can be arbitrary: some fonts use fake indices for processing   */
    360       /* internal to GSUB or GPOS, which is fully valid                 */
    361       if ( idx >= (hb_codepoint_t)globals->glyph_count )
    362         continue;
    363 
    364       if ( gstyles[idx] == AF_STYLE_UNASSIGNED )
    365         gstyles[idx] = (FT_Byte)style_class->style;
    366 #ifdef FT_DEBUG_LEVEL_TRACE
    367       else
    368         FT_TRACE4(( "*" ));
    369 #endif
    370     }
    371 
    372 #ifdef FT_DEBUG_LEVEL_TRACE
    373     if ( !count )
    374       FT_TRACE4(( "\n"
    375                   "    (none)" ));
    376     FT_TRACE4(( "\n\n" ));
    377 #endif
    378 
    379   Exit:
    380     hb_set_destroy( gsub_lookups );
    381     hb_set_destroy( gsub_glyphs  );
    382     hb_set_destroy( gpos_lookups );
    383     hb_set_destroy( gpos_glyphs  );
    384 
    385     return FT_Err_Ok;
    386   }
    387 
    388 
    389   /* construct HarfBuzz features */
    390 #undef  COVERAGE
    391 #define COVERAGE( name, NAME, description,                \
    392                   tag1, tag2, tag3, tag4 )                \
    393           static const hb_feature_t  name ## _feature[] = \
    394           {                                               \
    395             {                                             \
    396               HB_TAG( tag1, tag2, tag3, tag4 ),           \
    397               1, 0, (unsigned int)-1                      \
    398             }                                             \
    399           };
    400 
    401 
    402 #include "afcover.h"
    403 
    404 
    405   /* define mapping between HarfBuzz features and AF_Coverage */
    406 #undef  COVERAGE
    407 #define COVERAGE( name, NAME, description, \
    408                   tag1, tag2, tag3, tag4 ) \
    409           name ## _feature,
    410 
    411 
    412   static const hb_feature_t*  features[] =
    413   {
    414 #include "afcover.h"
    415 
    416     NULL /* AF_COVERAGE_DEFAULT */
    417   };
    418 
    419 
    420   FT_Error
    421   af_get_char_index( AF_StyleMetrics  metrics,
    422                      FT_ULong         charcode,
    423                      FT_ULong        *codepoint,
    424                      FT_Long         *y_offset )
    425   {
    426     AF_StyleClass  style_class;
    427 
    428     const hb_feature_t*  feature;
    429 
    430     FT_ULong  in_idx, out_idx;
    431 
    432 
    433     if ( !metrics )
    434       return FT_THROW( Invalid_Argument );
    435 
    436     in_idx = FT_Get_Char_Index( metrics->globals->face, charcode );
    437 
    438     style_class = metrics->style_class;
    439 
    440     feature = features[style_class->coverage];
    441 
    442     if ( feature )
    443     {
    444       FT_UInt  upem = metrics->globals->face->units_per_EM;
    445 
    446       hb_font_t*    font = metrics->globals->hb_font;
    447       hb_buffer_t*  buf  = hb_buffer_create();
    448 
    449       uint32_t  c = (uint32_t)charcode;
    450 
    451       hb_glyph_info_t*      ginfo;
    452       hb_glyph_position_t*  gpos;
    453       unsigned int          gcount;
    454 
    455 
    456       /* we shape at a size of units per EM; this means font units */
    457       hb_font_set_scale( font, upem, upem );
    458 
    459       /* XXX: is this sufficient for a single character of any script? */
    460       hb_buffer_set_direction( buf, HB_DIRECTION_LTR );
    461       hb_buffer_set_script( buf, scripts[style_class->script] );
    462 
    463       /* we add one character to `buf' ... */
    464       hb_buffer_add_utf32( buf, &c, 1, 0, 1 );
    465 
    466       /* ... and apply one feature */
    467       hb_shape( font, buf, feature, 1 );
    468 
    469       ginfo = hb_buffer_get_glyph_infos( buf, &gcount );
    470       gpos  = hb_buffer_get_glyph_positions( buf, &gcount );
    471 
    472       out_idx = ginfo[0].codepoint;
    473 
    474       /* getting the same index indicates no substitution,         */
    475       /* which means that the glyph isn't available in the feature */
    476       if ( in_idx == out_idx )
    477       {
    478         *codepoint = 0;
    479         *y_offset  = 0;
    480       }
    481       else
    482       {
    483         *codepoint = out_idx;
    484         *y_offset  = gpos[0].y_offset;
    485       }
    486 
    487       hb_buffer_destroy( buf );
    488 
    489 #ifdef FT_DEBUG_LEVEL_TRACE
    490       if ( gcount > 1 )
    491         FT_TRACE1(( "af_get_char_index:"
    492                     " input character mapped to multiple glyphs\n" ));
    493 #endif
    494     }
    495     else
    496     {
    497       *codepoint = in_idx;
    498       *y_offset  = 0;
    499     }
    500 
    501     return FT_Err_Ok;
    502   }
    503 
    504 
    505 #else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
    506 
    507 
    508   FT_Error
    509   af_get_coverage( AF_FaceGlobals  globals,
    510                    AF_StyleClass   style_class,
    511                    FT_Byte*        gstyles )
    512   {
    513     FT_UNUSED( globals );
    514     FT_UNUSED( style_class );
    515     FT_UNUSED( gstyles );
    516 
    517     return FT_Err_Ok;
    518   }
    519 
    520 
    521   FT_Error
    522   af_get_char_index( AF_StyleMetrics  metrics,
    523                      FT_ULong         charcode,
    524                      FT_ULong        *codepoint,
    525                      FT_Long         *y_offset )
    526   {
    527     FT_Face  face;
    528 
    529 
    530     if ( !metrics )
    531       return FT_THROW( Invalid_Argument );
    532 
    533     face = metrics->globals->face;
    534 
    535     *codepoint = FT_Get_Char_Index( face, charcode );
    536     *y_offset  = 0;
    537 
    538     return FT_Err_Ok;
    539   }
    540 
    541 
    542 #endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
    543 
    544 
    545 /* END */
    546