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 CGFloat font_size
= 36.; /* Default... */
160 /* No idea if the following is even a good idea. */
162 font_size
= font
->y_ppem
;
165 font_size
= -font_size
;
166 data
->x_mult
= (CGFloat
) font
->x_scale
/ font_size
;
167 data
->y_mult
= (CGFloat
) font
->y_scale
/ font_size
;
168 data
->ct_font
= CTFontCreateWithGraphicsFont (face_data
, font_size
, NULL
, NULL
);
169 if (unlikely (!data
->ct_font
)) {
170 DEBUG_MSG (CORETEXT
, font
, "Font CTFontCreateWithGraphicsFont() failed");
179 _hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t
*data
)
181 CFRelease (data
->ct_font
);
187 * shaper shape_plan data
190 struct hb_coretext_shaper_shape_plan_data_t
{};
192 hb_coretext_shaper_shape_plan_data_t
*
193 _hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t
*shape_plan HB_UNUSED
,
194 const hb_feature_t
*user_features HB_UNUSED
,
195 unsigned int num_user_features HB_UNUSED
)
197 return (hb_coretext_shaper_shape_plan_data_t
*) HB_SHAPER_DATA_SUCCEEDED
;
201 _hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t
*data HB_UNUSED
)
206 hb_coretext_font_get_ct_font (hb_font_t
*font
)
208 if (unlikely (!hb_coretext_shaper_font_data_ensure (font
))) return NULL
;
209 hb_coretext_shaper_font_data_t
*font_data
= HB_SHAPER_DATA_GET (font
);
210 return font_data
->ct_font
;
218 struct feature_record_t
{
219 unsigned int feature
;
220 unsigned int setting
;
223 struct active_feature_t
{
224 feature_record_t rec
;
227 static int cmp (const active_feature_t
*a
, const active_feature_t
*b
) {
228 return a
->rec
.feature
< b
->rec
.feature
? -1 : a
->rec
.feature
> b
->rec
.feature
? 1 :
229 a
->order
< b
->order
? -1 : a
->order
> b
->order
? 1 :
230 a
->rec
.setting
< b
->rec
.setting
? -1 : a
->rec
.setting
> b
->rec
.setting
? 1 :
233 bool operator== (const active_feature_t
*f
) {
234 return cmp (this, f
) == 0;
238 struct feature_event_t
{
241 active_feature_t feature
;
243 static int cmp (const feature_event_t
*a
, const feature_event_t
*b
) {
244 return a
->index
< b
->index
? -1 : a
->index
> b
->index
? 1 :
245 a
->start
< b
->start
? -1 : a
->start
> b
->start
? 1 :
246 active_feature_t::cmp (&a
->feature
, &b
->feature
);
250 struct range_record_t
{
252 unsigned int index_first
; /* == start */
253 unsigned int index_last
; /* == end - 1 */
257 /* The following enum members are added in OS X 10.8. */
258 #define kAltHalfWidthTextSelector 6
259 #define kAltProportionalTextSelector 5
260 #define kAlternateHorizKanaOffSelector 1
261 #define kAlternateHorizKanaOnSelector 0
262 #define kAlternateKanaType 34
263 #define kAlternateVertKanaOffSelector 3
264 #define kAlternateVertKanaOnSelector 2
265 #define kCaseSensitiveLayoutOffSelector 1
266 #define kCaseSensitiveLayoutOnSelector 0
267 #define kCaseSensitiveLayoutType 33
268 #define kCaseSensitiveSpacingOffSelector 3
269 #define kCaseSensitiveSpacingOnSelector 2
270 #define kContextualAlternatesOffSelector 1
271 #define kContextualAlternatesOnSelector 0
272 #define kContextualAlternatesType 36
273 #define kContextualLigaturesOffSelector 19
274 #define kContextualLigaturesOnSelector 18
275 #define kContextualSwashAlternatesOffSelector 5
276 #define kContextualSwashAlternatesOnSelector 4
277 #define kDefaultLowerCaseSelector 0
278 #define kDefaultUpperCaseSelector 0
279 #define kHistoricalLigaturesOffSelector 21
280 #define kHistoricalLigaturesOnSelector 20
281 #define kHojoCharactersSelector 12
282 #define kJIS2004CharactersSelector 11
283 #define kLowerCasePetiteCapsSelector 2
284 #define kLowerCaseSmallCapsSelector 1
285 #define kLowerCaseType 37
286 #define kMathematicalGreekOffSelector 11
287 #define kMathematicalGreekOnSelector 10
288 #define kNLCCharactersSelector 13
289 #define kQuarterWidthTextSelector 4
290 #define kScientificInferiorsSelector 4
291 #define kStylisticAltEightOffSelector 17
292 #define kStylisticAltEightOnSelector 16
293 #define kStylisticAltEighteenOffSelector 37
294 #define kStylisticAltEighteenOnSelector 36
295 #define kStylisticAltElevenOffSelector 23
296 #define kStylisticAltElevenOnSelector 22
297 #define kStylisticAltFifteenOffSelector 31
298 #define kStylisticAltFifteenOnSelector 30
299 #define kStylisticAltFiveOffSelector 11
300 #define kStylisticAltFiveOnSelector 10
301 #define kStylisticAltFourOffSelector 9
302 #define kStylisticAltFourOnSelector 8
303 #define kStylisticAltFourteenOffSelector 29
304 #define kStylisticAltFourteenOnSelector 28
305 #define kStylisticAltNineOffSelector 19
306 #define kStylisticAltNineOnSelector 18
307 #define kStylisticAltNineteenOffSelector 39
308 #define kStylisticAltNineteenOnSelector 38
309 #define kStylisticAltOneOffSelector 3
310 #define kStylisticAltOneOnSelector 2
311 #define kStylisticAltSevenOffSelector 15
312 #define kStylisticAltSevenOnSelector 14
313 #define kStylisticAltSeventeenOffSelector 35
314 #define kStylisticAltSeventeenOnSelector 34
315 #define kStylisticAltSixOffSelector 13
316 #define kStylisticAltSixOnSelector 12
317 #define kStylisticAltSixteenOffSelector 33
318 #define kStylisticAltSixteenOnSelector 32
319 #define kStylisticAltTenOffSelector 21
320 #define kStylisticAltTenOnSelector 20
321 #define kStylisticAltThirteenOffSelector 27
322 #define kStylisticAltThirteenOnSelector 26
323 #define kStylisticAltThreeOffSelector 7
324 #define kStylisticAltThreeOnSelector 6
325 #define kStylisticAltTwelveOffSelector 25
326 #define kStylisticAltTwelveOnSelector 24
327 #define kStylisticAltTwentyOffSelector 41
328 #define kStylisticAltTwentyOnSelector 40
329 #define kStylisticAltTwoOffSelector 5
330 #define kStylisticAltTwoOnSelector 4
331 #define kStylisticAlternativesType 35
332 #define kSwashAlternatesOffSelector 3
333 #define kSwashAlternatesOnSelector 2
334 #define kThirdWidthTextSelector 3
335 #define kTraditionalNamesCharactersSelector 14
336 #define kUpperCasePetiteCapsSelector 2
337 #define kUpperCaseSmallCapsSelector 1
338 #define kUpperCaseType 38
340 /* Table data courtesy of Apple. */
341 static const struct feature_mapping_t
{
342 FourCharCode otFeatureTag
;
343 uint16_t aatFeatureType
;
344 uint16_t selectorToEnable
;
345 uint16_t selectorToDisable
;
346 } feature_mappings
[] = {
347 { 'c2pc', kUpperCaseType
, kUpperCasePetiteCapsSelector
, kDefaultUpperCaseSelector
},
348 { 'c2sc', kUpperCaseType
, kUpperCaseSmallCapsSelector
, kDefaultUpperCaseSelector
},
349 { 'calt', kContextualAlternatesType
, kContextualAlternatesOnSelector
, kContextualAlternatesOffSelector
},
350 { 'case', kCaseSensitiveLayoutType
, kCaseSensitiveLayoutOnSelector
, kCaseSensitiveLayoutOffSelector
},
351 { 'clig', kLigaturesType
, kContextualLigaturesOnSelector
, kContextualLigaturesOffSelector
},
352 { 'cpsp', kCaseSensitiveLayoutType
, kCaseSensitiveSpacingOnSelector
, kCaseSensitiveSpacingOffSelector
},
353 { 'cswh', kContextualAlternatesType
, kContextualSwashAlternatesOnSelector
, kContextualSwashAlternatesOffSelector
},
354 { 'dlig', kLigaturesType
, kRareLigaturesOnSelector
, kRareLigaturesOffSelector
},
355 { 'expt', kCharacterShapeType
, kExpertCharactersSelector
, 16 },
356 { 'frac', kFractionsType
, kDiagonalFractionsSelector
, kNoFractionsSelector
},
357 { 'fwid', kTextSpacingType
, kMonospacedTextSelector
, 7 },
358 { 'halt', kTextSpacingType
, kAltHalfWidthTextSelector
, 7 },
359 { 'hist', kLigaturesType
, kHistoricalLigaturesOnSelector
, kHistoricalLigaturesOffSelector
},
360 { 'hkna', kAlternateKanaType
, kAlternateHorizKanaOnSelector
, kAlternateHorizKanaOffSelector
, },
361 { 'hlig', kLigaturesType
, kHistoricalLigaturesOnSelector
, kHistoricalLigaturesOffSelector
},
362 { 'hngl', kTransliterationType
, kHanjaToHangulSelector
, kNoTransliterationSelector
},
363 { 'hojo', kCharacterShapeType
, kHojoCharactersSelector
, 16 },
364 { 'hwid', kTextSpacingType
, kHalfWidthTextSelector
, 7 },
365 { 'ital', kItalicCJKRomanType
, kCJKItalicRomanOnSelector
, kCJKItalicRomanOffSelector
},
366 { 'jp04', kCharacterShapeType
, kJIS2004CharactersSelector
, 16 },
367 { 'jp78', kCharacterShapeType
, kJIS1978CharactersSelector
, 16 },
368 { 'jp83', kCharacterShapeType
, kJIS1983CharactersSelector
, 16 },
369 { 'jp90', kCharacterShapeType
, kJIS1990CharactersSelector
, 16 },
370 { 'liga', kLigaturesType
, kCommonLigaturesOnSelector
, kCommonLigaturesOffSelector
},
371 { 'lnum', kNumberCaseType
, kUpperCaseNumbersSelector
, 2 },
372 { 'mgrk', kMathematicalExtrasType
, kMathematicalGreekOnSelector
, kMathematicalGreekOffSelector
},
373 { 'nlck', kCharacterShapeType
, kNLCCharactersSelector
, 16 },
374 { 'onum', kNumberCaseType
, kLowerCaseNumbersSelector
, 2 },
375 { 'ordn', kVerticalPositionType
, kOrdinalsSelector
, kNormalPositionSelector
},
376 { 'palt', kTextSpacingType
, kAltProportionalTextSelector
, 7 },
377 { 'pcap', kLowerCaseType
, kLowerCasePetiteCapsSelector
, kDefaultLowerCaseSelector
},
378 { 'pkna', kTextSpacingType
, kProportionalTextSelector
, 7 },
379 { 'pnum', kNumberSpacingType
, kProportionalNumbersSelector
, 4 },
380 { 'pwid', kTextSpacingType
, kProportionalTextSelector
, 7 },
381 { 'qwid', kTextSpacingType
, kQuarterWidthTextSelector
, 7 },
382 { 'ruby', kRubyKanaType
, kRubyKanaOnSelector
, kRubyKanaOffSelector
},
383 { 'sinf', kVerticalPositionType
, kScientificInferiorsSelector
, kNormalPositionSelector
},
384 { 'smcp', kLowerCaseType
, kLowerCaseSmallCapsSelector
, kDefaultLowerCaseSelector
},
385 { 'smpl', kCharacterShapeType
, kSimplifiedCharactersSelector
, 16 },
386 { 'ss01', kStylisticAlternativesType
, kStylisticAltOneOnSelector
, kStylisticAltOneOffSelector
},
387 { 'ss02', kStylisticAlternativesType
, kStylisticAltTwoOnSelector
, kStylisticAltTwoOffSelector
},
388 { 'ss03', kStylisticAlternativesType
, kStylisticAltThreeOnSelector
, kStylisticAltThreeOffSelector
},
389 { 'ss04', kStylisticAlternativesType
, kStylisticAltFourOnSelector
, kStylisticAltFourOffSelector
},
390 { 'ss05', kStylisticAlternativesType
, kStylisticAltFiveOnSelector
, kStylisticAltFiveOffSelector
},
391 { 'ss06', kStylisticAlternativesType
, kStylisticAltSixOnSelector
, kStylisticAltSixOffSelector
},
392 { 'ss07', kStylisticAlternativesType
, kStylisticAltSevenOnSelector
, kStylisticAltSevenOffSelector
},
393 { 'ss08', kStylisticAlternativesType
, kStylisticAltEightOnSelector
, kStylisticAltEightOffSelector
},
394 { 'ss09', kStylisticAlternativesType
, kStylisticAltNineOnSelector
, kStylisticAltNineOffSelector
},
395 { 'ss10', kStylisticAlternativesType
, kStylisticAltTenOnSelector
, kStylisticAltTenOffSelector
},
396 { 'ss11', kStylisticAlternativesType
, kStylisticAltElevenOnSelector
, kStylisticAltElevenOffSelector
},
397 { 'ss12', kStylisticAlternativesType
, kStylisticAltTwelveOnSelector
, kStylisticAltTwelveOffSelector
},
398 { 'ss13', kStylisticAlternativesType
, kStylisticAltThirteenOnSelector
, kStylisticAltThirteenOffSelector
},
399 { 'ss14', kStylisticAlternativesType
, kStylisticAltFourteenOnSelector
, kStylisticAltFourteenOffSelector
},
400 { 'ss15', kStylisticAlternativesType
, kStylisticAltFifteenOnSelector
, kStylisticAltFifteenOffSelector
},
401 { 'ss16', kStylisticAlternativesType
, kStylisticAltSixteenOnSelector
, kStylisticAltSixteenOffSelector
},
402 { 'ss17', kStylisticAlternativesType
, kStylisticAltSeventeenOnSelector
, kStylisticAltSeventeenOffSelector
},
403 { 'ss18', kStylisticAlternativesType
, kStylisticAltEighteenOnSelector
, kStylisticAltEighteenOffSelector
},
404 { 'ss19', kStylisticAlternativesType
, kStylisticAltNineteenOnSelector
, kStylisticAltNineteenOffSelector
},
405 { 'ss20', kStylisticAlternativesType
, kStylisticAltTwentyOnSelector
, kStylisticAltTwentyOffSelector
},
406 { 'subs', kVerticalPositionType
, kInferiorsSelector
, kNormalPositionSelector
},
407 { 'sups', kVerticalPositionType
, kSuperiorsSelector
, kNormalPositionSelector
},
408 { 'swsh', kContextualAlternatesType
, kSwashAlternatesOnSelector
, kSwashAlternatesOffSelector
},
409 { 'titl', kStyleOptionsType
, kTitlingCapsSelector
, kNoStyleOptionsSelector
},
410 { 'tnam', kCharacterShapeType
, kTraditionalNamesCharactersSelector
, 16 },
411 { 'tnum', kNumberSpacingType
, kMonospacedNumbersSelector
, 4 },
412 { 'trad', kCharacterShapeType
, kTraditionalCharactersSelector
, 16 },
413 { 'twid', kTextSpacingType
, kThirdWidthTextSelector
, 7 },
414 { 'unic', kLetterCaseType
, 14, 15 },
415 { 'valt', kTextSpacingType
, kAltProportionalTextSelector
, 7 },
416 { 'vert', kVerticalSubstitutionType
, kSubstituteVerticalFormsOnSelector
, kSubstituteVerticalFormsOffSelector
},
417 { 'vhal', kTextSpacingType
, kAltHalfWidthTextSelector
, 7 },
418 { 'vkna', kAlternateKanaType
, kAlternateVertKanaOnSelector
, kAlternateVertKanaOffSelector
},
419 { 'vpal', kTextSpacingType
, kAltProportionalTextSelector
, 7 },
420 { 'vrt2', kVerticalSubstitutionType
, kSubstituteVerticalFormsOnSelector
, kSubstituteVerticalFormsOffSelector
},
421 { 'zero', kTypographicExtrasType
, kSlashedZeroOnSelector
, kSlashedZeroOffSelector
},
425 _hb_feature_mapping_cmp (const void *key_
, const void *entry_
)
427 unsigned int key
= * (unsigned int *) key_
;
428 const feature_mapping_t
* entry
= (const feature_mapping_t
*) entry_
;
429 return key
< entry
->otFeatureTag
? -1 :
430 key
> entry
->otFeatureTag
? 1 :
435 _hb_coretext_shape (hb_shape_plan_t
*shape_plan
,
438 const hb_feature_t
*features
,
439 unsigned int num_features
)
441 hb_face_t
*face
= font
->face
;
442 hb_coretext_shaper_face_data_t
*face_data
= HB_SHAPER_DATA_GET (face
);
443 hb_coretext_shaper_font_data_t
*font_data
= HB_SHAPER_DATA_GET (font
);
445 /* Attach marks to their bases, to match the 'ot' shaper.
446 * Adapted from hb-ot-shape:hb_form_clusters().
447 * Note that this only makes us be closer to the 'ot' shaper,
448 * but by no means the same. For example, if there's
449 * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will
450 * continue pointing to B2 even though B2 was merged into B1's
453 hb_unicode_funcs_t
*unicode
= buffer
->unicode
;
454 unsigned int count
= buffer
->len
;
455 hb_glyph_info_t
*info
= buffer
->info
;
456 for (unsigned int i
= 1; i
< count
; i
++)
457 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode
->general_category (info
[i
].codepoint
)))
458 buffer
->merge_clusters (i
- 1, i
+ 1);
461 hb_auto_array_t
<feature_record_t
> feature_records
;
462 hb_auto_array_t
<range_record_t
> range_records
;
466 * (copied + modified from code from hb-uniscribe.cc)
470 /* Sort features by start/end events. */
471 hb_auto_array_t
<feature_event_t
> feature_events
;
472 for (unsigned int i
= 0; i
< num_features
; i
++)
474 const feature_mapping_t
* mapping
= (const feature_mapping_t
*) bsearch (&features
[i
].tag
,
476 ARRAY_LENGTH (feature_mappings
),
477 sizeof (feature_mappings
[0]),
478 _hb_feature_mapping_cmp
);
482 active_feature_t feature
;
483 feature
.rec
.feature
= mapping
->aatFeatureType
;
484 feature
.rec
.setting
= features
[i
].value
? mapping
->selectorToEnable
: mapping
->selectorToDisable
;
487 feature_event_t
*event
;
489 event
= feature_events
.push ();
490 if (unlikely (!event
))
492 event
->index
= features
[i
].start
;
494 event
->feature
= feature
;
496 event
= feature_events
.push ();
497 if (unlikely (!event
))
499 event
->index
= features
[i
].end
;
500 event
->start
= false;
501 event
->feature
= feature
;
503 feature_events
.qsort ();
504 /* Add a strategic final event. */
506 active_feature_t feature
;
507 feature
.rec
.feature
= HB_TAG_NONE
;
508 feature
.rec
.setting
= 0;
509 feature
.order
= num_features
+ 1;
511 feature_event_t
*event
= feature_events
.push ();
512 if (unlikely (!event
))
514 event
->index
= 0; /* This value does magic. */
515 event
->start
= false;
516 event
->feature
= feature
;
519 /* Scan events and save features for each range. */
520 hb_auto_array_t
<active_feature_t
> active_features
;
521 unsigned int last_index
= 0;
522 for (unsigned int i
= 0; i
< feature_events
.len
; i
++)
524 feature_event_t
*event
= &feature_events
[i
];
526 if (event
->index
!= last_index
)
528 /* Save a snapshot of active features and the range. */
529 range_record_t
*range
= range_records
.push ();
530 if (unlikely (!range
))
533 if (active_features
.len
)
535 CFMutableArrayRef features_array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
537 /* TODO sort and resolve conflicting features? */
538 /* active_features.qsort (); */
539 for (unsigned int j
= 0; j
< active_features
.len
; j
++)
541 CFStringRef keys
[2] = {
542 kCTFontFeatureTypeIdentifierKey
,
543 kCTFontFeatureSelectorIdentifierKey
545 CFNumberRef values
[2] = {
546 CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &active_features
[j
].rec
.feature
),
547 CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &active_features
[j
].rec
.setting
)
549 CFDictionaryRef dict
= CFDictionaryCreate (kCFAllocatorDefault
,
550 (const void **) keys
,
551 (const void **) values
,
553 &kCFTypeDictionaryKeyCallBacks
,
554 &kCFTypeDictionaryValueCallBacks
);
555 CFRelease (values
[0]);
556 CFRelease (values
[1]);
558 CFArrayAppendValue (features_array
, dict
);
563 CFDictionaryRef attributes
= CFDictionaryCreate (kCFAllocatorDefault
,
564 (const void **) &kCTFontFeatureSettingsAttribute
,
565 (const void **) &features_array
,
567 &kCFTypeDictionaryKeyCallBacks
,
568 &kCFTypeDictionaryValueCallBacks
);
569 CFRelease (features_array
);
571 CTFontDescriptorRef font_desc
= CTFontDescriptorCreateWithAttributes (attributes
);
572 CFRelease (attributes
);
574 range
->font
= CTFontCreateCopyWithAttributes (font_data
->ct_font
, 0.0, NULL
, font_desc
);
575 CFRelease (font_desc
);
582 range
->index_first
= last_index
;
583 range
->index_last
= event
->index
- 1;
585 last_index
= event
->index
;
589 active_feature_t
*feature
= active_features
.push ();
590 if (unlikely (!feature
))
592 *feature
= event
->feature
;
594 active_feature_t
*feature
= active_features
.find (&event
->feature
);
596 active_features
.remove (feature
- active_features
.array
);
600 if (!range_records
.len
) /* No active feature found. */
609 unsigned int scratch_size
;
610 hb_buffer_t::scratch_buffer_t
*scratch
= buffer
->get_scratch_buffer (&scratch_size
);
612 #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \
613 Type *name = (Type *) scratch; \
615 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
616 if (unlikely (_consumed > scratch_size)) \
621 scratch += _consumed; \
622 scratch_size -= _consumed; \
625 ALLOCATE_ARRAY (UniChar
, pchars
, buffer
->len
* 2, /*nothing*/);
626 unsigned int chars_len
= 0;
627 for (unsigned int i
= 0; i
< buffer
->len
; i
++) {
628 hb_codepoint_t c
= buffer
->info
[i
].codepoint
;
629 if (likely (c
<= 0xFFFFu
))
630 pchars
[chars_len
++] = c
;
631 else if (unlikely (c
> 0x10FFFFu
))
632 pchars
[chars_len
++] = 0xFFFDu
;
634 pchars
[chars_len
++] = 0xD800u
+ ((c
- 0x10000u
) >> 10);
635 pchars
[chars_len
++] = 0xDC00u
+ ((c
- 0x10000u
) & ((1 << 10) - 1));
639 ALLOCATE_ARRAY (unsigned int, log_clusters
, chars_len
, /*nothing*/);
641 for (unsigned int i
= 0; i
< buffer
->len
; i
++)
643 hb_codepoint_t c
= buffer
->info
[i
].codepoint
;
644 unsigned int cluster
= buffer
->info
[i
].cluster
;
645 log_clusters
[chars_len
++] = cluster
;
646 if (hb_in_range (c
, 0x10000u
, 0x10FFFFu
))
647 log_clusters
[chars_len
++] = cluster
; /* Surrogates. */
652 DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
658 CFStringRef string_ref
= NULL
;
659 CTLineRef line
= NULL
;
664 DEBUG_MSG (CORETEXT
, buffer
, "Buffer resize");
665 /* string_ref uses the scratch-buffer for backing store, and line references
666 * string_ref (via attr_string). We must release those before resizing buffer. */
669 CFRelease (string_ref
);
674 /* Get previous start-of-scratch-area, that we use later for readjusting
675 * our existing scratch arrays. */
676 unsigned int old_scratch_used
;
677 hb_buffer_t::scratch_buffer_t
*old_scratch
;
678 old_scratch
= buffer
->get_scratch_buffer (&old_scratch_used
);
679 old_scratch_used
= scratch
- old_scratch
;
681 if (unlikely (!buffer
->ensure (buffer
->allocated
* 2)))
682 FAIL ("Buffer resize failed");
684 /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the
685 * cleanest way to do without completely restructuring the rest of this shaper. */
686 scratch
= buffer
->get_scratch_buffer (&scratch_size
);
687 pchars
= reinterpret_cast<UniChar
*> (((char *) scratch
+ ((char *) pchars
- (char *) old_scratch
)));
688 log_clusters
= reinterpret_cast<unsigned int *> (((char *) scratch
+ ((char *) log_clusters
- (char *) old_scratch
)));
689 scratch
+= old_scratch_used
;
690 scratch_size
-= old_scratch_used
;
694 string_ref
= CFStringCreateWithCharactersNoCopy (NULL
,
697 if (unlikely (!string_ref
))
698 FAIL ("CFStringCreateWithCharactersNoCopy failed");
700 /* Create an attributed string, populate it, and create a line from it, then release attributed string. */
702 CFMutableAttributedStringRef attr_string
= CFAttributedStringCreateMutable (kCFAllocatorDefault
,
704 if (unlikely (!attr_string
))
705 FAIL ("CFAttributedStringCreateMutable failed");
706 CFAttributedStringReplaceString (attr_string
, CFRangeMake (0, 0), string_ref
);
707 if (HB_DIRECTION_IS_VERTICAL (buffer
->props
.direction
))
709 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (0, chars_len
),
710 kCTVerticalFormsAttributeName
, kCFBooleanTrue
);
713 if (buffer
->props
.language
)
715 /* What's the iOS equivalent of this check?
716 * The symbols was introduced in iOS 7.0.
717 * At any rate, our fallback is safe and works fine. */
718 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
719 # define kCTLanguageAttributeName CFSTR ("NSLanguage")
721 CFStringRef lang
= CFStringCreateWithCStringNoCopy (kCFAllocatorDefault
,
722 hb_language_to_string (buffer
->props
.language
),
723 kCFStringEncodingUTF8
,
725 if (unlikely (!lang
))
726 FAIL ("CFStringCreateWithCStringNoCopy failed");
727 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (0, chars_len
),
728 kCTLanguageAttributeName
, lang
);
731 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (0, chars_len
),
732 kCTFontAttributeName
, font_data
->ct_font
);
736 unsigned int start
= 0;
737 range_record_t
*last_range
= &range_records
[0];
738 for (unsigned int k
= 0; k
< chars_len
; k
++)
740 range_record_t
*range
= last_range
;
741 while (log_clusters
[k
] < range
->index_first
)
743 while (log_clusters
[k
] > range
->index_last
)
745 if (range
!= last_range
)
747 if (last_range
->font
)
748 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (start
, k
- start
),
749 kCTFontAttributeName
, last_range
->font
);
756 if (start
!= chars_len
&& last_range
->font
)
757 CFAttributedStringSetAttribute (attr_string
, CFRangeMake (start
, chars_len
- start
),
758 kCTFontAttributeName
, last_range
->font
);
761 int level
= HB_DIRECTION_IS_FORWARD (buffer
->props
.direction
) ? 0 : 1;
762 CFNumberRef level_number
= CFNumberCreate (kCFAllocatorDefault
, kCFNumberIntType
, &level
);
763 CFDictionaryRef options
= CFDictionaryCreate (kCFAllocatorDefault
,
764 (const void **) &kCTTypesetterOptionForcedEmbeddingLevel
,
765 (const void **) &level_number
,
767 &kCFTypeDictionaryKeyCallBacks
,
768 &kCFTypeDictionaryValueCallBacks
);
769 if (unlikely (!options
))
770 FAIL ("CFDictionaryCreate failed");
772 CTTypesetterRef typesetter
= CTTypesetterCreateWithAttributedStringAndOptions (attr_string
, options
);
774 CFRelease (attr_string
);
775 if (unlikely (!typesetter
))
776 FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed");
778 line
= CTTypesetterCreateLine (typesetter
, CFRangeMake(0, 0));
779 CFRelease (typesetter
);
780 if (unlikely (!line
))
781 FAIL ("CTTypesetterCreateLine failed");
784 CFArrayRef glyph_runs
= CTLineGetGlyphRuns (line
);
785 unsigned int num_runs
= CFArrayGetCount (glyph_runs
);
786 DEBUG_MSG (CORETEXT
, NULL
, "Num runs: %d", num_runs
);
789 uint32_t status_and
= ~0, status_or
= 0;
790 double advances_so_far
= 0;
792 const CFRange range_all
= CFRangeMake (0, 0);
794 for (unsigned int i
= 0; i
< num_runs
; i
++)
796 CTRunRef run
= static_cast<CTRunRef
>(CFArrayGetValueAtIndex (glyph_runs
, i
));
797 CTRunStatus run_status
= CTRunGetStatus (run
);
798 status_or
|= run_status
;
799 status_and
&= run_status
;
800 DEBUG_MSG (CORETEXT
, run
, "CTRunStatus: %x", run_status
);
801 double run_advance
= CTRunGetTypographicBounds (run
, range_all
, NULL
, NULL
, NULL
);
802 if (HB_DIRECTION_IS_VERTICAL (buffer
->props
.direction
))
803 run_advance
= -run_advance
;
804 DEBUG_MSG (CORETEXT
, run
, "Run advance: %g", run_advance
);
806 /* CoreText does automatic font fallback (AKA "cascading") for characters
807 * not supported by the requested font, and provides no way to turn it off,
808 * so we must detect if the returned run uses a font other than the requested
809 * one and fill in the buffer with .notdef glyphs instead of random glyph
810 * indices from a different font.
812 CFDictionaryRef attributes
= CTRunGetAttributes (run
);
813 CTFontRef run_ct_font
= static_cast<CTFontRef
>(CFDictionaryGetValue (attributes
, kCTFontAttributeName
));
814 if (!CFEqual (run_ct_font
, font_data
->ct_font
))
816 /* The run doesn't use our main font instance. We have to figure out
817 * whether font fallback happened, or this is just CoreText giving us
818 * another CTFont using the same underlying CGFont. CoreText seems
819 * to do that in a variety of situations, one of which being vertical
820 * text, but also perhaps for caching reasons.
822 * First, see if it uses any of our subfonts created to set font features...
824 * Next, compare the CGFont to the one we used to create our fonts.
825 * Even this doesn't work all the time.
827 * Finally, we compare PS names, which I don't think are unique...
829 * Looks like if we really want to be sure here we have to modify the
830 * font to change the name table, similar to what we do in the uniscribe
833 * However, even that wouldn't work if we were passed in the CGFont to
836 * Webkit uses a slightly different approach: it installs LastResort
837 * as fallback chain, and then checks PS name of used font against
838 * LastResort. That one is safe for any font except for LastResort,
839 * as opposed to ours, which can fail if we are using any uninstalled
840 * font that has the same name as an installed font.
842 * See: http://github.com/behdad/harfbuzz/pull/36
844 bool matched
= false;
845 for (unsigned int i
= 0; i
< range_records
.len
; i
++)
846 if (range_records
[i
].font
&& CFEqual (run_ct_font
, range_records
[i
].font
))
853 CGFontRef run_cg_font
= CTFontCopyGraphicsFont (run_ct_font
, 0);
856 matched
= CFEqual (run_cg_font
, face_data
);
857 CFRelease (run_cg_font
);
862 CFStringRef font_ps_name
= CTFontCopyName (font_data
->ct_font
, kCTFontPostScriptNameKey
);
863 CFStringRef run_ps_name
= CTFontCopyName (run_ct_font
, kCTFontPostScriptNameKey
);
864 CFComparisonResult result
= CFStringCompare (run_ps_name
, font_ps_name
, 0);
865 CFRelease (run_ps_name
);
866 CFRelease (font_ps_name
);
867 if (result
== kCFCompareEqualTo
)
872 CFRange range
= CTRunGetStringRange (run
);
873 DEBUG_MSG (CORETEXT
, run
, "Run used fallback font: %ld..%ld",
874 range
.location
, range
.location
+ range
.length
);
875 if (!buffer
->ensure_inplace (buffer
->len
+ range
.length
))
876 goto resize_and_retry
;
877 hb_glyph_info_t
*info
= buffer
->info
+ buffer
->len
;
879 hb_codepoint_t notdef
= 0;
880 hb_direction_t dir
= buffer
->props
.direction
;
881 hb_position_t x_advance
, y_advance
, x_offset
, y_offset
;
882 hb_font_get_glyph_advance_for_direction (font
, notdef
, dir
, &x_advance
, &y_advance
);
883 hb_font_get_glyph_origin_for_direction (font
, notdef
, dir
, &x_offset
, &y_offset
);
884 hb_position_t advance
= x_advance
+ y_advance
;
885 x_offset
= -x_offset
;
886 y_offset
= -y_offset
;
888 unsigned int old_len
= buffer
->len
;
889 for (CFIndex j
= range
.location
; j
< range
.location
+ range
.length
; j
++)
891 UniChar ch
= CFStringGetCharacterAtIndex (string_ref
, j
);
892 if (hb_in_range
<UniChar
> (ch
, 0xDC00u
, 0xDFFFu
) && range
.location
< j
)
894 ch
= CFStringGetCharacterAtIndex (string_ref
, j
- 1);
895 if (hb_in_range
<UniChar
> (ch
, 0xD800u
, 0xDBFFu
))
896 /* This is the second of a surrogate pair. Don't need .notdef
900 if (buffer
->unicode
->is_default_ignorable (ch
))
903 info
->codepoint
= notdef
;
904 info
->cluster
= log_clusters
[j
];
906 info
->mask
= advance
;
907 info
->var1
.u32
= x_offset
;
908 info
->var2
.u32
= y_offset
;
913 if (HB_DIRECTION_IS_BACKWARD (buffer
->props
.direction
))
914 buffer
->reverse_range (old_len
, buffer
->len
);
915 advances_so_far
+= run_advance
;
920 unsigned int num_glyphs
= CTRunGetGlyphCount (run
);
924 if (!buffer
->ensure_inplace (buffer
->len
+ num_glyphs
))
925 goto resize_and_retry
;
927 hb_glyph_info_t
*run_info
= buffer
->info
+ buffer
->len
;
929 /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always
930 * succeed, and so copying data to our own buffer will be rare. Reports
931 * have it that this changed in OS X 10.10 Yosemite, and NULL is returned
932 * frequently. At any rate, we can test that codepath by setting USE_PTR
937 #define SCRATCH_SAVE() \
938 unsigned int scratch_size_saved = scratch_size; \
939 hb_buffer_t::scratch_buffer_t *scratch_saved = scratch
941 #define SCRATCH_RESTORE() \
942 scratch_size = scratch_size_saved; \
943 scratch = scratch_saved;
947 const CGGlyph
* glyphs
= USE_PTR
? CTRunGetGlyphsPtr (run
) : NULL
;
949 ALLOCATE_ARRAY (CGGlyph
, glyph_buf
, num_glyphs
, goto resize_and_retry
);
950 CTRunGetGlyphs (run
, range_all
, glyph_buf
);
953 const CFIndex
* string_indices
= USE_PTR
? CTRunGetStringIndicesPtr (run
) : NULL
;
954 if (!string_indices
) {
955 ALLOCATE_ARRAY (CFIndex
, index_buf
, num_glyphs
, goto resize_and_retry
);
956 CTRunGetStringIndices (run
, range_all
, index_buf
);
957 string_indices
= index_buf
;
959 hb_glyph_info_t
*info
= run_info
;
960 for (unsigned int j
= 0; j
< num_glyphs
; j
++)
962 info
->codepoint
= glyphs
[j
];
963 info
->cluster
= log_clusters
[string_indices
[j
]];
970 * Note that CoreText does not return advances for glyphs. As such,
971 * for all but last glyph, we use the delta position to next glyph as
972 * advance (in the advance direction only), and for last glyph we set
973 * whatever is needed to make the whole run's advance add up. */
975 const CGPoint
* positions
= USE_PTR
? CTRunGetPositionsPtr (run
) : NULL
;
977 ALLOCATE_ARRAY (CGPoint
, position_buf
, num_glyphs
, goto resize_and_retry
);
978 CTRunGetPositions (run
, range_all
, position_buf
);
979 positions
= position_buf
;
981 hb_glyph_info_t
*info
= run_info
;
982 CGFloat x_mult
= font_data
->x_mult
, y_mult
= font_data
->y_mult
;
983 if (HB_DIRECTION_IS_HORIZONTAL (buffer
->props
.direction
))
985 hb_position_t x_offset
= (positions
[0].x
- advances_so_far
) * x_mult
;
986 for (unsigned int j
= 0; j
< num_glyphs
; j
++)
989 if (likely (j
+ 1 < num_glyphs
))
990 advance
= positions
[j
+ 1].x
- positions
[j
].x
;
991 else /* last glyph */
992 advance
= run_advance
- (positions
[j
].x
- positions
[0].x
);
993 info
->mask
= advance
* x_mult
;
994 info
->var1
.u32
= x_offset
;
995 info
->var2
.u32
= positions
[j
].y
* y_mult
;
1001 hb_position_t y_offset
= (positions
[0].y
- advances_so_far
) * y_mult
;
1002 for (unsigned int j
= 0; j
< num_glyphs
; j
++)
1005 if (likely (j
+ 1 < num_glyphs
))
1006 advance
= positions
[j
+ 1].y
- positions
[j
].y
;
1007 else /* last glyph */
1008 advance
= run_advance
- (positions
[j
].y
- positions
[0].y
);
1009 info
->mask
= advance
* y_mult
;
1010 info
->var1
.u32
= positions
[j
].x
* x_mult
;
1011 info
->var2
.u32
= y_offset
;
1016 advances_so_far
+= run_advance
;
1018 #undef SCRATCH_RESTORE
1021 #undef ALLOCATE_ARRAY
1023 buffer
->len
+= num_glyphs
;
1026 /* Make sure all runs had the expected direction. */
1027 bool backward
= HB_DIRECTION_IS_BACKWARD (buffer
->props
.direction
);
1028 assert (bool (status_and
& kCTRunStatusRightToLeft
) == backward
);
1029 assert (bool (status_or
& kCTRunStatusRightToLeft
) == backward
);
1031 buffer
->clear_positions ();
1033 unsigned int count
= buffer
->len
;
1034 hb_glyph_info_t
*info
= buffer
->info
;
1035 hb_glyph_position_t
*pos
= buffer
->pos
;
1036 if (HB_DIRECTION_IS_HORIZONTAL (buffer
->props
.direction
))
1037 for (unsigned int i
= 0; i
< count
; i
++)
1039 pos
->x_advance
= info
->mask
;
1040 pos
->x_offset
= info
->var1
.u32
;
1041 pos
->y_offset
= info
->var2
.u32
;
1045 for (unsigned int i
= 0; i
< count
; i
++)
1047 pos
->y_advance
= info
->mask
;
1048 pos
->x_offset
= info
->var1
.u32
;
1049 pos
->y_offset
= info
->var2
.u32
;
1053 /* Fix up clusters so that we never return out-of-order indices;
1054 * if core text has reordered glyphs, we'll merge them to the
1055 * beginning of the reordered cluster. CoreText is nice enough
1056 * to tell us whenever it has produced nonmonotonic results...
1057 * Note that we assume the input clusters were nonmonotonic to
1060 * This does *not* mean we'll form the same clusters as Uniscribe
1061 * or the native OT backend, only that the cluster indices will be
1062 * monotonic in the output buffer. */
1063 if (count
> 1 && (status_or
& kCTRunStatusNonMonotonic
))
1065 hb_glyph_info_t
*info
= buffer
->info
;
1066 if (HB_DIRECTION_IS_FORWARD (buffer
->props
.direction
))
1068 unsigned int cluster
= info
[count
- 1].cluster
;
1069 for (unsigned int i
= count
- 1; i
> 0; i
--)
1071 cluster
= MIN (cluster
, info
[i
- 1].cluster
);
1072 info
[i
- 1].cluster
= cluster
;
1077 unsigned int cluster
= info
[0].cluster
;
1078 for (unsigned int i
= 1; i
< count
; i
++)
1080 cluster
= MIN (cluster
, info
[i
].cluster
);
1081 info
[i
].cluster
= cluster
;
1091 CFRelease (string_ref
);
1095 for (unsigned int i
= 0; i
< range_records
.len
; i
++)
1096 if (range_records
[i
].font
)
1097 CFRelease (range_records
[i
].font
);
1107 HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat
, face
)
1108 HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat
, font
)
1115 struct hb_coretext_aat_shaper_face_data_t
{};
1117 hb_coretext_aat_shaper_face_data_t
*
1118 _hb_coretext_aat_shaper_face_data_create (hb_face_t
*face
)
1120 hb_blob_t
*mort_blob
= face
->reference_table (HB_CORETEXT_TAG_MORT
);
1121 /* Umm, we just reference the table to check whether it exists.
1122 * Maybe add better API for this? */
1123 if (!hb_blob_get_length (mort_blob
))
1125 hb_blob_destroy (mort_blob
);
1126 mort_blob
= face
->reference_table (HB_CORETEXT_TAG_MORX
);
1127 if (!hb_blob_get_length (mort_blob
))
1129 hb_blob_destroy (mort_blob
);
1133 hb_blob_destroy (mort_blob
);
1135 return hb_coretext_shaper_face_data_ensure (face
) ? (hb_coretext_aat_shaper_face_data_t
*) HB_SHAPER_DATA_SUCCEEDED
: NULL
;
1139 _hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t
*data HB_UNUSED
)
1148 struct hb_coretext_aat_shaper_font_data_t
{};
1150 hb_coretext_aat_shaper_font_data_t
*
1151 _hb_coretext_aat_shaper_font_data_create (hb_font_t
*font
)
1153 return hb_coretext_shaper_font_data_ensure (font
) ? (hb_coretext_aat_shaper_font_data_t
*) HB_SHAPER_DATA_SUCCEEDED
: NULL
;
1157 _hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_shaper_font_data_t
*data HB_UNUSED
)
1163 * shaper shape_plan data
1166 struct hb_coretext_aat_shaper_shape_plan_data_t
{};
1168 hb_coretext_aat_shaper_shape_plan_data_t
*
1169 _hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t
*shape_plan HB_UNUSED
,
1170 const hb_feature_t
*user_features HB_UNUSED
,
1171 unsigned int num_user_features HB_UNUSED
)
1173 return (hb_coretext_aat_shaper_shape_plan_data_t
*) HB_SHAPER_DATA_SUCCEEDED
;
1177 _hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shaper_shape_plan_data_t
*data HB_UNUSED
)
1187 _hb_coretext_aat_shape (hb_shape_plan_t
*shape_plan
,
1189 hb_buffer_t
*buffer
,
1190 const hb_feature_t
*features
,
1191 unsigned int num_features
)
1193 return _hb_coretext_shape (shape_plan
, font
, buffer
, features
, num_features
);