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