1 /*
2 * Copyright © 2012,2013 Mozilla Foundation.
3 * Copyright © 2012,2013 Google, Inc.
4 *
5 * This is part of HarfBuzz, a text shaping library.
6 *
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
12 *
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 * DAMAGE.
18 *
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 *
25 * Mozilla Author(s): Jonathan Kew
26 * Google Author(s): Behdad Esfahbod
27 */
28
29 #define HB_SHAPER coretext
30 #include "hb-shaper-impl-private.hh"
31
32 #include "hb-coretext.h"
33
34
35 #ifndef HB_DEBUG_CORETEXT
36 #define HB_DEBUG_CORETEXT (HB_DEBUG+0)
37 #endif
38
39
40 static void
release_table_data(void * user_data)41 release_table_data (void *user_data)
42 {
43 CFDataRef cf_data = reinterpret_cast<CFDataRef> (user_data);
44 CFRelease(cf_data);
45 }
46
47 static hb_blob_t *
reference_table(hb_face_t * face HB_UNUSED,hb_tag_t tag,void * user_data)48 reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
49 {
50 CGFontRef cg_font = reinterpret_cast<CGFontRef> (user_data);
51 CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag);
52 if (unlikely (!cf_data))
53 return NULL;
54
55 const char *data = reinterpret_cast<const char*> (CFDataGetBytePtr (cf_data));
56 const size_t length = CFDataGetLength (cf_data);
57 if (!data || !length)
58 return NULL;
59
60 return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY,
61 reinterpret_cast<void *> (const_cast<__CFData *> (cf_data)),
62 release_table_data);
63 }
64
65 hb_face_t *
hb_coretext_face_create(CGFontRef cg_font)66 hb_coretext_face_create (CGFontRef cg_font)
67 {
68 return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), (hb_destroy_func_t) CGFontRelease);
69 }
70
71
HB_SHAPER_DATA_ENSURE_DECLARE(coretext,face)72 HB_SHAPER_DATA_ENSURE_DECLARE(coretext, face)
73 HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font)
74
75
76 /*
77 * shaper face data
78 */
79
80 static CTFontDescriptorRef
81 get_last_resort_font_desc (void)
82 {
83 // TODO Handle allocation failures?
84 CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0);
85 CFArrayRef cascade_list = CFArrayCreate (kCFAllocatorDefault,
86 (const void **) &last_resort,
87 1,
88 &kCFTypeArrayCallBacks);
89 CFRelease (last_resort);
90 CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault,
91 (const void **) &kCTFontCascadeListAttribute,
92 (const void **) &cascade_list,
93 1,
94 &kCFTypeDictionaryKeyCallBacks,
95 &kCFTypeDictionaryValueCallBacks);
96 CFRelease (cascade_list);
97
98 CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes);
99 CFRelease (attributes);
100 return font_desc;
101 }
102
103 static void
release_data(void * info,const void * data,size_t size)104 release_data (void *info, const void *data, size_t size)
105 {
106 assert (hb_blob_get_length ((hb_blob_t *) info) == size &&
107 hb_blob_get_data ((hb_blob_t *) info, NULL) == data);
108
109 hb_blob_destroy ((hb_blob_t *) info);
110 }
111
112 static CGFontRef
create_cg_font(hb_face_t * face)113 create_cg_font (hb_face_t *face)
114 {
115 CGFontRef cg_font = NULL;
116 if (face->destroy == (hb_destroy_func_t) CGFontRelease)
117 {
118 cg_font = CGFontRetain ((CGFontRef) face->user_data);
119 }
120 else
121 {
122 hb_blob_t *blob = hb_face_reference_blob (face);
123 unsigned int blob_length;
124 const char *blob_data = hb_blob_get_data (blob, &blob_length);
125 if (unlikely (!blob_length))
126 DEBUG_MSG (CORETEXT, face, "Face has empty blob");
127
128 CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data);
129 if (likely (provider))
130 {
131 cg_font = CGFontCreateWithDataProvider (provider);
132 if (unlikely (!cg_font))
133 DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed");
134 CGDataProviderRelease (provider);
135 }
136 }
137 return cg_font;
138 }
139
140 static CTFontRef
create_ct_font(CGFontRef cg_font,CGFloat font_size)141 create_ct_font (CGFontRef cg_font, CGFloat font_size)
142 {
143 CTFontRef ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, NULL, NULL);
144 if (unlikely (!ct_font)) {
145 DEBUG_MSG (CORETEXT, cg_font, "Font CTFontCreateWithGraphicsFont() failed");
146 return NULL;
147 }
148
149 /* Create font copy with cascade list that has LastResort first; this speeds up CoreText
150 * font fallback which we don't need anyway. */
151 {
152 CTFontDescriptorRef last_resort_font_desc = get_last_resort_font_desc ();
153 CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, NULL, last_resort_font_desc);
154 CFRelease (last_resort_font_desc);
155 if (new_ct_font)
156 {
157 CFRelease (ct_font);
158 ct_font = new_ct_font;
159 }
160 else
161 DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed");
162 }
163
164 return ct_font;
165 }
166
167 struct hb_coretext_shaper_face_data_t {
168 CGFontRef cg_font;
169 CTFontRef ct_font;
170 };
171
172 hb_coretext_shaper_face_data_t *
_hb_coretext_shaper_face_data_create(hb_face_t * face)173 _hb_coretext_shaper_face_data_create (hb_face_t *face)
174 {
175 hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) calloc (1, sizeof (hb_coretext_shaper_face_data_t));
176 if (unlikely (!data))
177 return NULL;
178
179 data->cg_font = create_cg_font (face);
180 if (unlikely (!data->cg_font))
181 {
182 DEBUG_MSG (CORETEXT, face, "CGFont creation failed..");
183 free (data);
184 return NULL;
185 }
186
187 /* We use 36pt size instead of UPEM, because CoreText implements the 'trak' table,
188 * which can make the font too tight at large sizes. 36pt should be a good semi-neutral
189 * size.
190 *
191 * Since we always create CTFont at a fixed size, our CTFont lives in face_data
192 * instead of font_data. Which is good, because when people change scale on
193 * hb_font_t, we won't need to update our CTFont. */
194 data->ct_font = create_ct_font (data->cg_font, 36.);
195 if (unlikely (!data->ct_font))
196 {
197 DEBUG_MSG (CORETEXT, face, "CTFont creation failed.");
198 CFRelease (data->cg_font);
199 free (data);
200 return NULL;
201 }
202
203 return data;
204 }
205
206 void
_hb_coretext_shaper_face_data_destroy(hb_coretext_shaper_face_data_t * data)207 _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data)
208 {
209 CFRelease (data->ct_font);
210 CFRelease (data->cg_font);
211 free (data);
212 }
213
214 /*
215 * Since: 0.9.10
216 */
217 CGFontRef
hb_coretext_face_get_cg_font(hb_face_t * face)218 hb_coretext_face_get_cg_font (hb_face_t *face)
219 {
220 if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL;
221 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
222 return face_data->cg_font;
223 }
224
225
226 /*
227 * shaper font data
228 */
229
230 struct hb_coretext_shaper_font_data_t {};
231
232 hb_coretext_shaper_font_data_t *
_hb_coretext_shaper_font_data_create(hb_font_t * font HB_UNUSED)233 _hb_coretext_shaper_font_data_create (hb_font_t *font HB_UNUSED)
234 {
235 return (hb_coretext_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
236 }
237
238 void
_hb_coretext_shaper_font_data_destroy(hb_coretext_shaper_font_data_t * data)239 _hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data)
240 {
241 }
242
243
244 /*
245 * shaper shape_plan data
246 */
247
248 struct hb_coretext_shaper_shape_plan_data_t {};
249
250 hb_coretext_shaper_shape_plan_data_t *
_hb_coretext_shaper_shape_plan_data_create(hb_shape_plan_t * shape_plan HB_UNUSED,const hb_feature_t * user_features HB_UNUSED,unsigned int num_user_features HB_UNUSED)251 _hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED,
252 const hb_feature_t *user_features HB_UNUSED,
253 unsigned int num_user_features HB_UNUSED)
254 {
255 return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
256 }
257
258 void
_hb_coretext_shaper_shape_plan_data_destroy(hb_coretext_shaper_shape_plan_data_t * data HB_UNUSED)259 _hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data HB_UNUSED)
260 {
261 }
262
263 CTFontRef
hb_coretext_font_get_ct_font(hb_font_t * font)264 hb_coretext_font_get_ct_font (hb_font_t *font)
265 {
266 hb_face_t *face = font->face;
267 if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL;
268 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
269 return face_data->ct_font;
270 }
271
272
273 /*
274 * shaper
275 */
276
277 struct feature_record_t {
278 unsigned int feature;
279 unsigned int setting;
280 };
281
282 struct active_feature_t {
283 feature_record_t rec;
284 unsigned int order;
285
cmpactive_feature_t286 static int cmp (const active_feature_t *a, const active_feature_t *b) {
287 return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 :
288 a->order < b->order ? -1 : a->order > b->order ? 1 :
289 a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 :
290 0;
291 }
operator ==active_feature_t292 bool operator== (const active_feature_t *f) {
293 return cmp (this, f) == 0;
294 }
295 };
296
297 struct feature_event_t {
298 unsigned int index;
299 bool start;
300 active_feature_t feature;
301
cmpfeature_event_t302 static int cmp (const feature_event_t *a, const feature_event_t *b) {
303 return a->index < b->index ? -1 : a->index > b->index ? 1 :
304 a->start < b->start ? -1 : a->start > b->start ? 1 :
305 active_feature_t::cmp (&a->feature, &b->feature);
306 }
307 };
308
309 struct range_record_t {
310 CTFontRef font;
311 unsigned int index_first; /* == start */
312 unsigned int index_last; /* == end - 1 */
313 };
314
315
316 /* The following enum members are added in OS X 10.8. */
317 #define kAltHalfWidthTextSelector 6
318 #define kAltProportionalTextSelector 5
319 #define kAlternateHorizKanaOffSelector 1
320 #define kAlternateHorizKanaOnSelector 0
321 #define kAlternateKanaType 34
322 #define kAlternateVertKanaOffSelector 3
323 #define kAlternateVertKanaOnSelector 2
324 #define kCaseSensitiveLayoutOffSelector 1
325 #define kCaseSensitiveLayoutOnSelector 0
326 #define kCaseSensitiveLayoutType 33
327 #define kCaseSensitiveSpacingOffSelector 3
328 #define kCaseSensitiveSpacingOnSelector 2
329 #define kContextualAlternatesOffSelector 1
330 #define kContextualAlternatesOnSelector 0
331 #define kContextualAlternatesType 36
332 #define kContextualLigaturesOffSelector 19
333 #define kContextualLigaturesOnSelector 18
334 #define kContextualSwashAlternatesOffSelector 5
335 #define kContextualSwashAlternatesOnSelector 4
336 #define kDefaultLowerCaseSelector 0
337 #define kDefaultUpperCaseSelector 0
338 #define kHistoricalLigaturesOffSelector 21
339 #define kHistoricalLigaturesOnSelector 20
340 #define kHojoCharactersSelector 12
341 #define kJIS2004CharactersSelector 11
342 #define kLowerCasePetiteCapsSelector 2
343 #define kLowerCaseSmallCapsSelector 1
344 #define kLowerCaseType 37
345 #define kMathematicalGreekOffSelector 11
346 #define kMathematicalGreekOnSelector 10
347 #define kNLCCharactersSelector 13
348 #define kQuarterWidthTextSelector 4
349 #define kScientificInferiorsSelector 4
350 #define kStylisticAltEightOffSelector 17
351 #define kStylisticAltEightOnSelector 16
352 #define kStylisticAltEighteenOffSelector 37
353 #define kStylisticAltEighteenOnSelector 36
354 #define kStylisticAltElevenOffSelector 23
355 #define kStylisticAltElevenOnSelector 22
356 #define kStylisticAltFifteenOffSelector 31
357 #define kStylisticAltFifteenOnSelector 30
358 #define kStylisticAltFiveOffSelector 11
359 #define kStylisticAltFiveOnSelector 10
360 #define kStylisticAltFourOffSelector 9
361 #define kStylisticAltFourOnSelector 8
362 #define kStylisticAltFourteenOffSelector 29
363 #define kStylisticAltFourteenOnSelector 28
364 #define kStylisticAltNineOffSelector 19
365 #define kStylisticAltNineOnSelector 18
366 #define kStylisticAltNineteenOffSelector 39
367 #define kStylisticAltNineteenOnSelector 38
368 #define kStylisticAltOneOffSelector 3
369 #define kStylisticAltOneOnSelector 2
370 #define kStylisticAltSevenOffSelector 15
371 #define kStylisticAltSevenOnSelector 14
372 #define kStylisticAltSeventeenOffSelector 35
373 #define kStylisticAltSeventeenOnSelector 34
374 #define kStylisticAltSixOffSelector 13
375 #define kStylisticAltSixOnSelector 12
376 #define kStylisticAltSixteenOffSelector 33
377 #define kStylisticAltSixteenOnSelector 32
378 #define kStylisticAltTenOffSelector 21
379 #define kStylisticAltTenOnSelector 20
380 #define kStylisticAltThirteenOffSelector 27
381 #define kStylisticAltThirteenOnSelector 26
382 #define kStylisticAltThreeOffSelector 7
383 #define kStylisticAltThreeOnSelector 6
384 #define kStylisticAltTwelveOffSelector 25
385 #define kStylisticAltTwelveOnSelector 24
386 #define kStylisticAltTwentyOffSelector 41
387 #define kStylisticAltTwentyOnSelector 40
388 #define kStylisticAltTwoOffSelector 5
389 #define kStylisticAltTwoOnSelector 4
390 #define kStylisticAlternativesType 35
391 #define kSwashAlternatesOffSelector 3
392 #define kSwashAlternatesOnSelector 2
393 #define kThirdWidthTextSelector 3
394 #define kTraditionalNamesCharactersSelector 14
395 #define kUpperCasePetiteCapsSelector 2
396 #define kUpperCaseSmallCapsSelector 1
397 #define kUpperCaseType 38
398
399 /* Table data courtesy of Apple. */
400 static const struct feature_mapping_t {
401 FourCharCode otFeatureTag;
402 uint16_t aatFeatureType;
403 uint16_t selectorToEnable;
404 uint16_t selectorToDisable;
405 } feature_mappings[] = {
406 { 'c2pc', kUpperCaseType, kUpperCasePetiteCapsSelector, kDefaultUpperCaseSelector },
407 { 'c2sc', kUpperCaseType, kUpperCaseSmallCapsSelector, kDefaultUpperCaseSelector },
408 { 'calt', kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector },
409 { 'case', kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector },
410 { 'clig', kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector },
411 { 'cpsp', kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector },
412 { 'cswh', kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector },
413 { 'dlig', kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector },
414 { 'expt', kCharacterShapeType, kExpertCharactersSelector, 16 },
415 { 'frac', kFractionsType, kDiagonalFractionsSelector, kNoFractionsSelector },
416 { 'fwid', kTextSpacingType, kMonospacedTextSelector, 7 },
417 { 'halt', kTextSpacingType, kAltHalfWidthTextSelector, 7 },
418 { 'hist', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector },
419 { 'hkna', kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, },
420 { 'hlig', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector },
421 { 'hngl', kTransliterationType, kHanjaToHangulSelector, kNoTransliterationSelector },
422 { 'hojo', kCharacterShapeType, kHojoCharactersSelector, 16 },
423 { 'hwid', kTextSpacingType, kHalfWidthTextSelector, 7 },
424 { 'ital', kItalicCJKRomanType, kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector },
425 { 'jp04', kCharacterShapeType, kJIS2004CharactersSelector, 16 },
426 { 'jp78', kCharacterShapeType, kJIS1978CharactersSelector, 16 },
427 { 'jp83', kCharacterShapeType, kJIS1983CharactersSelector, 16 },
428 { 'jp90', kCharacterShapeType, kJIS1990CharactersSelector, 16 },
429 { 'liga', kLigaturesType, kCommonLigaturesOnSelector, kCommonLigaturesOffSelector },
430 { 'lnum', kNumberCaseType, kUpperCaseNumbersSelector, 2 },
431 { 'mgrk', kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector },
432 { 'nlck', kCharacterShapeType, kNLCCharactersSelector, 16 },
433 { 'onum', kNumberCaseType, kLowerCaseNumbersSelector, 2 },
434 { 'ordn', kVerticalPositionType, kOrdinalsSelector, kNormalPositionSelector },
435 { 'palt', kTextSpacingType, kAltProportionalTextSelector, 7 },
436 { 'pcap', kLowerCaseType, kLowerCasePetiteCapsSelector, kDefaultLowerCaseSelector },
437 { 'pkna', kTextSpacingType, kProportionalTextSelector, 7 },
438 { 'pnum', kNumberSpacingType, kProportionalNumbersSelector, 4 },
439 { 'pwid', kTextSpacingType, kProportionalTextSelector, 7 },
440 { 'qwid', kTextSpacingType, kQuarterWidthTextSelector, 7 },
441 { 'ruby', kRubyKanaType, kRubyKanaOnSelector, kRubyKanaOffSelector },
442 { 'sinf', kVerticalPositionType, kScientificInferiorsSelector, kNormalPositionSelector },
443 { 'smcp', kLowerCaseType, kLowerCaseSmallCapsSelector, kDefaultLowerCaseSelector },
444 { 'smpl', kCharacterShapeType, kSimplifiedCharactersSelector, 16 },
445 { 'ss01', kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector },
446 { 'ss02', kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector },
447 { 'ss03', kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector },
448 { 'ss04', kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector },
449 { 'ss05', kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector },
450 { 'ss06', kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector },
451 { 'ss07', kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector },
452 { 'ss08', kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector },
453 { 'ss09', kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector },
454 { 'ss10', kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector },
455 { 'ss11', kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector },
456 { 'ss12', kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector },
457 { 'ss13', kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector },
458 { 'ss14', kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector },
459 { 'ss15', kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector },
460 { 'ss16', kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector },
461 { 'ss17', kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector },
462 { 'ss18', kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector },
463 { 'ss19', kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector },
464 { 'ss20', kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector },
465 { 'subs', kVerticalPositionType, kInferiorsSelector, kNormalPositionSelector },
466 { 'sups', kVerticalPositionType, kSuperiorsSelector, kNormalPositionSelector },
467 { 'swsh', kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector },
468 { 'titl', kStyleOptionsType, kTitlingCapsSelector, kNoStyleOptionsSelector },
469 { 'tnam', kCharacterShapeType, kTraditionalNamesCharactersSelector, 16 },
470 { 'tnum', kNumberSpacingType, kMonospacedNumbersSelector, 4 },
471 { 'trad', kCharacterShapeType, kTraditionalCharactersSelector, 16 },
472 { 'twid', kTextSpacingType, kThirdWidthTextSelector, 7 },
473 { 'unic', kLetterCaseType, 14, 15 },
474 { 'valt', kTextSpacingType, kAltProportionalTextSelector, 7 },
475 { 'vert', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector },
476 { 'vhal', kTextSpacingType, kAltHalfWidthTextSelector, 7 },
477 { 'vkna', kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector },
478 { 'vpal', kTextSpacingType, kAltProportionalTextSelector, 7 },
479 { 'vrt2', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector },
480 { 'zero', kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector },
481 };
482
483 static int
_hb_feature_mapping_cmp(const void * key_,const void * entry_)484 _hb_feature_mapping_cmp (const void *key_, const void *entry_)
485 {
486 unsigned int key = * (unsigned int *) key_;
487 const feature_mapping_t * entry = (const feature_mapping_t *) entry_;
488 return key < entry->otFeatureTag ? -1 :
489 key > entry->otFeatureTag ? 1 :
490 0;
491 }
492
493 hb_bool_t
_hb_coretext_shape(hb_shape_plan_t * shape_plan,hb_font_t * font,hb_buffer_t * buffer,const hb_feature_t * features,unsigned int num_features)494 _hb_coretext_shape (hb_shape_plan_t *shape_plan,
495 hb_font_t *font,
496 hb_buffer_t *buffer,
497 const hb_feature_t *features,
498 unsigned int num_features)
499 {
500 hb_face_t *face = font->face;
501 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
502
503 CGFloat ct_font_size = CTFontGetSize (face_data->ct_font);
504 CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size;
505 CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size;
506
507 /* Attach marks to their bases, to match the 'ot' shaper.
508 * Adapted from hb-ot-shape:hb_form_clusters().
509 * Note that this only makes us be closer to the 'ot' shaper,
510 * but by no means the same. For example, if there's
511 * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will
512 * continue pointing to B2 even though B2 was merged into B1's
513 * cluster... */
514 if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
515 {
516 hb_unicode_funcs_t *unicode = buffer->unicode;
517 unsigned int count = buffer->len;
518 hb_glyph_info_t *info = buffer->info;
519 for (unsigned int i = 1; i < count; i++)
520 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint)))
521 buffer->merge_clusters (i - 1, i + 1);
522 }
523
524 hb_auto_array_t<feature_record_t> feature_records;
525 hb_auto_array_t<range_record_t> range_records;
526
527 /*
528 * Set up features.
529 * (copied + modified from code from hb-uniscribe.cc)
530 */
531 if (num_features)
532 {
533 /* Sort features by start/end events. */
534 hb_auto_array_t<feature_event_t> feature_events;
535 for (unsigned int i = 0; i < num_features; i++)
536 {
537 const feature_mapping_t * mapping = (const feature_mapping_t *) bsearch (&features[i].tag,
538 feature_mappings,
539 ARRAY_LENGTH (feature_mappings),
540 sizeof (feature_mappings[0]),
541 _hb_feature_mapping_cmp);
542 if (!mapping)
543 continue;
544
545 active_feature_t feature;
546 feature.rec.feature = mapping->aatFeatureType;
547 feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable;
548 feature.order = i;
549
550 feature_event_t *event;
551
552 event = feature_events.push ();
553 if (unlikely (!event))
554 goto fail_features;
555 event->index = features[i].start;
556 event->start = true;
557 event->feature = feature;
558
559 event = feature_events.push ();
560 if (unlikely (!event))
561 goto fail_features;
562 event->index = features[i].end;
563 event->start = false;
564 event->feature = feature;
565 }
566 feature_events.qsort ();
567 /* Add a strategic final event. */
568 {
569 active_feature_t feature;
570 feature.rec.feature = HB_TAG_NONE;
571 feature.rec.setting = 0;
572 feature.order = num_features + 1;
573
574 feature_event_t *event = feature_events.push ();
575 if (unlikely (!event))
576 goto fail_features;
577 event->index = 0; /* This value does magic. */
578 event->start = false;
579 event->feature = feature;
580 }
581
582 /* Scan events and save features for each range. */
583 hb_auto_array_t<active_feature_t> active_features;
584 unsigned int last_index = 0;
585 for (unsigned int i = 0; i < feature_events.len; i++)
586 {
587 feature_event_t *event = &feature_events[i];
588
589 if (event->index != last_index)
590 {
591 /* Save a snapshot of active features and the range. */
592 range_record_t *range = range_records.push ();
593 if (unlikely (!range))
594 goto fail_features;
595
596 if (active_features.len)
597 {
598 CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
599
600 /* TODO sort and resolve conflicting features? */
601 /* active_features.qsort (); */
602 for (unsigned int j = 0; j < active_features.len; j++)
603 {
604 CFStringRef keys[2] = {
605 kCTFontFeatureTypeIdentifierKey,
606 kCTFontFeatureSelectorIdentifierKey
607 };
608 CFNumberRef values[2] = {
609 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature),
610 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting)
611 };
612 CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault,
613 (const void **) keys,
614 (const void **) values,
615 2,
616 &kCFTypeDictionaryKeyCallBacks,
617 &kCFTypeDictionaryValueCallBacks);
618 CFRelease (values[0]);
619 CFRelease (values[1]);
620
621 CFArrayAppendValue (features_array, dict);
622 CFRelease (dict);
623
624 }
625
626 CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault,
627 (const void **) &kCTFontFeatureSettingsAttribute,
628 (const void **) &features_array,
629 1,
630 &kCFTypeDictionaryKeyCallBacks,
631 &kCFTypeDictionaryValueCallBacks);
632 CFRelease (features_array);
633
634 CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes);
635 CFRelease (attributes);
636
637 range->font = CTFontCreateCopyWithAttributes (face_data->ct_font, 0.0, NULL, font_desc);
638 CFRelease (font_desc);
639 }
640 else
641 {
642 range->font = NULL;
643 }
644
645 range->index_first = last_index;
646 range->index_last = event->index - 1;
647
648 last_index = event->index;
649 }
650
651 if (event->start) {
652 active_feature_t *feature = active_features.push ();
653 if (unlikely (!feature))
654 goto fail_features;
655 *feature = event->feature;
656 } else {
657 active_feature_t *feature = active_features.find (&event->feature);
658 if (feature)
659 active_features.remove (feature - active_features.array);
660 }
661 }
662
663 if (!range_records.len) /* No active feature found. */
664 goto fail_features;
665 }
666 else
667 {
668 fail_features:
669 num_features = 0;
670 }
671
672 unsigned int scratch_size;
673 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
674
675 #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \
676 Type *name = (Type *) scratch; \
677 { \
678 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
679 if (unlikely (_consumed > scratch_size)) \
680 { \
681 on_no_room; \
682 assert (0); \
683 } \
684 scratch += _consumed; \
685 scratch_size -= _consumed; \
686 }
687
688 ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/);
689 unsigned int chars_len = 0;
690 for (unsigned int i = 0; i < buffer->len; i++) {
691 hb_codepoint_t c = buffer->info[i].codepoint;
692 if (likely (c <= 0xFFFFu))
693 pchars[chars_len++] = c;
694 else if (unlikely (c > 0x10FFFFu))
695 pchars[chars_len++] = 0xFFFDu;
696 else {
697 pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10);
698 pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1 << 10) - 1));
699 }
700 }
701
702 ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/);
703 chars_len = 0;
704 for (unsigned int i = 0; i < buffer->len; i++)
705 {
706 hb_codepoint_t c = buffer->info[i].codepoint;
707 unsigned int cluster = buffer->info[i].cluster;
708 log_clusters[chars_len++] = cluster;
709 if (hb_in_range (c, 0x10000u, 0x10FFFFu))
710 log_clusters[chars_len++] = cluster; /* Surrogates. */
711 }
712
713 #define FAIL(...) \
714 HB_STMT_START { \
715 DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
716 ret = false; \
717 goto fail; \
718 } HB_STMT_END;
719
720 bool ret = true;
721 CFStringRef string_ref = NULL;
722 CTLineRef line = NULL;
723
724 if (0)
725 {
726 resize_and_retry:
727 DEBUG_MSG (CORETEXT, buffer, "Buffer resize");
728 /* string_ref uses the scratch-buffer for backing store, and line references
729 * string_ref (via attr_string). We must release those before resizing buffer. */
730 assert (string_ref);
731 assert (line);
732 CFRelease (string_ref);
733 CFRelease (line);
734 string_ref = NULL;
735 line = NULL;
736
737 /* Get previous start-of-scratch-area, that we use later for readjusting
738 * our existing scratch arrays. */
739 unsigned int old_scratch_used;
740 hb_buffer_t::scratch_buffer_t *old_scratch;
741 old_scratch = buffer->get_scratch_buffer (&old_scratch_used);
742 old_scratch_used = scratch - old_scratch;
743
744 if (unlikely (!buffer->ensure (buffer->allocated * 2)))
745 FAIL ("Buffer resize failed");
746
747 /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the
748 * cleanest way to do without completely restructuring the rest of this shaper. */
749 scratch = buffer->get_scratch_buffer (&scratch_size);
750 pchars = reinterpret_cast<UniChar *> (((char *) scratch + ((char *) pchars - (char *) old_scratch)));
751 log_clusters = reinterpret_cast<unsigned int *> (((char *) scratch + ((char *) log_clusters - (char *) old_scratch)));
752 scratch += old_scratch_used;
753 scratch_size -= old_scratch_used;
754 }
755 {
756 string_ref = CFStringCreateWithCharactersNoCopy (NULL,
757 pchars, chars_len,
758 kCFAllocatorNull);
759 if (unlikely (!string_ref))
760 FAIL ("CFStringCreateWithCharactersNoCopy failed");
761
762 /* Create an attributed string, populate it, and create a line from it, then release attributed string. */
763 {
764 CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (kCFAllocatorDefault,
765 chars_len);
766 if (unlikely (!attr_string))
767 FAIL ("CFAttributedStringCreateMutable failed");
768 CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref);
769 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction))
770 {
771 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
772 kCTVerticalFormsAttributeName, kCFBooleanTrue);
773 }
774
775 if (buffer->props.language)
776 {
777 /* What's the iOS equivalent of this check?
778 * The symbols was introduced in iOS 7.0.
779 * At any rate, our fallback is safe and works fine. */
780 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
781 # define kCTLanguageAttributeName CFSTR ("NSLanguage")
782 #endif
783 CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault,
784 hb_language_to_string (buffer->props.language),
785 kCFStringEncodingUTF8,
786 kCFAllocatorNull);
787 if (unlikely (!lang))
788 FAIL ("CFStringCreateWithCStringNoCopy failed");
789 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
790 kCTLanguageAttributeName, lang);
791 CFRelease (lang);
792 }
793 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
794 kCTFontAttributeName, face_data->ct_font);
795
796 if (num_features)
797 {
798 unsigned int start = 0;
799 range_record_t *last_range = &range_records[0];
800 for (unsigned int k = 0; k < chars_len; k++)
801 {
802 range_record_t *range = last_range;
803 while (log_clusters[k] < range->index_first)
804 range--;
805 while (log_clusters[k] > range->index_last)
806 range++;
807 if (range != last_range)
808 {
809 if (last_range->font)
810 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start),
811 kCTFontAttributeName, last_range->font);
812
813 start = k;
814 }
815
816 last_range = range;
817 }
818 if (start != chars_len && last_range->font)
819 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start),
820 kCTFontAttributeName, last_range->font);
821 }
822
823 int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1;
824 CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level);
825 CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault,
826 (const void **) &kCTTypesetterOptionForcedEmbeddingLevel,
827 (const void **) &level_number,
828 1,
829 &kCFTypeDictionaryKeyCallBacks,
830 &kCFTypeDictionaryValueCallBacks);
831 if (unlikely (!options))
832 FAIL ("CFDictionaryCreate failed");
833
834 CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options);
835 CFRelease (options);
836 CFRelease (attr_string);
837 if (unlikely (!typesetter))
838 FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed");
839
840 line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0));
841 CFRelease (typesetter);
842 if (unlikely (!line))
843 FAIL ("CTTypesetterCreateLine failed");
844 }
845
846 CFArrayRef glyph_runs = CTLineGetGlyphRuns (line);
847 unsigned int num_runs = CFArrayGetCount (glyph_runs);
848 DEBUG_MSG (CORETEXT, NULL, "Num runs: %d", num_runs);
849
850 buffer->len = 0;
851 uint32_t status_and = ~0, status_or = 0;
852 double advances_so_far = 0;
853 /* For right-to-left runs, CoreText returns the glyphs positioned such that
854 * any trailing whitespace is to the left of (0,0). Adjust coordinate system
855 * to fix for that. Test with any RTL string with trailing spaces.
856 * https://code.google.com/p/chromium/issues/detail?id=469028
857 */
858 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
859 {
860 advances_so_far -= CTLineGetTrailingWhitespaceWidth (line);
861 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction))
862 advances_so_far = -advances_so_far;
863 }
864
865 const CFRange range_all = CFRangeMake (0, 0);
866
867 for (unsigned int i = 0; i < num_runs; i++)
868 {
869 CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex (glyph_runs, i));
870 CTRunStatus run_status = CTRunGetStatus (run);
871 status_or |= run_status;
872 status_and &= run_status;
873 DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status);
874 double run_advance = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL);
875 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction))
876 run_advance = -run_advance;
877 DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance);
878
879 /* CoreText does automatic font fallback (AKA "cascading") for characters
880 * not supported by the requested font, and provides no way to turn it off,
881 * so we must detect if the returned run uses a font other than the requested
882 * one and fill in the buffer with .notdef glyphs instead of random glyph
883 * indices from a different font.
884 */
885 CFDictionaryRef attributes = CTRunGetAttributes (run);
886 CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName));
887 if (!CFEqual (run_ct_font, face_data->ct_font))
888 {
889 /* The run doesn't use our main font instance. We have to figure out
890 * whether font fallback happened, or this is just CoreText giving us
891 * another CTFont using the same underlying CGFont. CoreText seems
892 * to do that in a variety of situations, one of which being vertical
893 * text, but also perhaps for caching reasons.
894 *
895 * First, see if it uses any of our subfonts created to set font features...
896 *
897 * Next, compare the CGFont to the one we used to create our fonts.
898 * Even this doesn't work all the time.
899 *
900 * Finally, we compare PS names, which I don't think are unique...
901 *
902 * Looks like if we really want to be sure here we have to modify the
903 * font to change the name table, similar to what we do in the uniscribe
904 * backend.
905 *
906 * However, even that wouldn't work if we were passed in the CGFont to
907 * construct a hb_face to begin with.
908 *
909 * See: http://github.com/behdad/harfbuzz/pull/36
910 *
911 * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098
912 */
913 bool matched = false;
914 for (unsigned int i = 0; i < range_records.len; i++)
915 if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font))
916 {
917 matched = true;
918 break;
919 }
920 if (!matched)
921 {
922 CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0);
923 if (run_cg_font)
924 {
925 matched = CFEqual (run_cg_font, face_data->cg_font);
926 CFRelease (run_cg_font);
927 }
928 }
929 if (!matched)
930 {
931 CFStringRef font_ps_name = CTFontCopyName (face_data->ct_font, kCTFontPostScriptNameKey);
932 CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey);
933 CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0);
934 CFRelease (run_ps_name);
935 CFRelease (font_ps_name);
936 if (result == kCFCompareEqualTo)
937 matched = true;
938 }
939 if (!matched)
940 {
941 CFRange range = CTRunGetStringRange (run);
942 DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld",
943 range.location, range.location + range.length);
944 if (!buffer->ensure_inplace (buffer->len + range.length))
945 goto resize_and_retry;
946 hb_glyph_info_t *info = buffer->info + buffer->len;
947
948 hb_codepoint_t notdef = 0;
949 hb_direction_t dir = buffer->props.direction;
950 hb_position_t x_advance, y_advance, x_offset, y_offset;
951 hb_font_get_glyph_advance_for_direction (font, notdef, dir, &x_advance, &y_advance);
952 hb_font_get_glyph_origin_for_direction (font, notdef, dir, &x_offset, &y_offset);
953 hb_position_t advance = x_advance + y_advance;
954 x_offset = -x_offset;
955 y_offset = -y_offset;
956
957 unsigned int old_len = buffer->len;
958 for (CFIndex j = range.location; j < range.location + range.length; j++)
959 {
960 UniChar ch = CFStringGetCharacterAtIndex (string_ref, j);
961 if (hb_in_range<UniChar> (ch, 0xDC00u, 0xDFFFu) && range.location < j)
962 {
963 ch = CFStringGetCharacterAtIndex (string_ref, j - 1);
964 if (hb_in_range<UniChar> (ch, 0xD800u, 0xDBFFu))
965 /* This is the second of a surrogate pair. Don't need .notdef
966 * for this one. */
967 continue;
968 }
969 if (buffer->unicode->is_default_ignorable (ch))
970 continue;
971
972 info->codepoint = notdef;
973 info->cluster = log_clusters[j];
974
975 info->mask = advance;
976 info->var1.i32 = x_offset;
977 info->var2.i32 = y_offset;
978
979 info++;
980 buffer->len++;
981 }
982 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
983 buffer->reverse_range (old_len, buffer->len);
984 advances_so_far += run_advance;
985 continue;
986 }
987 }
988
989 unsigned int num_glyphs = CTRunGetGlyphCount (run);
990 if (num_glyphs == 0)
991 continue;
992
993 if (!buffer->ensure_inplace (buffer->len + num_glyphs))
994 goto resize_and_retry;
995
996 hb_glyph_info_t *run_info = buffer->info + buffer->len;
997
998 /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always
999 * succeed, and so copying data to our own buffer will be rare. Reports
1000 * have it that this changed in OS X 10.10 Yosemite, and NULL is returned
1001 * frequently. At any rate, we can test that codepath by setting USE_PTR
1002 * to false. */
1003
1004 #define USE_PTR true
1005
1006 #define SCRATCH_SAVE() \
1007 unsigned int scratch_size_saved = scratch_size; \
1008 hb_buffer_t::scratch_buffer_t *scratch_saved = scratch
1009
1010 #define SCRATCH_RESTORE() \
1011 scratch_size = scratch_size_saved; \
1012 scratch = scratch_saved;
1013
1014 { /* Setup glyphs */
1015 SCRATCH_SAVE();
1016 const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : NULL;
1017 if (!glyphs) {
1018 ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry);
1019 CTRunGetGlyphs (run, range_all, glyph_buf);
1020 glyphs = glyph_buf;
1021 }
1022 const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : NULL;
1023 if (!string_indices) {
1024 ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry);
1025 CTRunGetStringIndices (run, range_all, index_buf);
1026 string_indices = index_buf;
1027 }
1028 hb_glyph_info_t *info = run_info;
1029 for (unsigned int j = 0; j < num_glyphs; j++)
1030 {
1031 info->codepoint = glyphs[j];
1032 info->cluster = log_clusters[string_indices[j]];
1033 info++;
1034 }
1035 SCRATCH_RESTORE();
1036 }
1037 {
1038 /* Setup positions.
1039 * Note that CoreText does not return advances for glyphs. As such,
1040 * for all but last glyph, we use the delta position to next glyph as
1041 * advance (in the advance direction only), and for last glyph we set
1042 * whatever is needed to make the whole run's advance add up. */
1043 SCRATCH_SAVE();
1044 const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : NULL;
1045 if (!positions) {
1046 ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry);
1047 CTRunGetPositions (run, range_all, position_buf);
1048 positions = position_buf;
1049 }
1050 hb_glyph_info_t *info = run_info;
1051 if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
1052 {
1053 hb_position_t x_offset = (positions[0].x - advances_so_far) * x_mult;
1054 for (unsigned int j = 0; j < num_glyphs; j++)
1055 {
1056 double advance;
1057 if (likely (j + 1 < num_glyphs))
1058 advance = positions[j + 1].x - positions[j].x;
1059 else /* last glyph */
1060 advance = run_advance - (positions[j].x - positions[0].x);
1061 info->mask = advance * x_mult;
1062 info->var1.i32 = x_offset;
1063 info->var2.i32 = positions[j].y * y_mult;
1064 info++;
1065 }
1066 }
1067 else
1068 {
1069 hb_position_t y_offset = (positions[0].y - advances_so_far) * y_mult;
1070 for (unsigned int j = 0; j < num_glyphs; j++)
1071 {
1072 double advance;
1073 if (likely (j + 1 < num_glyphs))
1074 advance = positions[j + 1].y - positions[j].y;
1075 else /* last glyph */
1076 advance = run_advance - (positions[j].y - positions[0].y);
1077 info->mask = advance * y_mult;
1078 info->var1.i32 = positions[j].x * x_mult;
1079 info->var2.i32 = y_offset;
1080 info++;
1081 }
1082 }
1083 SCRATCH_RESTORE();
1084 advances_so_far += run_advance;
1085 }
1086 #undef SCRATCH_RESTORE
1087 #undef SCRATCH_SAVE
1088 #undef USE_PTR
1089 #undef ALLOCATE_ARRAY
1090
1091 buffer->len += num_glyphs;
1092 }
1093
1094 /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel,
1095 * or if it does, it doesn't resepct it. So we get runs with wrong
1096 * directions. As such, disable the assert... It wouldn't crash, but
1097 * cursoring will be off...
1098 *
1099 * http://crbug.com/419769
1100 */
1101 if (0)
1102 {
1103 /* Make sure all runs had the expected direction. */
1104 bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
1105 assert (bool (status_and & kCTRunStatusRightToLeft) == backward);
1106 assert (bool (status_or & kCTRunStatusRightToLeft) == backward);
1107 }
1108
1109 buffer->clear_positions ();
1110
1111 unsigned int count = buffer->len;
1112 hb_glyph_info_t *info = buffer->info;
1113 hb_glyph_position_t *pos = buffer->pos;
1114 if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
1115 for (unsigned int i = 0; i < count; i++)
1116 {
1117 pos->x_advance = info->mask;
1118 pos->x_offset = info->var1.i32;
1119 pos->y_offset = info->var2.i32;
1120 info++, pos++;
1121 }
1122 else
1123 for (unsigned int i = 0; i < count; i++)
1124 {
1125 pos->y_advance = info->mask;
1126 pos->x_offset = info->var1.i32;
1127 pos->y_offset = info->var2.i32;
1128 info++, pos++;
1129 }
1130
1131 /* Fix up clusters so that we never return out-of-order indices;
1132 * if core text has reordered glyphs, we'll merge them to the
1133 * beginning of the reordered cluster. CoreText is nice enough
1134 * to tell us whenever it has produced nonmonotonic results...
1135 * Note that we assume the input clusters were nonmonotonic to
1136 * begin with.
1137 *
1138 * This does *not* mean we'll form the same clusters as Uniscribe
1139 * or the native OT backend, only that the cluster indices will be
1140 * monotonic in the output buffer. */
1141 if (count > 1 && (status_or & kCTRunStatusNonMonotonic))
1142 {
1143 hb_glyph_info_t *info = buffer->info;
1144 if (HB_DIRECTION_IS_FORWARD (buffer->props.direction))
1145 {
1146 unsigned int cluster = info[count - 1].cluster;
1147 for (unsigned int i = count - 1; i > 0; i--)
1148 {
1149 cluster = MIN (cluster, info[i - 1].cluster);
1150 info[i - 1].cluster = cluster;
1151 }
1152 }
1153 else
1154 {
1155 unsigned int cluster = info[0].cluster;
1156 for (unsigned int i = 1; i < count; i++)
1157 {
1158 cluster = MIN (cluster, info[i].cluster);
1159 info[i].cluster = cluster;
1160 }
1161 }
1162 }
1163 }
1164
1165 #undef FAIL
1166
1167 fail:
1168 if (string_ref)
1169 CFRelease (string_ref);
1170 if (line)
1171 CFRelease (line);
1172
1173 for (unsigned int i = 0; i < range_records.len; i++)
1174 if (range_records[i].font)
1175 CFRelease (range_records[i].font);
1176
1177 return ret;
1178 }
1179
1180
1181 /*
1182 * AAT shaper
1183 */
1184
1185 /*
1186 * shaper face data
1187 */
1188
1189 struct hb_coretext_aat_shaper_face_data_t {};
1190
1191 hb_coretext_aat_shaper_face_data_t *
_hb_coretext_aat_shaper_face_data_create(hb_face_t * face)1192 _hb_coretext_aat_shaper_face_data_create (hb_face_t *face)
1193 {
1194 hb_blob_t *mort_blob = face->reference_table (HB_CORETEXT_TAG_MORT);
1195 /* Umm, we just reference the table to check whether it exists.
1196 * Maybe add better API for this? */
1197 if (!hb_blob_get_length (mort_blob))
1198 {
1199 hb_blob_destroy (mort_blob);
1200 mort_blob = face->reference_table (HB_CORETEXT_TAG_MORX);
1201 if (!hb_blob_get_length (mort_blob))
1202 {
1203 hb_blob_destroy (mort_blob);
1204 return NULL;
1205 }
1206 }
1207 hb_blob_destroy (mort_blob);
1208
1209 return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL;
1210 }
1211
1212 void
_hb_coretext_aat_shaper_face_data_destroy(hb_coretext_aat_shaper_face_data_t * data HB_UNUSED)1213 _hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t *data HB_UNUSED)
1214 {
1215 }
1216
1217
1218 /*
1219 * shaper font data
1220 */
1221
1222 struct hb_coretext_aat_shaper_font_data_t {};
1223
1224 hb_coretext_aat_shaper_font_data_t *
_hb_coretext_aat_shaper_font_data_create(hb_font_t * font)1225 _hb_coretext_aat_shaper_font_data_create (hb_font_t *font)
1226 {
1227 return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL;
1228 }
1229
1230 void
_hb_coretext_aat_shaper_font_data_destroy(hb_coretext_aat_shaper_font_data_t * data HB_UNUSED)1231 _hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_shaper_font_data_t *data HB_UNUSED)
1232 {
1233 }
1234
1235
1236 /*
1237 * shaper shape_plan data
1238 */
1239
1240 struct hb_coretext_aat_shaper_shape_plan_data_t {};
1241
1242 hb_coretext_aat_shaper_shape_plan_data_t *
_hb_coretext_aat_shaper_shape_plan_data_create(hb_shape_plan_t * shape_plan HB_UNUSED,const hb_feature_t * user_features HB_UNUSED,unsigned int num_user_features HB_UNUSED)1243 _hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED,
1244 const hb_feature_t *user_features HB_UNUSED,
1245 unsigned int num_user_features HB_UNUSED)
1246 {
1247 return (hb_coretext_aat_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
1248 }
1249
1250 void
_hb_coretext_aat_shaper_shape_plan_data_destroy(hb_coretext_aat_shaper_shape_plan_data_t * data HB_UNUSED)1251 _hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shaper_shape_plan_data_t *data HB_UNUSED)
1252 {
1253 }
1254
1255
1256 /*
1257 * shaper
1258 */
1259
1260 hb_bool_t
_hb_coretext_aat_shape(hb_shape_plan_t * shape_plan,hb_font_t * font,hb_buffer_t * buffer,const hb_feature_t * features,unsigned int num_features)1261 _hb_coretext_aat_shape (hb_shape_plan_t *shape_plan,
1262 hb_font_t *font,
1263 hb_buffer_t *buffer,
1264 const hb_feature_t *features,
1265 unsigned int num_features)
1266 {
1267 return _hb_coretext_shape (shape_plan, font, buffer, features, num_features);
1268 }
1269