hb-coretext-shape.cc (29851B)
1 /* 2 * Copyright © 2012,2013 Mozilla Foundation. 3 * Copyright © 2012,2013 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 * Mozilla Author(s): Jonathan Kew 26 * Google Author(s): Behdad Esfahbod 27 */ 28 29 #include "hb.hh" 30 31 #ifdef HAVE_CORETEXT 32 33 #include "hb-shaper-impl.hh" 34 35 #include "hb-coretext.hh" 36 #include "hb-aat-layout.hh" 37 38 hb_coretext_face_data_t * 39 _hb_coretext_shaper_face_data_create (hb_face_t *face) 40 { 41 CGFontRef cg_font = create_cg_font (face); 42 43 if (unlikely (!cg_font)) 44 { 45 DEBUG_MSG (CORETEXT, face, "CGFont creation failed.."); 46 return nullptr; 47 } 48 49 return (hb_coretext_face_data_t *) cg_font; 50 } 51 52 void 53 _hb_coretext_shaper_face_data_destroy (hb_coretext_face_data_t *data) 54 { 55 CFRelease ((CGFontRef) data); 56 } 57 58 59 hb_coretext_font_data_t * 60 _hb_coretext_shaper_font_data_create (hb_font_t *font) 61 { 62 hb_face_t *face = font->face; 63 const hb_coretext_face_data_t *face_data = face->data.coretext; 64 if (unlikely (!face_data)) return nullptr; 65 CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; 66 67 CGFloat font_size = (CGFloat) (font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE); 68 CTFontRef ct_font = create_ct_font (cg_font, font_size); 69 70 if (unlikely (!ct_font)) 71 { 72 DEBUG_MSG (CORETEXT, font, "CGFont creation failed.."); 73 return nullptr; 74 } 75 76 if (font->num_coords) 77 { 78 CFMutableDictionaryRef variations = 79 CFDictionaryCreateMutable (kCFAllocatorDefault, 80 font->num_coords, 81 &kCFTypeDictionaryKeyCallBacks, 82 &kCFTypeDictionaryValueCallBacks); 83 84 unsigned count = font->num_coords; 85 for (unsigned i = 0; i < count; i++) 86 { 87 hb_ot_var_axis_info_t info; 88 unsigned int c = 1; 89 hb_ot_var_get_axis_infos (font->face, i, &c, &info); 90 91 float v = hb_clamp (font->design_coords[i], info.min_value, info.max_value); 92 93 CFNumberRef tag_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag); 94 CFNumberRef value_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberFloatType, &v); 95 CFDictionarySetValue (variations, tag_number, value_number); 96 CFRelease (tag_number); 97 CFRelease (value_number); 98 } 99 100 CFDictionaryRef attributes = 101 CFDictionaryCreate (kCFAllocatorDefault, 102 (const void **) &kCTFontVariationAttribute, 103 (const void **) &variations, 104 1, 105 &kCFTypeDictionaryKeyCallBacks, 106 &kCFTypeDictionaryValueCallBacks); 107 108 CTFontDescriptorRef varDesc = CTFontDescriptorCreateWithAttributes (attributes); 109 CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0, nullptr, varDesc); 110 111 CFRelease (ct_font); 112 CFRelease (attributes); 113 CFRelease (variations); 114 ct_font = new_ct_font; 115 } 116 117 return (hb_coretext_font_data_t *) ct_font; 118 } 119 120 void 121 _hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data) 122 { 123 CFRelease ((CTFontRef) data); 124 } 125 126 /* 127 * shaper 128 */ 129 130 struct feature_record_t { 131 unsigned int feature; 132 unsigned int setting; 133 }; 134 135 struct active_feature_t { 136 feature_record_t rec; 137 unsigned int order; 138 139 HB_INTERNAL static int cmp (const void *pa, const void *pb) { 140 const active_feature_t *a = (const active_feature_t *) pa; 141 const active_feature_t *b = (const active_feature_t *) pb; 142 return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : 143 a->order < b->order ? -1 : a->order > b->order ? 1 : 144 a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 : 145 0; 146 } 147 bool operator== (const active_feature_t& f) const { 148 return cmp (this, &f) == 0; 149 } 150 }; 151 152 struct feature_event_t { 153 unsigned int index; 154 bool start; 155 active_feature_t feature; 156 157 HB_INTERNAL static int cmp (const void *pa, const void *pb) { 158 const feature_event_t *a = (const feature_event_t *) pa; 159 const feature_event_t *b = (const feature_event_t *) pb; 160 return a->index < b->index ? -1 : a->index > b->index ? 1 : 161 a->start < b->start ? -1 : a->start > b->start ? 1 : 162 active_feature_t::cmp (&a->feature, &b->feature); 163 } 164 }; 165 166 struct range_record_t { 167 CTFontRef font; 168 unsigned int index_first; /* == start */ 169 unsigned int index_last; /* == end - 1 */ 170 }; 171 172 173 hb_bool_t 174 _hb_coretext_shape (hb_shape_plan_t *shape_plan, 175 hb_font_t *font, 176 hb_buffer_t *buffer, 177 const hb_feature_t *features, 178 unsigned int num_features) 179 { 180 hb_face_t *face = font->face; 181 CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; 182 CTFontRef ct_font = (CTFontRef) (const void *) font->data.coretext; 183 184 CGFloat ct_font_size = CTFontGetSize (ct_font); 185 CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size; 186 CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size; 187 188 /* Attach marks to their bases, to match the 'ot' shaper. 189 * Adapted from a very old version of hb-ot-shape:hb_form_clusters(). 190 * Note that this only makes us be closer to the 'ot' shaper, 191 * but by no means the same. For example, if there's 192 * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will 193 * continue pointing to B2 even though B2 was merged into B1's 194 * cluster... */ 195 if (HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (buffer->cluster_level)) 196 { 197 hb_unicode_funcs_t *unicode = buffer->unicode; 198 unsigned int count = buffer->len; 199 hb_glyph_info_t *info = buffer->info; 200 for (unsigned int i = 1; i < count; i++) 201 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint))) 202 buffer->merge_clusters (i - 1, i + 1); 203 } 204 205 hb_vector_t<range_record_t> range_records; 206 207 /* 208 * Set up features. 209 * (copied + modified from code from hb-uniscribe.cc) 210 */ 211 if (num_features) 212 { 213 /* Sort features by start/end events. */ 214 hb_vector_t<feature_event_t> feature_events; 215 for (unsigned int i = 0; i < num_features; i++) 216 { 217 active_feature_t feature; 218 219 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 220 const hb_aat_feature_mapping_t * mapping = hb_aat_layout_find_feature_mapping (features[i].tag); 221 if (!mapping) 222 continue; 223 224 feature.rec.feature = mapping->aatFeatureType; 225 feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable; 226 #else 227 feature.rec.feature = features[i].tag; 228 feature.rec.setting = features[i].value; 229 #endif 230 feature.order = i; 231 232 feature_event_t *event; 233 234 event = feature_events.push (); 235 event->index = features[i].start; 236 event->start = true; 237 event->feature = feature; 238 239 event = feature_events.push (); 240 event->index = features[i].end; 241 event->start = false; 242 event->feature = feature; 243 } 244 feature_events.qsort (); 245 /* Add a strategic final event. */ 246 { 247 active_feature_t feature; 248 feature.rec.feature = HB_TAG_NONE; 249 feature.rec.setting = 0; 250 feature.order = num_features + 1; 251 252 feature_event_t *event = feature_events.push (); 253 event->index = 0; /* This value does magic. */ 254 event->start = false; 255 event->feature = feature; 256 } 257 258 /* Scan events and save features for each range. */ 259 hb_vector_t<active_feature_t> active_features; 260 unsigned int last_index = 0; 261 for (unsigned int i = 0; i < feature_events.length; i++) 262 { 263 feature_event_t *event = &feature_events[i]; 264 265 if (event->index != last_index) 266 { 267 /* Save a snapshot of active features and the range. */ 268 range_record_t *range = range_records.push (); 269 270 if (active_features.length) 271 { 272 CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 273 274 /* TODO sort and resolve conflicting features? */ 275 /* active_features.qsort (); */ 276 for (unsigned int j = 0; j < active_features.length; j++) 277 { 278 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 279 CFStringRef keys[] = { 280 kCTFontFeatureTypeIdentifierKey, 281 kCTFontFeatureSelectorIdentifierKey 282 }; 283 CFNumberRef values[] = { 284 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), 285 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) 286 }; 287 #else 288 char tag[5] = {HB_UNTAG (active_features[j].rec.feature)}; 289 CFTypeRef keys[] = { 290 kCTFontOpenTypeFeatureTag, 291 kCTFontOpenTypeFeatureValue 292 }; 293 CFTypeRef values[] = { 294 CFStringCreateWithCString (kCFAllocatorDefault, tag, kCFStringEncodingASCII), 295 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) 296 }; 297 #endif 298 static_assert ((ARRAY_LENGTH_CONST (keys) == ARRAY_LENGTH_CONST (values)), ""); 299 CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, 300 (const void **) keys, 301 (const void **) values, 302 ARRAY_LENGTH (keys), 303 &kCFTypeDictionaryKeyCallBacks, 304 &kCFTypeDictionaryValueCallBacks); 305 for (unsigned int i = 0; i < ARRAY_LENGTH (values); i++) 306 CFRelease (values[i]); 307 308 CFArrayAppendValue (features_array, dict); 309 CFRelease (dict); 310 311 } 312 313 CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, 314 (const void **) &kCTFontFeatureSettingsAttribute, 315 (const void **) &features_array, 316 1, 317 &kCFTypeDictionaryKeyCallBacks, 318 &kCFTypeDictionaryValueCallBacks); 319 CFRelease (features_array); 320 321 CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); 322 CFRelease (attributes); 323 324 range->font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, font_desc); 325 CFRelease (font_desc); 326 } 327 else 328 { 329 range->font = nullptr; 330 } 331 332 range->index_first = last_index; 333 range->index_last = event->index - 1; 334 335 last_index = event->index; 336 } 337 338 if (event->start) 339 { 340 active_features.push (event->feature); 341 } else { 342 active_feature_t *feature = active_features.lsearch (event->feature); 343 if (feature) 344 active_features.remove_ordered (feature - active_features.arrayZ); 345 } 346 } 347 } 348 349 unsigned int scratch_size; 350 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); 351 352 #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \ 353 Type *name = (Type *) scratch; \ 354 do { \ 355 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ 356 if (unlikely (_consumed > scratch_size)) \ 357 { \ 358 on_no_room; \ 359 assert (0); \ 360 } \ 361 scratch += _consumed; \ 362 scratch_size -= _consumed; \ 363 } while (0) 364 365 ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, ((void)nullptr) /*nothing*/); 366 unsigned int chars_len = 0; 367 for (unsigned int i = 0; i < buffer->len; i++) { 368 hb_codepoint_t c = buffer->info[i].codepoint; 369 if (likely (c <= 0xFFFFu)) 370 pchars[chars_len++] = c; 371 else if (unlikely (c > 0x10FFFFu)) 372 pchars[chars_len++] = 0xFFFDu; 373 else { 374 pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); 375 pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); 376 } 377 } 378 379 ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, ((void)nullptr) /*nothing*/); 380 chars_len = 0; 381 for (unsigned int i = 0; i < buffer->len; i++) 382 { 383 hb_codepoint_t c = buffer->info[i].codepoint; 384 unsigned int cluster = buffer->info[i].cluster; 385 log_clusters[chars_len++] = cluster; 386 if (hb_in_range (c, 0x10000u, 0x10FFFFu)) 387 log_clusters[chars_len++] = cluster; /* Surrogates. */ 388 } 389 390 #define FAIL(...) \ 391 HB_STMT_START { \ 392 DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \ 393 ret = false; \ 394 goto fail; \ 395 } HB_STMT_END 396 397 bool ret = true; 398 CFStringRef string_ref = nullptr; 399 CTLineRef line = nullptr; 400 401 if (false) 402 { 403 resize_and_retry: 404 DEBUG_MSG (CORETEXT, buffer, "Buffer resize"); 405 /* string_ref uses the scratch-buffer for backing store, and line references 406 * string_ref (via attr_string). We must release those before resizing buffer. */ 407 assert (string_ref); 408 assert (line); 409 CFRelease (string_ref); 410 CFRelease (line); 411 string_ref = nullptr; 412 line = nullptr; 413 414 /* Get previous start-of-scratch-area, that we use later for readjusting 415 * our existing scratch arrays. */ 416 unsigned int old_scratch_used; 417 hb_buffer_t::scratch_buffer_t *old_scratch; 418 old_scratch = buffer->get_scratch_buffer (&old_scratch_used); 419 old_scratch_used = scratch - old_scratch; 420 421 if (unlikely (!buffer->ensure (buffer->allocated * 2))) 422 FAIL ("Buffer resize failed"); 423 424 /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the 425 * cleanest way to do without completely restructuring the rest of this shaper. */ 426 scratch = buffer->get_scratch_buffer (&scratch_size); 427 pchars = reinterpret_cast<UniChar *> (((char *) scratch + ((char *) pchars - (char *) old_scratch))); 428 log_clusters = reinterpret_cast<unsigned int *> (((char *) scratch + ((char *) log_clusters - (char *) old_scratch))); 429 scratch += old_scratch_used; 430 scratch_size -= old_scratch_used; 431 } 432 { 433 string_ref = CFStringCreateWithCharactersNoCopy (nullptr, 434 pchars, chars_len, 435 kCFAllocatorNull); 436 if (unlikely (!string_ref)) 437 FAIL ("CFStringCreateWithCharactersNoCopy failed"); 438 439 /* Create an attributed string, populate it, and create a line from it, then release attributed string. */ 440 { 441 CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (kCFAllocatorDefault, 442 chars_len); 443 if (unlikely (!attr_string)) 444 FAIL ("CFAttributedStringCreateMutable failed"); 445 CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref); 446 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) 447 { 448 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), 449 kCTVerticalFormsAttributeName, kCFBooleanTrue); 450 } 451 452 if (buffer->props.language) 453 { 454 /* What's the iOS equivalent of this check? 455 * The symbols was introduced in iOS 7.0. 456 * At any rate, our fallback is safe and works fine. */ 457 #if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1090 458 # define kCTLanguageAttributeName CFSTR ("NSLanguage") 459 #endif 460 CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, 461 hb_language_to_string (buffer->props.language), 462 kCFStringEncodingUTF8, 463 kCFAllocatorNull); 464 if (unlikely (!lang)) 465 { 466 CFRelease (attr_string); 467 FAIL ("CFStringCreateWithCStringNoCopy failed"); 468 } 469 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), 470 kCTLanguageAttributeName, lang); 471 CFRelease (lang); 472 } 473 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), 474 kCTFontAttributeName, ct_font); 475 476 if (num_features && range_records.length) 477 { 478 unsigned int start = 0; 479 range_record_t *last_range = &range_records[0]; 480 for (unsigned int k = 0; k < chars_len; k++) 481 { 482 range_record_t *range = last_range; 483 while (log_clusters[k] < range->index_first) 484 range--; 485 while (log_clusters[k] > range->index_last) 486 range++; 487 if (range != last_range) 488 { 489 if (last_range->font) 490 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start), 491 kCTFontAttributeName, last_range->font); 492 493 start = k; 494 } 495 496 last_range = range; 497 } 498 if (start != chars_len && last_range->font) 499 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start), 500 kCTFontAttributeName, last_range->font); 501 } 502 /* Enable/disable kern if requested. 503 * 504 * Note: once kern is disabled, reenabling it doesn't currently seem to work in CoreText. 505 */ 506 if (num_features) 507 { 508 unsigned int zeroint = 0; 509 CFNumberRef zero = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &zeroint); 510 for (unsigned int i = 0; i < num_features; i++) 511 { 512 const hb_feature_t &feature = features[i]; 513 if (feature.tag == HB_TAG('k','e','r','n') && 514 feature.start < chars_len && feature.start < feature.end) 515 { 516 CFRange feature_range = CFRangeMake (feature.start, 517 hb_min (feature.end, chars_len) - feature.start); 518 if (feature.value) 519 CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName); 520 else 521 CFAttributedStringSetAttribute (attr_string, feature_range, kCTKernAttributeName, zero); 522 } 523 } 524 CFRelease (zero); 525 } 526 527 int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; 528 CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level); 529 #if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 530 extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel; 531 #endif 532 CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, 533 (const void **) &kCTTypesetterOptionForcedEmbeddingLevel, 534 (const void **) &level_number, 535 1, 536 &kCFTypeDictionaryKeyCallBacks, 537 &kCFTypeDictionaryValueCallBacks); 538 CFRelease (level_number); 539 if (unlikely (!options)) 540 { 541 CFRelease (attr_string); 542 FAIL ("CFDictionaryCreate failed"); 543 } 544 545 CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options); 546 CFRelease (options); 547 CFRelease (attr_string); 548 if (unlikely (!typesetter)) 549 FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed"); 550 551 line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0)); 552 CFRelease (typesetter); 553 if (unlikely (!line)) 554 FAIL ("CTTypesetterCreateLine failed"); 555 } 556 557 CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); 558 unsigned int num_runs = CFArrayGetCount (glyph_runs); 559 DEBUG_MSG (CORETEXT, nullptr, "Num runs: %u", num_runs); 560 561 buffer->len = 0; 562 uint32_t status_or = 0; 563 CGFloat advances_so_far = 0; 564 /* For right-to-left runs, CoreText returns the glyphs positioned such that 565 * any trailing whitespace is to the left of (0,0). Adjust coordinate system 566 * to fix for that. Test with any RTL string with trailing spaces. 567 * https://crbug.com/469028 568 */ 569 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) 570 { 571 advances_so_far -= CTLineGetTrailingWhitespaceWidth (line); 572 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) 573 advances_so_far = -advances_so_far; 574 } 575 576 const CFRange range_all = CFRangeMake (0, 0); 577 578 for (unsigned int i = 0; i < num_runs; i++) 579 { 580 CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex (glyph_runs, i)); 581 CTRunStatus run_status = CTRunGetStatus (run); 582 status_or |= run_status; 583 DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); 584 CGFloat run_advance = CTRunGetTypographicBounds (run, range_all, nullptr, nullptr, nullptr); 585 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) 586 run_advance = -run_advance; 587 DEBUG_MSG (CORETEXT, run, "Run advance: %g", (double) run_advance); 588 589 /* CoreText does automatic font fallback (AKA "cascading") for characters 590 * not supported by the requested font, and provides no way to turn it off, 591 * so we must detect if the returned run uses a font other than the requested 592 * one and fill in the buffer with .notdef glyphs instead of random glyph 593 * indices from a different font. 594 */ 595 CFDictionaryRef attributes = CTRunGetAttributes (run); 596 CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName)); 597 if (!CFEqual (run_ct_font, ct_font)) 598 { 599 /* The run doesn't use our main font instance. We have to figure out 600 * whether font fallback happened, or this is just CoreText giving us 601 * another CTFont using the same underlying CGFont. CoreText seems 602 * to do that in a variety of situations, one of which being vertical 603 * text, but also perhaps for caching reasons. 604 * 605 * First, see if it uses any of our subfonts created to set font features... 606 * 607 * Next, compare the CGFont to the one we used to create our fonts. 608 * Even this doesn't work all the time. 609 * 610 * Finally, we compare PS names, which I don't think are unique... 611 * 612 * Looks like if we really want to be sure here we have to modify the 613 * font to change the name table, similar to what we do in the uniscribe 614 * backend. 615 * 616 * However, even that wouldn't work if we were passed in the CGFont to 617 * construct a hb_face to begin with. 618 * 619 * See: https://github.com/harfbuzz/harfbuzz/pull/36 620 * 621 * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098 622 */ 623 bool matched = false; 624 for (unsigned int i = 0; i < range_records.length; i++) 625 if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font)) 626 { 627 matched = true; 628 break; 629 } 630 if (!matched) 631 { 632 CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, nullptr); 633 if (run_cg_font) 634 { 635 matched = CFEqual (run_cg_font, cg_font); 636 CFRelease (run_cg_font); 637 } 638 } 639 if (!matched) 640 { 641 CFStringRef font_ps_name = CTFontCopyName (ct_font, kCTFontPostScriptNameKey); 642 CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey); 643 CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0); 644 CFRelease (run_ps_name); 645 CFRelease (font_ps_name); 646 if (result == kCFCompareEqualTo) 647 matched = true; 648 } 649 if (!matched) 650 { 651 CFRange range = CTRunGetStringRange (run); 652 DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld", 653 range.location, range.location + range.length); 654 if (!buffer->ensure_inplace (buffer->len + range.length)) 655 goto resize_and_retry; 656 hb_glyph_info_t *info = buffer->info + buffer->len; 657 658 hb_codepoint_t notdef = 0; 659 hb_direction_t dir = buffer->props.direction; 660 hb_position_t x_advance, y_advance, x_offset, y_offset; 661 hb_font_get_glyph_advance_for_direction (font, notdef, dir, &x_advance, &y_advance); 662 hb_font_get_glyph_origin_for_direction (font, notdef, dir, &x_offset, &y_offset); 663 hb_position_t advance = x_advance + y_advance; 664 x_offset = -x_offset; 665 y_offset = -y_offset; 666 667 unsigned int old_len = buffer->len; 668 for (CFIndex j = range.location; j < range.location + range.length; j++) 669 { 670 UniChar ch = CFStringGetCharacterAtIndex (string_ref, j); 671 if (hb_in_range<UniChar> (ch, 0xDC00u, 0xDFFFu) && range.location < j) 672 { 673 ch = CFStringGetCharacterAtIndex (string_ref, j - 1); 674 if (hb_in_range<UniChar> (ch, 0xD800u, 0xDBFFu)) 675 /* This is the second of a surrogate pair. Don't need .notdef 676 * for this one. */ 677 continue; 678 } 679 if (buffer->unicode->is_default_ignorable (ch)) 680 continue; 681 682 info->codepoint = notdef; 683 info->cluster = log_clusters[j]; 684 685 info->mask = advance; 686 info->var1.i32 = x_offset; 687 info->var2.i32 = y_offset; 688 689 info++; 690 buffer->len++; 691 } 692 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) 693 buffer->reverse_range (old_len, buffer->len); 694 advances_so_far += run_advance; 695 continue; 696 } 697 } 698 699 unsigned int num_glyphs = CTRunGetGlyphCount (run); 700 if (num_glyphs == 0) 701 continue; 702 703 if (!buffer->ensure_inplace (buffer->len + num_glyphs)) 704 goto resize_and_retry; 705 706 hb_glyph_info_t *run_info = buffer->info + buffer->len; 707 708 /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always 709 * succeed, and so copying data to our own buffer will be rare. Reports 710 * have it that this changed in OS X 10.10 Yosemite, and nullptr is returned 711 * frequently. At any rate, we can test that codepath by setting USE_PTR 712 * to false. */ 713 714 #define USE_PTR true 715 716 #define SCRATCH_SAVE() \ 717 unsigned int scratch_size_saved = scratch_size; \ 718 hb_buffer_t::scratch_buffer_t *scratch_saved = scratch 719 720 #define SCRATCH_RESTORE() \ 721 scratch_size = scratch_size_saved; \ 722 scratch = scratch_saved 723 724 { /* Setup glyphs */ 725 SCRATCH_SAVE(); 726 const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : nullptr; 727 if (!glyphs) { 728 ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry); 729 CTRunGetGlyphs (run, range_all, glyph_buf); 730 glyphs = glyph_buf; 731 } 732 const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : nullptr; 733 if (!string_indices) { 734 ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry); 735 CTRunGetStringIndices (run, range_all, index_buf); 736 string_indices = index_buf; 737 } 738 hb_glyph_info_t *info = run_info; 739 for (unsigned int j = 0; j < num_glyphs; j++) 740 { 741 info->codepoint = glyphs[j]; 742 info->cluster = log_clusters[string_indices[j]]; 743 info++; 744 } 745 SCRATCH_RESTORE(); 746 } 747 { 748 /* Setup positions. 749 * Note that CoreText does not return advances for glyphs. As such, 750 * for all but last glyph, we use the delta position to next glyph as 751 * advance (in the advance direction only), and for last glyph we set 752 * whatever is needed to make the whole run's advance add up. */ 753 SCRATCH_SAVE(); 754 const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : nullptr; 755 if (!positions) { 756 ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry); 757 CTRunGetPositions (run, range_all, position_buf); 758 positions = position_buf; 759 } 760 hb_glyph_info_t *info = run_info; 761 if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) 762 { 763 hb_position_t x_offset = round ((positions[0].x - advances_so_far) * x_mult); 764 for (unsigned int j = 0; j < num_glyphs; j++) 765 { 766 CGFloat advance; 767 if (likely (j + 1 < num_glyphs)) 768 advance = positions[j + 1].x - positions[j].x; 769 else /* last glyph */ 770 advance = run_advance - (positions[j].x - positions[0].x); 771 /* int cast necessary to pass through negative values. */ 772 info->mask = (int) round (advance * x_mult); 773 info->var1.i32 = x_offset; 774 info->var2.i32 = round (positions[j].y * y_mult); 775 info++; 776 } 777 } 778 else 779 { 780 hb_position_t y_offset = round ((positions[0].y - advances_so_far) * y_mult); 781 for (unsigned int j = 0; j < num_glyphs; j++) 782 { 783 CGFloat advance; 784 if (likely (j + 1 < num_glyphs)) 785 advance = positions[j + 1].y - positions[j].y; 786 else /* last glyph */ 787 advance = run_advance - (positions[j].y - positions[0].y); 788 /* int cast necessary to pass through negative values. */ 789 info->mask = (int) round (advance * y_mult); 790 info->var1.i32 = round (positions[j].x * x_mult); 791 info->var2.i32 = y_offset; 792 info++; 793 } 794 } 795 SCRATCH_RESTORE(); 796 advances_so_far += run_advance; 797 } 798 #undef SCRATCH_RESTORE 799 #undef SCRATCH_SAVE 800 #undef USE_PTR 801 #undef ALLOCATE_ARRAY 802 803 buffer->len += num_glyphs; 804 } 805 806 buffer->clear_positions (); 807 808 unsigned int count = buffer->len; 809 hb_glyph_info_t *info = buffer->info; 810 hb_glyph_position_t *pos = buffer->pos; 811 if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) 812 for (unsigned int i = 0; i < count; i++) 813 { 814 pos->x_advance = info->mask; 815 pos->x_offset = info->var1.i32; 816 pos->y_offset = info->var2.i32; 817 818 info++; pos++; 819 } 820 else 821 for (unsigned int i = 0; i < count; i++) 822 { 823 pos->y_advance = info->mask; 824 pos->x_offset = info->var1.i32; 825 pos->y_offset = info->var2.i32; 826 827 info++; pos++; 828 } 829 830 /* Fix up clusters so that we never return out-of-order indices; 831 * if core text has reordered glyphs, we'll merge them to the 832 * beginning of the reordered cluster. CoreText is nice enough 833 * to tell us whenever it has produced nonmonotonic results... 834 * Note that we assume the input clusters were nonmonotonic to 835 * begin with. 836 * 837 * This does *not* mean we'll form the same clusters as Uniscribe 838 * or the native OT backend, only that the cluster indices will be 839 * monotonic in the output buffer. */ 840 if (count > 1 && (status_or & kCTRunStatusNonMonotonic) && 841 HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (buffer->cluster_level)) 842 { 843 hb_glyph_info_t *info = buffer->info; 844 if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) 845 { 846 unsigned int cluster = info[count - 1].cluster; 847 for (unsigned int i = count - 1; i > 0; i--) 848 { 849 cluster = hb_min (cluster, info[i - 1].cluster); 850 info[i - 1].cluster = cluster; 851 } 852 } 853 else 854 { 855 unsigned int cluster = info[0].cluster; 856 for (unsigned int i = 1; i < count; i++) 857 { 858 cluster = hb_min (cluster, info[i].cluster); 859 info[i].cluster = cluster; 860 } 861 } 862 } 863 } 864 865 /* TODO: Sometimes the above positioning code generates negative 866 * advance values. Fix them up. Example, with NotoNastaliqUrdu 867 * font and sequence ابهد. */ 868 869 buffer->clear_glyph_flags (); 870 buffer->unsafe_to_break (); 871 872 #undef FAIL 873 874 fail: 875 if (string_ref) 876 CFRelease (string_ref); 877 if (line) 878 CFRelease (line); 879 880 for (unsigned int i = 0; i < range_records.length; i++) 881 if (range_records[i].font) 882 CFRelease (range_records[i].font); 883 884 return ret; 885 } 886 887 888 #endif