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