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.
251 AF_Blue_Stringset bss
= style_class
->blue_stringset
;
252 const AF_Blue_StringRec
* bs
= &af_blue_stringsets
[bss
];
257 for ( ; bs
->string
!= AF_BLUE_STRING_MAX
; bs
++ )
259 const char* p
= &af_blue_strings
[bs
->string
];
267 GET_UTF8_CHAR( ch
, p
);
269 for ( idx
= -1; hb_set_next( gsub_lookups
, &idx
); )
271 hb_codepoint_t gidx
= FT_Get_Char_Index( globals
->face
, ch
);
274 if ( hb_ot_layout_lookup_would_substitute( face
, idx
,
286 FT_TRACE4(( " no blue characters found; style skipped\n" ));
292 * Various OpenType features might use the same glyphs at different
293 * vertical positions; for example, superscript and subscript glyphs
294 * could be the same. However, the auto-hinter is completely
295 * agnostic of OpenType features after the feature analysis has been
296 * completed: The engine then simply receives a glyph index and returns a
297 * hinted and usually rendered glyph.
299 * Consider the superscript feature of font `pala.ttf': Some of the
300 * glyphs are `real', this is, they have a zero vertical offset, but
301 * most of them are small caps glyphs shifted up to the superscript
302 * position (this is, the `sups' feature is present in both the GSUB and
303 * GPOS tables). The code for blue zones computation actually uses a
304 * feature's y offset so that the `real' glyphs get correct hints. But
305 * later on it is impossible to decide whether a glyph index belongs to,
306 * say, the small caps or superscript feature.
308 * For this reason, we don't assign a style to a glyph if the current
309 * feature covers the glyph in both the GSUB and the GPOS tables. This
310 * is quite a broad condition, assuming that
312 * (a) glyphs that get used in multiple features are present in a
313 * feature without vertical shift,
317 * (b) a feature's GPOS data really moves the glyph vertically.
319 * Not fulfilling condition (a) makes a font larger; it would also
320 * reduce the number of glyphs that could be addressed directly without
321 * using OpenType features, so this assumption is rather strong.
323 * Condition (b) is much weaker, and there might be glyphs which get
324 * missed. However, the OpenType features we are going to handle are
325 * primarily located in GSUB, and HarfBuzz doesn't provide an API to
326 * directly get the necessary information from the GPOS table. A
327 * possible solution might be to directly parse the GPOS table to find
328 * out whether a glyph gets shifted vertically, but this is something I
329 * would like to avoid if not really necessary.
332 hb_set_subtract( gsub_glyphs
, gpos_glyphs
);
334 #ifdef FT_DEBUG_LEVEL_TRACE
335 FT_TRACE4(( " glyphs without GPOS data (`*' means already assigned)" ));
339 for ( idx
= -1; hb_set_next( gsub_glyphs
, &idx
); )
341 #ifdef FT_DEBUG_LEVEL_TRACE
342 if ( !( count
% 10 ) )
346 FT_TRACE4(( " %d", idx
));
350 if ( gstyles
[idx
] == AF_STYLE_UNASSIGNED
)
351 gstyles
[idx
] = (FT_Byte
)style_class
->style
;
352 #ifdef FT_DEBUG_LEVEL_TRACE
358 #ifdef FT_DEBUG_LEVEL_TRACE
362 FT_TRACE4(( "\n\n" ));
366 hb_set_destroy( gsub_lookups
);
367 hb_set_destroy( gsub_glyphs
);
368 hb_set_destroy( gpos_lookups
);
369 hb_set_destroy( gpos_glyphs
);
375 /* construct HarfBuzz features */
377 #define COVERAGE( name, NAME, description, \
378 tag1, tag2, tag3, tag4 ) \
379 static const hb_feature_t name ## _feature[] = \
382 HB_TAG( tag1, tag2, tag3, tag4 ), \
383 1, 0, (unsigned int)-1 \
391 /* define mapping between HarfBuzz features and AF_Coverage */
393 #define COVERAGE( name, NAME, description, \
394 tag1, tag2, tag3, tag4 ) \
398 static const hb_feature_t
* features
[] =
402 NULL
/* AF_COVERAGE_DEFAULT */
407 af_get_char_index( AF_StyleMetrics metrics
,
412 AF_StyleClass style_class
;
414 const hb_feature_t
* feature
;
416 FT_ULong in_idx
, out_idx
;
420 return FT_THROW( Invalid_Argument
);
422 in_idx
= FT_Get_Char_Index( metrics
->globals
->face
, charcode
);
424 style_class
= metrics
->style_class
;
426 feature
= features
[style_class
->coverage
];
430 FT_UInt upem
= metrics
->globals
->face
->units_per_EM
;
432 hb_font_t
* font
= metrics
->globals
->hb_font
;
433 hb_buffer_t
* buf
= hb_buffer_create();
435 uint32_t c
= (uint32_t)charcode
;
437 hb_glyph_info_t
* ginfo
;
438 hb_glyph_position_t
* gpos
;
442 /* we shape at a size of units per EM; this means font units */
443 hb_font_set_scale( font
, upem
, upem
);
445 /* XXX: is this sufficient for a single character of any script? */
446 hb_buffer_set_direction( buf
, HB_DIRECTION_LTR
);
447 hb_buffer_set_script( buf
, scripts
[style_class
->script
] );
449 /* we add one character to `buf' ... */
450 hb_buffer_add_utf32( buf
, &c
, 1, 0, 1 );
452 /* ... and apply one feature */
453 hb_shape( font
, buf
, feature
, 1 );
455 ginfo
= hb_buffer_get_glyph_infos( buf
, &gcount
);
456 gpos
= hb_buffer_get_glyph_positions( buf
, &gcount
);
458 out_idx
= ginfo
[0].codepoint
;
460 /* getting the same index indicates no substitution, */
461 /* which means that the glyph isn't available in the feature */
462 if ( in_idx
== out_idx
)
469 *codepoint
= out_idx
;
470 *y_offset
= gpos
[0].y_offset
;
473 hb_buffer_destroy( buf
);
475 #ifdef FT_DEBUG_LEVEL_TRACE
477 FT_TRACE1(( "af_get_char_index:"
478 " input character mapped to multiple glyphs\n" ));
491 #else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
495 af_get_coverage( AF_FaceGlobals globals
,
496 AF_StyleClass style_class
,
499 FT_UNUSED( globals
);
500 FT_UNUSED( style_class
);
501 FT_UNUSED( gstyles
);
508 af_get_char_index( AF_StyleMetrics metrics
,
517 return FT_THROW( Invalid_Argument
);
519 face
= metrics
->globals
->face
;
521 *codepoint
= FT_Get_Char_Index( face
, charcode
);
528 #endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */