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