tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

hb-coretext-shape.cc (29851B)


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