gfx/harfbuzz/src/hb-coretext.cc

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

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, &notdef, 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 }

mercurial