hb-ot-layout.cc revision 88474c6fdaf35c56368694a5b164f4988a004d49
1/* 2 * Copyright (C) 1998-2004 David Turner and Werner Lemberg 3 * Copyright (C) 2006 Behdad Esfahbod 4 * Copyright (C) 2007,2008,2009 Red Hat, Inc. 5 * 6 * This is part of HarfBuzz, a text shaping library. 7 * 8 * Permission is hereby granted, without written agreement and without 9 * license or royalty fees, to use, copy, modify, and distribute this 10 * software and its documentation for any purpose, provided that the 11 * above copyright notice and the following two paragraphs appear in 12 * all copies of this software. 13 * 14 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 15 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 16 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 17 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 18 * DAMAGE. 19 * 20 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 21 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 22 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 23 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 24 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 25 * 26 * Red Hat Author(s): Behdad Esfahbod 27 */ 28 29#define HB_OT_LAYOUT_CC 30 31#include "hb-ot-layout-private.hh" 32 33#include "hb-ot-layout-gdef-private.hh" 34#include "hb-ot-layout-gsub-private.hh" 35#include "hb-ot-layout-gpos-private.hh" 36 37 38#include <stdlib.h> 39#include <string.h> 40 41HB_BEGIN_DECLS 42 43 44hb_ot_layout_t * 45_hb_ot_layout_new (hb_face_t *face) 46{ 47 /* Remove this object altogether */ 48 hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t)); 49 50 layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_get_table (face, HB_OT_TAG_GDEF)); 51 layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob); 52 53 layout->gsub_blob = Sanitizer<GSUB>::sanitize (hb_face_get_table (face, HB_OT_TAG_GSUB)); 54 layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_blob); 55 56 layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_get_table (face, HB_OT_TAG_GPOS)); 57 layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob); 58 59 return layout; 60} 61 62void 63_hb_ot_layout_free (hb_ot_layout_t *layout) 64{ 65 hb_blob_unlock (layout->gdef_blob); 66 hb_blob_unlock (layout->gsub_blob); 67 hb_blob_unlock (layout->gpos_blob); 68 69 hb_blob_destroy (layout->gdef_blob); 70 hb_blob_destroy (layout->gsub_blob); 71 hb_blob_destroy (layout->gpos_blob); 72 73 free (layout->new_gdef.klasses); 74 75 free (layout); 76} 77 78static const GDEF& 79_get_gdef (hb_face_t *face) 80{ 81 return likely (face->ot_layout && face->ot_layout->gdef) ? *face->ot_layout->gdef : Null(GDEF); 82} 83 84static const GSUB& 85_get_gsub (hb_face_t *face) 86{ 87 return likely (face->ot_layout && face->ot_layout->gsub) ? *face->ot_layout->gsub : Null(GSUB); 88} 89 90static const GPOS& 91_get_gpos (hb_face_t *face) 92{ 93 return likely (face->ot_layout && face->ot_layout->gpos) ? *face->ot_layout->gpos : Null(GPOS); 94} 95 96 97/* 98 * GDEF 99 */ 100 101/* TODO the public class_t is a mess */ 102 103hb_bool_t 104hb_ot_layout_has_glyph_classes (hb_face_t *face) 105{ 106 return _get_gdef (face).has_glyph_classes (); 107} 108 109hb_bool_t 110_hb_ot_layout_has_new_glyph_classes (hb_face_t *face) 111{ 112 return face->ot_layout->new_gdef.len > 0; 113} 114 115static unsigned int 116_hb_ot_layout_get_glyph_property (hb_face_t *face, 117 hb_codepoint_t glyph) 118{ 119 hb_ot_layout_class_t klass; 120 const GDEF &gdef = _get_gdef (face); 121 122 klass = gdef.get_glyph_class (glyph); 123 124 if (!klass && glyph < face->ot_layout->new_gdef.len) 125 klass = face->ot_layout->new_gdef.klasses[glyph]; 126 127 switch (klass) { 128 default: 129 case GDEF::UnclassifiedGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED; 130 case GDEF::BaseGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH; 131 case GDEF::LigatureGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE; 132 case GDEF::ComponentGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT; 133 case GDEF::MarkGlyph: 134 klass = gdef.get_mark_attachment_type (glyph); 135 return HB_OT_LAYOUT_GLYPH_CLASS_MARK + (klass << 8); 136 } 137} 138 139hb_bool_t 140_hb_ot_layout_check_glyph_property (hb_face_t *face, 141 hb_glyph_info_t *ginfo, 142 unsigned int lookup_flags, 143 unsigned int *property_out) 144{ 145 unsigned int property; 146 147 if (ginfo->gproperty() == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN) 148 ginfo->gproperty() = _hb_ot_layout_get_glyph_property (face, ginfo->codepoint); 149 property = ginfo->gproperty(); 150 if (property_out) 151 *property_out = property; 152 153 /* Not covered, if, for example, glyph class is ligature and 154 * lookup_flags includes LookupFlags::IgnoreLigatures 155 */ 156 if (property & lookup_flags & LookupFlag::IgnoreFlags) 157 return false; 158 159 if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) 160 { 161 /* If using mark filtering sets, the high short of 162 * lookup_flags has the set index. 163 */ 164 if (lookup_flags & LookupFlag::UseMarkFilteringSet) 165 return _get_gdef (face).mark_set_covers (lookup_flags >> 16, ginfo->codepoint); 166 167 /* The second byte of lookup_flags has the meaning 168 * "ignore marks of attachment type different than 169 * the attachment type specified." 170 */ 171 if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType) 172 return (lookup_flags & LookupFlag::MarkAttachmentType) == (property & LookupFlag::MarkAttachmentType); 173 } 174 175 return true; 176} 177 178hb_bool_t 179_hb_ot_layout_skip_mark (hb_face_t *face, 180 hb_glyph_info_t *ginfo, 181 unsigned int lookup_flags, 182 unsigned int *property_out) 183{ 184 unsigned int property; 185 186 if (ginfo->gproperty() == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN) 187 ginfo->gproperty() = _hb_ot_layout_get_glyph_property (face, ginfo->codepoint); 188 property = ginfo->gproperty(); 189 if (property_out) 190 *property_out = property; 191 192 if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) 193 { 194 /* Skip mark if lookup_flags includes LookupFlags::IgnoreMarks */ 195 if (lookup_flags & LookupFlag::IgnoreMarks) 196 return true; 197 198 /* If using mark filtering sets, the high short of lookup_flags has the set index. */ 199 if (lookup_flags & LookupFlag::UseMarkFilteringSet) 200 return !_get_gdef (face).mark_set_covers (lookup_flags >> 16, ginfo->codepoint); 201 202 /* The second byte of lookup_flags has the meaning "ignore marks of attachment type 203 * different than the attachment type specified." */ 204 if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType) 205 return (lookup_flags & LookupFlag::MarkAttachmentType) != (property & LookupFlag::MarkAttachmentType); 206 } 207 208 return false; 209} 210 211void 212_hb_ot_layout_set_glyph_class (hb_face_t *face, 213 hb_codepoint_t glyph, 214 hb_ot_layout_glyph_class_t klass) 215{ 216 if (HB_OBJECT_IS_INERT (face)) 217 return; 218 219 /* TODO optimize this? similar to old harfbuzz code for example */ 220 221 hb_ot_layout_t *layout = face->ot_layout; 222 hb_ot_layout_class_t gdef_klass; 223 unsigned int len = layout->new_gdef.len; 224 225 if (unlikely (glyph > 65535)) 226 return; 227 228 /* XXX this is not threadsafe */ 229 if (glyph >= len) { 230 unsigned int new_len; 231 unsigned char *new_klasses; 232 233 new_len = len == 0 ? 120 : 2 * len; 234 while (new_len <= glyph) 235 new_len *= 2; 236 237 if (new_len > 65536) 238 new_len = 65536; 239 new_klasses = (unsigned char *) realloc (layout->new_gdef.klasses, new_len * sizeof (unsigned char)); 240 241 if (unlikely (!new_klasses)) 242 return; 243 244 memset (new_klasses + len, 0, new_len - len); 245 246 layout->new_gdef.klasses = new_klasses; 247 layout->new_gdef.len = new_len; 248 } 249 250 switch (klass) { 251 default: 252 case HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED: gdef_klass = GDEF::UnclassifiedGlyph; break; 253 case HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH: gdef_klass = GDEF::BaseGlyph; break; 254 case HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE: gdef_klass = GDEF::LigatureGlyph; break; 255 case HB_OT_LAYOUT_GLYPH_CLASS_MARK: gdef_klass = GDEF::MarkGlyph; break; 256 case HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT: gdef_klass = GDEF::ComponentGlyph; break; 257 } 258 259 layout->new_gdef.klasses[glyph] = gdef_klass; 260 return; 261} 262 263void 264_hb_ot_layout_set_glyph_property (hb_face_t *face, 265 hb_codepoint_t glyph, 266 unsigned int property) 267{ _hb_ot_layout_set_glyph_class (face, glyph, (hb_ot_layout_glyph_class_t) (property & 0xff)); } 268 269 270hb_ot_layout_glyph_class_t 271hb_ot_layout_get_glyph_class (hb_face_t *face, 272 hb_codepoint_t glyph) 273{ 274 return (hb_ot_layout_glyph_class_t) (_hb_ot_layout_get_glyph_property (face, glyph) & 0xff); 275} 276 277void 278hb_ot_layout_set_glyph_class (hb_face_t *face, 279 hb_codepoint_t glyph, 280 hb_ot_layout_glyph_class_t klass) 281{ 282 _hb_ot_layout_set_glyph_class (face, glyph, klass); 283} 284 285void 286hb_ot_layout_build_glyph_classes (hb_face_t *face, 287 hb_codepoint_t *glyphs, 288 unsigned char *klasses, 289 uint16_t count) 290{ 291 if (HB_OBJECT_IS_INERT (face)) 292 return; 293 294 hb_ot_layout_t *layout = face->ot_layout; 295 296 if (unlikely (!count || !glyphs || !klasses)) 297 return; 298 299 if (layout->new_gdef.len == 0) { 300 layout->new_gdef.klasses = (unsigned char *) calloc (count, sizeof (unsigned char)); 301 layout->new_gdef.len = count; 302 } 303 304 for (unsigned int i = 0; i < count; i++) 305 _hb_ot_layout_set_glyph_class (face, glyphs[i], (hb_ot_layout_glyph_class_t) klasses[i]); 306} 307 308unsigned int 309hb_ot_layout_get_attach_points (hb_face_t *face, 310 hb_codepoint_t glyph, 311 unsigned int start_offset, 312 unsigned int *point_count /* IN/OUT */, 313 unsigned int *point_array /* OUT */) 314{ 315 return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array); 316} 317 318unsigned int 319hb_ot_layout_get_ligature_carets (hb_font_t *font, 320 hb_face_t *face, 321 hb_direction_t direction, 322 hb_codepoint_t glyph, 323 unsigned int start_offset, 324 unsigned int *caret_count /* IN/OUT */, 325 int *caret_array /* OUT */) 326{ 327 hb_ot_layout_context_t c; 328 c.font = font; 329 c.face = face; 330 return _get_gdef (face).get_lig_carets (&c, direction, glyph, start_offset, caret_count, caret_array); 331} 332 333/* 334 * GSUB/GPOS 335 */ 336 337static const GSUBGPOS& 338get_gsubgpos_table (hb_face_t *face, 339 hb_tag_t table_tag) 340{ 341 switch (table_tag) { 342 case HB_OT_TAG_GSUB: return _get_gsub (face); 343 case HB_OT_TAG_GPOS: return _get_gpos (face); 344 default: return Null(GSUBGPOS); 345 } 346} 347 348 349unsigned int 350hb_ot_layout_table_get_script_tags (hb_face_t *face, 351 hb_tag_t table_tag, 352 unsigned int start_offset, 353 unsigned int *script_count /* IN/OUT */, 354 hb_tag_t *script_tags /* OUT */) 355{ 356 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 357 358 return g.get_script_tags (start_offset, script_count, script_tags); 359} 360 361hb_bool_t 362hb_ot_layout_table_find_script (hb_face_t *face, 363 hb_tag_t table_tag, 364 hb_tag_t script_tag, 365 unsigned int *script_index) 366{ 367 ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 368 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 369 370 if (g.find_script_index (script_tag, script_index)) 371 return TRUE; 372 373 /* try finding 'DFLT' */ 374 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) 375 return FALSE; 376 377 /* try with 'dflt'; MS site has had typos and many fonts use it now :(. 378 * including many versions of DejaVu Sans Mono! */ 379 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) 380 return FALSE; 381 382 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 383 return FALSE; 384} 385 386hb_bool_t 387hb_ot_layout_table_choose_script (hb_face_t *face, 388 hb_tag_t table_tag, 389 const hb_tag_t *script_tags, 390 unsigned int *script_index) 391{ 392 ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 393 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 394 395 while (*script_tags) 396 { 397 if (g.find_script_index (*script_tags, script_index)) 398 return TRUE; 399 script_tags++; 400 } 401 402 /* try finding 'DFLT' */ 403 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) 404 return FALSE; 405 406 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 407 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) 408 return FALSE; 409 410 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 411 return FALSE; 412} 413 414unsigned int 415hb_ot_layout_table_get_feature_tags (hb_face_t *face, 416 hb_tag_t table_tag, 417 unsigned int start_offset, 418 unsigned int *feature_count /* IN/OUT */, 419 hb_tag_t *feature_tags /* OUT */) 420{ 421 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 422 423 return g.get_feature_tags (start_offset, feature_count, feature_tags); 424} 425 426 427unsigned int 428hb_ot_layout_script_get_language_tags (hb_face_t *face, 429 hb_tag_t table_tag, 430 unsigned int script_index, 431 unsigned int start_offset, 432 unsigned int *language_count /* IN/OUT */, 433 hb_tag_t *language_tags /* OUT */) 434{ 435 const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 436 437 return s.get_lang_sys_tags (start_offset, language_count, language_tags); 438} 439 440hb_bool_t 441hb_ot_layout_script_find_language (hb_face_t *face, 442 hb_tag_t table_tag, 443 unsigned int script_index, 444 hb_tag_t language_tag, 445 unsigned int *language_index) 446{ 447 ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); 448 const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 449 450 if (s.find_lang_sys_index (language_tag, language_index)) 451 return TRUE; 452 453 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 454 if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) 455 return FALSE; 456 457 if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; 458 return FALSE; 459} 460 461hb_bool_t 462hb_ot_layout_language_get_required_feature_index (hb_face_t *face, 463 hb_tag_t table_tag, 464 unsigned int script_index, 465 unsigned int language_index, 466 unsigned int *feature_index) 467{ 468 const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index); 469 470 if (feature_index) *feature_index = l.get_required_feature_index (); 471 472 return l.has_required_feature (); 473} 474 475unsigned int 476hb_ot_layout_language_get_feature_indexes (hb_face_t *face, 477 hb_tag_t table_tag, 478 unsigned int script_index, 479 unsigned int language_index, 480 unsigned int start_offset, 481 unsigned int *feature_count /* IN/OUT */, 482 unsigned int *feature_indexes /* OUT */) 483{ 484 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 485 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 486 487 return l.get_feature_indexes (start_offset, feature_count, feature_indexes); 488} 489 490unsigned int 491hb_ot_layout_language_get_feature_tags (hb_face_t *face, 492 hb_tag_t table_tag, 493 unsigned int script_index, 494 unsigned int language_index, 495 unsigned int start_offset, 496 unsigned int *feature_count /* IN/OUT */, 497 hb_tag_t *feature_tags /* OUT */) 498{ 499 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 500 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 501 502 ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t)); 503 unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); 504 505 if (feature_tags) { 506 unsigned int count = *feature_count; 507 for (unsigned int i = 0; i < count; i++) 508 feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); 509 } 510 511 return ret; 512} 513 514 515hb_bool_t 516hb_ot_layout_language_find_feature (hb_face_t *face, 517 hb_tag_t table_tag, 518 unsigned int script_index, 519 unsigned int language_index, 520 hb_tag_t feature_tag, 521 unsigned int *feature_index) 522{ 523 ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); 524 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 525 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 526 527 unsigned int num_features = l.get_feature_count (); 528 for (unsigned int i = 0; i < num_features; i++) { 529 unsigned int f_index = l.get_feature_index (i); 530 531 if (feature_tag == g.get_feature_tag (f_index)) { 532 if (feature_index) *feature_index = f_index; 533 return TRUE; 534 } 535 } 536 537 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 538 return FALSE; 539} 540 541unsigned int 542hb_ot_layout_feature_get_lookup_indexes (hb_face_t *face, 543 hb_tag_t table_tag, 544 unsigned int feature_index, 545 unsigned int start_offset, 546 unsigned int *lookup_count /* IN/OUT */, 547 unsigned int *lookup_indexes /* OUT */) 548{ 549 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 550 const Feature &f = g.get_feature (feature_index); 551 552 return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); 553} 554 555 556/* 557 * GSUB 558 */ 559 560hb_bool_t 561hb_ot_layout_has_substitution (hb_face_t *face) 562{ 563 return &_get_gsub (face) != &Null(GSUB); 564} 565 566hb_bool_t 567hb_ot_layout_substitute_lookup (hb_face_t *face, 568 hb_buffer_t *buffer, 569 unsigned int lookup_index, 570 hb_mask_t mask) 571{ 572 hb_ot_layout_context_t c; 573 c.font = NULL; 574 c.face = face; 575 return _get_gsub (face).substitute_lookup (&c, buffer, lookup_index, mask); 576} 577 578 579/* 580 * GPOS 581 */ 582 583hb_bool_t 584hb_ot_layout_has_positioning (hb_face_t *face) 585{ 586 return &_get_gpos (face) != &Null(GPOS); 587} 588 589hb_bool_t 590hb_ot_layout_position_lookup (hb_font_t *font, 591 hb_face_t *face, 592 hb_buffer_t *buffer, 593 unsigned int lookup_index, 594 hb_mask_t mask) 595{ 596 hb_ot_layout_context_t c; 597 c.font = font; 598 c.face = face; 599 return _get_gpos (face).position_lookup (&c, buffer, lookup_index, mask); 600} 601 602void 603hb_ot_layout_position_finish (hb_font_t *font HB_UNUSED, 604 hb_face_t *face HB_UNUSED, 605 hb_buffer_t *buffer) 606{ 607 unsigned int i, j; 608 unsigned int len = hb_buffer_get_length (buffer); 609 hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer); 610 hb_direction_t direction = buffer->props.direction; 611 612 /* TODO: Vertical */ 613 614 /* Handle cursive connections: 615 * First handle all chain-back connections, then handle all chain-forward connections. */ 616 if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) 617 { 618 for (j = 0; j < len; j++) { 619 if (pos[j].cursive_chain() < 0) 620 pos[j].y_offset += pos[j + pos[j].cursive_chain()].y_offset; 621 } 622 for (i = len; i > 0; i--) { 623 j = i - 1; 624 if (pos[j].cursive_chain() > 0) 625 pos[j].y_offset += pos[j + pos[j].cursive_chain()].y_offset; 626 } 627 } 628 else 629 { 630 for (j = 0; j < len; j++) { 631 if (pos[j].cursive_chain() < 0) 632 pos[j].x_offset += pos[j + pos[j].cursive_chain()].x_offset; 633 } 634 for (i = len; i > 0; i--) { 635 j = i - 1; 636 if (pos[j].cursive_chain() > 0) 637 pos[j].x_offset += pos[j + pos[j].cursive_chain()].x_offset; 638 } 639 } 640 641 642 /* Handle attachments */ 643 for (i = 0; i < len; i++) 644 if (pos[i].back()) 645 { 646 unsigned int back = i - pos[i].back(); 647 pos[i].x_offset += pos[back].x_offset; 648 pos[i].y_offset += pos[back].y_offset; 649 650 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) 651 for (j = back + 1; j < i + 1; j++) { 652 pos[i].x_offset += pos[j].x_advance; 653 pos[i].y_offset += pos[j].y_advance; 654 } 655 else 656 for (j = back; j < i; j++) { 657 pos[i].x_offset -= pos[j].x_advance; 658 pos[i].y_offset -= pos[j].y_advance; 659 } 660 } 661} 662 663 664HB_END_DECLS 665