blob: f43abb9f5f4aca11fb01e98489dbede794868e92 [file] [log] [blame]
Jonathan Kewaa6d8492012-07-24 15:52:32 -04001/*
Behdad Esfahbod36136962013-08-12 00:33:28 -04002 * Copyright © 2012,2013 Mozilla Foundation.
3 * Copyright © 2012,2013 Google, Inc.
Jonathan Kewaa6d8492012-07-24 15:52:32 -04004 *
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
Behdad Esfahbod301168d2012-07-30 17:48:04 -040026 * Google Author(s): Behdad Esfahbod
Jonathan Kewaa6d8492012-07-24 15:52:32 -040027 */
28
Behdad Esfahbod027857d2012-07-26 17:34:25 -040029#define HB_SHAPER coretext
Behdad Esfahbod301168d2012-07-30 17:48:04 -040030#include "hb-shaper-impl-private.hh"
Jonathan Kewaa6d8492012-07-24 15:52:32 -040031
Jonathan Kewaa6d8492012-07-24 15:52:32 -040032#include "hb-coretext.h"
33
Jonathan Kewaa6d8492012-07-24 15:52:32 -040034
35#ifndef HB_DEBUG_CORETEXT
36#define HB_DEBUG_CORETEXT (HB_DEBUG+0)
37#endif
38
Behdad Esfahbod296d0132017-10-11 14:09:30 +020039#define HB_CORETEXT_FONT_SIZE 12.0
Jonathan Kewaa6d8492012-07-24 15:52:32 -040040
Behdad Esfahboda9e25e92014-03-14 19:55:46 -070041static void
42release_table_data (void *user_data)
43{
44 CFDataRef cf_data = reinterpret_cast<CFDataRef> (user_data);
45 CFRelease(cf_data);
46}
47
48static hb_blob_t *
49reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
50{
51 CGFontRef cg_font = reinterpret_cast<CGFontRef> (user_data);
52 CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag);
53 if (unlikely (!cf_data))
54 return NULL;
55
56 const char *data = reinterpret_cast<const char*> (CFDataGetBytePtr (cf_data));
57 const size_t length = CFDataGetLength (cf_data);
58 if (!data || !length)
59 return NULL;
60
61 return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY,
62 reinterpret_cast<void *> (const_cast<__CFData *> (cf_data)),
63 release_table_data);
64}
65
66hb_face_t *
67hb_coretext_face_create (CGFontRef cg_font)
68{
69 return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), (hb_destroy_func_t) CGFontRelease);
70}
71
72
Behdad Esfahbod466b3e52017-02-03 16:43:25 -080073HB_SHAPER_DATA_ENSURE_DEFINE(coretext, face)
Dominik Röttschesa5ebe1d2017-10-11 13:32:38 +020074HB_SHAPER_DATA_ENSURE_DEFINE_WITH_CONDITION(coretext, font,
75 abs (CTFontGetSize((CTFontRef) data) -
76 (font->ptem < 0 ? HB_CORETEXT_FONT_SIZE : font->ptem)) < 1)
Jonathan Kewaa6d8492012-07-24 15:52:32 -040077
Behdad Esfahbod301168d2012-07-30 17:48:04 -040078/*
79 * shaper face data
80 */
81
Behdad Esfahbod90194ef2016-02-22 15:42:53 +090082static CTFontDescriptorRef
83get_last_resort_font_desc (void)
84{
85 // TODO Handle allocation failures?
86 CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0);
87 CFArrayRef cascade_list = CFArrayCreate (kCFAllocatorDefault,
88 (const void **) &last_resort,
89 1,
90 &kCFTypeArrayCallBacks);
91 CFRelease (last_resort);
92 CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault,
93 (const void **) &kCTFontCascadeListAttribute,
94 (const void **) &cascade_list,
95 1,
96 &kCFTypeDictionaryKeyCallBacks,
97 &kCFTypeDictionaryValueCallBacks);
98 CFRelease (cascade_list);
99
100 CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes);
101 CFRelease (attributes);
102 return font_desc;
103}
104
Behdad Esfahbodba3d49d2016-02-22 15:50:12 +0900105static void
106release_data (void *info, const void *data, size_t size)
107{
108 assert (hb_blob_get_length ((hb_blob_t *) info) == size &&
109 hb_blob_get_data ((hb_blob_t *) info, NULL) == data);
110
111 hb_blob_destroy ((hb_blob_t *) info);
112}
113
114static CGFontRef
115create_cg_font (hb_face_t *face)
116{
117 CGFontRef cg_font = NULL;
118 if (face->destroy == (hb_destroy_func_t) CGFontRelease)
119 {
120 cg_font = CGFontRetain ((CGFontRef) face->user_data);
121 }
122 else
123 {
124 hb_blob_t *blob = hb_face_reference_blob (face);
125 unsigned int blob_length;
126 const char *blob_data = hb_blob_get_data (blob, &blob_length);
127 if (unlikely (!blob_length))
128 DEBUG_MSG (CORETEXT, face, "Face has empty blob");
129
130 CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data);
131 if (likely (provider))
132 {
133 cg_font = CGFontCreateWithDataProvider (provider);
134 if (unlikely (!cg_font))
135 DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed");
136 CGDataProviderRelease (provider);
137 }
138 }
139 return cg_font;
140}
141
Behdad Esfahbode5611222016-02-22 15:28:37 +0900142static CTFontRef
143create_ct_font (CGFontRef cg_font, CGFloat font_size)
144{
145 CTFontRef ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, NULL, NULL);
146 if (unlikely (!ct_font)) {
147 DEBUG_MSG (CORETEXT, cg_font, "Font CTFontCreateWithGraphicsFont() failed");
148 return NULL;
149 }
Behdad Esfahbod489acf62016-07-22 17:41:43 -0700150
151 /* crbug.com/576941 and crbug.com/625902 and the investigation in the latter
152 * bug indicate that the cascade list reconfiguration occasionally causes
153 * crashes in CoreText on OS X 10.9, thus let's skip this step on older
Dominik Röttschesb717cd72016-09-07 23:56:57 +0300154 * operating system versions. Except for the emoji font, where _not_
155 * reconfiguring the cascade list causes CoreText crashes. For details, see
156 * crbug.com/549610 */
Ebrahim Byagowifc4e6712016-09-09 23:28:28 +0430157 // 0x00070000 stands for "kCTVersionNumber10_10", see CoreText.h
158 if (&CTGetCoreTextVersion != NULL && CTGetCoreTextVersion() < 0x00070000) {
Dominik Röttschesb717cd72016-09-07 23:56:57 +0300159 CFStringRef fontName = CTFontCopyPostScriptName (ct_font);
160 bool isEmojiFont = CFStringCompare (fontName, CFSTR("AppleColorEmoji"), 0) == kCFCompareEqualTo;
161 CFRelease (fontName);
162 if (!isEmojiFont)
163 return ct_font;
164 }
Behdad Esfahbod489acf62016-07-22 17:41:43 -0700165
Dominik Röttschesa0223272016-06-16 14:19:39 +0200166 CFURLRef original_url = (CFURLRef)CTFontCopyAttribute(ct_font, kCTFontURLAttribute);
Behdad Esfahbode5611222016-02-22 15:28:37 +0900167
168 /* Create font copy with cascade list that has LastResort first; this speeds up CoreText
169 * font fallback which we don't need anyway. */
170 {
Behdad Esfahbod90194ef2016-02-22 15:42:53 +0900171 CTFontDescriptorRef last_resort_font_desc = get_last_resort_font_desc ();
172 CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, NULL, last_resort_font_desc);
173 CFRelease (last_resort_font_desc);
Behdad Esfahbode5611222016-02-22 15:28:37 +0900174 if (new_ct_font)
175 {
Behdad Esfahbodfc9de442016-06-30 09:46:52 -0700176 /* The CTFontCreateCopyWithAttributes call fails to stay on the same font
177 * when reconfiguring the cascade list and may switch to a different font
178 * when there are fonts that go by the same name, since the descriptor is
179 * just name and size.
180 *
181 * Avoid reconfiguring the cascade lists if the new font is outside the
182 * system locations that we cannot access from the sandboxed renderer
183 * process in Blink. This can be detected by the new file URL location
184 * that the newly found font points to. */
Ebrahim Byagowi87442122016-07-12 03:49:21 +0430185 CFURLRef new_url = (CFURLRef) CTFontCopyAttribute (new_ct_font, kCTFontURLAttribute);
186 // Keep reconfigured font if URL cannot be retrieved (seems to be the case
187 // on Mac OS 10.12 Sierra), speculative fix for crbug.com/625606
188 if (!original_url || !new_url || CFEqual (original_url, new_url)) {
Dominik Röttschesa0223272016-06-16 14:19:39 +0200189 CFRelease (ct_font);
190 ct_font = new_ct_font;
191 } else {
Ebrahim Byagowi87442122016-07-12 03:49:21 +0430192 CFRelease (new_ct_font);
Dominik Röttschesa0223272016-06-16 14:19:39 +0200193 DEBUG_MSG (CORETEXT, ct_font, "Discarding reconfigured CTFont, location changed.");
194 }
Ebrahim Byagowi87442122016-07-12 03:49:21 +0430195 if (new_url)
196 CFRelease (new_url);
Behdad Esfahbode5611222016-02-22 15:28:37 +0900197 }
198 else
199 DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed");
200 }
201
Ebrahim Byagowi87442122016-07-12 03:49:21 +0430202 if (original_url)
203 CFRelease (original_url);
Dominik Röttschesa0223272016-06-16 14:19:39 +0200204 return ct_font;
Behdad Esfahbode5611222016-02-22 15:28:37 +0900205}
206
Behdad Esfahbod301168d2012-07-30 17:48:04 -0400207hb_coretext_shaper_face_data_t *
208_hb_coretext_shaper_face_data_create (hb_face_t *face)
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400209{
Behdad Esfahbodf3341302017-10-11 13:17:46 +0200210 CGFontRef cg_font = create_cg_font (face);
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400211
Behdad Esfahboda8e466c2017-10-11 13:05:59 +0200212 if (unlikely (!cg_font))
Behdad Esfahboda9e25e92014-03-14 19:55:46 -0700213 {
Behdad Esfahbod15063b12016-02-22 15:56:29 +0900214 DEBUG_MSG (CORETEXT, face, "CGFont creation failed..");
Behdad Esfahbod15063b12016-02-22 15:56:29 +0900215 return NULL;
216 }
217
Behdad Esfahbodf3341302017-10-11 13:17:46 +0200218 return (hb_coretext_shaper_face_data_t *) cg_font;
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400219}
220
Behdad Esfahbod301168d2012-07-30 17:48:04 -0400221void
Behdad Esfahbodf3341302017-10-11 13:17:46 +0200222_hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data)
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400223{
Behdad Esfahbodf3341302017-10-11 13:17:46 +0200224 CFRelease ((CGFontRef) data);
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400225}
226
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430227/*
228 * Since: 0.9.10
229 */
Behdad Esfahbod9a839582012-12-09 18:47:36 -0500230CGFontRef
Behdad Esfahbode923e642012-12-09 19:39:40 -0500231hb_coretext_face_get_cg_font (hb_face_t *face)
Behdad Esfahbod9a839582012-12-09 18:47:36 -0500232{
233 if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL;
Behdad Esfahbodf3341302017-10-11 13:17:46 +0200234 return (CGFontRef) HB_SHAPER_DATA_GET (face);
Behdad Esfahbod9a839582012-12-09 18:47:36 -0500235}
236
Behdad Esfahbod301168d2012-07-30 17:48:04 -0400237
238/*
239 * shaper font data
240 */
241
Behdad Esfahbod301168d2012-07-30 17:48:04 -0400242hb_coretext_shaper_font_data_t *
Behdad Esfahboda8e466c2017-10-11 13:05:59 +0200243_hb_coretext_shaper_font_data_create (hb_font_t *font)
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400244{
Dominik Röttschesdb7a73c2017-10-11 13:24:39 +0200245 hb_face_t *face = font->face;
246 if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL;
Behdad Esfahbodf3341302017-10-11 13:17:46 +0200247 CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face);
Behdad Esfahboda8e466c2017-10-11 13:05:59 +0200248
249 float ptem = font->ptem < 0 ? HB_CORETEXT_FONT_SIZE : font->ptem;
250
Behdad Esfahbodf3341302017-10-11 13:17:46 +0200251 CTFontRef ct_font = create_ct_font (cg_font, ptem);
Behdad Esfahboda8e466c2017-10-11 13:05:59 +0200252
253 if (unlikely (!ct_font))
254 {
255 DEBUG_MSG (CORETEXT, font, "CGFont creation failed..");
256 return NULL;
257 }
258
Behdad Esfahbodf3341302017-10-11 13:17:46 +0200259 return (hb_coretext_shaper_font_data_t *) ct_font;
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400260}
261
Behdad Esfahbod301168d2012-07-30 17:48:04 -0400262void
Behdad Esfahbodf3341302017-10-11 13:17:46 +0200263_hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data)
Behdad Esfahbod301168d2012-07-30 17:48:04 -0400264{
Behdad Esfahbodf3341302017-10-11 13:17:46 +0200265 CFRelease ((CTFontRef) data);
Behdad Esfahbod301168d2012-07-30 17:48:04 -0400266}
267
268
269/*
270 * shaper shape_plan data
271 */
272
273struct hb_coretext_shaper_shape_plan_data_t {};
274
275hb_coretext_shaper_shape_plan_data_t *
Behdad Esfahbod45c13832012-08-14 09:33:18 -0400276_hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED,
277 const hb_feature_t *user_features HB_UNUSED,
Behdad Esfahbod72ada4f2016-09-10 03:57:24 -0700278 unsigned int num_user_features HB_UNUSED,
279 const int *coords HB_UNUSED,
280 unsigned int num_coords HB_UNUSED)
Behdad Esfahbod301168d2012-07-30 17:48:04 -0400281{
282 return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
283}
284
285void
Behdad Esfahbod45c13832012-08-14 09:33:18 -0400286_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data HB_UNUSED)
Behdad Esfahbod301168d2012-07-30 17:48:04 -0400287{
288}
289
Behdad Esfahbod9a839582012-12-09 18:47:36 -0500290CTFontRef
291hb_coretext_font_get_ct_font (hb_font_t *font)
292{
Behdad Esfahboda8e466c2017-10-11 13:05:59 +0200293 if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return NULL;
Dominik Röttschesdb7a73c2017-10-11 13:24:39 +0200294 return (CTFontRef)HB_SHAPER_DATA_GET (font);
Behdad Esfahbod9a839582012-12-09 18:47:36 -0500295}
296
Behdad Esfahbod301168d2012-07-30 17:48:04 -0400297
298/*
299 * shaper
300 */
301
Behdad Esfahbod36136962013-08-12 00:33:28 -0400302struct feature_record_t {
303 unsigned int feature;
304 unsigned int setting;
305};
306
307struct active_feature_t {
308 feature_record_t rec;
309 unsigned int order;
310
311 static int cmp (const active_feature_t *a, const active_feature_t *b) {
312 return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 :
313 a->order < b->order ? -1 : a->order > b->order ? 1 :
314 a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 :
315 0;
316 }
317 bool operator== (const active_feature_t *f) {
318 return cmp (this, f) == 0;
319 }
320};
321
322struct feature_event_t {
323 unsigned int index;
324 bool start;
325 active_feature_t feature;
326
327 static int cmp (const feature_event_t *a, const feature_event_t *b) {
328 return a->index < b->index ? -1 : a->index > b->index ? 1 :
329 a->start < b->start ? -1 : a->start > b->start ? 1 :
330 active_feature_t::cmp (&a->feature, &b->feature);
331 }
332};
333
334struct range_record_t {
335 CTFontRef font;
336 unsigned int index_first; /* == start */
337 unsigned int index_last; /* == end - 1 */
338};
339
340
341/* The following enum members are added in OS X 10.8. */
342#define kAltHalfWidthTextSelector 6
343#define kAltProportionalTextSelector 5
344#define kAlternateHorizKanaOffSelector 1
345#define kAlternateHorizKanaOnSelector 0
346#define kAlternateKanaType 34
347#define kAlternateVertKanaOffSelector 3
348#define kAlternateVertKanaOnSelector 2
349#define kCaseSensitiveLayoutOffSelector 1
350#define kCaseSensitiveLayoutOnSelector 0
351#define kCaseSensitiveLayoutType 33
352#define kCaseSensitiveSpacingOffSelector 3
353#define kCaseSensitiveSpacingOnSelector 2
354#define kContextualAlternatesOffSelector 1
355#define kContextualAlternatesOnSelector 0
356#define kContextualAlternatesType 36
357#define kContextualLigaturesOffSelector 19
358#define kContextualLigaturesOnSelector 18
359#define kContextualSwashAlternatesOffSelector 5
360#define kContextualSwashAlternatesOnSelector 4
361#define kDefaultLowerCaseSelector 0
362#define kDefaultUpperCaseSelector 0
363#define kHistoricalLigaturesOffSelector 21
364#define kHistoricalLigaturesOnSelector 20
365#define kHojoCharactersSelector 12
366#define kJIS2004CharactersSelector 11
367#define kLowerCasePetiteCapsSelector 2
368#define kLowerCaseSmallCapsSelector 1
369#define kLowerCaseType 37
370#define kMathematicalGreekOffSelector 11
371#define kMathematicalGreekOnSelector 10
372#define kNLCCharactersSelector 13
373#define kQuarterWidthTextSelector 4
374#define kScientificInferiorsSelector 4
375#define kStylisticAltEightOffSelector 17
376#define kStylisticAltEightOnSelector 16
377#define kStylisticAltEighteenOffSelector 37
378#define kStylisticAltEighteenOnSelector 36
379#define kStylisticAltElevenOffSelector 23
380#define kStylisticAltElevenOnSelector 22
381#define kStylisticAltFifteenOffSelector 31
382#define kStylisticAltFifteenOnSelector 30
383#define kStylisticAltFiveOffSelector 11
384#define kStylisticAltFiveOnSelector 10
385#define kStylisticAltFourOffSelector 9
386#define kStylisticAltFourOnSelector 8
387#define kStylisticAltFourteenOffSelector 29
388#define kStylisticAltFourteenOnSelector 28
389#define kStylisticAltNineOffSelector 19
390#define kStylisticAltNineOnSelector 18
391#define kStylisticAltNineteenOffSelector 39
392#define kStylisticAltNineteenOnSelector 38
393#define kStylisticAltOneOffSelector 3
394#define kStylisticAltOneOnSelector 2
395#define kStylisticAltSevenOffSelector 15
396#define kStylisticAltSevenOnSelector 14
397#define kStylisticAltSeventeenOffSelector 35
398#define kStylisticAltSeventeenOnSelector 34
399#define kStylisticAltSixOffSelector 13
400#define kStylisticAltSixOnSelector 12
401#define kStylisticAltSixteenOffSelector 33
402#define kStylisticAltSixteenOnSelector 32
403#define kStylisticAltTenOffSelector 21
404#define kStylisticAltTenOnSelector 20
405#define kStylisticAltThirteenOffSelector 27
406#define kStylisticAltThirteenOnSelector 26
407#define kStylisticAltThreeOffSelector 7
408#define kStylisticAltThreeOnSelector 6
409#define kStylisticAltTwelveOffSelector 25
410#define kStylisticAltTwelveOnSelector 24
411#define kStylisticAltTwentyOffSelector 41
412#define kStylisticAltTwentyOnSelector 40
413#define kStylisticAltTwoOffSelector 5
414#define kStylisticAltTwoOnSelector 4
415#define kStylisticAlternativesType 35
416#define kSwashAlternatesOffSelector 3
417#define kSwashAlternatesOnSelector 2
418#define kThirdWidthTextSelector 3
419#define kTraditionalNamesCharactersSelector 14
420#define kUpperCasePetiteCapsSelector 2
421#define kUpperCaseSmallCapsSelector 1
422#define kUpperCaseType 38
423
424/* Table data courtesy of Apple. */
Behdad Esfahbod522b1cc2014-08-14 13:29:30 -0400425static const struct feature_mapping_t {
Behdad Esfahbod36136962013-08-12 00:33:28 -0400426 FourCharCode otFeatureTag;
427 uint16_t aatFeatureType;
428 uint16_t selectorToEnable;
429 uint16_t selectorToDisable;
430} feature_mappings[] = {
431 { 'c2pc', kUpperCaseType, kUpperCasePetiteCapsSelector, kDefaultUpperCaseSelector },
432 { 'c2sc', kUpperCaseType, kUpperCaseSmallCapsSelector, kDefaultUpperCaseSelector },
433 { 'calt', kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector },
434 { 'case', kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector },
435 { 'clig', kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector },
436 { 'cpsp', kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector },
437 { 'cswh', kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector },
438 { 'dlig', kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector },
439 { 'expt', kCharacterShapeType, kExpertCharactersSelector, 16 },
440 { 'frac', kFractionsType, kDiagonalFractionsSelector, kNoFractionsSelector },
441 { 'fwid', kTextSpacingType, kMonospacedTextSelector, 7 },
442 { 'halt', kTextSpacingType, kAltHalfWidthTextSelector, 7 },
443 { 'hist', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector },
444 { 'hkna', kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, },
445 { 'hlig', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector },
446 { 'hngl', kTransliterationType, kHanjaToHangulSelector, kNoTransliterationSelector },
447 { 'hojo', kCharacterShapeType, kHojoCharactersSelector, 16 },
448 { 'hwid', kTextSpacingType, kHalfWidthTextSelector, 7 },
449 { 'ital', kItalicCJKRomanType, kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector },
450 { 'jp04', kCharacterShapeType, kJIS2004CharactersSelector, 16 },
451 { 'jp78', kCharacterShapeType, kJIS1978CharactersSelector, 16 },
452 { 'jp83', kCharacterShapeType, kJIS1983CharactersSelector, 16 },
453 { 'jp90', kCharacterShapeType, kJIS1990CharactersSelector, 16 },
454 { 'liga', kLigaturesType, kCommonLigaturesOnSelector, kCommonLigaturesOffSelector },
455 { 'lnum', kNumberCaseType, kUpperCaseNumbersSelector, 2 },
456 { 'mgrk', kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector },
457 { 'nlck', kCharacterShapeType, kNLCCharactersSelector, 16 },
458 { 'onum', kNumberCaseType, kLowerCaseNumbersSelector, 2 },
459 { 'ordn', kVerticalPositionType, kOrdinalsSelector, kNormalPositionSelector },
460 { 'palt', kTextSpacingType, kAltProportionalTextSelector, 7 },
461 { 'pcap', kLowerCaseType, kLowerCasePetiteCapsSelector, kDefaultLowerCaseSelector },
462 { 'pkna', kTextSpacingType, kProportionalTextSelector, 7 },
463 { 'pnum', kNumberSpacingType, kProportionalNumbersSelector, 4 },
464 { 'pwid', kTextSpacingType, kProportionalTextSelector, 7 },
465 { 'qwid', kTextSpacingType, kQuarterWidthTextSelector, 7 },
466 { 'ruby', kRubyKanaType, kRubyKanaOnSelector, kRubyKanaOffSelector },
467 { 'sinf', kVerticalPositionType, kScientificInferiorsSelector, kNormalPositionSelector },
468 { 'smcp', kLowerCaseType, kLowerCaseSmallCapsSelector, kDefaultLowerCaseSelector },
469 { 'smpl', kCharacterShapeType, kSimplifiedCharactersSelector, 16 },
470 { 'ss01', kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector },
471 { 'ss02', kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector },
472 { 'ss03', kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector },
473 { 'ss04', kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector },
474 { 'ss05', kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector },
475 { 'ss06', kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector },
476 { 'ss07', kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector },
477 { 'ss08', kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector },
478 { 'ss09', kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector },
479 { 'ss10', kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector },
480 { 'ss11', kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector },
481 { 'ss12', kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector },
482 { 'ss13', kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector },
483 { 'ss14', kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector },
484 { 'ss15', kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector },
485 { 'ss16', kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector },
486 { 'ss17', kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector },
487 { 'ss18', kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector },
488 { 'ss19', kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector },
489 { 'ss20', kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector },
490 { 'subs', kVerticalPositionType, kInferiorsSelector, kNormalPositionSelector },
491 { 'sups', kVerticalPositionType, kSuperiorsSelector, kNormalPositionSelector },
492 { 'swsh', kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector },
493 { 'titl', kStyleOptionsType, kTitlingCapsSelector, kNoStyleOptionsSelector },
494 { 'tnam', kCharacterShapeType, kTraditionalNamesCharactersSelector, 16 },
495 { 'tnum', kNumberSpacingType, kMonospacedNumbersSelector, 4 },
496 { 'trad', kCharacterShapeType, kTraditionalCharactersSelector, 16 },
497 { 'twid', kTextSpacingType, kThirdWidthTextSelector, 7 },
498 { 'unic', kLetterCaseType, 14, 15 },
499 { 'valt', kTextSpacingType, kAltProportionalTextSelector, 7 },
500 { 'vert', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector },
501 { 'vhal', kTextSpacingType, kAltHalfWidthTextSelector, 7 },
502 { 'vkna', kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector },
503 { 'vpal', kTextSpacingType, kAltProportionalTextSelector, 7 },
504 { 'vrt2', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector },
505 { 'zero', kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector },
506};
507
508static int
509_hb_feature_mapping_cmp (const void *key_, const void *entry_)
510{
511 unsigned int key = * (unsigned int *) key_;
512 const feature_mapping_t * entry = (const feature_mapping_t *) entry_;
513 return key < entry->otFeatureTag ? -1 :
514 key > entry->otFeatureTag ? 1 :
515 0;
516}
517
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400518hb_bool_t
Behdad Esfahbod301168d2012-07-30 17:48:04 -0400519_hb_coretext_shape (hb_shape_plan_t *shape_plan,
520 hb_font_t *font,
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400521 hb_buffer_t *buffer,
522 const hb_feature_t *features,
523 unsigned int num_features)
524{
Behdad Esfahbod301168d2012-07-30 17:48:04 -0400525 hb_face_t *face = font->face;
Behdad Esfahbodf3341302017-10-11 13:17:46 +0200526 CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face);
527 CTFontRef ct_font = (CTFontRef) HB_SHAPER_DATA_GET (font);
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400528
Behdad Esfahboda8e466c2017-10-11 13:05:59 +0200529 CGFloat ct_font_size = CTFontGetSize (ct_font);
Behdad Esfahbod061105e2016-02-22 14:59:39 +0900530 CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size;
531 CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size;
532
Behdad Esfahbod624a2992014-08-11 15:29:18 -0400533 /* Attach marks to their bases, to match the 'ot' shaper.
534 * Adapted from hb-ot-shape:hb_form_clusters().
535 * Note that this only makes us be closer to the 'ot' shaper,
536 * but by no means the same. For example, if there's
537 * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will
538 * continue pointing to B2 even though B2 was merged into B1's
539 * cluster... */
Behdad Esfahbod62c27112016-02-22 15:07:20 +0900540 if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
Behdad Esfahbod624a2992014-08-11 15:29:18 -0400541 {
542 hb_unicode_funcs_t *unicode = buffer->unicode;
543 unsigned int count = buffer->len;
544 hb_glyph_info_t *info = buffer->info;
545 for (unsigned int i = 1; i < count; i++)
546 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint)))
547 buffer->merge_clusters (i - 1, i + 1);
548 }
549
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400550 hb_auto_array_t<feature_record_t> feature_records;
551 hb_auto_array_t<range_record_t> range_records;
552
Behdad Esfahbod36136962013-08-12 00:33:28 -0400553 /*
554 * Set up features.
555 * (copied + modified from code from hb-uniscribe.cc)
556 */
Behdad Esfahbod36136962013-08-12 00:33:28 -0400557 if (num_features)
558 {
559 /* Sort features by start/end events. */
560 hb_auto_array_t<feature_event_t> feature_events;
561 for (unsigned int i = 0; i < num_features; i++)
562 {
563 const feature_mapping_t * mapping = (const feature_mapping_t *) bsearch (&features[i].tag,
564 feature_mappings,
565 ARRAY_LENGTH (feature_mappings),
566 sizeof (feature_mappings[0]),
567 _hb_feature_mapping_cmp);
568 if (!mapping)
569 continue;
570
571 active_feature_t feature;
572 feature.rec.feature = mapping->aatFeatureType;
573 feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable;
574 feature.order = i;
575
576 feature_event_t *event;
577
578 event = feature_events.push ();
579 if (unlikely (!event))
580 goto fail_features;
581 event->index = features[i].start;
582 event->start = true;
583 event->feature = feature;
584
585 event = feature_events.push ();
586 if (unlikely (!event))
587 goto fail_features;
588 event->index = features[i].end;
589 event->start = false;
590 event->feature = feature;
591 }
Behdad Esfahbodfb8cc862014-06-19 15:30:18 -0400592 feature_events.qsort ();
Behdad Esfahbod36136962013-08-12 00:33:28 -0400593 /* Add a strategic final event. */
594 {
595 active_feature_t feature;
596 feature.rec.feature = HB_TAG_NONE;
597 feature.rec.setting = 0;
598 feature.order = num_features + 1;
599
600 feature_event_t *event = feature_events.push ();
601 if (unlikely (!event))
602 goto fail_features;
603 event->index = 0; /* This value does magic. */
604 event->start = false;
605 event->feature = feature;
606 }
607
608 /* Scan events and save features for each range. */
609 hb_auto_array_t<active_feature_t> active_features;
610 unsigned int last_index = 0;
611 for (unsigned int i = 0; i < feature_events.len; i++)
612 {
613 feature_event_t *event = &feature_events[i];
614
615 if (event->index != last_index)
616 {
617 /* Save a snapshot of active features and the range. */
618 range_record_t *range = range_records.push ();
619 if (unlikely (!range))
620 goto fail_features;
621
Behdad Esfahbod36136962013-08-12 00:33:28 -0400622 if (active_features.len)
623 {
624 CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
625
626 /* TODO sort and resolve conflicting features? */
Behdad Esfahbodfb8cc862014-06-19 15:30:18 -0400627 /* active_features.qsort (); */
Behdad Esfahbod36136962013-08-12 00:33:28 -0400628 for (unsigned int j = 0; j < active_features.len; j++)
629 {
Cosimo Lupo9813be32017-07-14 17:11:46 +0100630 CFStringRef keys[] = {
Behdad Esfahbod36136962013-08-12 00:33:28 -0400631 kCTFontFeatureTypeIdentifierKey,
632 kCTFontFeatureSelectorIdentifierKey
633 };
Cosimo Lupo9813be32017-07-14 17:11:46 +0100634 CFNumberRef values[] = {
Behdad Esfahbod36136962013-08-12 00:33:28 -0400635 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature),
636 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting)
637 };
Cosimo Lupo9813be32017-07-14 17:11:46 +0100638 ASSERT_STATIC (ARRAY_LENGTH (keys) == ARRAY_LENGTH (values));
Behdad Esfahbod36136962013-08-12 00:33:28 -0400639 CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault,
640 (const void **) keys,
641 (const void **) values,
Cosimo Lupo9813be32017-07-14 17:11:46 +0100642 ARRAY_LENGTH (keys),
Behdad Esfahbod36136962013-08-12 00:33:28 -0400643 &kCFTypeDictionaryKeyCallBacks,
644 &kCFTypeDictionaryValueCallBacks);
Cosimo Lupo9813be32017-07-14 17:11:46 +0100645 for (unsigned int i = 0; i < ARRAY_LENGTH (values); i++)
646 CFRelease (values[i]);
Behdad Esfahbod36136962013-08-12 00:33:28 -0400647
648 CFArrayAppendValue (features_array, dict);
649 CFRelease (dict);
650
651 }
652
653 CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault,
654 (const void **) &kCTFontFeatureSettingsAttribute,
655 (const void **) &features_array,
656 1,
657 &kCFTypeDictionaryKeyCallBacks,
658 &kCFTypeDictionaryValueCallBacks);
659 CFRelease (features_array);
660
661 CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes);
662 CFRelease (attributes);
663
Behdad Esfahboda8e466c2017-10-11 13:05:59 +0200664 range->font = CTFontCreateCopyWithAttributes (ct_font, 0.0, NULL, font_desc);
Behdad Esfahbod36136962013-08-12 00:33:28 -0400665 CFRelease (font_desc);
666 }
667 else
668 {
669 range->font = NULL;
670 }
671
672 range->index_first = last_index;
673 range->index_last = event->index - 1;
674
675 last_index = event->index;
676 }
677
678 if (event->start) {
679 active_feature_t *feature = active_features.push ();
680 if (unlikely (!feature))
681 goto fail_features;
682 *feature = event->feature;
683 } else {
684 active_feature_t *feature = active_features.find (&event->feature);
685 if (feature)
686 active_features.remove (feature - active_features.array);
687 }
688 }
Behdad Esfahbod36136962013-08-12 00:33:28 -0400689 }
690 else
691 {
692 fail_features:
693 num_features = 0;
694 }
695
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400696 unsigned int scratch_size;
Behdad Esfahbod68c372e2013-11-13 14:44:01 -0500697 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
Behdad Esfahbod8fcadb92013-11-13 14:33:57 -0500698
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400699#define ALLOCATE_ARRAY(Type, name, len, on_no_room) \
Behdad Esfahbod8fcadb92013-11-13 14:33:57 -0500700 Type *name = (Type *) scratch; \
701 { \
702 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400703 if (unlikely (_consumed > scratch_size)) \
704 { \
705 on_no_room; \
706 assert (0); \
707 } \
Behdad Esfahbod8fcadb92013-11-13 14:33:57 -0500708 scratch += _consumed; \
709 scratch_size -= _consumed; \
710 }
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400711
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400712 ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/);
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400713 unsigned int chars_len = 0;
714 for (unsigned int i = 0; i < buffer->len; i++) {
715 hb_codepoint_t c = buffer->info[i].codepoint;
Behdad Esfahbod76271002014-07-11 14:54:42 -0400716 if (likely (c <= 0xFFFFu))
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400717 pchars[chars_len++] = c;
Behdad Esfahbod76271002014-07-11 14:54:42 -0400718 else if (unlikely (c > 0x10FFFFu))
719 pchars[chars_len++] = 0xFFFDu;
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400720 else {
Behdad Esfahbod76271002014-07-11 14:54:42 -0400721 pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10);
Behdad Esfahbod33317312016-08-08 17:24:04 -0700722 pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1));
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400723 }
724 }
725
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400726 ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/);
Behdad Esfahbod9b3c60c2014-08-11 13:25:43 -0400727 chars_len = 0;
728 for (unsigned int i = 0; i < buffer->len; i++)
729 {
730 hb_codepoint_t c = buffer->info[i].codepoint;
731 unsigned int cluster = buffer->info[i].cluster;
732 log_clusters[chars_len++] = cluster;
733 if (hb_in_range (c, 0x10000u, 0x10FFFFu))
734 log_clusters[chars_len++] = cluster; /* Surrogates. */
735 }
736
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400737#define FAIL(...) \
738 HB_STMT_START { \
739 DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
740 ret = false; \
741 goto fail; \
742 } HB_STMT_END;
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400743
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400744 bool ret = true;
745 CFStringRef string_ref = NULL;
746 CTLineRef line = NULL;
Behdad Esfahboda782a5e2013-08-07 21:08:54 -0400747
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400748 if (0)
Behdad Esfahboda782a5e2013-08-07 21:08:54 -0400749 {
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400750resize_and_retry:
Behdad Esfahbod1b550772014-08-11 20:45:12 -0400751 DEBUG_MSG (CORETEXT, buffer, "Buffer resize");
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400752 /* string_ref uses the scratch-buffer for backing store, and line references
753 * string_ref (via attr_string). We must release those before resizing buffer. */
754 assert (string_ref);
755 assert (line);
756 CFRelease (string_ref);
757 CFRelease (line);
758 string_ref = NULL;
759 line = NULL;
Behdad Esfahbod81b8d972014-08-12 15:49:47 -0400760
761 /* Get previous start-of-scratch-area, that we use later for readjusting
762 * our existing scratch arrays. */
763 unsigned int old_scratch_used;
764 hb_buffer_t::scratch_buffer_t *old_scratch;
765 old_scratch = buffer->get_scratch_buffer (&old_scratch_used);
766 old_scratch_used = scratch - old_scratch;
767
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400768 if (unlikely (!buffer->ensure (buffer->allocated * 2)))
Behdad Esfahbodb9993d82014-08-10 17:40:24 -0400769 FAIL ("Buffer resize failed");
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400770
Behdad Esfahbod81b8d972014-08-12 15:49:47 -0400771 /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the
772 * cleanest way to do without completely restructuring the rest of this shaper. */
Behdad Esfahbod68c372e2013-11-13 14:44:01 -0500773 scratch = buffer->get_scratch_buffer (&scratch_size);
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400774 pchars = reinterpret_cast<UniChar *> (((char *) scratch + ((char *) pchars - (char *) old_scratch)));
775 log_clusters = reinterpret_cast<unsigned int *> (((char *) scratch + ((char *) log_clusters - (char *) old_scratch)));
Behdad Esfahbod81b8d972014-08-12 15:49:47 -0400776 scratch += old_scratch_used;
777 scratch_size -= old_scratch_used;
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400778 }
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400779 {
780 string_ref = CFStringCreateWithCharactersNoCopy (NULL,
781 pchars, chars_len,
782 kCFAllocatorNull);
783 if (unlikely (!string_ref))
784 FAIL ("CFStringCreateWithCharactersNoCopy failed");
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400785
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400786 /* Create an attributed string, populate it, and create a line from it, then release attributed string. */
787 {
Behdad Esfahbodfd1a6aa2014-08-11 20:01:37 -0400788 CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (kCFAllocatorDefault,
789 chars_len);
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400790 if (unlikely (!attr_string))
791 FAIL ("CFAttributedStringCreateMutable failed");
792 CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref);
Behdad Esfahbod3eb6a4d2014-08-12 19:10:33 -0400793 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction))
794 {
795 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
796 kCTVerticalFormsAttributeName, kCFBooleanTrue);
797 }
Behdad Esfahbod20076cc2014-08-12 19:26:35 -0400798
Behdad Esfahbod1b3011c2014-08-12 19:17:19 -0400799 if (buffer->props.language)
800 {
Behdad Esfahbod20076cc2014-08-12 19:26:35 -0400801/* What's the iOS equivalent of this check?
802 * The symbols was introduced in iOS 7.0.
803 * At any rate, our fallback is safe and works fine. */
804#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
805# define kCTLanguageAttributeName CFSTR ("NSLanguage")
806#endif
Behdad Esfahbod1b3011c2014-08-12 19:17:19 -0400807 CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault,
808 hb_language_to_string (buffer->props.language),
809 kCFStringEncodingUTF8,
810 kCFAllocatorNull);
811 if (unlikely (!lang))
812 FAIL ("CFStringCreateWithCStringNoCopy failed");
813 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
814 kCTLanguageAttributeName, lang);
815 CFRelease (lang);
816 }
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -0400817 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
Behdad Esfahboda8e466c2017-10-11 13:05:59 +0200818 kCTFontAttributeName, ct_font);
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400819
Cosimo Lupo9813be32017-07-14 17:11:46 +0100820 if (num_features && range_records.len)
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400821 {
822 unsigned int start = 0;
823 range_record_t *last_range = &range_records[0];
824 for (unsigned int k = 0; k < chars_len; k++)
825 {
826 range_record_t *range = last_range;
827 while (log_clusters[k] < range->index_first)
828 range--;
829 while (log_clusters[k] > range->index_last)
830 range++;
831 if (range != last_range)
832 {
833 if (last_range->font)
834 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start),
835 kCTFontAttributeName, last_range->font);
836
837 start = k;
838 }
839
840 last_range = range;
841 }
842 if (start != chars_len && last_range->font)
843 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start),
844 kCTFontAttributeName, last_range->font);
845 }
Cosimo Lupo9813be32017-07-14 17:11:46 +0100846 /* Enable/disable kern if requested.
847 *
848 * Note: once kern is disabled, reenabling it doesn't currently seem to work in CoreText.
849 */
850 if (num_features)
851 {
852 unsigned int zeroint = 0;
853 CFNumberRef zero = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &zeroint);
854 for (unsigned int i = 0; i < num_features; i++)
855 {
856 const hb_feature_t &feature = features[i];
857 if (feature.tag == HB_TAG('k','e','r','n') &&
858 feature.start < chars_len && feature.start < feature.end)
859 {
860 CFRange feature_range = CFRangeMake (feature.start,
861 MIN (feature.end, chars_len) - feature.start);
862 if (feature.value)
863 CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName);
864 else
865 CFAttributedStringSetAttribute (attr_string, feature_range, kCTKernAttributeName, zero);
866 }
867 }
868 CFRelease (zero);
869 }
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400870
Behdad Esfahbod4acce772014-08-11 17:46:50 -0400871 int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1;
872 CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level);
873 CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault,
874 (const void **) &kCTTypesetterOptionForcedEmbeddingLevel,
875 (const void **) &level_number,
876 1,
877 &kCFTypeDictionaryKeyCallBacks,
878 &kCFTypeDictionaryValueCallBacks);
Cosimo Lupo9813be32017-07-14 17:11:46 +0100879 CFRelease (level_number);
Behdad Esfahbod4acce772014-08-11 17:46:50 -0400880 if (unlikely (!options))
881 FAIL ("CFDictionaryCreate failed");
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400882
Behdad Esfahbod4acce772014-08-11 17:46:50 -0400883 CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options);
884 CFRelease (options);
885 CFRelease (attr_string);
886 if (unlikely (!typesetter))
887 FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed");
888
889 line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0));
890 CFRelease (typesetter);
891 if (unlikely (!line))
892 FAIL ("CTTypesetterCreateLine failed");
893 }
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400894
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400895 CFArrayRef glyph_runs = CTLineGetGlyphRuns (line);
896 unsigned int num_runs = CFArrayGetCount (glyph_runs);
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -0400897 DEBUG_MSG (CORETEXT, NULL, "Num runs: %d", num_runs);
Jonathan Kewaa6d8492012-07-24 15:52:32 -0400898
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400899 buffer->len = 0;
Behdad Esfahbod10b11042014-08-11 20:02:45 -0400900 uint32_t status_and = ~0, status_or = 0;
Behdad Esfahbod6917a042015-01-28 10:43:32 -0800901 double advances_so_far = 0;
Behdad Esfahbod24f17af2015-04-21 19:21:32 -0700902 /* For right-to-left runs, CoreText returns the glyphs positioned such that
903 * any trailing whitespace is to the left of (0,0). Adjust coordinate system
904 * to fix for that. Test with any RTL string with trailing spaces.
Behdad Esfahbod39851ce2015-04-21 19:23:27 -0700905 * https://code.google.com/p/chromium/issues/detail?id=469028
Behdad Esfahbod24f17af2015-04-21 19:21:32 -0700906 */
907 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
908 {
909 advances_so_far -= CTLineGetTrailingWhitespaceWidth (line);
910 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction))
911 advances_so_far = -advances_so_far;
912 }
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400913
914 const CFRange range_all = CFRangeMake (0, 0);
915
916 for (unsigned int i = 0; i < num_runs; i++)
917 {
918 CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex (glyph_runs, i));
Behdad Esfahbod10b11042014-08-11 20:02:45 -0400919 CTRunStatus run_status = CTRunGetStatus (run);
920 status_or |= run_status;
921 status_and &= run_status;
922 DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status);
Behdad Esfahbod6917a042015-01-28 10:43:32 -0800923 double run_advance = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL);
924 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction))
925 run_advance = -run_advance;
926 DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance);
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400927
928 /* CoreText does automatic font fallback (AKA "cascading") for characters
929 * not supported by the requested font, and provides no way to turn it off,
Behdad Esfahbodfd0001d2014-08-12 10:32:41 -0400930 * so we must detect if the returned run uses a font other than the requested
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400931 * one and fill in the buffer with .notdef glyphs instead of random glyph
932 * indices from a different font.
933 */
934 CFDictionaryRef attributes = CTRunGetAttributes (run);
935 CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName));
Behdad Esfahboda8e466c2017-10-11 13:05:59 +0200936 if (!CFEqual (run_ct_font, ct_font))
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400937 {
Behdad Esfahbodfd0001d2014-08-12 10:32:41 -0400938 /* The run doesn't use our main font instance. We have to figure out
939 * whether font fallback happened, or this is just CoreText giving us
940 * another CTFont using the same underlying CGFont. CoreText seems
941 * to do that in a variety of situations, one of which being vertical
942 * text, but also perhaps for caching reasons.
943 *
944 * First, see if it uses any of our subfonts created to set font features...
945 *
946 * Next, compare the CGFont to the one we used to create our fonts.
947 * Even this doesn't work all the time.
948 *
949 * Finally, we compare PS names, which I don't think are unique...
950 *
951 * Looks like if we really want to be sure here we have to modify the
952 * font to change the name table, similar to what we do in the uniscribe
953 * backend.
954 *
955 * However, even that wouldn't work if we were passed in the CGFont to
Behdad Esfahbod59089622016-04-04 14:54:32 -0700956 * construct a hb_face to begin with.
Behdad Esfahbodfd0001d2014-08-12 10:32:41 -0400957 *
958 * See: http://github.com/behdad/harfbuzz/pull/36
Behdad Esfahbod59089622016-04-04 14:54:32 -0700959 *
960 * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098
Behdad Esfahbodfd0001d2014-08-12 10:32:41 -0400961 */
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400962 bool matched = false;
963 for (unsigned int i = 0; i < range_records.len; i++)
964 if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font))
965 {
966 matched = true;
967 break;
968 }
969 if (!matched)
970 {
Behdad Esfahbodfd0001d2014-08-12 10:32:41 -0400971 CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0);
972 if (run_cg_font)
973 {
Behdad Esfahboda8e466c2017-10-11 13:05:59 +0200974 matched = CFEqual (run_cg_font, cg_font);
Behdad Esfahbodfd0001d2014-08-12 10:32:41 -0400975 CFRelease (run_cg_font);
976 }
977 }
978 if (!matched)
979 {
Behdad Esfahboda8e466c2017-10-11 13:05:59 +0200980 CFStringRef font_ps_name = CTFontCopyName (ct_font, kCTFontPostScriptNameKey);
Behdad Esfahbodfd0001d2014-08-12 10:32:41 -0400981 CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey);
982 CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0);
983 CFRelease (run_ps_name);
984 CFRelease (font_ps_name);
985 if (result == kCFCompareEqualTo)
986 matched = true;
987 }
988 if (!matched)
989 {
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400990 CFRange range = CTRunGetStringRange (run);
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -0400991 DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld",
992 range.location, range.location + range.length);
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -0400993 if (!buffer->ensure_inplace (buffer->len + range.length))
994 goto resize_and_retry;
995 hb_glyph_info_t *info = buffer->info + buffer->len;
996
Behdad Esfahbodb0b38bb2015-01-21 19:19:33 -0800997 hb_codepoint_t notdef = 0;
998 hb_direction_t dir = buffer->props.direction;
999 hb_position_t x_advance, y_advance, x_offset, y_offset;
1000 hb_font_get_glyph_advance_for_direction (font, notdef, dir, &x_advance, &y_advance);
1001 hb_font_get_glyph_origin_for_direction (font, notdef, dir, &x_offset, &y_offset);
1002 hb_position_t advance = x_advance + y_advance;
1003 x_offset = -x_offset;
1004 y_offset = -y_offset;
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001005
Behdad Esfahbod08acfe02014-08-12 18:57:08 -04001006 unsigned int old_len = buffer->len;
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001007 for (CFIndex j = range.location; j < range.location + range.length; j++)
1008 {
1009 UniChar ch = CFStringGetCharacterAtIndex (string_ref, j);
1010 if (hb_in_range<UniChar> (ch, 0xDC00u, 0xDFFFu) && range.location < j)
1011 {
1012 ch = CFStringGetCharacterAtIndex (string_ref, j - 1);
1013 if (hb_in_range<UniChar> (ch, 0xD800u, 0xDBFFu))
1014 /* This is the second of a surrogate pair. Don't need .notdef
1015 * for this one. */
1016 continue;
1017 }
Behdad Esfahbod982d94e2015-01-28 10:51:33 -08001018 if (buffer->unicode->is_default_ignorable (ch))
1019 continue;
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001020
1021 info->codepoint = notdef;
Behdad Esfahbod3c41ccb2014-08-11 15:11:59 -04001022 info->cluster = log_clusters[j];
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001023
1024 info->mask = advance;
Behdad Esfahboded6962c2015-08-20 15:39:53 +01001025 info->var1.i32 = x_offset;
1026 info->var2.i32 = y_offset;
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001027
1028 info++;
1029 buffer->len++;
1030 }
Behdad Esfahbod08acfe02014-08-12 18:57:08 -04001031 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
1032 buffer->reverse_range (old_len, buffer->len);
Behdad Esfahbod6917a042015-01-28 10:43:32 -08001033 advances_so_far += run_advance;
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001034 continue;
1035 }
1036 }
1037
1038 unsigned int num_glyphs = CTRunGetGlyphCount (run);
1039 if (num_glyphs == 0)
1040 continue;
1041
Behdad Esfahbod81b8d972014-08-12 15:49:47 -04001042 if (!buffer->ensure_inplace (buffer->len + num_glyphs))
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001043 goto resize_and_retry;
1044
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001045 hb_glyph_info_t *run_info = buffer->info + buffer->len;
1046
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001047 /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always
1048 * succeed, and so copying data to our own buffer will be rare. Reports
1049 * have it that this changed in OS X 10.10 Yosemite, and NULL is returned
1050 * frequently. At any rate, we can test that codepath by setting USE_PTR
1051 * to false. */
Behdad Esfahbodc3e924f2014-08-12 14:25:11 -04001052
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001053#define USE_PTR true
Behdad Esfahbodc3e924f2014-08-12 14:25:11 -04001054
1055#define SCRATCH_SAVE() \
1056 unsigned int scratch_size_saved = scratch_size; \
1057 hb_buffer_t::scratch_buffer_t *scratch_saved = scratch
1058
1059#define SCRATCH_RESTORE() \
1060 scratch_size = scratch_size_saved; \
1061 scratch = scratch_saved;
1062
Behdad Esfahbod6917a042015-01-28 10:43:32 -08001063 { /* Setup glyphs */
Behdad Esfahbodc3e924f2014-08-12 14:25:11 -04001064 SCRATCH_SAVE();
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001065 const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : NULL;
1066 if (!glyphs) {
1067 ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry);
1068 CTRunGetGlyphs (run, range_all, glyph_buf);
1069 glyphs = glyph_buf;
1070 }
1071 const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : NULL;
1072 if (!string_indices) {
1073 ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry);
1074 CTRunGetStringIndices (run, range_all, index_buf);
1075 string_indices = index_buf;
1076 }
1077 hb_glyph_info_t *info = run_info;
1078 for (unsigned int j = 0; j < num_glyphs; j++)
1079 {
1080 info->codepoint = glyphs[j];
1081 info->cluster = log_clusters[string_indices[j]];
1082 info++;
1083 }
Behdad Esfahbodc3e924f2014-08-12 14:25:11 -04001084 SCRATCH_RESTORE();
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001085 }
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001086 {
Behdad Esfahbod6917a042015-01-28 10:43:32 -08001087 /* Setup positions.
1088 * Note that CoreText does not return advances for glyphs. As such,
1089 * for all but last glyph, we use the delta position to next glyph as
1090 * advance (in the advance direction only), and for last glyph we set
1091 * whatever is needed to make the whole run's advance add up. */
Behdad Esfahbodc3e924f2014-08-12 14:25:11 -04001092 SCRATCH_SAVE();
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001093 const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : NULL;
1094 if (!positions) {
1095 ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry);
1096 CTRunGetPositions (run, range_all, position_buf);
1097 positions = position_buf;
1098 }
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001099 hb_glyph_info_t *info = run_info;
1100 if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
1101 {
Behdad Esfahbod6917a042015-01-28 10:43:32 -08001102 hb_position_t x_offset = (positions[0].x - advances_so_far) * x_mult;
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001103 for (unsigned int j = 0; j < num_glyphs; j++)
1104 {
Behdad Esfahbod6917a042015-01-28 10:43:32 -08001105 double advance;
1106 if (likely (j + 1 < num_glyphs))
1107 advance = positions[j + 1].x - positions[j].x;
1108 else /* last glyph */
1109 advance = run_advance - (positions[j].x - positions[0].x);
Behdad Esfahbod70622e52015-01-21 18:50:57 -08001110 info->mask = advance * x_mult;
Behdad Esfahboded6962c2015-08-20 15:39:53 +01001111 info->var1.i32 = x_offset;
1112 info->var2.i32 = positions[j].y * y_mult;
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001113 info++;
1114 }
1115 }
1116 else
1117 {
Behdad Esfahbod6917a042015-01-28 10:43:32 -08001118 hb_position_t y_offset = (positions[0].y - advances_so_far) * y_mult;
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001119 for (unsigned int j = 0; j < num_glyphs; j++)
1120 {
Behdad Esfahbod6917a042015-01-28 10:43:32 -08001121 double advance;
1122 if (likely (j + 1 < num_glyphs))
1123 advance = positions[j + 1].y - positions[j].y;
1124 else /* last glyph */
1125 advance = run_advance - (positions[j].y - positions[0].y);
Behdad Esfahbod70622e52015-01-21 18:50:57 -08001126 info->mask = advance * y_mult;
Behdad Esfahboded6962c2015-08-20 15:39:53 +01001127 info->var1.i32 = positions[j].x * x_mult;
1128 info->var2.i32 = y_offset;
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001129 info++;
1130 }
1131 }
Behdad Esfahbodc3e924f2014-08-12 14:25:11 -04001132 SCRATCH_RESTORE();
Behdad Esfahbod6917a042015-01-28 10:43:32 -08001133 advances_so_far += run_advance;
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001134 }
Behdad Esfahbodc3e924f2014-08-12 14:25:11 -04001135#undef SCRATCH_RESTORE
1136#undef SCRATCH_SAVE
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001137#undef USE_PTR
Jonathan Kewaa6d8492012-07-24 15:52:32 -04001138#undef ALLOCATE_ARRAY
1139
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001140 buffer->len += num_glyphs;
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001141 }
1142
Behdad Esfahbod50ad7782015-08-18 10:22:16 +01001143 /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel,
1144 * or if it does, it doesn't resepct it. So we get runs with wrong
1145 * directions. As such, disable the assert... It wouldn't crash, but
1146 * cursoring will be off...
1147 *
1148 * http://crbug.com/419769
1149 */
1150 if (0)
1151 {
1152 /* Make sure all runs had the expected direction. */
1153 bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
1154 assert (bool (status_and & kCTRunStatusRightToLeft) == backward);
1155 assert (bool (status_or & kCTRunStatusRightToLeft) == backward);
1156 }
Behdad Esfahbod10b11042014-08-11 20:02:45 -04001157
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001158 buffer->clear_positions ();
1159
1160 unsigned int count = buffer->len;
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001161 hb_glyph_info_t *info = buffer->info;
1162 hb_glyph_position_t *pos = buffer->pos;
1163 if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
1164 for (unsigned int i = 0; i < count; i++)
1165 {
1166 pos->x_advance = info->mask;
Behdad Esfahboded6962c2015-08-20 15:39:53 +01001167 pos->x_offset = info->var1.i32;
1168 pos->y_offset = info->var2.i32;
Behdad Esfahbod239119a2017-08-13 15:08:34 -07001169
1170 info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
1171
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001172 info++, pos++;
1173 }
1174 else
1175 for (unsigned int i = 0; i < count; i++)
1176 {
1177 pos->y_advance = info->mask;
Behdad Esfahboded6962c2015-08-20 15:39:53 +01001178 pos->x_offset = info->var1.i32;
1179 pos->y_offset = info->var2.i32;
Behdad Esfahbod239119a2017-08-13 15:08:34 -07001180
1181 info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
1182
Behdad Esfahbod5a0eed32014-08-11 23:47:16 -04001183 info++, pos++;
1184 }
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001185
1186 /* Fix up clusters so that we never return out-of-order indices;
1187 * if core text has reordered glyphs, we'll merge them to the
Behdad Esfahbod10b11042014-08-11 20:02:45 -04001188 * beginning of the reordered cluster. CoreText is nice enough
1189 * to tell us whenever it has produced nonmonotonic results...
1190 * Note that we assume the input clusters were nonmonotonic to
1191 * begin with.
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001192 *
1193 * This does *not* mean we'll form the same clusters as Uniscribe
1194 * or the native OT backend, only that the cluster indices will be
1195 * monotonic in the output buffer. */
Behdad Esfahbod10b11042014-08-11 20:02:45 -04001196 if (count > 1 && (status_or & kCTRunStatusNonMonotonic))
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001197 {
1198 hb_glyph_info_t *info = buffer->info;
1199 if (HB_DIRECTION_IS_FORWARD (buffer->props.direction))
1200 {
1201 unsigned int cluster = info[count - 1].cluster;
1202 for (unsigned int i = count - 1; i > 0; i--)
1203 {
1204 cluster = MIN (cluster, info[i - 1].cluster);
1205 info[i - 1].cluster = cluster;
1206 }
1207 }
1208 else
1209 {
1210 unsigned int cluster = info[0].cluster;
1211 for (unsigned int i = 1; i < count; i++)
1212 {
1213 cluster = MIN (cluster, info[i].cluster);
1214 info[i].cluster = cluster;
1215 }
1216 }
Jonathan Kewaa6d8492012-07-24 15:52:32 -04001217 }
1218 }
1219
Behdad Esfahbod4acce772014-08-11 17:46:50 -04001220#undef FAIL
1221
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001222fail:
1223 if (string_ref)
1224 CFRelease (string_ref);
1225 if (line)
1226 CFRelease (line);
1227
Behdad Esfahbod25f4fb92014-08-10 19:05:25 -04001228 for (unsigned int i = 0; i < range_records.len; i++)
1229 if (range_records[i].font)
1230 CFRelease (range_records[i].font);
1231
Behdad Esfahboda6b8dc82014-08-11 15:08:19 -04001232 return ret;
Jonathan Kewaa6d8492012-07-24 15:52:32 -04001233}
Behdad Esfahbodc79865f2014-03-14 19:37:55 -04001234
1235
1236/*
1237 * AAT shaper
1238 */
1239
Behdad Esfahbodd4bb52b2017-02-09 14:13:25 -08001240HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, face)
1241HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, font)
1242
Behdad Esfahbodc79865f2014-03-14 19:37:55 -04001243/*
1244 * shaper face data
1245 */
1246
1247struct hb_coretext_aat_shaper_face_data_t {};
1248
1249hb_coretext_aat_shaper_face_data_t *
1250_hb_coretext_aat_shaper_face_data_create (hb_face_t *face)
1251{
Behdad Esfahbod84686bf2017-10-11 15:02:48 +02001252 static const hb_tag_t tags[] = {HB_CORETEXT_TAG_MORX, HB_CORETEXT_TAG_MORT, HB_CORETEXT_TAG_KERX};
Behdad Esfahbodc79865f2014-03-14 19:37:55 -04001253
Behdad Esfahbod84686bf2017-10-11 15:02:48 +02001254 for (unsigned int i = 0; i < ARRAY_LENGTH (tags); i++)
1255 {
1256 hb_blob_t *blob = face->reference_table (tags[i]);
1257 if (hb_blob_get_length (blob))
1258 {
1259 hb_blob_destroy (blob);
1260 return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL;
1261 }
1262 hb_blob_destroy (blob);
1263 }
1264
1265 return NULL;
Behdad Esfahbodc79865f2014-03-14 19:37:55 -04001266}
1267
1268void
1269_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t *data HB_UNUSED)
1270{
1271}
1272
1273
1274/*
1275 * shaper font data
1276 */
1277
1278struct hb_coretext_aat_shaper_font_data_t {};
1279
1280hb_coretext_aat_shaper_font_data_t *
1281_hb_coretext_aat_shaper_font_data_create (hb_font_t *font)
1282{
1283 return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL;
1284}
1285
1286void
1287_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_shaper_font_data_t *data HB_UNUSED)
1288{
1289}
1290
1291
1292/*
1293 * shaper shape_plan data
1294 */
1295
1296struct hb_coretext_aat_shaper_shape_plan_data_t {};
1297
1298hb_coretext_aat_shaper_shape_plan_data_t *
1299_hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED,
1300 const hb_feature_t *user_features HB_UNUSED,
Behdad Esfahbod72ada4f2016-09-10 03:57:24 -07001301 unsigned int num_user_features HB_UNUSED,
1302 const int *coords HB_UNUSED,
1303 unsigned int num_coords HB_UNUSED)
Behdad Esfahbodc79865f2014-03-14 19:37:55 -04001304{
1305 return (hb_coretext_aat_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
1306}
1307
1308void
1309_hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shaper_shape_plan_data_t *data HB_UNUSED)
1310{
1311}
1312
1313
1314/*
1315 * shaper
1316 */
1317
1318hb_bool_t
1319_hb_coretext_aat_shape (hb_shape_plan_t *shape_plan,
1320 hb_font_t *font,
1321 hb_buffer_t *buffer,
1322 const hb_feature_t *features,
1323 unsigned int num_features)
1324{
1325 return _hb_coretext_shape (shape_plan, font, buffer, features, num_features);
1326}