hb-ot-layout.cc (93143B)
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.hh" 32 33 #ifndef HB_NO_OT_LAYOUT 34 35 #ifdef HB_NO_OT_TAG 36 #error "Cannot compile hb-ot-layout.cc with HB_NO_OT_TAG." 37 #endif 38 39 #include "hb-open-type.hh" 40 #include "hb-ot-layout.hh" 41 #include "hb-ot-face.hh" 42 #include "hb-ot-map.hh" 43 #include "hb-map.hh" 44 45 #include "hb-ot-kern-table.hh" 46 #include "hb-ot-layout-gdef-table.hh" 47 #include "hb-ot-layout-gsub-table.hh" 48 #include "hb-ot-layout-gpos-table.hh" 49 #include "hb-ot-layout-base-table.hh" 50 #include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise. 51 #include "hb-ot-name-table.hh" 52 #include "hb-ot-os2-table.hh" 53 54 #include "hb-aat-layout-morx-table.hh" 55 #include "hb-aat-layout-opbd-table.hh" // Just so we compile it; unused otherwise. 56 57 using OT::Layout::GSUB; 58 using OT::Layout::GPOS; 59 60 /** 61 * SECTION:hb-ot-layout 62 * @title: hb-ot-layout 63 * @short_description: OpenType Layout 64 * @include: hb-ot.h 65 * 66 * Functions for querying OpenType Layout features in the font face. 67 * See the [OpenType specification](http://www.microsoft.com/typography/otspec/) 68 * for details. 69 **/ 70 71 72 /* 73 * kern 74 */ 75 76 #ifndef HB_NO_OT_KERN 77 /** 78 * hb_ot_layout_has_kerning: 79 * @face: The #hb_face_t to work on 80 * 81 * Tests whether a face includes any kerning data in the 'kern' table. 82 * Does NOT test for kerning lookups in the GPOS table. 83 * 84 * Return value: `true` if data found, `false` otherwise 85 * 86 **/ 87 bool 88 hb_ot_layout_has_kerning (hb_face_t *face) 89 { 90 return face->table.kern->table->has_data (); 91 } 92 93 /** 94 * hb_ot_layout_has_machine_kerning: 95 * @face: The #hb_face_t to work on 96 * 97 * Tests whether a face includes any state-machine kerning in the 'kern' table. 98 * Does NOT examine the GPOS table. 99 * 100 * Return value: `true` if data found, `false` otherwise 101 * 102 **/ 103 bool 104 hb_ot_layout_has_machine_kerning (hb_face_t *face) 105 { 106 return face->table.kern->table->has_state_machine (); 107 } 108 109 /** 110 * hb_ot_layout_has_cross_kerning: 111 * @face: The #hb_face_t to work on 112 * 113 * Tests whether a face has any cross-stream kerning (i.e., kerns 114 * that make adjustments perpendicular to the direction of the text 115 * flow: Y adjustments in horizontal text or X adjustments in 116 * vertical text) in the 'kern' table. 117 * 118 * Does NOT examine the GPOS table. 119 * 120 * Return value: `true` is data found, `false` otherwise 121 * 122 **/ 123 bool 124 hb_ot_layout_has_cross_kerning (hb_face_t *face) 125 { 126 return face->table.kern->table->has_cross_stream (); 127 } 128 129 void 130 hb_ot_layout_kern (const hb_ot_shape_plan_t *plan, 131 hb_font_t *font, 132 hb_buffer_t *buffer) 133 { 134 auto &accel = *font->face->table.kern; 135 hb_blob_t *blob = accel.get_blob (); 136 137 AAT::hb_aat_apply_context_t c (plan, font, buffer, blob); 138 139 if (!buffer->message (font, "start table kern")) return; 140 c.buffer_glyph_set = accel.scratch.create_buffer_glyph_set (); 141 accel.apply (&c); 142 accel.scratch.destroy_buffer_glyph_set (c.buffer_glyph_set); 143 (void) buffer->message (font, "end table kern"); 144 } 145 #endif 146 147 148 /* 149 * GDEF 150 */ 151 152 bool 153 OT::GDEF::is_blocklisted (hb_blob_t *blob, 154 hb_face_t *face) const 155 { 156 #ifdef HB_NO_OT_LAYOUT_BLOCKLIST 157 return false; 158 #endif 159 /* The ugly business of blocklisting individual fonts' tables happen here! 160 * See this thread for why we finally had to bend in and do this: 161 * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html 162 * 163 * In certain versions of Times New Roman Italic and Bold Italic, 164 * ASCII double quotation mark U+0022 has wrong glyph class 3 (mark) 165 * in GDEF. Many versions of Tahoma have bad GDEF tables that 166 * incorrectly classify some spacing marks such as certain IPA 167 * symbols as glyph class 3. So do older versions of Microsoft 168 * Himalaya, and the version of Cantarell shipped by Ubuntu 16.04. 169 * 170 * Nuke the GDEF tables of to avoid unwanted width-zeroing. 171 * 172 * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 173 * https://bugzilla.mozilla.org/show_bug.cgi?id=1279693 174 * https://bugzilla.mozilla.org/show_bug.cgi?id=1279875 175 */ 176 switch HB_CODEPOINT_ENCODE3(blob->length, 177 face->table.GSUB->table.get_length (), 178 face->table.GPOS->table.get_length ()) 179 { 180 /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */ 181 case HB_CODEPOINT_ENCODE3 (442, 2874, 42038): 182 /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */ 183 case HB_CODEPOINT_ENCODE3 (430, 2874, 40662): 184 /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */ 185 case HB_CODEPOINT_ENCODE3 (442, 2874, 39116): 186 /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */ 187 case HB_CODEPOINT_ENCODE3 (430, 2874, 39374): 188 /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */ 189 case HB_CODEPOINT_ENCODE3 (490, 3046, 41638): 190 /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */ 191 case HB_CODEPOINT_ENCODE3 (478, 3046, 41902): 192 /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c tahoma.ttf from Windows 8 */ 193 case HB_CODEPOINT_ENCODE3 (898, 12554, 46470): 194 /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc tahomabd.ttf from Windows 8 */ 195 case HB_CODEPOINT_ENCODE3 (910, 12566, 47732): 196 /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e tahoma.ttf from Windows 8.1 */ 197 case HB_CODEPOINT_ENCODE3 (928, 23298, 59332): 198 /* sha1sum:6d400781948517c3c0441ba42acb309584b73033 tahomabd.ttf from Windows 8.1 */ 199 case HB_CODEPOINT_ENCODE3 (940, 23310, 60732): 200 /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ 201 case HB_CODEPOINT_ENCODE3 (964, 23836, 60072): 202 /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ 203 case HB_CODEPOINT_ENCODE3 (976, 23832, 61456): 204 /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846 tahoma.ttf from Windows 10 */ 205 case HB_CODEPOINT_ENCODE3 (994, 24474, 60336): 206 /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343 tahomabd.ttf from Windows 10 */ 207 case HB_CODEPOINT_ENCODE3 (1006, 24470, 61740): 208 /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ 209 case HB_CODEPOINT_ENCODE3 (1006, 24576, 61346): 210 /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ 211 case HB_CODEPOINT_ENCODE3 (1018, 24572, 62828): 212 /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5 tahoma.ttf from Windows 10 AU */ 213 case HB_CODEPOINT_ENCODE3 (1006, 24576, 61352): 214 /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2 tahomabd.ttf from Windows 10 AU */ 215 case HB_CODEPOINT_ENCODE3 (1018, 24572, 62834): 216 /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7 Tahoma.ttf from Mac OS X 10.9 */ 217 case HB_CODEPOINT_ENCODE3 (832, 7324, 47162): 218 /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba Tahoma Bold.ttf from Mac OS X 10.9 */ 219 case HB_CODEPOINT_ENCODE3 (844, 7302, 45474): 220 /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc himalaya.ttf from Windows 7 */ 221 case HB_CODEPOINT_ENCODE3 (180, 13054, 7254): 222 /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0 himalaya.ttf from Windows 8 */ 223 case HB_CODEPOINT_ENCODE3 (192, 12638, 7254): 224 /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427 himalaya.ttf from Windows 8.1 */ 225 case HB_CODEPOINT_ENCODE3 (192, 12690, 7254): 226 /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44 cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */ 227 /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371 cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */ 228 case HB_CODEPOINT_ENCODE3 (188, 248, 3852): 229 /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */ 230 /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */ 231 case HB_CODEPOINT_ENCODE3 (188, 264, 3426): 232 /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */ 233 case HB_CODEPOINT_ENCODE3 (1058, 47032, 11818): 234 /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/ 235 case HB_CODEPOINT_ENCODE3 (1046, 47030, 12600): 236 /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */ 237 case HB_CODEPOINT_ENCODE3 (1058, 71796, 16770): 238 /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */ 239 case HB_CODEPOINT_ENCODE3 (1046, 71790, 17862): 240 /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */ 241 case HB_CODEPOINT_ENCODE3 (1046, 71788, 17112): 242 /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */ 243 case HB_CODEPOINT_ENCODE3 (1058, 71794, 17514): 244 /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */ 245 case HB_CODEPOINT_ENCODE3 (1330, 109904, 57938): 246 /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */ 247 case HB_CODEPOINT_ENCODE3 (1330, 109904, 58972): 248 /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85 Padauk.ttf 249 * "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */ 250 case HB_CODEPOINT_ENCODE3 (1004, 59092, 14836): 251 /* 88d2006ca084f04af2df1954ed714a8c71e8400f Courier New.ttf from macOS 15 */ 252 case HB_CODEPOINT_ENCODE3 (588, 5078, 14418): 253 /* 608e3ebb6dd1aee521cff08eb07d500a2c59df68 Courier New Bold.ttf from macOS 15 */ 254 case HB_CODEPOINT_ENCODE3 (588, 5078, 14238): 255 /* d13221044ff054efd78f1cd8631b853c3ce85676 cour.ttf from Windows 10 */ 256 case HB_CODEPOINT_ENCODE3 (894, 17162, 33960): 257 /* 68ed4a22d8067fcf1622ac6f6e2f4d3a2e3ec394 courbd.ttf from Windows 10 */ 258 case HB_CODEPOINT_ENCODE3 (894, 17154, 34472): 259 /* 4cdb0259c96b7fd7c103821bb8f08f7cc6b211d7 cour.ttf from Windows 8.1 */ 260 case HB_CODEPOINT_ENCODE3 (816, 7868, 17052): 261 /* 920483d8a8ed37f7f0afdabbe7f679aece7c75d8 courbd.ttf from Windows 8.1 */ 262 case HB_CODEPOINT_ENCODE3 (816, 7868, 17138): 263 return true; 264 } 265 return false; 266 } 267 268 static void 269 _hb_ot_layout_set_glyph_props (hb_font_t *font, 270 hb_buffer_t *buffer) 271 { 272 _hb_buffer_assert_gsubgpos_vars (buffer); 273 274 const auto &gdef = *font->face->table.GDEF; 275 unsigned int count = buffer->len; 276 hb_glyph_info_t *info = buffer->info; 277 for (unsigned int i = 0; i < count; i++) 278 { 279 _hb_glyph_info_set_glyph_props (&info[i], gdef.get_glyph_props (info[i].codepoint)); 280 _hb_glyph_info_clear_lig_props (&info[i]); 281 } 282 } 283 284 /* Public API */ 285 286 /** 287 * hb_ot_layout_has_glyph_classes: 288 * @face: #hb_face_t to work upon 289 * 290 * Tests whether a face has any glyph classes defined in its GDEF table. 291 * 292 * Return value: `true` if data found, `false` otherwise 293 * 294 **/ 295 hb_bool_t 296 hb_ot_layout_has_glyph_classes (hb_face_t *face) 297 { 298 return face->table.GDEF->table->has_glyph_classes (); 299 } 300 301 /** 302 * hb_ot_layout_get_glyph_class: 303 * @face: The #hb_face_t to work on 304 * @glyph: The #hb_codepoint_t code point to query 305 * 306 * Fetches the GDEF class of the requested glyph in the specified face. 307 * 308 * Return value: The #hb_ot_layout_glyph_class_t glyph class of the given code 309 * point in the GDEF table of the face. 310 * 311 * Since: 0.9.7 312 **/ 313 hb_ot_layout_glyph_class_t 314 hb_ot_layout_get_glyph_class (hb_face_t *face, 315 hb_codepoint_t glyph) 316 { 317 return (hb_ot_layout_glyph_class_t) face->table.GDEF->table->get_glyph_class (glyph); 318 } 319 320 /** 321 * hb_ot_layout_get_glyphs_in_class: 322 * @face: The #hb_face_t to work on 323 * @klass: The #hb_ot_layout_glyph_class_t GDEF class to retrieve 324 * @glyphs: (out): The #hb_set_t set of all glyphs belonging to the requested 325 * class. 326 * 327 * Retrieves the set of all glyphs from the face that belong to the requested 328 * glyph class in the face's GDEF table. 329 * 330 * Since: 0.9.7 331 **/ 332 void 333 hb_ot_layout_get_glyphs_in_class (hb_face_t *face, 334 hb_ot_layout_glyph_class_t klass, 335 hb_set_t *glyphs /* OUT */) 336 { 337 return face->table.GDEF->table->get_glyphs_in_class (klass, glyphs); 338 } 339 340 #ifndef HB_NO_LAYOUT_UNUSED 341 /** 342 * hb_ot_layout_get_attach_points: 343 * @face: The #hb_face_t to work on 344 * @glyph: The #hb_codepoint_t code point to query 345 * @start_offset: offset of the first attachment point to retrieve 346 * @point_count: (inout) (nullable): Input = the maximum number of attachment points to return; 347 * Output = the actual number of attachment points returned (may be zero) 348 * @point_array: (out) (array length=point_count): The array of attachment points found for the query 349 * 350 * Fetches a list of all attachment points for the specified glyph in the GDEF 351 * table of the face. The list returned will begin at the offset provided. 352 * 353 * Useful if the client program wishes to cache the list. 354 * 355 * Return value: Total number of attachment points for @glyph. 356 * 357 **/ 358 unsigned int 359 hb_ot_layout_get_attach_points (hb_face_t *face, 360 hb_codepoint_t glyph, 361 unsigned int start_offset, 362 unsigned int *point_count /* IN/OUT */, 363 unsigned int *point_array /* OUT */) 364 { 365 return face->table.GDEF->table->get_attach_points (glyph, 366 start_offset, 367 point_count, 368 point_array); 369 } 370 /** 371 * hb_ot_layout_get_ligature_carets: 372 * @font: The #hb_font_t to work on 373 * @direction: The #hb_direction_t text direction to use 374 * @glyph: The #hb_codepoint_t code point to query 375 * @start_offset: offset of the first caret position to retrieve 376 * @caret_count: (inout) (nullable): Input = the maximum number of caret positions to return; 377 * Output = the actual number of caret positions returned (may be zero) 378 * @caret_array: (out) (array length=caret_count): The array of caret positions found for the query 379 * 380 * Fetches a list of the caret positions defined for a ligature glyph in the GDEF 381 * table of the font. The list returned will begin at the offset provided. 382 * 383 * Note that a ligature that is formed from n characters will have n-1 384 * caret positions. The first character is not represented in the array, 385 * since its caret position is the glyph position. 386 * 387 * The positions returned by this function are 'unshaped', and will have to 388 * be fixed up for kerning that may be applied to the ligature glyph. 389 * 390 * Return value: Total number of ligature caret positions for @glyph. 391 * 392 **/ 393 unsigned int 394 hb_ot_layout_get_ligature_carets (hb_font_t *font, 395 hb_direction_t direction, 396 hb_codepoint_t glyph, 397 unsigned int start_offset, 398 unsigned int *caret_count /* IN/OUT */, 399 hb_position_t *caret_array /* OUT */) 400 { 401 return font->face->table.GDEF->table->get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); 402 } 403 #endif 404 405 406 /* 407 * GSUB/GPOS 408 */ 409 410 bool 411 GSUB::is_blocklisted (hb_blob_t *blob HB_UNUSED, 412 hb_face_t *face) const 413 { 414 #ifdef HB_NO_OT_LAYOUT_BLOCKLIST 415 return false; 416 #endif 417 return false; 418 } 419 420 bool 421 GPOS::is_blocklisted (hb_blob_t *blob HB_UNUSED, 422 hb_face_t *face HB_UNUSED) const 423 { 424 #ifdef HB_NO_OT_LAYOUT_BLOCKLIST 425 return false; 426 #endif 427 return false; 428 } 429 430 static const OT::GSUBGPOS& 431 get_gsubgpos_table (hb_face_t *face, 432 hb_tag_t table_tag) 433 { 434 switch (table_tag) { 435 case HB_OT_TAG_GSUB: return *face->table.GSUB->table; 436 case HB_OT_TAG_GPOS: return *face->table.GPOS->table; 437 default: return Null (OT::GSUBGPOS); 438 } 439 } 440 441 442 /** 443 * hb_ot_layout_table_get_script_tags: 444 * @face: #hb_face_t to work upon 445 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 446 * @start_offset: offset of the first script tag to retrieve 447 * @script_count: (inout) (nullable): Input = the maximum number of script tags to return; 448 * Output = the actual number of script tags returned (may be zero) 449 * @script_tags: (out) (array length=script_count): The array of #hb_tag_t script tags found for the query 450 * 451 * Fetches a list of all scripts enumerated in the specified face's GSUB table 452 * or GPOS table. The list returned will begin at the offset provided. 453 * 454 * Return value: Total number of script tags. 455 * 456 **/ 457 unsigned int 458 hb_ot_layout_table_get_script_tags (hb_face_t *face, 459 hb_tag_t table_tag, 460 unsigned int start_offset, 461 unsigned int *script_count /* IN/OUT */, 462 hb_tag_t *script_tags /* OUT */) 463 { 464 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 465 466 return g.get_script_tags (start_offset, script_count, script_tags); 467 } 468 469 #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') 470 471 /** 472 * hb_ot_layout_table_find_script: 473 * @face: #hb_face_t to work upon 474 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 475 * @script_tag: #hb_tag_t of the script tag requested 476 * @script_index: (out): The index of the requested script tag 477 * 478 * Fetches the index if a given script tag in the specified face's GSUB table 479 * or GPOS table. 480 * 481 * Return value: `true` if the script is found, `false` otherwise 482 * 483 **/ 484 hb_bool_t 485 hb_ot_layout_table_find_script (hb_face_t *face, 486 hb_tag_t table_tag, 487 hb_tag_t script_tag, 488 unsigned int *script_index /* OUT */) 489 { 490 static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), ""); 491 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 492 493 if (g.find_script_index (script_tag, script_index)) 494 return true; 495 496 /* try finding 'DFLT' */ 497 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) 498 return false; 499 500 /* try with 'dflt'; MS site has had typos and many fonts use it now :(. 501 * including many versions of DejaVu Sans Mono! */ 502 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) 503 return false; 504 505 /* try with 'latn'; some old fonts put their features there even though 506 they're really trying to support Thai, for example :( */ 507 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) 508 return false; 509 510 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 511 return false; 512 } 513 514 #ifndef HB_DISABLE_DEPRECATED 515 /** 516 * hb_ot_layout_table_choose_script: 517 * @face: #hb_face_t to work upon 518 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 519 * @script_tags: Array of #hb_tag_t script tags 520 * @script_index: (out): The index of the chosen script 521 * @chosen_script: (out): #hb_tag_t of the chosen script 522 * 523 * Deprecated since 2.0.0 524 **/ 525 hb_bool_t 526 hb_ot_layout_table_choose_script (hb_face_t *face, 527 hb_tag_t table_tag, 528 const hb_tag_t *script_tags, 529 unsigned int *script_index /* OUT */, 530 hb_tag_t *chosen_script /* OUT */) 531 { 532 const hb_tag_t *t; 533 for (t = script_tags; *t; t++); 534 return hb_ot_layout_table_select_script (face, table_tag, t - script_tags, script_tags, script_index, chosen_script); 535 } 536 #endif 537 538 /** 539 * hb_ot_layout_table_select_script: 540 * @face: #hb_face_t to work upon 541 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 542 * @script_count: Number of script tags in the array 543 * @script_tags: Array of #hb_tag_t script tags 544 * @script_index: (out) (nullable): The index of the requested script 545 * @chosen_script: (out) (nullable): #hb_tag_t of the requested script 546 * 547 * Selects an OpenType script for @table_tag from the @script_tags array. 548 * 549 * If the table does not have any of the requested scripts, then `DFLT`, 550 * `dflt`, and `latn` tags are tried in that order. If the table still does not 551 * have any of these scripts, @script_index is set to 552 * #HB_OT_LAYOUT_NO_SCRIPT_INDEX and @chosen_script is set to #HB_TAG_NONE. 553 * 554 * Return value: 555 * `true` if one of the requested scripts is selected, `false` if a fallback 556 * script is selected or if no scripts are selected. 557 * 558 * Since: 2.0.0 559 **/ 560 hb_bool_t 561 hb_ot_layout_table_select_script (hb_face_t *face, 562 hb_tag_t table_tag, 563 unsigned int script_count, 564 const hb_tag_t *script_tags, 565 unsigned int *script_index /* OUT */, 566 hb_tag_t *chosen_script /* OUT */) 567 { 568 static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), ""); 569 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 570 unsigned int i; 571 572 for (i = 0; i < script_count; i++) 573 { 574 if (g.find_script_index (script_tags[i], script_index)) 575 { 576 if (chosen_script) 577 *chosen_script = script_tags[i]; 578 return true; 579 } 580 } 581 582 /* try finding 'DFLT' */ 583 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { 584 if (chosen_script) 585 *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; 586 return false; 587 } 588 589 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 590 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) { 591 if (chosen_script) 592 *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE; 593 return false; 594 } 595 596 /* try with 'latn'; some old fonts put their features there even though 597 they're really trying to support Thai, for example :( */ 598 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { 599 if (chosen_script) 600 *chosen_script = HB_OT_TAG_LATIN_SCRIPT; 601 return false; 602 } 603 604 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 605 if (chosen_script) 606 *chosen_script = HB_TAG_NONE; 607 return false; 608 } 609 610 611 /** 612 * hb_ot_layout_table_get_feature_tags: 613 * @face: #hb_face_t to work upon 614 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 615 * @start_offset: offset of the first feature tag to retrieve 616 * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return; 617 * Output = the actual number of feature tags returned (may be zero) 618 * @feature_tags: (out) (array length=feature_count): Array of feature tags found in the table 619 * 620 * Fetches a list of all feature tags in the given face's GSUB or GPOS table. 621 * Note that there might be duplicate feature tags, belonging to different 622 * script/language-system pairs of the table. 623 * 624 * Return value: Total number of feature tags. 625 * 626 * Since: 0.6.0 627 * 628 **/ 629 unsigned int 630 hb_ot_layout_table_get_feature_tags (hb_face_t *face, 631 hb_tag_t table_tag, 632 unsigned int start_offset, 633 unsigned int *feature_count /* IN/OUT */, 634 hb_tag_t *feature_tags /* OUT */) 635 { 636 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 637 638 return g.get_feature_tags (start_offset, feature_count, feature_tags); 639 } 640 641 642 /** 643 * hb_ot_layout_table_find_feature: 644 * @face: #hb_face_t to work upon 645 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 646 * @feature_tag: The #hb_tag_t of the requested feature tag 647 * @feature_index: (out): The index of the requested feature 648 * 649 * Fetches the index for a given feature tag in the specified face's GSUB table 650 * or GPOS table. 651 * 652 * Return value: `true` if the feature is found, `false` otherwise 653 * 654 * Since: 0.6.0 655 * 656 **/ 657 bool 658 hb_ot_layout_table_find_feature (hb_face_t *face, 659 hb_tag_t table_tag, 660 hb_tag_t feature_tag, 661 unsigned int *feature_index /* OUT */) 662 { 663 static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), ""); 664 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 665 666 unsigned int num_features = g.get_feature_count (); 667 for (unsigned int i = 0; i < num_features; i++) 668 { 669 if (feature_tag == g.get_feature_tag (i)) { 670 if (feature_index) *feature_index = i; 671 return true; 672 } 673 } 674 675 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 676 return false; 677 } 678 679 680 /** 681 * hb_ot_layout_script_get_language_tags: 682 * @face: #hb_face_t to work upon 683 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 684 * @script_index: The index of the requested script tag 685 * @start_offset: offset of the first language tag to retrieve 686 * @language_count: (inout) (nullable): Input = the maximum number of language tags to return; 687 * Output = the actual number of language tags returned (may be zero) 688 * @language_tags: (out) (array length=language_count): Array of language tags found in the table 689 * 690 * Fetches a list of language tags in the given face's GSUB or GPOS table, underneath 691 * the specified script index. The list returned will begin at the offset provided. 692 * 693 * Return value: Total number of language tags. 694 * 695 * Since: 0.6.0 696 * 697 **/ 698 unsigned int 699 hb_ot_layout_script_get_language_tags (hb_face_t *face, 700 hb_tag_t table_tag, 701 unsigned int script_index, 702 unsigned int start_offset, 703 unsigned int *language_count /* IN/OUT */, 704 hb_tag_t *language_tags /* OUT */) 705 { 706 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 707 708 return s.get_lang_sys_tags (start_offset, language_count, language_tags); 709 } 710 711 712 #ifndef HB_DISABLE_DEPRECATED 713 /** 714 * hb_ot_layout_script_find_language: 715 * @face: #hb_face_t to work upon 716 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 717 * @script_index: The index of the requested script tag 718 * @language_tag: The #hb_tag_t of the requested language 719 * @language_index: The index of the requested language 720 * 721 * Fetches the index of a given language tag in the specified face's GSUB table 722 * or GPOS table, underneath the specified script tag. 723 * 724 * Return value: `true` if the language tag is found, `false` otherwise 725 * 726 * Since: 0.6.0 727 * Deprecated: 2.0.0 728 **/ 729 hb_bool_t 730 hb_ot_layout_script_find_language (hb_face_t *face, 731 hb_tag_t table_tag, 732 unsigned int script_index, 733 hb_tag_t language_tag, 734 unsigned int *language_index) 735 { 736 return hb_ot_layout_script_select_language (face, 737 table_tag, 738 script_index, 739 1, 740 &language_tag, 741 language_index); 742 } 743 #endif 744 745 746 /** 747 * hb_ot_layout_script_select_language2: 748 * @face: #hb_face_t to work upon 749 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 750 * @script_index: The index of the requested script tag 751 * @language_count: The number of languages in the specified script 752 * @language_tags: The array of language tags 753 * @language_index: (out): The index of the chosen language 754 * @chosen_language: (out): #hb_tag_t of the chosen language 755 * 756 * Fetches the index of the first language tag fom @language_tags that is present 757 * in the specified face's GSUB or GPOS table, underneath the specified script 758 * index. 759 * 760 * If none of the given language tags is found, `false` is returned and 761 * @language_index is set to #HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX and 762 * @chosen_language is set to #HB_TAG_NONE. 763 * 764 * Return value: `true` if one of the given language tags is found, `false` otherwise 765 * 766 * Since: 7.0.0 767 **/ 768 hb_bool_t 769 hb_ot_layout_script_select_language2 (hb_face_t *face, 770 hb_tag_t table_tag, 771 unsigned int script_index, 772 unsigned int language_count, 773 const hb_tag_t *language_tags, 774 unsigned int *language_index /* OUT */, 775 hb_tag_t *chosen_language /* OUT */) 776 { 777 static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX), ""); 778 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 779 unsigned int i; 780 781 for (i = 0; i < language_count; i++) 782 { 783 if (s.find_lang_sys_index (language_tags[i], language_index)) 784 { 785 if (chosen_language) 786 *chosen_language = language_tags[i]; 787 return true; 788 } 789 } 790 791 /* try finding 'dflt' */ 792 if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) 793 { 794 if (chosen_language) 795 *chosen_language = HB_OT_TAG_DEFAULT_LANGUAGE; 796 return false; 797 } 798 799 if (language_index) 800 *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; 801 if (chosen_language) 802 *chosen_language = HB_TAG_NONE; 803 return false; 804 } 805 806 /** 807 * hb_ot_layout_script_select_language: 808 * @face: #hb_face_t to work upon 809 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 810 * @script_index: The index of the requested script tag 811 * @language_count: The number of languages in the specified script 812 * @language_tags: The array of language tags 813 * @language_index: (out): The index of the requested language 814 * 815 * Fetches the index of the first language tag fom @language_tags that is present 816 * in the specified face's GSUB or GPOS table, underneath the specified script 817 * index. 818 * 819 * If none of the given language tags is found, `false` is returned and 820 * @language_index is set to the default language index. 821 * 822 * Return value: `true` if one of the given language tags is found, `false` otherwise 823 * 824 * Since: 2.0.0 825 **/ 826 hb_bool_t 827 hb_ot_layout_script_select_language (hb_face_t *face, 828 hb_tag_t table_tag, 829 unsigned int script_index, 830 unsigned int language_count, 831 const hb_tag_t *language_tags, 832 unsigned int *language_index /* OUT */) 833 { 834 return hb_ot_layout_script_select_language2 (face, table_tag, 835 script_index, 836 language_count, language_tags, 837 language_index, nullptr); 838 } 839 840 /** 841 * hb_ot_layout_language_get_required_feature_index: 842 * @face: #hb_face_t to work upon 843 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 844 * @script_index: The index of the requested script tag 845 * @language_index: The index of the requested language tag 846 * @feature_index: (out): The index of the requested feature 847 * 848 * Fetches the index of a requested feature in the given face's GSUB or GPOS table, 849 * underneath the specified script and language. 850 * 851 * Return value: `true` if the feature is found, `false` otherwise 852 * 853 * Since: 0.6.0 854 * 855 **/ 856 hb_bool_t 857 hb_ot_layout_language_get_required_feature_index (hb_face_t *face, 858 hb_tag_t table_tag, 859 unsigned int script_index, 860 unsigned int language_index, 861 unsigned int *feature_index /* OUT */) 862 { 863 return hb_ot_layout_language_get_required_feature (face, 864 table_tag, 865 script_index, 866 language_index, 867 feature_index, 868 nullptr); 869 } 870 871 872 /** 873 * hb_ot_layout_language_get_required_feature: 874 * @face: #hb_face_t to work upon 875 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 876 * @script_index: The index of the requested script tag 877 * @language_index: The index of the requested language tag 878 * @feature_index: (out): The index of the requested feature 879 * @feature_tag: (out): The #hb_tag_t of the requested feature 880 * 881 * Fetches the tag of a requested feature index in the given face's GSUB or GPOS table, 882 * underneath the specified script and language. 883 * 884 * Return value: `true` if the feature is found, `false` otherwise 885 * 886 * Since: 0.9.30 887 **/ 888 hb_bool_t 889 hb_ot_layout_language_get_required_feature (hb_face_t *face, 890 hb_tag_t table_tag, 891 unsigned int script_index, 892 unsigned int language_index, 893 unsigned int *feature_index /* OUT */, 894 hb_tag_t *feature_tag /* OUT */) 895 { 896 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 897 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 898 899 unsigned int index = l.get_required_feature_index (); 900 if (feature_index) *feature_index = index; 901 if (feature_tag) *feature_tag = g.get_feature_tag (index); 902 903 return l.has_required_feature (); 904 } 905 906 907 /** 908 * hb_ot_layout_language_get_feature_indexes: 909 * @face: #hb_face_t to work upon 910 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 911 * @script_index: The index of the requested script tag 912 * @language_index: The index of the requested language tag 913 * @start_offset: offset of the first feature tag to retrieve 914 * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return; 915 * Output: the actual number of feature tags returned (may be zero) 916 * @feature_indexes: (out) (array length=feature_count): The array of feature indexes found for the query 917 * 918 * Fetches a list of all features in the specified face's GSUB table 919 * or GPOS table, underneath the specified script and language. The list 920 * returned will begin at the offset provided. 921 * 922 * Return value: Total number of features. 923 * 924 * Since: 0.6.0 925 * 926 **/ 927 unsigned int 928 hb_ot_layout_language_get_feature_indexes (hb_face_t *face, 929 hb_tag_t table_tag, 930 unsigned int script_index, 931 unsigned int language_index, 932 unsigned int start_offset, 933 unsigned int *feature_count /* IN/OUT */, 934 unsigned int *feature_indexes /* OUT */) 935 { 936 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 937 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 938 939 return l.get_feature_indexes (start_offset, feature_count, feature_indexes); 940 } 941 942 943 /** 944 * hb_ot_layout_language_get_feature_tags: 945 * @face: #hb_face_t to work upon 946 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 947 * @script_index: The index of the requested script tag 948 * @language_index: The index of the requested language tag 949 * @start_offset: offset of the first feature tag to retrieve 950 * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return; 951 * Output = the actual number of feature tags returned (may be zero) 952 * @feature_tags: (out) (array length=feature_count): The array of #hb_tag_t feature tags found for the query 953 * 954 * Fetches a list of all features in the specified face's GSUB table 955 * or GPOS table, underneath the specified script and language. The list 956 * returned will begin at the offset provided. 957 * 958 * Return value: Total number of feature tags. 959 * 960 * Since: 0.6.0 961 * 962 **/ 963 unsigned int 964 hb_ot_layout_language_get_feature_tags (hb_face_t *face, 965 hb_tag_t table_tag, 966 unsigned int script_index, 967 unsigned int language_index, 968 unsigned int start_offset, 969 unsigned int *feature_count /* IN/OUT */, 970 hb_tag_t *feature_tags /* OUT */) 971 { 972 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 973 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 974 975 static_assert ((sizeof (unsigned int) == sizeof (hb_tag_t)), ""); 976 unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); 977 978 if (feature_tags) { 979 unsigned int count = *feature_count; 980 for (unsigned int i = 0; i < count; i++) 981 feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); 982 } 983 984 return ret; 985 } 986 987 988 /** 989 * hb_ot_layout_language_find_feature: 990 * @face: #hb_face_t to work upon 991 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 992 * @script_index: The index of the requested script tag 993 * @language_index: The index of the requested language tag 994 * @feature_tag: #hb_tag_t of the feature tag requested 995 * @feature_index: (out): The index of the requested feature 996 * 997 * Fetches the index of a given feature tag in the specified face's GSUB table 998 * or GPOS table, underneath the specified script and language. 999 * 1000 * Return value: `true` if the feature is found, `false` otherwise 1001 * 1002 * Since: 0.6.0 1003 * 1004 **/ 1005 hb_bool_t 1006 hb_ot_layout_language_find_feature (hb_face_t *face, 1007 hb_tag_t table_tag, 1008 unsigned int script_index, 1009 unsigned int language_index, 1010 hb_tag_t feature_tag, 1011 unsigned int *feature_index /* OUT */) 1012 { 1013 static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), ""); 1014 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1015 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 1016 1017 unsigned int num_features = l.get_feature_count (); 1018 for (unsigned int i = 0; i < num_features; i++) { 1019 unsigned int f_index = l.get_feature_index (i); 1020 1021 if (feature_tag == g.get_feature_tag (f_index)) { 1022 if (feature_index) *feature_index = f_index; 1023 return true; 1024 } 1025 } 1026 1027 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 1028 return false; 1029 } 1030 1031 1032 /** 1033 * hb_ot_layout_feature_get_lookups: 1034 * @face: #hb_face_t to work upon 1035 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 1036 * @feature_index: The index of the requested feature 1037 * @start_offset: offset of the first lookup to retrieve 1038 * @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return; 1039 * Output = the actual number of lookups returned (may be zero) 1040 * @lookup_indexes: (out) (array length=lookup_count): The array of lookup indexes found for the query 1041 * 1042 * Fetches a list of all lookups enumerated for the specified feature, in 1043 * the specified face's GSUB table or GPOS table. The list returned will 1044 * begin at the offset provided. 1045 * 1046 * Return value: Total number of lookups. 1047 * 1048 * Since: 0.9.7 1049 **/ 1050 unsigned int 1051 hb_ot_layout_feature_get_lookups (hb_face_t *face, 1052 hb_tag_t table_tag, 1053 unsigned int feature_index, 1054 unsigned int start_offset, 1055 unsigned int *lookup_count /* IN/OUT */, 1056 unsigned int *lookup_indexes /* OUT */) 1057 { 1058 return hb_ot_layout_feature_with_variations_get_lookups (face, 1059 table_tag, 1060 feature_index, 1061 HB_OT_LAYOUT_NO_VARIATIONS_INDEX, 1062 start_offset, 1063 lookup_count, 1064 lookup_indexes); 1065 } 1066 1067 1068 /** 1069 * hb_ot_layout_table_get_lookup_count: 1070 * @face: #hb_face_t to work upon 1071 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 1072 * 1073 * Fetches the total number of lookups enumerated in the specified 1074 * face's GSUB table or GPOS table. 1075 * 1076 * Return value: Total number of lookups. 1077 * 1078 * Since: 0.9.22 1079 **/ 1080 unsigned int 1081 hb_ot_layout_table_get_lookup_count (hb_face_t *face, 1082 hb_tag_t table_tag) 1083 { 1084 return get_gsubgpos_table (face, table_tag).get_lookup_count (); 1085 } 1086 1087 1088 struct hb_collect_features_context_t 1089 { 1090 hb_collect_features_context_t (hb_face_t *face, 1091 hb_tag_t table_tag, 1092 hb_set_t *feature_indices_, 1093 const hb_tag_t *features) 1094 1095 : g (get_gsubgpos_table (face, table_tag)), 1096 feature_indices (feature_indices_), 1097 has_feature_filter (false), 1098 script_count (0),langsys_count (0), feature_index_count (0) 1099 { 1100 compute_feature_filter (features); 1101 } 1102 1103 void compute_feature_filter (const hb_tag_t *features) 1104 { 1105 if (features == nullptr) 1106 { 1107 has_feature_filter = false; 1108 return; 1109 } 1110 1111 has_feature_filter = true; 1112 hb_set_t features_set; 1113 for (; *features; features++) 1114 features_set.add (*features); 1115 1116 for (unsigned i = 0; i < g.get_feature_count (); i++) 1117 { 1118 hb_tag_t tag = g.get_feature_tag (i); 1119 if (features_set.has (tag)) 1120 feature_indices_filter.add(i); 1121 } 1122 } 1123 1124 bool visited (const OT::Script &s) 1125 { 1126 /* We might have Null() object here. Don't want to involve 1127 * that in the memoize. So, detect empty objects and return. */ 1128 if (unlikely (!s.has_default_lang_sys () && 1129 !s.get_lang_sys_count ())) 1130 return true; 1131 1132 if (script_count++ > HB_MAX_SCRIPTS) 1133 return true; 1134 1135 return visited (s, visited_script); 1136 } 1137 bool visited (const OT::LangSys &l) 1138 { 1139 /* We might have Null() object here. Don't want to involve 1140 * that in the memoize. So, detect empty objects and return. */ 1141 if (unlikely (!l.has_required_feature () && 1142 !l.get_feature_count ())) 1143 return true; 1144 1145 if (langsys_count++ > HB_MAX_LANGSYS) 1146 return true; 1147 1148 return visited (l, visited_langsys); 1149 } 1150 1151 bool visited_feature_indices (unsigned count) 1152 { 1153 feature_index_count += count; 1154 return feature_index_count > HB_MAX_FEATURE_INDICES; 1155 } 1156 1157 private: 1158 template <typename T> 1159 bool visited (const T &p, hb_set_t &visited_set) 1160 { 1161 hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) &p - (uintptr_t) &g); 1162 if (visited_set.has (delta)) 1163 return true; 1164 1165 visited_set.add (delta); 1166 return false; 1167 } 1168 1169 public: 1170 const OT::GSUBGPOS &g; 1171 hb_set_t *feature_indices; 1172 hb_set_t feature_indices_filter; 1173 bool has_feature_filter; 1174 1175 private: 1176 hb_set_t visited_script; 1177 hb_set_t visited_langsys; 1178 unsigned int script_count; 1179 unsigned int langsys_count; 1180 unsigned int feature_index_count; 1181 }; 1182 1183 static void 1184 langsys_collect_features (hb_collect_features_context_t *c, 1185 const OT::LangSys &l) 1186 { 1187 if (c->visited (l)) return; 1188 1189 if (!c->has_feature_filter) 1190 { 1191 /* All features. */ 1192 if (l.has_required_feature () && !c->visited_feature_indices (1)) 1193 c->feature_indices->add (l.get_required_feature_index ()); 1194 1195 // TODO(garretrieger): filter out indices >= feature count? 1196 if (!c->visited_feature_indices (l.featureIndex.len)) 1197 l.add_feature_indexes_to (c->feature_indices); 1198 } 1199 else 1200 { 1201 if (c->feature_indices_filter.is_empty()) return; 1202 unsigned int num_features = l.get_feature_count (); 1203 for (unsigned int i = 0; i < num_features; i++) 1204 { 1205 unsigned int feature_index = l.get_feature_index (i); 1206 if (!c->feature_indices_filter.has (feature_index)) continue; 1207 1208 c->feature_indices->add (feature_index); 1209 c->feature_indices_filter.del (feature_index); 1210 } 1211 } 1212 } 1213 1214 static void 1215 script_collect_features (hb_collect_features_context_t *c, 1216 const OT::Script &s, 1217 const hb_tag_t *languages) 1218 { 1219 if (c->visited (s)) return; 1220 1221 if (!languages) 1222 { 1223 /* All languages. */ 1224 if (s.has_default_lang_sys ()) 1225 langsys_collect_features (c, 1226 s.get_default_lang_sys ()); 1227 1228 1229 unsigned int count = s.get_lang_sys_count (); 1230 for (unsigned int language_index = 0; language_index < count; language_index++) 1231 langsys_collect_features (c, 1232 s.get_lang_sys (language_index)); 1233 } 1234 else 1235 { 1236 for (; *languages; languages++) 1237 { 1238 unsigned int language_index; 1239 if (s.find_lang_sys_index (*languages, &language_index)) 1240 langsys_collect_features (c, 1241 s.get_lang_sys (language_index)); 1242 1243 } 1244 } 1245 } 1246 1247 1248 /** 1249 * hb_ot_layout_collect_features: 1250 * @face: #hb_face_t to work upon 1251 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 1252 * @scripts: (nullable) (array zero-terminated=1): The array of scripts to collect features for, 1253 * terminated by %HB_TAG_NONE 1254 * @languages: (nullable) (array zero-terminated=1): The array of languages to collect features for, 1255 * terminated by %HB_TAG_NONE 1256 * @features: (nullable) (array zero-terminated=1): The array of features to collect, 1257 * terminated by %HB_TAG_NONE 1258 * @feature_indexes: (out): The set of feature indexes found for the query 1259 * 1260 * Fetches a list of all feature indexes in the specified face's GSUB table 1261 * or GPOS table, underneath the specified scripts, languages, and features. 1262 * If no list of scripts is provided, all scripts will be queried. If no list 1263 * of languages is provided, all languages will be queried. If no list of 1264 * features is provided, all features will be queried. 1265 * 1266 * Since: 1.8.5 1267 **/ 1268 void 1269 hb_ot_layout_collect_features (hb_face_t *face, 1270 hb_tag_t table_tag, 1271 const hb_tag_t *scripts, 1272 const hb_tag_t *languages, 1273 const hb_tag_t *features, 1274 hb_set_t *feature_indexes /* OUT */) 1275 { 1276 hb_collect_features_context_t c (face, table_tag, feature_indexes, features); 1277 if (!scripts) 1278 { 1279 /* All scripts. */ 1280 unsigned int count = c.g.get_script_count (); 1281 for (unsigned int script_index = 0; script_index < count; script_index++) 1282 script_collect_features (&c, 1283 c.g.get_script (script_index), 1284 languages); 1285 } 1286 else 1287 { 1288 for (; *scripts; scripts++) 1289 { 1290 unsigned int script_index; 1291 if (c.g.find_script_index (*scripts, &script_index)) 1292 script_collect_features (&c, 1293 c.g.get_script (script_index), 1294 languages); 1295 } 1296 } 1297 } 1298 1299 /** 1300 * hb_ot_layout_collect_features_map: 1301 * @face: #hb_face_t to work upon 1302 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 1303 * @script_index: The index of the requested script tag 1304 * @language_index: The index of the requested language tag 1305 * @feature_map: (out): The map of feature tag to feature index. 1306 * 1307 * Fetches the mapping from feature tags to feature indexes for 1308 * the specified script and language. 1309 * 1310 * Since: 8.1.0 1311 **/ 1312 void 1313 hb_ot_layout_collect_features_map (hb_face_t *face, 1314 hb_tag_t table_tag, 1315 unsigned script_index, 1316 unsigned language_index, 1317 hb_map_t *feature_map /* OUT */) 1318 { 1319 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1320 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 1321 1322 unsigned int count = l.get_feature_indexes (0, nullptr, nullptr); 1323 feature_map->alloc (count); 1324 1325 /* Loop in reverse, such that earlier entries win. That emulates 1326 * a linear search, which seems to be what other implementations do. 1327 * We found that with arialuni_t.ttf, the "ur" language system has 1328 * duplicate features, and the earlier ones work but not later ones. 1329 */ 1330 for (unsigned int i = count; i; i--) 1331 { 1332 unsigned feature_index = 0; 1333 unsigned feature_count = 1; 1334 l.get_feature_indexes (i - 1, &feature_count, &feature_index); 1335 if (!feature_count) 1336 break; 1337 hb_tag_t feature_tag = g.get_feature_tag (feature_index); 1338 feature_map->set (feature_tag, feature_index); 1339 } 1340 } 1341 1342 1343 /** 1344 * hb_ot_layout_collect_lookups: 1345 * @face: #hb_face_t to work upon 1346 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 1347 * @scripts: (nullable) (array zero-terminated=1): The array of scripts to collect lookups for, 1348 * terminated by %HB_TAG_NONE 1349 * @languages: (nullable) (array zero-terminated=1): The array of languages to collect lookups for, 1350 * terminated by %HB_TAG_NONE 1351 * @features: (nullable) (array zero-terminated=1): The array of features to collect lookups for, 1352 * terminated by %HB_TAG_NONE 1353 * @lookup_indexes: (out): The array of lookup indexes found for the query 1354 * 1355 * Fetches a list of all feature-lookup indexes in the specified face's GSUB 1356 * table or GPOS table, underneath the specified scripts, languages, and 1357 * features. If no list of scripts is provided, all scripts will be queried. 1358 * If no list of languages is provided, all languages will be queried. If no 1359 * list of features is provided, all features will be queried. 1360 * 1361 * Since: 0.9.8 1362 **/ 1363 void 1364 hb_ot_layout_collect_lookups (hb_face_t *face, 1365 hb_tag_t table_tag, 1366 const hb_tag_t *scripts, 1367 const hb_tag_t *languages, 1368 const hb_tag_t *features, 1369 hb_set_t *lookup_indexes /* OUT */) 1370 { 1371 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1372 1373 hb_set_t feature_indexes; 1374 hb_ot_layout_collect_features (face, table_tag, scripts, languages, features, &feature_indexes); 1375 1376 for (auto feature_index : feature_indexes) 1377 g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes); 1378 1379 g.feature_variation_collect_lookups (&feature_indexes, nullptr, lookup_indexes); 1380 } 1381 1382 1383 #ifndef HB_NO_LAYOUT_COLLECT_GLYPHS 1384 /** 1385 * hb_ot_layout_lookup_collect_glyphs: 1386 * @face: #hb_face_t to work upon 1387 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 1388 * @lookup_index: The index of the feature lookup to query 1389 * @glyphs_before: (out) (nullable): Array of glyphs preceding the substitution range 1390 * @glyphs_input: (out) (nullable): Array of input glyphs that would be substituted by the lookup 1391 * @glyphs_after: (out) (nullable): Array of glyphs following the substitution range 1392 * @glyphs_output: (out) (nullable): Array of glyphs that would be the substituted output of the lookup 1393 * 1394 * Fetches a list of all glyphs affected by the specified lookup in the 1395 * specified face's GSUB table or GPOS table. 1396 * 1397 * Since: 0.9.7 1398 **/ 1399 void 1400 hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, 1401 hb_tag_t table_tag, 1402 unsigned int lookup_index, 1403 hb_set_t *glyphs_before, /* OUT. May be NULL */ 1404 hb_set_t *glyphs_input, /* OUT. May be NULL */ 1405 hb_set_t *glyphs_after, /* OUT. May be NULL */ 1406 hb_set_t *glyphs_output /* OUT. May be NULL */) 1407 { 1408 OT::hb_collect_glyphs_context_t c (face, 1409 glyphs_before, 1410 glyphs_input, 1411 glyphs_after, 1412 glyphs_output); 1413 1414 switch (table_tag) 1415 { 1416 case HB_OT_TAG_GSUB: 1417 { 1418 const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index); 1419 l.collect_glyphs (&c); 1420 return; 1421 } 1422 case HB_OT_TAG_GPOS: 1423 { 1424 const OT::PosLookup& l = face->table.GPOS->table->get_lookup (lookup_index); 1425 l.collect_glyphs (&c); 1426 return; 1427 } 1428 } 1429 } 1430 #endif 1431 1432 1433 /* Variations support */ 1434 1435 1436 /** 1437 * hb_ot_layout_table_find_feature_variations: 1438 * @face: #hb_face_t to work upon 1439 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 1440 * @coords: The variation coordinates to query 1441 * @num_coords: The number of variation coordinates 1442 * @variations_index: (out): The array of feature variations found for the query 1443 * 1444 * Fetches a list of feature variations in the specified face's GSUB table 1445 * or GPOS table, at the specified variation coordinates. 1446 * 1447 * Return value: `true` if feature variations were found, `false` otherwise. 1448 * 1449 * Since: 1.4.0 1450 * 1451 **/ 1452 hb_bool_t 1453 hb_ot_layout_table_find_feature_variations (hb_face_t *face, 1454 hb_tag_t table_tag, 1455 const int *coords, 1456 unsigned int num_coords, 1457 unsigned int *variations_index /* out */) 1458 { 1459 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1460 const OT::GDEF &gdef = *face->table.GDEF->table; 1461 1462 auto instancer = OT::ItemVarStoreInstancer(&gdef.get_var_store(), nullptr, 1463 hb_array (coords, num_coords)); 1464 1465 return g.find_variations_index (coords, num_coords, variations_index, &instancer); 1466 } 1467 1468 1469 /** 1470 * hb_ot_layout_feature_with_variations_get_lookups: 1471 * @face: #hb_face_t to work upon 1472 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS 1473 * @feature_index: The index of the feature to query 1474 * @variations_index: The index of the feature variation to query 1475 * @start_offset: offset of the first lookup to retrieve 1476 * @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return; 1477 * Output = the actual number of lookups returned (may be zero) 1478 * @lookup_indexes: (out) (array length=lookup_count): The array of lookups found for the query 1479 * 1480 * Fetches a list of all lookups enumerated for the specified feature, in 1481 * the specified face's GSUB table or GPOS table, enabled at the specified 1482 * variations index. The list returned will begin at the offset provided. 1483 * 1484 * Return value: Total number of lookups. 1485 * 1486 * Since: 1.4.0 1487 * 1488 **/ 1489 unsigned int 1490 hb_ot_layout_feature_with_variations_get_lookups (hb_face_t *face, 1491 hb_tag_t table_tag, 1492 unsigned int feature_index, 1493 unsigned int variations_index, 1494 unsigned int start_offset, 1495 unsigned int *lookup_count /* IN/OUT */, 1496 unsigned int *lookup_indexes /* OUT */) 1497 { 1498 static_assert ((OT::FeatureVariations::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_VARIATIONS_INDEX), ""); 1499 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1500 1501 const OT::Feature &f = g.get_feature_variation (feature_index, variations_index); 1502 1503 return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); 1504 } 1505 1506 1507 /* 1508 * OT::GSUB 1509 */ 1510 1511 1512 /** 1513 * hb_ot_layout_has_substitution: 1514 * @face: #hb_face_t to work upon 1515 * 1516 * Tests whether the specified face includes any GSUB substitutions. 1517 * 1518 * Return value: `true` if data found, `false` otherwise 1519 * 1520 * Since: 0.6.0 1521 * 1522 **/ 1523 hb_bool_t 1524 hb_ot_layout_has_substitution (hb_face_t *face) 1525 { 1526 return face->table.GSUB->table->has_data (); 1527 } 1528 1529 1530 /** 1531 * hb_ot_layout_lookup_would_substitute: 1532 * @face: #hb_face_t to work upon 1533 * @lookup_index: The index of the lookup to query 1534 * @glyphs: The sequence of glyphs to query for substitution 1535 * @glyphs_length: The length of the glyph sequence 1536 * @zero_context: #hb_bool_t indicating whether pre-/post-context are disallowed 1537 * in substitutions 1538 * 1539 * Tests whether a specified lookup in the specified face would 1540 * trigger a substitution on the given glyph sequence. 1541 * 1542 * Return value: `true` if a substitution would be triggered, `false` otherwise 1543 * 1544 * Since: 0.9.7 1545 **/ 1546 hb_bool_t 1547 hb_ot_layout_lookup_would_substitute (hb_face_t *face, 1548 unsigned int lookup_index, 1549 const hb_codepoint_t *glyphs, 1550 unsigned int glyphs_length, 1551 hb_bool_t zero_context) 1552 { 1553 auto &gsub = face->table.GSUB; 1554 if (unlikely (lookup_index >= gsub->lookup_count)) return false; 1555 OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context); 1556 1557 const OT::SubstLookup& l = gsub->table->get_lookup (lookup_index); 1558 auto *accel = gsub->get_accel (lookup_index); 1559 return accel && l.would_apply (&c, accel); 1560 } 1561 1562 1563 /** 1564 * hb_ot_layout_substitute_start: 1565 * @font: #hb_font_t to use 1566 * @buffer: #hb_buffer_t buffer to work upon 1567 * 1568 * Called before substitution lookups are performed, to ensure that glyph 1569 * class and other properties are set on the glyphs in the buffer. 1570 * 1571 **/ 1572 void 1573 hb_ot_layout_substitute_start (hb_font_t *font, 1574 hb_buffer_t *buffer) 1575 { 1576 _hb_ot_layout_set_glyph_props (font, buffer); 1577 } 1578 1579 /** 1580 * hb_ot_layout_lookup_substitute_closure: 1581 * @face: #hb_face_t to work upon 1582 * @lookup_index: index of the feature lookup to query 1583 * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookup 1584 * 1585 * Compute the transitive closure of glyphs needed for a 1586 * specified lookup. 1587 * 1588 * Since: 0.9.7 1589 **/ 1590 void 1591 hb_ot_layout_lookup_substitute_closure (hb_face_t *face, 1592 unsigned int lookup_index, 1593 hb_set_t *glyphs /* OUT */) 1594 { 1595 hb_map_t done_lookups_glyph_count; 1596 hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> done_lookups_glyph_set; 1597 OT::hb_closure_context_t c (face, glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set); 1598 1599 const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index); 1600 1601 l.closure (&c, lookup_index); 1602 } 1603 1604 /** 1605 * hb_ot_layout_lookups_substitute_closure: 1606 * @face: #hb_face_t to work upon 1607 * @lookups: The set of lookups to query 1608 * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookups 1609 * 1610 * Compute the transitive closure of glyphs needed for all of the 1611 * provided lookups. 1612 * 1613 * Since: 1.8.1 1614 **/ 1615 void 1616 hb_ot_layout_lookups_substitute_closure (hb_face_t *face, 1617 const hb_set_t *lookups, 1618 hb_set_t *glyphs /* OUT */) 1619 { 1620 hb_map_t done_lookups_glyph_count; 1621 hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> done_lookups_glyph_set; 1622 OT::hb_closure_context_t c (face, glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set); 1623 const GSUB& gsub = *face->table.GSUB->table; 1624 1625 unsigned int iteration_count = 0; 1626 unsigned int glyphs_length; 1627 do 1628 { 1629 c.reset_lookup_visit_count (); 1630 glyphs_length = glyphs->get_population (); 1631 if (lookups) 1632 { 1633 for (auto lookup_index : *lookups) 1634 gsub.get_lookup (lookup_index).closure (&c, lookup_index); 1635 } 1636 else 1637 { 1638 for (unsigned int i = 0; i < gsub.get_lookup_count (); i++) 1639 gsub.get_lookup (i).closure (&c, i); 1640 } 1641 } while (iteration_count++ <= HB_CLOSURE_MAX_STAGES && 1642 glyphs_length != glyphs->get_population ()); 1643 } 1644 1645 /* 1646 * GPOS 1647 */ 1648 1649 1650 /** 1651 * hb_ot_layout_has_positioning: 1652 * @face: #hb_face_t to work upon 1653 * 1654 * Tests whether the specified face includes any GPOS positioning. 1655 * 1656 * Return value: `true` if the face has GPOS data, `false` otherwise 1657 * 1658 **/ 1659 hb_bool_t 1660 hb_ot_layout_has_positioning (hb_face_t *face) 1661 { 1662 return face->table.GPOS->table->has_data (); 1663 } 1664 1665 /** 1666 * hb_ot_layout_position_start: 1667 * @font: #hb_font_t to use 1668 * @buffer: #hb_buffer_t buffer to work upon 1669 * 1670 * Called before positioning lookups are performed, to ensure that glyph 1671 * attachment types and glyph-attachment chains are set for the glyphs in the buffer. 1672 * 1673 **/ 1674 void 1675 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) 1676 { 1677 GPOS::position_start (font, buffer); 1678 } 1679 1680 1681 /** 1682 * hb_ot_layout_position_finish_advances: 1683 * @font: #hb_font_t to use 1684 * @buffer: #hb_buffer_t buffer to work upon 1685 * 1686 * Called after positioning lookups are performed, to finish glyph advances. 1687 * 1688 **/ 1689 void 1690 hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer) 1691 { 1692 GPOS::position_finish_advances (font, buffer); 1693 } 1694 1695 /** 1696 * hb_ot_layout_position_finish_offsets: 1697 * @font: #hb_font_t to use 1698 * @buffer: #hb_buffer_t buffer to work upon 1699 * 1700 * Called after positioning lookups are performed, to finish glyph offsets. 1701 * 1702 **/ 1703 void 1704 hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) 1705 { 1706 GPOS::position_finish_offsets (font, buffer); 1707 } 1708 1709 1710 #ifndef HB_NO_LAYOUT_FEATURE_PARAMS 1711 /** 1712 * hb_ot_layout_get_size_params: 1713 * @face: #hb_face_t to work upon 1714 * @design_size: (out): The design size of the face 1715 * @subfamily_id: (out): The identifier of the face within the font subfamily 1716 * @subfamily_name_id: (out): The ‘name’ table name ID of the face within the font subfamily 1717 * @range_start: (out): The minimum size of the recommended size range for the face 1718 * @range_end: (out): The maximum size of the recommended size range for the face 1719 * 1720 * Fetches optical-size feature data (i.e., the `size` feature from GPOS). Note that 1721 * the subfamily_id and the subfamily name string (accessible via the subfamily_name_id) 1722 * as used here are defined as pertaining only to fonts within a font family that differ 1723 * specifically in their respective size ranges; other ways to differentiate fonts within 1724 * a subfamily are not covered by the `size` feature. 1725 * 1726 * For more information on this distinction, see the [`size` feature documentation]( 1727 * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-size). 1728 * 1729 * Return value: `true` if data found, `false` otherwise 1730 * 1731 * Since: 0.9.10 1732 **/ 1733 hb_bool_t 1734 hb_ot_layout_get_size_params (hb_face_t *face, 1735 unsigned int *design_size, /* OUT. May be NULL */ 1736 unsigned int *subfamily_id, /* OUT. May be NULL */ 1737 hb_ot_name_id_t *subfamily_name_id, /* OUT. May be NULL */ 1738 unsigned int *range_start, /* OUT. May be NULL */ 1739 unsigned int *range_end /* OUT. May be NULL */) 1740 { 1741 const GPOS &gpos = *face->table.GPOS->table; 1742 const hb_tag_t tag = HB_TAG ('s','i','z','e'); 1743 1744 unsigned int num_features = gpos.get_feature_count (); 1745 for (unsigned int i = 0; i < num_features; i++) 1746 { 1747 if (tag == gpos.get_feature_tag (i)) 1748 { 1749 const OT::Feature &f = gpos.get_feature (i); 1750 const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag); 1751 1752 if (params.designSize) 1753 { 1754 if (design_size) *design_size = params.designSize; 1755 if (subfamily_id) *subfamily_id = params.subfamilyID; 1756 if (subfamily_name_id) *subfamily_name_id = params.subfamilyNameID; 1757 if (range_start) *range_start = params.rangeStart; 1758 if (range_end) *range_end = params.rangeEnd; 1759 1760 return true; 1761 } 1762 } 1763 } 1764 1765 if (design_size) *design_size = 0; 1766 if (subfamily_id) *subfamily_id = 0; 1767 if (subfamily_name_id) *subfamily_name_id = HB_OT_NAME_ID_INVALID; 1768 if (range_start) *range_start = 0; 1769 if (range_end) *range_end = 0; 1770 1771 return false; 1772 } 1773 1774 1775 /** 1776 * hb_ot_layout_feature_get_name_ids: 1777 * @face: #hb_face_t to work upon 1778 * @table_tag: table tag to query, "GSUB" or "GPOS". 1779 * @feature_index: index of feature to query. 1780 * @label_id: (out) (nullable): The ‘name’ table name ID that specifies a string 1781 * for a user-interface label for this feature. 1782 * @tooltip_id: (out) (nullable): The ‘name’ table name ID that specifies a string 1783 * that an application can use for tooltip text for this 1784 * feature. 1785 * @sample_id: (out) (nullable): The ‘name’ table name ID that specifies sample text 1786 * that illustrates the effect of this feature. 1787 * @num_named_parameters: (out) (nullable): Number of named parameters. 1788 * @first_param_id: (out) (nullable): The first ‘name’ table name ID used to specify 1789 * strings for user-interface labels for the feature 1790 * parameters. (Must be zero if numParameters is zero.) 1791 * 1792 * Fetches name indices from feature parameters for "Stylistic Set" ('ssXX') or 1793 * "Character Variant" ('cvXX') features. 1794 * 1795 * Return value: `true` if data found, `false` otherwise 1796 * 1797 * Since: 2.0.0 1798 **/ 1799 hb_bool_t 1800 hb_ot_layout_feature_get_name_ids (hb_face_t *face, 1801 hb_tag_t table_tag, 1802 unsigned int feature_index, 1803 hb_ot_name_id_t *label_id, /* OUT. May be NULL */ 1804 hb_ot_name_id_t *tooltip_id, /* OUT. May be NULL */ 1805 hb_ot_name_id_t *sample_id, /* OUT. May be NULL */ 1806 unsigned int *num_named_parameters, /* OUT. May be NULL */ 1807 hb_ot_name_id_t *first_param_id /* OUT. May be NULL */) 1808 { 1809 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1810 1811 hb_tag_t feature_tag = g.get_feature_tag (feature_index); 1812 const OT::Feature &f = g.get_feature (feature_index); 1813 1814 const OT::FeatureParams &feature_params = f.get_feature_params (); 1815 if (&feature_params != &Null (OT::FeatureParams)) 1816 { 1817 const OT::FeatureParamsStylisticSet& ss_params = 1818 feature_params.get_stylistic_set_params (feature_tag); 1819 if (&ss_params != &Null (OT::FeatureParamsStylisticSet)) /* ssXX */ 1820 { 1821 if (label_id) *label_id = ss_params.uiNameID; 1822 // ssXX features don't have the rest 1823 if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID; 1824 if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID; 1825 if (num_named_parameters) *num_named_parameters = 0; 1826 if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID; 1827 return true; 1828 } 1829 const OT::FeatureParamsCharacterVariants& cv_params = 1830 feature_params.get_character_variants_params (feature_tag); 1831 if (&cv_params != &Null (OT::FeatureParamsCharacterVariants)) /* cvXX */ 1832 { 1833 if (label_id) *label_id = cv_params.featUILableNameID; 1834 if (tooltip_id) *tooltip_id = cv_params.featUITooltipTextNameID; 1835 if (sample_id) *sample_id = cv_params.sampleTextNameID; 1836 if (num_named_parameters) *num_named_parameters = cv_params.numNamedParameters; 1837 if (first_param_id) *first_param_id = cv_params.firstParamUILabelNameID; 1838 return true; 1839 } 1840 } 1841 1842 if (label_id) *label_id = HB_OT_NAME_ID_INVALID; 1843 if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID; 1844 if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID; 1845 if (num_named_parameters) *num_named_parameters = 0; 1846 if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID; 1847 return false; 1848 } 1849 /** 1850 * hb_ot_layout_feature_get_characters: 1851 * @face: #hb_face_t to work upon 1852 * @table_tag: table tag to query, "GSUB" or "GPOS". 1853 * @feature_index: index of feature to query. 1854 * @start_offset: offset of the first character to retrieve 1855 * @char_count: (inout) (nullable): Input = the maximum number of characters to return; 1856 * Output = the actual number of characters returned (may be zero) 1857 * @characters: (out caller-allocates) (array length=char_count): A buffer pointer. 1858 * The Unicode codepoints of the characters for which this feature provides 1859 * glyph variants. 1860 * 1861 * Fetches a list of the characters defined as having a variant under the specified 1862 * "Character Variant" ("cvXX") feature tag. 1863 * 1864 * Return value: Number of total sample characters in the cvXX feature. 1865 * 1866 * Since: 2.0.0 1867 **/ 1868 unsigned int 1869 hb_ot_layout_feature_get_characters (hb_face_t *face, 1870 hb_tag_t table_tag, 1871 unsigned int feature_index, 1872 unsigned int start_offset, 1873 unsigned int *char_count, /* IN/OUT. May be NULL */ 1874 hb_codepoint_t *characters /* OUT. May be NULL */) 1875 { 1876 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1877 return g.get_feature (feature_index) 1878 .get_feature_params () 1879 .get_character_variants_params(g.get_feature_tag (feature_index)) 1880 .get_characters (start_offset, char_count, characters); 1881 } 1882 #endif 1883 1884 1885 /* 1886 * Parts of different types are implemented here such that they have direct 1887 * access to GSUB/GPOS lookups. 1888 */ 1889 1890 1891 struct GSUBProxy 1892 { 1893 static constexpr unsigned table_index = 0u; 1894 static constexpr bool always_inplace = false; 1895 typedef OT::SubstLookup Lookup; 1896 1897 GSUBProxy (hb_face_t *face) : 1898 accel (*face->table.GSUB) {} 1899 1900 const GSUB::accelerator_t &accel; 1901 }; 1902 1903 struct GPOSProxy 1904 { 1905 static constexpr unsigned table_index = 1u; 1906 static constexpr bool always_inplace = true; 1907 typedef OT::PosLookup Lookup; 1908 1909 GPOSProxy (hb_face_t *face) : 1910 accel (*face->table.GPOS) {} 1911 1912 const GPOS::accelerator_t &accel; 1913 }; 1914 1915 1916 static inline bool 1917 apply_forward (OT::hb_ot_apply_context_t *c, 1918 const OT::hb_ot_layout_lookup_accelerator_t &accel) 1919 { 1920 bool use_hot_subtable_cache = accel.cache_enter (c); 1921 1922 bool ret = false; 1923 hb_buffer_t *buffer = c->buffer; 1924 while (buffer->successful) 1925 { 1926 hb_glyph_info_t *info = buffer->info; 1927 unsigned j = buffer->idx; 1928 while (j < buffer->len && 1929 !(accel.digest.may_have (info[j].codepoint) && 1930 (info[j].mask & c->lookup_mask) && 1931 c->check_glyph_property (&info[j], c->lookup_props))) 1932 j++; 1933 if (unlikely (j > buffer->idx && !buffer->next_glyphs (j - buffer->idx))) 1934 break; 1935 if (buffer->idx >= buffer->len) 1936 break; 1937 1938 if (accel.apply (c, use_hot_subtable_cache)) 1939 ret = true; 1940 else 1941 (void) buffer->next_glyph (); 1942 } 1943 1944 if (use_hot_subtable_cache) 1945 accel.cache_leave (c); 1946 1947 return ret; 1948 } 1949 1950 static inline bool 1951 apply_backward (OT::hb_ot_apply_context_t *c, 1952 const OT::hb_ot_layout_lookup_accelerator_t &accel) 1953 { 1954 bool ret = false; 1955 hb_buffer_t *buffer = c->buffer; 1956 do 1957 { 1958 auto &cur = buffer->cur(); 1959 if (accel.digest.may_have (cur.codepoint) && 1960 (cur.mask & c->lookup_mask) && 1961 c->check_glyph_property (&cur, c->lookup_props)) 1962 ret |= accel.apply (c, false); 1963 1964 /* The reverse lookup doesn't "advance" cursor (for good reason). */ 1965 buffer->idx--; 1966 } 1967 while ((int) buffer->idx >= 0); 1968 return ret; 1969 } 1970 1971 template <typename Proxy> 1972 static inline bool 1973 apply_string (OT::hb_ot_apply_context_t *c, 1974 const typename Proxy::Lookup &lookup, 1975 const OT::hb_ot_layout_lookup_accelerator_t &accel) 1976 { 1977 hb_buffer_t *buffer = c->buffer; 1978 1979 if (unlikely (!buffer->len || !c->lookup_mask)) 1980 return false; 1981 1982 bool ret = false; 1983 1984 c->set_lookup_props (lookup.get_props ()); 1985 1986 if (likely (!lookup.is_reverse ())) 1987 { 1988 /* in/out forward substitution/positioning */ 1989 if (!Proxy::always_inplace) 1990 buffer->clear_output (); 1991 1992 buffer->idx = 0; 1993 ret = apply_forward (c, accel); 1994 1995 if (!Proxy::always_inplace) 1996 buffer->sync (); 1997 } 1998 else 1999 { 2000 /* in-place backward substitution/positioning */ 2001 assert (!buffer->have_output); 2002 buffer->idx = buffer->len - 1; 2003 ret = apply_backward (c, accel); 2004 } 2005 2006 return ret; 2007 } 2008 2009 template <typename Proxy> 2010 inline void hb_ot_map_t::apply (const Proxy &proxy, 2011 const hb_ot_shape_plan_t *plan, 2012 hb_font_t *font, 2013 hb_buffer_t *buffer) const 2014 { 2015 const unsigned int table_index = proxy.table_index; 2016 unsigned int i = 0; 2017 2018 auto *font_data = font->data.ot.get (); 2019 auto *var_store_cache = (OT::hb_scalar_cache_t *) font_data; 2020 2021 OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob (), var_store_cache); 2022 c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func<OT::hb_ot_apply_context_t>); 2023 2024 for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++) 2025 { 2026 const stage_map_t *stage = &stages[table_index][stage_index]; 2027 for (; i < stage->last_lookup; i++) 2028 { 2029 auto &lookup = lookups[table_index][i]; 2030 2031 unsigned int lookup_index = lookup.index; 2032 2033 auto *accel = proxy.accel.get_accel (lookup_index); 2034 if (unlikely (!accel)) continue; 2035 2036 if (buffer->messaging () && 2037 !buffer->message (font, "start lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag))) continue; 2038 2039 /* Only try applying the lookup if there is any overlap. */ 2040 if (accel->digest.may_intersect (buffer->digest)) 2041 { 2042 c.set_lookup_index (lookup_index); 2043 c.set_lookup_mask (lookup.mask, false); 2044 c.set_auto_zwj (lookup.auto_zwj, false); 2045 c.set_auto_zwnj (lookup.auto_zwnj, false); 2046 c.set_random (lookup.random); 2047 c.set_per_syllable (lookup.per_syllable, false); 2048 /* apply_string's set_lookup_props initializes the iterators. */ 2049 2050 apply_string<Proxy> (&c, 2051 proxy.accel.table->get_lookup (lookup_index), 2052 *accel); 2053 } 2054 else if (buffer->messaging ()) 2055 (void) buffer->message (font, "skipped lookup %u feature '%c%c%c%c' because no glyph matches", lookup_index, HB_UNTAG (lookup.feature_tag)); 2056 2057 if (buffer->messaging ()) 2058 (void) buffer->message (font, "end lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag)); 2059 } 2060 2061 if (stage->pause_func) 2062 { 2063 if (stage->pause_func (plan, font, buffer)) 2064 { 2065 /* Refresh working buffer digest since buffer changed. */ 2066 buffer->update_digest (); 2067 } 2068 } 2069 } 2070 } 2071 2072 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const 2073 { 2074 GSUBProxy proxy (font->face); 2075 if (buffer->messaging () && 2076 !buffer->message (font, "start table GSUB script tag '%c%c%c%c'", HB_UNTAG (chosen_script[0]))) return; 2077 apply (proxy, plan, font, buffer); 2078 if (buffer->messaging ()) 2079 (void) buffer->message (font, "end table GSUB script tag '%c%c%c%c'", HB_UNTAG (chosen_script[0])); 2080 } 2081 2082 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const 2083 { 2084 GPOSProxy proxy (font->face); 2085 if (buffer->messaging () && 2086 !buffer->message (font, "start table GPOS script tag '%c%c%c%c'", HB_UNTAG (chosen_script[1]))) return; 2087 apply (proxy, plan, font, buffer); 2088 if (buffer->messaging ()) 2089 (void) buffer->message (font, "end table GPOS script tag '%c%c%c%c'", HB_UNTAG (chosen_script[1])); 2090 } 2091 2092 void 2093 hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c, 2094 const OT::SubstLookup &lookup, 2095 const OT::hb_ot_layout_lookup_accelerator_t &accel) 2096 { 2097 apply_string<GSUBProxy> (c, lookup, accel); 2098 } 2099 2100 #ifndef HB_NO_BASE 2101 2102 static void 2103 choose_base_tags (hb_script_t script, 2104 hb_language_t language, 2105 hb_tag_t *script_tag, 2106 hb_tag_t *language_tag) 2107 { 2108 hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT]; 2109 unsigned script_count = ARRAY_LENGTH (script_tags); 2110 2111 hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE]; 2112 unsigned language_count = ARRAY_LENGTH (language_tags); 2113 2114 hb_ot_tags_from_script_and_language (script, language, 2115 &script_count, script_tags, 2116 &language_count, language_tags); 2117 2118 *script_tag = script_count ? script_tags[script_count - 1] : HB_OT_TAG_DEFAULT_SCRIPT; 2119 *language_tag = language_count ? language_tags[language_count - 1] : HB_OT_TAG_DEFAULT_LANGUAGE; 2120 } 2121 2122 /** 2123 * hb_ot_layout_get_font_extents: 2124 * @font: a font 2125 * @direction: text direction. 2126 * @script_tag: script tag. 2127 * @language_tag: language tag. 2128 * @extents: (out) (nullable): font extents if found. 2129 * 2130 * Fetches script/language-specific font extents. These values are 2131 * looked up in the `BASE` table's `MinMax` records. 2132 * 2133 * If no such extents are found, the default extents for the font are 2134 * fetched. As such, the return value of this function can for the 2135 * most part be ignored. Note that the per-script/language extents 2136 * do not have a line-gap value, and the line-gap is set to zero in 2137 * that case. 2138 * 2139 * Return value: `true` if found script/language-specific font extents. 2140 * 2141 * Since: 8.0.0 2142 **/ 2143 hb_bool_t 2144 hb_ot_layout_get_font_extents (hb_font_t *font, 2145 hb_direction_t direction, 2146 hb_tag_t script_tag, 2147 hb_tag_t language_tag, 2148 hb_font_extents_t *extents) 2149 { 2150 hb_position_t min = 0, max = 0; 2151 if (font->face->table.BASE->get_min_max (font, direction, script_tag, language_tag, HB_TAG_NONE, 2152 &min, &max)) 2153 { 2154 if (extents) 2155 { 2156 extents->ascender = max; 2157 extents->descender = min; 2158 extents->line_gap = 0; 2159 } 2160 return true; 2161 } 2162 2163 hb_font_get_extents_for_direction (font, direction, extents); 2164 return false; 2165 } 2166 2167 /** 2168 * hb_ot_layout_get_font_extents2: 2169 * @font: a font 2170 * @direction: text direction. 2171 * @script: script. 2172 * @language: (nullable): language. 2173 * @extents: (out) (nullable): font extents if found. 2174 * 2175 * Fetches script/language-specific font extents. These values are 2176 * looked up in the `BASE` table's `MinMax` records. 2177 * 2178 * If no such extents are found, the default extents for the font are 2179 * fetched. As such, the return value of this function can for the 2180 * most part be ignored. Note that the per-script/language extents 2181 * do not have a line-gap value, and the line-gap is set to zero in 2182 * that case. 2183 * 2184 * This function is like hb_ot_layout_get_font_extents() but takes 2185 * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t. 2186 * 2187 * Return value: `true` if found script/language-specific font extents. 2188 * 2189 * Since: 8.0.0 2190 **/ 2191 hb_bool_t 2192 hb_ot_layout_get_font_extents2 (hb_font_t *font, 2193 hb_direction_t direction, 2194 hb_script_t script, 2195 hb_language_t language, 2196 hb_font_extents_t *extents) 2197 { 2198 hb_tag_t script_tag, language_tag; 2199 choose_base_tags (script, language, &script_tag, &language_tag); 2200 return hb_ot_layout_get_font_extents (font, 2201 direction, 2202 script_tag, 2203 language_tag, 2204 extents); 2205 } 2206 2207 /** 2208 * hb_ot_layout_get_horizontal_baseline_tag_for_script: 2209 * @script: a script tag. 2210 * 2211 * Fetches the dominant horizontal baseline tag used by @script. 2212 * 2213 * Return value: dominant baseline tag for the @script. 2214 * 2215 * Since: 4.0.0 2216 **/ 2217 hb_ot_layout_baseline_tag_t 2218 hb_ot_layout_get_horizontal_baseline_tag_for_script (hb_script_t script) 2219 { 2220 /* Keep in sync with hb_ot_layout_get_baseline_with_fallback */ 2221 switch ((int) script) 2222 { 2223 /* Unicode-1.1 additions */ 2224 case HB_SCRIPT_BENGALI: 2225 case HB_SCRIPT_DEVANAGARI: 2226 case HB_SCRIPT_GUJARATI: 2227 case HB_SCRIPT_GURMUKHI: 2228 /* Unicode-2.0 additions */ 2229 case HB_SCRIPT_TIBETAN: 2230 /* Unicode-4.0 additions */ 2231 case HB_SCRIPT_LIMBU: 2232 /* Unicode-4.1 additions */ 2233 case HB_SCRIPT_SYLOTI_NAGRI: 2234 /* Unicode-5.0 additions */ 2235 case HB_SCRIPT_PHAGS_PA: 2236 /* Unicode-5.2 additions */ 2237 case HB_SCRIPT_MEETEI_MAYEK: 2238 /* Unicode-6.1 additions */ 2239 case HB_SCRIPT_SHARADA: 2240 case HB_SCRIPT_TAKRI: 2241 /* Unicode-7.0 additions */ 2242 case HB_SCRIPT_MODI: 2243 case HB_SCRIPT_SIDDHAM: 2244 case HB_SCRIPT_TIRHUTA: 2245 /* Unicode-9.0 additions */ 2246 case HB_SCRIPT_MARCHEN: 2247 case HB_SCRIPT_NEWA: 2248 /* Unicode-10.0 additions */ 2249 case HB_SCRIPT_SOYOMBO: 2250 case HB_SCRIPT_ZANABAZAR_SQUARE: 2251 /* Unicode-11.0 additions */ 2252 case HB_SCRIPT_DOGRA: 2253 case HB_SCRIPT_GUNJALA_GONDI: 2254 /* Unicode-12.0 additions */ 2255 case HB_SCRIPT_NANDINAGARI: 2256 return HB_OT_LAYOUT_BASELINE_TAG_HANGING; 2257 2258 /* Unicode-1.1 additions */ 2259 case HB_SCRIPT_HANGUL: 2260 case HB_SCRIPT_HAN: 2261 case HB_SCRIPT_HIRAGANA: 2262 case HB_SCRIPT_KATAKANA: 2263 /* Unicode-3.0 additions */ 2264 case HB_SCRIPT_BOPOMOFO: 2265 /* Unicode-9.0 additions */ 2266 case HB_SCRIPT_TANGUT: 2267 /* Unicode-10.0 additions */ 2268 case HB_SCRIPT_NUSHU: 2269 /* Unicode-13.0 additions */ 2270 case HB_SCRIPT_KHITAN_SMALL_SCRIPT: 2271 return HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT; 2272 2273 default: 2274 return HB_OT_LAYOUT_BASELINE_TAG_ROMAN; 2275 } 2276 } 2277 2278 /** 2279 * hb_ot_layout_get_baseline: 2280 * @font: a font 2281 * @baseline_tag: a baseline tag 2282 * @direction: text direction. 2283 * @script_tag: script tag. 2284 * @language_tag: language tag, currently unused. 2285 * @coord: (out) (nullable): baseline value if found. 2286 * 2287 * Fetches a baseline value from the face. 2288 * 2289 * Return value: `true` if found baseline value in the font. 2290 * 2291 * Since: 2.6.0 2292 **/ 2293 hb_bool_t 2294 hb_ot_layout_get_baseline (hb_font_t *font, 2295 hb_ot_layout_baseline_tag_t baseline_tag, 2296 hb_direction_t direction, 2297 hb_tag_t script_tag, 2298 hb_tag_t language_tag, 2299 hb_position_t *coord /* OUT. May be NULL. */) 2300 { 2301 return font->face->table.BASE->get_baseline (font, baseline_tag, direction, script_tag, language_tag, coord); 2302 } 2303 2304 /** 2305 * hb_ot_layout_get_baseline2: 2306 * @font: a font 2307 * @baseline_tag: a baseline tag 2308 * @direction: text direction. 2309 * @script: script. 2310 * @language: (nullable): language, currently unused. 2311 * @coord: (out) (nullable): baseline value if found. 2312 * 2313 * Fetches a baseline value from the face. 2314 * 2315 * This function is like hb_ot_layout_get_baseline() but takes 2316 * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t. 2317 * 2318 * Return value: `true` if found baseline value in the font. 2319 * 2320 * Since: 8.0.0 2321 **/ 2322 hb_bool_t 2323 hb_ot_layout_get_baseline2 (hb_font_t *font, 2324 hb_ot_layout_baseline_tag_t baseline_tag, 2325 hb_direction_t direction, 2326 hb_script_t script, 2327 hb_language_t language, 2328 hb_position_t *coord /* OUT. May be NULL. */) 2329 { 2330 hb_tag_t script_tag, language_tag; 2331 choose_base_tags (script, language, &script_tag, &language_tag); 2332 return hb_ot_layout_get_baseline (font, 2333 baseline_tag, 2334 direction, 2335 script_tag, 2336 language_tag, 2337 coord); 2338 } 2339 2340 /** 2341 * hb_ot_layout_get_baseline_with_fallback: 2342 * @font: a font 2343 * @baseline_tag: a baseline tag 2344 * @direction: text direction. 2345 * @script_tag: script tag. 2346 * @language_tag: language tag, currently unused. 2347 * @coord: (out): baseline value if found. 2348 * 2349 * Fetches a baseline value from the face, and synthesizes 2350 * it if the font does not have it. 2351 * 2352 * Since: 4.0.0 2353 **/ 2354 void 2355 hb_ot_layout_get_baseline_with_fallback (hb_font_t *font, 2356 hb_ot_layout_baseline_tag_t baseline_tag, 2357 hb_direction_t direction, 2358 hb_tag_t script_tag, 2359 hb_tag_t language_tag, 2360 hb_position_t *coord /* OUT */) 2361 { 2362 if (hb_ot_layout_get_baseline (font, 2363 baseline_tag, 2364 direction, 2365 script_tag, 2366 language_tag, 2367 coord)) 2368 return; 2369 2370 /* Synthesize missing baselines. 2371 * See https://www.w3.org/TR/css-inline-3/#baseline-synthesis-fonts 2372 */ 2373 switch (baseline_tag) 2374 { 2375 case HB_OT_LAYOUT_BASELINE_TAG_ROMAN: 2376 *coord = 0; // FIXME origin ? 2377 break; 2378 2379 case HB_OT_LAYOUT_BASELINE_TAG_MATH: 2380 { 2381 hb_codepoint_t glyph; 2382 hb_glyph_extents_t extents; 2383 if (HB_DIRECTION_IS_HORIZONTAL (direction) && 2384 (hb_font_get_nominal_glyph (font, 0x2212u, &glyph) || 2385 hb_font_get_nominal_glyph (font, '-', &glyph)) && 2386 hb_font_get_glyph_extents (font, glyph, &extents)) 2387 { 2388 *coord = extents.y_bearing + extents.height / 2; 2389 } 2390 else 2391 { 2392 hb_position_t x_height = font->y_scale / 2; 2393 #ifndef HB_NO_METRICS 2394 hb_ot_metrics_get_position_with_fallback (font, HB_OT_METRICS_TAG_X_HEIGHT, &x_height); 2395 #endif 2396 *coord = x_height / 2; 2397 } 2398 } 2399 break; 2400 2401 case HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT: 2402 case HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT: 2403 { 2404 hb_position_t embox_top, embox_bottom; 2405 2406 hb_ot_layout_get_baseline_with_fallback (font, 2407 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, 2408 direction, 2409 script_tag, 2410 language_tag, 2411 &embox_top); 2412 hb_ot_layout_get_baseline_with_fallback (font, 2413 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT, 2414 direction, 2415 script_tag, 2416 language_tag, 2417 &embox_bottom); 2418 2419 if (baseline_tag == HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT) 2420 *coord = embox_top + (embox_bottom - embox_top) / 10; 2421 else 2422 *coord = embox_bottom + (embox_top - embox_bottom) / 10; 2423 } 2424 break; 2425 2426 case HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT: 2427 if (hb_ot_layout_get_baseline (font, 2428 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT, 2429 direction, 2430 script_tag, 2431 language_tag, 2432 coord)) 2433 *coord += HB_DIRECTION_IS_HORIZONTAL (direction) ? font->y_scale : font->x_scale; 2434 else 2435 { 2436 hb_font_extents_t font_extents; 2437 hb_font_get_extents_for_direction (font, direction, &font_extents); 2438 *coord = font_extents.ascender; 2439 } 2440 break; 2441 2442 case HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT: 2443 if (hb_ot_layout_get_baseline (font, 2444 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, 2445 direction, 2446 script_tag, 2447 language_tag, 2448 coord)) 2449 *coord -= HB_DIRECTION_IS_HORIZONTAL (direction) ? font->y_scale : font->x_scale; 2450 else 2451 { 2452 hb_font_extents_t font_extents; 2453 hb_font_get_extents_for_direction (font, direction, &font_extents); 2454 *coord = font_extents.descender; 2455 } 2456 break; 2457 2458 case HB_OT_LAYOUT_BASELINE_TAG_HANGING: 2459 if (HB_DIRECTION_IS_HORIZONTAL (direction)) 2460 { 2461 hb_codepoint_t ch; 2462 hb_codepoint_t glyph; 2463 hb_glyph_extents_t extents; 2464 2465 /* Keep in sync with hb_ot_layout_get_horizontal_baseline_for_script */ 2466 switch ((int) script_tag) 2467 { 2468 /* Unicode-1.1 additions */ 2469 case HB_SCRIPT_BENGALI: ch = 0x0995u; break; 2470 case HB_SCRIPT_DEVANAGARI: ch = 0x0915u; break; 2471 case HB_SCRIPT_GUJARATI: ch = 0x0a95u; break; 2472 case HB_SCRIPT_GURMUKHI: ch = 0x0a15u; break; 2473 /* Unicode-2.0 additions */ 2474 case HB_SCRIPT_TIBETAN: ch = 0x0f40u; break; 2475 /* Unicode-4.0 additions */ 2476 case HB_SCRIPT_LIMBU: ch = 0x1901u; break; 2477 /* Unicode-4.1 additions */ 2478 case HB_SCRIPT_SYLOTI_NAGRI: ch = 0xa807u; break; 2479 /* Unicode-5.0 additions */ 2480 case HB_SCRIPT_PHAGS_PA: ch = 0xa840u; break; 2481 /* Unicode-5.2 additions */ 2482 case HB_SCRIPT_MEETEI_MAYEK: ch = 0xabc0u; break; 2483 /* Unicode-6.1 additions */ 2484 case HB_SCRIPT_SHARADA: ch = 0x11191u; break; 2485 case HB_SCRIPT_TAKRI: ch = 0x1168cu; break; 2486 /* Unicode-7.0 additions */ 2487 case HB_SCRIPT_MODI: ch = 0x1160eu;break; 2488 case HB_SCRIPT_SIDDHAM: ch = 0x11590u; break; 2489 case HB_SCRIPT_TIRHUTA: ch = 0x1148fu; break; 2490 /* Unicode-9.0 additions */ 2491 case HB_SCRIPT_MARCHEN: ch = 0x11c72u; break; 2492 case HB_SCRIPT_NEWA: ch = 0x1140eu; break; 2493 /* Unicode-10.0 additions */ 2494 case HB_SCRIPT_SOYOMBO: ch = 0x11a5cu; break; 2495 case HB_SCRIPT_ZANABAZAR_SQUARE: ch = 0x11a0bu; break; 2496 /* Unicode-11.0 additions */ 2497 case HB_SCRIPT_DOGRA: ch = 0x1180au; break; 2498 case HB_SCRIPT_GUNJALA_GONDI: ch = 0x11d6cu; break; 2499 /* Unicode-12.0 additions */ 2500 case HB_SCRIPT_NANDINAGARI: ch = 0x119b0u; break; 2501 default: ch = 0; break; 2502 } 2503 2504 if (ch && 2505 hb_font_get_nominal_glyph (font, ch, &glyph) && 2506 hb_font_get_glyph_extents (font, glyph, &extents)) 2507 *coord = extents.y_bearing; 2508 else 2509 *coord = font->y_scale * 6 / 10; // FIXME makes assumptions about origin 2510 } 2511 else 2512 *coord = font->x_scale * 6 / 10; // FIXME makes assumptions about origin 2513 break; 2514 2515 case HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_CENTRAL: 2516 { 2517 hb_position_t top, bottom; 2518 hb_ot_layout_get_baseline_with_fallback (font, 2519 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, 2520 direction, 2521 script_tag, 2522 language_tag, 2523 &top); 2524 hb_ot_layout_get_baseline_with_fallback (font, 2525 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT, 2526 direction, 2527 script_tag, 2528 language_tag, 2529 &bottom); 2530 *coord = (top + bottom) / 2; 2531 2532 } 2533 break; 2534 2535 case HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_CENTRAL: 2536 { 2537 hb_position_t top, bottom; 2538 hb_ot_layout_get_baseline_with_fallback (font, 2539 HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT, 2540 direction, 2541 script_tag, 2542 language_tag, 2543 &top); 2544 hb_ot_layout_get_baseline_with_fallback (font, 2545 HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT, 2546 direction, 2547 script_tag, 2548 language_tag, 2549 &bottom); 2550 *coord = (top + bottom) / 2; 2551 2552 } 2553 break; 2554 2555 case _HB_OT_LAYOUT_BASELINE_TAG_MAX_VALUE: 2556 default: 2557 *coord = 0; 2558 break; 2559 } 2560 } 2561 2562 /** 2563 * hb_ot_layout_get_baseline_with_fallback2: 2564 * @font: a font 2565 * @baseline_tag: a baseline tag 2566 * @direction: text direction. 2567 * @script: script. 2568 * @language: (nullable): language, currently unused. 2569 * @coord: (out): baseline value if found. 2570 * 2571 * Fetches a baseline value from the face, and synthesizes 2572 * it if the font does not have it. 2573 * 2574 * This function is like hb_ot_layout_get_baseline_with_fallback() but takes 2575 * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t. 2576 * 2577 * Since: 8.0.0 2578 **/ 2579 void 2580 hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font, 2581 hb_ot_layout_baseline_tag_t baseline_tag, 2582 hb_direction_t direction, 2583 hb_script_t script, 2584 hb_language_t language, 2585 hb_position_t *coord /* OUT */) 2586 { 2587 hb_tag_t script_tag, language_tag; 2588 choose_base_tags (script, language, &script_tag, &language_tag); 2589 hb_ot_layout_get_baseline_with_fallback (font, 2590 baseline_tag, 2591 direction, 2592 script_tag, 2593 language_tag, 2594 coord); 2595 } 2596 2597 #endif 2598 2599 2600 #ifndef HB_NO_LAYOUT_RARELY_USED 2601 struct hb_get_glyph_alternates_dispatch_t : 2602 hb_dispatch_context_t<hb_get_glyph_alternates_dispatch_t, unsigned> 2603 { 2604 static return_t default_return_value () { return 0; } 2605 bool stop_sublookup_iteration (return_t r) const { return r; } 2606 2607 private: 2608 template <typename T, typename ...Ts> auto 2609 _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN 2610 ( obj.get_glyph_alternates (std::forward<Ts> (ds)...) ) 2611 template <typename T, typename ...Ts> auto 2612 _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN 2613 ( default_return_value () ) 2614 public: 2615 template <typename T, typename ...Ts> auto 2616 dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN 2617 ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) ) 2618 }; 2619 2620 /** 2621 * hb_ot_layout_lookup_get_glyph_alternates: 2622 * @face: a face. 2623 * @lookup_index: index of the feature lookup to query. 2624 * @glyph: a glyph id. 2625 * @start_offset: starting offset. 2626 * @alternate_count: (inout) (nullable): Input = the maximum number of alternate glyphs to return; 2627 * Output = the actual number of alternate glyphs returned (may be zero). 2628 * @alternate_glyphs: (out caller-allocates) (array length=alternate_count): A glyphs buffer. 2629 * Alternate glyphs associated with the glyph id. 2630 * 2631 * Fetches alternates of a glyph from a given GSUB lookup index. Note that for one-to-one GSUB 2632 * glyph substitutions, this function fetches the substituted glyph. 2633 * 2634 * Return value: Total number of alternates found in the specific lookup index for the given glyph id. 2635 * 2636 * Since: 2.6.8 2637 **/ 2638 HB_EXTERN unsigned 2639 hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face, 2640 unsigned lookup_index, 2641 hb_codepoint_t glyph, 2642 unsigned start_offset, 2643 unsigned *alternate_count /* IN/OUT. May be NULL. */, 2644 hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) 2645 { 2646 hb_get_glyph_alternates_dispatch_t c; 2647 const OT::SubstLookup &lookup = face->table.GSUB->table->get_lookup (lookup_index); 2648 auto ret = lookup.dispatch (&c, glyph, start_offset, alternate_count, alternate_glyphs); 2649 if (!ret && alternate_count) *alternate_count = 0; 2650 return ret; 2651 } 2652 2653 struct hb_collect_glyph_alternates_dispatch_t : 2654 hb_dispatch_context_t<hb_collect_glyph_alternates_dispatch_t, bool> 2655 { 2656 static return_t default_return_value () { return false; } 2657 bool stop_sublookup_iteration (return_t r) const { return false; } 2658 2659 private: 2660 template <typename T, typename ...Ts> auto 2661 _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN 2662 ( (obj.collect_glyph_alternates (std::forward<Ts> (ds)...), true) ) 2663 template <typename T, typename ...Ts> auto 2664 _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN 2665 ( default_return_value () ) 2666 public: 2667 template <typename T, typename ...Ts> auto 2668 dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN 2669 ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) ) 2670 }; 2671 2672 /** 2673 * hb_ot_layout_lookup_collect_glyph_alternates: 2674 * @face: a face. 2675 * @lookup_index: index of the feature lookup to query. 2676 * @alternate_count: (inout): mapping from glyph index to number of alternates for that glyph. 2677 * @alternate_glyphs: (inout): mapping from encoded glyph index and alternate index, to alternate glyph ids. 2678 * 2679 * Collects alternates of glyphs from a given GSUB lookup index. 2680 * 2681 * For one-to-one GSUB glyph substitutions, this function collects the 2682 * substituted glyph. 2683 * 2684 * For lookups that assign multiple alternates to a glyph, all alternate glyphs are collected. 2685 * 2686 * For other lookup types, nothing is performed and `false` is returned. 2687 * 2688 * The `alternate_count` mapping will contain the number of alternates for each glyph id. 2689 * Upon entry, this mapping should contain the glyph ids as keys, and the number of alternates 2690 * currently known for each glyph id as values. 2691 * 2692 * The `alternate_glyphs` mapping will contain the alternate glyph ids for each glyph id. 2693 * The mapping is encoded in the following way, upon entry and after processing: 2694 * If G is the glyph id, and A0, A1, ..., A(n-1) are the alternate glyph ids, 2695 * the mapping will contain the following entries: (G + (i << 24)) -> A(i) 2696 * for i = 0, 1, ..., n-1 where n is the number of alternates for G as per `alternate_count`. 2697 * 2698 * Return value: `true` if alternates were collected, `false` otherwise. 2699 * Since: 12.1.0 2700 */ 2701 HB_EXTERN hb_bool_t 2702 hb_ot_layout_lookup_collect_glyph_alternates (hb_face_t *face, 2703 unsigned lookup_index, 2704 hb_map_t *alternate_count /* IN/OUT */, 2705 hb_map_t *alternate_glyphs /* IN/OUT */) 2706 { 2707 hb_collect_glyph_alternates_dispatch_t c; 2708 const OT::SubstLookup &lookup = face->table.GSUB->table->get_lookup (lookup_index); 2709 return lookup.dispatch (&c, alternate_count, alternate_glyphs); 2710 } 2711 2712 struct hb_position_single_dispatch_t : 2713 hb_dispatch_context_t<hb_position_single_dispatch_t, bool> 2714 { 2715 static return_t default_return_value () { return false; } 2716 bool stop_sublookup_iteration (return_t r) const { return r; } 2717 2718 private: 2719 template <typename T, typename ...Ts> auto 2720 _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN 2721 ( obj.position_single (std::forward<Ts> (ds)...) ) 2722 template <typename T, typename ...Ts> auto 2723 _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN 2724 ( default_return_value () ) 2725 public: 2726 template <typename T, typename ...Ts> auto 2727 dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN 2728 ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) ) 2729 }; 2730 2731 /** 2732 * hb_ot_layout_lookup_get_optical_bound: 2733 * @font: a font. 2734 * @lookup_index: index of the feature lookup to query. 2735 * @direction: edge of the glyph to query. 2736 * @glyph: a glyph id. 2737 * 2738 * Fetches the optical bound of a glyph positioned at the margin of text. 2739 * The direction identifies which edge of the glyph to query. 2740 * 2741 * Return value: Adjustment value. Negative values mean the glyph will stick out of the margin. 2742 * 2743 * Since: 5.3.0 2744 **/ 2745 hb_position_t 2746 hb_ot_layout_lookup_get_optical_bound (hb_font_t *font, 2747 unsigned lookup_index, 2748 hb_direction_t direction, 2749 hb_codepoint_t glyph) 2750 { 2751 const OT::PosLookup &lookup = font->face->table.GPOS->table->get_lookup (lookup_index); 2752 hb_blob_t *blob = font->face->table.GPOS->get_blob (); 2753 hb_glyph_position_t pos = {0}; 2754 hb_position_single_dispatch_t c; 2755 lookup.dispatch (&c, font, blob, direction, glyph, pos); 2756 hb_position_t ret = 0; 2757 switch (direction) 2758 { 2759 case HB_DIRECTION_LTR: 2760 ret = pos.x_offset; 2761 break; 2762 case HB_DIRECTION_RTL: 2763 ret = pos.x_advance - pos.x_offset; 2764 break; 2765 case HB_DIRECTION_TTB: 2766 ret = pos.y_offset; 2767 break; 2768 case HB_DIRECTION_BTT: 2769 ret = pos.y_advance - pos.y_offset; 2770 break; 2771 case HB_DIRECTION_INVALID: 2772 default: 2773 break; 2774 } 2775 return ret; 2776 } 2777 #endif 2778 2779 2780 #endif