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
{
143 CGFloat x_mult
, y_mult
; /* From CT space to HB space. */
146 hb_coretext_shaper_font_data_t
*
147 _hb_coretext_shaper_font_data_create (hb_font_t
*font
)
149 if (unlikely (!hb_coretext_shaper_face_data_ensure (font
->face
))) return NULL
;
151 hb_coretext_shaper_font_data_t
*data
= (hb_coretext_shaper_font_data_t
*) calloc (1, sizeof (hb_coretext_shaper_font_data_t
));
152 if (unlikely (!data
))
155 hb_face_t
*face
= font
->face
;
156 hb_coretext_shaper_face_data_t
*face_data
= HB_SHAPER_DATA_GET (face
);
158 /* Choose a CoreText font size and calculate multipliers to convert to HarfBuzz space. */
159 /* TODO: use upem instead of 36? */
160 CGFloat font_size
= 36.; /* Default... */
161 /* No idea if the following is even a good idea. */
163 font_size
= font
->y_ppem
;
166 font_size
= -font_size
;
167 data
->x_mult
= (CGFloat
) font
->x_scale
/ font_size
;
168 data
->y_mult
= (CGFloat
) font
->y_scale
/ font_size
;
169 data
->ct_font
= CTFontCreateWithGraphicsFont (face_data
, font_size
, NULL
, NULL
);
170 if (unlikely (!data
->ct_font
)) {
171 DEBUG_MSG (CORETEXT
, font
, "Font CTFontCreateWithGraphicsFont() failed");
180 _hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t
*data
)
182 CFRelease (data
->ct_font
);
188 * shaper shape_plan data
191 struct hb_coretext_shaper_shape_plan_data_t
{};
193 hb_coretext_shaper_shape_plan_data_t
*
194 _hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t
*shape_plan HB_UNUSED
,
195 const hb_feature_t
*user_features HB_UNUSED
,
196 unsigned int num_user_features HB_UNUSED
)
198 return (hb_coretext_shaper_shape_plan_data_t
*) HB_SHAPER_DATA_SUCCEEDED
;
202 _hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t
*data HB_UNUSED
)
207 hb_coretext_font_get_ct_font (hb_font_t
*font
)
209 if (unlikely (!hb_coretext_shaper_font_data_ensure (font
))) return NULL
;
210 hb_coretext_shaper_font_data_t
*font_data
= HB_SHAPER_DATA_GET (font
);
211 return font_data
->ct_font
;
219 struct feature_record_t
{
220 unsigned int feature
;
221 unsigned int setting
;
224 struct active_feature_t
{
225 feature_record_t rec
;
228 static int cmp (const active_feature_t
*a
, const active_feature_t
*b
) {
229 return a
->rec
.feature
< b
->rec
.feature
? -1 : a
->rec
.feature
> b
->rec
.feature
? 1 :
230 a
->order
< b
->order
? -1 : a
->order
> b
->order
? 1 :
231 a
->rec
.setting
< b
->rec
.setting
? -1 : a
->rec
.setting
> b
->rec
.setting
? 1 :
234 bool operator== (const active_feature_t
*f
) {
235 return cmp (this, f
) == 0;
239 struct feature_event_t
{
242 active_feature_t feature
;
244 static int cmp (const feature_event_t
*a
, const feature_event_t
*b
) {
245 return a
->index
< b
->index
? -1 : a
->index
> b
->index
? 1 :
246 a
->start
< b
->start
? -1 : a
->start
> b
->start
? 1 :
247 active_feature_t::cmp (&a
->feature
, &b
->feature
);
251 struct range_record_t
{
253 unsigned int index_first
; /* == start */
254 unsigned int index_last
; /* == end - 1 */
258 /* The following enum members are added in OS X 10.8. */
259 #define kAltHalfWidthTextSelector 6
260 #define kAltProportionalTextSelector 5
261 #define kAlternateHorizKanaOffSelector 1
262 #define kAlternateHorizKanaOnSelector 0
263 #define kAlternateKanaType 34
264 #define kAlternateVertKanaOffSelector 3
265 #define kAlternateVertKanaOnSelector 2
266 #define kCaseSensitiveLayoutOffSelector 1
267 #define kCaseSensitiveLayoutOnSelector 0
268 #define kCaseSensitiveLayoutType 33
269 #define kCaseSensitiveSpacingOffSelector 3
270 #define kCaseSensitiveSpacingOnSelector 2
271 #define kContextualAlternatesOffSelector 1
272 #define kContextualAlternatesOnSelector 0
273 #define kContextualAlternatesType 36
274 #define kContextualLigaturesOffSelector 19
275 #define kContextualLigaturesOnSelector 18
276 #define kContextualSwashAlternatesOffSelector 5
277 #define kContextualSwashAlternatesOnSelector 4
278 #define kDefaultLowerCaseSelector 0
279 #define kDefaultUpperCaseSelector 0
280 #define kHistoricalLigaturesOffSelector 21
281 #define kHistoricalLigaturesOnSelector 20
282 #define kHojoCharactersSelector 12
283 #define kJIS2004CharactersSelector 11
284 #define kLowerCasePetiteCapsSelector 2
285 #define kLowerCaseSmallCapsSelector 1
286 #define kLowerCaseType 37
287 #define kMathematicalGreekOffSelector 11
288 #define kMathematicalGreekOnSelector 10
289 #define kNLCCharactersSelector 13
290 #define kQuarterWidthTextSelector 4
291 #define kScientificInferiorsSelector 4
292 #define kStylisticAltEightOffSelector 17
293 #define kStylisticAltEightOnSelector 16
294 #define kStylisticAltEighteenOffSelector 37
295 #define kStylisticAltEighteenOnSelector 36
296 #define kStylisticAltElevenOffSelector 23
297 #define kStylisticAltElevenOnSelector 22
298 #define kStylisticAltFifteenOffSelector 31
299 #define kStylisticAltFifteenOnSelector 30
300 #define kStylisticAltFiveOffSelector 11
301 #define kStylisticAltFiveOnSelector 10
302 #define kStylisticAltFourOffSelector 9
303 #define kStylisticAltFourOnSelector 8
304 #define kStylisticAltFourteenOffSelector 29
305 #define kStylisticAltFourteenOnSelector 28
306 #define kStylisticAltNineOffSelector 19
307 #define kStylisticAltNineOnSelector 18
308 #define kStylisticAltNineteenOffSelector 39
309 #define kStylisticAltNineteenOnSelector 38
310 #define kStylisticAltOneOffSelector 3
311 #define kStylisticAltOneOnSelector 2
312 #define kStylisticAltSevenOffSelector 15
313 #define kStylisticAltSevenOnSelector 14
314 #define kStylisticAltSeventeenOffSelector 35
315 #define kStylisticAltSeventeenOnSelector 34
316 #define kStylisticAltSixOffSelector 13
317 #define kStylisticAltSixOnSelector 12
318 #define kStylisticAltSixteenOffSelector 33
319 #define kStylisticAltSixteenOnSelector 32
320 #define kStylisticAltTenOffSelector 21
321 #define kStylisticAltTenOnSelector 20
322 #define kStylisticAltThirteenOffSelector 27
323 #define kStylisticAltThirteenOnSelector 26
324 #define kStylisticAltThreeOffSelector 7
325 #define kStylisticAltThreeOnSelector 6
326 #define kStylisticAltTwelveOffSelector 25
327 #define kStylisticAltTwelveOnSelector 24
328 #define kStylisticAltTwentyOffSelector 41
329 #define kStylisticAltTwentyOnSelector 40
330 #define kStylisticAltTwoOffSelector 5
331 #define kStylisticAltTwoOnSelector 4
332 #define kStylisticAlternativesType 35
333 #define kSwashAlternatesOffSelector 3
334 #define kSwashAlternatesOnSelector 2
335 #define kThirdWidthTextSelector 3
336 #define kTraditionalNamesCharactersSelector 14
337 #define kUpperCasePetiteCapsSelector 2
338 #define kUpperCaseSmallCapsSelector 1
339 #define kUpperCaseType 38
341 /* Table data courtesy of Apple. */
342 static const struct feature_mapping_t
{
343 FourCharCode otFeatureTag
;
344 uint16_t aatFeatureType
;
345 uint16_t selectorToEnable
;
346 uint16_t selectorToDisable
;
347 } feature_mappings
[] = {
348 { 'c2pc', kUpperCaseType
, kUpperCasePetiteCapsSelector
, kDefaultUpperCaseSelector
},
349 { 'c2sc', kUpperCaseType
, kUpperCaseSmallCapsSelector
, kDefaultUpperCaseSelector
},
350 { 'calt', kContextualAlternatesType
, kContextualAlternatesOnSelector
, kContextualAlternatesOffSelector
},
351 { 'case', kCaseSensitiveLayoutType
, kCaseSensitiveLayoutOnSelector
, kCaseSensitiveLayoutOffSelector
},
352 { 'clig', kLigaturesType
, kContextualLigaturesOnSelector
, kContextualLigaturesOffSelector
},
353 { 'cpsp', kCaseSensitiveLayoutType
, kCaseSensitiveSpacingOnSelector
, kCaseSensitiveSpacingOffSelector
},
354 { 'cswh', kContextualAlternatesType
, kContextualSwashAlternatesOnSelector
, kContextualSwashAlternatesOffSelector
},
355 { 'dlig', kLigaturesType
, kRareLigaturesOnSelector
, kRareLigaturesOffSelector
},
356 { 'expt', kCharacterShapeType
, kExpertCharactersSelector
, 16 },
357 { 'frac', kFractionsType
, kDiagonalFractionsSelector
, kNoFractionsSelector
},
358 { 'fwid', kTextSpacingType
, kMonospacedTextSelector
, 7 },
359 { 'halt', kTextSpacingType
, kAltHalfWidthTextSelector
, 7 },
360 { 'hist', kLigaturesType
, kHistoricalLigaturesOnSelector
, kHistoricalLigaturesOffSelector
},
361 { 'hkna', kAlternateKanaType
, kAlternateHorizKanaOnSelector
, kAlternateHorizKanaOffSelector
, },
362 { 'hlig', kLigaturesType
, kHistoricalLigaturesOnSelector
, kHistoricalLigaturesOffSelector
},
363 { 'hngl', kTransliterationType
, kHanjaToHangulSelector
, kNoTransliterationSelector
},
364 { 'hojo', kCharacterShapeType
, kHojoCharactersSelector
, 16 },
365 { 'hwid', kTextSpacingType
, kHalfWidthTextSelector
, 7 },
366 { 'ital', kItalicCJKRomanType
, kCJKItalicRomanOnSelector
, kCJKItalicRomanOffSelector
},
367 { 'jp04', kCharacterShapeType
, kJIS2004CharactersSelector
, 16 },
368 { 'jp78', kCharacterShapeType
, kJIS1978CharactersSelector
, 16 },
369 { 'jp83', kCharacterShapeType
, kJIS1983CharactersSelector
, 16 },
370 { 'jp90', kCharacterShapeType
, kJIS1990CharactersSelector
, 16 },
371 { 'liga', kLigaturesType
, kCommonLigaturesOnSelector
, kCommonLigaturesOffSelector
},
372 { 'lnum', kNumberCaseType
, kUpperCaseNumbersSelector
, 2 },
373 { 'mgrk', kMathematicalExtrasType
, kMathematicalGreekOnSelector
, kMathematicalGreekOffSelector
},
374 { 'nlck', kCharacterShapeType
, kNLCCharactersSelector
, 16 },
375 { 'onum', kNumberCaseType
, kLowerCaseNumbersSelector
, 2 },
376 { 'ordn', kVerticalPositionType
, kOrdinalsSelector
, kNormalPositionSelector
},
377 { 'palt', kTextSpacingType
, kAltProportionalTextSelector
, 7 },
378 { 'pcap', kLowerCaseType
, kLowerCasePetiteCapsSelector
, kDefaultLowerCaseSelector
},
379 { 'pkna', kTextSpacingType
, kProportionalTextSelector
, 7 },
380 { 'pnum', kNumberSpacingType
, kProportionalNumbersSelector
, 4 },
381 { 'pwid', kTextSpacingType
, kProportionalTextSelector
, 7 },
382 { 'qwid', kTextSpacingType
, kQuarterWidthTextSelector
, 7 },
383 { 'ruby', kRubyKanaType
, kRubyKanaOnSelector
, kRubyKanaOffSelector
},
384 { 'sinf', kVerticalPositionType
, kScientificInferiorsSelector
, kNormalPositionSelector
},
385 { 'smcp', kLowerCaseType
, kLowerCaseSmallCapsSelector
, kDefaultLowerCaseSelector
},
386 { 'smpl', kCharacterShapeType
, kSimplifiedCharactersSelector
, 16 },
387 { 'ss01', kStylisticAlternativesType
, kStylisticAltOneOnSelector
, kStylisticAltOneOffSelector
},
388 { 'ss02', kStylisticAlternativesType
, kStylisticAltTwoOnSelector
, kStylisticAltTwoOffSelector
},
389 { 'ss03', kStylisticAlternativesType
, kStylisticAltThreeOnSelector
, kStylisticAltThreeOffSelector
},
390 { 'ss04', kStylisticAlternativesType
, kStylisticAltFourOnSelector
, kStylisticAltFourOffSelector
},
391 { 'ss05', kStylisticAlternativesType
, kStylisticAltFiveOnSelector
, kStylisticAltFiveOffSelector
},
392 { 'ss06', kStylisticAlternativesType
, kStylisticAltSixOnSelector
, kStylisticAltSixOffSelector
},
393 { 'ss07', kStylisticAlternativesType
, kStylisticAltSevenOnSelector
, kStylisticAltSevenOffSelector
},
394 { 'ss08', kStylisticAlternativesType
, kStylisticAltEightOnSelector
, kStylisticAltEightOffSelector
},
395 { 'ss09', kStylisticAlternativesType
, kStylisticAltNineOnSelector
, kStylisticAltNineOffSelector
},
396 { 'ss10', kStylisticAlternativesType
, kStylisticAltTenOnSelector
, kStylisticAltTenOffSelector
},
397 { 'ss11', kStylisticAlternativesType
, kStylisticAltElevenOnSelector
, kStylisticAltElevenOffSelector
},
398 { 'ss12', kStylisticAlternativesType
, kStylisticAltTwelveOnSelector
, kStylisticAltTwelveOffSelector
},
399 { 'ss13', kStylisticAlternativesType
, kStylisticAltThirteenOnSelector
, kStylisticAltThirteenOffSelector
},
400 { 'ss14', kStylisticAlternativesType
, kStylisticAltFourteenOnSelector
, kStylisticAltFourteenOffSelector
},
401 { 'ss15', kStylisticAlternativesType
, kStylisticAltFifteenOnSelector
, kStylisticAltFifteenOffSelector
},
402 { 'ss16', kStylisticAlternativesType
, kStylisticAltSixteenOnSelector
, kStylisticAltSixteenOffSelector
},
403 { 'ss17', kStylisticAlternativesType
, kStylisticAltSeventeenOnSelector
, kStylisticAltSeventeenOffSelector
},
404 { 'ss18', kStylisticAlternativesType
, kStylisticAltEighteenOnSelector
, kStylisticAltEighteenOffSelector
},
405 { 'ss19', kStylisticAlternativesType
, kStylisticAltNineteenOnSelector
, kStylisticAltNineteenOffSelector
},
406 { 'ss20', kStylisticAlternativesType
, kStylisticAltTwentyOnSelector
, kStylisticAltTwentyOffSelector
},
407 { 'subs', kVerticalPositionType
, kInferiorsSelector
, kNormalPositionSelector
},
408 { 'sups', kVerticalPositionType
, kSuperiorsSelector
, kNormalPositionSelector
},
409 { 'swsh', kContextualAlternatesType
, kSwashAlternatesOnSelector
, kSwashAlternatesOffSelector
},
410 { 'titl', kStyleOptionsType
, kTitlingCapsSelector
, kNoStyleOptionsSelector
},
411 { 'tnam', kCharacterShapeType
, kTraditionalNamesCharactersSelector
, 16 },
412 { 'tnum', kNumberSpacingType
, kMonospacedNumbersSelector
, 4 },
413 { 'trad', kCharacterShapeType
, kTraditionalCharactersSelector
, 16 },
414 { 'twid', kTextSpacingType
, kThirdWidthTextSelector
, 7 },
415 { 'unic', kLetterCaseType
, 14, 15 },
416 { 'valt', kTextSpacingType
, kAltProportionalTextSelector
, 7 },
417 { 'vert', kVerticalSubstitutionType
, kSubstituteVerticalFormsOnSelector
, kSubstituteVerticalFormsOffSelector
},
418 { 'vhal', kTextSpacingType
, kAltHalfWidthTextSelector
, 7 },
419 { 'vkna', kAlternateKanaType
, kAlternateVertKanaOnSelector
, kAlternateVertKanaOffSelector
},
420 { 'vpal', kTextSpacingType
, kAltProportionalTextSelector
, 7 },
421 { 'vrt2', kVerticalSubstitutionType
, kSubstituteVerticalFormsOnSelector
, kSubstituteVerticalFormsOffSelector
},
422 { 'zero', kTypographicExtrasType
, kSlashedZeroOnSelector
, kSlashedZeroOffSelector
},
426 _hb_feature_mapping_cmp (const void *key_
, const void *entry_
)
428 unsigned int key
= * (unsigned int *) key_
;
429 const feature_mapping_t
* entry
= (const feature_mapping_t
*) entry_
;
430 return key
< entry
->otFeatureTag
? -1 :
431 key
> entry
->otFeatureTag
? 1 :
436 _hb_coretext_shape (hb_shape_plan_t
*shape_plan
,
439 const hb_feature_t
*features
,
440 unsigned int num_features
)
442 hb_face_t
*face
= font
->face
;
443 hb_coretext_shaper_face_data_t
*face_data
= HB_SHAPER_DATA_GET (face
);
444 hb_coretext_shaper_font_data_t
*font_data
= HB_SHAPER_DATA_GET (font
);
446 /* Attach marks to their bases, to match the 'ot' shaper.
447 * Adapted from hb-ot-shape:hb_form_clusters().
448 * Note that this only makes us be closer to the 'ot' shaper,
449 * but by no means the same. For example, if there's
450 * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will
451 * continue pointing to B2 even though B2 was merged into B1's
454 hb_unicode_funcs_t
*unicode
= buffer
->unicode
;
455 unsigned int count
= buffer
->len
;
456 hb_glyph_info_t
*info
= buffer
->info
;
457 for (unsigned int i
= 1; i
< count
; i
++)
458 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode
->general_category (info
[i
].codepoint
)))
459 buffer
->merge_clusters (i
- 1, i
+ 1);
462 hb_auto_array_t
<feature_record_t
> feature_records
;
463 hb_auto_array_t
<range_record_t
> range_records
;
467 * (copied + modified from code from hb-uniscribe.cc)
471 /* Sort features by start/end events. */
472 hb_auto_array_t
<feature_event_t
> feature_events
;
473 for (unsigned int i
= 0; i
< num_features
; i
++)
475 const feature_mapping_t
* mapping
= (const feature_mapping_t
*) bsearch (&features
[i
].tag
,
477 ARRAY_LENGTH (feature_mappings
),
478 sizeof (feature_mappings
[0]),
479 _hb_feature_mapping_cmp
);
483 active_feature_t feature
;
484 feature
.rec
.feature
= mapping
->aatFeatureType
;
485 feature
.rec
.setting
= features
[i
].value
? mapping
->selectorToEnable
: mapping
->selectorToDisable
;
488 feature_event_t
*event
;
490 event
= feature_events
.push ();
491 if (unlikely (!event
))
493 event
->index
= features
[i
].start
;
495 event
->feature
= feature
;
497 event
= feature_events
.push ();
498 if (unlikely (!event
))
500 event
->index
= features
[i
].end
;
501 event
->start
= false;
502 event
->feature
= feature
;
504 feature_events
.qsort ();
505 /* Add a strategic final event. */
507 active_feature_t feature
;
508 feature
.rec
.feature
= HB_TAG_NONE
;
509 feature
.rec
.setting
= 0;
510 feature
.order
= num_features
+ 1;
512 feature_event_t
*event
= feature_events
.push ();
513 if (unlikely (!event
))
515 event
->index
= 0; /* This value does magic. */
516 event
->start
= false;
517 event
->feature
= feature
;
520 /* Scan events and save features for each range. */
521 hb_auto_array_t
<active_feature_t
> active_features
;
522 unsigned int last_index
= 0;
523 for (unsigned int i
= 0; i
< feature_events
.len
; i
++)
525 feature_event_t
*event
= &feature_events
[i
];
527 if (event
->index
!= last_index
)
529 /* Save a snapshot of active features and the range. */
530 range_record_t
*range
= range_records
.push ();
531 if (unlikely (!range
))
534 if (active_features
.len
)
536 CFMutableArrayRef features_array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
538 /* TODO sort and resolve conflicting features? */
539 /* active_features.qsort (); */
540 for (unsigned int j
= 0; j
< active_features
.len
; j
++)
542 CFStringRef keys
[2] = {
543 kCTFontFeatureTypeIdentifierKey
,
544 kCTFontFeatureSelectorIdentifierKey
546 CFNumberRef values
[2] = {
547 CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &active_features
[j
].rec
.feature
),
548 CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &active_features
[j
].rec
.setting
)
550 CFDictionaryRef dict
= CFDictionaryCreate (kCFAllocatorDefault
,
551 (const void **) keys
,
552 (const void **) values
,
554 &kCFTypeDictionaryKeyCallBacks
,
555 &kCFTypeDictionaryValueCallBacks
);
556 CFRelease (values
[0]);
557 CFRelease (values
[1]);
559 CFArrayAppendValue (features_array
, dict
);
564 CFDictionaryRef attributes
= CFDictionaryCreate (kCFAllocatorDefault
,
565 (const void **) &kCTFontFeatureSettingsAttribute
,
566 (const void **) &features_array
,
568 &kCFTypeDictionaryKeyCallBacks
,
569 &kCFTypeDictionaryValueCallBacks
);
570 CFRelease (features_array
);
572 CTFontDescriptorRef font_desc
= CTFontDescriptorCreateWithAttributes (attributes
);
573 CFRelease (attributes
);
575 range
->font
= CTFontCreateCopyWithAttributes (font_data
->ct_font
, 0.0, NULL
, font_desc
);
576 CFRelease (font_desc
);
583 range
->index_first
= last_index
;
584 range
->index_last
= event
->index
- 1;
586 last_index
= event
->index
;
590 active_feature_t
*feature
= active_features
.push ();
591 if (unlikely (!feature
))
593 *feature
= event
->feature
;
595 active_feature_t
*feature
= active_features
.find (&event
->feature
);
597 active_features
.remove (feature
- active_features
.array
);
601 if (!range_records
.len
) /* No active feature found. */
610 unsigned int scratch_size
;
611 hb_buffer_t::scratch_buffer_t
*scratch
= buffer
->get_scratch_buffer (&scratch_size
);
613 #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \
614 Type *name = (Type *) scratch; \
616 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
617 if (unlikely (_consumed > scratch_size)) \
622 scratch += _consumed; \
623 scratch_size -= _consumed; \
626 ALLOCATE_ARRAY (UniChar
, pchars
, buffer
->len
* 2, /*nothing*/);
627 unsigned int chars_len
= 0;
628 for (unsigned int i
= 0; i
< buffer
->len
; i
++) {
629 hb_codepoint_t c
= buffer
->info
[i
].codepoint
;
630 if (likely (c
<= 0xFFFFu
))
631 pchars
[chars_len
++] = c
;
632 else if (unlikely (c
> 0x10FFFFu
))
633 pchars
[chars_len
++] = 0xFFFDu
;
635 pchars
[chars_len
++] = 0xD800u
+ ((c
- 0x10000u
) >> 10);
636 pchars
[chars_len
++] = 0xDC00u
+ ((c
- 0x10000u
) & ((1 << 10) - 1));
640 ALLOCATE_ARRAY (unsigned int, log_clusters
, chars_len
, /*nothing*/);
642 for (unsigned int i
= 0; i
< buffer
->len
; i
++)
644 hb_codepoint_t c
= buffer
->info
[i
].codepoint
;
645 unsigned int cluster
= buffer
->info
[i
].cluster
;
646 log_clusters
[chars_len
++] = cluster
;
647 if (hb_in_range (c
, 0x10000u
, 0x10FFFFu
))
648 log_clusters
[chars_len
++] = cluster
; /* Surrogates. */
653 DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
659 CFStringRef string_ref
= NULL
;
660 CTLineRef line
= NULL
;
665 DEBUG_MSG (CORETEXT
, buffer
, "Buffer resize");
666 /* string_ref uses the scratch-buffer for backing store, and line references
667 * string_ref (via attr_string). We must release those before resizing buffer. */
670 CFRelease (string_ref
);
675 /* Get previous start-of-scratch-area, that we use later for readjusting
676 * our existing scratch arrays. */
677 unsigned int old_scratch_used
;
678 hb_buffer_t::scratch_buffer_t
*old_scratch
;
679 old_scratch
= buffer
->get_scratch_buffer (&old_scratch_used
);
680 old_scratch_used
= scratch
- old_scratch
;
682 if (unlikely (!buffer
->ensure (buffer
->allocated
* 2)))
683 FAIL ("Buffer resize failed");
685 /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the
686 * cleanest way to do without completely restructuring the rest of this shaper. */
687 scratch
= buffer
->get_scratch_buffer (&scratch_size
);
688 pchars
= reinterpret_cast<UniChar
*> (((char *) scratch
+ ((char *) pchars
- (char *) old_scratch
)));
689 log_clusters
= reinterpret_cast<unsigned int *> (((char *) scratch
+ ((char *) log_clusters
- (char *) old_scratch
)));
690 scratch
+= old_scratch_used
;
691 scratch_size
-= old_scratch_used
;
695 string_ref
= CFStringCreateWithCharactersNoCopy (NULL
,
698 if (unlikely (!string_ref
))
699 FAIL ("CFStringCreateWithCharactersNoCopy failed");
701 /* Create an attributed string, populate it, and create a line from it, then release attributed string. */
703 CFMutableAttributedStringRef attr_string
= CFAttributedStringCreateMutable (kCFAllocatorDefault
,
705 if (unlikely (!attr_string
))
706 FAIL ("CFAttributedStringCreateMutable failed");
707 CFAttributedStringReplaceString (attr_string
, CFRangeMake (0, 0), string_ref
);
708 if (HB_DIRECTION_IS_VERTICAL (buffer
->props
.direction
))
710 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (0, chars_len
),
711 kCTVerticalFormsAttributeName
, kCFBooleanTrue
);
714 if (buffer
->props
.language
)
716 /* What's the iOS equivalent of this check?
717 * The symbols was introduced in iOS 7.0.
718 * At any rate, our fallback is safe and works fine. */
719 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
720 # define kCTLanguageAttributeName CFSTR ("NSLanguage")
722 CFStringRef lang
= CFStringCreateWithCStringNoCopy (kCFAllocatorDefault
,
723 hb_language_to_string (buffer
->props
.language
),
724 kCFStringEncodingUTF8
,
726 if (unlikely (!lang
))
727 FAIL ("CFStringCreateWithCStringNoCopy failed");
728 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (0, chars_len
),
729 kCTLanguageAttributeName
, lang
);
732 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (0, chars_len
),
733 kCTFontAttributeName
, font_data
->ct_font
);
737 unsigned int start
= 0;
738 range_record_t
*last_range
= &range_records
[0];
739 for (unsigned int k
= 0; k
< chars_len
; k
++)
741 range_record_t
*range
= last_range
;
742 while (log_clusters
[k
] < range
->index_first
)
744 while (log_clusters
[k
] > range
->index_last
)
746 if (range
!= last_range
)
748 if (last_range
->font
)
749 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (start
, k
- start
),
750 kCTFontAttributeName
, last_range
->font
);
757 if (start
!= chars_len
&& last_range
->font
)
758 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (start
, chars_len
- start
),
759 kCTFontAttributeName
, last_range
->font
);
762 int level
= HB_DIRECTION_IS_FORWARD (buffer
->props
.direction
) ? 0 : 1;
763 CFNumberRef level_number
= CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &level
);
764 CFDictionaryRef options
= CFDictionaryCreate (kCFAllocatorDefault
,
765 (const void **) &kCTTypesetterOptionForcedEmbeddingLevel
,
766 (const void **) &level_number
,
768 &kCFTypeDictionaryKeyCallBacks
,
769 &kCFTypeDictionaryValueCallBacks
);
770 if (unlikely (!options
))
771 FAIL ("CFDictionaryCreate failed");
773 CTTypesetterRef typesetter
= CTTypesetterCreateWithAttributedStringAndOptions (attr_string
, options
);
775 CFRelease (attr_string
);
776 if (unlikely (!typesetter
))
777 FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed");
779 line
= CTTypesetterCreateLine (typesetter
, CFRangeMake(0, 0));
780 CFRelease (typesetter
);
781 if (unlikely (!line
))
782 FAIL ("CTTypesetterCreateLine failed");
785 CFArrayRef glyph_runs
= CTLineGetGlyphRuns (line
);
786 unsigned int num_runs
= CFArrayGetCount (glyph_runs
);
787 DEBUG_MSG (CORETEXT
, NULL
, "Num runs: %d", num_runs
);
790 uint32_t status_and
= ~0, status_or
= 0;
791 double advances_so_far
= 0;
792 /* For right-to-left runs, CoreText returns the glyphs positioned such that
793 * any trailing whitespace is to the left of (0,0). Adjust coordinate system
794 * to fix for that. Test with any RTL string with trailing spaces.
795 * https://code.google.com/p/chromium/issues/detail?id=469028
797 if (HB_DIRECTION_IS_BACKWARD (buffer
->props
.direction
))
799 advances_so_far
-= CTLineGetTrailingWhitespaceWidth (line
);
800 if (HB_DIRECTION_IS_VERTICAL (buffer
->props
.direction
))
801 advances_so_far
= -advances_so_far
;
804 const CFRange range_all
= CFRangeMake (0, 0);
806 for (unsigned int i
= 0; i
< num_runs
; i
++)
808 CTRunRef run
= static_cast<CTRunRef
>(CFArrayGetValueAtIndex (glyph_runs
, i
));
809 CTRunStatus run_status
= CTRunGetStatus (run
);
810 status_or
|= run_status
;
811 status_and
&= run_status
;
812 DEBUG_MSG (CORETEXT
, run
, "CTRunStatus: %x", run_status
);
813 double run_advance
= CTRunGetTypographicBounds (run
, range_all
, NULL
, NULL
, NULL
);
814 if (HB_DIRECTION_IS_VERTICAL (buffer
->props
.direction
))
815 run_advance
= -run_advance
;
816 DEBUG_MSG (CORETEXT
, run
, "Run advance: %g", run_advance
);
818 /* CoreText does automatic font fallback (AKA "cascading") for characters
819 * not supported by the requested font, and provides no way to turn it off,
820 * so we must detect if the returned run uses a font other than the requested
821 * one and fill in the buffer with .notdef glyphs instead of random glyph
822 * indices from a different font.
824 CFDictionaryRef attributes
= CTRunGetAttributes (run
);
825 CTFontRef run_ct_font
= static_cast<CTFontRef
>(CFDictionaryGetValue (attributes
, kCTFontAttributeName
));
826 if (!CFEqual (run_ct_font
, font_data
->ct_font
))
828 /* The run doesn't use our main font instance. We have to figure out
829 * whether font fallback happened, or this is just CoreText giving us
830 * another CTFont using the same underlying CGFont. CoreText seems
831 * to do that in a variety of situations, one of which being vertical
832 * text, but also perhaps for caching reasons.
834 * First, see if it uses any of our subfonts created to set font features...
836 * Next, compare the CGFont to the one we used to create our fonts.
837 * Even this doesn't work all the time.
839 * Finally, we compare PS names, which I don't think are unique...
841 * Looks like if we really want to be sure here we have to modify the
842 * font to change the name table, similar to what we do in the uniscribe
845 * However, even that wouldn't work if we were passed in the CGFont to
848 * Webkit uses a slightly different approach: it installs LastResort
849 * as fallback chain, and then checks PS name of used font against
850 * LastResort. That one is safe for any font except for LastResort,
851 * as opposed to ours, which can fail if we are using any uninstalled
852 * font that has the same name as an installed font.
854 * See: http://github.com/behdad/harfbuzz/pull/36
856 bool matched
= false;
857 for (unsigned int i
= 0; i
< range_records
.len
; i
++)
858 if (range_records
[i
].font
&& CFEqual (run_ct_font
, range_records
[i
].font
))
865 CGFontRef run_cg_font
= CTFontCopyGraphicsFont (run_ct_font
, 0);
868 matched
= CFEqual (run_cg_font
, face_data
);
869 CFRelease (run_cg_font
);
874 CFStringRef font_ps_name
= CTFontCopyName (font_data
->ct_font
, kCTFontPostScriptNameKey
);
875 CFStringRef run_ps_name
= CTFontCopyName (run_ct_font
, kCTFontPostScriptNameKey
);
876 CFComparisonResult result
= CFStringCompare (run_ps_name
, font_ps_name
, 0);
877 CFRelease (run_ps_name
);
878 CFRelease (font_ps_name
);
879 if (result
== kCFCompareEqualTo
)
884 CFRange range
= CTRunGetStringRange (run
);
885 DEBUG_MSG (CORETEXT
, run
, "Run used fallback font: %ld..%ld",
886 range
.location
, range
.location
+ range
.length
);
887 if (!buffer
->ensure_inplace (buffer
->len
+ range
.length
))
888 goto resize_and_retry
;
889 hb_glyph_info_t
*info
= buffer
->info
+ buffer
->len
;
891 hb_codepoint_t notdef
= 0;
892 hb_direction_t dir
= buffer
->props
.direction
;
893 hb_position_t x_advance
, y_advance
, x_offset
, y_offset
;
894 hb_font_get_glyph_advance_for_direction (font
, notdef
, dir
, &x_advance
, &y_advance
);
895 hb_font_get_glyph_origin_for_direction (font
, notdef
, dir
, &x_offset
, &y_offset
);
896 hb_position_t advance
= x_advance
+ y_advance
;
897 x_offset
= -x_offset
;
898 y_offset
= -y_offset
;
900 unsigned int old_len
= buffer
->len
;
901 for (CFIndex j
= range
.location
; j
< range
.location
+ range
.length
; j
++)
903 UniChar ch
= CFStringGetCharacterAtIndex (string_ref
, j
);
904 if (hb_in_range
<UniChar
> (ch
, 0xDC00u
, 0xDFFFu
) && range
.location
< j
)
906 ch
= CFStringGetCharacterAtIndex (string_ref
, j
- 1);
907 if (hb_in_range
<UniChar
> (ch
, 0xD800u
, 0xDBFFu
))
908 /* This is the second of a surrogate pair. Don't need .notdef
912 if (buffer
->unicode
->is_default_ignorable (ch
))
915 info
->codepoint
= notdef
;
916 info
->cluster
= log_clusters
[j
];
918 info
->mask
= advance
;
919 info
->var1
.i32
= x_offset
;
920 info
->var2
.i32
= y_offset
;
925 if (HB_DIRECTION_IS_BACKWARD (buffer
->props
.direction
))
926 buffer
->reverse_range (old_len
, buffer
->len
);
927 advances_so_far
+= run_advance
;
932 unsigned int num_glyphs
= CTRunGetGlyphCount (run
);
936 if (!buffer
->ensure_inplace (buffer
->len
+ num_glyphs
))
937 goto resize_and_retry
;
939 hb_glyph_info_t
*run_info
= buffer
->info
+ buffer
->len
;
941 /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always
942 * succeed, and so copying data to our own buffer will be rare. Reports
943 * have it that this changed in OS X 10.10 Yosemite, and NULL is returned
944 * frequently. At any rate, we can test that codepath by setting USE_PTR
949 #define SCRATCH_SAVE() \
950 unsigned int scratch_size_saved = scratch_size; \
951 hb_buffer_t::scratch_buffer_t *scratch_saved = scratch
953 #define SCRATCH_RESTORE() \
954 scratch_size = scratch_size_saved; \
955 scratch = scratch_saved;
959 const CGGlyph
* glyphs
= USE_PTR
? CTRunGetGlyphsPtr (run
) : NULL
;
961 ALLOCATE_ARRAY (CGGlyph
, glyph_buf
, num_glyphs
, goto resize_and_retry
);
962 CTRunGetGlyphs (run
, range_all
, glyph_buf
);
965 const CFIndex
* string_indices
= USE_PTR
? CTRunGetStringIndicesPtr (run
) : NULL
;
966 if (!string_indices
) {
967 ALLOCATE_ARRAY (CFIndex
, index_buf
, num_glyphs
, goto resize_and_retry
);
968 CTRunGetStringIndices (run
, range_all
, index_buf
);
969 string_indices
= index_buf
;
971 hb_glyph_info_t
*info
= run_info
;
972 for (unsigned int j
= 0; j
< num_glyphs
; j
++)
974 info
->codepoint
= glyphs
[j
];
975 info
->cluster
= log_clusters
[string_indices
[j
]];
982 * Note that CoreText does not return advances for glyphs. As such,
983 * for all but last glyph, we use the delta position to next glyph as
984 * advance (in the advance direction only), and for last glyph we set
985 * whatever is needed to make the whole run's advance add up. */
987 const CGPoint
* positions
= USE_PTR
? CTRunGetPositionsPtr (run
) : NULL
;
989 ALLOCATE_ARRAY (CGPoint
, position_buf
, num_glyphs
, goto resize_and_retry
);
990 CTRunGetPositions (run
, range_all
, position_buf
);
991 positions
= position_buf
;
993 hb_glyph_info_t
*info
= run_info
;
994 CGFloat x_mult
= font_data
->x_mult
, y_mult
= font_data
->y_mult
;
995 if (HB_DIRECTION_IS_HORIZONTAL (buffer
->props
.direction
))
997 hb_position_t x_offset
= (positions
[0].x
- advances_so_far
) * x_mult
;
998 for (unsigned int j
= 0; j
< num_glyphs
; j
++)
1001 if (likely (j
+ 1 < num_glyphs
))
1002 advance
= positions
[j
+ 1].x
- positions
[j
].x
;
1003 else /* last glyph */
1004 advance
= run_advance
- (positions
[j
].x
- positions
[0].x
);
1005 info
->mask
= advance
* x_mult
;
1006 info
->var1
.i32
= x_offset
;
1007 info
->var2
.i32
= positions
[j
].y
* y_mult
;
1013 hb_position_t y_offset
= (positions
[0].y
- advances_so_far
) * y_mult
;
1014 for (unsigned int j
= 0; j
< num_glyphs
; j
++)
1017 if (likely (j
+ 1 < num_glyphs
))
1018 advance
= positions
[j
+ 1].y
- positions
[j
].y
;
1019 else /* last glyph */
1020 advance
= run_advance
- (positions
[j
].y
- positions
[0].y
);
1021 info
->mask
= advance
* y_mult
;
1022 info
->var1
.i32
= positions
[j
].x
* x_mult
;
1023 info
->var2
.i32
= y_offset
;
1028 advances_so_far
+= run_advance
;
1030 #undef SCRATCH_RESTORE
1033 #undef ALLOCATE_ARRAY
1035 buffer
->len
+= num_glyphs
;
1038 /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel,
1039 * or if it does, it doesn't resepct it. So we get runs with wrong
1040 * directions. As such, disable the assert... It wouldn't crash, but
1041 * cursoring will be off...
1043 * http://crbug.com/419769
1047 /* Make sure all runs had the expected direction. */
1048 bool backward
= HB_DIRECTION_IS_BACKWARD (buffer
->props
.direction
);
1049 assert (bool (status_and
& kCTRunStatusRightToLeft
) == backward
);
1050 assert (bool (status_or
& kCTRunStatusRightToLeft
) == backward
);
1053 buffer
->clear_positions ();
1055 unsigned int count
= buffer
->len
;
1056 hb_glyph_info_t
*info
= buffer
->info
;
1057 hb_glyph_position_t
*pos
= buffer
->pos
;
1058 if (HB_DIRECTION_IS_HORIZONTAL (buffer
->props
.direction
))
1059 for (unsigned int i
= 0; i
< count
; i
++)
1061 pos
->x_advance
= info
->mask
;
1062 pos
->x_offset
= info
->var1
.i32
;
1063 pos
->y_offset
= info
->var2
.i32
;
1067 for (unsigned int i
= 0; i
< count
; i
++)
1069 pos
->y_advance
= info
->mask
;
1070 pos
->x_offset
= info
->var1
.i32
;
1071 pos
->y_offset
= info
->var2
.i32
;
1075 /* Fix up clusters so that we never return out-of-order indices;
1076 * if core text has reordered glyphs, we'll merge them to the
1077 * beginning of the reordered cluster. CoreText is nice enough
1078 * to tell us whenever it has produced nonmonotonic results...
1079 * Note that we assume the input clusters were nonmonotonic to
1082 * This does *not* mean we'll form the same clusters as Uniscribe
1083 * or the native OT backend, only that the cluster indices will be
1084 * monotonic in the output buffer. */
1085 if (count
> 1 && (status_or
& kCTRunStatusNonMonotonic
))
1087 hb_glyph_info_t
*info
= buffer
->info
;
1088 if (HB_DIRECTION_IS_FORWARD (buffer
->props
.direction
))
1090 unsigned int cluster
= info
[count
- 1].cluster
;
1091 for (unsigned int i
= count
- 1; i
> 0; i
--)
1093 cluster
= MIN (cluster
, info
[i
- 1].cluster
);
1094 info
[i
- 1].cluster
= cluster
;
1099 unsigned int cluster
= info
[0].cluster
;
1100 for (unsigned int i
= 1; i
< count
; i
++)
1102 cluster
= MIN (cluster
, info
[i
].cluster
);
1103 info
[i
].cluster
= cluster
;
1113 CFRelease (string_ref
);
1117 for (unsigned int i
= 0; i
< range_records
.len
; i
++)
1118 if (range_records
[i
].font
)
1119 CFRelease (range_records
[i
].font
);
1129 HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat
, face
)
1130 HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat
, font
)
1137 struct hb_coretext_aat_shaper_face_data_t
{};
1139 hb_coretext_aat_shaper_face_data_t
*
1140 _hb_coretext_aat_shaper_face_data_create (hb_face_t
*face
)
1142 hb_blob_t
*mort_blob
= face
->reference_table (HB_CORETEXT_TAG_MORT
);
1143 /* Umm, we just reference the table to check whether it exists.
1144 * Maybe add better API for this? */
1145 if (!hb_blob_get_length (mort_blob
))
1147 hb_blob_destroy (mort_blob
);
1148 mort_blob
= face
->reference_table (HB_CORETEXT_TAG_MORX
);
1149 if (!hb_blob_get_length (mort_blob
))
1151 hb_blob_destroy (mort_blob
);
1155 hb_blob_destroy (mort_blob
);
1157 return hb_coretext_shaper_face_data_ensure (face
) ? (hb_coretext_aat_shaper_face_data_t
*) HB_SHAPER_DATA_SUCCEEDED
: NULL
;
1161 _hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t
*data HB_UNUSED
)
1170 struct hb_coretext_aat_shaper_font_data_t
{};
1172 hb_coretext_aat_shaper_font_data_t
*
1173 _hb_coretext_aat_shaper_font_data_create (hb_font_t
*font
)
1175 return hb_coretext_shaper_font_data_ensure (font
) ? (hb_coretext_aat_shaper_font_data_t
*) HB_SHAPER_DATA_SUCCEEDED
: NULL
;
1179 _hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_shaper_font_data_t
*data HB_UNUSED
)
1185 * shaper shape_plan data
1188 struct hb_coretext_aat_shaper_shape_plan_data_t
{};
1190 hb_coretext_aat_shaper_shape_plan_data_t
*
1191 _hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t
*shape_plan HB_UNUSED
,
1192 const hb_feature_t
*user_features HB_UNUSED
,
1193 unsigned int num_user_features HB_UNUSED
)
1195 return (hb_coretext_aat_shaper_shape_plan_data_t
*) HB_SHAPER_DATA_SUCCEEDED
;
1199 _hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shaper_shape_plan_data_t
*data HB_UNUSED
)
1209 _hb_coretext_aat_shape (hb_shape_plan_t
*shape_plan
,
1211 hb_buffer_t
*buffer
,
1212 const hb_feature_t
*features
,
1213 unsigned int num_features
)
1215 return _hb_coretext_shape (shape_plan
, font
, buffer
, features
, num_features
);