1 /* 2 * Copyright © 1998-2004 David Turner and Werner Lemberg 3 * Copyright © 2006 Behdad Esfahbod 4 * Copyright © 2007,2008,2009 Red Hat, Inc. 5 * Copyright © 2012,2013 Google, Inc. 6 * 7 * This is part of HarfBuzz, a text shaping library. 8 * 9 * Permission is hereby granted, without written agreement and without 10 * license or royalty fees, to use, copy, modify, and distribute this 11 * software and its documentation for any purpose, provided that the 12 * above copyright notice and the following two paragraphs appear in 13 * all copies of this software. 14 * 15 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 16 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 17 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 18 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 19 * DAMAGE. 20 * 21 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 22 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 23 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 24 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 25 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 26 * 27 * Red Hat Author(s): Behdad Esfahbod 28 * Google Author(s): Behdad Esfahbod 29 */ 30 31 #include "hb-open-type-private.hh" 32 #include "hb-ot-layout-private.hh" 33 34 #include "hb-ot-layout-gdef-table.hh" 35 #include "hb-ot-layout-gsub-table.hh" 36 #include "hb-ot-layout-gpos-table.hh" 37 #include "hb-ot-layout-jstf-table.hh" 38 39 #include "hb-ot-map-private.hh" 40 41 #include <stdlib.h> 42 #include <string.h> 43 44 45 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face) 46 47 hb_ot_layout_t * 48 _hb_ot_layout_create (hb_face_t *face) 49 { 50 hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t)); 51 if (unlikely (!layout)) 52 return NULL; 53 54 layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF)); 55 layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob); 56 57 layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB)); 58 layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob); 59 60 layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS)); 61 layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob); 62 63 { 64 /* 65 * The ugly business of blacklisting individual fonts' tables happen here! 66 * See this thread for why we finally had to bend in and do this: 67 * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html 68 */ 69 unsigned int gdef_len = hb_blob_get_length (layout->gdef_blob); 70 unsigned int gsub_len = hb_blob_get_length (layout->gsub_blob); 71 unsigned int gpos_len = hb_blob_get_length (layout->gpos_blob); 72 if (0 73 /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */ 74 || (442 == gdef_len && 42038 == gpos_len && 2874 == gsub_len) 75 /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */ 76 || (430 == gdef_len && 40662 == gpos_len && 2874 == gsub_len) 77 /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */ 78 || (442 == gdef_len && 39116 == gpos_len && 2874 == gsub_len) 79 /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */ 80 || (430 == gdef_len && 39374 == gpos_len && 2874 == gsub_len) 81 /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */ 82 || (490 == gdef_len && 41638 == gpos_len && 3046 == gsub_len) 83 /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */ 84 || (478 == gdef_len && 41902 == gpos_len && 3046 == gsub_len) 85 ) 86 { 87 /* In certain versions of Times New Roman Italic and Bold Italic, 88 * ASCII double quotation mark U+0022, mapped to glyph 5, has wrong 89 * glyph class 3 (mark) in GDEF. Nuke the GDEF to avoid zero-width 90 * double-quote. See: 91 * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html 92 */ 93 if (3 == layout->gdef->get_glyph_class (5)) 94 layout->gdef = &OT::Null(OT::GDEF); 95 } 96 else if (0 97 /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c tahoma.ttf from Windows 8 */ 98 || (898 == gdef_len && 46470 == gpos_len && 12554 == gsub_len) 99 /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc tahomabd.ttf from Windows 8 */ 100 || (910 == gdef_len && 47732 == gpos_len && 12566 == gsub_len) 101 /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e tahoma.ttf from Windows 8.1 */ 102 || (928 == gdef_len && 59332 == gpos_len && 23298 == gsub_len) 103 /* sha1sum:6d400781948517c3c0441ba42acb309584b73033 tahomabd.ttf from Windows 8.1 */ 104 || (940 == gdef_len && 60732 == gpos_len && 23310 == gsub_len) 105 /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846 tahoma.ttf from Windows 10 */ 106 || (994 == gdef_len && 60336 == gpos_len && 24474 == gsub_len) 107 /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343 tahomabd.ttf from Windows 10 */ 108 || (1006 == gdef_len && 61740 == gpos_len && 24470 == gsub_len) 109 /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7 Tahoma.ttf from Mac OS X 10.9 */ 110 || (832 == gdef_len && 47162 == gpos_len && 7324 == gsub_len) 111 /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba Tahoma Bold.ttf from Mac OS X 10.9 */ 112 || (844 == gdef_len && 45474 == gpos_len && 7302 == gsub_len) 113 /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0 himalaya.ttf from Windows 8 */ 114 || (192 == gdef_len && 7254 == gpos_len && 12638 == gsub_len) 115 /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427 himalaya.ttf from Windows 8.1 */ 116 || (192 == gdef_len && 7254 == gpos_len && 12690 == gsub_len) 117 /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44 cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */ 118 /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371 cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */ 119 || (188 == gdef_len && 3852 == gpos_len && 248 == gsub_len) 120 /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */ 121 /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */ 122 || (188 == gdef_len && 3426 == gpos_len && 264 == gsub_len) 123 ) 124 { 125 /* Many versions of Tahoma have bad GDEF tables that incorrectly classify some spacing marks 126 * such as certain IPA symbols as glyph class 3. So do older versions of Microsoft Himalaya, 127 * and the version of Cantarell shipped by Ubuntu 16.04. 128 * Nuke the GDEF tables of these fonts to avoid unwanted width-zeroing. 129 * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 130 * https://bugzilla.mozilla.org/show_bug.cgi?id=1279693 131 * https://bugzilla.mozilla.org/show_bug.cgi?id=1279875 132 */ 133 layout->gdef = &OT::Null(OT::GDEF); 134 } 135 } 136 137 layout->gsub_lookup_count = layout->gsub->get_lookup_count (); 138 layout->gpos_lookup_count = layout->gpos->get_lookup_count (); 139 140 layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t)); 141 layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t)); 142 143 if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) || 144 (layout->gpos_lookup_count && !layout->gpos_accels))) 145 { 146 _hb_ot_layout_destroy (layout); 147 return NULL; 148 } 149 150 for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) 151 layout->gsub_accels[i].init (layout->gsub->get_lookup (i)); 152 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) 153 layout->gpos_accels[i].init (layout->gpos->get_lookup (i)); 154 155 return layout; 156 } 157 158 void 159 _hb_ot_layout_destroy (hb_ot_layout_t *layout) 160 { 161 for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) 162 layout->gsub_accels[i].fini (); 163 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) 164 layout->gpos_accels[i].fini (); 165 166 free (layout->gsub_accels); 167 free (layout->gpos_accels); 168 169 hb_blob_destroy (layout->gdef_blob); 170 hb_blob_destroy (layout->gsub_blob); 171 hb_blob_destroy (layout->gpos_blob); 172 173 free (layout); 174 } 175 176 static inline const OT::GDEF& 177 _get_gdef (hb_face_t *face) 178 { 179 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF); 180 return *hb_ot_layout_from_face (face)->gdef; 181 } 182 static inline const OT::GSUB& 183 _get_gsub (hb_face_t *face) 184 { 185 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB); 186 return *hb_ot_layout_from_face (face)->gsub; 187 } 188 static inline const OT::GPOS& 189 _get_gpos (hb_face_t *face) 190 { 191 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS); 192 return *hb_ot_layout_from_face (face)->gpos; 193 } 194 195 196 /* 197 * GDEF 198 */ 199 200 hb_bool_t 201 hb_ot_layout_has_glyph_classes (hb_face_t *face) 202 { 203 return _get_gdef (face).has_glyph_classes (); 204 } 205 206 /** 207 * hb_ot_layout_get_glyph_class: 208 * 209 * Since: 0.9.7 210 **/ 211 hb_ot_layout_glyph_class_t 212 hb_ot_layout_get_glyph_class (hb_face_t *face, 213 hb_codepoint_t glyph) 214 { 215 return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph); 216 } 217 218 /** 219 * hb_ot_layout_get_glyphs_in_class: 220 * 221 * Since: 0.9.7 222 **/ 223 void 224 hb_ot_layout_get_glyphs_in_class (hb_face_t *face, 225 hb_ot_layout_glyph_class_t klass, 226 hb_set_t *glyphs /* OUT */) 227 { 228 return _get_gdef (face).get_glyphs_in_class (klass, glyphs); 229 } 230 231 unsigned int 232 hb_ot_layout_get_attach_points (hb_face_t *face, 233 hb_codepoint_t glyph, 234 unsigned int start_offset, 235 unsigned int *point_count /* IN/OUT */, 236 unsigned int *point_array /* OUT */) 237 { 238 return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array); 239 } 240 241 unsigned int 242 hb_ot_layout_get_ligature_carets (hb_font_t *font, 243 hb_direction_t direction, 244 hb_codepoint_t glyph, 245 unsigned int start_offset, 246 unsigned int *caret_count /* IN/OUT */, 247 int *caret_array /* OUT */) 248 { 249 return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); 250 } 251 252 253 /* 254 * GSUB/GPOS 255 */ 256 257 static const OT::GSUBGPOS& 258 get_gsubgpos_table (hb_face_t *face, 259 hb_tag_t table_tag) 260 { 261 switch (table_tag) { 262 case HB_OT_TAG_GSUB: return _get_gsub (face); 263 case HB_OT_TAG_GPOS: return _get_gpos (face); 264 default: return OT::Null(OT::GSUBGPOS); 265 } 266 } 267 268 269 unsigned int 270 hb_ot_layout_table_get_script_tags (hb_face_t *face, 271 hb_tag_t table_tag, 272 unsigned int start_offset, 273 unsigned int *script_count /* IN/OUT */, 274 hb_tag_t *script_tags /* OUT */) 275 { 276 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 277 278 return g.get_script_tags (start_offset, script_count, script_tags); 279 } 280 281 #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') 282 283 hb_bool_t 284 hb_ot_layout_table_find_script (hb_face_t *face, 285 hb_tag_t table_tag, 286 hb_tag_t script_tag, 287 unsigned int *script_index) 288 { 289 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 290 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 291 292 if (g.find_script_index (script_tag, script_index)) 293 return true; 294 295 /* try finding 'DFLT' */ 296 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) 297 return false; 298 299 /* try with 'dflt'; MS site has had typos and many fonts use it now :(. 300 * including many versions of DejaVu Sans Mono! */ 301 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) 302 return false; 303 304 /* try with 'latn'; some old fonts put their features there even though 305 they're really trying to support Thai, for example :( */ 306 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) 307 return false; 308 309 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 310 return false; 311 } 312 313 hb_bool_t 314 hb_ot_layout_table_choose_script (hb_face_t *face, 315 hb_tag_t table_tag, 316 const hb_tag_t *script_tags, 317 unsigned int *script_index, 318 hb_tag_t *chosen_script) 319 { 320 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 321 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 322 323 while (*script_tags) 324 { 325 if (g.find_script_index (*script_tags, script_index)) { 326 if (chosen_script) 327 *chosen_script = *script_tags; 328 return true; 329 } 330 script_tags++; 331 } 332 333 /* try finding 'DFLT' */ 334 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { 335 if (chosen_script) 336 *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; 337 return false; 338 } 339 340 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 341 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) { 342 if (chosen_script) 343 *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE; 344 return false; 345 } 346 347 /* try with 'latn'; some old fonts put their features there even though 348 they're really trying to support Thai, for example :( */ 349 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { 350 if (chosen_script) 351 *chosen_script = HB_OT_TAG_LATIN_SCRIPT; 352 return false; 353 } 354 355 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 356 if (chosen_script) 357 *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 358 return false; 359 } 360 361 unsigned int 362 hb_ot_layout_table_get_feature_tags (hb_face_t *face, 363 hb_tag_t table_tag, 364 unsigned int start_offset, 365 unsigned int *feature_count /* IN/OUT */, 366 hb_tag_t *feature_tags /* OUT */) 367 { 368 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 369 370 return g.get_feature_tags (start_offset, feature_count, feature_tags); 371 } 372 373 hb_bool_t 374 hb_ot_layout_table_find_feature (hb_face_t *face, 375 hb_tag_t table_tag, 376 hb_tag_t feature_tag, 377 unsigned int *feature_index) 378 { 379 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); 380 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 381 382 unsigned int num_features = g.get_feature_count (); 383 for (unsigned int i = 0; i < num_features; i++) 384 { 385 if (feature_tag == g.get_feature_tag (i)) { 386 if (feature_index) *feature_index = i; 387 return true; 388 } 389 } 390 391 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 392 return false; 393 } 394 395 396 unsigned int 397 hb_ot_layout_script_get_language_tags (hb_face_t *face, 398 hb_tag_t table_tag, 399 unsigned int script_index, 400 unsigned int start_offset, 401 unsigned int *language_count /* IN/OUT */, 402 hb_tag_t *language_tags /* OUT */) 403 { 404 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 405 406 return s.get_lang_sys_tags (start_offset, language_count, language_tags); 407 } 408 409 hb_bool_t 410 hb_ot_layout_script_find_language (hb_face_t *face, 411 hb_tag_t table_tag, 412 unsigned int script_index, 413 hb_tag_t language_tag, 414 unsigned int *language_index) 415 { 416 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); 417 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 418 419 if (s.find_lang_sys_index (language_tag, language_index)) 420 return true; 421 422 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 423 if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) 424 return false; 425 426 if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; 427 return false; 428 } 429 430 hb_bool_t 431 hb_ot_layout_language_get_required_feature_index (hb_face_t *face, 432 hb_tag_t table_tag, 433 unsigned int script_index, 434 unsigned int language_index, 435 unsigned int *feature_index) 436 { 437 return hb_ot_layout_language_get_required_feature (face, 438 table_tag, 439 script_index, 440 language_index, 441 feature_index, 442 NULL); 443 } 444 445 /** 446 * hb_ot_layout_language_get_required_feature: 447 * 448 * Since: 0.9.30 449 **/ 450 hb_bool_t 451 hb_ot_layout_language_get_required_feature (hb_face_t *face, 452 hb_tag_t table_tag, 453 unsigned int script_index, 454 unsigned int language_index, 455 unsigned int *feature_index, 456 hb_tag_t *feature_tag) 457 { 458 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 459 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 460 461 unsigned int index = l.get_required_feature_index (); 462 if (feature_index) *feature_index = index; 463 if (feature_tag) *feature_tag = g.get_feature_tag (index); 464 465 return l.has_required_feature (); 466 } 467 468 unsigned int 469 hb_ot_layout_language_get_feature_indexes (hb_face_t *face, 470 hb_tag_t table_tag, 471 unsigned int script_index, 472 unsigned int language_index, 473 unsigned int start_offset, 474 unsigned int *feature_count /* IN/OUT */, 475 unsigned int *feature_indexes /* OUT */) 476 { 477 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 478 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 479 480 return l.get_feature_indexes (start_offset, feature_count, feature_indexes); 481 } 482 483 unsigned int 484 hb_ot_layout_language_get_feature_tags (hb_face_t *face, 485 hb_tag_t table_tag, 486 unsigned int script_index, 487 unsigned int language_index, 488 unsigned int start_offset, 489 unsigned int *feature_count /* IN/OUT */, 490 hb_tag_t *feature_tags /* OUT */) 491 { 492 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 493 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 494 495 ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t)); 496 unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); 497 498 if (feature_tags) { 499 unsigned int count = *feature_count; 500 for (unsigned int i = 0; i < count; i++) 501 feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); 502 } 503 504 return ret; 505 } 506 507 508 hb_bool_t 509 hb_ot_layout_language_find_feature (hb_face_t *face, 510 hb_tag_t table_tag, 511 unsigned int script_index, 512 unsigned int language_index, 513 hb_tag_t feature_tag, 514 unsigned int *feature_index) 515 { 516 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); 517 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 518 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 519 520 unsigned int num_features = l.get_feature_count (); 521 for (unsigned int i = 0; i < num_features; i++) { 522 unsigned int f_index = l.get_feature_index (i); 523 524 if (feature_tag == g.get_feature_tag (f_index)) { 525 if (feature_index) *feature_index = f_index; 526 return true; 527 } 528 } 529 530 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 531 return false; 532 } 533 534 /** 535 * hb_ot_layout_feature_get_lookups: 536 * 537 * Since: 0.9.7 538 **/ 539 unsigned int 540 hb_ot_layout_feature_get_lookups (hb_face_t *face, 541 hb_tag_t table_tag, 542 unsigned int feature_index, 543 unsigned int start_offset, 544 unsigned int *lookup_count /* IN/OUT */, 545 unsigned int *lookup_indexes /* OUT */) 546 { 547 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 548 const OT::Feature &f = g.get_feature (feature_index); 549 550 return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); 551 } 552 553 /** 554 * hb_ot_layout_table_get_lookup_count: 555 * 556 * Since: 0.9.22 557 **/ 558 unsigned int 559 hb_ot_layout_table_get_lookup_count (hb_face_t *face, 560 hb_tag_t table_tag) 561 { 562 switch (table_tag) 563 { 564 case HB_OT_TAG_GSUB: 565 { 566 return hb_ot_layout_from_face (face)->gsub_lookup_count; 567 } 568 case HB_OT_TAG_GPOS: 569 { 570 return hb_ot_layout_from_face (face)->gpos_lookup_count; 571 } 572 } 573 return 0; 574 } 575 576 static void 577 _hb_ot_layout_collect_lookups_lookups (hb_face_t *face, 578 hb_tag_t table_tag, 579 unsigned int feature_index, 580 hb_set_t *lookup_indexes /* OUT */) 581 { 582 unsigned int lookup_indices[32]; 583 unsigned int offset, len; 584 585 offset = 0; 586 do { 587 len = ARRAY_LENGTH (lookup_indices); 588 hb_ot_layout_feature_get_lookups (face, 589 table_tag, 590 feature_index, 591 offset, &len, 592 lookup_indices); 593 594 for (unsigned int i = 0; i < len; i++) 595 lookup_indexes->add (lookup_indices[i]); 596 597 offset += len; 598 } while (len == ARRAY_LENGTH (lookup_indices)); 599 } 600 601 static void 602 _hb_ot_layout_collect_lookups_features (hb_face_t *face, 603 hb_tag_t table_tag, 604 unsigned int script_index, 605 unsigned int language_index, 606 const hb_tag_t *features, 607 hb_set_t *lookup_indexes /* OUT */) 608 { 609 if (!features) 610 { 611 unsigned int required_feature_index; 612 if (hb_ot_layout_language_get_required_feature (face, 613 table_tag, 614 script_index, 615 language_index, 616 &required_feature_index, 617 NULL)) 618 _hb_ot_layout_collect_lookups_lookups (face, 619 table_tag, 620 required_feature_index, 621 lookup_indexes); 622 623 /* All features */ 624 unsigned int feature_indices[32]; 625 unsigned int offset, len; 626 627 offset = 0; 628 do { 629 len = ARRAY_LENGTH (feature_indices); 630 hb_ot_layout_language_get_feature_indexes (face, 631 table_tag, 632 script_index, 633 language_index, 634 offset, &len, 635 feature_indices); 636 637 for (unsigned int i = 0; i < len; i++) 638 _hb_ot_layout_collect_lookups_lookups (face, 639 table_tag, 640 feature_indices[i], 641 lookup_indexes); 642 643 offset += len; 644 } while (len == ARRAY_LENGTH (feature_indices)); 645 } 646 else 647 { 648 for (; *features; features++) 649 { 650 unsigned int feature_index; 651 if (hb_ot_layout_language_find_feature (face, 652 table_tag, 653 script_index, 654 language_index, 655 *features, 656 &feature_index)) 657 _hb_ot_layout_collect_lookups_lookups (face, 658 table_tag, 659 feature_index, 660 lookup_indexes); 661 } 662 } 663 } 664 665 static void 666 _hb_ot_layout_collect_lookups_languages (hb_face_t *face, 667 hb_tag_t table_tag, 668 unsigned int script_index, 669 const hb_tag_t *languages, 670 const hb_tag_t *features, 671 hb_set_t *lookup_indexes /* OUT */) 672 { 673 _hb_ot_layout_collect_lookups_features (face, 674 table_tag, 675 script_index, 676 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, 677 features, 678 lookup_indexes); 679 680 if (!languages) 681 { 682 /* All languages */ 683 unsigned int count = hb_ot_layout_script_get_language_tags (face, 684 table_tag, 685 script_index, 686 0, NULL, NULL); 687 for (unsigned int language_index = 0; language_index < count; language_index++) 688 _hb_ot_layout_collect_lookups_features (face, 689 table_tag, 690 script_index, 691 language_index, 692 features, 693 lookup_indexes); 694 } 695 else 696 { 697 for (; *languages; languages++) 698 { 699 unsigned int language_index; 700 if (hb_ot_layout_script_find_language (face, 701 table_tag, 702 script_index, 703 *languages, 704 &language_index)) 705 _hb_ot_layout_collect_lookups_features (face, 706 table_tag, 707 script_index, 708 language_index, 709 features, 710 lookup_indexes); 711 } 712 } 713 } 714 715 /** 716 * hb_ot_layout_collect_lookups: 717 * 718 * Since: 0.9.8 719 **/ 720 void 721 hb_ot_layout_collect_lookups (hb_face_t *face, 722 hb_tag_t table_tag, 723 const hb_tag_t *scripts, 724 const hb_tag_t *languages, 725 const hb_tag_t *features, 726 hb_set_t *lookup_indexes /* OUT */) 727 { 728 if (!scripts) 729 { 730 /* All scripts */ 731 unsigned int count = hb_ot_layout_table_get_script_tags (face, 732 table_tag, 733 0, NULL, NULL); 734 for (unsigned int script_index = 0; script_index < count; script_index++) 735 _hb_ot_layout_collect_lookups_languages (face, 736 table_tag, 737 script_index, 738 languages, 739 features, 740 lookup_indexes); 741 } 742 else 743 { 744 for (; *scripts; scripts++) 745 { 746 unsigned int script_index; 747 if (hb_ot_layout_table_find_script (face, 748 table_tag, 749 *scripts, 750 &script_index)) 751 _hb_ot_layout_collect_lookups_languages (face, 752 table_tag, 753 script_index, 754 languages, 755 features, 756 lookup_indexes); 757 } 758 } 759 } 760 761 /** 762 * hb_ot_layout_lookup_collect_glyphs: 763 * 764 * Since: 0.9.7 765 **/ 766 void 767 hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, 768 hb_tag_t table_tag, 769 unsigned int lookup_index, 770 hb_set_t *glyphs_before, /* OUT. May be NULL */ 771 hb_set_t *glyphs_input, /* OUT. May be NULL */ 772 hb_set_t *glyphs_after, /* OUT. May be NULL */ 773 hb_set_t *glyphs_output /* OUT. May be NULL */) 774 { 775 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return; 776 777 OT::hb_collect_glyphs_context_t c (face, 778 glyphs_before, 779 glyphs_input, 780 glyphs_after, 781 glyphs_output); 782 783 switch (table_tag) 784 { 785 case HB_OT_TAG_GSUB: 786 { 787 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); 788 l.collect_glyphs (&c); 789 return; 790 } 791 case HB_OT_TAG_GPOS: 792 { 793 const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index); 794 l.collect_glyphs (&c); 795 return; 796 } 797 } 798 } 799 800 801 /* 802 * OT::GSUB 803 */ 804 805 hb_bool_t 806 hb_ot_layout_has_substitution (hb_face_t *face) 807 { 808 return &_get_gsub (face) != &OT::Null(OT::GSUB); 809 } 810 811 /** 812 * hb_ot_layout_lookup_would_substitute: 813 * 814 * Since: 0.9.7 815 **/ 816 hb_bool_t 817 hb_ot_layout_lookup_would_substitute (hb_face_t *face, 818 unsigned int lookup_index, 819 const hb_codepoint_t *glyphs, 820 unsigned int glyphs_length, 821 hb_bool_t zero_context) 822 { 823 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false; 824 return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context); 825 } 826 827 hb_bool_t 828 hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, 829 unsigned int lookup_index, 830 const hb_codepoint_t *glyphs, 831 unsigned int glyphs_length, 832 hb_bool_t zero_context) 833 { 834 if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false; 835 OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context); 836 837 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); 838 839 return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index]); 840 } 841 842 void 843 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) 844 { 845 OT::GSUB::substitute_start (font, buffer); 846 } 847 848 /** 849 * hb_ot_layout_lookup_substitute_closure: 850 * 851 * Since: 0.9.7 852 **/ 853 void 854 hb_ot_layout_lookup_substitute_closure (hb_face_t *face, 855 unsigned int lookup_index, 856 hb_set_t *glyphs) 857 { 858 OT::hb_closure_context_t c (face, glyphs); 859 860 const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index); 861 862 l.closure (&c); 863 } 864 865 /* 866 * OT::GPOS 867 */ 868 869 hb_bool_t 870 hb_ot_layout_has_positioning (hb_face_t *face) 871 { 872 return &_get_gpos (face) != &OT::Null(OT::GPOS); 873 } 874 875 void 876 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) 877 { 878 OT::GPOS::position_start (font, buffer); 879 } 880 881 void 882 hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer) 883 { 884 OT::GPOS::position_finish_advances (font, buffer); 885 } 886 887 void 888 hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) 889 { 890 OT::GPOS::position_finish_offsets (font, buffer); 891 } 892 893 /** 894 * hb_ot_layout_get_size_params: 895 * 896 * Since: 0.9.10 897 **/ 898 hb_bool_t 899 hb_ot_layout_get_size_params (hb_face_t *face, 900 unsigned int *design_size, /* OUT. May be NULL */ 901 unsigned int *subfamily_id, /* OUT. May be NULL */ 902 unsigned int *subfamily_name_id, /* OUT. May be NULL */ 903 unsigned int *range_start, /* OUT. May be NULL */ 904 unsigned int *range_end /* OUT. May be NULL */) 905 { 906 const OT::GPOS &gpos = _get_gpos (face); 907 const hb_tag_t tag = HB_TAG ('s','i','z','e'); 908 909 unsigned int num_features = gpos.get_feature_count (); 910 for (unsigned int i = 0; i < num_features; i++) 911 { 912 if (tag == gpos.get_feature_tag (i)) 913 { 914 const OT::Feature &f = gpos.get_feature (i); 915 const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag); 916 917 if (params.designSize) 918 { 919 #define PARAM(a, A) if (a) *a = params.A 920 PARAM (design_size, designSize); 921 PARAM (subfamily_id, subfamilyID); 922 PARAM (subfamily_name_id, subfamilyNameID); 923 PARAM (range_start, rangeStart); 924 PARAM (range_end, rangeEnd); 925 #undef PARAM 926 927 return true; 928 } 929 } 930 } 931 932 #define PARAM(a, A) if (a) *a = 0 933 PARAM (design_size, designSize); 934 PARAM (subfamily_id, subfamilyID); 935 PARAM (subfamily_name_id, subfamilyNameID); 936 PARAM (range_start, rangeStart); 937 PARAM (range_end, rangeEnd); 938 #undef PARAM 939 940 return false; 941 } 942 943 944 /* 945 * Parts of different types are implemented here such that they have direct 946 * access to GSUB/GPOS lookups. 947 */ 948 949 950 struct GSUBProxy 951 { 952 static const unsigned int table_index = 0; 953 static const bool inplace = false; 954 typedef OT::SubstLookup Lookup; 955 956 GSUBProxy (hb_face_t *face) : 957 table (*hb_ot_layout_from_face (face)->gsub), 958 accels (hb_ot_layout_from_face (face)->gsub_accels) {} 959 960 const OT::GSUB &table; 961 const hb_ot_layout_lookup_accelerator_t *accels; 962 }; 963 964 struct GPOSProxy 965 { 966 static const unsigned int table_index = 1; 967 static const bool inplace = true; 968 typedef OT::PosLookup Lookup; 969 970 GPOSProxy (hb_face_t *face) : 971 table (*hb_ot_layout_from_face (face)->gpos), 972 accels (hb_ot_layout_from_face (face)->gpos_accels) {} 973 974 const OT::GPOS &table; 975 const hb_ot_layout_lookup_accelerator_t *accels; 976 }; 977 978 979 struct hb_get_subtables_context_t : 980 OT::hb_dispatch_context_t<hb_get_subtables_context_t, hb_void_t, HB_DEBUG_APPLY> 981 { 982 template <typename Type> 983 static inline bool apply_to (const void *obj, OT::hb_apply_context_t *c) 984 { 985 const Type *typed_obj = (const Type *) obj; 986 return typed_obj->apply (c); 987 } 988 989 typedef bool (*hb_apply_func_t) (const void *obj, OT::hb_apply_context_t *c); 990 991 struct hb_applicable_t 992 { 993 inline void init (const void *obj_, hb_apply_func_t apply_func_) 994 { 995 obj = obj_; 996 apply_func = apply_func_; 997 } 998 999 inline bool apply (OT::hb_apply_context_t *c) const { return apply_func (obj, c); } 1000 1001 private: 1002 const void *obj; 1003 hb_apply_func_t apply_func; 1004 }; 1005 1006 typedef hb_auto_array_t<hb_applicable_t> array_t; 1007 1008 /* Dispatch interface. */ 1009 inline const char *get_name (void) { return "GET_SUBTABLES"; } 1010 template <typename T> 1011 inline return_t dispatch (const T &obj) 1012 { 1013 hb_applicable_t *entry = array.push(); 1014 if (likely (entry)) 1015 entry->init (&obj, apply_to<T>); 1016 return HB_VOID; 1017 } 1018 static return_t default_return_value (void) { return HB_VOID; } 1019 bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; } 1020 1021 hb_get_subtables_context_t (array_t &array_) : 1022 array (array_), 1023 debug_depth (0) {} 1024 1025 array_t &array; 1026 unsigned int debug_depth; 1027 }; 1028 1029 static inline bool 1030 apply_forward (OT::hb_apply_context_t *c, 1031 const hb_ot_layout_lookup_accelerator_t &accel, 1032 const hb_get_subtables_context_t::array_t &subtables) 1033 { 1034 bool ret = false; 1035 hb_buffer_t *buffer = c->buffer; 1036 while (buffer->idx < buffer->len && !buffer->in_error) 1037 { 1038 bool applied = false; 1039 if (accel.may_have (buffer->cur().codepoint) && 1040 (buffer->cur().mask & c->lookup_mask) && 1041 c->check_glyph_property (&buffer->cur(), c->lookup_props)) 1042 { 1043 for (unsigned int i = 0; i < subtables.len; i++) 1044 if (subtables[i].apply (c)) 1045 { 1046 applied = true; 1047 break; 1048 } 1049 } 1050 1051 if (applied) 1052 ret = true; 1053 else 1054 buffer->next_glyph (); 1055 } 1056 return ret; 1057 } 1058 1059 static inline bool 1060 apply_backward (OT::hb_apply_context_t *c, 1061 const hb_ot_layout_lookup_accelerator_t &accel, 1062 const hb_get_subtables_context_t::array_t &subtables) 1063 { 1064 bool ret = false; 1065 hb_buffer_t *buffer = c->buffer; 1066 do 1067 { 1068 if (accel.may_have (buffer->cur().codepoint) && 1069 (buffer->cur().mask & c->lookup_mask) && 1070 c->check_glyph_property (&buffer->cur(), c->lookup_props)) 1071 { 1072 for (unsigned int i = 0; i < subtables.len; i++) 1073 if (subtables[i].apply (c)) 1074 { 1075 ret = true; 1076 break; 1077 } 1078 } 1079 /* The reverse lookup doesn't "advance" cursor (for good reason). */ 1080 buffer->idx--; 1081 1082 } 1083 while ((int) buffer->idx >= 0); 1084 return ret; 1085 } 1086 1087 template <typename Proxy> 1088 static inline void 1089 apply_string (OT::hb_apply_context_t *c, 1090 const typename Proxy::Lookup &lookup, 1091 const hb_ot_layout_lookup_accelerator_t &accel) 1092 { 1093 hb_buffer_t *buffer = c->buffer; 1094 1095 if (unlikely (!buffer->len || !c->lookup_mask)) 1096 return; 1097 1098 c->set_lookup_props (lookup.get_props ()); 1099 1100 hb_get_subtables_context_t::array_t subtables; 1101 hb_get_subtables_context_t c_get_subtables (subtables); 1102 lookup.dispatch (&c_get_subtables); 1103 1104 if (likely (!lookup.is_reverse ())) 1105 { 1106 /* in/out forward substitution/positioning */ 1107 if (Proxy::table_index == 0) 1108 buffer->clear_output (); 1109 buffer->idx = 0; 1110 1111 bool ret; 1112 ret = apply_forward (c, accel, subtables); 1113 if (ret) 1114 { 1115 if (!Proxy::inplace) 1116 buffer->swap_buffers (); 1117 else 1118 assert (!buffer->has_separate_output ()); 1119 } 1120 } 1121 else 1122 { 1123 /* in-place backward substitution/positioning */ 1124 if (Proxy::table_index == 0) 1125 buffer->remove_output (); 1126 buffer->idx = buffer->len - 1; 1127 1128 apply_backward (c, accel, subtables); 1129 } 1130 } 1131 1132 template <typename Proxy> 1133 inline void hb_ot_map_t::apply (const Proxy &proxy, 1134 const hb_ot_shape_plan_t *plan, 1135 hb_font_t *font, 1136 hb_buffer_t *buffer) const 1137 { 1138 const unsigned int table_index = proxy.table_index; 1139 unsigned int i = 0; 1140 OT::hb_apply_context_t c (table_index, font, buffer); 1141 c.set_recurse_func (Proxy::Lookup::apply_recurse_func); 1142 1143 for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) { 1144 const stage_map_t *stage = &stages[table_index][stage_index]; 1145 for (; i < stage->last_lookup; i++) 1146 { 1147 unsigned int lookup_index = lookups[table_index][i].index; 1148 if (!buffer->message (font, "start lookup %d", lookup_index)) continue; 1149 c.set_lookup_index (lookup_index); 1150 c.set_lookup_mask (lookups[table_index][i].mask); 1151 c.set_auto_zwj (lookups[table_index][i].auto_zwj); 1152 apply_string<Proxy> (&c, 1153 proxy.table.get_lookup (lookup_index), 1154 proxy.accels[lookup_index]); 1155 (void) buffer->message (font, "end lookup %d", lookup_index); 1156 } 1157 1158 if (stage->pause_func) 1159 { 1160 buffer->clear_output (); 1161 stage->pause_func (plan, font, buffer); 1162 } 1163 } 1164 } 1165 1166 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const 1167 { 1168 GSUBProxy proxy (font->face); 1169 apply (proxy, plan, font, buffer); 1170 } 1171 1172 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const 1173 { 1174 GPOSProxy proxy (font->face); 1175 apply (proxy, plan, font, buffer); 1176 } 1177 1178 HB_INTERNAL void 1179 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c, 1180 const OT::SubstLookup &lookup, 1181 const hb_ot_layout_lookup_accelerator_t &accel) 1182 { 1183 apply_string<GSUBProxy> (c, lookup, accel); 1184 }