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