1/***************************************************************************/
2/* */
3/* afshaper.c */
4/* */
5/* HarfBuzz interface for accessing OpenType features (body). */
6/* */
7/* Copyright 2013-2018 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 FT_ADVANCES_H
22#include "afglobal.h"
23#include "aftypes.h"
24#include "afshaper.h"
25
26#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
27
28
29 /*************************************************************************/
30 /* */
31 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
32 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
33 /* messages during execution. */
34 /* */
35#undef FT_COMPONENT
36#define FT_COMPONENT trace_afshaper
37
38
39 /*
40 * We use `sets' (in the HarfBuzz sense, which comes quite near to the
41 * usual mathematical meaning) to manage both lookups and glyph indices.
42 *
43 * 1. For each coverage, collect lookup IDs in a set. Note that an
44 * auto-hinter `coverage' is represented by one `feature', and a
45 * feature consists of an arbitrary number of (font specific) `lookup's
46 * that actually do the mapping job. Please check the OpenType
47 * specification for more details on features and lookups.
48 *
49 * 2. Create glyph ID sets from the corresponding lookup sets.
50 *
51 * 3. The glyph set corresponding to AF_COVERAGE_DEFAULT is computed
52 * with all lookups specific to the OpenType script activated. It
53 * relies on the order of AF_DEFINE_STYLE_CLASS entries so that
54 * special coverages (like `oldstyle figures') don't get overwritten.
55 *
56 */
57
58
59 /* load coverage tags */
60#undef COVERAGE
61#define COVERAGE( name, NAME, description, \
62 tag1, tag2, tag3, tag4 ) \
63 static const hb_tag_t name ## _coverage[] = \
64 { \
65 HB_TAG( tag1, tag2, tag3, tag4 ), \
66 HB_TAG_NONE \
67 };
68
69
70#include "afcover.h"
71
72
73 /* define mapping between coverage tags and AF_Coverage */
74#undef COVERAGE
75#define COVERAGE( name, NAME, description, \
76 tag1, tag2, tag3, tag4 ) \
77 name ## _coverage,
78
79
80 static const hb_tag_t* coverages[] =
81 {
82#include "afcover.h"
83
84 NULL /* AF_COVERAGE_DEFAULT */
85 };
86
87
88 /* load HarfBuzz script tags */
89#undef SCRIPT
90#define SCRIPT( s, S, d, h, H, ss ) h,
91
92
93 static const hb_script_t scripts[] =
94 {
95#include "afscript.h"
96 };
97
98
99 FT_Error
100 af_shaper_get_coverage( AF_FaceGlobals globals,
101 AF_StyleClass style_class,
102 FT_UShort* gstyles,
103 FT_Bool default_script )
104 {
105 hb_face_t* face;
106
107 hb_set_t* gsub_lookups = NULL; /* GSUB lookups for a given script */
108 hb_set_t* gsub_glyphs = NULL; /* glyphs covered by GSUB lookups */
109 hb_set_t* gpos_lookups = NULL; /* GPOS lookups for a given script */
110 hb_set_t* gpos_glyphs = NULL; /* glyphs covered by GPOS lookups */
111
112 hb_script_t script;
113 const hb_tag_t* coverage_tags;
114 hb_tag_t script_tags[] = { HB_TAG_NONE,
115 HB_TAG_NONE,
116 HB_TAG_NONE,
117 HB_TAG_NONE };
118
119 hb_codepoint_t idx;
120#ifdef FT_DEBUG_LEVEL_TRACE
121 int count;
122#endif
123
124
125 if ( !globals || !style_class || !gstyles )
126 return FT_THROW( Invalid_Argument );
127
128 face = hb_font_get_face( globals->hb_font );
129
130 coverage_tags = coverages[style_class->coverage];
131 script = scripts[style_class->script];
132
133 /* Convert a HarfBuzz script tag into the corresponding OpenType */
134 /* tag or tags -- some Indic scripts like Devanagari have an old */
135 /* and a new set of features. */
136 hb_ot_tags_from_script( script,
137 &script_tags[0],
138 &script_tags[1] );
139
140 /* `hb_ot_tags_from_script' usually returns HB_OT_TAG_DEFAULT_SCRIPT */
141 /* as the second tag. We change that to HB_TAG_NONE except for the */
142 /* default script. */
143 if ( default_script )
144 {
145 if ( script_tags[0] == HB_TAG_NONE )
146 script_tags[0] = HB_OT_TAG_DEFAULT_SCRIPT;
147 else
148 {
149 if ( script_tags[1] == HB_TAG_NONE )
150 script_tags[1] = HB_OT_TAG_DEFAULT_SCRIPT;
151 else if ( script_tags[1] != HB_OT_TAG_DEFAULT_SCRIPT )
152 script_tags[2] = HB_OT_TAG_DEFAULT_SCRIPT;
153 }
154 }
155 else
156 {
157 /* we use non-standard tags like `khms' for special purposes; */
158 /* HarfBuzz maps them to `DFLT', which we don't want to handle here */
159 if ( script_tags[0] == HB_OT_TAG_DEFAULT_SCRIPT )
160 goto Exit;
161
162 if ( script_tags[1] == HB_OT_TAG_DEFAULT_SCRIPT )
163 script_tags[1] = HB_TAG_NONE;
164 }
165
166 gsub_lookups = hb_set_create();
167 hb_ot_layout_collect_lookups( face,
168 HB_OT_TAG_GSUB,
169 script_tags,
170 NULL,
171 coverage_tags,
172 gsub_lookups );
173
174 if ( hb_set_is_empty( gsub_lookups ) )
175 goto Exit; /* nothing to do */
176
177 FT_TRACE4(( "GSUB lookups (style `%s'):\n"
178 " ",
179 af_style_names[style_class->style] ));
180
181#ifdef FT_DEBUG_LEVEL_TRACE
182 count = 0;
183#endif
184
185 gsub_glyphs = hb_set_create();
186 for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups, &idx ); )
187 {
188#ifdef FT_DEBUG_LEVEL_TRACE
189 FT_TRACE4(( " %d", idx ));
190 count++;
191#endif
192
193 /* get output coverage of GSUB feature */
194 hb_ot_layout_lookup_collect_glyphs( face,
195 HB_OT_TAG_GSUB,
196 idx,
197 NULL,
198 NULL,
199 NULL,
200 gsub_glyphs );
201 }
202
203#ifdef FT_DEBUG_LEVEL_TRACE
204 if ( !count )
205 FT_TRACE4(( " (none)" ));
206 FT_TRACE4(( "\n\n" ));
207#endif
208
209 FT_TRACE4(( "GPOS lookups (style `%s'):\n"
210 " ",
211 af_style_names[style_class->style] ));
212
213 gpos_lookups = hb_set_create();
214 hb_ot_layout_collect_lookups( face,
215 HB_OT_TAG_GPOS,
216 script_tags,
217 NULL,
218 coverage_tags,
219 gpos_lookups );
220
221#ifdef FT_DEBUG_LEVEL_TRACE
222 count = 0;
223#endif
224
225 gpos_glyphs = hb_set_create();
226 for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gpos_lookups, &idx ); )
227 {
228#ifdef FT_DEBUG_LEVEL_TRACE
229 FT_TRACE4(( " %d", idx ));
230 count++;
231#endif
232
233 /* get input coverage of GPOS feature */
234 hb_ot_layout_lookup_collect_glyphs( face,
235 HB_OT_TAG_GPOS,
236 idx,
237 NULL,
238 gpos_glyphs,
239 NULL,
240 NULL );
241 }
242
243#ifdef FT_DEBUG_LEVEL_TRACE
244 if ( !count )
245 FT_TRACE4(( " (none)" ));
246 FT_TRACE4(( "\n\n" ));
247#endif
248
249 /*
250 * We now check whether we can construct blue zones, using glyphs
251 * covered by the feature only. In case there is not a single zone
252 * (this is, not a single character is covered), we skip this coverage.
253 *
254 */
255 if ( style_class->coverage != AF_COVERAGE_DEFAULT )
256 {
257 AF_Blue_Stringset bss = style_class->blue_stringset;
258 const AF_Blue_StringRec* bs = &af_blue_stringsets[bss];
259
260 FT_Bool found = 0;
261
262
263 for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
264 {
265 const char* p = &af_blue_strings[bs->string];
266
267
268 while ( *p )
269 {
270 hb_codepoint_t ch;
271
272
273 GET_UTF8_CHAR( ch, p );
274
275 for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups,
276 &idx ); )
277 {
278 hb_codepoint_t gidx = FT_Get_Char_Index( globals->face, ch );
279
280
281 if ( hb_ot_layout_lookup_would_substitute( face, idx,
282 &gidx, 1, 1 ) )
283 {
284 found = 1;
285 break;
286 }
287 }
288 }
289 }
290
291 if ( !found )
292 {
293 FT_TRACE4(( " no blue characters found; style skipped\n" ));
294 goto Exit;
295 }
296 }
297
298 /*
299 * Various OpenType features might use the same glyphs at different
300 * vertical positions; for example, superscript and subscript glyphs
301 * could be the same. However, the auto-hinter is completely
302 * agnostic of OpenType features after the feature analysis has been
303 * completed: The engine then simply receives a glyph index and returns a
304 * hinted and usually rendered glyph.
305 *
306 * Consider the superscript feature of font `pala.ttf': Some of the
307 * glyphs are `real', this is, they have a zero vertical offset, but
308 * most of them are small caps glyphs shifted up to the superscript
309 * position (this is, the `sups' feature is present in both the GSUB and
310 * GPOS tables). The code for blue zones computation actually uses a
311 * feature's y offset so that the `real' glyphs get correct hints. But
312 * later on it is impossible to decide whether a glyph index belongs to,
313 * say, the small caps or superscript feature.
314 *
315 * For this reason, we don't assign a style to a glyph if the current
316 * feature covers the glyph in both the GSUB and the GPOS tables. This
317 * is quite a broad condition, assuming that
318 *
319 * (a) glyphs that get used in multiple features are present in a
320 * feature without vertical shift,
321 *
322 * and
323 *
324 * (b) a feature's GPOS data really moves the glyph vertically.
325 *
326 * Not fulfilling condition (a) makes a font larger; it would also
327 * reduce the number of glyphs that could be addressed directly without
328 * using OpenType features, so this assumption is rather strong.
329 *
330 * Condition (b) is much weaker, and there might be glyphs which get
331 * missed. However, the OpenType features we are going to handle are
332 * primarily located in GSUB, and HarfBuzz doesn't provide an API to
333 * directly get the necessary information from the GPOS table. A
334 * possible solution might be to directly parse the GPOS table to find
335 * out whether a glyph gets shifted vertically, but this is something I
336 * would like to avoid if not really necessary.
337 *
338 * Note that we don't follow this logic for the default coverage.
339 * Complex scripts like Devanagari have mandatory GPOS features to
340 * position many glyph elements, using mark-to-base or mark-to-ligature
341 * tables; the number of glyphs missed due to condition (b) would be far
342 * too large.
343 *
344 */
345 if ( style_class->coverage != AF_COVERAGE_DEFAULT )
346 hb_set_subtract( gsub_glyphs, gpos_glyphs );
347
348#ifdef FT_DEBUG_LEVEL_TRACE
349 FT_TRACE4(( " glyphs without GPOS data (`*' means already assigned)" ));
350 count = 0;
351#endif
352
353 for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_glyphs, &idx ); )
354 {
355#ifdef FT_DEBUG_LEVEL_TRACE
356 if ( !( count % 10 ) )
357 FT_TRACE4(( "\n"
358 " " ));
359
360 FT_TRACE4(( " %d", idx ));
361 count++;
362#endif
363
364 /* glyph indices returned by `hb_ot_layout_lookup_collect_glyphs' */
365 /* can be arbitrary: some fonts use fake indices for processing */
366 /* internal to GSUB or GPOS, which is fully valid */
367 if ( idx >= (hb_codepoint_t)globals->glyph_count )
368 continue;
369
370 if ( gstyles[idx] == AF_STYLE_UNASSIGNED )
371 gstyles[idx] = (FT_UShort)style_class->style;
372#ifdef FT_DEBUG_LEVEL_TRACE
373 else
374 FT_TRACE4(( "*" ));
375#endif
376 }
377
378#ifdef FT_DEBUG_LEVEL_TRACE
379 if ( !count )
380 FT_TRACE4(( "\n"
381 " (none)" ));
382 FT_TRACE4(( "\n\n" ));
383#endif
384
385 Exit:
386 hb_set_destroy( gsub_lookups );
387 hb_set_destroy( gsub_glyphs );
388 hb_set_destroy( gpos_lookups );
389 hb_set_destroy( gpos_glyphs );
390
391 return FT_Err_Ok;
392 }
393
394
395 /* construct HarfBuzz features */
396#undef COVERAGE
397#define COVERAGE( name, NAME, description, \
398 tag1, tag2, tag3, tag4 ) \
399 static const hb_feature_t name ## _feature[] = \
400 { \
401 { \
402 HB_TAG( tag1, tag2, tag3, tag4 ), \
403 1, 0, (unsigned int)-1 \
404 } \
405 };
406
407
408#include "afcover.h"
409
410
411 /* define mapping between HarfBuzz features and AF_Coverage */
412#undef COVERAGE
413#define COVERAGE( name, NAME, description, \
414 tag1, tag2, tag3, tag4 ) \
415 name ## _feature,
416
417
418 static const hb_feature_t* features[] =
419 {
420#include "afcover.h"
421
422 NULL /* AF_COVERAGE_DEFAULT */
423 };
424
425
426 void*
427 af_shaper_buf_create( FT_Face face )
428 {
429 FT_UNUSED( face );
430
431 return (void*)hb_buffer_create();
432 }
433
434
435 void
436 af_shaper_buf_destroy( FT_Face face,
437 void* buf )
438 {
439 FT_UNUSED( face );
440
441 hb_buffer_destroy( (hb_buffer_t*)buf );
442 }
443
444
445 const char*
446 af_shaper_get_cluster( const char* p,
447 AF_StyleMetrics metrics,
448 void* buf_,
449 unsigned int* count )
450 {
451 AF_StyleClass style_class;
452 const hb_feature_t* feature;
453 FT_Int upem;
454 const char* q;
455 int len;
456
457 hb_buffer_t* buf = (hb_buffer_t*)buf_;
458 hb_font_t* font;
459 hb_codepoint_t dummy;
460
461
462 upem = (FT_Int)metrics->globals->face->units_per_EM;
463 style_class = metrics->style_class;
464 feature = features[style_class->coverage];
465
466 font = metrics->globals->hb_font;
467
468 /* we shape at a size of units per EM; this means font units */
469 hb_font_set_scale( font, upem, upem );
470
471 while ( *p == ' ' )
472 p++;
473
474 /* count bytes up to next space (or end of buffer) */
475 q = p;
476 while ( !( *q == ' ' || *q == '\0' ) )
477 GET_UTF8_CHAR( dummy, q );
478 len = (int)( q - p );
479
480 /* feed character(s) to the HarfBuzz buffer */
481 hb_buffer_clear_contents( buf );
482 hb_buffer_add_utf8( buf, p, len, 0, len );
483
484 /* we let HarfBuzz guess the script and writing direction */
485 hb_buffer_guess_segment_properties( buf );
486
487 /* shape buffer, which means conversion from character codes to */
488 /* glyph indices, possibly applying a feature */
489 hb_shape( font, buf, feature, feature ? 1 : 0 );
490
491 if ( feature )
492 {
493 hb_buffer_t* hb_buf = metrics->globals->hb_buf;
494
495 unsigned int gcount;
496 hb_glyph_info_t* ginfo;
497
498 unsigned int hb_gcount;
499 hb_glyph_info_t* hb_ginfo;
500
501
502 /* we have to check whether applying a feature does actually change */
503 /* glyph indices; otherwise the affected glyph or glyphs aren't */
504 /* available at all in the feature */
505
506 hb_buffer_clear_contents( hb_buf );
507 hb_buffer_add_utf8( hb_buf, p, len, 0, len );
508 hb_buffer_guess_segment_properties( hb_buf );
509 hb_shape( font, hb_buf, NULL, 0 );
510
511 ginfo = hb_buffer_get_glyph_infos( buf, &gcount );
512 hb_ginfo = hb_buffer_get_glyph_infos( hb_buf, &hb_gcount );
513
514 if ( gcount == hb_gcount )
515 {
516 unsigned int i;
517
518
519 for (i = 0; i < gcount; i++ )
520 if ( ginfo[i].codepoint != hb_ginfo[i].codepoint )
521 break;
522
523 if ( i == gcount )
524 {
525 /* both buffers have identical glyph indices */
526 hb_buffer_clear_contents( buf );
527 }
528 }
529 }
530
531 *count = hb_buffer_get_length( buf );
532
533#ifdef FT_DEBUG_LEVEL_TRACE
534 if ( feature && *count > 1 )
535 FT_TRACE1(( "af_shaper_get_cluster:"
536 " input character mapped to multiple glyphs\n" ));
537#endif
538
539 return q;
540 }
541
542
543 FT_ULong
544 af_shaper_get_elem( AF_StyleMetrics metrics,
545 void* buf_,
546 unsigned int idx,
547 FT_Long* advance,
548 FT_Long* y_offset )
549 {
550 hb_buffer_t* buf = (hb_buffer_t*)buf_;
551 hb_glyph_info_t* ginfo;
552 hb_glyph_position_t* gpos;
553 unsigned int gcount;
554
555 FT_UNUSED( metrics );
556
557
558 ginfo = hb_buffer_get_glyph_infos( buf, &gcount );
559 gpos = hb_buffer_get_glyph_positions( buf, &gcount );
560
561 if ( idx >= gcount )
562 return 0;
563
564 if ( advance )
565 *advance = gpos[idx].x_advance;
566 if ( y_offset )
567 *y_offset = gpos[idx].y_offset;
568
569 return ginfo[idx].codepoint;
570 }
571
572
573#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
574
575
576 FT_Error
577 af_shaper_get_coverage( AF_FaceGlobals globals,
578 AF_StyleClass style_class,
579 FT_UShort* gstyles,
580 FT_Bool default_script )
581 {
582 FT_UNUSED( globals );
583 FT_UNUSED( style_class );
584 FT_UNUSED( gstyles );
585 FT_UNUSED( default_script );
586
587 return FT_Err_Ok;
588 }
589
590
591 void*
592 af_shaper_buf_create( FT_Face face )
593 {
594 FT_Error error;
595 FT_Memory memory = face->memory;
596 FT_ULong* buf;
597
598
599 FT_MEM_ALLOC( buf, sizeof ( FT_ULong ) );
600
601 return (void*)buf;
602 }
603
604
605 void
606 af_shaper_buf_destroy( FT_Face face,
607 void* buf )
608 {
609 FT_Memory memory = face->memory;
610
611
612 FT_FREE( buf );
613 }
614
615
616 const char*
617 af_shaper_get_cluster( const char* p,
618 AF_StyleMetrics metrics,
619 void* buf_,
620 unsigned int* count )
621 {
622 FT_Face face = metrics->globals->face;
623 FT_ULong ch, dummy = 0;
624 FT_ULong* buf = (FT_ULong*)buf_;
625
626
627 while ( *p == ' ' )
628 p++;
629
630 GET_UTF8_CHAR( ch, p );
631
632 /* since we don't have an engine to handle clusters, */
633 /* we scan the characters but return zero */
634 while ( !( *p == ' ' || *p == '\0' ) )
635 GET_UTF8_CHAR( dummy, p );
636
637 if ( dummy )
638 {
639 *buf = 0;
640 *count = 0;
641 }
642 else
643 {
644 *buf = FT_Get_Char_Index( face, ch );
645 *count = 1;
646 }
647
648 return p;
649 }
650
651
652 FT_ULong
653 af_shaper_get_elem( AF_StyleMetrics metrics,
654 void* buf_,
655 unsigned int idx,
656 FT_Long* advance,
657 FT_Long* y_offset )
658 {
659 FT_Face face = metrics->globals->face;
660 FT_ULong glyph_index = *(FT_ULong*)buf_;
661
662 FT_UNUSED( idx );
663
664
665 if ( advance )
666 FT_Get_Advance( face,
667 glyph_index,
668 FT_LOAD_NO_SCALE |
669 FT_LOAD_NO_HINTING |
670 FT_LOAD_IGNORE_TRANSFORM,
671 advance );
672
673 if ( y_offset )
674 *y_offset = 0;
675
676 return glyph_index;
677 }
678
679
680#endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
681
682
683/* END */
684