hb-ot-shape.cc revision 16c6a27b4bffc19026944c7bea9cf0a3a8ff1d8f
1/* 2 * Copyright © 2009,2010 Red Hat, Inc. 3 * Copyright © 2010,2011 Google, Inc. 4 * 5 * This is part of HarfBuzz, a text shaping library. 6 * 7 * Permission is hereby granted, without written agreement and without 8 * license or royalty fees, to use, copy, modify, and distribute this 9 * software and its documentation for any purpose, provided that the 10 * above copyright notice and the following two paragraphs appear in 11 * all copies of this software. 12 * 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 17 * DAMAGE. 18 * 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 * 25 * Red Hat Author(s): Behdad Esfahbod 26 * Google Author(s): Behdad Esfahbod 27 */ 28 29#define HB_SHAPER ot 30#define hb_ot_shaper_face_data_t hb_ot_layout_t 31#define hb_ot_shaper_shape_plan_data_t hb_ot_shape_plan_t 32#include "hb-shaper-impl-private.hh" 33 34#include "hb-ot-shape-private.hh" 35#include "hb-ot-shape-normalize-private.hh" 36#include "hb-ot-shape-complex-private.hh" 37 38#include "hb-ot-layout-private.hh" 39#include "hb-set-private.hh" 40 41 42hb_tag_t common_features[] = { 43 HB_TAG('c','c','m','p'), 44 HB_TAG('l','i','g','a'), 45 HB_TAG('l','o','c','l'), 46 HB_TAG('m','a','r','k'), 47 HB_TAG('m','k','m','k'), 48 HB_TAG('r','l','i','g'), 49}; 50 51 52hb_tag_t horizontal_features[] = { 53 HB_TAG('c','a','l','t'), 54 HB_TAG('c','l','i','g'), 55 HB_TAG('c','u','r','s'), 56 HB_TAG('k','e','r','n'), 57}; 58 59/* Note: 60 * Technically speaking, vrt2 and vert are mutually exclusive. 61 * According to the spec, valt and vpal are also mutually exclusive. 62 * But we apply them all for now. 63 */ 64hb_tag_t vertical_features[] = { 65 HB_TAG('v','a','l','t'), 66 HB_TAG('v','e','r','t'), 67 HB_TAG('v','k','r','n'), 68 HB_TAG('v','p','a','l'), 69 HB_TAG('v','r','t','2'), 70}; 71 72 73 74static void 75hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, 76 const hb_segment_properties_t *props, 77 const hb_feature_t *user_features, 78 unsigned int num_user_features) 79{ 80 switch (props->direction) { 81 case HB_DIRECTION_LTR: 82 planner->map.add_bool_feature (HB_TAG ('l','t','r','a')); 83 planner->map.add_bool_feature (HB_TAG ('l','t','r','m')); 84 break; 85 case HB_DIRECTION_RTL: 86 planner->map.add_bool_feature (HB_TAG ('r','t','l','a')); 87 planner->map.add_bool_feature (HB_TAG ('r','t','l','m'), false); 88 break; 89 case HB_DIRECTION_TTB: 90 case HB_DIRECTION_BTT: 91 case HB_DIRECTION_INVALID: 92 default: 93 break; 94 } 95 96#define ADD_FEATURES(array) \ 97 HB_STMT_START { \ 98 for (unsigned int i = 0; i < ARRAY_LENGTH (array); i++) \ 99 planner->map.add_bool_feature (array[i]); \ 100 } HB_STMT_END 101 102 if (planner->shaper->collect_features) 103 planner->shaper->collect_features (planner); 104 105 ADD_FEATURES (common_features); 106 107 if (HB_DIRECTION_IS_HORIZONTAL (props->direction)) 108 ADD_FEATURES (horizontal_features); 109 else 110 ADD_FEATURES (vertical_features); 111 112 if (planner->shaper->override_features) 113 planner->shaper->override_features (planner); 114 115#undef ADD_FEATURES 116 117 for (unsigned int i = 0; i < num_user_features; i++) { 118 const hb_feature_t *feature = &user_features[i]; 119 planner->map.add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1)); 120 } 121} 122 123 124/* 125 * shaper face data 126 */ 127 128hb_ot_shaper_face_data_t * 129_hb_ot_shaper_face_data_create (hb_face_t *face) 130{ 131 return _hb_ot_layout_create (face); 132} 133 134void 135_hb_ot_shaper_face_data_destroy (hb_ot_shaper_face_data_t *data) 136{ 137 _hb_ot_layout_destroy (data); 138} 139 140 141/* 142 * shaper font data 143 */ 144 145struct hb_ot_shaper_font_data_t {}; 146 147hb_ot_shaper_font_data_t * 148_hb_ot_shaper_font_data_create (hb_font_t *font) 149{ 150 return (hb_ot_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; 151} 152 153void 154_hb_ot_shaper_font_data_destroy (hb_ot_shaper_font_data_t *data) 155{ 156} 157 158 159/* 160 * shaper shape_plan data 161 */ 162 163hb_ot_shaper_shape_plan_data_t * 164_hb_ot_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan, 165 const hb_feature_t *user_features, 166 unsigned int num_user_features) 167{ 168 hb_ot_shape_plan_t *data = (hb_ot_shape_plan_t *) calloc (1, sizeof (hb_ot_shape_plan_t)); 169 if (unlikely (!data)) 170 return NULL; 171 172 hb_ot_shape_planner_t planner (shape_plan); 173 174 planner.shaper = hb_ot_shape_complex_categorize (&shape_plan->props); 175 176 hb_ot_shape_collect_features (&planner, &shape_plan->props, user_features, num_user_features); 177 178 planner.compile (*data); 179 180 return data; 181} 182 183void 184_hb_ot_shaper_shape_plan_data_destroy (hb_ot_shaper_shape_plan_data_t *data) 185{ 186 data->map.finish (); 187 188 free (data); 189} 190 191 192/* 193 * shaper 194 */ 195 196struct hb_ot_shape_context_t 197{ 198 /* Input to hb_ot_shape_internal() */ 199 hb_ot_shape_plan_t *plan; 200 hb_font_t *font; 201 hb_face_t *face; 202 hb_buffer_t *buffer; 203 const hb_feature_t *user_features; 204 unsigned int num_user_features; 205 206 /* Transient stuff */ 207 hb_direction_t target_direction; 208 hb_bool_t applied_position_complex; 209}; 210 211static void 212hb_ot_shape_setup_masks (hb_ot_shape_context_t *c) 213{ 214 hb_mask_t global_mask = c->plan->map.get_global_mask (); 215 c->buffer->reset_masks (global_mask); 216 217 if (c->plan->shaper->setup_masks) 218 c->plan->shaper->setup_masks (c->plan, c->buffer, c->font); 219 220 for (unsigned int i = 0; i < c->num_user_features; i++) 221 { 222 const hb_feature_t *feature = &c->user_features[i]; 223 if (!(feature->start == 0 && feature->end == (unsigned int)-1)) { 224 unsigned int shift; 225 hb_mask_t mask = c->plan->map.get_mask (feature->tag, &shift); 226 c->buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); 227 } 228 } 229} 230 231 232/* Main shaper */ 233 234/* Prepare */ 235 236static void 237hb_set_unicode_props (hb_buffer_t *buffer) 238{ 239 unsigned int count = buffer->len; 240 for (unsigned int i = 0; i < count; i++) 241 _hb_glyph_info_set_unicode_props (&buffer->info[i], buffer->unicode); 242} 243 244static void 245hb_form_clusters (hb_buffer_t *buffer) 246{ 247 unsigned int count = buffer->len; 248 for (unsigned int i = 1; i < count; i++) 249 if (FLAG (_hb_glyph_info_get_general_category (&buffer->info[i])) & 250 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | 251 FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | 252 FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) 253 buffer->merge_clusters (i - 1, i + 1); 254} 255 256static void 257hb_ensure_native_direction (hb_buffer_t *buffer) 258{ 259 hb_direction_t direction = buffer->props.direction; 260 261 /* TODO vertical: 262 * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType 263 * Ogham fonts are supposed to be implemented BTT or not. Need to research that 264 * first. */ 265 if ((HB_DIRECTION_IS_HORIZONTAL (direction) && direction != hb_script_get_horizontal_direction (buffer->props.script)) || 266 (HB_DIRECTION_IS_VERTICAL (direction) && direction != HB_DIRECTION_TTB)) 267 { 268 hb_buffer_reverse_clusters (buffer); 269 buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); 270 } 271} 272 273 274/* Substitute */ 275 276static void 277hb_mirror_chars (hb_ot_shape_context_t *c) 278{ 279 hb_unicode_funcs_t *unicode = c->buffer->unicode; 280 281 if (HB_DIRECTION_IS_FORWARD (c->target_direction)) 282 return; 283 284 hb_mask_t rtlm_mask = c->plan->map.get_1_mask (HB_TAG ('r','t','l','m')); 285 286 unsigned int count = c->buffer->len; 287 for (unsigned int i = 0; i < count; i++) { 288 hb_codepoint_t codepoint = unicode->mirroring (c->buffer->info[i].codepoint); 289 if (likely (codepoint == c->buffer->info[i].codepoint)) 290 c->buffer->info[i].mask |= rtlm_mask; /* XXX this should be moved to before setting user-feature masks */ 291 else 292 c->buffer->info[i].codepoint = codepoint; 293 } 294} 295 296static void 297hb_map_glyphs (hb_font_t *font, 298 hb_buffer_t *buffer) 299{ 300 hb_codepoint_t glyph; 301 302 if (unlikely (!buffer->len)) 303 return; 304 305 buffer->clear_output (); 306 307 unsigned int count = buffer->len - 1; 308 for (buffer->idx = 0; buffer->idx < count;) { 309 if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) { 310 font->get_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &glyph); 311 buffer->replace_glyphs (2, 1, &glyph); 312 } else { 313 font->get_glyph (buffer->cur().codepoint, 0, &glyph); 314 buffer->replace_glyph (glyph); 315 } 316 } 317 if (likely (buffer->idx < buffer->len)) { 318 font->get_glyph (buffer->cur().codepoint, 0, &glyph); 319 buffer->replace_glyph (glyph); 320 } 321 buffer->swap_buffers (); 322} 323 324static void 325hb_substitute_default (hb_ot_shape_context_t *c) 326{ 327 hb_mirror_chars (c); 328 329 hb_map_glyphs (c->font, c->buffer); 330} 331 332static void 333hb_synthesize_glyph_classes (hb_ot_shape_context_t *c) 334{ 335 unsigned int count = c->buffer->len; 336 for (unsigned int i = 0; i < count; i++) 337 c->buffer->info[i].glyph_props() = FLAG (_hb_glyph_info_get_general_category (&c->buffer->info[i])) & 338 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | 339 FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | 340 FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)) ? 341 HB_OT_LAYOUT_GLYPH_CLASS_MARK : 342 HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH; 343} 344 345 346static void 347hb_ot_substitute_complex (hb_ot_shape_context_t *c) 348{ 349 hb_ot_layout_substitute_start (c->font, c->buffer); 350 351 if (!hb_ot_layout_has_glyph_classes (c->face)) 352 hb_synthesize_glyph_classes (c); 353 354 if (hb_ot_layout_has_substitution (c->face)) 355 c->plan->map.substitute (c->font, c->buffer); 356 357 hb_ot_layout_substitute_finish (c->font, c->buffer); 358 359 return; 360} 361 362 363/* Position */ 364 365static void 366hb_position_default (hb_ot_shape_context_t *c) 367{ 368 hb_ot_layout_position_start (c->font, c->buffer); 369 370 unsigned int count = c->buffer->len; 371 for (unsigned int i = 0; i < count; i++) { 372 c->font->get_glyph_advance_for_direction (c->buffer->info[i].codepoint, 373 c->buffer->props.direction, 374 &c->buffer->pos[i].x_advance, 375 &c->buffer->pos[i].y_advance); 376 c->font->subtract_glyph_origin_for_direction (c->buffer->info[i].codepoint, 377 c->buffer->props.direction, 378 &c->buffer->pos[i].x_offset, 379 &c->buffer->pos[i].y_offset); 380 } 381} 382 383static void 384hb_zero_mark_advances (hb_ot_shape_context_t *c) 385{ 386 unsigned int count = c->buffer->len; 387 for (unsigned int i = 0; i < count; i++) 388 if (c->buffer->info[i].glyph_props() & HB_OT_LAYOUT_GLYPH_CLASS_MARK) 389 { 390 c->buffer->pos[i].x_advance = 0; 391 c->buffer->pos[i].y_advance = 0; 392 } 393} 394 395static void 396hb_ot_position_complex (hb_ot_shape_context_t *c) 397{ 398 399 if (hb_ot_layout_has_positioning (c->face)) 400 { 401 /* Change glyph origin to what GPOS expects, apply GPOS, change it back. */ 402 403 unsigned int count = c->buffer->len; 404 for (unsigned int i = 0; i < count; i++) { 405 c->font->add_glyph_origin_for_direction (c->buffer->info[i].codepoint, 406 HB_DIRECTION_LTR, 407 &c->buffer->pos[i].x_offset, 408 &c->buffer->pos[i].y_offset); 409 } 410 411 c->plan->map.position (c->font, c->buffer); 412 413 for (unsigned int i = 0; i < count; i++) { 414 c->font->subtract_glyph_origin_for_direction (c->buffer->info[i].codepoint, 415 HB_DIRECTION_LTR, 416 &c->buffer->pos[i].x_offset, 417 &c->buffer->pos[i].y_offset); 418 } 419 420 c->applied_position_complex = true; 421 } else 422 hb_zero_mark_advances (c); 423 424 hb_ot_layout_position_finish (c->font, c->buffer, c->plan->shaper->zero_width_attached_marks); 425 426 return; 427} 428 429static void 430hb_position_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED) 431{ 432 /* TODO Mark pos */ 433} 434 435static void 436hb_truetype_kern (hb_ot_shape_context_t *c) 437{ 438 /* TODO Check for kern=0 */ 439 unsigned int count = c->buffer->len; 440 for (unsigned int i = 1; i < count; i++) { 441 hb_position_t x_kern, y_kern, kern1, kern2; 442 c->font->get_glyph_kerning_for_direction (c->buffer->info[i - 1].codepoint, c->buffer->info[i].codepoint, 443 c->buffer->props.direction, 444 &x_kern, &y_kern); 445 446 kern1 = x_kern >> 1; 447 kern2 = x_kern - kern1; 448 c->buffer->pos[i - 1].x_advance += kern1; 449 c->buffer->pos[i].x_advance += kern2; 450 c->buffer->pos[i].x_offset += kern2; 451 452 kern1 = y_kern >> 1; 453 kern2 = y_kern - kern1; 454 c->buffer->pos[i - 1].y_advance += kern1; 455 c->buffer->pos[i].y_advance += kern2; 456 c->buffer->pos[i].y_offset += kern2; 457 } 458} 459 460static void 461hb_position_complex_fallback_visual (hb_ot_shape_context_t *c) 462{ 463 hb_truetype_kern (c); 464} 465 466static void 467hb_hide_zerowidth (hb_ot_shape_context_t *c) 468{ 469 hb_codepoint_t space; 470 if (!c->font->get_glyph (' ', 0, &space)) 471 return; /* No point! */ 472 473 unsigned int count = c->buffer->len; 474 for (unsigned int i = 0; i < count; i++) 475 if (unlikely (!is_a_ligature (c->buffer->info[i]) && 476 _hb_glyph_info_is_zero_width (&c->buffer->info[i]))) { 477 c->buffer->info[i].codepoint = space; 478 c->buffer->pos[i].x_advance = 0; 479 c->buffer->pos[i].y_advance = 0; 480 } 481} 482 483 484/* Do it! */ 485 486static void 487hb_ot_shape_internal (hb_ot_shape_context_t *c) 488{ 489 c->buffer->deallocate_var_all (); 490 491 /* Save the original direction, we use it later. */ 492 c->target_direction = c->buffer->props.direction; 493 494 HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props0); 495 HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props1); 496 497 c->buffer->clear_output (); 498 499 hb_set_unicode_props (c->buffer); 500 501 hb_form_clusters (c->buffer); 502 503 hb_ensure_native_direction (c->buffer); 504 505 _hb_ot_shape_normalize (c->font, c->buffer, 506 c->plan->shaper->normalization_preference ? 507 c->plan->shaper->normalization_preference (c->plan) : 508 HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT); 509 510 hb_ot_shape_setup_masks (c); 511 512 /* SUBSTITUTE */ 513 { 514 hb_substitute_default (c); 515 516 hb_ot_substitute_complex (c); 517 } 518 519 /* POSITION */ 520 { 521 hb_position_default (c); 522 523 hb_ot_position_complex (c); 524 525 hb_bool_t position_fallback = !c->applied_position_complex; 526 if (position_fallback) 527 hb_position_complex_fallback (c); 528 529 if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) 530 hb_buffer_reverse (c->buffer); 531 532 if (position_fallback) 533 hb_position_complex_fallback_visual (c); 534 } 535 536 hb_hide_zerowidth (c); 537 538 HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props1); 539 HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props0); 540 541 c->buffer->props.direction = c->target_direction; 542 543 c->buffer->deallocate_var_all (); 544} 545 546 547hb_bool_t 548_hb_ot_shape (hb_shape_plan_t *shape_plan, 549 hb_font_t *font, 550 hb_buffer_t *buffer, 551 const hb_feature_t *features, 552 unsigned int num_features) 553{ 554 hb_ot_shape_context_t c = {HB_SHAPER_DATA_GET (shape_plan), font, font->face, buffer, features, num_features}; 555 hb_ot_shape_internal (&c); 556 557 return true; 558} 559 560 561void 562hb_ot_shape_glyphs_closure (hb_font_t *font, 563 hb_buffer_t *buffer, 564 const hb_feature_t *features, 565 unsigned int num_features, 566 hb_set_t *glyphs) 567{ 568 hb_ot_shape_plan_t plan; 569 570 buffer->guess_properties (); 571 572 /* TODO cache / ensure correct backend, etc. */ 573 hb_shape_plan_t *shape_plan = hb_shape_plan_create (font->face, &buffer->props, features, num_features, NULL); 574 575 /* TODO: normalization? have shapers do closure()? */ 576 /* TODO: Deal with mirrored chars? */ 577 hb_map_glyphs (font, buffer); 578 579 /* Seed it. It's user's responsibility to have cleard glyphs 580 * if that's what they desire. */ 581 unsigned int count = buffer->len; 582 for (unsigned int i = 0; i < count; i++) 583 hb_set_add (glyphs, buffer->info[i].codepoint); 584 585 /* And find transitive closure. */ 586 hb_set_t copy; 587 copy.init (); 588 589 do { 590 copy.set (glyphs); 591 HB_SHAPER_DATA_GET (shape_plan)->map.substitute_closure (font->face, glyphs); 592 } while (!copy.equal (glyphs)); 593 594 hb_shape_plan_destroy (shape_plan); 595} 596