2 * Copyright © 2012,2013 Mozilla Foundation.
3 * Copyright © 2012,2013 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
25 * Mozilla Author(s): Jonathan Kew
26 * Google Author(s): Behdad Esfahbod
33 #include "hb-shaper-impl.hh"
35 #include "hb-coretext.h"
36 #include "hb-aat-layout.hh"
42 * @short_description: CoreText integration
43 * @include: hb-coretext.h
45 * Functions for using HarfBuzz with the CoreText fonts.
48 /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
49 #define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
52 release_table_data (void *user_data
)
54 CFDataRef cf_data
= reinterpret_cast<CFDataRef
> (user_data
);
59 _hb_cg_reference_table (hb_face_t
*face HB_UNUSED
, hb_tag_t tag
, void *user_data
)
61 CGFontRef cg_font
= reinterpret_cast<CGFontRef
> (user_data
);
62 CFDataRef cf_data
= CGFontCopyTableForTag (cg_font
, tag
);
63 if (unlikely (!cf_data
))
66 const char *data
= reinterpret_cast<const char*> (CFDataGetBytePtr (cf_data
));
67 const size_t length
= CFDataGetLength (cf_data
);
74 return hb_blob_create (data
, length
, HB_MEMORY_MODE_READONLY
,
75 reinterpret_cast<void *> (const_cast<__CFData
*> (cf_data
)),
80 _hb_cg_font_release (void *data
)
82 CGFontRelease ((CGFontRef
) data
);
86 static CTFontDescriptorRef
87 get_last_resort_font_desc ()
89 // TODO Handle allocation failures?
90 CTFontDescriptorRef last_resort
= CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0);
91 CFArrayRef cascade_list
= CFArrayCreate (kCFAllocatorDefault
,
92 (const void **) &last_resort
,
94 &kCFTypeArrayCallBacks
);
95 CFRelease (last_resort
);
96 CFDictionaryRef attributes
= CFDictionaryCreate (kCFAllocatorDefault
,
97 (const void **) &kCTFontCascadeListAttribute
,
98 (const void **) &cascade_list
,
100 &kCFTypeDictionaryKeyCallBacks
,
101 &kCFTypeDictionaryValueCallBacks
);
102 CFRelease (cascade_list
);
104 CTFontDescriptorRef font_desc
= CTFontDescriptorCreateWithAttributes (attributes
);
105 CFRelease (attributes
);
110 release_data (void *info
, const void *data
, size_t size
)
112 assert (hb_blob_get_length ((hb_blob_t
*) info
) == size
&&
113 hb_blob_get_data ((hb_blob_t
*) info
, nullptr) == data
);
115 hb_blob_destroy ((hb_blob_t
*) info
);
119 create_cg_font (hb_face_t
*face
)
121 CGFontRef cg_font
= nullptr;
122 if (face
->destroy
== _hb_cg_font_release
)
124 cg_font
= CGFontRetain ((CGFontRef
) face
->user_data
);
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");
134 CGDataProviderRef provider
= CGDataProviderCreateWithData (blob
, blob_data
, blob_length
, &release_data
);
135 if (likely (provider
))
137 cg_font
= CGFontCreateWithDataProvider (provider
);
138 if (unlikely (!cg_font
))
139 DEBUG_MSG (CORETEXT
, face
, "Face CGFontCreateWithDataProvider() failed");
140 CGDataProviderRelease (provider
);
147 create_ct_font (CGFontRef cg_font
, CGFloat font_size
)
149 CTFontRef ct_font
= nullptr;
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")))
158 #if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1080
159 # define kCTFontUIFontSystem kCTFontSystemFontType
160 # define kCTFontUIFontEmphasizedSystem kCTFontEmphasizedSystemFontType
162 CTFontUIFontType font_type
= kCTFontUIFontSystem
;
163 if (CFStringHasSuffix (cg_postscript_name
, CFSTR ("-Bold")))
164 font_type
= kCTFontUIFontEmphasizedSystem
;
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
)
173 CFRelease (ct_result_name
);
175 CFRelease (cg_postscript_name
);
178 ct_font
= CTFontCreateWithGraphicsFont (cg_font
, font_size
, nullptr, nullptr);
180 if (unlikely (!ct_font
)) {
181 DEBUG_MSG (CORETEXT
, cg_font
, "Font CTFontCreateWithGraphicsFont() failed");
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
);
203 CFURLRef original_url
= nullptr;
204 #if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
208 atsFont
= CTFontGetPlatformFont (ct_font
, NULL
);
209 status
= ATSFontGetFileReference (atsFont
, &fsref
);
211 original_url
= CFURLCreateFromFSRef (NULL
, &fsref
);
213 original_url
= (CFURLRef
) CTFontCopyAttribute (ct_font
, kCTFontURLAttribute
);
216 /* Create font copy with cascade list that has LastResort first; this speeds up CoreText
217 * font fallback which we don't need anyway. */
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
);
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.
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
);
238 new_url
= CFURLCreateFromFSRef (NULL
, &fsref
);
240 new_url
= (CFURLRef
) CTFontCopyAttribute (new_ct_font
, kCTFontURLAttribute
);
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
)) {
246 ct_font
= new_ct_font
;
248 CFRelease (new_ct_font
);
249 DEBUG_MSG (CORETEXT
, ct_font
, "Discarding reconfigured CTFont, location changed.");
255 DEBUG_MSG (CORETEXT
, ct_font
, "Font copy with empty cascade list failed");
259 CFRelease (original_url
);
263 hb_coretext_face_data_t
*
264 _hb_coretext_shaper_face_data_create (hb_face_t
*face
)
266 CGFontRef cg_font
= create_cg_font (face
);
268 if (unlikely (!cg_font
))
270 DEBUG_MSG (CORETEXT
, face
, "CGFont creation failed..");
274 return (hb_coretext_face_data_t
*) cg_font
;
278 _hb_coretext_shaper_face_data_destroy (hb_coretext_face_data_t
*data
)
280 CFRelease ((CGFontRef
) data
);
284 * hb_coretext_face_create:
285 * @cg_font: The CGFontRef to work upon
287 * Creates an #hb_face_t face object from the specified
290 * Return value: the new #hb_face_t face object
295 hb_coretext_face_create (CGFontRef cg_font
)
297 return hb_face_create_for_tables (_hb_cg_reference_table
, CGFontRetain (cg_font
), _hb_cg_font_release
);
301 * hb_coretext_face_get_cg_font:
302 * @face: The #hb_face_t to work upon
304 * Fetches the CGFontRef associated with an #hb_face_t
307 * Return value: the CGFontRef found
312 hb_coretext_face_get_cg_font (hb_face_t
*face
)
314 return (CGFontRef
) (const void *) face
->data
.coretext
;
318 hb_coretext_font_data_t
*
319 _hb_coretext_shaper_font_data_create (hb_font_t
*font
)
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
;
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
);
329 if (unlikely (!ct_font
))
331 DEBUG_MSG (CORETEXT
, font
, "CGFont creation failed..");
335 if (font
->num_coords
)
337 CFMutableDictionaryRef variations
=
338 CFDictionaryCreateMutable (kCFAllocatorDefault
,
340 &kCFTypeDictionaryKeyCallBacks
,
341 &kCFTypeDictionaryValueCallBacks
);
343 for (unsigned i
= 0; i
< font
->num_coords
; i
++)
345 if (font
->coords
[i
] == 0.) continue;
347 hb_ot_var_axis_info_t info
;
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
);
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
);
359 CFDictionaryRef attributes
=
360 CFDictionaryCreate (kCFAllocatorDefault
,
361 (const void **) &kCTFontVariationAttribute
,
362 (const void **) &variations
,
364 &kCFTypeDictionaryKeyCallBacks
,
365 &kCFTypeDictionaryValueCallBacks
);
367 CTFontDescriptorRef varDesc
= CTFontDescriptorCreateWithAttributes (attributes
);
368 CTFontRef new_ct_font
= CTFontCreateCopyWithAttributes (ct_font
, 0, nullptr, varDesc
);
371 CFRelease (attributes
);
372 CFRelease (variations
);
373 ct_font
= new_ct_font
;
376 return (hb_coretext_font_data_t
*) ct_font
;
380 _hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t
*data
)
382 CFRelease ((CTFontRef
) data
);
386 * hb_coretext_font_create:
387 * @ct_font: The CTFontRef to work upon
389 * Creates an #hb_font_t font object from the specified
392 * Return value: the new #hb_font_t font object
397 hb_coretext_font_create (CTFontRef ct_font
)
399 CGFontRef cg_font
= CTFontCopyGraphicsFont (ct_font
, nullptr);
400 hb_face_t
*face
= hb_coretext_face_create (cg_font
);
402 hb_font_t
*font
= hb_font_create (face
);
403 hb_face_destroy (face
);
405 if (unlikely (hb_object_is_immutable (font
)))
408 hb_font_set_ptem (font
, CTFontGetSize (ct_font
));
410 /* Let there be dragons here... */
411 font
->data
.coretext
.cmpexch (nullptr, (hb_coretext_font_data_t
*) CFRetain (ct_font
));
417 * hb_coretext_font_get_ct_font:
418 * @font: #hb_font_t to work upon
420 * Fetches the CTFontRef associated with the specified
421 * #hb_font_t font object.
423 * Return value: the CTFontRef found
428 hb_coretext_font_get_ct_font (hb_font_t
*font
)
430 CTFontRef ct_font
= (CTFontRef
) (const void *) font
->data
.coretext
;
431 return ct_font
? (CTFontRef
) ct_font
: nullptr;
439 struct feature_record_t
{
440 unsigned int feature
;
441 unsigned int setting
;
444 struct active_feature_t
{
445 feature_record_t rec
;
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 :
456 bool operator== (const active_feature_t
& f
) const {
457 return cmp (this, &f
) == 0;
461 struct feature_event_t
{
464 active_feature_t feature
;
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
);
475 struct range_record_t
{
477 unsigned int index_first
; /* == start */
478 unsigned int index_last
; /* == end - 1 */
483 _hb_coretext_shape (hb_shape_plan_t
*shape_plan
,
486 const hb_feature_t
*features
,
487 unsigned int num_features
)
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
;
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
;
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
504 if (buffer
->cluster_level
== HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES
)
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);
514 hb_vector_t
<range_record_t
> range_records
;
518 * (copied + modified from code from hb-uniscribe.cc)
522 /* Sort features by start/end events. */
523 hb_vector_t
<feature_event_t
> feature_events
;
524 for (unsigned int i
= 0; i
< num_features
; i
++)
526 active_feature_t feature
;
528 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
529 const hb_aat_feature_mapping_t
* mapping
= hb_aat_layout_find_feature_mapping (features
[i
].tag
);
533 feature
.rec
.feature
= mapping
->aatFeatureType
;
534 feature
.rec
.setting
= features
[i
].value
? mapping
->selectorToEnable
: mapping
->selectorToDisable
;
536 feature
.rec
.feature
= features
[i
].tag
;
537 feature
.rec
.setting
= features
[i
].value
;
541 feature_event_t
*event
;
543 event
= feature_events
.push ();
544 event
->index
= features
[i
].start
;
546 event
->feature
= feature
;
548 event
= feature_events
.push ();
549 event
->index
= features
[i
].end
;
550 event
->start
= false;
551 event
->feature
= feature
;
553 feature_events
.qsort ();
554 /* Add a strategic final event. */
556 active_feature_t feature
;
557 feature
.rec
.feature
= HB_TAG_NONE
;
558 feature
.rec
.setting
= 0;
559 feature
.order
= num_features
+ 1;
561 feature_event_t
*event
= feature_events
.push ();
562 event
->index
= 0; /* This value does magic. */
563 event
->start
= false;
564 event
->feature
= feature
;
567 /* Scan events and save features for each range. */
568 hb_vector_t
<active_feature_t
> active_features
;
569 unsigned int last_index
= 0;
570 for (unsigned int i
= 0; i
< feature_events
.length
; i
++)
572 feature_event_t
*event
= &feature_events
[i
];
574 if (event
->index
!= last_index
)
576 /* Save a snapshot of active features and the range. */
577 range_record_t
*range
= range_records
.push ();
579 if (active_features
.length
)
581 CFMutableArrayRef features_array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
583 /* TODO sort and resolve conflicting features? */
584 /* active_features.qsort (); */
585 for (unsigned int j
= 0; j
< active_features
.length
; j
++)
587 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
588 CFStringRef keys
[] = {
589 kCTFontFeatureTypeIdentifierKey
,
590 kCTFontFeatureSelectorIdentifierKey
592 CFNumberRef values
[] = {
593 CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &active_features
[j
].rec
.feature
),
594 CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &active_features
[j
].rec
.setting
)
597 char tag
[5] = {HB_UNTAG (active_features
[j
].rec
.feature
)};
599 kCTFontOpenTypeFeatureTag
,
600 kCTFontOpenTypeFeatureValue
602 CFTypeRef values
[] = {
603 CFStringCreateWithCString (kCFAllocatorDefault
, tag
, kCFStringEncodingASCII
),
604 CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &active_features
[j
].rec
.setting
)
607 static_assert ((ARRAY_LENGTH_CONST (keys
) == ARRAY_LENGTH_CONST (values
)), "");
608 CFDictionaryRef dict
= CFDictionaryCreate (kCFAllocatorDefault
,
609 (const void **) keys
,
610 (const void **) values
,
612 &kCFTypeDictionaryKeyCallBacks
,
613 &kCFTypeDictionaryValueCallBacks
);
614 for (unsigned int i
= 0; i
< ARRAY_LENGTH (values
); i
++)
615 CFRelease (values
[i
]);
617 CFArrayAppendValue (features_array
, dict
);
622 CFDictionaryRef attributes
= CFDictionaryCreate (kCFAllocatorDefault
,
623 (const void **) &kCTFontFeatureSettingsAttribute
,
624 (const void **) &features_array
,
626 &kCFTypeDictionaryKeyCallBacks
,
627 &kCFTypeDictionaryValueCallBacks
);
628 CFRelease (features_array
);
630 CTFontDescriptorRef font_desc
= CTFontDescriptorCreateWithAttributes (attributes
);
631 CFRelease (attributes
);
633 range
->font
= CTFontCreateCopyWithAttributes (ct_font
, 0.0, nullptr, font_desc
);
634 CFRelease (font_desc
);
638 range
->font
= nullptr;
641 range
->index_first
= last_index
;
642 range
->index_last
= event
->index
- 1;
644 last_index
= event
->index
;
649 active_features
.push (event
->feature
);
651 active_feature_t
*feature
= active_features
.lsearch (event
->feature
);
653 active_features
.remove_ordered (feature
- active_features
.arrayZ
);
658 unsigned int scratch_size
;
659 hb_buffer_t::scratch_buffer_t
*scratch
= buffer
->get_scratch_buffer (&scratch_size
);
661 #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \
662 Type *name = (Type *) scratch; \
664 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
665 if (unlikely (_consumed > scratch_size)) \
670 scratch += _consumed; \
671 scratch_size -= _consumed; \
674 ALLOCATE_ARRAY (UniChar
, pchars
, buffer
->len
* 2, ((void)nullptr) /*nothing*/);
675 unsigned int chars_len
= 0;
676 for (unsigned int i
= 0; i
< buffer
->len
; i
++) {
677 hb_codepoint_t c
= buffer
->info
[i
].codepoint
;
678 if (likely (c
<= 0xFFFFu
))
679 pchars
[chars_len
++] = c
;
680 else if (unlikely (c
> 0x10FFFFu
))
681 pchars
[chars_len
++] = 0xFFFDu
;
683 pchars
[chars_len
++] = 0xD800u
+ ((c
- 0x10000u
) >> 10);
684 pchars
[chars_len
++] = 0xDC00u
+ ((c
- 0x10000u
) & ((1u << 10) - 1));
688 ALLOCATE_ARRAY (unsigned int, log_clusters
, chars_len
, ((void)nullptr) /*nothing*/);
690 for (unsigned int i
= 0; i
< buffer
->len
; i
++)
692 hb_codepoint_t c
= buffer
->info
[i
].codepoint
;
693 unsigned int cluster
= buffer
->info
[i
].cluster
;
694 log_clusters
[chars_len
++] = cluster
;
695 if (hb_in_range (c
, 0x10000u
, 0x10FFFFu
))
696 log_clusters
[chars_len
++] = cluster
; /* Surrogates. */
701 DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \
707 CFStringRef string_ref
= nullptr;
708 CTLineRef line
= nullptr;
713 DEBUG_MSG (CORETEXT
, buffer
, "Buffer resize");
714 /* string_ref uses the scratch-buffer for backing store, and line references
715 * string_ref (via attr_string). We must release those before resizing buffer. */
718 CFRelease (string_ref
);
720 string_ref
= nullptr;
723 /* Get previous start-of-scratch-area, that we use later for readjusting
724 * our existing scratch arrays. */
725 unsigned int old_scratch_used
;
726 hb_buffer_t::scratch_buffer_t
*old_scratch
;
727 old_scratch
= buffer
->get_scratch_buffer (&old_scratch_used
);
728 old_scratch_used
= scratch
- old_scratch
;
730 if (unlikely (!buffer
->ensure (buffer
->allocated
* 2)))
731 FAIL ("Buffer resize failed");
733 /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the
734 * cleanest way to do without completely restructuring the rest of this shaper. */
735 scratch
= buffer
->get_scratch_buffer (&scratch_size
);
736 pchars
= reinterpret_cast<UniChar
*> (((char *) scratch
+ ((char *) pchars
- (char *) old_scratch
)));
737 log_clusters
= reinterpret_cast<unsigned int *> (((char *) scratch
+ ((char *) log_clusters
- (char *) old_scratch
)));
738 scratch
+= old_scratch_used
;
739 scratch_size
-= old_scratch_used
;
742 string_ref
= CFStringCreateWithCharactersNoCopy (nullptr,
745 if (unlikely (!string_ref
))
746 FAIL ("CFStringCreateWithCharactersNoCopy failed");
748 /* Create an attributed string, populate it, and create a line from it, then release attributed string. */
750 CFMutableAttributedStringRef attr_string
= CFAttributedStringCreateMutable (kCFAllocatorDefault
,
752 if (unlikely (!attr_string
))
753 FAIL ("CFAttributedStringCreateMutable failed");
754 CFAttributedStringReplaceString (attr_string
, CFRangeMake (0, 0), string_ref
);
755 if (HB_DIRECTION_IS_VERTICAL (buffer
->props
.direction
))
757 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (0, chars_len
),
758 kCTVerticalFormsAttributeName
, kCFBooleanTrue
);
761 if (buffer
->props
.language
)
763 /* What's the iOS equivalent of this check?
764 * The symbols was introduced in iOS 7.0.
765 * At any rate, our fallback is safe and works fine. */
766 #if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1090
767 # define kCTLanguageAttributeName CFSTR ("NSLanguage")
769 CFStringRef lang
= CFStringCreateWithCStringNoCopy (kCFAllocatorDefault
,
770 hb_language_to_string (buffer
->props
.language
),
771 kCFStringEncodingUTF8
,
773 if (unlikely (!lang
))
775 CFRelease (attr_string
);
776 FAIL ("CFStringCreateWithCStringNoCopy failed");
778 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (0, chars_len
),
779 kCTLanguageAttributeName
, lang
);
782 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (0, chars_len
),
783 kCTFontAttributeName
, ct_font
);
785 if (num_features
&& range_records
.length
)
787 unsigned int start
= 0;
788 range_record_t
*last_range
= &range_records
[0];
789 for (unsigned int k
= 0; k
< chars_len
; k
++)
791 range_record_t
*range
= last_range
;
792 while (log_clusters
[k
] < range
->index_first
)
794 while (log_clusters
[k
] > range
->index_last
)
796 if (range
!= last_range
)
798 if (last_range
->font
)
799 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (start
, k
- start
),
800 kCTFontAttributeName
, last_range
->font
);
807 if (start
!= chars_len
&& last_range
->font
)
808 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (start
, chars_len
- start
),
809 kCTFontAttributeName
, last_range
->font
);
811 /* Enable/disable kern if requested.
813 * Note: once kern is disabled, reenabling it doesn't currently seem to work in CoreText.
817 unsigned int zeroint
= 0;
818 CFNumberRef zero
= CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &zeroint
);
819 for (unsigned int i
= 0; i
< num_features
; i
++)
821 const hb_feature_t
&feature
= features
[i
];
822 if (feature
.tag
== HB_TAG('k','e','r','n') &&
823 feature
.start
< chars_len
&& feature
.start
< feature
.end
)
825 CFRange feature_range
= CFRangeMake (feature
.start
,
826 hb_min (feature
.end
, chars_len
) - feature
.start
);
828 CFAttributedStringRemoveAttribute (attr_string
, feature_range
, kCTKernAttributeName
);
830 CFAttributedStringSetAttribute (attr_string
, feature_range
, kCTKernAttributeName
, zero
);
836 int level
= HB_DIRECTION_IS_FORWARD (buffer
->props
.direction
) ? 0 : 1;
837 CFNumberRef level_number
= CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &level
);
838 #if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
839 extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel
;
841 CFDictionaryRef options
= CFDictionaryCreate (kCFAllocatorDefault
,
842 (const void **) &kCTTypesetterOptionForcedEmbeddingLevel
,
843 (const void **) &level_number
,
845 &kCFTypeDictionaryKeyCallBacks
,
846 &kCFTypeDictionaryValueCallBacks
);
847 CFRelease (level_number
);
848 if (unlikely (!options
))
850 CFRelease (attr_string
);
851 FAIL ("CFDictionaryCreate failed");
854 CTTypesetterRef typesetter
= CTTypesetterCreateWithAttributedStringAndOptions (attr_string
, options
);
856 CFRelease (attr_string
);
857 if (unlikely (!typesetter
))
858 FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed");
860 line
= CTTypesetterCreateLine (typesetter
, CFRangeMake(0, 0));
861 CFRelease (typesetter
);
862 if (unlikely (!line
))
863 FAIL ("CTTypesetterCreateLine failed");
866 CFArrayRef glyph_runs
= CTLineGetGlyphRuns (line
);
867 unsigned int num_runs
= CFArrayGetCount (glyph_runs
);
868 DEBUG_MSG (CORETEXT
, nullptr, "Num runs: %d", num_runs
);
871 uint32_t status_or
= 0;
872 CGFloat advances_so_far
= 0;
873 /* For right-to-left runs, CoreText returns the glyphs positioned such that
874 * any trailing whitespace is to the left of (0,0). Adjust coordinate system
875 * to fix for that. Test with any RTL string with trailing spaces.
876 * https://crbug.com/469028
878 if (HB_DIRECTION_IS_BACKWARD (buffer
->props
.direction
))
880 advances_so_far
-= CTLineGetTrailingWhitespaceWidth (line
);
881 if (HB_DIRECTION_IS_VERTICAL (buffer
->props
.direction
))
882 advances_so_far
= -advances_so_far
;
885 const CFRange range_all
= CFRangeMake (0, 0);
887 for (unsigned int i
= 0; i
< num_runs
; i
++)
889 CTRunRef run
= static_cast<CTRunRef
>(CFArrayGetValueAtIndex (glyph_runs
, i
));
890 CTRunStatus run_status
= CTRunGetStatus (run
);
891 status_or
|= run_status
;
892 DEBUG_MSG (CORETEXT
, run
, "CTRunStatus: %x", run_status
);
893 CGFloat run_advance
= CTRunGetTypographicBounds (run
, range_all
, nullptr, nullptr, nullptr);
894 if (HB_DIRECTION_IS_VERTICAL (buffer
->props
.direction
))
895 run_advance
= -run_advance
;
896 DEBUG_MSG (CORETEXT
, run
, "Run advance: %g", (double) run_advance
);
898 /* CoreText does automatic font fallback (AKA "cascading") for characters
899 * not supported by the requested font, and provides no way to turn it off,
900 * so we must detect if the returned run uses a font other than the requested
901 * one and fill in the buffer with .notdef glyphs instead of random glyph
902 * indices from a different font.
904 CFDictionaryRef attributes
= CTRunGetAttributes (run
);
905 CTFontRef run_ct_font
= static_cast<CTFontRef
>(CFDictionaryGetValue (attributes
, kCTFontAttributeName
));
906 if (!CFEqual (run_ct_font
, ct_font
))
908 /* The run doesn't use our main font instance. We have to figure out
909 * whether font fallback happened, or this is just CoreText giving us
910 * another CTFont using the same underlying CGFont. CoreText seems
911 * to do that in a variety of situations, one of which being vertical
912 * text, but also perhaps for caching reasons.
914 * First, see if it uses any of our subfonts created to set font features...
916 * Next, compare the CGFont to the one we used to create our fonts.
917 * Even this doesn't work all the time.
919 * Finally, we compare PS names, which I don't think are unique...
921 * Looks like if we really want to be sure here we have to modify the
922 * font to change the name table, similar to what we do in the uniscribe
925 * However, even that wouldn't work if we were passed in the CGFont to
926 * construct a hb_face to begin with.
928 * See: https://github.com/harfbuzz/harfbuzz/pull/36
930 * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098
932 bool matched
= false;
933 for (unsigned int i
= 0; i
< range_records
.length
; i
++)
934 if (range_records
[i
].font
&& CFEqual (run_ct_font
, range_records
[i
].font
))
941 CGFontRef run_cg_font
= CTFontCopyGraphicsFont (run_ct_font
, nullptr);
944 matched
= CFEqual (run_cg_font
, cg_font
);
945 CFRelease (run_cg_font
);
950 CFStringRef font_ps_name
= CTFontCopyName (ct_font
, kCTFontPostScriptNameKey
);
951 CFStringRef run_ps_name
= CTFontCopyName (run_ct_font
, kCTFontPostScriptNameKey
);
952 CFComparisonResult result
= CFStringCompare (run_ps_name
, font_ps_name
, 0);
953 CFRelease (run_ps_name
);
954 CFRelease (font_ps_name
);
955 if (result
== kCFCompareEqualTo
)
960 CFRange range
= CTRunGetStringRange (run
);
961 DEBUG_MSG (CORETEXT
, run
, "Run used fallback font: %ld..%ld",
962 range
.location
, range
.location
+ range
.length
);
963 if (!buffer
->ensure_inplace (buffer
->len
+ range
.length
))
964 goto resize_and_retry
;
965 hb_glyph_info_t
*info
= buffer
->info
+ buffer
->len
;
967 hb_codepoint_t notdef
= 0;
968 hb_direction_t dir
= buffer
->props
.direction
;
969 hb_position_t x_advance
, y_advance
, x_offset
, y_offset
;
970 hb_font_get_glyph_advance_for_direction (font
, notdef
, dir
, &x_advance
, &y_advance
);
971 hb_font_get_glyph_origin_for_direction (font
, notdef
, dir
, &x_offset
, &y_offset
);
972 hb_position_t advance
= x_advance
+ y_advance
;
973 x_offset
= -x_offset
;
974 y_offset
= -y_offset
;
976 unsigned int old_len
= buffer
->len
;
977 for (CFIndex j
= range
.location
; j
< range
.location
+ range
.length
; j
++)
979 UniChar ch
= CFStringGetCharacterAtIndex (string_ref
, j
);
980 if (hb_in_range
<UniChar
> (ch
, 0xDC00u
, 0xDFFFu
) && range
.location
< j
)
982 ch
= CFStringGetCharacterAtIndex (string_ref
, j
- 1);
983 if (hb_in_range
<UniChar
> (ch
, 0xD800u
, 0xDBFFu
))
984 /* This is the second of a surrogate pair. Don't need .notdef
988 if (buffer
->unicode
->is_default_ignorable (ch
))
991 info
->codepoint
= notdef
;
992 info
->cluster
= log_clusters
[j
];
994 info
->mask
= advance
;
995 info
->var1
.i32
= x_offset
;
996 info
->var2
.i32
= y_offset
;
1001 if (HB_DIRECTION_IS_BACKWARD (buffer
->props
.direction
))
1002 buffer
->reverse_range (old_len
, buffer
->len
);
1003 advances_so_far
+= run_advance
;
1008 unsigned int num_glyphs
= CTRunGetGlyphCount (run
);
1009 if (num_glyphs
== 0)
1012 if (!buffer
->ensure_inplace (buffer
->len
+ num_glyphs
))
1013 goto resize_and_retry
;
1015 hb_glyph_info_t
*run_info
= buffer
->info
+ buffer
->len
;
1017 /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always
1018 * succeed, and so copying data to our own buffer will be rare. Reports
1019 * have it that this changed in OS X 10.10 Yosemite, and nullptr is returned
1020 * frequently. At any rate, we can test that codepath by setting USE_PTR
1023 #define USE_PTR true
1025 #define SCRATCH_SAVE() \
1026 unsigned int scratch_size_saved = scratch_size; \
1027 hb_buffer_t::scratch_buffer_t *scratch_saved = scratch
1029 #define SCRATCH_RESTORE() \
1030 scratch_size = scratch_size_saved; \
1031 scratch = scratch_saved
1033 { /* Setup glyphs */
1035 const CGGlyph
* glyphs
= USE_PTR
? CTRunGetGlyphsPtr (run
) : nullptr;
1037 ALLOCATE_ARRAY (CGGlyph
, glyph_buf
, num_glyphs
, goto resize_and_retry
);
1038 CTRunGetGlyphs (run
, range_all
, glyph_buf
);
1041 const CFIndex
* string_indices
= USE_PTR
? CTRunGetStringIndicesPtr (run
) : nullptr;
1042 if (!string_indices
) {
1043 ALLOCATE_ARRAY (CFIndex
, index_buf
, num_glyphs
, goto resize_and_retry
);
1044 CTRunGetStringIndices (run
, range_all
, index_buf
);
1045 string_indices
= index_buf
;
1047 hb_glyph_info_t
*info
= run_info
;
1048 for (unsigned int j
= 0; j
< num_glyphs
; j
++)
1050 info
->codepoint
= glyphs
[j
];
1051 info
->cluster
= log_clusters
[string_indices
[j
]];
1058 * Note that CoreText does not return advances for glyphs. As such,
1059 * for all but last glyph, we use the delta position to next glyph as
1060 * advance (in the advance direction only), and for last glyph we set
1061 * whatever is needed to make the whole run's advance add up. */
1063 const CGPoint
* positions
= USE_PTR
? CTRunGetPositionsPtr (run
) : nullptr;
1065 ALLOCATE_ARRAY (CGPoint
, position_buf
, num_glyphs
, goto resize_and_retry
);
1066 CTRunGetPositions (run
, range_all
, position_buf
);
1067 positions
= position_buf
;
1069 hb_glyph_info_t
*info
= run_info
;
1070 if (HB_DIRECTION_IS_HORIZONTAL (buffer
->props
.direction
))
1072 hb_position_t x_offset
= round ((positions
[0].x
- advances_so_far
) * x_mult
);
1073 for (unsigned int j
= 0; j
< num_glyphs
; j
++)
1076 if (likely (j
+ 1 < num_glyphs
))
1077 advance
= positions
[j
+ 1].x
- positions
[j
].x
;
1078 else /* last glyph */
1079 advance
= run_advance
- (positions
[j
].x
- positions
[0].x
);
1080 /* int cast necessary to pass through negative values. */
1081 info
->mask
= (int) round (advance
* x_mult
);
1082 info
->var1
.i32
= x_offset
;
1083 info
->var2
.i32
= round (positions
[j
].y
* y_mult
);
1089 hb_position_t y_offset
= round ((positions
[0].y
- advances_so_far
) * y_mult
);
1090 for (unsigned int j
= 0; j
< num_glyphs
; j
++)
1093 if (likely (j
+ 1 < num_glyphs
))
1094 advance
= positions
[j
+ 1].y
- positions
[j
].y
;
1095 else /* last glyph */
1096 advance
= run_advance
- (positions
[j
].y
- positions
[0].y
);
1097 /* int cast necessary to pass through negative values. */
1098 info
->mask
= (int) round (advance
* y_mult
);
1099 info
->var1
.i32
= round (positions
[j
].x
* x_mult
);
1100 info
->var2
.i32
= y_offset
;
1105 advances_so_far
+= run_advance
;
1107 #undef SCRATCH_RESTORE
1110 #undef ALLOCATE_ARRAY
1112 buffer
->len
+= num_glyphs
;
1115 buffer
->clear_positions ();
1117 unsigned int count
= buffer
->len
;
1118 hb_glyph_info_t
*info
= buffer
->info
;
1119 hb_glyph_position_t
*pos
= buffer
->pos
;
1120 if (HB_DIRECTION_IS_HORIZONTAL (buffer
->props
.direction
))
1121 for (unsigned int i
= 0; i
< count
; i
++)
1123 pos
->x_advance
= info
->mask
;
1124 pos
->x_offset
= info
->var1
.i32
;
1125 pos
->y_offset
= info
->var2
.i32
;
1130 for (unsigned int i
= 0; i
< count
; i
++)
1132 pos
->y_advance
= info
->mask
;
1133 pos
->x_offset
= info
->var1
.i32
;
1134 pos
->y_offset
= info
->var2
.i32
;
1139 /* Fix up clusters so that we never return out-of-order indices;
1140 * if core text has reordered glyphs, we'll merge them to the
1141 * beginning of the reordered cluster. CoreText is nice enough
1142 * to tell us whenever it has produced nonmonotonic results...
1143 * Note that we assume the input clusters were nonmonotonic to
1146 * This does *not* mean we'll form the same clusters as Uniscribe
1147 * or the native OT backend, only that the cluster indices will be
1148 * monotonic in the output buffer. */
1149 if (count
> 1 && (status_or
& kCTRunStatusNonMonotonic
) &&
1150 buffer
->cluster_level
!= HB_BUFFER_CLUSTER_LEVEL_CHARACTERS
)
1152 hb_glyph_info_t
*info
= buffer
->info
;
1153 if (HB_DIRECTION_IS_FORWARD (buffer
->props
.direction
))
1155 unsigned int cluster
= info
[count
- 1].cluster
;
1156 for (unsigned int i
= count
- 1; i
> 0; i
--)
1158 cluster
= hb_min (cluster
, info
[i
- 1].cluster
);
1159 info
[i
- 1].cluster
= cluster
;
1164 unsigned int cluster
= info
[0].cluster
;
1165 for (unsigned int i
= 1; i
< count
; i
++)
1167 cluster
= hb_min (cluster
, info
[i
].cluster
);
1168 info
[i
].cluster
= cluster
;
1174 /* TODO: Sometimes the above positioning code generates negative
1175 * advance values. Fix them up. Example, with NotoNastaliqUrdu
1176 * font and sequence ابهد. */
1178 buffer
->clear_glyph_flags ();
1179 buffer
->unsafe_to_break ();
1185 CFRelease (string_ref
);
1189 for (unsigned int i
= 0; i
< range_records
.length
; i
++)
1190 if (range_records
[i
].font
)
1191 CFRelease (range_records
[i
].font
);