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