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 }