25 * Mozilla Author(s): Jonathan Kew |
25 * Mozilla Author(s): Jonathan Kew |
26 * Google Author(s): Behdad Esfahbod |
26 * Google Author(s): Behdad Esfahbod |
27 */ |
27 */ |
28 |
28 |
29 #define HB_SHAPER coretext |
29 #define HB_SHAPER coretext |
|
30 |
|
31 #include "hb-private.hh" |
|
32 #include "hb-debug.hh" |
30 #include "hb-shaper-impl-private.hh" |
33 #include "hb-shaper-impl-private.hh" |
31 |
34 |
32 #include "hb-coretext.h" |
35 #include "hb-coretext.h" |
33 |
36 #include <math.h> |
34 |
37 |
35 #ifndef HB_DEBUG_CORETEXT |
38 /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */ |
36 #define HB_DEBUG_CORETEXT (HB_DEBUG+0) |
39 #define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f |
37 #endif |
40 |
38 |
41 static CGFloat |
|
42 coretext_font_size (float ptem) |
|
43 { |
|
44 /* CoreText points are CSS pixels (96 per inch), |
|
45 * NOT typographic points (72 per inch). |
|
46 * |
|
47 * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html |
|
48 */ |
|
49 ptem *= 96.f / 72.f; |
|
50 return ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : ptem; |
|
51 } |
39 |
52 |
40 static void |
53 static void |
41 release_table_data (void *user_data) |
54 release_table_data (void *user_data) |
42 { |
55 { |
43 CFDataRef cf_data = reinterpret_cast<CFDataRef> (user_data); |
56 CFDataRef cf_data = reinterpret_cast<CFDataRef> (user_data); |
48 reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) |
61 reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) |
49 { |
62 { |
50 CGFontRef cg_font = reinterpret_cast<CGFontRef> (user_data); |
63 CGFontRef cg_font = reinterpret_cast<CGFontRef> (user_data); |
51 CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag); |
64 CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag); |
52 if (unlikely (!cf_data)) |
65 if (unlikely (!cf_data)) |
53 return NULL; |
66 return nullptr; |
54 |
67 |
55 const char *data = reinterpret_cast<const char*> (CFDataGetBytePtr (cf_data)); |
68 const char *data = reinterpret_cast<const char*> (CFDataGetBytePtr (cf_data)); |
56 const size_t length = CFDataGetLength (cf_data); |
69 const size_t length = CFDataGetLength (cf_data); |
57 if (!data || !length) |
70 if (!data || !length) |
58 return NULL; |
71 return nullptr; |
59 |
72 |
60 return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY, |
73 return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY, |
61 reinterpret_cast<void *> (const_cast<__CFData *> (cf_data)), |
74 reinterpret_cast<void *> (const_cast<__CFData *> (cf_data)), |
62 release_table_data); |
75 release_table_data); |
63 } |
76 } |
64 |
77 |
|
78 static void |
|
79 _hb_cg_font_release (void *data) |
|
80 { |
|
81 CGFontRelease ((CGFontRef) data); |
|
82 } |
|
83 |
65 hb_face_t * |
84 hb_face_t * |
66 hb_coretext_face_create (CGFontRef cg_font) |
85 hb_coretext_face_create (CGFontRef cg_font) |
67 { |
86 { |
68 return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), (hb_destroy_func_t) CGFontRelease); |
87 return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), _hb_cg_font_release); |
69 } |
88 } |
70 |
89 |
71 |
90 HB_SHAPER_DATA_ENSURE_DEFINE(coretext, face) |
72 HB_SHAPER_DATA_ENSURE_DECLARE(coretext, face) |
91 HB_SHAPER_DATA_ENSURE_DEFINE_WITH_CONDITION(coretext, font, |
73 HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font) |
92 fabs (CTFontGetSize((CTFontRef) data) - coretext_font_size (font->ptem)) <= .5 |
74 |
93 ) |
75 |
94 |
76 /* |
95 /* |
77 * shaper face data |
96 * shaper face data |
78 */ |
97 */ |
79 |
98 |
102 |
121 |
103 static void |
122 static void |
104 release_data (void *info, const void *data, size_t size) |
123 release_data (void *info, const void *data, size_t size) |
105 { |
124 { |
106 assert (hb_blob_get_length ((hb_blob_t *) info) == size && |
125 assert (hb_blob_get_length ((hb_blob_t *) info) == size && |
107 hb_blob_get_data ((hb_blob_t *) info, NULL) == data); |
126 hb_blob_get_data ((hb_blob_t *) info, nullptr) == data); |
108 |
127 |
109 hb_blob_destroy ((hb_blob_t *) info); |
128 hb_blob_destroy ((hb_blob_t *) info); |
110 } |
129 } |
111 |
130 |
112 static CGFontRef |
131 static CGFontRef |
113 create_cg_font (hb_face_t *face) |
132 create_cg_font (hb_face_t *face) |
114 { |
133 { |
115 CGFontRef cg_font = NULL; |
134 CGFontRef cg_font = nullptr; |
116 if (face->destroy == (hb_destroy_func_t) CGFontRelease) |
135 if (face->destroy == _hb_cg_font_release) |
117 { |
136 { |
118 cg_font = CGFontRetain ((CGFontRef) face->user_data); |
137 cg_font = CGFontRetain ((CGFontRef) face->user_data); |
119 } |
138 } |
120 else |
139 else |
121 { |
140 { |
138 } |
157 } |
139 |
158 |
140 static CTFontRef |
159 static CTFontRef |
141 create_ct_font (CGFontRef cg_font, CGFloat font_size) |
160 create_ct_font (CGFontRef cg_font, CGFloat font_size) |
142 { |
161 { |
143 CTFontRef ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, NULL, NULL); |
162 CTFontRef ct_font = nullptr; |
|
163 |
|
164 /* CoreText does not enable trak table usage / tracking when creating a CTFont |
|
165 * using CTFontCreateWithGraphicsFont. The only way of enabling tracking seems |
|
166 * to be through the CTFontCreateUIFontForLanguage call. */ |
|
167 CFStringRef cg_postscript_name = CGFontCopyPostScriptName (cg_font); |
|
168 if (CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSText")) || |
|
169 CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSDisplay"))) |
|
170 { |
|
171 CTFontUIFontType font_type = kCTFontUIFontSystem; |
|
172 if (CFStringHasSuffix (cg_postscript_name, CFSTR ("-Bold"))) |
|
173 font_type = kCTFontUIFontEmphasizedSystem; |
|
174 |
|
175 ct_font = CTFontCreateUIFontForLanguage (font_type, font_size, nullptr); |
|
176 CFStringRef ct_result_name = CTFontCopyPostScriptName(ct_font); |
|
177 if (CFStringCompare (ct_result_name, cg_postscript_name, 0) != kCFCompareEqualTo) |
|
178 { |
|
179 CFRelease(ct_font); |
|
180 ct_font = nullptr; |
|
181 } |
|
182 CFRelease (ct_result_name); |
|
183 } |
|
184 CFRelease (cg_postscript_name); |
|
185 |
|
186 if (!ct_font) |
|
187 ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, nullptr, nullptr); |
|
188 |
144 if (unlikely (!ct_font)) { |
189 if (unlikely (!ct_font)) { |
145 DEBUG_MSG (CORETEXT, cg_font, "Font CTFontCreateWithGraphicsFont() failed"); |
190 DEBUG_MSG (CORETEXT, cg_font, "Font CTFontCreateWithGraphicsFont() failed"); |
146 return NULL; |
191 return nullptr; |
147 } |
192 } |
148 |
193 |
149 /* crbug.com/576941 and crbug.com/625902 and the investigation in the latter |
194 /* crbug.com/576941 and crbug.com/625902 and the investigation in the latter |
150 * bug indicate that the cascade list reconfiguration occasionally causes |
195 * bug indicate that the cascade list reconfiguration occasionally causes |
151 * crashes in CoreText on OS X 10.9, thus let's skip this step on older |
196 * crashes in CoreText on OS X 10.9, thus let's skip this step on older |
152 * operating system versions. Except for the emoji font, where _not_ |
197 * operating system versions. Except for the emoji font, where _not_ |
153 * reconfiguring the cascade list causes CoreText crashes. For details, see |
198 * reconfiguring the cascade list causes CoreText crashes. For details, see |
154 * crbug.com/549610 */ |
199 * crbug.com/549610 */ |
155 // 0x00070000 stands for "kCTVersionNumber10_10", see CoreText.h |
200 // 0x00070000 stands for "kCTVersionNumber10_10", see CoreText.h |
156 if (&CTGetCoreTextVersion != NULL && CTGetCoreTextVersion() < 0x00070000) { |
201 if (&CTGetCoreTextVersion != nullptr && CTGetCoreTextVersion() < 0x00070000) { |
157 CFStringRef fontName = CTFontCopyPostScriptName (ct_font); |
202 CFStringRef fontName = CTFontCopyPostScriptName (ct_font); |
158 bool isEmojiFont = CFStringCompare (fontName, CFSTR("AppleColorEmoji"), 0) == kCFCompareEqualTo; |
203 bool isEmojiFont = CFStringCompare (fontName, CFSTR("AppleColorEmoji"), 0) == kCFCompareEqualTo; |
159 CFRelease (fontName); |
204 CFRelease (fontName); |
160 if (!isEmojiFont) |
205 if (!isEmojiFont) |
161 return ct_font; |
206 return ct_font; |
165 |
210 |
166 /* Create font copy with cascade list that has LastResort first; this speeds up CoreText |
211 /* Create font copy with cascade list that has LastResort first; this speeds up CoreText |
167 * font fallback which we don't need anyway. */ |
212 * font fallback which we don't need anyway. */ |
168 { |
213 { |
169 CTFontDescriptorRef last_resort_font_desc = get_last_resort_font_desc (); |
214 CTFontDescriptorRef last_resort_font_desc = get_last_resort_font_desc (); |
170 CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, NULL, last_resort_font_desc); |
215 CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, last_resort_font_desc); |
171 CFRelease (last_resort_font_desc); |
216 CFRelease (last_resort_font_desc); |
172 if (new_ct_font) |
217 if (new_ct_font) |
173 { |
218 { |
174 /* The CTFontCreateCopyWithAttributes call fails to stay on the same font |
219 /* The CTFontCreateCopyWithAttributes call fails to stay on the same font |
175 * when reconfiguring the cascade list and may switch to a different font |
220 * when reconfiguring the cascade list and may switch to a different font |
200 if (original_url) |
245 if (original_url) |
201 CFRelease (original_url); |
246 CFRelease (original_url); |
202 return ct_font; |
247 return ct_font; |
203 } |
248 } |
204 |
249 |
205 struct hb_coretext_shaper_face_data_t { |
|
206 CGFontRef cg_font; |
|
207 CTFontRef ct_font; |
|
208 }; |
|
209 |
|
210 hb_coretext_shaper_face_data_t * |
250 hb_coretext_shaper_face_data_t * |
211 _hb_coretext_shaper_face_data_create (hb_face_t *face) |
251 _hb_coretext_shaper_face_data_create (hb_face_t *face) |
212 { |
252 { |
213 hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) calloc (1, sizeof (hb_coretext_shaper_face_data_t)); |
253 CGFontRef cg_font = create_cg_font (face); |
214 if (unlikely (!data)) |
254 |
215 return NULL; |
255 if (unlikely (!cg_font)) |
216 |
|
217 data->cg_font = create_cg_font (face); |
|
218 if (unlikely (!data->cg_font)) |
|
219 { |
256 { |
220 DEBUG_MSG (CORETEXT, face, "CGFont creation failed.."); |
257 DEBUG_MSG (CORETEXT, face, "CGFont creation failed.."); |
221 free (data); |
258 return nullptr; |
222 return NULL; |
259 } |
223 } |
260 |
224 |
261 return (hb_coretext_shaper_face_data_t *) cg_font; |
225 /* We use 36pt size instead of UPEM, because CoreText implements the 'trak' table, |
|
226 * which can make the font too tight at large sizes. 36pt should be a good semi-neutral |
|
227 * size. |
|
228 * |
|
229 * Since we always create CTFont at a fixed size, our CTFont lives in face_data |
|
230 * instead of font_data. Which is good, because when people change scale on |
|
231 * hb_font_t, we won't need to update our CTFont. */ |
|
232 data->ct_font = create_ct_font (data->cg_font, 36.); |
|
233 if (unlikely (!data->ct_font)) |
|
234 { |
|
235 DEBUG_MSG (CORETEXT, face, "CTFont creation failed."); |
|
236 CFRelease (data->cg_font); |
|
237 free (data); |
|
238 return NULL; |
|
239 } |
|
240 |
|
241 return data; |
|
242 } |
262 } |
243 |
263 |
244 void |
264 void |
245 _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) |
265 _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) |
246 { |
266 { |
247 CFRelease (data->ct_font); |
267 CFRelease ((CGFontRef) data); |
248 CFRelease (data->cg_font); |
|
249 free (data); |
|
250 } |
268 } |
251 |
269 |
252 /* |
270 /* |
253 * Since: 0.9.10 |
271 * Since: 0.9.10 |
254 */ |
272 */ |
255 CGFontRef |
273 CGFontRef |
256 hb_coretext_face_get_cg_font (hb_face_t *face) |
274 hb_coretext_face_get_cg_font (hb_face_t *face) |
257 { |
275 { |
258 if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL; |
276 if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr; |
259 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); |
277 return (CGFontRef) HB_SHAPER_DATA_GET (face); |
260 return face_data->cg_font; |
|
261 } |
278 } |
262 |
279 |
263 |
280 |
264 /* |
281 /* |
265 * shaper font data |
282 * shaper font data |
266 */ |
283 */ |
267 |
284 |
268 struct hb_coretext_shaper_font_data_t {}; |
|
269 |
|
270 hb_coretext_shaper_font_data_t * |
285 hb_coretext_shaper_font_data_t * |
271 _hb_coretext_shaper_font_data_create (hb_font_t *font HB_UNUSED) |
286 _hb_coretext_shaper_font_data_create (hb_font_t *font) |
272 { |
287 { |
273 return (hb_coretext_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; |
288 hb_face_t *face = font->face; |
|
289 if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr; |
|
290 CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face); |
|
291 |
|
292 CTFontRef ct_font = create_ct_font (cg_font, coretext_font_size (font->ptem)); |
|
293 |
|
294 if (unlikely (!ct_font)) |
|
295 { |
|
296 DEBUG_MSG (CORETEXT, font, "CGFont creation failed.."); |
|
297 return nullptr; |
|
298 } |
|
299 |
|
300 return (hb_coretext_shaper_font_data_t *) ct_font; |
274 } |
301 } |
275 |
302 |
276 void |
303 void |
277 _hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data) |
304 _hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data) |
278 { |
305 { |
|
306 CFRelease ((CTFontRef) data); |
279 } |
307 } |
280 |
308 |
281 |
309 |
282 /* |
310 /* |
283 * shaper shape_plan data |
311 * shaper shape_plan data |
321 |
347 |
322 struct active_feature_t { |
348 struct active_feature_t { |
323 feature_record_t rec; |
349 feature_record_t rec; |
324 unsigned int order; |
350 unsigned int order; |
325 |
351 |
326 static int cmp (const active_feature_t *a, const active_feature_t *b) { |
352 static int cmp (const void *pa, const void *pb) { |
|
353 const active_feature_t *a = (const active_feature_t *) pa; |
|
354 const active_feature_t *b = (const active_feature_t *) pb; |
327 return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : |
355 return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : |
328 a->order < b->order ? -1 : a->order > b->order ? 1 : |
356 a->order < b->order ? -1 : a->order > b->order ? 1 : |
329 a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 : |
357 a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 : |
330 0; |
358 0; |
331 } |
359 } |
337 struct feature_event_t { |
365 struct feature_event_t { |
338 unsigned int index; |
366 unsigned int index; |
339 bool start; |
367 bool start; |
340 active_feature_t feature; |
368 active_feature_t feature; |
341 |
369 |
342 static int cmp (const feature_event_t *a, const feature_event_t *b) { |
370 static int cmp (const void *pa, const void *pb) { |
|
371 const feature_event_t *a = (const feature_event_t *) pa; |
|
372 const feature_event_t *b = (const feature_event_t *) pb; |
343 return a->index < b->index ? -1 : a->index > b->index ? 1 : |
373 return a->index < b->index ? -1 : a->index > b->index ? 1 : |
344 a->start < b->start ? -1 : a->start > b->start ? 1 : |
374 a->start < b->start ? -1 : a->start > b->start ? 1 : |
345 active_feature_t::cmp (&a->feature, &b->feature); |
375 active_feature_t::cmp (&a->feature, &b->feature); |
346 } |
376 } |
347 }; |
377 }; |
536 hb_buffer_t *buffer, |
566 hb_buffer_t *buffer, |
537 const hb_feature_t *features, |
567 const hb_feature_t *features, |
538 unsigned int num_features) |
568 unsigned int num_features) |
539 { |
569 { |
540 hb_face_t *face = font->face; |
570 hb_face_t *face = font->face; |
541 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); |
571 CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face); |
542 |
572 CTFontRef ct_font = (CTFontRef) HB_SHAPER_DATA_GET (font); |
543 CGFloat ct_font_size = CTFontGetSize (face_data->ct_font); |
573 |
|
574 CGFloat ct_font_size = CTFontGetSize (ct_font); |
544 CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size; |
575 CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size; |
545 CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size; |
576 CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size; |
546 |
577 |
547 /* Attach marks to their bases, to match the 'ot' shaper. |
578 /* Attach marks to their bases, to match the 'ot' shaper. |
548 * Adapted from hb-ot-shape:hb_form_clusters(). |
579 * Adapted from hb-ot-shape:hb_form_clusters(). |
639 |
670 |
640 /* TODO sort and resolve conflicting features? */ |
671 /* TODO sort and resolve conflicting features? */ |
641 /* active_features.qsort (); */ |
672 /* active_features.qsort (); */ |
642 for (unsigned int j = 0; j < active_features.len; j++) |
673 for (unsigned int j = 0; j < active_features.len; j++) |
643 { |
674 { |
644 CFStringRef keys[2] = { |
675 CFStringRef keys[] = { |
645 kCTFontFeatureTypeIdentifierKey, |
676 kCTFontFeatureTypeIdentifierKey, |
646 kCTFontFeatureSelectorIdentifierKey |
677 kCTFontFeatureSelectorIdentifierKey |
647 }; |
678 }; |
648 CFNumberRef values[2] = { |
679 CFNumberRef values[] = { |
649 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), |
680 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), |
650 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) |
681 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) |
651 }; |
682 }; |
|
683 static_assert ((ARRAY_LENGTH_CONST (keys) == ARRAY_LENGTH_CONST (values)), ""); |
652 CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, |
684 CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, |
653 (const void **) keys, |
685 (const void **) keys, |
654 (const void **) values, |
686 (const void **) values, |
655 2, |
687 ARRAY_LENGTH (keys), |
656 &kCFTypeDictionaryKeyCallBacks, |
688 &kCFTypeDictionaryKeyCallBacks, |
657 &kCFTypeDictionaryValueCallBacks); |
689 &kCFTypeDictionaryValueCallBacks); |
658 CFRelease (values[0]); |
690 for (unsigned int i = 0; i < ARRAY_LENGTH (values); i++) |
659 CFRelease (values[1]); |
691 CFRelease (values[i]); |
660 |
692 |
661 CFArrayAppendValue (features_array, dict); |
693 CFArrayAppendValue (features_array, dict); |
662 CFRelease (dict); |
694 CFRelease (dict); |
663 |
695 |
664 } |
696 } |
672 CFRelease (features_array); |
704 CFRelease (features_array); |
673 |
705 |
674 CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); |
706 CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); |
675 CFRelease (attributes); |
707 CFRelease (attributes); |
676 |
708 |
677 range->font = CTFontCreateCopyWithAttributes (face_data->ct_font, 0.0, NULL, font_desc); |
709 range->font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, font_desc); |
678 CFRelease (font_desc); |
710 CFRelease (font_desc); |
679 } |
711 } |
680 else |
712 else |
681 { |
713 { |
682 range->font = NULL; |
714 range->font = nullptr; |
683 } |
715 } |
684 |
716 |
685 range->index_first = last_index; |
717 range->index_first = last_index; |
686 range->index_last = event->index - 1; |
718 range->index_last = event->index - 1; |
687 |
719 |
750 log_clusters[chars_len++] = cluster; /* Surrogates. */ |
779 log_clusters[chars_len++] = cluster; /* Surrogates. */ |
751 } |
780 } |
752 |
781 |
753 #define FAIL(...) \ |
782 #define FAIL(...) \ |
754 HB_STMT_START { \ |
783 HB_STMT_START { \ |
755 DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \ |
784 DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \ |
756 ret = false; \ |
785 ret = false; \ |
757 goto fail; \ |
786 goto fail; \ |
758 } HB_STMT_END; |
787 } HB_STMT_END; |
759 |
788 |
760 bool ret = true; |
789 bool ret = true; |
761 CFStringRef string_ref = NULL; |
790 CFStringRef string_ref = nullptr; |
762 CTLineRef line = NULL; |
791 CTLineRef line = nullptr; |
763 |
792 |
764 if (0) |
793 if (0) |
765 { |
794 { |
766 resize_and_retry: |
795 resize_and_retry: |
767 DEBUG_MSG (CORETEXT, buffer, "Buffer resize"); |
796 DEBUG_MSG (CORETEXT, buffer, "Buffer resize"); |
769 * string_ref (via attr_string). We must release those before resizing buffer. */ |
798 * string_ref (via attr_string). We must release those before resizing buffer. */ |
770 assert (string_ref); |
799 assert (string_ref); |
771 assert (line); |
800 assert (line); |
772 CFRelease (string_ref); |
801 CFRelease (string_ref); |
773 CFRelease (line); |
802 CFRelease (line); |
774 string_ref = NULL; |
803 string_ref = nullptr; |
775 line = NULL; |
804 line = nullptr; |
776 |
805 |
777 /* Get previous start-of-scratch-area, that we use later for readjusting |
806 /* Get previous start-of-scratch-area, that we use later for readjusting |
778 * our existing scratch arrays. */ |
807 * our existing scratch arrays. */ |
779 unsigned int old_scratch_used; |
808 unsigned int old_scratch_used; |
780 hb_buffer_t::scratch_buffer_t *old_scratch; |
809 hb_buffer_t::scratch_buffer_t *old_scratch; |
791 log_clusters = reinterpret_cast<unsigned int *> (((char *) scratch + ((char *) log_clusters - (char *) old_scratch))); |
820 log_clusters = reinterpret_cast<unsigned int *> (((char *) scratch + ((char *) log_clusters - (char *) old_scratch))); |
792 scratch += old_scratch_used; |
821 scratch += old_scratch_used; |
793 scratch_size -= old_scratch_used; |
822 scratch_size -= old_scratch_used; |
794 } |
823 } |
795 { |
824 { |
796 string_ref = CFStringCreateWithCharactersNoCopy (NULL, |
825 string_ref = CFStringCreateWithCharactersNoCopy (nullptr, |
797 pchars, chars_len, |
826 pchars, chars_len, |
798 kCFAllocatorNull); |
827 kCFAllocatorNull); |
799 if (unlikely (!string_ref)) |
828 if (unlikely (!string_ref)) |
800 FAIL ("CFStringCreateWithCharactersNoCopy failed"); |
829 FAIL ("CFStringCreateWithCharactersNoCopy failed"); |
801 |
830 |
829 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), |
858 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), |
830 kCTLanguageAttributeName, lang); |
859 kCTLanguageAttributeName, lang); |
831 CFRelease (lang); |
860 CFRelease (lang); |
832 } |
861 } |
833 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), |
862 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), |
834 kCTFontAttributeName, face_data->ct_font); |
863 kCTFontAttributeName, ct_font); |
835 |
864 |
836 if (num_features) |
865 if (num_features && range_records.len) |
837 { |
866 { |
838 unsigned int start = 0; |
867 unsigned int start = 0; |
839 range_record_t *last_range = &range_records[0]; |
868 range_record_t *last_range = &range_records[0]; |
840 for (unsigned int k = 0; k < chars_len; k++) |
869 for (unsigned int k = 0; k < chars_len; k++) |
841 { |
870 { |
857 } |
886 } |
858 if (start != chars_len && last_range->font) |
887 if (start != chars_len && last_range->font) |
859 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start), |
888 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start), |
860 kCTFontAttributeName, last_range->font); |
889 kCTFontAttributeName, last_range->font); |
861 } |
890 } |
|
891 /* Enable/disable kern if requested. |
|
892 * |
|
893 * Note: once kern is disabled, reenabling it doesn't currently seem to work in CoreText. |
|
894 */ |
|
895 if (num_features) |
|
896 { |
|
897 unsigned int zeroint = 0; |
|
898 CFNumberRef zero = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &zeroint); |
|
899 for (unsigned int i = 0; i < num_features; i++) |
|
900 { |
|
901 const hb_feature_t &feature = features[i]; |
|
902 if (feature.tag == HB_TAG('k','e','r','n') && |
|
903 feature.start < chars_len && feature.start < feature.end) |
|
904 { |
|
905 CFRange feature_range = CFRangeMake (feature.start, |
|
906 MIN (feature.end, chars_len) - feature.start); |
|
907 if (feature.value) |
|
908 CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName); |
|
909 else |
|
910 CFAttributedStringSetAttribute (attr_string, feature_range, kCTKernAttributeName, zero); |
|
911 } |
|
912 } |
|
913 CFRelease (zero); |
|
914 } |
862 |
915 |
863 int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; |
916 int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; |
864 CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level); |
917 CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level); |
865 CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, |
918 CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, |
866 (const void **) &kCTTypesetterOptionForcedEmbeddingLevel, |
919 (const void **) &kCTTypesetterOptionForcedEmbeddingLevel, |
867 (const void **) &level_number, |
920 (const void **) &level_number, |
868 1, |
921 1, |
869 &kCFTypeDictionaryKeyCallBacks, |
922 &kCFTypeDictionaryKeyCallBacks, |
870 &kCFTypeDictionaryValueCallBacks); |
923 &kCFTypeDictionaryValueCallBacks); |
|
924 CFRelease (level_number); |
871 if (unlikely (!options)) |
925 if (unlikely (!options)) |
872 FAIL ("CFDictionaryCreate failed"); |
926 FAIL ("CFDictionaryCreate failed"); |
873 |
927 |
874 CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options); |
928 CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options); |
875 CFRelease (options); |
929 CFRelease (options); |
883 FAIL ("CTTypesetterCreateLine failed"); |
937 FAIL ("CTTypesetterCreateLine failed"); |
884 } |
938 } |
885 |
939 |
886 CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); |
940 CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); |
887 unsigned int num_runs = CFArrayGetCount (glyph_runs); |
941 unsigned int num_runs = CFArrayGetCount (glyph_runs); |
888 DEBUG_MSG (CORETEXT, NULL, "Num runs: %d", num_runs); |
942 DEBUG_MSG (CORETEXT, nullptr, "Num runs: %d", num_runs); |
889 |
943 |
890 buffer->len = 0; |
944 buffer->len = 0; |
891 uint32_t status_and = ~0, status_or = 0; |
945 uint32_t status_and = ~0, status_or = 0; |
892 double advances_so_far = 0; |
946 double advances_so_far = 0; |
893 /* For right-to-left runs, CoreText returns the glyphs positioned such that |
947 /* For right-to-left runs, CoreText returns the glyphs positioned such that |
909 CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex (glyph_runs, i)); |
963 CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex (glyph_runs, i)); |
910 CTRunStatus run_status = CTRunGetStatus (run); |
964 CTRunStatus run_status = CTRunGetStatus (run); |
911 status_or |= run_status; |
965 status_or |= run_status; |
912 status_and &= run_status; |
966 status_and &= run_status; |
913 DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); |
967 DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); |
914 double run_advance = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL); |
968 double run_advance = CTRunGetTypographicBounds (run, range_all, nullptr, nullptr, nullptr); |
915 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) |
969 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) |
916 run_advance = -run_advance; |
970 run_advance = -run_advance; |
917 DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance); |
971 DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance); |
918 |
972 |
919 /* CoreText does automatic font fallback (AKA "cascading") for characters |
973 /* CoreText does automatic font fallback (AKA "cascading") for characters |
922 * one and fill in the buffer with .notdef glyphs instead of random glyph |
976 * one and fill in the buffer with .notdef glyphs instead of random glyph |
923 * indices from a different font. |
977 * indices from a different font. |
924 */ |
978 */ |
925 CFDictionaryRef attributes = CTRunGetAttributes (run); |
979 CFDictionaryRef attributes = CTRunGetAttributes (run); |
926 CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName)); |
980 CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName)); |
927 if (!CFEqual (run_ct_font, face_data->ct_font)) |
981 if (!CFEqual (run_ct_font, ct_font)) |
928 { |
982 { |
929 /* The run doesn't use our main font instance. We have to figure out |
983 /* The run doesn't use our main font instance. We have to figure out |
930 * whether font fallback happened, or this is just CoreText giving us |
984 * whether font fallback happened, or this is just CoreText giving us |
931 * another CTFont using the same underlying CGFont. CoreText seems |
985 * another CTFont using the same underlying CGFont. CoreText seems |
932 * to do that in a variety of situations, one of which being vertical |
986 * to do that in a variety of situations, one of which being vertical |
960 if (!matched) |
1014 if (!matched) |
961 { |
1015 { |
962 CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0); |
1016 CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0); |
963 if (run_cg_font) |
1017 if (run_cg_font) |
964 { |
1018 { |
965 matched = CFEqual (run_cg_font, face_data->cg_font); |
1019 matched = CFEqual (run_cg_font, cg_font); |
966 CFRelease (run_cg_font); |
1020 CFRelease (run_cg_font); |
967 } |
1021 } |
968 } |
1022 } |
969 if (!matched) |
1023 if (!matched) |
970 { |
1024 { |
971 CFStringRef font_ps_name = CTFontCopyName (face_data->ct_font, kCTFontPostScriptNameKey); |
1025 CFStringRef font_ps_name = CTFontCopyName (ct_font, kCTFontPostScriptNameKey); |
972 CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey); |
1026 CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey); |
973 CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0); |
1027 CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0); |
974 CFRelease (run_ps_name); |
1028 CFRelease (run_ps_name); |
975 CFRelease (font_ps_name); |
1029 CFRelease (font_ps_name); |
976 if (result == kCFCompareEqualTo) |
1030 if (result == kCFCompareEqualTo) |
1035 |
1089 |
1036 hb_glyph_info_t *run_info = buffer->info + buffer->len; |
1090 hb_glyph_info_t *run_info = buffer->info + buffer->len; |
1037 |
1091 |
1038 /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always |
1092 /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always |
1039 * succeed, and so copying data to our own buffer will be rare. Reports |
1093 * succeed, and so copying data to our own buffer will be rare. Reports |
1040 * have it that this changed in OS X 10.10 Yosemite, and NULL is returned |
1094 * have it that this changed in OS X 10.10 Yosemite, and nullptr is returned |
1041 * frequently. At any rate, we can test that codepath by setting USE_PTR |
1095 * frequently. At any rate, we can test that codepath by setting USE_PTR |
1042 * to false. */ |
1096 * to false. */ |
1043 |
1097 |
1044 #define USE_PTR true |
1098 #define USE_PTR true |
1045 |
1099 |
1051 scratch_size = scratch_size_saved; \ |
1105 scratch_size = scratch_size_saved; \ |
1052 scratch = scratch_saved; |
1106 scratch = scratch_saved; |
1053 |
1107 |
1054 { /* Setup glyphs */ |
1108 { /* Setup glyphs */ |
1055 SCRATCH_SAVE(); |
1109 SCRATCH_SAVE(); |
1056 const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : NULL; |
1110 const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : nullptr; |
1057 if (!glyphs) { |
1111 if (!glyphs) { |
1058 ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry); |
1112 ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry); |
1059 CTRunGetGlyphs (run, range_all, glyph_buf); |
1113 CTRunGetGlyphs (run, range_all, glyph_buf); |
1060 glyphs = glyph_buf; |
1114 glyphs = glyph_buf; |
1061 } |
1115 } |
1062 const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : NULL; |
1116 const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : nullptr; |
1063 if (!string_indices) { |
1117 if (!string_indices) { |
1064 ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry); |
1118 ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry); |
1065 CTRunGetStringIndices (run, range_all, index_buf); |
1119 CTRunGetStringIndices (run, range_all, index_buf); |
1066 string_indices = index_buf; |
1120 string_indices = index_buf; |
1067 } |
1121 } |
1079 * Note that CoreText does not return advances for glyphs. As such, |
1133 * Note that CoreText does not return advances for glyphs. As such, |
1080 * for all but last glyph, we use the delta position to next glyph as |
1134 * for all but last glyph, we use the delta position to next glyph as |
1081 * advance (in the advance direction only), and for last glyph we set |
1135 * advance (in the advance direction only), and for last glyph we set |
1082 * whatever is needed to make the whole run's advance add up. */ |
1136 * whatever is needed to make the whole run's advance add up. */ |
1083 SCRATCH_SAVE(); |
1137 SCRATCH_SAVE(); |
1084 const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : NULL; |
1138 const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : nullptr; |
1085 if (!positions) { |
1139 if (!positions) { |
1086 ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry); |
1140 ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry); |
1087 CTRunGetPositions (run, range_all, position_buf); |
1141 CTRunGetPositions (run, range_all, position_buf); |
1088 positions = position_buf; |
1142 positions = position_buf; |
1089 } |
1143 } |
1155 for (unsigned int i = 0; i < count; i++) |
1209 for (unsigned int i = 0; i < count; i++) |
1156 { |
1210 { |
1157 pos->x_advance = info->mask; |
1211 pos->x_advance = info->mask; |
1158 pos->x_offset = info->var1.i32; |
1212 pos->x_offset = info->var1.i32; |
1159 pos->y_offset = info->var2.i32; |
1213 pos->y_offset = info->var2.i32; |
|
1214 |
|
1215 info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK; |
|
1216 |
1160 info++, pos++; |
1217 info++, pos++; |
1161 } |
1218 } |
1162 else |
1219 else |
1163 for (unsigned int i = 0; i < count; i++) |
1220 for (unsigned int i = 0; i < count; i++) |
1164 { |
1221 { |
1165 pos->y_advance = info->mask; |
1222 pos->y_advance = info->mask; |
1166 pos->x_offset = info->var1.i32; |
1223 pos->x_offset = info->var1.i32; |
1167 pos->y_offset = info->var2.i32; |
1224 pos->y_offset = info->var2.i32; |
|
1225 |
|
1226 info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK; |
|
1227 |
1168 info++, pos++; |
1228 info++, pos++; |
1169 } |
1229 } |
1170 |
1230 |
1171 /* Fix up clusters so that we never return out-of-order indices; |
1231 /* Fix up clusters so that we never return out-of-order indices; |
1172 * if core text has reordered glyphs, we'll merge them to the |
1232 * if core text has reordered glyphs, we'll merge them to the |
1220 |
1282 |
1221 /* |
1283 /* |
1222 * AAT shaper |
1284 * AAT shaper |
1223 */ |
1285 */ |
1224 |
1286 |
|
1287 HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, face) |
|
1288 HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, font) |
|
1289 |
1225 /* |
1290 /* |
1226 * shaper face data |
1291 * shaper face data |
1227 */ |
1292 */ |
1228 |
1293 |
1229 struct hb_coretext_aat_shaper_face_data_t {}; |
1294 struct hb_coretext_aat_shaper_face_data_t {}; |
1230 |
1295 |
1231 hb_coretext_aat_shaper_face_data_t * |
1296 hb_coretext_aat_shaper_face_data_t * |
1232 _hb_coretext_aat_shaper_face_data_create (hb_face_t *face) |
1297 _hb_coretext_aat_shaper_face_data_create (hb_face_t *face) |
1233 { |
1298 { |
1234 hb_blob_t *mort_blob = face->reference_table (HB_CORETEXT_TAG_MORT); |
1299 static const hb_tag_t tags[] = {HB_CORETEXT_TAG_MORX, HB_CORETEXT_TAG_MORT, HB_CORETEXT_TAG_KERX}; |
1235 /* Umm, we just reference the table to check whether it exists. |
1300 |
1236 * Maybe add better API for this? */ |
1301 for (unsigned int i = 0; i < ARRAY_LENGTH (tags); i++) |
1237 if (!hb_blob_get_length (mort_blob)) |
|
1238 { |
1302 { |
1239 hb_blob_destroy (mort_blob); |
1303 hb_blob_t *blob = face->reference_table (tags[i]); |
1240 mort_blob = face->reference_table (HB_CORETEXT_TAG_MORX); |
1304 if (hb_blob_get_length (blob)) |
1241 if (!hb_blob_get_length (mort_blob)) |
|
1242 { |
1305 { |
1243 hb_blob_destroy (mort_blob); |
1306 hb_blob_destroy (blob); |
1244 return NULL; |
1307 return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; |
1245 } |
1308 } |
1246 } |
1309 hb_blob_destroy (blob); |
1247 hb_blob_destroy (mort_blob); |
1310 } |
1248 |
1311 |
1249 return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL; |
1312 return nullptr; |
1250 } |
1313 } |
1251 |
1314 |
1252 void |
1315 void |
1253 _hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t *data HB_UNUSED) |
1316 _hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t *data HB_UNUSED) |
1254 { |
1317 { |