Sync with trunk r63343.
[reactos.git] / lib / 3rdparty / freetype / 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 {
251 AF_Blue_Stringset bss = style_class->blue_stringset;
252 const AF_Blue_StringRec* bs = &af_blue_stringsets[bss];
253
254 FT_Bool found = 0;
255
256
257 for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
258 {
259 const char* p = &af_blue_strings[bs->string];
260
261
262 while ( *p )
263 {
264 hb_codepoint_t ch;
265
266
267 GET_UTF8_CHAR( ch, p );
268
269 for ( idx = -1; hb_set_next( gsub_lookups, &idx ); )
270 {
271 hb_codepoint_t gidx = FT_Get_Char_Index( globals->face, ch );
272
273
274 if ( hb_ot_layout_lookup_would_substitute( face, idx,
275 &gidx, 1, 1 ) )
276 {
277 found = 1;
278 break;
279 }
280 }
281 }
282 }
283
284 if ( !found )
285 {
286 FT_TRACE4(( " no blue characters found; style skipped\n" ));
287 goto Exit;
288 }
289 }
290
291 /*
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.
298 *
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.
307 *
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
311 *
312 * (a) glyphs that get used in multiple features are present in a
313 * feature without vertical shift,
314 *
315 * and
316 *
317 * (b) a feature's GPOS data really moves the glyph vertically.
318 *
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.
322 *
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.
330 *
331 */
332 hb_set_subtract( gsub_glyphs, gpos_glyphs );
333
334 #ifdef FT_DEBUG_LEVEL_TRACE
335 FT_TRACE4(( " glyphs without GPOS data (`*' means already assigned)" ));
336 count = 0;
337 #endif
338
339 for ( idx = -1; hb_set_next( gsub_glyphs, &idx ); )
340 {
341 #ifdef FT_DEBUG_LEVEL_TRACE
342 if ( !( count % 10 ) )
343 FT_TRACE4(( "\n"
344 " " ));
345
346 FT_TRACE4(( " %d", idx ));
347 count++;
348 #endif
349
350 if ( gstyles[idx] == AF_STYLE_UNASSIGNED )
351 gstyles[idx] = (FT_Byte)style_class->style;
352 #ifdef FT_DEBUG_LEVEL_TRACE
353 else
354 FT_TRACE4(( "*" ));
355 #endif
356 }
357
358 #ifdef FT_DEBUG_LEVEL_TRACE
359 if ( !count )
360 FT_TRACE4(( "\n"
361 " (none)" ));
362 FT_TRACE4(( "\n\n" ));
363 #endif
364
365 Exit:
366 hb_set_destroy( gsub_lookups );
367 hb_set_destroy( gsub_glyphs );
368 hb_set_destroy( gpos_lookups );
369 hb_set_destroy( gpos_glyphs );
370
371 return FT_Err_Ok;
372 }
373
374
375 /* construct HarfBuzz features */
376 #undef COVERAGE
377 #define COVERAGE( name, NAME, description, \
378 tag1, tag2, tag3, tag4 ) \
379 static const hb_feature_t name ## _feature[] = \
380 { \
381 { \
382 HB_TAG( tag1, tag2, tag3, tag4 ), \
383 1, 0, (unsigned int)-1 \
384 } \
385 };
386
387
388 #include "afcover.h"
389
390
391 /* define mapping between HarfBuzz features and AF_Coverage */
392 #undef COVERAGE
393 #define COVERAGE( name, NAME, description, \
394 tag1, tag2, tag3, tag4 ) \
395 name ## _feature,
396
397
398 static const hb_feature_t* features[] =
399 {
400 #include "afcover.h"
401
402 NULL /* AF_COVERAGE_DEFAULT */
403 };
404
405
406 FT_Error
407 af_get_char_index( AF_StyleMetrics metrics,
408 FT_ULong charcode,
409 FT_ULong *codepoint,
410 FT_Long *y_offset )
411 {
412 AF_StyleClass style_class;
413
414 const hb_feature_t* feature;
415
416 FT_ULong in_idx, out_idx;
417
418
419 if ( !metrics )
420 return FT_THROW( Invalid_Argument );
421
422 in_idx = FT_Get_Char_Index( metrics->globals->face, charcode );
423
424 style_class = metrics->style_class;
425
426 feature = features[style_class->coverage];
427
428 if ( feature )
429 {
430 FT_UInt upem = metrics->globals->face->units_per_EM;
431
432 hb_font_t* font = metrics->globals->hb_font;
433 hb_buffer_t* buf = hb_buffer_create();
434
435 uint32_t c = (uint32_t)charcode;
436
437 hb_glyph_info_t* ginfo;
438 hb_glyph_position_t* gpos;
439 unsigned int gcount;
440
441
442 /* we shape at a size of units per EM; this means font units */
443 hb_font_set_scale( font, upem, upem );
444
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] );
448
449 /* we add one character to `buf' ... */
450 hb_buffer_add_utf32( buf, &c, 1, 0, 1 );
451
452 /* ... and apply one feature */
453 hb_shape( font, buf, feature, 1 );
454
455 ginfo = hb_buffer_get_glyph_infos( buf, &gcount );
456 gpos = hb_buffer_get_glyph_positions( buf, &gcount );
457
458 out_idx = ginfo[0].codepoint;
459
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 )
463 {
464 *codepoint = 0;
465 *y_offset = 0;
466 }
467 else
468 {
469 *codepoint = out_idx;
470 *y_offset = gpos[0].y_offset;
471 }
472
473 hb_buffer_destroy( buf );
474
475 #ifdef FT_DEBUG_LEVEL_TRACE
476 if ( gcount > 1 )
477 FT_TRACE1(( "af_get_char_index:"
478 " input character mapped to multiple glyphs\n" ));
479 #endif
480 }
481 else
482 {
483 *codepoint = in_idx;
484 *y_offset = 0;
485 }
486
487 return FT_Err_Ok;
488 }
489
490
491 #else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
492
493
494 FT_Error
495 af_get_coverage( AF_FaceGlobals globals,
496 AF_StyleClass style_class,
497 FT_Byte* gstyles )
498 {
499 FT_UNUSED( globals );
500 FT_UNUSED( style_class );
501 FT_UNUSED( gstyles );
502
503 return FT_Err_Ok;
504 }
505
506
507 FT_Error
508 af_get_char_index( AF_StyleMetrics metrics,
509 FT_ULong charcode,
510 FT_ULong *codepoint,
511 FT_Long *y_offset )
512 {
513 FT_Face face;
514
515
516 if ( !metrics )
517 return FT_THROW( Invalid_Argument );
518
519 face = metrics->globals->face;
520
521 *codepoint = FT_Get_Char_Index( face, charcode );
522 *y_offset = 0;
523
524 return FT_Err_Ok;
525 }
526
527
528 #endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
529
530
531 /* END */