Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /* | |
| 2 * Copyright © 2017 Google, Inc. | |
| 3 * | |
| 4 * This is part of HarfBuzz, a text shaping library. | |
| 5 * | |
| 6 * Permission is hereby granted, without written agreement and without | |
| 7 * license or royalty fees, to use, copy, modify, and distribute this | |
| 8 * software and its documentation for any purpose, provided that the | |
| 9 * above copyright notice and the following two paragraphs appear in | |
| 10 * all copies of this software. | |
| 11 * | |
| 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | |
| 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | |
| 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | |
| 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | |
| 16 * DAMAGE. | |
| 17 * | |
| 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | |
| 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
| 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS | |
| 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | |
| 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
| 23 * | |
| 24 * Google Author(s): Behdad Esfahbod | |
| 25 */ | |
| 26 | |
| 27 #ifndef HB_AAT_LAYOUT_MORX_TABLE_HH | |
| 28 #define HB_AAT_LAYOUT_MORX_TABLE_HH | |
| 29 | |
| 30 #include "hb-open-type.hh" | |
| 31 #include "hb-aat-layout-common.hh" | |
| 32 #include "hb-ot-layout-common.hh" | |
| 33 #include "hb-ot-layout-gdef-table.hh" | |
| 34 #include "hb-aat-map.hh" | |
| 35 | |
| 36 /* | |
| 37 * morx -- Extended Glyph Metamorphosis | |
| 38 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html | |
| 39 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html | |
| 40 */ | |
| 41 #define HB_AAT_TAG_morx HB_TAG('m','o','r','x') | |
| 42 #define HB_AAT_TAG_mort HB_TAG('m','o','r','t') | |
| 43 | |
| 44 | |
| 45 namespace AAT { | |
| 46 | |
| 47 using namespace OT; | |
| 48 | |
| 49 template <typename Types> | |
| 50 struct RearrangementSubtable | |
| 51 { | |
| 52 typedef typename Types::HBUINT HBUINT; | |
| 53 | |
| 54 typedef void EntryData; | |
| 55 | |
| 56 struct driver_context_t | |
| 57 { | |
| 58 static constexpr bool in_place = true; | |
| 59 enum Flags | |
| 60 { | |
| 61 MarkFirst = 0x8000, /* If set, make the current glyph the first | |
| 62 * glyph to be rearranged. */ | |
| 63 DontAdvance = 0x4000, /* If set, don't advance to the next glyph | |
| 64 * before going to the new state. This means | |
| 65 * that the glyph index doesn't change, even | |
| 66 * if the glyph at that index has changed. */ | |
| 67 MarkLast = 0x2000, /* If set, make the current glyph the last | |
| 68 * glyph to be rearranged. */ | |
| 69 Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */ | |
| 70 Verb = 0x000F, /* The type of rearrangement specified. */ | |
| 71 }; | |
| 72 | |
| 73 driver_context_t (const RearrangementSubtable *table HB_UNUSED) : | |
| 74 ret (false), | |
| 75 start (0), end (0) {} | |
| 76 | |
| 77 bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED, | |
| 78 const Entry<EntryData> &entry) | |
| 79 { | |
| 80 return (entry.flags & Verb) && start < end; | |
| 81 } | |
| 82 void transition (StateTableDriver<Types, EntryData> *driver, | |
| 83 const Entry<EntryData> &entry) | |
| 84 { | |
| 85 hb_buffer_t *buffer = driver->buffer; | |
| 86 unsigned int flags = entry.flags; | |
| 87 | |
| 88 if (flags & MarkFirst) | |
| 89 start = buffer->idx; | |
| 90 | |
| 91 if (flags & MarkLast) | |
| 92 end = hb_min (buffer->idx + 1, buffer->len); | |
| 93 | |
| 94 if ((flags & Verb) && start < end) | |
| 95 { | |
| 96 /* The following map has two nibbles, for start-side | |
| 97 * and end-side. Values of 0,1,2 mean move that many | |
| 98 * to the other side. Value of 3 means move 2 and | |
| 99 * flip them. */ | |
| 100 const unsigned char map[16] = | |
| 101 { | |
| 102 0x00, /* 0 no change */ | |
| 103 0x10, /* 1 Ax => xA */ | |
| 104 0x01, /* 2 xD => Dx */ | |
| 105 0x11, /* 3 AxD => DxA */ | |
| 106 0x20, /* 4 ABx => xAB */ | |
| 107 0x30, /* 5 ABx => xBA */ | |
| 108 0x02, /* 6 xCD => CDx */ | |
| 109 0x03, /* 7 xCD => DCx */ | |
| 110 0x12, /* 8 AxCD => CDxA */ | |
| 111 0x13, /* 9 AxCD => DCxA */ | |
| 112 0x21, /* 10 ABxD => DxAB */ | |
| 113 0x31, /* 11 ABxD => DxBA */ | |
| 114 0x22, /* 12 ABxCD => CDxAB */ | |
| 115 0x32, /* 13 ABxCD => CDxBA */ | |
| 116 0x23, /* 14 ABxCD => DCxAB */ | |
| 117 0x33, /* 15 ABxCD => DCxBA */ | |
| 118 }; | |
| 119 | |
| 120 unsigned int m = map[flags & Verb]; | |
| 121 unsigned int l = hb_min (2u, m >> 4); | |
| 122 unsigned int r = hb_min (2u, m & 0x0F); | |
| 123 bool reverse_l = 3 == (m >> 4); | |
| 124 bool reverse_r = 3 == (m & 0x0F); | |
| 125 | |
| 126 if (end - start >= l + r && end-start <= HB_MAX_CONTEXT_LENGTH) | |
| 127 { | |
| 128 buffer->merge_clusters (start, hb_min (buffer->idx + 1, buffer->len)); | |
| 129 buffer->merge_clusters (start, end); | |
| 130 | |
| 131 hb_glyph_info_t *info = buffer->info; | |
| 132 hb_glyph_info_t buf[4]; | |
| 133 | |
| 134 hb_memcpy (buf, info + start, l * sizeof (buf[0])); | |
| 135 hb_memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); | |
| 136 | |
| 137 if (l != r) | |
| 138 memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0])); | |
| 139 | |
| 140 hb_memcpy (info + start, buf + 2, r * sizeof (buf[0])); | |
| 141 hb_memcpy (info + end - l, buf, l * sizeof (buf[0])); | |
| 142 if (reverse_l) | |
| 143 { | |
| 144 buf[0] = info[end - 1]; | |
| 145 info[end - 1] = info[end - 2]; | |
| 146 info[end - 2] = buf[0]; | |
| 147 } | |
| 148 if (reverse_r) | |
| 149 { | |
| 150 buf[0] = info[start]; | |
| 151 info[start] = info[start + 1]; | |
| 152 info[start + 1] = buf[0]; | |
| 153 } | |
| 154 } | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 public: | |
| 159 bool ret; | |
| 160 private: | |
| 161 unsigned int start; | |
| 162 unsigned int end; | |
| 163 }; | |
| 164 | |
| 165 bool apply (hb_aat_apply_context_t *c) const | |
| 166 { | |
| 167 TRACE_APPLY (this); | |
| 168 | |
| 169 driver_context_t dc (this); | |
| 170 | |
| 171 StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face); | |
| 172 driver.drive (&dc); | |
| 173 | |
| 174 return_trace (dc.ret); | |
| 175 } | |
| 176 | |
| 177 bool sanitize (hb_sanitize_context_t *c) const | |
| 178 { | |
| 179 TRACE_SANITIZE (this); | |
| 180 return_trace (machine.sanitize (c)); | |
| 181 } | |
| 182 | |
| 183 protected: | |
| 184 StateTable<Types, EntryData> machine; | |
| 185 public: | |
| 186 DEFINE_SIZE_STATIC (16); | |
| 187 }; | |
| 188 | |
| 189 template <typename Types> | |
| 190 struct ContextualSubtable | |
| 191 { | |
| 192 typedef typename Types::HBUINT HBUINT; | |
| 193 | |
| 194 struct EntryData | |
| 195 { | |
| 196 HBUINT16 markIndex; /* Index of the substitution table for the | |
| 197 * marked glyph (use 0xFFFF for none). */ | |
| 198 HBUINT16 currentIndex; /* Index of the substitution table for the | |
| 199 * current glyph (use 0xFFFF for none). */ | |
| 200 public: | |
| 201 DEFINE_SIZE_STATIC (4); | |
| 202 }; | |
| 203 | |
| 204 struct driver_context_t | |
| 205 { | |
| 206 static constexpr bool in_place = true; | |
| 207 enum Flags | |
| 208 { | |
| 209 SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */ | |
| 210 DontAdvance = 0x4000, /* If set, don't advance to the next glyph before | |
| 211 * going to the new state. */ | |
| 212 Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */ | |
| 213 }; | |
| 214 | |
| 215 driver_context_t (const ContextualSubtable *table_, | |
| 216 hb_aat_apply_context_t *c_) : | |
| 217 ret (false), | |
| 218 c (c_), | |
| 219 gdef (*c->gdef_table), | |
| 220 mark_set (false), | |
| 221 has_glyph_classes (gdef.has_glyph_classes ()), | |
| 222 mark (0), | |
| 223 table (table_), | |
| 224 subs (table+table->substitutionTables) {} | |
| 225 | |
| 226 bool is_actionable (StateTableDriver<Types, EntryData> *driver, | |
| 227 const Entry<EntryData> &entry) | |
| 228 { | |
| 229 hb_buffer_t *buffer = driver->buffer; | |
| 230 | |
| 231 if (buffer->idx == buffer->len && !mark_set) | |
| 232 return false; | |
| 233 | |
| 234 return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF; | |
| 235 } | |
| 236 void transition (StateTableDriver<Types, EntryData> *driver, | |
| 237 const Entry<EntryData> &entry) | |
| 238 { | |
| 239 hb_buffer_t *buffer = driver->buffer; | |
| 240 | |
| 241 /* Looks like CoreText applies neither mark nor current substitution for | |
| 242 * end-of-text if mark was not explicitly set. */ | |
| 243 if (buffer->idx == buffer->len && !mark_set) | |
| 244 return; | |
| 245 | |
| 246 const HBGlyphID16 *replacement; | |
| 247 | |
| 248 replacement = nullptr; | |
| 249 if (Types::extended) | |
| 250 { | |
| 251 if (entry.data.markIndex != 0xFFFF) | |
| 252 { | |
| 253 const Lookup<HBGlyphID16> &lookup = subs[entry.data.markIndex]; | |
| 254 replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs); | |
| 255 } | |
| 256 } | |
| 257 else | |
| 258 { | |
| 259 unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint; | |
| 260 const UnsizedArrayOf<HBGlyphID16> &subs_old = (const UnsizedArrayOf<HBGlyphID16> &) subs; | |
| 261 replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; | |
| 262 if (!replacement->sanitize (&c->sanitizer) || !*replacement) | |
| 263 replacement = nullptr; | |
| 264 } | |
| 265 if (replacement) | |
| 266 { | |
| 267 buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len)); | |
| 268 buffer->info[mark].codepoint = *replacement; | |
| 269 if (has_glyph_classes) | |
| 270 _hb_glyph_info_set_glyph_props (&buffer->info[mark], | |
| 271 gdef.get_glyph_props (*replacement)); | |
| 272 ret = true; | |
| 273 } | |
| 274 | |
| 275 replacement = nullptr; | |
| 276 unsigned int idx = hb_min (buffer->idx, buffer->len - 1); | |
| 277 if (Types::extended) | |
| 278 { | |
| 279 if (entry.data.currentIndex != 0xFFFF) | |
| 280 { | |
| 281 const Lookup<HBGlyphID16> &lookup = subs[entry.data.currentIndex]; | |
| 282 replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs); | |
| 283 } | |
| 284 } | |
| 285 else | |
| 286 { | |
| 287 unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint; | |
| 288 const UnsizedArrayOf<HBGlyphID16> &subs_old = (const UnsizedArrayOf<HBGlyphID16> &) subs; | |
| 289 replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; | |
| 290 if (!replacement->sanitize (&c->sanitizer) || !*replacement) | |
| 291 replacement = nullptr; | |
| 292 } | |
| 293 if (replacement) | |
| 294 { | |
| 295 buffer->info[idx].codepoint = *replacement; | |
| 296 if (has_glyph_classes) | |
| 297 _hb_glyph_info_set_glyph_props (&buffer->info[idx], | |
| 298 gdef.get_glyph_props (*replacement)); | |
| 299 ret = true; | |
| 300 } | |
| 301 | |
| 302 if (entry.flags & SetMark) | |
| 303 { | |
| 304 mark_set = true; | |
| 305 mark = buffer->idx; | |
| 306 } | |
| 307 } | |
| 308 | |
| 309 public: | |
| 310 bool ret; | |
| 311 private: | |
| 312 hb_aat_apply_context_t *c; | |
| 313 const OT::GDEF &gdef; | |
| 314 bool mark_set; | |
| 315 bool has_glyph_classes; | |
| 316 unsigned int mark; | |
| 317 const ContextualSubtable *table; | |
| 318 const UnsizedListOfOffset16To<Lookup<HBGlyphID16>, HBUINT, false> &subs; | |
| 319 }; | |
| 320 | |
| 321 bool apply (hb_aat_apply_context_t *c) const | |
| 322 { | |
| 323 TRACE_APPLY (this); | |
| 324 | |
| 325 driver_context_t dc (this, c); | |
| 326 | |
| 327 StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face); | |
| 328 driver.drive (&dc); | |
| 329 | |
| 330 return_trace (dc.ret); | |
| 331 } | |
| 332 | |
| 333 bool sanitize (hb_sanitize_context_t *c) const | |
| 334 { | |
| 335 TRACE_SANITIZE (this); | |
| 336 | |
| 337 unsigned int num_entries = 0; | |
| 338 if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false); | |
| 339 | |
| 340 if (!Types::extended) | |
| 341 return_trace (substitutionTables.sanitize (c, this, 0)); | |
| 342 | |
| 343 unsigned int num_lookups = 0; | |
| 344 | |
| 345 const Entry<EntryData> *entries = machine.get_entries (); | |
| 346 for (unsigned int i = 0; i < num_entries; i++) | |
| 347 { | |
| 348 const EntryData &data = entries[i].data; | |
| 349 | |
| 350 if (data.markIndex != 0xFFFF) | |
| 351 num_lookups = hb_max (num_lookups, 1u + data.markIndex); | |
| 352 if (data.currentIndex != 0xFFFF) | |
| 353 num_lookups = hb_max (num_lookups, 1u + data.currentIndex); | |
| 354 } | |
| 355 | |
| 356 return_trace (substitutionTables.sanitize (c, this, num_lookups)); | |
| 357 } | |
| 358 | |
| 359 protected: | |
| 360 StateTable<Types, EntryData> | |
| 361 machine; | |
| 362 NNOffsetTo<UnsizedListOfOffset16To<Lookup<HBGlyphID16>, HBUINT, false>, HBUINT> | |
| 363 substitutionTables; | |
| 364 public: | |
| 365 DEFINE_SIZE_STATIC (20); | |
| 366 }; | |
| 367 | |
| 368 | |
| 369 template <bool extended> | |
| 370 struct LigatureEntry; | |
| 371 | |
| 372 template <> | |
| 373 struct LigatureEntry<true> | |
| 374 { | |
| 375 enum Flags | |
| 376 { | |
| 377 SetComponent = 0x8000, /* Push this glyph onto the component stack for | |
| 378 * eventual processing. */ | |
| 379 DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the | |
| 380 next iteration. */ | |
| 381 PerformAction = 0x2000, /* Use the ligActionIndex to process a ligature | |
| 382 * group. */ | |
| 383 Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */ | |
| 384 }; | |
| 385 | |
| 386 struct EntryData | |
| 387 { | |
| 388 HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry | |
| 389 * for processing this group, if indicated | |
| 390 * by the flags. */ | |
| 391 public: | |
| 392 DEFINE_SIZE_STATIC (2); | |
| 393 }; | |
| 394 | |
| 395 static bool performAction (const Entry<EntryData> &entry) | |
| 396 { return entry.flags & PerformAction; } | |
| 397 | |
| 398 static unsigned int ligActionIndex (const Entry<EntryData> &entry) | |
| 399 { return entry.data.ligActionIndex; } | |
| 400 }; | |
| 401 template <> | |
| 402 struct LigatureEntry<false> | |
| 403 { | |
| 404 enum Flags | |
| 405 { | |
| 406 SetComponent = 0x8000, /* Push this glyph onto the component stack for | |
| 407 * eventual processing. */ | |
| 408 DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the | |
| 409 next iteration. */ | |
| 410 Offset = 0x3FFF, /* Byte offset from beginning of subtable to the | |
| 411 * ligature action list. This value must be a | |
| 412 * multiple of 4. */ | |
| 413 }; | |
| 414 | |
| 415 typedef void EntryData; | |
| 416 | |
| 417 static bool performAction (const Entry<EntryData> &entry) | |
| 418 { return entry.flags & Offset; } | |
| 419 | |
| 420 static unsigned int ligActionIndex (const Entry<EntryData> &entry) | |
| 421 { return entry.flags & Offset; } | |
| 422 }; | |
| 423 | |
| 424 | |
| 425 template <typename Types> | |
| 426 struct LigatureSubtable | |
| 427 { | |
| 428 typedef typename Types::HBUINT HBUINT; | |
| 429 | |
| 430 typedef LigatureEntry<Types::extended> LigatureEntryT; | |
| 431 typedef typename LigatureEntryT::EntryData EntryData; | |
| 432 | |
| 433 struct driver_context_t | |
| 434 { | |
| 435 static constexpr bool in_place = false; | |
| 436 enum | |
| 437 { | |
| 438 DontAdvance = LigatureEntryT::DontAdvance, | |
| 439 }; | |
| 440 enum LigActionFlags | |
| 441 { | |
| 442 LigActionLast = 0x80000000, /* This is the last action in the list. This also | |
| 443 * implies storage. */ | |
| 444 LigActionStore = 0x40000000, /* Store the ligature at the current cumulated index | |
| 445 * in the ligature table in place of the marked | |
| 446 * (i.e. currently-popped) glyph. */ | |
| 447 LigActionOffset = 0x3FFFFFFF, /* A 30-bit value which is sign-extended to 32-bits | |
| 448 * and added to the glyph ID, resulting in an index | |
| 449 * into the component table. */ | |
| 450 }; | |
| 451 | |
| 452 driver_context_t (const LigatureSubtable *table_, | |
| 453 hb_aat_apply_context_t *c_) : | |
| 454 ret (false), | |
| 455 c (c_), | |
| 456 table (table_), | |
| 457 ligAction (table+table->ligAction), | |
| 458 component (table+table->component), | |
| 459 ligature (table+table->ligature), | |
| 460 match_length (0) {} | |
| 461 | |
| 462 bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED, | |
| 463 const Entry<EntryData> &entry) | |
| 464 { | |
| 465 return LigatureEntryT::performAction (entry); | |
| 466 } | |
| 467 void transition (StateTableDriver<Types, EntryData> *driver, | |
| 468 const Entry<EntryData> &entry) | |
| 469 { | |
| 470 hb_buffer_t *buffer = driver->buffer; | |
| 471 | |
| 472 DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx); | |
| 473 if (entry.flags & LigatureEntryT::SetComponent) | |
| 474 { | |
| 475 /* Never mark same index twice, in case DontAdvance was used... */ | |
| 476 if (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len) | |
| 477 match_length--; | |
| 478 | |
| 479 match_positions[match_length++ % ARRAY_LENGTH (match_positions)] = buffer->out_len; | |
| 480 DEBUG_MSG (APPLY, nullptr, "Set component at %u", buffer->out_len); | |
| 481 } | |
| 482 | |
| 483 if (LigatureEntryT::performAction (entry)) | |
| 484 { | |
| 485 DEBUG_MSG (APPLY, nullptr, "Perform action with %u", match_length); | |
| 486 unsigned int end = buffer->out_len; | |
| 487 | |
| 488 if (unlikely (!match_length)) | |
| 489 return; | |
| 490 | |
| 491 if (buffer->idx >= buffer->len) | |
| 492 return; /* TODO Work on previous instead? */ | |
| 493 | |
| 494 unsigned int cursor = match_length; | |
| 495 | |
| 496 unsigned int action_idx = LigatureEntryT::ligActionIndex (entry); | |
| 497 action_idx = Types::offsetToIndex (action_idx, table, ligAction.arrayZ); | |
| 498 const HBUINT32 *actionData = &ligAction[action_idx]; | |
| 499 | |
| 500 unsigned int ligature_idx = 0; | |
| 501 unsigned int action; | |
| 502 do | |
| 503 { | |
| 504 if (unlikely (!cursor)) | |
| 505 { | |
| 506 /* Stack underflow. Clear the stack. */ | |
| 507 DEBUG_MSG (APPLY, nullptr, "Stack underflow"); | |
| 508 match_length = 0; | |
| 509 break; | |
| 510 } | |
| 511 | |
| 512 DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1); | |
| 513 if (unlikely (!buffer->move_to (match_positions[--cursor % ARRAY_LENGTH (match_positions)]))) return; | |
| 514 | |
| 515 if (unlikely (!actionData->sanitize (&c->sanitizer))) break; | |
| 516 action = *actionData; | |
| 517 | |
| 518 uint32_t uoffset = action & LigActionOffset; | |
| 519 if (uoffset & 0x20000000) | |
| 520 uoffset |= 0xC0000000; /* Sign-extend. */ | |
| 521 int32_t offset = (int32_t) uoffset; | |
| 522 unsigned int component_idx = buffer->cur().codepoint + offset; | |
| 523 component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ); | |
| 524 const HBUINT16 &componentData = component[component_idx]; | |
| 525 if (unlikely (!componentData.sanitize (&c->sanitizer))) break; | |
| 526 ligature_idx += componentData; | |
| 527 | |
| 528 DEBUG_MSG (APPLY, nullptr, "Action store %u last %u", | |
| 529 bool (action & LigActionStore), | |
| 530 bool (action & LigActionLast)); | |
| 531 if (action & (LigActionStore | LigActionLast)) | |
| 532 { | |
| 533 ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ); | |
| 534 const HBGlyphID16 &ligatureData = ligature[ligature_idx]; | |
| 535 if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break; | |
| 536 hb_codepoint_t lig = ligatureData; | |
| 537 | |
| 538 DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig); | |
| 539 if (unlikely (!buffer->replace_glyph (lig))) return; | |
| 540 | |
| 541 unsigned int lig_end = match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] + 1u; | |
| 542 /* Now go and delete all subsequent components. */ | |
| 543 while (match_length - 1u > cursor) | |
| 544 { | |
| 545 DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); | |
| 546 if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return; | |
| 547 if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return; | |
| 548 } | |
| 549 | |
| 550 if (unlikely (!buffer->move_to (lig_end))) return; | |
| 551 buffer->merge_out_clusters (match_positions[cursor % ARRAY_LENGTH (match_positions)], buffer->out_len); | |
| 552 } | |
| 553 | |
| 554 actionData++; | |
| 555 } | |
| 556 while (!(action & LigActionLast)); | |
| 557 if (unlikely (!buffer->move_to (end))) return; | |
| 558 } | |
| 559 } | |
| 560 | |
| 561 public: | |
| 562 bool ret; | |
| 563 private: | |
| 564 hb_aat_apply_context_t *c; | |
| 565 const LigatureSubtable *table; | |
| 566 const UnsizedArrayOf<HBUINT32> &ligAction; | |
| 567 const UnsizedArrayOf<HBUINT16> &component; | |
| 568 const UnsizedArrayOf<HBGlyphID16> &ligature; | |
| 569 unsigned int match_length; | |
| 570 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; | |
| 571 }; | |
| 572 | |
| 573 bool apply (hb_aat_apply_context_t *c) const | |
| 574 { | |
| 575 TRACE_APPLY (this); | |
| 576 | |
| 577 driver_context_t dc (this, c); | |
| 578 | |
| 579 StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face); | |
| 580 driver.drive (&dc); | |
| 581 | |
| 582 return_trace (dc.ret); | |
| 583 } | |
| 584 | |
| 585 bool sanitize (hb_sanitize_context_t *c) const | |
| 586 { | |
| 587 TRACE_SANITIZE (this); | |
| 588 /* The rest of array sanitizations are done at run-time. */ | |
| 589 return_trace (c->check_struct (this) && machine.sanitize (c) && | |
| 590 ligAction && component && ligature); | |
| 591 } | |
| 592 | |
| 593 protected: | |
| 594 StateTable<Types, EntryData> | |
| 595 machine; | |
| 596 NNOffsetTo<UnsizedArrayOf<HBUINT32>, HBUINT> | |
| 597 ligAction; /* Offset to the ligature action table. */ | |
| 598 NNOffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT> | |
| 599 component; /* Offset to the component table. */ | |
| 600 NNOffsetTo<UnsizedArrayOf<HBGlyphID16>, HBUINT> | |
| 601 ligature; /* Offset to the actual ligature lists. */ | |
| 602 public: | |
| 603 DEFINE_SIZE_STATIC (28); | |
| 604 }; | |
| 605 | |
| 606 template <typename Types> | |
| 607 struct NoncontextualSubtable | |
| 608 { | |
| 609 bool apply (hb_aat_apply_context_t *c) const | |
| 610 { | |
| 611 TRACE_APPLY (this); | |
| 612 | |
| 613 const OT::GDEF &gdef (*c->gdef_table); | |
| 614 bool has_glyph_classes = gdef.has_glyph_classes (); | |
| 615 | |
| 616 bool ret = false; | |
| 617 unsigned int num_glyphs = c->face->get_num_glyphs (); | |
| 618 | |
| 619 hb_glyph_info_t *info = c->buffer->info; | |
| 620 unsigned int count = c->buffer->len; | |
| 621 for (unsigned int i = 0; i < count; i++) | |
| 622 { | |
| 623 const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs); | |
| 624 if (replacement) | |
| 625 { | |
| 626 info[i].codepoint = *replacement; | |
| 627 if (has_glyph_classes) | |
| 628 _hb_glyph_info_set_glyph_props (&info[i], | |
| 629 gdef.get_glyph_props (*replacement)); | |
| 630 ret = true; | |
| 631 } | |
| 632 } | |
| 633 | |
| 634 return_trace (ret); | |
| 635 } | |
| 636 | |
| 637 bool sanitize (hb_sanitize_context_t *c) const | |
| 638 { | |
| 639 TRACE_SANITIZE (this); | |
| 640 return_trace (substitute.sanitize (c)); | |
| 641 } | |
| 642 | |
| 643 protected: | |
| 644 Lookup<HBGlyphID16> substitute; | |
| 645 public: | |
| 646 DEFINE_SIZE_MIN (2); | |
| 647 }; | |
| 648 | |
| 649 template <typename Types> | |
| 650 struct InsertionSubtable | |
| 651 { | |
| 652 typedef typename Types::HBUINT HBUINT; | |
| 653 | |
| 654 struct EntryData | |
| 655 { | |
| 656 HBUINT16 currentInsertIndex; /* Zero-based index into the insertion glyph table. | |
| 657 * The number of glyphs to be inserted is contained | |
| 658 * in the currentInsertCount field in the flags. | |
| 659 * A value of 0xFFFF indicates no insertion is to | |
| 660 * be done. */ | |
| 661 HBUINT16 markedInsertIndex; /* Zero-based index into the insertion glyph table. | |
| 662 * The number of glyphs to be inserted is contained | |
| 663 * in the markedInsertCount field in the flags. | |
| 664 * A value of 0xFFFF indicates no insertion is to | |
| 665 * be done. */ | |
| 666 public: | |
| 667 DEFINE_SIZE_STATIC (4); | |
| 668 }; | |
| 669 | |
| 670 struct driver_context_t | |
| 671 { | |
| 672 static constexpr bool in_place = false; | |
| 673 enum Flags | |
| 674 { | |
| 675 SetMark = 0x8000, /* If set, mark the current glyph. */ | |
| 676 DontAdvance = 0x4000, /* If set, don't advance to the next glyph before | |
| 677 * going to the new state. This does not mean | |
| 678 * that the glyph pointed to is the same one as | |
| 679 * before. If you've made insertions immediately | |
| 680 * downstream of the current glyph, the next glyph | |
| 681 * processed would in fact be the first one | |
| 682 * inserted. */ | |
| 683 CurrentIsKashidaLike= 0x2000, /* If set, and the currentInsertList is nonzero, | |
| 684 * then the specified glyph list will be inserted | |
| 685 * as a kashida-like insertion, either before or | |
| 686 * after the current glyph (depending on the state | |
| 687 * of the currentInsertBefore flag). If clear, and | |
| 688 * the currentInsertList is nonzero, then the | |
| 689 * specified glyph list will be inserted as a | |
| 690 * split-vowel-like insertion, either before or | |
| 691 * after the current glyph (depending on the state | |
| 692 * of the currentInsertBefore flag). */ | |
| 693 MarkedIsKashidaLike= 0x1000, /* If set, and the markedInsertList is nonzero, | |
| 694 * then the specified glyph list will be inserted | |
| 695 * as a kashida-like insertion, either before or | |
| 696 * after the marked glyph (depending on the state | |
| 697 * of the markedInsertBefore flag). If clear, and | |
| 698 * the markedInsertList is nonzero, then the | |
| 699 * specified glyph list will be inserted as a | |
| 700 * split-vowel-like insertion, either before or | |
| 701 * after the marked glyph (depending on the state | |
| 702 * of the markedInsertBefore flag). */ | |
| 703 CurrentInsertBefore= 0x0800, /* If set, specifies that insertions are to be made | |
| 704 * to the left of the current glyph. If clear, | |
| 705 * they're made to the right of the current glyph. */ | |
| 706 MarkedInsertBefore= 0x0400, /* If set, specifies that insertions are to be | |
| 707 * made to the left of the marked glyph. If clear, | |
| 708 * they're made to the right of the marked glyph. */ | |
| 709 CurrentInsertCount= 0x3E0, /* This 5-bit field is treated as a count of the | |
| 710 * number of glyphs to insert at the current | |
| 711 * position. Since zero means no insertions, the | |
| 712 * largest number of insertions at any given | |
| 713 * current location is 31 glyphs. */ | |
| 714 MarkedInsertCount= 0x001F, /* This 5-bit field is treated as a count of the | |
| 715 * number of glyphs to insert at the marked | |
| 716 * position. Since zero means no insertions, the | |
| 717 * largest number of insertions at any given | |
| 718 * marked location is 31 glyphs. */ | |
| 719 }; | |
| 720 | |
| 721 driver_context_t (const InsertionSubtable *table, | |
| 722 hb_aat_apply_context_t *c_) : | |
| 723 ret (false), | |
| 724 c (c_), | |
| 725 mark (0), | |
| 726 insertionAction (table+table->insertionAction) {} | |
| 727 | |
| 728 bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED, | |
| 729 const Entry<EntryData> &entry) | |
| 730 { | |
| 731 return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) && | |
| 732 (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF); | |
| 733 } | |
| 734 void transition (StateTableDriver<Types, EntryData> *driver, | |
| 735 const Entry<EntryData> &entry) | |
| 736 { | |
| 737 hb_buffer_t *buffer = driver->buffer; | |
| 738 unsigned int flags = entry.flags; | |
| 739 | |
| 740 unsigned mark_loc = buffer->out_len; | |
| 741 | |
| 742 if (entry.data.markedInsertIndex != 0xFFFF) | |
| 743 { | |
| 744 unsigned int count = (flags & MarkedInsertCount); | |
| 745 if (unlikely ((buffer->max_ops -= count) <= 0)) return; | |
| 746 unsigned int start = entry.data.markedInsertIndex; | |
| 747 const HBGlyphID16 *glyphs = &insertionAction[start]; | |
| 748 if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; | |
| 749 | |
| 750 bool before = flags & MarkedInsertBefore; | |
| 751 | |
| 752 unsigned int end = buffer->out_len; | |
| 753 if (unlikely (!buffer->move_to (mark))) return; | |
| 754 | |
| 755 if (buffer->idx < buffer->len && !before) | |
| 756 if (unlikely (!buffer->copy_glyph ())) return; | |
| 757 /* TODO We ignore KashidaLike setting. */ | |
| 758 if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; | |
| 759 if (buffer->idx < buffer->len && !before) | |
| 760 buffer->skip_glyph (); | |
| 761 | |
| 762 if (unlikely (!buffer->move_to (end + count))) return; | |
| 763 | |
| 764 buffer->unsafe_to_break_from_outbuffer (mark, hb_min (buffer->idx + 1, buffer->len)); | |
| 765 } | |
| 766 | |
| 767 if (flags & SetMark) | |
| 768 mark = mark_loc; | |
| 769 | |
| 770 if (entry.data.currentInsertIndex != 0xFFFF) | |
| 771 { | |
| 772 unsigned int count = (flags & CurrentInsertCount) >> 5; | |
| 773 if (unlikely ((buffer->max_ops -= count) <= 0)) return; | |
| 774 unsigned int start = entry.data.currentInsertIndex; | |
| 775 const HBGlyphID16 *glyphs = &insertionAction[start]; | |
| 776 if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; | |
| 777 | |
| 778 bool before = flags & CurrentInsertBefore; | |
| 779 | |
| 780 unsigned int end = buffer->out_len; | |
| 781 | |
| 782 if (buffer->idx < buffer->len && !before) | |
| 783 if (unlikely (!buffer->copy_glyph ())) return; | |
| 784 /* TODO We ignore KashidaLike setting. */ | |
| 785 if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; | |
| 786 if (buffer->idx < buffer->len && !before) | |
| 787 buffer->skip_glyph (); | |
| 788 | |
| 789 /* Humm. Not sure where to move to. There's this wording under | |
| 790 * DontAdvance flag: | |
| 791 * | |
| 792 * "If set, don't update the glyph index before going to the new state. | |
| 793 * This does not mean that the glyph pointed to is the same one as | |
| 794 * before. If you've made insertions immediately downstream of the | |
| 795 * current glyph, the next glyph processed would in fact be the first | |
| 796 * one inserted." | |
| 797 * | |
| 798 * This suggests that if DontAdvance is NOT set, we should move to | |
| 799 * end+count. If it *was*, then move to end, such that newly inserted | |
| 800 * glyphs are now visible. | |
| 801 * | |
| 802 * https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417 | |
| 803 */ | |
| 804 if (unlikely (!buffer->move_to ((flags & DontAdvance) ? end : end + count))) return; | |
| 805 } | |
| 806 } | |
| 807 | |
| 808 public: | |
| 809 bool ret; | |
| 810 private: | |
| 811 hb_aat_apply_context_t *c; | |
| 812 unsigned int mark; | |
| 813 const UnsizedArrayOf<HBGlyphID16> &insertionAction; | |
| 814 }; | |
| 815 | |
| 816 bool apply (hb_aat_apply_context_t *c) const | |
| 817 { | |
| 818 TRACE_APPLY (this); | |
| 819 | |
| 820 driver_context_t dc (this, c); | |
| 821 | |
| 822 StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face); | |
| 823 driver.drive (&dc); | |
| 824 | |
| 825 return_trace (dc.ret); | |
| 826 } | |
| 827 | |
| 828 bool sanitize (hb_sanitize_context_t *c) const | |
| 829 { | |
| 830 TRACE_SANITIZE (this); | |
| 831 /* The rest of array sanitizations are done at run-time. */ | |
| 832 return_trace (c->check_struct (this) && machine.sanitize (c) && | |
| 833 insertionAction); | |
| 834 } | |
| 835 | |
| 836 protected: | |
| 837 StateTable<Types, EntryData> | |
| 838 machine; | |
| 839 NNOffsetTo<UnsizedArrayOf<HBGlyphID16>, HBUINT> | |
| 840 insertionAction; /* Byte offset from stateHeader to the start of | |
| 841 * the insertion glyph table. */ | |
| 842 public: | |
| 843 DEFINE_SIZE_STATIC (20); | |
| 844 }; | |
| 845 | |
| 846 | |
| 847 struct Feature | |
| 848 { | |
| 849 bool sanitize (hb_sanitize_context_t *c) const | |
| 850 { | |
| 851 TRACE_SANITIZE (this); | |
| 852 return_trace (c->check_struct (this)); | |
| 853 } | |
| 854 | |
| 855 public: | |
| 856 HBUINT16 featureType; /* The type of feature. */ | |
| 857 HBUINT16 featureSetting; /* The feature's setting (aka selector). */ | |
| 858 HBUINT32 enableFlags; /* Flags for the settings that this feature | |
| 859 * and setting enables. */ | |
| 860 HBUINT32 disableFlags; /* Complement of flags for the settings that this | |
| 861 * feature and setting disable. */ | |
| 862 | |
| 863 public: | |
| 864 DEFINE_SIZE_STATIC (12); | |
| 865 }; | |
| 866 | |
| 867 template <typename Types> | |
| 868 struct ChainSubtable | |
| 869 { | |
| 870 typedef typename Types::HBUINT HBUINT; | |
| 871 | |
| 872 template <typename T> | |
| 873 friend struct Chain; | |
| 874 | |
| 875 unsigned int get_size () const { return length; } | |
| 876 unsigned int get_type () const { return coverage & 0xFF; } | |
| 877 unsigned int get_coverage () const { return coverage >> (sizeof (HBUINT) * 8 - 8); } | |
| 878 | |
| 879 enum Coverage | |
| 880 { | |
| 881 Vertical = 0x80, /* If set, this subtable will only be applied | |
| 882 * to vertical text. If clear, this subtable | |
| 883 * will only be applied to horizontal text. */ | |
| 884 Backwards = 0x40, /* If set, this subtable will process glyphs | |
| 885 * in descending order. If clear, it will | |
| 886 * process the glyphs in ascending order. */ | |
| 887 AllDirections = 0x20, /* If set, this subtable will be applied to | |
| 888 * both horizontal and vertical text (i.e. | |
| 889 * the state of bit 0x80000000 is ignored). */ | |
| 890 Logical = 0x10, /* If set, this subtable will process glyphs | |
| 891 * in logical order (or reverse logical order, | |
| 892 * depending on the value of bit 0x80000000). */ | |
| 893 }; | |
| 894 enum Type | |
| 895 { | |
| 896 Rearrangement = 0, | |
| 897 Contextual = 1, | |
| 898 Ligature = 2, | |
| 899 Noncontextual = 4, | |
| 900 Insertion = 5 | |
| 901 }; | |
| 902 | |
| 903 template <typename context_t, typename ...Ts> | |
| 904 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const | |
| 905 { | |
| 906 unsigned int subtable_type = get_type (); | |
| 907 TRACE_DISPATCH (this, subtable_type); | |
| 908 switch (subtable_type) { | |
| 909 case Rearrangement: return_trace (c->dispatch (u.rearrangement, std::forward<Ts> (ds)...)); | |
| 910 case Contextual: return_trace (c->dispatch (u.contextual, std::forward<Ts> (ds)...)); | |
| 911 case Ligature: return_trace (c->dispatch (u.ligature, std::forward<Ts> (ds)...)); | |
| 912 case Noncontextual: return_trace (c->dispatch (u.noncontextual, std::forward<Ts> (ds)...)); | |
| 913 case Insertion: return_trace (c->dispatch (u.insertion, std::forward<Ts> (ds)...)); | |
| 914 default: return_trace (c->default_return_value ()); | |
| 915 } | |
| 916 } | |
| 917 | |
| 918 bool apply (hb_aat_apply_context_t *c) const | |
| 919 { | |
| 920 TRACE_APPLY (this); | |
| 921 hb_sanitize_with_object_t with (&c->sanitizer, this); | |
| 922 return_trace (dispatch (c)); | |
| 923 } | |
| 924 | |
| 925 bool sanitize (hb_sanitize_context_t *c) const | |
| 926 { | |
| 927 TRACE_SANITIZE (this); | |
| 928 if (!length.sanitize (c) || | |
| 929 length <= min_size || | |
| 930 !c->check_range (this, length)) | |
| 931 return_trace (false); | |
| 932 | |
| 933 hb_sanitize_with_object_t with (c, this); | |
| 934 return_trace (dispatch (c)); | |
| 935 } | |
| 936 | |
| 937 protected: | |
| 938 HBUINT length; /* Total subtable length, including this header. */ | |
| 939 HBUINT coverage; /* Coverage flags and subtable type. */ | |
| 940 HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */ | |
| 941 union { | |
| 942 RearrangementSubtable<Types> rearrangement; | |
| 943 ContextualSubtable<Types> contextual; | |
| 944 LigatureSubtable<Types> ligature; | |
| 945 NoncontextualSubtable<Types> noncontextual; | |
| 946 InsertionSubtable<Types> insertion; | |
| 947 } u; | |
| 948 public: | |
| 949 DEFINE_SIZE_MIN (2 * sizeof (HBUINT) + 4); | |
| 950 }; | |
| 951 | |
| 952 template <typename Types> | |
| 953 struct Chain | |
| 954 { | |
| 955 typedef typename Types::HBUINT HBUINT; | |
| 956 | |
| 957 hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const | |
| 958 { | |
| 959 hb_mask_t flags = defaultFlags; | |
| 960 { | |
| 961 unsigned int count = featureCount; | |
| 962 for (unsigned i = 0; i < count; i++) | |
| 963 { | |
| 964 const Feature &feature = featureZ[i]; | |
| 965 hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType; | |
| 966 hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting; | |
| 967 retry: | |
| 968 // Check whether this type/setting pair was requested in the map, and if so, apply its flags. | |
| 969 // (The search here only looks at the type and setting fields of feature_info_t.) | |
| 970 hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 }; | |
| 971 if (map->features.bsearch (info)) | |
| 972 { | |
| 973 flags &= feature.disableFlags; | |
| 974 flags |= feature.enableFlags; | |
| 975 } | |
| 976 else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE && setting == HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS) | |
| 977 { | |
| 978 /* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */ | |
| 979 type = HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE; | |
| 980 setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS; | |
| 981 goto retry; | |
| 982 } | |
| 983 #ifndef HB_NO_AAT | |
| 984 else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE && setting && | |
| 985 /* TODO: Rudimentary language matching. */ | |
| 986 hb_language_matches (map->face->table.ltag->get_language (setting - 1), map->props.language)) | |
| 987 { | |
| 988 flags &= feature.disableFlags; | |
| 989 flags |= feature.enableFlags; | |
| 990 } | |
| 991 #endif | |
| 992 } | |
| 993 } | |
| 994 return flags; | |
| 995 } | |
| 996 | |
| 997 void apply (hb_aat_apply_context_t *c, | |
| 998 hb_mask_t flags) const | |
| 999 { | |
| 1000 const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount)); | |
| 1001 unsigned int count = subtableCount; | |
| 1002 for (unsigned int i = 0; i < count; i++) | |
| 1003 { | |
| 1004 bool reverse; | |
| 1005 | |
| 1006 if (!(subtable->subFeatureFlags & flags)) | |
| 1007 goto skip; | |
| 1008 | |
| 1009 if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) && | |
| 1010 HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != | |
| 1011 bool (subtable->get_coverage() & ChainSubtable<Types>::Vertical)) | |
| 1012 goto skip; | |
| 1013 | |
| 1014 /* Buffer contents is always in logical direction. Determine if | |
| 1015 * we need to reverse before applying this subtable. We reverse | |
| 1016 * back after if we did reverse indeed. | |
| 1017 * | |
| 1018 * Quoting the spac: | |
| 1019 * """ | |
| 1020 * Bits 28 and 30 of the coverage field control the order in which | |
| 1021 * glyphs are processed when the subtable is run by the layout engine. | |
| 1022 * Bit 28 is used to indicate if the glyph processing direction is | |
| 1023 * the same as logical order or layout order. Bit 30 is used to | |
| 1024 * indicate whether glyphs are processed forwards or backwards within | |
| 1025 * that order. | |
| 1026 | |
| 1027 Bit 30 Bit 28 Interpretation for Horizontal Text | |
| 1028 0 0 The subtable is processed in layout order | |
| 1029 (the same order as the glyphs, which is | |
| 1030 always left-to-right). | |
| 1031 1 0 The subtable is processed in reverse layout order | |
| 1032 (the order opposite that of the glyphs, which is | |
| 1033 always right-to-left). | |
| 1034 0 1 The subtable is processed in logical order | |
| 1035 (the same order as the characters, which may be | |
| 1036 left-to-right or right-to-left). | |
| 1037 1 1 The subtable is processed in reverse logical order | |
| 1038 (the order opposite that of the characters, which | |
| 1039 may be right-to-left or left-to-right). | |
| 1040 */ | |
| 1041 reverse = subtable->get_coverage () & ChainSubtable<Types>::Logical ? | |
| 1042 bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) : | |
| 1043 bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) != | |
| 1044 HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); | |
| 1045 | |
| 1046 if (!c->buffer->message (c->font, "start chainsubtable %d", c->lookup_index)) | |
| 1047 goto skip; | |
| 1048 | |
| 1049 if (reverse) | |
| 1050 c->buffer->reverse (); | |
| 1051 | |
| 1052 subtable->apply (c); | |
| 1053 | |
| 1054 if (reverse) | |
| 1055 c->buffer->reverse (); | |
| 1056 | |
| 1057 (void) c->buffer->message (c->font, "end chainsubtable %d", c->lookup_index); | |
| 1058 | |
| 1059 if (unlikely (!c->buffer->successful)) return; | |
| 1060 | |
| 1061 skip: | |
| 1062 subtable = &StructAfter<ChainSubtable<Types>> (*subtable); | |
| 1063 c->set_lookup_index (c->lookup_index + 1); | |
| 1064 } | |
| 1065 } | |
| 1066 | |
| 1067 unsigned int get_size () const { return length; } | |
| 1068 | |
| 1069 bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const | |
| 1070 { | |
| 1071 TRACE_SANITIZE (this); | |
| 1072 if (!length.sanitize (c) || | |
| 1073 length < min_size || | |
| 1074 !c->check_range (this, length)) | |
| 1075 return_trace (false); | |
| 1076 | |
| 1077 if (!c->check_array (featureZ.arrayZ, featureCount)) | |
| 1078 return_trace (false); | |
| 1079 | |
| 1080 const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount)); | |
| 1081 unsigned int count = subtableCount; | |
| 1082 for (unsigned int i = 0; i < count; i++) | |
| 1083 { | |
| 1084 if (!subtable->sanitize (c)) | |
| 1085 return_trace (false); | |
| 1086 subtable = &StructAfter<ChainSubtable<Types>> (*subtable); | |
| 1087 } | |
| 1088 | |
| 1089 return_trace (true); | |
| 1090 } | |
| 1091 | |
| 1092 protected: | |
| 1093 HBUINT32 defaultFlags; /* The default specification for subtables. */ | |
| 1094 HBUINT32 length; /* Total byte count, including this header. */ | |
| 1095 HBUINT featureCount; /* Number of feature subtable entries. */ | |
| 1096 HBUINT subtableCount; /* The number of subtables in the chain. */ | |
| 1097 | |
| 1098 UnsizedArrayOf<Feature> featureZ; /* Features. */ | |
| 1099 /*ChainSubtable firstSubtable;*//* Subtables. */ | |
| 1100 /*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ | |
| 1101 | |
| 1102 public: | |
| 1103 DEFINE_SIZE_MIN (8 + 2 * sizeof (HBUINT)); | |
| 1104 }; | |
| 1105 | |
| 1106 | |
| 1107 /* | |
| 1108 * The 'mort'/'morx' Table | |
| 1109 */ | |
| 1110 | |
| 1111 template <typename Types, hb_tag_t TAG> | |
| 1112 struct mortmorx | |
| 1113 { | |
| 1114 static constexpr hb_tag_t tableTag = TAG; | |
| 1115 | |
| 1116 bool has_data () const { return version != 0; } | |
| 1117 | |
| 1118 void compile_flags (const hb_aat_map_builder_t *mapper, | |
| 1119 hb_aat_map_t *map) const | |
| 1120 { | |
| 1121 const Chain<Types> *chain = &firstChain; | |
| 1122 unsigned int count = chainCount; | |
| 1123 for (unsigned int i = 0; i < count; i++) | |
| 1124 { | |
| 1125 map->chain_flags.push (chain->compile_flags (mapper)); | |
| 1126 chain = &StructAfter<Chain<Types>> (*chain); | |
| 1127 } | |
| 1128 } | |
| 1129 | |
| 1130 void apply (hb_aat_apply_context_t *c) const | |
| 1131 { | |
| 1132 if (unlikely (!c->buffer->successful)) return; | |
| 1133 c->set_lookup_index (0); | |
| 1134 const Chain<Types> *chain = &firstChain; | |
| 1135 unsigned int count = chainCount; | |
| 1136 for (unsigned int i = 0; i < count; i++) | |
| 1137 { | |
| 1138 chain->apply (c, c->plan->aat_map.chain_flags[i]); | |
| 1139 if (unlikely (!c->buffer->successful)) return; | |
| 1140 chain = &StructAfter<Chain<Types>> (*chain); | |
| 1141 } | |
| 1142 } | |
| 1143 | |
| 1144 bool sanitize (hb_sanitize_context_t *c) const | |
| 1145 { | |
| 1146 TRACE_SANITIZE (this); | |
| 1147 if (!version.sanitize (c) || !version || !chainCount.sanitize (c)) | |
| 1148 return_trace (false); | |
| 1149 | |
| 1150 const Chain<Types> *chain = &firstChain; | |
| 1151 unsigned int count = chainCount; | |
| 1152 for (unsigned int i = 0; i < count; i++) | |
| 1153 { | |
| 1154 if (!chain->sanitize (c, version)) | |
| 1155 return_trace (false); | |
| 1156 chain = &StructAfter<Chain<Types>> (*chain); | |
| 1157 } | |
| 1158 | |
| 1159 return_trace (true); | |
| 1160 } | |
| 1161 | |
| 1162 protected: | |
| 1163 HBUINT16 version; /* Version number of the glyph metamorphosis table. | |
| 1164 * 1, 2, or 3. */ | |
| 1165 HBUINT16 unused; /* Set to 0. */ | |
| 1166 HBUINT32 chainCount; /* Number of metamorphosis chains contained in this | |
| 1167 * table. */ | |
| 1168 Chain<Types> firstChain; /* Chains. */ | |
| 1169 | |
| 1170 public: | |
| 1171 DEFINE_SIZE_MIN (8); | |
| 1172 }; | |
| 1173 | |
| 1174 struct morx : mortmorx<ExtendedTypes, HB_AAT_TAG_morx> {}; | |
| 1175 struct mort : mortmorx<ObsoleteTypes, HB_AAT_TAG_mort> {}; | |
| 1176 | |
| 1177 | |
| 1178 } /* namespace AAT */ | |
| 1179 | |
| 1180 | |
| 1181 #endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */ |
