2 * Copyright © 2012,2013 Mozilla Foundation.
3 * Copyright © 2012,2013 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 * Mozilla Author(s): Jonathan Kew
26 * Google Author(s): Behdad Esfahbod
29 #define HB_SHAPER coretext
30 #define hb_coretext_shaper_face_data_t CGFont
31 #include "hb-shaper-impl-private.hh"
33 #include "hb-coretext.h"
36 #ifndef HB_DEBUG_CORETEXT
37 #define HB_DEBUG_CORETEXT (HB_DEBUG+0)
42 release_table_data (void *user_data
)
44 CFDataRef cf_data
= reinterpret_cast<CFDataRef
> (user_data
);
49 reference_table (hb_face_t
*face HB_UNUSED
, hb_tag_t tag
, void *user_data
)
51 CGFontRef cg_font
= reinterpret_cast<CGFontRef
> (user_data
);
52 CFDataRef cf_data
= CGFontCopyTableForTag (cg_font
, tag
);
53 if (unlikely (!cf_data
))
56 const char *data
= reinterpret_cast<const char*> (CFDataGetBytePtr (cf_data
));
57 const size_t length
= CFDataGetLength (cf_data
);
61 return hb_blob_create (data
, length
, HB_MEMORY_MODE_READONLY
,
62 reinterpret_cast<void *> (const_cast<__CFData
*> (cf_data
)),
67 hb_coretext_face_create (CGFontRef cg_font
)
69 return hb_face_create_for_tables (reference_table
, CGFontRetain (cg_font
), (hb_destroy_func_t
) CGFontRelease
);
73 HB_SHAPER_DATA_ENSURE_DECLARE(coretext
, face
)
74 HB_SHAPER_DATA_ENSURE_DECLARE(coretext
, font
)
82 release_data (void *info
, const void *data
, size_t size
)
84 assert (hb_blob_get_length ((hb_blob_t
*) info
) == size
&&
85 hb_blob_get_data ((hb_blob_t
*) info
, NULL
) == data
);
87 hb_blob_destroy ((hb_blob_t
*) info
);
90 hb_coretext_shaper_face_data_t
*
91 _hb_coretext_shaper_face_data_create (hb_face_t
*face
)
93 hb_coretext_shaper_face_data_t
*data
= NULL
;
95 if (face
->destroy
== (hb_destroy_func_t
) CGFontRelease
)
97 data
= CGFontRetain ((CGFontRef
) face
->user_data
);
101 hb_blob_t
*blob
= hb_face_reference_blob (face
);
102 unsigned int blob_length
;
103 const char *blob_data
= hb_blob_get_data (blob
, &blob_length
);
104 if (unlikely (!blob_length
))
105 DEBUG_MSG (CORETEXT
, face
, "Face has empty blob");
107 CGDataProviderRef provider
= CGDataProviderCreateWithData (blob
, blob_data
, blob_length
, &release_data
);
108 if (likely (provider
))
110 data
= CGFontCreateWithDataProvider (provider
);
111 CGDataProviderRelease (provider
);
115 if (unlikely (!data
)) {
116 DEBUG_MSG (CORETEXT
, face
, "Face CGFontCreateWithDataProvider() failed");
123 _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t
*data
)
129 hb_coretext_face_get_cg_font (hb_face_t
*face
)
131 if (unlikely (!hb_coretext_shaper_face_data_ensure (face
))) return NULL
;
132 hb_coretext_shaper_face_data_t
*face_data
= HB_SHAPER_DATA_GET (face
);
141 struct hb_coretext_shaper_font_data_t
{
145 hb_coretext_shaper_font_data_t
*
146 _hb_coretext_shaper_font_data_create (hb_font_t
*font
)
148 if (unlikely (!hb_coretext_shaper_face_data_ensure (font
->face
))) return NULL
;
150 hb_coretext_shaper_font_data_t
*data
= (hb_coretext_shaper_font_data_t
*) calloc (1, sizeof (hb_coretext_shaper_font_data_t
));
151 if (unlikely (!data
))
154 hb_face_t
*face
= font
->face
;
155 hb_coretext_shaper_face_data_t
*face_data
= HB_SHAPER_DATA_GET (face
);
157 data
->ct_font
= CTFontCreateWithGraphicsFont (face_data
, font
->y_scale
, NULL
, NULL
);
158 if (unlikely (!data
->ct_font
)) {
159 DEBUG_MSG (CORETEXT
, font
, "Font CTFontCreateWithGraphicsFont() failed");
168 _hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t
*data
)
170 CFRelease (data
->ct_font
);
176 * shaper shape_plan data
179 struct hb_coretext_shaper_shape_plan_data_t
{};
181 hb_coretext_shaper_shape_plan_data_t
*
182 _hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t
*shape_plan HB_UNUSED
,
183 const hb_feature_t
*user_features HB_UNUSED
,
184 unsigned int num_user_features HB_UNUSED
)
186 return (hb_coretext_shaper_shape_plan_data_t
*) HB_SHAPER_DATA_SUCCEEDED
;
190 _hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t
*data HB_UNUSED
)
195 hb_coretext_font_get_ct_font (hb_font_t
*font
)
197 if (unlikely (!hb_coretext_shaper_font_data_ensure (font
))) return NULL
;
198 hb_coretext_shaper_font_data_t
*font_data
= HB_SHAPER_DATA_GET (font
);
199 return font_data
->ct_font
;
207 struct feature_record_t
{
208 unsigned int feature
;
209 unsigned int setting
;
212 struct active_feature_t
{
213 feature_record_t rec
;
216 static int cmp (const active_feature_t
*a
, const active_feature_t
*b
) {
217 return a
->rec
.feature
< b
->rec
.feature
? -1 : a
->rec
.feature
> b
->rec
.feature
? 1 :
218 a
->order
< b
->order
? -1 : a
->order
> b
->order
? 1 :
219 a
->rec
.setting
< b
->rec
.setting
? -1 : a
->rec
.setting
> b
->rec
.setting
? 1 :
222 bool operator== (const active_feature_t
*f
) {
223 return cmp (this, f
) == 0;
227 struct feature_event_t
{
230 active_feature_t feature
;
232 static int cmp (const feature_event_t
*a
, const feature_event_t
*b
) {
233 return a
->index
< b
->index
? -1 : a
->index
> b
->index
? 1 :
234 a
->start
< b
->start
? -1 : a
->start
> b
->start
? 1 :
235 active_feature_t::cmp (&a
->feature
, &b
->feature
);
239 struct range_record_t
{
241 unsigned int index_first
; /* == start */
242 unsigned int index_last
; /* == end - 1 */
246 /* The following enum members are added in OS X 10.8. */
247 #define kAltHalfWidthTextSelector 6
248 #define kAltProportionalTextSelector 5
249 #define kAlternateHorizKanaOffSelector 1
250 #define kAlternateHorizKanaOnSelector 0
251 #define kAlternateKanaType 34
252 #define kAlternateVertKanaOffSelector 3
253 #define kAlternateVertKanaOnSelector 2
254 #define kCaseSensitiveLayoutOffSelector 1
255 #define kCaseSensitiveLayoutOnSelector 0
256 #define kCaseSensitiveLayoutType 33
257 #define kCaseSensitiveSpacingOffSelector 3
258 #define kCaseSensitiveSpacingOnSelector 2
259 #define kContextualAlternatesOffSelector 1
260 #define kContextualAlternatesOnSelector 0
261 #define kContextualAlternatesType 36
262 #define kContextualLigaturesOffSelector 19
263 #define kContextualLigaturesOnSelector 18
264 #define kContextualSwashAlternatesOffSelector 5
265 #define kContextualSwashAlternatesOnSelector 4
266 #define kDefaultLowerCaseSelector 0
267 #define kDefaultUpperCaseSelector 0
268 #define kHistoricalLigaturesOffSelector 21
269 #define kHistoricalLigaturesOnSelector 20
270 #define kHojoCharactersSelector 12
271 #define kJIS2004CharactersSelector 11
272 #define kLowerCasePetiteCapsSelector 2
273 #define kLowerCaseSmallCapsSelector 1
274 #define kLowerCaseType 37
275 #define kMathematicalGreekOffSelector 11
276 #define kMathematicalGreekOnSelector 10
277 #define kNLCCharactersSelector 13
278 #define kQuarterWidthTextSelector 4
279 #define kScientificInferiorsSelector 4
280 #define kStylisticAltEightOffSelector 17
281 #define kStylisticAltEightOnSelector 16
282 #define kStylisticAltEighteenOffSelector 37
283 #define kStylisticAltEighteenOnSelector 36
284 #define kStylisticAltElevenOffSelector 23
285 #define kStylisticAltElevenOnSelector 22
286 #define kStylisticAltFifteenOffSelector 31
287 #define kStylisticAltFifteenOnSelector 30
288 #define kStylisticAltFiveOffSelector 11
289 #define kStylisticAltFiveOnSelector 10
290 #define kStylisticAltFourOffSelector 9
291 #define kStylisticAltFourOnSelector 8
292 #define kStylisticAltFourteenOffSelector 29
293 #define kStylisticAltFourteenOnSelector 28
294 #define kStylisticAltNineOffSelector 19
295 #define kStylisticAltNineOnSelector 18
296 #define kStylisticAltNineteenOffSelector 39
297 #define kStylisticAltNineteenOnSelector 38
298 #define kStylisticAltOneOffSelector 3
299 #define kStylisticAltOneOnSelector 2
300 #define kStylisticAltSevenOffSelector 15
301 #define kStylisticAltSevenOnSelector 14
302 #define kStylisticAltSeventeenOffSelector 35
303 #define kStylisticAltSeventeenOnSelector 34
304 #define kStylisticAltSixOffSelector 13
305 #define kStylisticAltSixOnSelector 12
306 #define kStylisticAltSixteenOffSelector 33
307 #define kStylisticAltSixteenOnSelector 32
308 #define kStylisticAltTenOffSelector 21
309 #define kStylisticAltTenOnSelector 20
310 #define kStylisticAltThirteenOffSelector 27
311 #define kStylisticAltThirteenOnSelector 26
312 #define kStylisticAltThreeOffSelector 7
313 #define kStylisticAltThreeOnSelector 6
314 #define kStylisticAltTwelveOffSelector 25
315 #define kStylisticAltTwelveOnSelector 24
316 #define kStylisticAltTwentyOffSelector 41
317 #define kStylisticAltTwentyOnSelector 40
318 #define kStylisticAltTwoOffSelector 5
319 #define kStylisticAltTwoOnSelector 4
320 #define kStylisticAlternativesType 35
321 #define kSwashAlternatesOffSelector 3
322 #define kSwashAlternatesOnSelector 2
323 #define kThirdWidthTextSelector 3
324 #define kTraditionalNamesCharactersSelector 14
325 #define kUpperCasePetiteCapsSelector 2
326 #define kUpperCaseSmallCapsSelector 1
327 #define kUpperCaseType 38
329 /* Table data courtesy of Apple. */
330 static const struct feature_mapping_t
{
331 FourCharCode otFeatureTag
;
332 uint16_t aatFeatureType
;
333 uint16_t selectorToEnable
;
334 uint16_t selectorToDisable
;
335 } feature_mappings
[] = {
336 { 'c2pc', kUpperCaseType
, kUpperCasePetiteCapsSelector
, kDefaultUpperCaseSelector
},
337 { 'c2sc', kUpperCaseType
, kUpperCaseSmallCapsSelector
, kDefaultUpperCaseSelector
},
338 { 'calt', kContextualAlternatesType
, kContextualAlternatesOnSelector
, kContextualAlternatesOffSelector
},
339 { 'case', kCaseSensitiveLayoutType
, kCaseSensitiveLayoutOnSelector
, kCaseSensitiveLayoutOffSelector
},
340 { 'clig', kLigaturesType
, kContextualLigaturesOnSelector
, kContextualLigaturesOffSelector
},
341 { 'cpsp', kCaseSensitiveLayoutType
, kCaseSensitiveSpacingOnSelector
, kCaseSensitiveSpacingOffSelector
},
342 { 'cswh', kContextualAlternatesType
, kContextualSwashAlternatesOnSelector
, kContextualSwashAlternatesOffSelector
},
343 { 'dlig', kLigaturesType
, kRareLigaturesOnSelector
, kRareLigaturesOffSelector
},
344 { 'expt', kCharacterShapeType
, kExpertCharactersSelector
, 16 },
345 { 'frac', kFractionsType
, kDiagonalFractionsSelector
, kNoFractionsSelector
},
346 { 'fwid', kTextSpacingType
, kMonospacedTextSelector
, 7 },
347 { 'halt', kTextSpacingType
, kAltHalfWidthTextSelector
, 7 },
348 { 'hist', kLigaturesType
, kHistoricalLigaturesOnSelector
, kHistoricalLigaturesOffSelector
},
349 { 'hkna', kAlternateKanaType
, kAlternateHorizKanaOnSelector
, kAlternateHorizKanaOffSelector
, },
350 { 'hlig', kLigaturesType
, kHistoricalLigaturesOnSelector
, kHistoricalLigaturesOffSelector
},
351 { 'hngl', kTransliterationType
, kHanjaToHangulSelector
, kNoTransliterationSelector
},
352 { 'hojo', kCharacterShapeType
, kHojoCharactersSelector
, 16 },
353 { 'hwid', kTextSpacingType
, kHalfWidthTextSelector
, 7 },
354 { 'ital', kItalicCJKRomanType
, kCJKItalicRomanOnSelector
, kCJKItalicRomanOffSelector
},
355 { 'jp04', kCharacterShapeType
, kJIS2004CharactersSelector
, 16 },
356 { 'jp78', kCharacterShapeType
, kJIS1978CharactersSelector
, 16 },
357 { 'jp83', kCharacterShapeType
, kJIS1983CharactersSelector
, 16 },
358 { 'jp90', kCharacterShapeType
, kJIS1990CharactersSelector
, 16 },
359 { 'liga', kLigaturesType
, kCommonLigaturesOnSelector
, kCommonLigaturesOffSelector
},
360 { 'lnum', kNumberCaseType
, kUpperCaseNumbersSelector
, 2 },
361 { 'mgrk', kMathematicalExtrasType
, kMathematicalGreekOnSelector
, kMathematicalGreekOffSelector
},
362 { 'nlck', kCharacterShapeType
, kNLCCharactersSelector
, 16 },
363 { 'onum', kNumberCaseType
, kLowerCaseNumbersSelector
, 2 },
364 { 'ordn', kVerticalPositionType
, kOrdinalsSelector
, kNormalPositionSelector
},
365 { 'palt', kTextSpacingType
, kAltProportionalTextSelector
, 7 },
366 { 'pcap', kLowerCaseType
, kLowerCasePetiteCapsSelector
, kDefaultLowerCaseSelector
},
367 { 'pkna', kTextSpacingType
, kProportionalTextSelector
, 7 },
368 { 'pnum', kNumberSpacingType
, kProportionalNumbersSelector
, 4 },
369 { 'pwid', kTextSpacingType
, kProportionalTextSelector
, 7 },
370 { 'qwid', kTextSpacingType
, kQuarterWidthTextSelector
, 7 },
371 { 'ruby', kRubyKanaType
, kRubyKanaOnSelector
, kRubyKanaOffSelector
},
372 { 'sinf', kVerticalPositionType
, kScientificInferiorsSelector
, kNormalPositionSelector
},
373 { 'smcp', kLowerCaseType
, kLowerCaseSmallCapsSelector
, kDefaultLowerCaseSelector
},
374 { 'smpl', kCharacterShapeType
, kSimplifiedCharactersSelector
, 16 },
375 { 'ss01', kStylisticAlternativesType
, kStylisticAltOneOnSelector
, kStylisticAltOneOffSelector
},
376 { 'ss02', kStylisticAlternativesType
, kStylisticAltTwoOnSelector
, kStylisticAltTwoOffSelector
},
377 { 'ss03', kStylisticAlternativesType
, kStylisticAltThreeOnSelector
, kStylisticAltThreeOffSelector
},
378 { 'ss04', kStylisticAlternativesType
, kStylisticAltFourOnSelector
, kStylisticAltFourOffSelector
},
379 { 'ss05', kStylisticAlternativesType
, kStylisticAltFiveOnSelector
, kStylisticAltFiveOffSelector
},
380 { 'ss06', kStylisticAlternativesType
, kStylisticAltSixOnSelector
, kStylisticAltSixOffSelector
},
381 { 'ss07', kStylisticAlternativesType
, kStylisticAltSevenOnSelector
, kStylisticAltSevenOffSelector
},
382 { 'ss08', kStylisticAlternativesType
, kStylisticAltEightOnSelector
, kStylisticAltEightOffSelector
},
383 { 'ss09', kStylisticAlternativesType
, kStylisticAltNineOnSelector
, kStylisticAltNineOffSelector
},
384 { 'ss10', kStylisticAlternativesType
, kStylisticAltTenOnSelector
, kStylisticAltTenOffSelector
},
385 { 'ss11', kStylisticAlternativesType
, kStylisticAltElevenOnSelector
, kStylisticAltElevenOffSelector
},
386 { 'ss12', kStylisticAlternativesType
, kStylisticAltTwelveOnSelector
, kStylisticAltTwelveOffSelector
},
387 { 'ss13', kStylisticAlternativesType
, kStylisticAltThirteenOnSelector
, kStylisticAltThirteenOffSelector
},
388 { 'ss14', kStylisticAlternativesType
, kStylisticAltFourteenOnSelector
, kStylisticAltFourteenOffSelector
},
389 { 'ss15', kStylisticAlternativesType
, kStylisticAltFifteenOnSelector
, kStylisticAltFifteenOffSelector
},
390 { 'ss16', kStylisticAlternativesType
, kStylisticAltSixteenOnSelector
, kStylisticAltSixteenOffSelector
},
391 { 'ss17', kStylisticAlternativesType
, kStylisticAltSeventeenOnSelector
, kStylisticAltSeventeenOffSelector
},
392 { 'ss18', kStylisticAlternativesType
, kStylisticAltEighteenOnSelector
, kStylisticAltEighteenOffSelector
},
393 { 'ss19', kStylisticAlternativesType
, kStylisticAltNineteenOnSelector
, kStylisticAltNineteenOffSelector
},
394 { 'ss20', kStylisticAlternativesType
, kStylisticAltTwentyOnSelector
, kStylisticAltTwentyOffSelector
},
395 { 'subs', kVerticalPositionType
, kInferiorsSelector
, kNormalPositionSelector
},
396 { 'sups', kVerticalPositionType
, kSuperiorsSelector
, kNormalPositionSelector
},
397 { 'swsh', kContextualAlternatesType
, kSwashAlternatesOnSelector
, kSwashAlternatesOffSelector
},
398 { 'titl', kStyleOptionsType
, kTitlingCapsSelector
, kNoStyleOptionsSelector
},
399 { 'tnam', kCharacterShapeType
, kTraditionalNamesCharactersSelector
, 16 },
400 { 'tnum', kNumberSpacingType
, kMonospacedNumbersSelector
, 4 },
401 { 'trad', kCharacterShapeType
, kTraditionalCharactersSelector
, 16 },
402 { 'twid', kTextSpacingType
, kThirdWidthTextSelector
, 7 },
403 { 'unic', kLetterCaseType
, 14, 15 },
404 { 'valt', kTextSpacingType
, kAltProportionalTextSelector
, 7 },
405 { 'vert', kVerticalSubstitutionType
, kSubstituteVerticalFormsOnSelector
, kSubstituteVerticalFormsOffSelector
},
406 { 'vhal', kTextSpacingType
, kAltHalfWidthTextSelector
, 7 },
407 { 'vkna', kAlternateKanaType
, kAlternateVertKanaOnSelector
, kAlternateVertKanaOffSelector
},
408 { 'vpal', kTextSpacingType
, kAltProportionalTextSelector
, 7 },
409 { 'vrt2', kVerticalSubstitutionType
, kSubstituteVerticalFormsOnSelector
, kSubstituteVerticalFormsOffSelector
},
410 { 'zero', kTypographicExtrasType
, kSlashedZeroOnSelector
, kSlashedZeroOffSelector
},
414 _hb_feature_mapping_cmp (const void *key_
, const void *entry_
)
416 unsigned int key
= * (unsigned int *) key_
;
417 const feature_mapping_t
* entry
= (const feature_mapping_t
*) entry_
;
418 return key
< entry
->otFeatureTag
? -1 :
419 key
> entry
->otFeatureTag
? 1 :
424 _hb_coretext_shape (hb_shape_plan_t
*shape_plan
,
427 const hb_feature_t
*features
,
428 unsigned int num_features
)
430 hb_face_t
*face
= font
->face
;
431 hb_coretext_shaper_face_data_t
*face_data
= HB_SHAPER_DATA_GET (face
);
432 hb_coretext_shaper_font_data_t
*font_data
= HB_SHAPER_DATA_GET (font
);
434 /* Attach marks to their bases, to match the 'ot' shaper.
435 * Adapted from hb-ot-shape:hb_form_clusters().
436 * Note that this only makes us be closer to the 'ot' shaper,
437 * but by no means the same. For example, if there's
438 * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will
439 * continue pointing to B2 even though B2 was merged into B1's
442 hb_unicode_funcs_t
*unicode
= buffer
->unicode
;
443 unsigned int count
= buffer
->len
;
444 hb_glyph_info_t
*info
= buffer
->info
;
445 for (unsigned int i
= 1; i
< count
; i
++)
446 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode
->general_category (info
[i
].codepoint
)))
447 buffer
->merge_clusters (i
- 1, i
+ 1);
450 hb_auto_array_t
<feature_record_t
> feature_records
;
451 hb_auto_array_t
<range_record_t
> range_records
;
455 * (copied + modified from code from hb-uniscribe.cc)
459 /* Sort features by start/end events. */
460 hb_auto_array_t
<feature_event_t
> feature_events
;
461 for (unsigned int i
= 0; i
< num_features
; i
++)
463 const feature_mapping_t
* mapping
= (const feature_mapping_t
*) bsearch (&features
[i
].tag
,
465 ARRAY_LENGTH (feature_mappings
),
466 sizeof (feature_mappings
[0]),
467 _hb_feature_mapping_cmp
);
471 active_feature_t feature
;
472 feature
.rec
.feature
= mapping
->aatFeatureType
;
473 feature
.rec
.setting
= features
[i
].value
? mapping
->selectorToEnable
: mapping
->selectorToDisable
;
476 feature_event_t
*event
;
478 event
= feature_events
.push ();
479 if (unlikely (!event
))
481 event
->index
= features
[i
].start
;
483 event
->feature
= feature
;
485 event
= feature_events
.push ();
486 if (unlikely (!event
))
488 event
->index
= features
[i
].end
;
489 event
->start
= false;
490 event
->feature
= feature
;
492 feature_events
.qsort ();
493 /* Add a strategic final event. */
495 active_feature_t feature
;
496 feature
.rec
.feature
= HB_TAG_NONE
;
497 feature
.rec
.setting
= 0;
498 feature
.order
= num_features
+ 1;
500 feature_event_t
*event
= feature_events
.push ();
501 if (unlikely (!event
))
503 event
->index
= 0; /* This value does magic. */
504 event
->start
= false;
505 event
->feature
= feature
;
508 /* Scan events and save features for each range. */
509 hb_auto_array_t
<active_feature_t
> active_features
;
510 unsigned int last_index
= 0;
511 for (unsigned int i
= 0; i
< feature_events
.len
; i
++)
513 feature_event_t
*event
= &feature_events
[i
];
515 if (event
->index
!= last_index
)
517 /* Save a snapshot of active features and the range. */
518 range_record_t
*range
= range_records
.push ();
519 if (unlikely (!range
))
522 if (active_features
.len
)
524 CFMutableArrayRef features_array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
526 /* TODO sort and resolve conflicting features? */
527 /* active_features.qsort (); */
528 for (unsigned int j
= 0; j
< active_features
.len
; j
++)
530 CFStringRef keys
[2] = {
531 kCTFontFeatureTypeIdentifierKey
,
532 kCTFontFeatureSelectorIdentifierKey
534 CFNumberRef values
[2] = {
535 CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &active_features
[j
].rec
.feature
),
536 CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &active_features
[j
].rec
.setting
)
538 CFDictionaryRef dict
= CFDictionaryCreate (kCFAllocatorDefault
,
539 (const void **) keys
,
540 (const void **) values
,
542 &kCFTypeDictionaryKeyCallBacks
,
543 &kCFTypeDictionaryValueCallBacks
);
544 CFRelease (values
[0]);
545 CFRelease (values
[1]);
547 CFArrayAppendValue (features_array
, dict
);
552 CFDictionaryRef attributes
= CFDictionaryCreate (kCFAllocatorDefault
,
553 (const void **) &kCTFontFeatureSettingsAttribute
,
554 (const void **) &features_array
,
556 &kCFTypeDictionaryKeyCallBacks
,
557 &kCFTypeDictionaryValueCallBacks
);
558 CFRelease (features_array
);
560 CTFontDescriptorRef font_desc
= CTFontDescriptorCreateWithAttributes (attributes
);
561 CFRelease (attributes
);
563 range
->font
= CTFontCreateCopyWithAttributes (font_data
->ct_font
, 0.0, NULL
, font_desc
);
564 CFRelease (font_desc
);
571 range
->index_first
= last_index
;
572 range
->index_last
= event
->index
- 1;
574 last_index
= event
->index
;
578 active_feature_t
*feature
= active_features
.push ();
579 if (unlikely (!feature
))
581 *feature
= event
->feature
;
583 active_feature_t
*feature
= active_features
.find (&event
->feature
);
585 active_features
.remove (feature
- active_features
.array
);
589 if (!range_records
.len
) /* No active feature found. */
598 unsigned int scratch_size
;
599 hb_buffer_t::scratch_buffer_t
*scratch
= buffer
->get_scratch_buffer (&scratch_size
);
601 #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \
602 Type *name = (Type *) scratch; \
604 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
605 if (unlikely (_consumed > scratch_size)) \
610 scratch += _consumed; \
611 scratch_size -= _consumed; \
614 ALLOCATE_ARRAY (UniChar
, pchars
, buffer
->len
* 2, /*nothing*/);
615 unsigned int chars_len
= 0;
616 for (unsigned int i
= 0; i
< buffer
->len
; i
++) {
617 hb_codepoint_t c
= buffer
->info
[i
].codepoint
;
618 if (likely (c
<= 0xFFFFu
))
619 pchars
[chars_len
++] = c
;
620 else if (unlikely (c
> 0x10FFFFu
))
621 pchars
[chars_len
++] = 0xFFFDu
;
623 pchars
[chars_len
++] = 0xD800u
+ ((c
- 0x10000u
) >> 10);
624 pchars
[chars_len
++] = 0xDC00u
+ ((c
- 0x10000u
) & ((1 << 10) - 1));
628 ALLOCATE_ARRAY (unsigned int, log_clusters
, chars_len
, /*nothing*/);
630 for (unsigned int i
= 0; i
< buffer
->len
; i
++)
632 hb_codepoint_t c
= buffer
->info
[i
].codepoint
;
633 unsigned int cluster
= buffer
->info
[i
].cluster
;
634 log_clusters
[chars_len
++] = cluster
;
635 if (hb_in_range (c
, 0x10000u
, 0x10FFFFu
))
636 log_clusters
[chars_len
++] = cluster
; /* Surrogates. */
641 DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
647 CFStringRef string_ref
= NULL
;
648 CTLineRef line
= NULL
;
653 DEBUG_MSG (CORETEXT
, buffer
, "Buffer resize");
654 /* string_ref uses the scratch-buffer for backing store, and line references
655 * string_ref (via attr_string). We must release those before resizing buffer. */
658 CFRelease (string_ref
);
663 /* Get previous start-of-scratch-area, that we use later for readjusting
664 * our existing scratch arrays. */
665 unsigned int old_scratch_used
;
666 hb_buffer_t::scratch_buffer_t
*old_scratch
;
667 old_scratch
= buffer
->get_scratch_buffer (&old_scratch_used
);
668 old_scratch_used
= scratch
- old_scratch
;
670 if (unlikely (!buffer
->ensure (buffer
->allocated
* 2)))
671 FAIL ("Buffer resize failed");
673 /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the
674 * cleanest way to do without completely restructuring the rest of this shaper. */
675 scratch
= buffer
->get_scratch_buffer (&scratch_size
);
676 pchars
= reinterpret_cast<UniChar
*> (((char *) scratch
+ ((char *) pchars
- (char *) old_scratch
)));
677 log_clusters
= reinterpret_cast<unsigned int *> (((char *) scratch
+ ((char *) log_clusters
- (char *) old_scratch
)));
678 scratch
+= old_scratch_used
;
679 scratch_size
-= old_scratch_used
;
683 string_ref
= CFStringCreateWithCharactersNoCopy (NULL
,
686 if (unlikely (!string_ref
))
687 FAIL ("CFStringCreateWithCharactersNoCopy failed");
689 /* Create an attributed string, populate it, and create a line from it, then release attributed string. */
691 CFMutableAttributedStringRef attr_string
= CFAttributedStringCreateMutable (kCFAllocatorDefault
,
693 if (unlikely (!attr_string
))
694 FAIL ("CFAttributedStringCreateMutable failed");
695 CFAttributedStringReplaceString (attr_string
, CFRangeMake (0, 0), string_ref
);
696 if (HB_DIRECTION_IS_VERTICAL (buffer
->props
.direction
))
698 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (0, chars_len
),
699 kCTVerticalFormsAttributeName
, kCFBooleanTrue
);
702 if (buffer
->props
.language
)
704 /* What's the iOS equivalent of this check?
705 * The symbols was introduced in iOS 7.0.
706 * At any rate, our fallback is safe and works fine. */
707 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
708 # define kCTLanguageAttributeName CFSTR ("NSLanguage")
710 CFStringRef lang
= CFStringCreateWithCStringNoCopy (kCFAllocatorDefault
,
711 hb_language_to_string (buffer
->props
.language
),
712 kCFStringEncodingUTF8
,
714 if (unlikely (!lang
))
715 FAIL ("CFStringCreateWithCStringNoCopy failed");
716 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (0, chars_len
),
717 kCTLanguageAttributeName
, lang
);
720 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (0, chars_len
),
721 kCTFontAttributeName
, font_data
->ct_font
);
725 unsigned int start
= 0;
726 range_record_t
*last_range
= &range_records
[0];
727 for (unsigned int k
= 0; k
< chars_len
; k
++)
729 range_record_t
*range
= last_range
;
730 while (log_clusters
[k
] < range
->index_first
)
732 while (log_clusters
[k
] > range
->index_last
)
734 if (range
!= last_range
)
736 if (last_range
->font
)
737 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (start
, k
- start
),
738 kCTFontAttributeName
, last_range
->font
);
745 if (start
!= chars_len
&& last_range
->font
)
746 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (start
, chars_len
- start
),
747 kCTFontAttributeName
, last_range
->font
);
750 int level
= HB_DIRECTION_IS_FORWARD (buffer
->props
.direction
) ? 0 : 1;
751 CFNumberRef level_number
= CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &level
);
752 CFDictionaryRef options
= CFDictionaryCreate (kCFAllocatorDefault
,
753 (const void **) &kCTTypesetterOptionForcedEmbeddingLevel
,
754 (const void **) &level_number
,
756 &kCFTypeDictionaryKeyCallBacks
,
757 &kCFTypeDictionaryValueCallBacks
);
758 if (unlikely (!options
))
759 FAIL ("CFDictionaryCreate failed");
761 CTTypesetterRef typesetter
= CTTypesetterCreateWithAttributedStringAndOptions (attr_string
, options
);
763 CFRelease (attr_string
);
764 if (unlikely (!typesetter
))
765 FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed");
767 line
= CTTypesetterCreateLine (typesetter
, CFRangeMake(0, 0));
768 CFRelease (typesetter
);
769 if (unlikely (!line
))
770 FAIL ("CTTypesetterCreateLine failed");
773 CFArrayRef glyph_runs
= CTLineGetGlyphRuns (line
);
774 unsigned int num_runs
= CFArrayGetCount (glyph_runs
);
775 DEBUG_MSG (CORETEXT
, NULL
, "Num runs: %d", num_runs
);
778 uint32_t status_and
= ~0, status_or
= 0;
780 const CFRange range_all
= CFRangeMake (0, 0);
782 for (unsigned int i
= 0; i
< num_runs
; i
++)
784 CTRunRef run
= static_cast<CTRunRef
>(CFArrayGetValueAtIndex (glyph_runs
, i
));
785 CTRunStatus run_status
= CTRunGetStatus (run
);
786 status_or
|= run_status
;
787 status_and
&= run_status
;
788 DEBUG_MSG (CORETEXT
, run
, "CTRunStatus: %x", run_status
);
790 /* CoreText does automatic font fallback (AKA "cascading") for characters
791 * not supported by the requested font, and provides no way to turn it off,
792 * so we must detect if the returned run uses a font other than the requested
793 * one and fill in the buffer with .notdef glyphs instead of random glyph
794 * indices from a different font.
796 CFDictionaryRef attributes
= CTRunGetAttributes (run
);
797 CTFontRef run_ct_font
= static_cast<CTFontRef
>(CFDictionaryGetValue (attributes
, kCTFontAttributeName
));
798 if (!CFEqual (run_ct_font
, font_data
->ct_font
))
800 /* The run doesn't use our main font instance. We have to figure out
801 * whether font fallback happened, or this is just CoreText giving us
802 * another CTFont using the same underlying CGFont. CoreText seems
803 * to do that in a variety of situations, one of which being vertical
804 * text, but also perhaps for caching reasons.
806 * First, see if it uses any of our subfonts created to set font features...
808 * Next, compare the CGFont to the one we used to create our fonts.
809 * Even this doesn't work all the time.
811 * Finally, we compare PS names, which I don't think are unique...
813 * Looks like if we really want to be sure here we have to modify the
814 * font to change the name table, similar to what we do in the uniscribe
817 * However, even that wouldn't work if we were passed in the CGFont to
820 * Webkit uses a slightly different approach: it installs LastResort
821 * as fallback chain, and then checks PS name of used font against
822 * LastResort. That one is safe for any font except for LastResort,
823 * as opposed to ours, which can fail if we are using any uninstalled
824 * font that has the same name as an installed font.
826 * See: http://github.com/behdad/harfbuzz/pull/36
828 bool matched
= false;
829 for (unsigned int i
= 0; i
< range_records
.len
; i
++)
830 if (range_records
[i
].font
&& CFEqual (run_ct_font
, range_records
[i
].font
))
837 CGFontRef run_cg_font
= CTFontCopyGraphicsFont (run_ct_font
, 0);
840 matched
= CFEqual (run_cg_font
, face_data
);
841 CFRelease (run_cg_font
);
846 CFStringRef font_ps_name
= CTFontCopyName (font_data
->ct_font
, kCTFontPostScriptNameKey
);
847 CFStringRef run_ps_name
= CTFontCopyName (run_ct_font
, kCTFontPostScriptNameKey
);
848 CFComparisonResult result
= CFStringCompare (run_ps_name
, font_ps_name
, 0);
849 CFRelease (run_ps_name
);
850 CFRelease (font_ps_name
);
851 if (result
== kCFCompareEqualTo
)
856 CFRange range
= CTRunGetStringRange (run
);
857 DEBUG_MSG (CORETEXT
, run
, "Run used fallback font: %ld..%ld",
858 range
.location
, range
.location
+ range
.length
);
859 if (!buffer
->ensure_inplace (buffer
->len
+ range
.length
))
860 goto resize_and_retry
;
861 hb_glyph_info_t
*info
= buffer
->info
+ buffer
->len
;
864 double advance
= CTFontGetAdvancesForGlyphs (font_data
->ct_font
, kCTFontHorizontalOrientation
, ¬def
, NULL
, 1);
866 unsigned int old_len
= buffer
->len
;
867 for (CFIndex j
= range
.location
; j
< range
.location
+ range
.length
; j
++)
869 UniChar ch
= CFStringGetCharacterAtIndex (string_ref
, j
);
870 if (hb_in_range
<UniChar
> (ch
, 0xDC00u
, 0xDFFFu
) && range
.location
< j
)
872 ch
= CFStringGetCharacterAtIndex (string_ref
, j
- 1);
873 if (hb_in_range
<UniChar
> (ch
, 0xD800u
, 0xDBFFu
))
874 /* This is the second of a surrogate pair. Don't need .notdef
879 info
->codepoint
= notdef
;
880 info
->cluster
= log_clusters
[j
];
882 info
->mask
= advance
;
889 if (HB_DIRECTION_IS_BACKWARD (buffer
->props
.direction
))
890 buffer
->reverse_range (old_len
, buffer
->len
);
895 unsigned int num_glyphs
= CTRunGetGlyphCount (run
);
899 if (!buffer
->ensure_inplace (buffer
->len
+ num_glyphs
))
900 goto resize_and_retry
;
902 hb_glyph_info_t
*run_info
= buffer
->info
+ buffer
->len
;
904 /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always
905 * succeed, and so copying data to our own buffer will be rare. Reports
906 * have it that this changed in OS X 10.10 Yosemite, and NULL is returned
907 * frequently. At any rate, we can test that codepath by setting USE_PTR
912 #define SCRATCH_SAVE() \
913 unsigned int scratch_size_saved = scratch_size; \
914 hb_buffer_t::scratch_buffer_t *scratch_saved = scratch
916 #define SCRATCH_RESTORE() \
917 scratch_size = scratch_size_saved; \
918 scratch = scratch_saved;
922 const CGGlyph
* glyphs
= USE_PTR
? CTRunGetGlyphsPtr (run
) : NULL
;
924 ALLOCATE_ARRAY (CGGlyph
, glyph_buf
, num_glyphs
, goto resize_and_retry
);
925 CTRunGetGlyphs (run
, range_all
, glyph_buf
);
928 const CFIndex
* string_indices
= USE_PTR
? CTRunGetStringIndicesPtr (run
) : NULL
;
929 if (!string_indices
) {
930 ALLOCATE_ARRAY (CFIndex
, index_buf
, num_glyphs
, goto resize_and_retry
);
931 CTRunGetStringIndices (run
, range_all
, index_buf
);
932 string_indices
= index_buf
;
934 hb_glyph_info_t
*info
= run_info
;
935 for (unsigned int j
= 0; j
< num_glyphs
; j
++)
937 info
->codepoint
= glyphs
[j
];
938 info
->cluster
= log_clusters
[string_indices
[j
]];
945 const CGPoint
* positions
= USE_PTR
? CTRunGetPositionsPtr (run
) : NULL
;
947 ALLOCATE_ARRAY (CGPoint
, position_buf
, num_glyphs
, goto resize_and_retry
);
948 CTRunGetPositions (run
, range_all
, position_buf
);
949 positions
= position_buf
;
951 double run_advance
= CTRunGetTypographicBounds (run
, range_all
, NULL
, NULL
, NULL
);
952 DEBUG_MSG (CORETEXT
, run
, "Run advance: %g", run_advance
);
953 hb_glyph_info_t
*info
= run_info
;
954 if (HB_DIRECTION_IS_HORIZONTAL (buffer
->props
.direction
))
956 for (unsigned int j
= 0; j
< num_glyphs
; j
++)
958 double advance
= (j
+ 1 < num_glyphs
? positions
[j
+ 1].x
: positions
[0].x
+ run_advance
) - positions
[j
].x
;
959 info
->mask
= advance
;
960 info
->var1
.u32
= positions
[0].x
; /* Yes, zero. */
961 info
->var2
.u32
= positions
[j
].y
;
967 run_advance
= -run_advance
;
968 for (unsigned int j
= 0; j
< num_glyphs
; j
++)
970 double advance
= (j
+ 1 < num_glyphs
? positions
[j
+ 1].y
: positions
[0].y
+ run_advance
) - positions
[j
].y
;
971 info
->mask
= advance
;
972 info
->var1
.u32
= positions
[j
].x
;
973 info
->var2
.u32
= positions
[0].y
; /* Yes, zero. */
979 #undef SCRATCH_RESTORE
982 #undef ALLOCATE_ARRAY
984 buffer
->len
+= num_glyphs
;
987 /* Make sure all runs had the expected direction. */
988 bool backward
= HB_DIRECTION_IS_BACKWARD (buffer
->props
.direction
);
989 assert (bool (status_and
& kCTRunStatusRightToLeft
) == backward
);
990 assert (bool (status_or
& kCTRunStatusRightToLeft
) == backward
);
992 buffer
->clear_positions ();
994 unsigned int count
= buffer
->len
;
995 hb_glyph_info_t
*info
= buffer
->info
;
996 hb_glyph_position_t
*pos
= buffer
->pos
;
997 if (HB_DIRECTION_IS_HORIZONTAL (buffer
->props
.direction
))
998 for (unsigned int i
= 0; i
< count
; i
++)
1000 pos
->x_advance
= info
->mask
;
1001 pos
->x_offset
= info
->var1
.u32
;
1002 pos
->y_offset
= info
->var2
.u32
;
1006 for (unsigned int i
= 0; i
< count
; i
++)
1008 pos
->y_advance
= info
->mask
;
1009 pos
->x_offset
= info
->var1
.u32
;
1010 pos
->y_offset
= info
->var2
.u32
;
1014 /* Fix up clusters so that we never return out-of-order indices;
1015 * if core text has reordered glyphs, we'll merge them to the
1016 * beginning of the reordered cluster. CoreText is nice enough
1017 * to tell us whenever it has produced nonmonotonic results...
1018 * Note that we assume the input clusters were nonmonotonic to
1021 * This does *not* mean we'll form the same clusters as Uniscribe
1022 * or the native OT backend, only that the cluster indices will be
1023 * monotonic in the output buffer. */
1024 if (count
> 1 && (status_or
& kCTRunStatusNonMonotonic
))
1026 hb_glyph_info_t
*info
= buffer
->info
;
1027 if (HB_DIRECTION_IS_FORWARD (buffer
->props
.direction
))
1029 unsigned int cluster
= info
[count
- 1].cluster
;
1030 for (unsigned int i
= count
- 1; i
> 0; i
--)
1032 cluster
= MIN (cluster
, info
[i
- 1].cluster
);
1033 info
[i
- 1].cluster
= cluster
;
1038 unsigned int cluster
= info
[0].cluster
;
1039 for (unsigned int i
= 1; i
< count
; i
++)
1041 cluster
= MIN (cluster
, info
[i
].cluster
);
1042 info
[i
].cluster
= cluster
;
1052 CFRelease (string_ref
);
1056 for (unsigned int i
= 0; i
< range_records
.len
; i
++)
1057 if (range_records
[i
].font
)
1058 CFRelease (range_records
[i
].font
);
1068 HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat
, face
)
1069 HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat
, font
)
1076 struct hb_coretext_aat_shaper_face_data_t
{};
1078 hb_coretext_aat_shaper_face_data_t
*
1079 _hb_coretext_aat_shaper_face_data_create (hb_face_t
*face
)
1081 hb_blob_t
*mort_blob
= face
->reference_table (HB_CORETEXT_TAG_MORT
);
1082 /* Umm, we just reference the table to check whether it exists.
1083 * Maybe add better API for this? */
1084 if (!hb_blob_get_length (mort_blob
))
1086 hb_blob_destroy (mort_blob
);
1087 mort_blob
= face
->reference_table (HB_CORETEXT_TAG_MORX
);
1088 if (!hb_blob_get_length (mort_blob
))
1090 hb_blob_destroy (mort_blob
);
1094 hb_blob_destroy (mort_blob
);
1096 return hb_coretext_shaper_face_data_ensure (face
) ? (hb_coretext_aat_shaper_face_data_t
*) HB_SHAPER_DATA_SUCCEEDED
: NULL
;
1100 _hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t
*data HB_UNUSED
)
1109 struct hb_coretext_aat_shaper_font_data_t
{};
1111 hb_coretext_aat_shaper_font_data_t
*
1112 _hb_coretext_aat_shaper_font_data_create (hb_font_t
*font
)
1114 return hb_coretext_shaper_font_data_ensure (font
) ? (hb_coretext_aat_shaper_font_data_t
*) HB_SHAPER_DATA_SUCCEEDED
: NULL
;
1118 _hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_shaper_font_data_t
*data HB_UNUSED
)
1124 * shaper shape_plan data
1127 struct hb_coretext_aat_shaper_shape_plan_data_t
{};
1129 hb_coretext_aat_shaper_shape_plan_data_t
*
1130 _hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t
*shape_plan HB_UNUSED
,
1131 const hb_feature_t
*user_features HB_UNUSED
,
1132 unsigned int num_user_features HB_UNUSED
)
1134 return (hb_coretext_aat_shaper_shape_plan_data_t
*) HB_SHAPER_DATA_SUCCEEDED
;
1138 _hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shaper_shape_plan_data_t
*data HB_UNUSED
)
1148 _hb_coretext_aat_shape (hb_shape_plan_t
*shape_plan
,
1150 hb_buffer_t
*buffer
,
1151 const hb_feature_t
*features
,
1152 unsigned int num_features
)
1154 return _hb_coretext_shape (shape_plan
, font
, buffer
, features
, num_features
);