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 #include "hb-shaper-impl.hh" 31 32 #include "hb-coretext.h" 33 #include "hb-aat-layout.hh" 34 #include <math.h> 35 36 37 /** 38 * SECTION:hb-coretext 39 * @title: hb-coretext 40 * @short_description: CoreText integration 41 * @include: hb-coretext.h 42 * 43 * Functions for using HarfBuzz with the CoreText fonts. 44 **/ 45 46 /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */ 47 #define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f 48 49 static CGFloat 50 coretext_font_size_from_ptem (float ptem) 51 { 52 /* CoreText points are CSS pixels (96 per inch), 53 * NOT typographic points (72 per inch). 54 * 55 * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html 56 */ 57 ptem *= 96.f / 72.f; 58 return ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : ptem; 59 } 60 static float 61 coretext_font_size_to_ptem (CGFloat size) 62 { 63 size *= 72.f / 96.f; 64 return size <= 0.f ? 0 : size; 65 } 66 67 static void 68 release_table_data (void *user_data) 69 { 70 CFDataRef cf_data = reinterpret_cast<CFDataRef> (user_data); 71 CFRelease(cf_data); 72 } 73 74 static hb_blob_t * 75 reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) 76 { 77 CGFontRef cg_font = reinterpret_cast<CGFontRef> (user_data); 78 CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag); 79 if (unlikely (!cf_data)) 80 return nullptr; 81 82 const char *data = reinterpret_cast<const char*> (CFDataGetBytePtr (cf_data)); 83 const size_t length = CFDataGetLength (cf_data); 84 if (!data || !length) 85 { 86 CFRelease (cf_data); 87 return nullptr; 88 } 89 90 return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY, 91 reinterpret_cast<void *> (const_cast<__CFData *> (cf_data)), 92 release_table_data); 93 } 94 95 static void 96 _hb_cg_font_release (void *data) 97 { 98 CGFontRelease ((CGFontRef) data); 99 } 100 101 102 static CTFontDescriptorRef 103 get_last_resort_font_desc () 104 { 105 // TODO Handle allocation failures? 106 CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0); 107 CFArrayRef cascade_list = CFArrayCreate (kCFAllocatorDefault, 108 (const void **) &last_resort, 109 1, 110 &kCFTypeArrayCallBacks); 111 CFRelease (last_resort); 112 CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, 113 (const void **) &kCTFontCascadeListAttribute, 114 (const void **) &cascade_list, 115 1, 116 &kCFTypeDictionaryKeyCallBacks, 117 &kCFTypeDictionaryValueCallBacks); 118 CFRelease (cascade_list); 119 120 CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); 121 CFRelease (attributes); 122 return font_desc; 123 } 124 125 static void 126 release_data (void *info, const void *data, size_t size) 127 { 128 assert (hb_blob_get_length ((hb_blob_t *) info) == size && 129 hb_blob_get_data ((hb_blob_t *) info, nullptr) == data); 130 131 hb_blob_destroy ((hb_blob_t *) info); 132 } 133 134 static CGFontRef 135 create_cg_font (hb_face_t *face) 136 { 137 CGFontRef cg_font = nullptr; 138 if (face->destroy == _hb_cg_font_release) 139 { 140 cg_font = CGFontRetain ((CGFontRef) face->user_data); 141 } 142 else 143 { 144 hb_blob_t *blob = hb_face_reference_blob (face); 145 unsigned int blob_length; 146 const char *blob_data = hb_blob_get_data (blob, &blob_length); 147 if (unlikely (!blob_length)) 148 DEBUG_MSG (CORETEXT, face, "Face has empty blob"); 149 150 CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data); 151 if (likely (provider)) 152 { 153 cg_font = CGFontCreateWithDataProvider (provider); 154 if (unlikely (!cg_font)) 155 DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); 156 CGDataProviderRelease (provider); 157 } 158 } 159 return cg_font; 160 } 161 162 static CTFontRef 163 create_ct_font (CGFontRef cg_font, CGFloat font_size) 164 { 165 CTFontRef ct_font = nullptr; 166 167 /* CoreText does not enable trak table usage / tracking when creating a CTFont 168 * using CTFontCreateWithGraphicsFont. The only way of enabling tracking seems 169 * to be through the CTFontCreateUIFontForLanguage call. */ 170 CFStringRef cg_postscript_name = CGFontCopyPostScriptName (cg_font); 171 if (CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSText")) || 172 CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSDisplay"))) 173 { 174 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 175 # define kCTFontUIFontSystem kCTFontSystemFontType 176 # define kCTFontUIFontEmphasizedSystem kCTFontEmphasizedSystemFontType 177 #endif 178 CTFontUIFontType font_type = kCTFontUIFontSystem; 179 if (CFStringHasSuffix (cg_postscript_name, CFSTR ("-Bold"))) 180 font_type = kCTFontUIFontEmphasizedSystem; 181 182 ct_font = CTFontCreateUIFontForLanguage (font_type, font_size, nullptr); 183 CFStringRef ct_result_name = CTFontCopyPostScriptName(ct_font); 184 if (CFStringCompare (ct_result_name, cg_postscript_name, 0) != kCFCompareEqualTo) 185 { 186 CFRelease(ct_font); 187 ct_font = nullptr; 188 } 189 CFRelease (ct_result_name); 190 } 191 CFRelease (cg_postscript_name); 192 193 if (!ct_font) 194 ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, nullptr, nullptr); 195 196 if (unlikely (!ct_font)) { 197 DEBUG_MSG (CORETEXT, cg_font, "Font CTFontCreateWithGraphicsFont() failed"); 198 return nullptr; 199 } 200 201 /* crbug.com/576941 and crbug.com/625902 and the investigation in the latter 202 * bug indicate that the cascade list reconfiguration occasionally causes 203 * crashes in CoreText on OS X 10.9, thus let's skip this step on older 204 * operating system versions. Except for the emoji font, where _not_ 205 * reconfiguring the cascade list causes CoreText crashes. For details, see 206 * crbug.com/549610 */ 207 // 0x00070000 stands for "kCTVersionNumber10_10", see CoreText.h 208 if (&CTGetCoreTextVersion != nullptr && CTGetCoreTextVersion() < 0x00070000) { 209 CFStringRef fontName = CTFontCopyPostScriptName (ct_font); 210 bool isEmojiFont = CFStringCompare (fontName, CFSTR("AppleColorEmoji"), 0) == kCFCompareEqualTo; 211 CFRelease (fontName); 212 if (!isEmojiFont) 213 return ct_font; 214 } 215 216 CFURLRef original_url = nullptr; 217 #if TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 218 ATSFontRef atsFont; 219 FSRef fsref; 220 OSStatus status; 221 atsFont = CTFontGetPlatformFont (ct_font, NULL); 222 status = ATSFontGetFileReference (atsFont, &fsref); 223 if (status == noErr) 224 original_url = CFURLCreateFromFSRef (NULL, &fsref); 225 #else 226 original_url = (CFURLRef) CTFontCopyAttribute (ct_font, kCTFontURLAttribute); 227 #endif 228 229 /* Create font copy with cascade list that has LastResort first; this speeds up CoreText 230 * font fallback which we don't need anyway. */ 231 { 232 CTFontDescriptorRef last_resort_font_desc = get_last_resort_font_desc (); 233 CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, last_resort_font_desc); 234 CFRelease (last_resort_font_desc); 235 if (new_ct_font) 236 { 237 /* The CTFontCreateCopyWithAttributes call fails to stay on the same font 238 * when reconfiguring the cascade list and may switch to a different font 239 * when there are fonts that go by the same name, since the descriptor is 240 * just name and size. 241 * 242 * Avoid reconfiguring the cascade lists if the new font is outside the 243 * system locations that we cannot access from the sandboxed renderer 244 * process in Blink. This can be detected by the new file URL location 245 * that the newly found font points to. */ 246 CFURLRef new_url = nullptr; 247 #if TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 248 atsFont = CTFontGetPlatformFont (new_ct_font, NULL); 249 status = ATSFontGetFileReference (atsFont, &fsref); 250 if (status == noErr) 251 new_url = CFURLCreateFromFSRef (NULL, &fsref); 252 #else 253 new_url = (CFURLRef) CTFontCopyAttribute (new_ct_font, kCTFontURLAttribute); 254 #endif 255 // Keep reconfigured font if URL cannot be retrieved (seems to be the case 256 // on Mac OS 10.12 Sierra), speculative fix for crbug.com/625606 257 if (!original_url || !new_url || CFEqual (original_url, new_url)) { 258 CFRelease (ct_font); 259 ct_font = new_ct_font; 260 } else { 261 CFRelease (new_ct_font); 262 DEBUG_MSG (CORETEXT, ct_font, "Discarding reconfigured CTFont, location changed."); 263 } 264 if (new_url) 265 CFRelease (new_url); 266 } 267 else 268 DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed"); 269 } 270 271 if (original_url) 272 CFRelease (original_url); 273 return ct_font; 274 } 275 276 hb_coretext_face_data_t * 277 _hb_coretext_shaper_face_data_create (hb_face_t *face) 278 { 279 CGFontRef cg_font = create_cg_font (face); 280 281 if (unlikely (!cg_font)) 282 { 283 DEBUG_MSG (CORETEXT, face, "CGFont creation failed.."); 284 return nullptr; 285 } 286 287 return (hb_coretext_face_data_t *) cg_font; 288 } 289 290 void 291 _hb_coretext_shaper_face_data_destroy (hb_coretext_face_data_t *data) 292 { 293 CFRelease ((CGFontRef) data); 294 } 295 296 hb_face_t * 297 hb_coretext_face_create (CGFontRef cg_font) 298 { 299 return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), _hb_cg_font_release); 300 } 301 302 /* 303 * Since: 0.9.10 304 */ 305 CGFontRef 306 hb_coretext_face_get_cg_font (hb_face_t *face) 307 { 308 return (CGFontRef) (const void *) face->data.coretext; 309 } 310 311 312 hb_coretext_font_data_t * 313 _hb_coretext_shaper_font_data_create (hb_font_t *font) 314 { 315 hb_face_t *face = font->face; 316 const hb_coretext_face_data_t *face_data = face->data.coretext; 317 if (unlikely (!face_data)) return nullptr; 318 CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; 319 320 CTFontRef ct_font = create_ct_font (cg_font, coretext_font_size_from_ptem (font->ptem)); 321 322 if (unlikely (!ct_font)) 323 { 324 DEBUG_MSG (CORETEXT, font, "CGFont creation failed.."); 325 return nullptr; 326 } 327 328 return (hb_coretext_font_data_t *) ct_font; 329 } 330 331 void 332 _hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data) 333 { 334 CFRelease ((CTFontRef) data); 335 } 336 337 static const hb_coretext_font_data_t * 338 hb_coretext_font_data_sync (hb_font_t *font) 339 { 340 retry: 341 const hb_coretext_font_data_t *data = font->data.coretext; 342 if (unlikely (!data)) return nullptr; 343 344 if (fabs (CTFontGetSize((CTFontRef) data) - coretext_font_size_from_ptem (font->ptem)) > .5) 345 { 346 /* XXX-MT-bug 347 * Note that evaluating condition above can be dangerous if another thread 348 * got here first and destructed data. That's, as always, bad use pattern. 349 * If you modify the font (change font size), other threads must not be 350 * using it at the same time. However, since this check is delayed to 351 * when one actually tries to shape something, this is a XXX race condition 352 * (and the only one we have that I know of) right now. Ie. you modify the 353 * font size in one thread, then (supposedly safely) try to use it from two 354 * or more threads and BOOM! I'm not sure how to fix this. We want RCU. 355 */ 356 357 /* Drop and recreate. */ 358 /* If someone dropped it in the mean time, throw it away and don't touch it. 359 * Otherwise, destruct it. */ 360 if (likely (font->data.coretext.cmpexch (const_cast<hb_coretext_font_data_t *> (data), nullptr))) 361 _hb_coretext_shaper_font_data_destroy (const_cast<hb_coretext_font_data_t *> (data)); 362 else 363 goto retry; 364 } 365 return font->data.coretext; 366 } 367 368 369 /* 370 * Since: 1.7.2 371 */ 372 hb_font_t * 373 hb_coretext_font_create (CTFontRef ct_font) 374 { 375 CGFontRef cg_font = CTFontCopyGraphicsFont (ct_font, nullptr); 376 hb_face_t *face = hb_coretext_face_create (cg_font); 377 CFRelease (cg_font); 378 hb_font_t *font = hb_font_create (face); 379 hb_face_destroy (face); 380 381 if (unlikely (hb_object_is_immutable (font))) 382 return font; 383 384 hb_font_set_ptem (font, coretext_font_size_to_ptem (CTFontGetSize(ct_font))); 385 386 /* Let there be dragons here... */ 387 font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font)); 388 389 return font; 390 } 391 392 CTFontRef 393 hb_coretext_font_get_ct_font (hb_font_t *font) 394 { 395 const hb_coretext_font_data_t *data = hb_coretext_font_data_sync (font); 396 return data ? (CTFontRef) data : nullptr; 397 } 398 399 400 /* 401 * shaper 402 */ 403 404 struct feature_record_t { 405 unsigned int feature; 406 unsigned int setting; 407 }; 408 409 struct active_feature_t { 410 feature_record_t rec; 411 unsigned int order; 412 413 static int cmp (const void *pa, const void *pb) { 414 const active_feature_t *a = (const active_feature_t *) pa; 415 const active_feature_t *b = (const active_feature_t *) pb; 416 return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : 417 a->order < b->order ? -1 : a->order > b->order ? 1 : 418 a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 : 419 0; 420 } 421 bool operator== (const active_feature_t *f) { 422 return cmp (this, f) == 0; 423 } 424 }; 425 426 struct feature_event_t { 427 unsigned int index; 428 bool start; 429 active_feature_t feature; 430 431 static int cmp (const void *pa, const void *pb) { 432 const feature_event_t *a = (const feature_event_t *) pa; 433 const feature_event_t *b = (const feature_event_t *) pb; 434 return a->index < b->index ? -1 : a->index > b->index ? 1 : 435 a->start < b->start ? -1 : a->start > b->start ? 1 : 436 active_feature_t::cmp (&a->feature, &b->feature); 437 } 438 }; 439 440 struct range_record_t { 441 CTFontRef font; 442 unsigned int index_first; /* == start */ 443 unsigned int index_last; /* == end - 1 */ 444 }; 445 446 447 hb_bool_t 448 _hb_coretext_shape (hb_shape_plan_t *shape_plan, 449 hb_font_t *font, 450 hb_buffer_t *buffer, 451 const hb_feature_t *features, 452 unsigned int num_features) 453 { 454 hb_face_t *face = font->face; 455 CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; 456 CTFontRef ct_font = (CTFontRef) hb_coretext_font_data_sync (font); 457 458 CGFloat ct_font_size = CTFontGetSize (ct_font); 459 CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size; 460 CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size; 461 462 /* Attach marks to their bases, to match the 'ot' shaper. 463 * Adapted from a very old version of hb-ot-shape:hb_form_clusters(). 464 * Note that this only makes us be closer to the 'ot' shaper, 465 * but by no means the same. For example, if there's 466 * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will 467 * continue pointing to B2 even though B2 was merged into B1's 468 * cluster... */ 469 if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) 470 { 471 hb_unicode_funcs_t *unicode = buffer->unicode; 472 unsigned int count = buffer->len; 473 hb_glyph_info_t *info = buffer->info; 474 for (unsigned int i = 1; i < count; i++) 475 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint))) 476 buffer->merge_clusters (i - 1, i + 1); 477 } 478 479 hb_vector_t<feature_record_t> feature_records; 480 hb_vector_t<range_record_t> range_records; 481 482 /* 483 * Set up features. 484 * (copied + modified from code from hb-uniscribe.cc) 485 */ 486 if (num_features) 487 { 488 /* Sort features by start/end events. */ 489 hb_vector_t<feature_event_t> feature_events; 490 for (unsigned int i = 0; i < num_features; i++) 491 { 492 const hb_aat_feature_mapping_t * mapping = hb_aat_layout_find_feature_mapping (features[i].tag); 493 if (!mapping) 494 continue; 495 496 active_feature_t feature; 497 feature.rec.feature = mapping->aatFeatureType; 498 feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable; 499 feature.order = i; 500 501 feature_event_t *event; 502 503 event = feature_events.push (); 504 event->index = features[i].start; 505 event->start = true; 506 event->feature = feature; 507 508 event = feature_events.push (); 509 event->index = features[i].end; 510 event->start = false; 511 event->feature = feature; 512 } 513 feature_events.qsort (); 514 /* Add a strategic final event. */ 515 { 516 active_feature_t feature; 517 feature.rec.feature = HB_TAG_NONE; 518 feature.rec.setting = 0; 519 feature.order = num_features + 1; 520 521 feature_event_t *event = feature_events.push (); 522 event->index = 0; /* This value does magic. */ 523 event->start = false; 524 event->feature = feature; 525 } 526 527 /* Scan events and save features for each range. */ 528 hb_vector_t<active_feature_t> active_features; 529 unsigned int last_index = 0; 530 for (unsigned int i = 0; i < feature_events.length; i++) 531 { 532 feature_event_t *event = &feature_events[i]; 533 534 if (event->index != last_index) 535 { 536 /* Save a snapshot of active features and the range. */ 537 range_record_t *range = range_records.push (); 538 539 if (active_features.length) 540 { 541 CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 542 543 /* TODO sort and resolve conflicting features? */ 544 /* active_features.qsort (); */ 545 for (unsigned int j = 0; j < active_features.length; j++) 546 { 547 CFStringRef keys[] = { 548 kCTFontFeatureTypeIdentifierKey, 549 kCTFontFeatureSelectorIdentifierKey 550 }; 551 CFNumberRef values[] = { 552 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), 553 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) 554 }; 555 static_assert ((ARRAY_LENGTH_CONST (keys) == ARRAY_LENGTH_CONST (values)), ""); 556 CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, 557 (const void **) keys, 558 (const void **) values, 559 ARRAY_LENGTH (keys), 560 &kCFTypeDictionaryKeyCallBacks, 561 &kCFTypeDictionaryValueCallBacks); 562 for (unsigned int i = 0; i < ARRAY_LENGTH (values); i++) 563 CFRelease (values[i]); 564 565 CFArrayAppendValue (features_array, dict); 566 CFRelease (dict); 567 568 } 569 570 CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, 571 (const void **) &kCTFontFeatureSettingsAttribute, 572 (const void **) &features_array, 573 1, 574 &kCFTypeDictionaryKeyCallBacks, 575 &kCFTypeDictionaryValueCallBacks); 576 CFRelease (features_array); 577 578 CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); 579 CFRelease (attributes); 580 581 range->font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, font_desc); 582 CFRelease (font_desc); 583 } 584 else 585 { 586 range->font = nullptr; 587 } 588 589 range->index_first = last_index; 590 range->index_last = event->index - 1; 591 592 last_index = event->index; 593 } 594 595 if (event->start) 596 { 597 active_features.push (event->feature); 598 } else { 599 active_feature_t *feature = active_features.find (&event->feature); 600 if (feature) 601 active_features.remove (feature - active_features.arrayZ ()); 602 } 603 } 604 } 605 606 unsigned int scratch_size; 607 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); 608 609 #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \ 610 Type *name = (Type *) scratch; \ 611 { \ 612 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ 613 if (unlikely (_consumed > scratch_size)) \ 614 { \ 615 on_no_room; \ 616 assert (0); \ 617 } \ 618 scratch += _consumed; \ 619 scratch_size -= _consumed; \ 620 } 621 622 ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/); 623 unsigned int chars_len = 0; 624 for (unsigned int i = 0; i < buffer->len; i++) { 625 hb_codepoint_t c = buffer->info[i].codepoint; 626 if (likely (c <= 0xFFFFu)) 627 pchars[chars_len++] = c; 628 else if (unlikely (c > 0x10FFFFu)) 629 pchars[chars_len++] = 0xFFFDu; 630 else { 631 pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); 632 pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); 633 } 634 } 635 636 ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/); 637 chars_len = 0; 638 for (unsigned int i = 0; i < buffer->len; i++) 639 { 640 hb_codepoint_t c = buffer->info[i].codepoint; 641 unsigned int cluster = buffer->info[i].cluster; 642 log_clusters[chars_len++] = cluster; 643 if (hb_in_range (c, 0x10000u, 0x10FFFFu)) 644 log_clusters[chars_len++] = cluster; /* Surrogates. */ 645 } 646 647 #define FAIL(...) \ 648 HB_STMT_START { \ 649 DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \ 650 ret = false; \ 651 goto fail; \ 652 } HB_STMT_END; 653 654 bool ret = true; 655 CFStringRef string_ref = nullptr; 656 CTLineRef line = nullptr; 657 658 if (false) 659 { 660 resize_and_retry: 661 DEBUG_MSG (CORETEXT, buffer, "Buffer resize"); 662 /* string_ref uses the scratch-buffer for backing store, and line references 663 * string_ref (via attr_string). We must release those before resizing buffer. */ 664 assert (string_ref); 665 assert (line); 666 CFRelease (string_ref); 667 CFRelease (line); 668 string_ref = nullptr; 669 line = nullptr; 670 671 /* Get previous start-of-scratch-area, that we use later for readjusting 672 * our existing scratch arrays. */ 673 unsigned int old_scratch_used; 674 hb_buffer_t::scratch_buffer_t *old_scratch; 675 old_scratch = buffer->get_scratch_buffer (&old_scratch_used); 676 old_scratch_used = scratch - old_scratch; 677 678 if (unlikely (!buffer->ensure (buffer->allocated * 2))) 679 FAIL ("Buffer resize failed"); 680 681 /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the 682 * cleanest way to do without completely restructuring the rest of this shaper. */ 683 scratch = buffer->get_scratch_buffer (&scratch_size); 684 pchars = reinterpret_cast<UniChar *> (((char *) scratch + ((char *) pchars - (char *) old_scratch))); 685 log_clusters = reinterpret_cast<unsigned int *> (((char *) scratch + ((char *) log_clusters - (char *) old_scratch))); 686 scratch += old_scratch_used; 687 scratch_size -= old_scratch_used; 688 } 689 { 690 string_ref = CFStringCreateWithCharactersNoCopy (nullptr, 691 pchars, chars_len, 692 kCFAllocatorNull); 693 if (unlikely (!string_ref)) 694 FAIL ("CFStringCreateWithCharactersNoCopy failed"); 695 696 /* Create an attributed string, populate it, and create a line from it, then release attributed string. */ 697 { 698 CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (kCFAllocatorDefault, 699 chars_len); 700 if (unlikely (!attr_string)) 701 FAIL ("CFAttributedStringCreateMutable failed"); 702 CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref); 703 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) 704 { 705 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), 706 kCTVerticalFormsAttributeName, kCFBooleanTrue); 707 } 708 709 if (buffer->props.language) 710 { 711 /* What's the iOS equivalent of this check? 712 * The symbols was introduced in iOS 7.0. 713 * At any rate, our fallback is safe and works fine. */ 714 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 715 # define kCTLanguageAttributeName CFSTR ("NSLanguage") 716 #endif 717 CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, 718 hb_language_to_string (buffer->props.language), 719 kCFStringEncodingUTF8, 720 kCFAllocatorNull); 721 if (unlikely (!lang)) 722 { 723 CFRelease (attr_string); 724 FAIL ("CFStringCreateWithCStringNoCopy failed"); 725 } 726 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), 727 kCTLanguageAttributeName, lang); 728 CFRelease (lang); 729 } 730 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), 731 kCTFontAttributeName, ct_font); 732 733 if (num_features && range_records.length) 734 { 735 unsigned int start = 0; 736 range_record_t *last_range = &range_records[0]; 737 for (unsigned int k = 0; k < chars_len; k++) 738 { 739 range_record_t *range = last_range; 740 while (log_clusters[k] < range->index_first) 741 range--; 742 while (log_clusters[k] > range->index_last) 743 range++; 744 if (range != last_range) 745 { 746 if (last_range->font) 747 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start), 748 kCTFontAttributeName, last_range->font); 749 750 start = k; 751 } 752 753 last_range = range; 754 } 755 if (start != chars_len && last_range->font) 756 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start), 757 kCTFontAttributeName, last_range->font); 758 } 759 /* Enable/disable kern if requested. 760 * 761 * Note: once kern is disabled, reenabling it doesn't currently seem to work in CoreText. 762 */ 763 if (num_features) 764 { 765 unsigned int zeroint = 0; 766 CFNumberRef zero = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &zeroint); 767 for (unsigned int i = 0; i < num_features; i++) 768 { 769 const hb_feature_t &feature = features[i]; 770 if (feature.tag == HB_TAG('k','e','r','n') && 771 feature.start < chars_len && feature.start < feature.end) 772 { 773 CFRange feature_range = CFRangeMake (feature.start, 774 MIN (feature.end, chars_len) - feature.start); 775 if (feature.value) 776 CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName); 777 else 778 CFAttributedStringSetAttribute (attr_string, feature_range, kCTKernAttributeName, zero); 779 } 780 } 781 CFRelease (zero); 782 } 783 784 int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; 785 CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level); 786 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 787 extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel; 788 #endif 789 CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, 790 (const void **) &kCTTypesetterOptionForcedEmbeddingLevel, 791 (const void **) &level_number, 792 1, 793 &kCFTypeDictionaryKeyCallBacks, 794 &kCFTypeDictionaryValueCallBacks); 795 CFRelease (level_number); 796 if (unlikely (!options)) 797 { 798 CFRelease (attr_string); 799 FAIL ("CFDictionaryCreate failed"); 800 } 801 802 CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options); 803 CFRelease (options); 804 CFRelease (attr_string); 805 if (unlikely (!typesetter)) 806 FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed"); 807 808 line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0)); 809 CFRelease (typesetter); 810 if (unlikely (!line)) 811 FAIL ("CTTypesetterCreateLine failed"); 812 } 813 814 CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); 815 unsigned int num_runs = CFArrayGetCount (glyph_runs); 816 DEBUG_MSG (CORETEXT, nullptr, "Num runs: %d", num_runs); 817 818 buffer->len = 0; 819 uint32_t status_and = ~0, status_or = 0; 820 double advances_so_far = 0; 821 /* For right-to-left runs, CoreText returns the glyphs positioned such that 822 * any trailing whitespace is to the left of (0,0). Adjust coordinate system 823 * to fix for that. Test with any RTL string with trailing spaces. 824 * https://crbug.com/469028 825 */ 826 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) 827 { 828 advances_so_far -= CTLineGetTrailingWhitespaceWidth (line); 829 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) 830 advances_so_far = -advances_so_far; 831 } 832 833 const CFRange range_all = CFRangeMake (0, 0); 834 835 for (unsigned int i = 0; i < num_runs; i++) 836 { 837 CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex (glyph_runs, i)); 838 CTRunStatus run_status = CTRunGetStatus (run); 839 status_or |= run_status; 840 status_and &= run_status; 841 DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); 842 double run_advance = CTRunGetTypographicBounds (run, range_all, nullptr, nullptr, nullptr); 843 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) 844 run_advance = -run_advance; 845 DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance); 846 847 /* CoreText does automatic font fallback (AKA "cascading") for characters 848 * not supported by the requested font, and provides no way to turn it off, 849 * so we must detect if the returned run uses a font other than the requested 850 * one and fill in the buffer with .notdef glyphs instead of random glyph 851 * indices from a different font. 852 */ 853 CFDictionaryRef attributes = CTRunGetAttributes (run); 854 CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName)); 855 if (!CFEqual (run_ct_font, ct_font)) 856 { 857 /* The run doesn't use our main font instance. We have to figure out 858 * whether font fallback happened, or this is just CoreText giving us 859 * another CTFont using the same underlying CGFont. CoreText seems 860 * to do that in a variety of situations, one of which being vertical 861 * text, but also perhaps for caching reasons. 862 * 863 * First, see if it uses any of our subfonts created to set font features... 864 * 865 * Next, compare the CGFont to the one we used to create our fonts. 866 * Even this doesn't work all the time. 867 * 868 * Finally, we compare PS names, which I don't think are unique... 869 * 870 * Looks like if we really want to be sure here we have to modify the 871 * font to change the name table, similar to what we do in the uniscribe 872 * backend. 873 * 874 * However, even that wouldn't work if we were passed in the CGFont to 875 * construct a hb_face to begin with. 876 * 877 * See: https://github.com/harfbuzz/harfbuzz/pull/36 878 * 879 * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098 880 */ 881 bool matched = false; 882 for (unsigned int i = 0; i < range_records.length; i++) 883 if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font)) 884 { 885 matched = true; 886 break; 887 } 888 if (!matched) 889 { 890 CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, nullptr); 891 if (run_cg_font) 892 { 893 matched = CFEqual (run_cg_font, cg_font); 894 CFRelease (run_cg_font); 895 } 896 } 897 if (!matched) 898 { 899 CFStringRef font_ps_name = CTFontCopyName (ct_font, kCTFontPostScriptNameKey); 900 CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey); 901 CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0); 902 CFRelease (run_ps_name); 903 CFRelease (font_ps_name); 904 if (result == kCFCompareEqualTo) 905 matched = true; 906 } 907 if (!matched) 908 { 909 CFRange range = CTRunGetStringRange (run); 910 DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld", 911 range.location, range.location + range.length); 912 if (!buffer->ensure_inplace (buffer->len + range.length)) 913 goto resize_and_retry; 914 hb_glyph_info_t *info = buffer->info + buffer->len; 915 916 hb_codepoint_t notdef = 0; 917 hb_direction_t dir = buffer->props.direction; 918 hb_position_t x_advance, y_advance, x_offset, y_offset; 919 hb_font_get_glyph_advance_for_direction (font, notdef, dir, &x_advance, &y_advance); 920 hb_font_get_glyph_origin_for_direction (font, notdef, dir, &x_offset, &y_offset); 921 hb_position_t advance = x_advance + y_advance; 922 x_offset = -x_offset; 923 y_offset = -y_offset; 924 925 unsigned int old_len = buffer->len; 926 for (CFIndex j = range.location; j < range.location + range.length; j++) 927 { 928 UniChar ch = CFStringGetCharacterAtIndex (string_ref, j); 929 if (hb_in_range<UniChar> (ch, 0xDC00u, 0xDFFFu) && range.location < j) 930 { 931 ch = CFStringGetCharacterAtIndex (string_ref, j - 1); 932 if (hb_in_range<UniChar> (ch, 0xD800u, 0xDBFFu)) 933 /* This is the second of a surrogate pair. Don't need .notdef 934 * for this one. */ 935 continue; 936 } 937 if (buffer->unicode->is_default_ignorable (ch)) 938 continue; 939 940 info->codepoint = notdef; 941 info->cluster = log_clusters[j]; 942 943 info->mask = advance; 944 info->var1.i32 = x_offset; 945 info->var2.i32 = y_offset; 946 947 info++; 948 buffer->len++; 949 } 950 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) 951 buffer->reverse_range (old_len, buffer->len); 952 advances_so_far += run_advance; 953 continue; 954 } 955 } 956 957 unsigned int num_glyphs = CTRunGetGlyphCount (run); 958 if (num_glyphs == 0) 959 continue; 960 961 if (!buffer->ensure_inplace (buffer->len + num_glyphs)) 962 goto resize_and_retry; 963 964 hb_glyph_info_t *run_info = buffer->info + buffer->len; 965 966 /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always 967 * succeed, and so copying data to our own buffer will be rare. Reports 968 * have it that this changed in OS X 10.10 Yosemite, and nullptr is returned 969 * frequently. At any rate, we can test that codepath by setting USE_PTR 970 * to false. */ 971 972 #define USE_PTR true 973 974 #define SCRATCH_SAVE() \ 975 unsigned int scratch_size_saved = scratch_size; \ 976 hb_buffer_t::scratch_buffer_t *scratch_saved = scratch 977 978 #define SCRATCH_RESTORE() \ 979 scratch_size = scratch_size_saved; \ 980 scratch = scratch_saved; 981 982 { /* Setup glyphs */ 983 SCRATCH_SAVE(); 984 const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : nullptr; 985 if (!glyphs) { 986 ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry); 987 CTRunGetGlyphs (run, range_all, glyph_buf); 988 glyphs = glyph_buf; 989 } 990 const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : nullptr; 991 if (!string_indices) { 992 ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry); 993 CTRunGetStringIndices (run, range_all, index_buf); 994 string_indices = index_buf; 995 } 996 hb_glyph_info_t *info = run_info; 997 for (unsigned int j = 0; j < num_glyphs; j++) 998 { 999 info->codepoint = glyphs[j]; 1000 info->cluster = log_clusters[string_indices[j]]; 1001 info++; 1002 } 1003 SCRATCH_RESTORE(); 1004 } 1005 { 1006 /* Setup positions. 1007 * Note that CoreText does not return advances for glyphs. As such, 1008 * for all but last glyph, we use the delta position to next glyph as 1009 * advance (in the advance direction only), and for last glyph we set 1010 * whatever is needed to make the whole run's advance add up. */ 1011 SCRATCH_SAVE(); 1012 const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : nullptr; 1013 if (!positions) { 1014 ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry); 1015 CTRunGetPositions (run, range_all, position_buf); 1016 positions = position_buf; 1017 } 1018 hb_glyph_info_t *info = run_info; 1019 if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) 1020 { 1021 hb_position_t x_offset = (positions[0].x - advances_so_far) * x_mult; 1022 for (unsigned int j = 0; j < num_glyphs; j++) 1023 { 1024 double advance; 1025 if (likely (j + 1 < num_glyphs)) 1026 advance = positions[j + 1].x - positions[j].x; 1027 else /* last glyph */ 1028 advance = run_advance - (positions[j].x - positions[0].x); 1029 info->mask = advance * x_mult; 1030 info->var1.i32 = x_offset; 1031 info->var2.i32 = positions[j].y * y_mult; 1032 info++; 1033 } 1034 } 1035 else 1036 { 1037 hb_position_t y_offset = (positions[0].y - advances_so_far) * y_mult; 1038 for (unsigned int j = 0; j < num_glyphs; j++) 1039 { 1040 double advance; 1041 if (likely (j + 1 < num_glyphs)) 1042 advance = positions[j + 1].y - positions[j].y; 1043 else /* last glyph */ 1044 advance = run_advance - (positions[j].y - positions[0].y); 1045 info->mask = advance * y_mult; 1046 info->var1.i32 = positions[j].x * x_mult; 1047 info->var2.i32 = y_offset; 1048 info++; 1049 } 1050 } 1051 SCRATCH_RESTORE(); 1052 advances_so_far += run_advance; 1053 } 1054 #undef SCRATCH_RESTORE 1055 #undef SCRATCH_SAVE 1056 #undef USE_PTR 1057 #undef ALLOCATE_ARRAY 1058 1059 buffer->len += num_glyphs; 1060 } 1061 1062 /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel, 1063 * or if it does, it doesn't respect it. So we get runs with wrong 1064 * directions. As such, disable the assert... It wouldn't crash, but 1065 * cursoring will be off... 1066 * 1067 * https://crbug.com/419769 1068 */ 1069 if (false) 1070 { 1071 /* Make sure all runs had the expected direction. */ 1072 bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); 1073 assert (bool (status_and & kCTRunStatusRightToLeft) == backward); 1074 assert (bool (status_or & kCTRunStatusRightToLeft) == backward); 1075 } 1076 1077 buffer->clear_positions (); 1078 1079 unsigned int count = buffer->len; 1080 hb_glyph_info_t *info = buffer->info; 1081 hb_glyph_position_t *pos = buffer->pos; 1082 if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) 1083 for (unsigned int i = 0; i < count; i++) 1084 { 1085 pos->x_advance = info->mask; 1086 pos->x_offset = info->var1.i32; 1087 pos->y_offset = info->var2.i32; 1088 1089 info++, pos++; 1090 } 1091 else 1092 for (unsigned int i = 0; i < count; i++) 1093 { 1094 pos->y_advance = info->mask; 1095 pos->x_offset = info->var1.i32; 1096 pos->y_offset = info->var2.i32; 1097 1098 info++, pos++; 1099 } 1100 1101 /* Fix up clusters so that we never return out-of-order indices; 1102 * if core text has reordered glyphs, we'll merge them to the 1103 * beginning of the reordered cluster. CoreText is nice enough 1104 * to tell us whenever it has produced nonmonotonic results... 1105 * Note that we assume the input clusters were nonmonotonic to 1106 * begin with. 1107 * 1108 * This does *not* mean we'll form the same clusters as Uniscribe 1109 * or the native OT backend, only that the cluster indices will be 1110 * monotonic in the output buffer. */ 1111 if (count > 1 && (status_or & kCTRunStatusNonMonotonic)) 1112 { 1113 hb_glyph_info_t *info = buffer->info; 1114 if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) 1115 { 1116 unsigned int cluster = info[count - 1].cluster; 1117 for (unsigned int i = count - 1; i > 0; i--) 1118 { 1119 cluster = MIN (cluster, info[i - 1].cluster); 1120 info[i - 1].cluster = cluster; 1121 } 1122 } 1123 else 1124 { 1125 unsigned int cluster = info[0].cluster; 1126 for (unsigned int i = 1; i < count; i++) 1127 { 1128 cluster = MIN (cluster, info[i].cluster); 1129 info[i].cluster = cluster; 1130 } 1131 } 1132 } 1133 } 1134 1135 buffer->unsafe_to_break_all (); 1136 1137 #undef FAIL 1138 1139 fail: 1140 if (string_ref) 1141 CFRelease (string_ref); 1142 if (line) 1143 CFRelease (line); 1144 1145 for (unsigned int i = 0; i < range_records.length; i++) 1146 if (range_records[i].font) 1147 CFRelease (range_records[i].font); 1148 1149 return ret; 1150 } 1151 1152 1153 /* 1154 * AAT shaper 1155 */ 1156 1157 /* 1158 * shaper face data 1159 */ 1160 1161 struct hb_coretext_aat_face_data_t {}; 1162 1163 hb_coretext_aat_face_data_t * 1164 _hb_coretext_aat_shaper_face_data_create (hb_face_t *face) 1165 { 1166 return hb_aat_layout_has_substitution (face) || hb_aat_layout_has_positioning (face) ? 1167 (hb_coretext_aat_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; 1168 } 1169 1170 void 1171 _hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_face_data_t *data HB_UNUSED) 1172 { 1173 } 1174 1175 1176 /* 1177 * shaper font data 1178 */ 1179 1180 struct hb_coretext_aat_font_data_t {}; 1181 1182 hb_coretext_aat_font_data_t * 1183 _hb_coretext_aat_shaper_font_data_create (hb_font_t *font) 1184 { 1185 return font->data.coretext ? (hb_coretext_aat_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; 1186 } 1187 1188 void 1189 _hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_font_data_t *data HB_UNUSED) 1190 { 1191 } 1192 1193 1194 /* 1195 * shaper 1196 */ 1197 1198 hb_bool_t 1199 _hb_coretext_aat_shape (hb_shape_plan_t *shape_plan, 1200 hb_font_t *font, 1201 hb_buffer_t *buffer, 1202 const hb_feature_t *features, 1203 unsigned int num_features) 1204 { 1205 return _hb_coretext_shape (shape_plan, font, buffer, features, num_features); 1206 }