• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************/
2 /*                                                                         */
3 /*  afshaper.c                                                             */
4 /*                                                                         */
5 /*    HarfBuzz interface for accessing OpenType features (body).           */
6 /*                                                                         */
7 /*  Copyright 2013-2015 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 "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  trace_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, ss )  h,
90 
91 
92   static const hb_script_t  scripts[] =
93   {
94 #include "afscript.h"
95   };
96 
97 
98   FT_Error
af_shaper_get_coverage(AF_FaceGlobals globals,AF_StyleClass style_class,FT_UShort * gstyles)99   af_shaper_get_coverage( AF_FaceGlobals  globals,
100                           AF_StyleClass   style_class,
101                           FT_UShort*      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 = HB_SET_VALUE_INVALID; 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 = HB_SET_VALUE_INVALID; 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 = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups,
271                                                          &idx ); )
272           {
273             hb_codepoint_t  gidx = FT_Get_Char_Index( globals->face, ch );
274 
275 
276             if ( hb_ot_layout_lookup_would_substitute( face, idx,
277                                                        &gidx, 1, 1 ) )
278             {
279               found = 1;
280               break;
281             }
282           }
283         }
284       }
285 
286       if ( !found )
287       {
288         FT_TRACE4(( "  no blue characters found; style skipped\n" ));
289         goto Exit;
290       }
291     }
292 
293     /*
294      * Various OpenType features might use the same glyphs at different
295      * vertical positions; for example, superscript and subscript glyphs
296      * could be the same.  However, the auto-hinter is completely
297      * agnostic of OpenType features after the feature analysis has been
298      * completed: The engine then simply receives a glyph index and returns a
299      * hinted and usually rendered glyph.
300      *
301      * Consider the superscript feature of font `pala.ttf': Some of the
302      * glyphs are `real', this is, they have a zero vertical offset, but
303      * most of them are small caps glyphs shifted up to the superscript
304      * position (this is, the `sups' feature is present in both the GSUB and
305      * GPOS tables).  The code for blue zones computation actually uses a
306      * feature's y offset so that the `real' glyphs get correct hints.  But
307      * later on it is impossible to decide whether a glyph index belongs to,
308      * say, the small caps or superscript feature.
309      *
310      * For this reason, we don't assign a style to a glyph if the current
311      * feature covers the glyph in both the GSUB and the GPOS tables.  This
312      * is quite a broad condition, assuming that
313      *
314      *   (a) glyphs that get used in multiple features are present in a
315      *       feature without vertical shift,
316      *
317      * and
318      *
319      *   (b) a feature's GPOS data really moves the glyph vertically.
320      *
321      * Not fulfilling condition (a) makes a font larger; it would also
322      * reduce the number of glyphs that could be addressed directly without
323      * using OpenType features, so this assumption is rather strong.
324      *
325      * Condition (b) is much weaker, and there might be glyphs which get
326      * missed.  However, the OpenType features we are going to handle are
327      * primarily located in GSUB, and HarfBuzz doesn't provide an API to
328      * directly get the necessary information from the GPOS table.  A
329      * possible solution might be to directly parse the GPOS table to find
330      * out whether a glyph gets shifted vertically, but this is something I
331      * would like to avoid if not really necessary.
332      *
333      * Note that we don't follow this logic for the default coverage.
334      * Complex scripts like Devanagari have mandatory GPOS features to
335      * position many glyph elements, using mark-to-base or mark-to-ligature
336      * tables; the number of glyphs missed due to condition (b) would be far
337      * too large.
338      *
339      */
340     if ( style_class->coverage != AF_COVERAGE_DEFAULT )
341       hb_set_subtract( gsub_glyphs, gpos_glyphs );
342 
343 #ifdef FT_DEBUG_LEVEL_TRACE
344     FT_TRACE4(( "  glyphs without GPOS data (`*' means already assigned)" ));
345     count = 0;
346 #endif
347 
348     for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_glyphs, &idx ); )
349     {
350 #ifdef FT_DEBUG_LEVEL_TRACE
351       if ( !( count % 10 ) )
352         FT_TRACE4(( "\n"
353                     "   " ));
354 
355       FT_TRACE4(( " %d", idx ));
356       count++;
357 #endif
358 
359       /* glyph indices returned by `hb_ot_layout_lookup_collect_glyphs' */
360       /* can be arbitrary: some fonts use fake indices for processing   */
361       /* internal to GSUB or GPOS, which is fully valid                 */
362       if ( idx >= (hb_codepoint_t)globals->glyph_count )
363         continue;
364 
365       if ( gstyles[idx] == AF_STYLE_UNASSIGNED )
366         gstyles[idx] = (FT_UShort)style_class->style;
367 #ifdef FT_DEBUG_LEVEL_TRACE
368       else
369         FT_TRACE4(( "*" ));
370 #endif
371     }
372 
373 #ifdef FT_DEBUG_LEVEL_TRACE
374     if ( !count )
375       FT_TRACE4(( "\n"
376                   "    (none)" ));
377     FT_TRACE4(( "\n\n" ));
378 #endif
379 
380   Exit:
381     hb_set_destroy( gsub_lookups );
382     hb_set_destroy( gsub_glyphs  );
383     hb_set_destroy( gpos_lookups );
384     hb_set_destroy( gpos_glyphs  );
385 
386     return FT_Err_Ok;
387   }
388 
389 
390   /* construct HarfBuzz features */
391 #undef  COVERAGE
392 #define COVERAGE( name, NAME, description,                \
393                   tag1, tag2, tag3, tag4 )                \
394           static const hb_feature_t  name ## _feature[] = \
395           {                                               \
396             {                                             \
397               HB_TAG( tag1, tag2, tag3, tag4 ),           \
398               1, 0, (unsigned int)-1                      \
399             }                                             \
400           };
401 
402 
403 #include "afcover.h"
404 
405 
406   /* define mapping between HarfBuzz features and AF_Coverage */
407 #undef  COVERAGE
408 #define COVERAGE( name, NAME, description, \
409                   tag1, tag2, tag3, tag4 ) \
410           name ## _feature,
411 
412 
413   static const hb_feature_t*  features[] =
414   {
415 #include "afcover.h"
416 
417     NULL /* AF_COVERAGE_DEFAULT */
418   };
419 
420 
421   void*
af_shaper_buf_create(FT_Face face)422   af_shaper_buf_create( FT_Face  face )
423   {
424     FT_UNUSED( face );
425 
426     return (void*)hb_buffer_create();
427   }
428 
429 
430   void
af_shaper_buf_destroy(FT_Face face,void * buf)431   af_shaper_buf_destroy( FT_Face  face,
432                          void*    buf )
433   {
434     FT_UNUSED( face );
435 
436     hb_buffer_destroy( (hb_buffer_t*)buf );
437   }
438 
439 
440   const char*
af_shaper_get_cluster(const char * p,AF_StyleMetrics metrics,void * buf_,unsigned int * count)441   af_shaper_get_cluster( const char*      p,
442                          AF_StyleMetrics  metrics,
443                          void*            buf_,
444                          unsigned int*    count )
445   {
446     AF_StyleClass        style_class;
447     const hb_feature_t*  feature;
448     FT_Int               upem;
449     const char*          q;
450     int                  len;
451 
452     hb_buffer_t*    buf = (hb_buffer_t*)buf_;
453     hb_font_t*      font;
454     hb_codepoint_t  dummy;
455 
456 
457     upem        = (FT_Int)metrics->globals->face->units_per_EM;
458     style_class = metrics->style_class;
459     feature     = features[style_class->coverage];
460 
461     font = metrics->globals->hb_font;
462 
463     /* we shape at a size of units per EM; this means font units */
464     hb_font_set_scale( font, upem, upem );
465 
466     while ( *p == ' ' )
467       p++;
468 
469     /* count bytes up to next space (or end of buffer) */
470     q = p;
471     while ( !( *q == ' ' || *q == '\0' ) )
472       GET_UTF8_CHAR( dummy, q );
473     len = (int)( q - p );
474 
475     /* feed character(s) to the HarfBuzz buffer */
476     hb_buffer_clear_contents( buf );
477     hb_buffer_add_utf8( buf, p, len, 0, len );
478 
479     /* we let HarfBuzz guess the script and writing direction */
480     hb_buffer_guess_segment_properties( buf );
481 
482     /* shape buffer, which means conversion from character codes to */
483     /* glyph indices, possibly applying a feature                   */
484     hb_shape( font, buf, feature, feature ? 1 : 0 );
485 
486     if ( feature )
487     {
488       hb_buffer_t*  hb_buf = metrics->globals->hb_buf;
489 
490       unsigned int      gcount;
491       hb_glyph_info_t*  ginfo;
492 
493       unsigned int      hb_gcount;
494       hb_glyph_info_t*  hb_ginfo;
495 
496 
497       /* we have to check whether applying a feature does actually change */
498       /* glyph indices; otherwise the affected glyph or glyphs aren't     */
499       /* available at all in the feature                                  */
500 
501       hb_buffer_clear_contents( hb_buf );
502       hb_buffer_add_utf8( hb_buf, p, len, 0, len );
503       hb_buffer_guess_segment_properties( hb_buf );
504       hb_shape( font, hb_buf, NULL, 0 );
505 
506       ginfo    = hb_buffer_get_glyph_infos( buf, &gcount );
507       hb_ginfo = hb_buffer_get_glyph_infos( hb_buf, &hb_gcount );
508 
509       if ( gcount == hb_gcount )
510       {
511         unsigned int  i;
512 
513 
514         for (i = 0; i < gcount; i++ )
515           if ( ginfo[i].codepoint != hb_ginfo[i].codepoint )
516             break;
517 
518         if ( i == gcount )
519         {
520           /* both buffers have identical glyph indices */
521           hb_buffer_clear_contents( buf );
522         }
523       }
524     }
525 
526     *count = hb_buffer_get_length( buf );
527 
528 #ifdef FT_DEBUG_LEVEL_TRACE
529     if ( feature && *count > 1 )
530       FT_TRACE1(( "af_shaper_get_cluster:"
531                   " input character mapped to multiple glyphs\n" ));
532 #endif
533 
534     return q;
535   }
536 
537 
538   FT_ULong
af_shaper_get_elem(AF_StyleMetrics metrics,void * buf_,unsigned int idx,FT_Long * advance,FT_Long * y_offset)539   af_shaper_get_elem( AF_StyleMetrics  metrics,
540                       void*            buf_,
541                       unsigned int     idx,
542                       FT_Long*         advance,
543                       FT_Long*         y_offset )
544   {
545     hb_buffer_t*          buf = (hb_buffer_t*)buf_;
546     hb_glyph_info_t*      ginfo;
547     hb_glyph_position_t*  gpos;
548     unsigned int          gcount;
549 
550     FT_UNUSED( metrics );
551 
552 
553     ginfo = hb_buffer_get_glyph_infos( buf, &gcount );
554     gpos  = hb_buffer_get_glyph_positions( buf, &gcount );
555 
556     if ( idx >= gcount )
557       return 0;
558 
559     if ( advance )
560       *advance = gpos[idx].x_advance;
561     if ( y_offset )
562       *y_offset = gpos[idx].y_offset;
563 
564     return ginfo[idx].codepoint;
565   }
566 
567 
568 #else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
569 
570 
571   FT_Error
af_shaper_get_coverage(AF_FaceGlobals globals,AF_StyleClass style_class,FT_UShort * gstyles)572   af_shaper_get_coverage( AF_FaceGlobals  globals,
573                           AF_StyleClass   style_class,
574                           FT_UShort*      gstyles )
575   {
576     FT_UNUSED( globals );
577     FT_UNUSED( style_class );
578     FT_UNUSED( gstyles );
579 
580     return FT_Err_Ok;
581   }
582 
583 
584   void*
af_shaper_buf_create(FT_Face face)585   af_shaper_buf_create( FT_Face  face )
586   {
587     FT_Error   error;
588     FT_Memory  memory = face->memory;
589     FT_ULong*  buf;
590 
591 
592     FT_MEM_ALLOC( buf, sizeof ( FT_ULong ) );
593 
594     return (void*)buf;
595   }
596 
597 
598   void
af_shaper_buf_destroy(FT_Face face,void * buf)599   af_shaper_buf_destroy( FT_Face  face,
600                          void*    buf )
601   {
602     FT_Memory  memory = face->memory;
603 
604 
605     FT_FREE( buf );
606   }
607 
608 
609   const char*
af_shaper_get_cluster(const char * p,AF_StyleMetrics metrics,void * buf_,unsigned int * count)610   af_shaper_get_cluster( const char*      p,
611                          AF_StyleMetrics  metrics,
612                          void*            buf_,
613                          unsigned int*    count )
614   {
615     FT_Face    face      = metrics->globals->face;
616     FT_ULong   ch, dummy = 0;
617     FT_ULong*  buf       = (FT_ULong*)buf_;
618 
619 
620     while ( *p == ' ' )
621       p++;
622 
623     GET_UTF8_CHAR( ch, p );
624 
625     /* since we don't have an engine to handle clusters, */
626     /* we scan the characters but return zero            */
627     while ( !( *p == ' ' || *p == '\0' ) )
628       GET_UTF8_CHAR( dummy, p );
629 
630     if ( dummy )
631     {
632       *buf   = 0;
633       *count = 0;
634     }
635     else
636     {
637       *buf   = FT_Get_Char_Index( face, ch );
638       *count = 1;
639     }
640 
641     return p;
642   }
643 
644 
645   FT_ULong
af_shaper_get_elem(AF_StyleMetrics metrics,void * buf_,unsigned int idx,FT_Long * advance,FT_Long * y_offset)646   af_shaper_get_elem( AF_StyleMetrics  metrics,
647                       void*            buf_,
648                       unsigned int     idx,
649                       FT_Long*         advance,
650                       FT_Long*         y_offset )
651   {
652     FT_Face   face        = metrics->globals->face;
653     FT_ULong  glyph_index = *(FT_ULong*)buf_;
654 
655     FT_UNUSED( idx );
656 
657 
658     if ( advance )
659       FT_Get_Advance( face,
660                       glyph_index,
661                       FT_LOAD_NO_SCALE         |
662                       FT_LOAD_NO_HINTING       |
663                       FT_LOAD_IGNORE_TRANSFORM,
664                       advance );
665 
666     if ( y_offset )
667       *y_offset = 0;
668 
669     return glyph_index;
670   }
671 
672 
673 #endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
674 
675 
676 /* END */
677