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