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