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