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 af_shaper_get_coverage(AF_FaceGlobals globals,AF_StyleClass style_class,FT_UShort * gstyles,FT_Bool default_script)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* af_shaper_buf_create(FT_Face face)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 af_shaper_buf_destroy(FT_Face face,void * buf)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* af_shaper_get_cluster(const char * p,AF_StyleMetrics metrics,void * buf_,unsigned int * count)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 af_shaper_get_elem(AF_StyleMetrics metrics,void * buf_,unsigned int idx,FT_Long * advance,FT_Long * y_offset)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 af_shaper_get_coverage(AF_FaceGlobals globals,AF_StyleClass style_class,FT_UShort * gstyles,FT_Bool default_script)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* af_shaper_buf_create(FT_Face face)606 af_shaper_buf_create( FT_Face face ) 607 { 608 FT_UNUSED( face ); 609 610 return NULL; 611 } 612 613 614 void af_shaper_buf_destroy(FT_Face face,void * buf)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* af_shaper_get_cluster(const char * p,AF_StyleMetrics metrics,void * buf_,unsigned int * count)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 af_shaper_get_elem(AF_StyleMetrics metrics,void * buf_,unsigned int idx,FT_Long * advance,FT_Long * y_offset)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 */ 691