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_Error   error;
 595     FT_Memory  memory = face->memory;
 596     FT_ULong*  buf;
 597 
 598 
 599     FT_MEM_ALLOC( buf, sizeof ( FT_ULong ) );
 600 
 601     return (void*)buf;
 602   }
 603 
 604 
 605   void
 606   af_shaper_buf_destroy( FT_Face  face,
 607                          void*    buf )
 608   {
 609     FT_Memory  memory = face->memory;
 610 
 611 
 612     FT_FREE( buf );
 613   }
 614 
 615 
 616   const char*
 617   af_shaper_get_cluster( const char*      p,
 618                          AF_StyleMetrics  metrics,
 619                          void*            buf_,
 620                          unsigned int*    count )
 621   {
 622     FT_Face    face      = metrics->globals->face;
 623     FT_ULong   ch, dummy = 0;
 624     FT_ULong*  buf       = (FT_ULong*)buf_;
 625 
 626 
 627     while ( *p == ' ' )
 628       p++;
 629 
 630     GET_UTF8_CHAR( ch, p );
 631 
 632     /* since we don't have an engine to handle clusters, */
 633     /* we scan the characters but return zero            */
 634     while ( !( *p == ' ' || *p == '\0' ) )
 635       GET_UTF8_CHAR( dummy, p );
 636 
 637     if ( dummy )
 638     {
 639       *buf   = 0;
 640       *count = 0;
 641     }
 642     else
 643     {
 644       *buf   = FT_Get_Char_Index( face, ch );
 645       *count = 1;
 646     }
 647 
 648     return p;
 649   }
 650 
 651 
 652   FT_ULong
 653   af_shaper_get_elem( AF_StyleMetrics  metrics,
 654                       void*            buf_,
 655                       unsigned int     idx,
 656                       FT_Long*         advance,
 657                       FT_Long*         y_offset )
 658   {
 659     FT_Face   face        = metrics->globals->face;
 660     FT_ULong  glyph_index = *(FT_ULong*)buf_;
 661 
 662     FT_UNUSED( idx );
 663 
 664 
 665     if ( advance )
 666       FT_Get_Advance( face,
 667                       glyph_index,
 668                       FT_LOAD_NO_SCALE         |
 669                       FT_LOAD_NO_HINTING       |
 670                       FT_LOAD_IGNORE_TRANSFORM,
 671                       advance );
 672 
 673     if ( y_offset )
 674       *y_offset = 0;
 675 
 676     return glyph_index;
 677   }
 678 
 679 
 680 #endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
 681 
 682 
 683 /* END */