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_UNUSED( face ); 595 596 return NULL; 597 } 598 599 600 void 601 af_shaper_buf_destroy( FT_Face face, 602 void* buf ) 603 { 604 FT_UNUSED( face ); 605 FT_UNUSED( buf ); 606 } 607 608 609 const char* 610 af_shaper_get_cluster( const char* p, 611 AF_StyleMetrics metrics, 612 void* buf_, 613 unsigned int* count ) 614 { 615 FT_Face face = metrics->globals->face; 616 FT_ULong ch, dummy = 0; 617 FT_ULong* buf = (FT_ULong*)buf_; 618 619 620 while ( *p == ' ' ) 621 p++; 622 623 GET_UTF8_CHAR( ch, p ); 624 625 /* since we don't have an engine to handle clusters, */ 626 /* we scan the characters but return zero */ 627 while ( !( *p == ' ' || *p == '\0' ) ) 628 GET_UTF8_CHAR( dummy, p ); 629 630 if ( dummy ) 631 { 632 *buf = 0; 633 *count = 0; 634 } 635 else 636 { 637 *buf = FT_Get_Char_Index( face, ch ); 638 *count = 1; 639 } 640 641 return p; 642 } 643 644 645 FT_ULong 646 af_shaper_get_elem( AF_StyleMetrics metrics, 647 void* buf_, 648 unsigned int idx, 649 FT_Long* advance, 650 FT_Long* y_offset ) 651 { 652 FT_Face face = metrics->globals->face; 653 FT_ULong glyph_index = *(FT_ULong*)buf_; 654 655 FT_UNUSED( idx ); 656 657 658 if ( advance ) 659 FT_Get_Advance( face, 660 glyph_index, 661 FT_LOAD_NO_SCALE | 662 FT_LOAD_NO_HINTING | 663 FT_LOAD_IGNORE_TRANSFORM, 664 advance ); 665 666 if ( y_offset ) 667 *y_offset = 0; 668 669 return glyph_index; 670 } 671 672 673 #endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */ 674 675 676 /* END */ 677