|
1 /* |
|
2 * Copyright © 2012,2013 Mozilla Foundation. |
|
3 * Copyright © 2012,2013 Google, Inc. |
|
4 * |
|
5 * This is part of HarfBuzz, a text shaping library. |
|
6 * |
|
7 * Permission is hereby granted, without written agreement and without |
|
8 * license or royalty fees, to use, copy, modify, and distribute this |
|
9 * software and its documentation for any purpose, provided that the |
|
10 * above copyright notice and the following two paragraphs appear in |
|
11 * all copies of this software. |
|
12 * |
|
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
|
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
|
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
|
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
|
17 * DAMAGE. |
|
18 * |
|
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
|
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
|
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
|
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
|
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
|
24 * |
|
25 * Mozilla Author(s): Jonathan Kew |
|
26 * Google Author(s): Behdad Esfahbod |
|
27 */ |
|
28 |
|
29 #define HB_SHAPER coretext |
|
30 #include "hb-shaper-impl-private.hh" |
|
31 |
|
32 #include "hb-coretext.h" |
|
33 |
|
34 |
|
35 #ifndef HB_DEBUG_CORETEXT |
|
36 #define HB_DEBUG_CORETEXT (HB_DEBUG+0) |
|
37 #endif |
|
38 |
|
39 |
|
40 HB_SHAPER_DATA_ENSURE_DECLARE(coretext, face) |
|
41 HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font) |
|
42 |
|
43 |
|
44 /* |
|
45 * shaper face data |
|
46 */ |
|
47 |
|
48 struct hb_coretext_shaper_face_data_t { |
|
49 CGFontRef cg_font; |
|
50 }; |
|
51 |
|
52 static void |
|
53 release_data (void *info, const void *data, size_t size) |
|
54 { |
|
55 assert (hb_blob_get_length ((hb_blob_t *) info) == size && |
|
56 hb_blob_get_data ((hb_blob_t *) info, NULL) == data); |
|
57 |
|
58 hb_blob_destroy ((hb_blob_t *) info); |
|
59 } |
|
60 |
|
61 hb_coretext_shaper_face_data_t * |
|
62 _hb_coretext_shaper_face_data_create (hb_face_t *face) |
|
63 { |
|
64 hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) calloc (1, sizeof (hb_coretext_shaper_face_data_t)); |
|
65 if (unlikely (!data)) |
|
66 return NULL; |
|
67 |
|
68 hb_blob_t *blob = hb_face_reference_blob (face); |
|
69 unsigned int blob_length; |
|
70 const char *blob_data = hb_blob_get_data (blob, &blob_length); |
|
71 if (unlikely (!blob_length)) |
|
72 DEBUG_MSG (CORETEXT, face, "Face has empty blob"); |
|
73 |
|
74 CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data); |
|
75 data->cg_font = CGFontCreateWithDataProvider (provider); |
|
76 CGDataProviderRelease (provider); |
|
77 |
|
78 if (unlikely (!data->cg_font)) { |
|
79 DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); |
|
80 free (data); |
|
81 return NULL; |
|
82 } |
|
83 |
|
84 return data; |
|
85 } |
|
86 |
|
87 void |
|
88 _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) |
|
89 { |
|
90 CFRelease (data->cg_font); |
|
91 free (data); |
|
92 } |
|
93 |
|
94 CGFontRef |
|
95 hb_coretext_face_get_cg_font (hb_face_t *face) |
|
96 { |
|
97 if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL; |
|
98 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); |
|
99 return face_data->cg_font; |
|
100 } |
|
101 |
|
102 |
|
103 /* |
|
104 * shaper font data |
|
105 */ |
|
106 |
|
107 struct hb_coretext_shaper_font_data_t { |
|
108 CTFontRef ct_font; |
|
109 }; |
|
110 |
|
111 hb_coretext_shaper_font_data_t * |
|
112 _hb_coretext_shaper_font_data_create (hb_font_t *font) |
|
113 { |
|
114 if (unlikely (!hb_coretext_shaper_face_data_ensure (font->face))) return NULL; |
|
115 |
|
116 hb_coretext_shaper_font_data_t *data = (hb_coretext_shaper_font_data_t *) calloc (1, sizeof (hb_coretext_shaper_font_data_t)); |
|
117 if (unlikely (!data)) |
|
118 return NULL; |
|
119 |
|
120 hb_face_t *face = font->face; |
|
121 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); |
|
122 |
|
123 data->ct_font = CTFontCreateWithGraphicsFont (face_data->cg_font, font->y_scale, NULL, NULL); |
|
124 if (unlikely (!data->ct_font)) { |
|
125 DEBUG_MSG (CORETEXT, font, "Font CTFontCreateWithGraphicsFont() failed"); |
|
126 free (data); |
|
127 return NULL; |
|
128 } |
|
129 |
|
130 return data; |
|
131 } |
|
132 |
|
133 void |
|
134 _hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data) |
|
135 { |
|
136 CFRelease (data->ct_font); |
|
137 free (data); |
|
138 } |
|
139 |
|
140 |
|
141 /* |
|
142 * shaper shape_plan data |
|
143 */ |
|
144 |
|
145 struct hb_coretext_shaper_shape_plan_data_t {}; |
|
146 |
|
147 hb_coretext_shaper_shape_plan_data_t * |
|
148 _hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, |
|
149 const hb_feature_t *user_features HB_UNUSED, |
|
150 unsigned int num_user_features HB_UNUSED) |
|
151 { |
|
152 return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; |
|
153 } |
|
154 |
|
155 void |
|
156 _hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data HB_UNUSED) |
|
157 { |
|
158 } |
|
159 |
|
160 CTFontRef |
|
161 hb_coretext_font_get_ct_font (hb_font_t *font) |
|
162 { |
|
163 if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return NULL; |
|
164 hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); |
|
165 return font_data->ct_font; |
|
166 } |
|
167 |
|
168 |
|
169 /* |
|
170 * shaper |
|
171 */ |
|
172 |
|
173 struct feature_record_t { |
|
174 unsigned int feature; |
|
175 unsigned int setting; |
|
176 }; |
|
177 |
|
178 struct active_feature_t { |
|
179 feature_record_t rec; |
|
180 unsigned int order; |
|
181 |
|
182 static int cmp (const active_feature_t *a, const active_feature_t *b) { |
|
183 return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : |
|
184 a->order < b->order ? -1 : a->order > b->order ? 1 : |
|
185 a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 : |
|
186 0; |
|
187 } |
|
188 bool operator== (const active_feature_t *f) { |
|
189 return cmp (this, f) == 0; |
|
190 } |
|
191 }; |
|
192 |
|
193 struct feature_event_t { |
|
194 unsigned int index; |
|
195 bool start; |
|
196 active_feature_t feature; |
|
197 |
|
198 static int cmp (const feature_event_t *a, const feature_event_t *b) { |
|
199 return a->index < b->index ? -1 : a->index > b->index ? 1 : |
|
200 a->start < b->start ? -1 : a->start > b->start ? 1 : |
|
201 active_feature_t::cmp (&a->feature, &b->feature); |
|
202 } |
|
203 }; |
|
204 |
|
205 struct range_record_t { |
|
206 CTFontRef font; |
|
207 unsigned int index_first; /* == start */ |
|
208 unsigned int index_last; /* == end - 1 */ |
|
209 }; |
|
210 |
|
211 |
|
212 /* The following enum members are added in OS X 10.8. */ |
|
213 #define kAltHalfWidthTextSelector 6 |
|
214 #define kAltProportionalTextSelector 5 |
|
215 #define kAlternateHorizKanaOffSelector 1 |
|
216 #define kAlternateHorizKanaOnSelector 0 |
|
217 #define kAlternateKanaType 34 |
|
218 #define kAlternateVertKanaOffSelector 3 |
|
219 #define kAlternateVertKanaOnSelector 2 |
|
220 #define kCaseSensitiveLayoutOffSelector 1 |
|
221 #define kCaseSensitiveLayoutOnSelector 0 |
|
222 #define kCaseSensitiveLayoutType 33 |
|
223 #define kCaseSensitiveSpacingOffSelector 3 |
|
224 #define kCaseSensitiveSpacingOnSelector 2 |
|
225 #define kContextualAlternatesOffSelector 1 |
|
226 #define kContextualAlternatesOnSelector 0 |
|
227 #define kContextualAlternatesType 36 |
|
228 #define kContextualLigaturesOffSelector 19 |
|
229 #define kContextualLigaturesOnSelector 18 |
|
230 #define kContextualSwashAlternatesOffSelector 5 |
|
231 #define kContextualSwashAlternatesOnSelector 4 |
|
232 #define kDefaultLowerCaseSelector 0 |
|
233 #define kDefaultUpperCaseSelector 0 |
|
234 #define kHistoricalLigaturesOffSelector 21 |
|
235 #define kHistoricalLigaturesOnSelector 20 |
|
236 #define kHojoCharactersSelector 12 |
|
237 #define kJIS2004CharactersSelector 11 |
|
238 #define kLowerCasePetiteCapsSelector 2 |
|
239 #define kLowerCaseSmallCapsSelector 1 |
|
240 #define kLowerCaseType 37 |
|
241 #define kMathematicalGreekOffSelector 11 |
|
242 #define kMathematicalGreekOnSelector 10 |
|
243 #define kNLCCharactersSelector 13 |
|
244 #define kQuarterWidthTextSelector 4 |
|
245 #define kScientificInferiorsSelector 4 |
|
246 #define kStylisticAltEightOffSelector 17 |
|
247 #define kStylisticAltEightOnSelector 16 |
|
248 #define kStylisticAltEighteenOffSelector 37 |
|
249 #define kStylisticAltEighteenOnSelector 36 |
|
250 #define kStylisticAltElevenOffSelector 23 |
|
251 #define kStylisticAltElevenOnSelector 22 |
|
252 #define kStylisticAltFifteenOffSelector 31 |
|
253 #define kStylisticAltFifteenOnSelector 30 |
|
254 #define kStylisticAltFiveOffSelector 11 |
|
255 #define kStylisticAltFiveOnSelector 10 |
|
256 #define kStylisticAltFourOffSelector 9 |
|
257 #define kStylisticAltFourOnSelector 8 |
|
258 #define kStylisticAltFourteenOffSelector 29 |
|
259 #define kStylisticAltFourteenOnSelector 28 |
|
260 #define kStylisticAltNineOffSelector 19 |
|
261 #define kStylisticAltNineOnSelector 18 |
|
262 #define kStylisticAltNineteenOffSelector 39 |
|
263 #define kStylisticAltNineteenOnSelector 38 |
|
264 #define kStylisticAltOneOffSelector 3 |
|
265 #define kStylisticAltOneOnSelector 2 |
|
266 #define kStylisticAltSevenOffSelector 15 |
|
267 #define kStylisticAltSevenOnSelector 14 |
|
268 #define kStylisticAltSeventeenOffSelector 35 |
|
269 #define kStylisticAltSeventeenOnSelector 34 |
|
270 #define kStylisticAltSixOffSelector 13 |
|
271 #define kStylisticAltSixOnSelector 12 |
|
272 #define kStylisticAltSixteenOffSelector 33 |
|
273 #define kStylisticAltSixteenOnSelector 32 |
|
274 #define kStylisticAltTenOffSelector 21 |
|
275 #define kStylisticAltTenOnSelector 20 |
|
276 #define kStylisticAltThirteenOffSelector 27 |
|
277 #define kStylisticAltThirteenOnSelector 26 |
|
278 #define kStylisticAltThreeOffSelector 7 |
|
279 #define kStylisticAltThreeOnSelector 6 |
|
280 #define kStylisticAltTwelveOffSelector 25 |
|
281 #define kStylisticAltTwelveOnSelector 24 |
|
282 #define kStylisticAltTwentyOffSelector 41 |
|
283 #define kStylisticAltTwentyOnSelector 40 |
|
284 #define kStylisticAltTwoOffSelector 5 |
|
285 #define kStylisticAltTwoOnSelector 4 |
|
286 #define kStylisticAlternativesType 35 |
|
287 #define kSwashAlternatesOffSelector 3 |
|
288 #define kSwashAlternatesOnSelector 2 |
|
289 #define kThirdWidthTextSelector 3 |
|
290 #define kTraditionalNamesCharactersSelector 14 |
|
291 #define kUpperCasePetiteCapsSelector 2 |
|
292 #define kUpperCaseSmallCapsSelector 1 |
|
293 #define kUpperCaseType 38 |
|
294 |
|
295 /* Table data courtesy of Apple. */ |
|
296 struct feature_mapping_t { |
|
297 FourCharCode otFeatureTag; |
|
298 uint16_t aatFeatureType; |
|
299 uint16_t selectorToEnable; |
|
300 uint16_t selectorToDisable; |
|
301 } feature_mappings[] = { |
|
302 { 'c2pc', kUpperCaseType, kUpperCasePetiteCapsSelector, kDefaultUpperCaseSelector }, |
|
303 { 'c2sc', kUpperCaseType, kUpperCaseSmallCapsSelector, kDefaultUpperCaseSelector }, |
|
304 { 'calt', kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector }, |
|
305 { 'case', kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector }, |
|
306 { 'clig', kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector }, |
|
307 { 'cpsp', kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector }, |
|
308 { 'cswh', kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector }, |
|
309 { 'dlig', kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector }, |
|
310 { 'expt', kCharacterShapeType, kExpertCharactersSelector, 16 }, |
|
311 { 'frac', kFractionsType, kDiagonalFractionsSelector, kNoFractionsSelector }, |
|
312 { 'fwid', kTextSpacingType, kMonospacedTextSelector, 7 }, |
|
313 { 'halt', kTextSpacingType, kAltHalfWidthTextSelector, 7 }, |
|
314 { 'hist', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector }, |
|
315 { 'hkna', kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, }, |
|
316 { 'hlig', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector }, |
|
317 { 'hngl', kTransliterationType, kHanjaToHangulSelector, kNoTransliterationSelector }, |
|
318 { 'hojo', kCharacterShapeType, kHojoCharactersSelector, 16 }, |
|
319 { 'hwid', kTextSpacingType, kHalfWidthTextSelector, 7 }, |
|
320 { 'ital', kItalicCJKRomanType, kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector }, |
|
321 { 'jp04', kCharacterShapeType, kJIS2004CharactersSelector, 16 }, |
|
322 { 'jp78', kCharacterShapeType, kJIS1978CharactersSelector, 16 }, |
|
323 { 'jp83', kCharacterShapeType, kJIS1983CharactersSelector, 16 }, |
|
324 { 'jp90', kCharacterShapeType, kJIS1990CharactersSelector, 16 }, |
|
325 { 'liga', kLigaturesType, kCommonLigaturesOnSelector, kCommonLigaturesOffSelector }, |
|
326 { 'lnum', kNumberCaseType, kUpperCaseNumbersSelector, 2 }, |
|
327 { 'mgrk', kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector }, |
|
328 { 'nlck', kCharacterShapeType, kNLCCharactersSelector, 16 }, |
|
329 { 'onum', kNumberCaseType, kLowerCaseNumbersSelector, 2 }, |
|
330 { 'ordn', kVerticalPositionType, kOrdinalsSelector, kNormalPositionSelector }, |
|
331 { 'palt', kTextSpacingType, kAltProportionalTextSelector, 7 }, |
|
332 { 'pcap', kLowerCaseType, kLowerCasePetiteCapsSelector, kDefaultLowerCaseSelector }, |
|
333 { 'pkna', kTextSpacingType, kProportionalTextSelector, 7 }, |
|
334 { 'pnum', kNumberSpacingType, kProportionalNumbersSelector, 4 }, |
|
335 { 'pwid', kTextSpacingType, kProportionalTextSelector, 7 }, |
|
336 { 'qwid', kTextSpacingType, kQuarterWidthTextSelector, 7 }, |
|
337 { 'ruby', kRubyKanaType, kRubyKanaOnSelector, kRubyKanaOffSelector }, |
|
338 { 'sinf', kVerticalPositionType, kScientificInferiorsSelector, kNormalPositionSelector }, |
|
339 { 'smcp', kLowerCaseType, kLowerCaseSmallCapsSelector, kDefaultLowerCaseSelector }, |
|
340 { 'smpl', kCharacterShapeType, kSimplifiedCharactersSelector, 16 }, |
|
341 { 'ss01', kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector }, |
|
342 { 'ss02', kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector }, |
|
343 { 'ss03', kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector }, |
|
344 { 'ss04', kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector }, |
|
345 { 'ss05', kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector }, |
|
346 { 'ss06', kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector }, |
|
347 { 'ss07', kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector }, |
|
348 { 'ss08', kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector }, |
|
349 { 'ss09', kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector }, |
|
350 { 'ss10', kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector }, |
|
351 { 'ss11', kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector }, |
|
352 { 'ss12', kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector }, |
|
353 { 'ss13', kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector }, |
|
354 { 'ss14', kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector }, |
|
355 { 'ss15', kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector }, |
|
356 { 'ss16', kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector }, |
|
357 { 'ss17', kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector }, |
|
358 { 'ss18', kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector }, |
|
359 { 'ss19', kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector }, |
|
360 { 'ss20', kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector }, |
|
361 { 'subs', kVerticalPositionType, kInferiorsSelector, kNormalPositionSelector }, |
|
362 { 'sups', kVerticalPositionType, kSuperiorsSelector, kNormalPositionSelector }, |
|
363 { 'swsh', kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector }, |
|
364 { 'titl', kStyleOptionsType, kTitlingCapsSelector, kNoStyleOptionsSelector }, |
|
365 { 'tnam', kCharacterShapeType, kTraditionalNamesCharactersSelector, 16 }, |
|
366 { 'tnum', kNumberSpacingType, kMonospacedNumbersSelector, 4 }, |
|
367 { 'trad', kCharacterShapeType, kTraditionalCharactersSelector, 16 }, |
|
368 { 'twid', kTextSpacingType, kThirdWidthTextSelector, 7 }, |
|
369 { 'unic', kLetterCaseType, 14, 15 }, |
|
370 { 'valt', kTextSpacingType, kAltProportionalTextSelector, 7 }, |
|
371 { 'vert', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector }, |
|
372 { 'vhal', kTextSpacingType, kAltHalfWidthTextSelector, 7 }, |
|
373 { 'vkna', kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector }, |
|
374 { 'vpal', kTextSpacingType, kAltProportionalTextSelector, 7 }, |
|
375 { 'vrt2', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector }, |
|
376 { 'zero', kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector }, |
|
377 }; |
|
378 |
|
379 static int |
|
380 _hb_feature_mapping_cmp (const void *key_, const void *entry_) |
|
381 { |
|
382 unsigned int key = * (unsigned int *) key_; |
|
383 const feature_mapping_t * entry = (const feature_mapping_t *) entry_; |
|
384 return key < entry->otFeatureTag ? -1 : |
|
385 key > entry->otFeatureTag ? 1 : |
|
386 0; |
|
387 } |
|
388 |
|
389 hb_bool_t |
|
390 _hb_coretext_shape (hb_shape_plan_t *shape_plan, |
|
391 hb_font_t *font, |
|
392 hb_buffer_t *buffer, |
|
393 const hb_feature_t *features, |
|
394 unsigned int num_features) |
|
395 { |
|
396 hb_face_t *face = font->face; |
|
397 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); |
|
398 hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); |
|
399 |
|
400 /* |
|
401 * Set up features. |
|
402 * (copied + modified from code from hb-uniscribe.cc) |
|
403 */ |
|
404 hb_auto_array_t<feature_record_t> feature_records; |
|
405 hb_auto_array_t<range_record_t> range_records; |
|
406 if (num_features) |
|
407 { |
|
408 /* Sort features by start/end events. */ |
|
409 hb_auto_array_t<feature_event_t> feature_events; |
|
410 for (unsigned int i = 0; i < num_features; i++) |
|
411 { |
|
412 const feature_mapping_t * mapping = (const feature_mapping_t *) bsearch (&features[i].tag, |
|
413 feature_mappings, |
|
414 ARRAY_LENGTH (feature_mappings), |
|
415 sizeof (feature_mappings[0]), |
|
416 _hb_feature_mapping_cmp); |
|
417 if (!mapping) |
|
418 continue; |
|
419 |
|
420 active_feature_t feature; |
|
421 feature.rec.feature = mapping->aatFeatureType; |
|
422 feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable; |
|
423 feature.order = i; |
|
424 |
|
425 feature_event_t *event; |
|
426 |
|
427 event = feature_events.push (); |
|
428 if (unlikely (!event)) |
|
429 goto fail_features; |
|
430 event->index = features[i].start; |
|
431 event->start = true; |
|
432 event->feature = feature; |
|
433 |
|
434 event = feature_events.push (); |
|
435 if (unlikely (!event)) |
|
436 goto fail_features; |
|
437 event->index = features[i].end; |
|
438 event->start = false; |
|
439 event->feature = feature; |
|
440 } |
|
441 feature_events.sort (); |
|
442 /* Add a strategic final event. */ |
|
443 { |
|
444 active_feature_t feature; |
|
445 feature.rec.feature = HB_TAG_NONE; |
|
446 feature.rec.setting = 0; |
|
447 feature.order = num_features + 1; |
|
448 |
|
449 feature_event_t *event = feature_events.push (); |
|
450 if (unlikely (!event)) |
|
451 goto fail_features; |
|
452 event->index = 0; /* This value does magic. */ |
|
453 event->start = false; |
|
454 event->feature = feature; |
|
455 } |
|
456 |
|
457 /* Scan events and save features for each range. */ |
|
458 hb_auto_array_t<active_feature_t> active_features; |
|
459 unsigned int last_index = 0; |
|
460 for (unsigned int i = 0; i < feature_events.len; i++) |
|
461 { |
|
462 feature_event_t *event = &feature_events[i]; |
|
463 |
|
464 if (event->index != last_index) |
|
465 { |
|
466 /* Save a snapshot of active features and the range. */ |
|
467 range_record_t *range = range_records.push (); |
|
468 if (unlikely (!range)) |
|
469 goto fail_features; |
|
470 |
|
471 unsigned int offset = feature_records.len; |
|
472 |
|
473 if (active_features.len) |
|
474 { |
|
475 CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); |
|
476 |
|
477 /* TODO sort and resolve conflicting features? */ |
|
478 /* active_features.sort (); */ |
|
479 for (unsigned int j = 0; j < active_features.len; j++) |
|
480 { |
|
481 CFStringRef keys[2] = { |
|
482 kCTFontFeatureTypeIdentifierKey, |
|
483 kCTFontFeatureSelectorIdentifierKey |
|
484 }; |
|
485 CFNumberRef values[2] = { |
|
486 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), |
|
487 CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) |
|
488 }; |
|
489 CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, |
|
490 (const void **) keys, |
|
491 (const void **) values, |
|
492 2, |
|
493 &kCFTypeDictionaryKeyCallBacks, |
|
494 &kCFTypeDictionaryValueCallBacks); |
|
495 CFRelease (values[0]); |
|
496 CFRelease (values[1]); |
|
497 |
|
498 CFArrayAppendValue (features_array, dict); |
|
499 CFRelease (dict); |
|
500 |
|
501 } |
|
502 |
|
503 CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, |
|
504 (const void **) &kCTFontFeatureSettingsAttribute, |
|
505 (const void **) &features_array, |
|
506 1, |
|
507 &kCFTypeDictionaryKeyCallBacks, |
|
508 &kCFTypeDictionaryValueCallBacks); |
|
509 CFRelease (features_array); |
|
510 |
|
511 CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); |
|
512 CFRelease (attributes); |
|
513 |
|
514 range->font = CTFontCreateCopyWithAttributes (font_data->ct_font, 0.0, NULL, font_desc); |
|
515 |
|
516 CFRelease (font_desc); |
|
517 } |
|
518 else |
|
519 { |
|
520 range->font = NULL; |
|
521 } |
|
522 |
|
523 range->index_first = last_index; |
|
524 range->index_last = event->index - 1; |
|
525 |
|
526 last_index = event->index; |
|
527 } |
|
528 |
|
529 if (event->start) { |
|
530 active_feature_t *feature = active_features.push (); |
|
531 if (unlikely (!feature)) |
|
532 goto fail_features; |
|
533 *feature = event->feature; |
|
534 } else { |
|
535 active_feature_t *feature = active_features.find (&event->feature); |
|
536 if (feature) |
|
537 active_features.remove (feature - active_features.array); |
|
538 } |
|
539 } |
|
540 |
|
541 if (!range_records.len) /* No active feature found. */ |
|
542 goto fail_features; |
|
543 } |
|
544 else |
|
545 { |
|
546 fail_features: |
|
547 num_features = 0; |
|
548 } |
|
549 |
|
550 #define FAIL(...) \ |
|
551 HB_STMT_START { \ |
|
552 DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \ |
|
553 return false; \ |
|
554 } HB_STMT_END; |
|
555 |
|
556 unsigned int scratch_size; |
|
557 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); |
|
558 |
|
559 #define ALLOCATE_ARRAY(Type, name, len) \ |
|
560 Type *name = (Type *) scratch; \ |
|
561 { \ |
|
562 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ |
|
563 assert (_consumed <= scratch_size); \ |
|
564 scratch += _consumed; \ |
|
565 scratch_size -= _consumed; \ |
|
566 } |
|
567 |
|
568 #define utf16_index() var1.u32 |
|
569 |
|
570 ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2); |
|
571 |
|
572 unsigned int chars_len = 0; |
|
573 for (unsigned int i = 0; i < buffer->len; i++) { |
|
574 hb_codepoint_t c = buffer->info[i].codepoint; |
|
575 buffer->info[i].utf16_index() = chars_len; |
|
576 if (likely (c < 0x10000)) |
|
577 pchars[chars_len++] = c; |
|
578 else if (unlikely (c >= 0x110000)) |
|
579 pchars[chars_len++] = 0xFFFD; |
|
580 else { |
|
581 pchars[chars_len++] = 0xD800 + ((c - 0x10000) >> 10); |
|
582 pchars[chars_len++] = 0xDC00 + ((c - 0x10000) & ((1 << 10) - 1)); |
|
583 } |
|
584 } |
|
585 |
|
586 #undef utf16_index |
|
587 |
|
588 CFStringRef string_ref = CFStringCreateWithCharactersNoCopy (NULL, |
|
589 pchars, chars_len, |
|
590 kCFAllocatorNull); |
|
591 |
|
592 CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (NULL, chars_len); |
|
593 CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref); |
|
594 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), |
|
595 kCTFontAttributeName, font_data->ct_font); |
|
596 |
|
597 if (num_features) |
|
598 { |
|
599 ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len); |
|
600 |
|
601 /* Need log_clusters to assign features. */ |
|
602 chars_len = 0; |
|
603 for (unsigned int i = 0; i < buffer->len; i++) |
|
604 { |
|
605 hb_codepoint_t c = buffer->info[i].codepoint; |
|
606 unsigned int cluster = buffer->info[i].cluster; |
|
607 log_clusters[chars_len++] = cluster; |
|
608 if (c >= 0x10000 && c < 0x110000) |
|
609 log_clusters[chars_len++] = cluster; /* Surrogates. */ |
|
610 } |
|
611 |
|
612 unsigned int start = 0; |
|
613 range_record_t *last_range = &range_records[0]; |
|
614 for (unsigned int k = 0; k < chars_len; k++) |
|
615 { |
|
616 range_record_t *range = last_range; |
|
617 while (log_clusters[k] < range->index_first) |
|
618 range--; |
|
619 while (log_clusters[k] > range->index_last) |
|
620 range++; |
|
621 if (range != last_range) |
|
622 { |
|
623 if (last_range->font) |
|
624 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start), |
|
625 kCTFontAttributeName, last_range->font); |
|
626 |
|
627 start = k; |
|
628 } |
|
629 |
|
630 last_range = range; |
|
631 } |
|
632 if (start != chars_len && last_range->font) |
|
633 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start - 1), |
|
634 kCTFontAttributeName, last_range->font); |
|
635 |
|
636 for (unsigned int i = 0; i < range_records.len; i++) |
|
637 if (range_records[i].font) |
|
638 CFRelease (range_records[i].font); |
|
639 } |
|
640 |
|
641 CTLineRef line = CTLineCreateWithAttributedString (attr_string); |
|
642 CFRelease (attr_string); |
|
643 |
|
644 CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); |
|
645 unsigned int num_runs = CFArrayGetCount (glyph_runs); |
|
646 |
|
647 buffer->len = 0; |
|
648 |
|
649 const CFRange range_all = CFRangeMake (0, 0); |
|
650 |
|
651 for (unsigned int i = 0; i < num_runs; i++) |
|
652 { |
|
653 CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (glyph_runs, i); |
|
654 |
|
655 /* CoreText does automatic font fallback (AKA "cascading") for characters |
|
656 * not supported by the requested font, and provides no way to turn it off, |
|
657 * so we detect if the returned run uses a font other than the requested |
|
658 * one and fill in the buffer with .notdef glyphs instead of random glyph |
|
659 * indices from a different font. |
|
660 */ |
|
661 CFDictionaryRef attributes = CTRunGetAttributes (run); |
|
662 CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName)); |
|
663 CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0); |
|
664 if (!CFEqual (run_cg_font, face_data->cg_font)) |
|
665 { |
|
666 CFRelease (run_cg_font); |
|
667 |
|
668 CFRange range = CTRunGetStringRange (run); |
|
669 buffer->ensure (buffer->len + range.length); |
|
670 if (buffer->in_error) |
|
671 FAIL ("Buffer resize failed"); |
|
672 hb_glyph_info_t *info = buffer->info + buffer->len; |
|
673 |
|
674 CGGlyph notdef = 0; |
|
675 double advance = CTFontGetAdvancesForGlyphs (font_data->ct_font, kCTFontHorizontalOrientation, ¬def, NULL, 1); |
|
676 |
|
677 for (CFIndex j = range.location; j < range.location + range.length; j++) |
|
678 { |
|
679 UniChar ch = CFStringGetCharacterAtIndex (string_ref, j); |
|
680 if (hb_in_range<UniChar> (ch, 0xDC00, 0xDFFF) && range.location < j) |
|
681 { |
|
682 ch = CFStringGetCharacterAtIndex (string_ref, j - 1); |
|
683 if (hb_in_range<UniChar> (ch, 0xD800, 0xDBFF)) |
|
684 /* This is the second of a surrogate pair. Don't need .notdef |
|
685 * for this one. */ |
|
686 continue; |
|
687 } |
|
688 |
|
689 info->codepoint = notdef; |
|
690 /* TODO We have to fixup clusters later. See vis_clusters in |
|
691 * hb-uniscribe.cc for example. */ |
|
692 info->cluster = j; |
|
693 |
|
694 info->mask = advance; |
|
695 info->var1.u32 = 0; |
|
696 info->var2.u32 = 0; |
|
697 |
|
698 info++; |
|
699 buffer->len++; |
|
700 } |
|
701 continue; |
|
702 } |
|
703 CFRelease (run_cg_font); |
|
704 |
|
705 unsigned int num_glyphs = CTRunGetGlyphCount (run); |
|
706 if (num_glyphs == 0) |
|
707 continue; |
|
708 |
|
709 buffer->ensure (buffer->len + num_glyphs); |
|
710 |
|
711 scratch = buffer->get_scratch_buffer (&scratch_size); |
|
712 |
|
713 /* Testing indicates that CTRunGetGlyphsPtr, etc (almost?) always |
|
714 * succeed, and so copying data to our own buffer will be rare. */ |
|
715 |
|
716 const CGGlyph* glyphs = CTRunGetGlyphsPtr (run); |
|
717 if (!glyphs) { |
|
718 ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs); |
|
719 CTRunGetGlyphs (run, range_all, glyph_buf); |
|
720 glyphs = glyph_buf; |
|
721 } |
|
722 |
|
723 const CGPoint* positions = CTRunGetPositionsPtr (run); |
|
724 if (!positions) { |
|
725 ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs); |
|
726 CTRunGetPositions (run, range_all, position_buf); |
|
727 positions = position_buf; |
|
728 } |
|
729 |
|
730 const CFIndex* string_indices = CTRunGetStringIndicesPtr (run); |
|
731 if (!string_indices) { |
|
732 ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs); |
|
733 CTRunGetStringIndices (run, range_all, index_buf); |
|
734 string_indices = index_buf; |
|
735 } |
|
736 |
|
737 #undef ALLOCATE_ARRAY |
|
738 |
|
739 double run_width = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL); |
|
740 |
|
741 for (unsigned int j = 0; j < num_glyphs; j++) { |
|
742 double advance = (j + 1 < num_glyphs ? positions[j + 1].x : positions[0].x + run_width) - positions[j].x; |
|
743 |
|
744 hb_glyph_info_t *info = &buffer->info[buffer->len]; |
|
745 |
|
746 info->codepoint = glyphs[j]; |
|
747 info->cluster = string_indices[j]; |
|
748 |
|
749 /* Currently, we do all x-positioning by setting the advance, we never use x-offset. */ |
|
750 info->mask = advance; |
|
751 info->var1.u32 = 0; |
|
752 info->var2.u32 = positions[j].y; |
|
753 |
|
754 buffer->len++; |
|
755 } |
|
756 } |
|
757 |
|
758 buffer->clear_positions (); |
|
759 |
|
760 unsigned int count = buffer->len; |
|
761 for (unsigned int i = 0; i < count; ++i) { |
|
762 hb_glyph_info_t *info = &buffer->info[i]; |
|
763 hb_glyph_position_t *pos = &buffer->pos[i]; |
|
764 |
|
765 /* TODO vertical */ |
|
766 pos->x_advance = info->mask; |
|
767 pos->x_offset = info->var1.u32; |
|
768 pos->y_offset = info->var2.u32; |
|
769 } |
|
770 |
|
771 /* Fix up clusters so that we never return out-of-order indices; |
|
772 * if core text has reordered glyphs, we'll merge them to the |
|
773 * beginning of the reordered cluster. |
|
774 * |
|
775 * This does *not* mean we'll form the same clusters as Uniscribe |
|
776 * or the native OT backend, only that the cluster indices will be |
|
777 * monotonic in the output buffer. */ |
|
778 if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) { |
|
779 unsigned int prev_cluster = 0; |
|
780 for (unsigned int i = 0; i < count; i++) { |
|
781 unsigned int curr_cluster = buffer->info[i].cluster; |
|
782 if (curr_cluster < prev_cluster) { |
|
783 for (unsigned int j = i; j > 0; j--) { |
|
784 if (buffer->info[j - 1].cluster > curr_cluster) |
|
785 buffer->info[j - 1].cluster = curr_cluster; |
|
786 else |
|
787 break; |
|
788 } |
|
789 } |
|
790 prev_cluster = curr_cluster; |
|
791 } |
|
792 } else { |
|
793 unsigned int prev_cluster = (unsigned int)-1; |
|
794 for (unsigned int i = 0; i < count; i++) { |
|
795 unsigned int curr_cluster = buffer->info[i].cluster; |
|
796 if (curr_cluster > prev_cluster) { |
|
797 for (unsigned int j = i; j > 0; j--) { |
|
798 if (buffer->info[j - 1].cluster < curr_cluster) |
|
799 buffer->info[j - 1].cluster = curr_cluster; |
|
800 else |
|
801 break; |
|
802 } |
|
803 } |
|
804 prev_cluster = curr_cluster; |
|
805 } |
|
806 } |
|
807 |
|
808 CFRelease (string_ref); |
|
809 CFRelease (line); |
|
810 |
|
811 return true; |
|
812 } |