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