1 /***************************************************************************/
5 /* HarfBuzz interface for accessing OpenType features (body). */
7 /* Copyright 2013, 2014 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
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. */
16 /***************************************************************************/
20 #include FT_FREETYPE_H
25 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
28 /*************************************************************************/
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. */
35 #define FT_COMPONENT trace_afharfbuzz
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.
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.
48 * 2. Create glyph ID sets from the corresponding lookup sets.
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.
58 /* load coverage tags */
60 #define COVERAGE( name, NAME, description, \
61 tag1, tag2, tag3, tag4 ) \
62 static const hb_tag_t name ## _coverage[] = \
64 HB_TAG( tag1, tag2, tag3, tag4 ), \
72 /* define mapping between coverage tags and AF_Coverage */
74 #define COVERAGE( name, NAME, description, \
75 tag1, tag2, tag3, tag4 ) \
79 static const hb_tag_t* coverages[] =
83 NULL /* AF_COVERAGE_DEFAULT */
87 /* load HarfBuzz script tags */
89 #define SCRIPT( s, S, d, h, sc1, sc2, sc3 ) h,
92 static const hb_script_t scripts[] =
99 af_get_coverage( AF_FaceGlobals globals,
100 AF_StyleClass style_class,
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 */
111 const hb_tag_t* coverage_tags;
112 hb_tag_t script_tags[] = { HB_TAG_NONE,
118 #ifdef FT_DEBUG_LEVEL_TRACE
123 if ( !globals || !style_class || !gstyles )
124 return FT_THROW( Invalid_Argument );
126 face = hb_font_get_face( globals->hb_font );
128 gsub_lookups = hb_set_create();
129 gsub_glyphs = hb_set_create();
130 gpos_lookups = hb_set_create();
131 gpos_glyphs = hb_set_create();
133 coverage_tags = coverages[style_class->coverage];
134 script = scripts[style_class->script];
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,
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 )
149 if ( script_tags[0] == HB_TAG_NONE )
150 script_tags[0] = HB_OT_TAG_DEFAULT_SCRIPT;
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;
161 if ( script_tags[1] == HB_OT_TAG_DEFAULT_SCRIPT )
162 script_tags[1] = HB_TAG_NONE;
165 hb_ot_layout_collect_lookups( face,
172 if ( hb_set_is_empty( gsub_lookups ) )
173 goto Exit; /* nothing to do */
175 hb_ot_layout_collect_lookups( face,
182 FT_TRACE4(( "GSUB lookups (style `%s'):\n"
184 af_style_names[style_class->style] ));
186 #ifdef FT_DEBUG_LEVEL_TRACE
190 for ( idx = -1; hb_set_next( gsub_lookups, &idx ); )
192 #ifdef FT_DEBUG_LEVEL_TRACE
193 FT_TRACE4(( " %d", idx ));
197 /* get output coverage of GSUB feature */
198 hb_ot_layout_lookup_collect_glyphs( face,
207 #ifdef FT_DEBUG_LEVEL_TRACE
209 FT_TRACE4(( " (none)" ));
210 FT_TRACE4(( "\n\n" ));
213 FT_TRACE4(( "GPOS lookups (style `%s'):\n"
215 af_style_names[style_class->style] ));
217 #ifdef FT_DEBUG_LEVEL_TRACE
221 for ( idx = -1; hb_set_next( gpos_lookups, &idx ); )
223 #ifdef FT_DEBUG_LEVEL_TRACE
224 FT_TRACE4(( " %d", idx ));
228 /* get input coverage of GPOS feature */
229 hb_ot_layout_lookup_collect_glyphs( face,
238 #ifdef FT_DEBUG_LEVEL_TRACE
240 FT_TRACE4(( " (none)" ));
241 FT_TRACE4(( "\n\n" ));
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.
250 if ( style_class->coverage != AF_COVERAGE_DEFAULT )
252 AF_Blue_Stringset bss = style_class->blue_stringset;
253 const AF_Blue_StringRec* bs = &af_blue_stringsets[bss];
258 for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
260 const char* p = &af_blue_strings[bs->string];
268 GET_UTF8_CHAR( ch, p );
270 for ( idx = -1; hb_set_next( gsub_lookups, &idx ); )
272 hb_codepoint_t gidx = FT_Get_Char_Index( globals->face, ch );
275 if ( hb_ot_layout_lookup_would_substitute( face, idx,
287 FT_TRACE4(( " no blue characters found; style skipped\n" ));
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.
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.
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
313 * (a) glyphs that get used in multiple features are present in a
314 * feature without vertical shift,
318 * (b) a feature's GPOS data really moves the glyph vertically.
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.
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.
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
339 if ( style_class->coverage != AF_COVERAGE_DEFAULT )
340 hb_set_subtract( gsub_glyphs, gpos_glyphs );
342 #ifdef FT_DEBUG_LEVEL_TRACE
343 FT_TRACE4(( " glyphs without GPOS data (`*' means already assigned)" ));
347 for ( idx = -1; hb_set_next( gsub_glyphs, &idx ); )
349 #ifdef FT_DEBUG_LEVEL_TRACE
350 if ( !( count % 10 ) )
354 FT_TRACE4(( " %d", idx ));
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 )
364 if ( gstyles[idx] == AF_STYLE_UNASSIGNED )
365 gstyles[idx] = (FT_Byte)style_class->style;
366 #ifdef FT_DEBUG_LEVEL_TRACE
372 #ifdef FT_DEBUG_LEVEL_TRACE
376 FT_TRACE4(( "\n\n" ));
380 hb_set_destroy( gsub_lookups );
381 hb_set_destroy( gsub_glyphs );
382 hb_set_destroy( gpos_lookups );
383 hb_set_destroy( gpos_glyphs );
389 /* construct HarfBuzz features */
391 #define COVERAGE( name, NAME, description, \
392 tag1, tag2, tag3, tag4 ) \
393 static const hb_feature_t name ## _feature[] = \
396 HB_TAG( tag1, tag2, tag3, tag4 ), \
397 1, 0, (unsigned int)-1 \
405 /* define mapping between HarfBuzz features and AF_Coverage */
407 #define COVERAGE( name, NAME, description, \
408 tag1, tag2, tag3, tag4 ) \
412 static const hb_feature_t* features[] =
416 NULL /* AF_COVERAGE_DEFAULT */
421 af_get_char_index( AF_StyleMetrics metrics,
426 AF_StyleClass style_class;
428 const hb_feature_t* feature;
430 FT_ULong in_idx, out_idx;
434 return FT_THROW( Invalid_Argument );
436 in_idx = FT_Get_Char_Index( metrics->globals->face, charcode );
438 style_class = metrics->style_class;
440 feature = features[style_class->coverage];
444 FT_UInt upem = metrics->globals->face->units_per_EM;
446 hb_font_t* font = metrics->globals->hb_font;
447 hb_buffer_t* buf = hb_buffer_create();
449 uint32_t c = (uint32_t)charcode;
451 hb_glyph_info_t* ginfo;
452 hb_glyph_position_t* gpos;
456 /* we shape at a size of units per EM; this means font units */
457 hb_font_set_scale( font, upem, upem );
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] );
463 /* we add one character to `buf' ... */
464 hb_buffer_add_utf32( buf, &c, 1, 0, 1 );
466 /* ... and apply one feature */
467 hb_shape( font, buf, feature, 1 );
469 ginfo = hb_buffer_get_glyph_infos( buf, &gcount );
470 gpos = hb_buffer_get_glyph_positions( buf, &gcount );
472 out_idx = ginfo[0].codepoint;
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 )
483 *codepoint = out_idx;
484 *y_offset = gpos[0].y_offset;
487 hb_buffer_destroy( buf );
489 #ifdef FT_DEBUG_LEVEL_TRACE
491 FT_TRACE1(( "af_get_char_index:"
492 " input character mapped to multiple glyphs\n" ));
505 #else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
509 af_get_coverage( AF_FaceGlobals globals,
510 AF_StyleClass style_class,
513 FT_UNUSED( globals );
514 FT_UNUSED( style_class );
515 FT_UNUSED( gstyles );
522 af_get_char_index( AF_StyleMetrics metrics,
531 return FT_THROW( Invalid_Argument );
533 face = metrics->globals->face;
535 *codepoint = FT_Get_Char_Index( face, charcode );
542 #endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */