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, c); |
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, c); |
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 %d last %d" , |
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, c); |
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 | // If there's only one range, we already checked the flag. |
622 | auto *last_range = c->range_flags && (c->range_flags->length > 1) ? &(*c->range_flags)[0] : nullptr; |
623 | for (unsigned int i = 0; i < count; i++) |
624 | { |
625 | /* This block copied from StateTableDriver::drive. Keep in sync. */ |
626 | if (last_range) |
627 | { |
628 | auto *range = last_range; |
629 | { |
630 | unsigned cluster = info[i].cluster; |
631 | while (cluster < range->cluster_first) |
632 | range--; |
633 | while (cluster > range->cluster_last) |
634 | range++; |
635 | |
636 | last_range = range; |
637 | } |
638 | if (!(range->flags & c->subtable_flags)) |
639 | continue; |
640 | } |
641 | |
642 | const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs); |
643 | if (replacement) |
644 | { |
645 | info[i].codepoint = *replacement; |
646 | if (has_glyph_classes) |
647 | _hb_glyph_info_set_glyph_props (&info[i], |
648 | gdef.get_glyph_props (*replacement)); |
649 | ret = true; |
650 | } |
651 | } |
652 | |
653 | return_trace (ret); |
654 | } |
655 | |
656 | bool sanitize (hb_sanitize_context_t *c) const |
657 | { |
658 | TRACE_SANITIZE (this); |
659 | return_trace (substitute.sanitize (c)); |
660 | } |
661 | |
662 | protected: |
663 | Lookup<HBGlyphID16> substitute; |
664 | public: |
665 | DEFINE_SIZE_MIN (2); |
666 | }; |
667 | |
668 | template <typename Types> |
669 | struct InsertionSubtable |
670 | { |
671 | typedef typename Types::HBUINT HBUINT; |
672 | |
673 | struct EntryData |
674 | { |
675 | HBUINT16 currentInsertIndex; /* Zero-based index into the insertion glyph table. |
676 | * The number of glyphs to be inserted is contained |
677 | * in the currentInsertCount field in the flags. |
678 | * A value of 0xFFFF indicates no insertion is to |
679 | * be done. */ |
680 | HBUINT16 markedInsertIndex; /* Zero-based index into the insertion glyph table. |
681 | * The number of glyphs to be inserted is contained |
682 | * in the markedInsertCount field in the flags. |
683 | * A value of 0xFFFF indicates no insertion is to |
684 | * be done. */ |
685 | public: |
686 | DEFINE_SIZE_STATIC (4); |
687 | }; |
688 | |
689 | struct driver_context_t |
690 | { |
691 | static constexpr bool in_place = false; |
692 | enum Flags |
693 | { |
694 | SetMark = 0x8000, /* If set, mark the current glyph. */ |
695 | DontAdvance = 0x4000, /* If set, don't advance to the next glyph before |
696 | * going to the new state. This does not mean |
697 | * that the glyph pointed to is the same one as |
698 | * before. If you've made insertions immediately |
699 | * downstream of the current glyph, the next glyph |
700 | * processed would in fact be the first one |
701 | * inserted. */ |
702 | CurrentIsKashidaLike= 0x2000, /* If set, and the currentInsertList is nonzero, |
703 | * then the specified glyph list will be inserted |
704 | * as a kashida-like insertion, either before or |
705 | * after the current glyph (depending on the state |
706 | * of the currentInsertBefore flag). If clear, and |
707 | * the currentInsertList is nonzero, then the |
708 | * specified glyph list will be inserted as a |
709 | * split-vowel-like insertion, either before or |
710 | * after the current glyph (depending on the state |
711 | * of the currentInsertBefore flag). */ |
712 | MarkedIsKashidaLike= 0x1000, /* If set, and the markedInsertList is nonzero, |
713 | * then the specified glyph list will be inserted |
714 | * as a kashida-like insertion, either before or |
715 | * after the marked glyph (depending on the state |
716 | * of the markedInsertBefore flag). If clear, and |
717 | * the markedInsertList is nonzero, then the |
718 | * specified glyph list will be inserted as a |
719 | * split-vowel-like insertion, either before or |
720 | * after the marked glyph (depending on the state |
721 | * of the markedInsertBefore flag). */ |
722 | CurrentInsertBefore= 0x0800, /* If set, specifies that insertions are to be made |
723 | * to the left of the current glyph. If clear, |
724 | * they're made to the right of the current glyph. */ |
725 | MarkedInsertBefore= 0x0400, /* If set, specifies that insertions are to be |
726 | * made to the left of the marked glyph. If clear, |
727 | * they're made to the right of the marked glyph. */ |
728 | CurrentInsertCount= 0x3E0, /* This 5-bit field is treated as a count of the |
729 | * number of glyphs to insert at the current |
730 | * position. Since zero means no insertions, the |
731 | * largest number of insertions at any given |
732 | * current location is 31 glyphs. */ |
733 | MarkedInsertCount= 0x001F, /* This 5-bit field is treated as a count of the |
734 | * number of glyphs to insert at the marked |
735 | * position. Since zero means no insertions, the |
736 | * largest number of insertions at any given |
737 | * marked location is 31 glyphs. */ |
738 | }; |
739 | |
740 | driver_context_t (const InsertionSubtable *table, |
741 | hb_aat_apply_context_t *c_) : |
742 | ret (false), |
743 | c (c_), |
744 | mark (0), |
745 | insertionAction (table+table->insertionAction) {} |
746 | |
747 | bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED, |
748 | const Entry<EntryData> &entry) |
749 | { |
750 | return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) && |
751 | (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF); |
752 | } |
753 | void transition (StateTableDriver<Types, EntryData> *driver, |
754 | const Entry<EntryData> &entry) |
755 | { |
756 | hb_buffer_t *buffer = driver->buffer; |
757 | unsigned int flags = entry.flags; |
758 | |
759 | unsigned mark_loc = buffer->out_len; |
760 | |
761 | if (entry.data.markedInsertIndex != 0xFFFF) |
762 | { |
763 | unsigned int count = (flags & MarkedInsertCount); |
764 | if (unlikely ((buffer->max_ops -= count) <= 0)) return; |
765 | unsigned int start = entry.data.markedInsertIndex; |
766 | const HBGlyphID16 *glyphs = &insertionAction[start]; |
767 | if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; |
768 | |
769 | bool before = flags & MarkedInsertBefore; |
770 | |
771 | unsigned int end = buffer->out_len; |
772 | if (unlikely (!buffer->move_to (mark))) return; |
773 | |
774 | if (buffer->idx < buffer->len && !before) |
775 | if (unlikely (!buffer->copy_glyph ())) return; |
776 | /* TODO We ignore KashidaLike setting. */ |
777 | if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; |
778 | if (buffer->idx < buffer->len && !before) |
779 | buffer->skip_glyph (); |
780 | |
781 | if (unlikely (!buffer->move_to (end + count))) return; |
782 | |
783 | buffer->unsafe_to_break_from_outbuffer (mark, hb_min (buffer->idx + 1, buffer->len)); |
784 | } |
785 | |
786 | if (flags & SetMark) |
787 | mark = mark_loc; |
788 | |
789 | if (entry.data.currentInsertIndex != 0xFFFF) |
790 | { |
791 | unsigned int count = (flags & CurrentInsertCount) >> 5; |
792 | if (unlikely ((buffer->max_ops -= count) <= 0)) return; |
793 | unsigned int start = entry.data.currentInsertIndex; |
794 | const HBGlyphID16 *glyphs = &insertionAction[start]; |
795 | if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; |
796 | |
797 | bool before = flags & CurrentInsertBefore; |
798 | |
799 | unsigned int end = buffer->out_len; |
800 | |
801 | if (buffer->idx < buffer->len && !before) |
802 | if (unlikely (!buffer->copy_glyph ())) return; |
803 | /* TODO We ignore KashidaLike setting. */ |
804 | if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; |
805 | if (buffer->idx < buffer->len && !before) |
806 | buffer->skip_glyph (); |
807 | |
808 | /* Humm. Not sure where to move to. There's this wording under |
809 | * DontAdvance flag: |
810 | * |
811 | * "If set, don't update the glyph index before going to the new state. |
812 | * This does not mean that the glyph pointed to is the same one as |
813 | * before. If you've made insertions immediately downstream of the |
814 | * current glyph, the next glyph processed would in fact be the first |
815 | * one inserted." |
816 | * |
817 | * This suggests that if DontAdvance is NOT set, we should move to |
818 | * end+count. If it *was*, then move to end, such that newly inserted |
819 | * glyphs are now visible. |
820 | * |
821 | * https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417 |
822 | */ |
823 | if (unlikely (!buffer->move_to ((flags & DontAdvance) ? end : end + count))) return; |
824 | } |
825 | } |
826 | |
827 | public: |
828 | bool ret; |
829 | private: |
830 | hb_aat_apply_context_t *c; |
831 | unsigned int mark; |
832 | const UnsizedArrayOf<HBGlyphID16> &insertionAction; |
833 | }; |
834 | |
835 | bool apply (hb_aat_apply_context_t *c) const |
836 | { |
837 | TRACE_APPLY (this); |
838 | |
839 | driver_context_t dc (this, c); |
840 | |
841 | StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face); |
842 | driver.drive (&dc, c); |
843 | |
844 | return_trace (dc.ret); |
845 | } |
846 | |
847 | bool sanitize (hb_sanitize_context_t *c) const |
848 | { |
849 | TRACE_SANITIZE (this); |
850 | /* The rest of array sanitizations are done at run-time. */ |
851 | return_trace (c->check_struct (this) && machine.sanitize (c) && |
852 | insertionAction); |
853 | } |
854 | |
855 | protected: |
856 | StateTable<Types, EntryData> |
857 | machine; |
858 | NNOffsetTo<UnsizedArrayOf<HBGlyphID16>, HBUINT> |
859 | insertionAction; /* Byte offset from stateHeader to the start of |
860 | * the insertion glyph table. */ |
861 | public: |
862 | DEFINE_SIZE_STATIC (20); |
863 | }; |
864 | |
865 | |
866 | struct Feature |
867 | { |
868 | bool sanitize (hb_sanitize_context_t *c) const |
869 | { |
870 | TRACE_SANITIZE (this); |
871 | return_trace (c->check_struct (this)); |
872 | } |
873 | |
874 | public: |
875 | HBUINT16 featureType; /* The type of feature. */ |
876 | HBUINT16 featureSetting; /* The feature's setting (aka selector). */ |
877 | HBUINT32 enableFlags; /* Flags for the settings that this feature |
878 | * and setting enables. */ |
879 | HBUINT32 disableFlags; /* Complement of flags for the settings that this |
880 | * feature and setting disable. */ |
881 | |
882 | public: |
883 | DEFINE_SIZE_STATIC (12); |
884 | }; |
885 | |
886 | template <typename Types> |
887 | struct ChainSubtable |
888 | { |
889 | typedef typename Types::HBUINT HBUINT; |
890 | |
891 | template <typename T> |
892 | friend struct Chain; |
893 | |
894 | unsigned int get_size () const { return length; } |
895 | unsigned int get_type () const { return coverage & 0xFF; } |
896 | unsigned int get_coverage () const { return coverage >> (sizeof (HBUINT) * 8 - 8); } |
897 | |
898 | enum Coverage |
899 | { |
900 | Vertical = 0x80, /* If set, this subtable will only be applied |
901 | * to vertical text. If clear, this subtable |
902 | * will only be applied to horizontal text. */ |
903 | Backwards = 0x40, /* If set, this subtable will process glyphs |
904 | * in descending order. If clear, it will |
905 | * process the glyphs in ascending order. */ |
906 | AllDirections = 0x20, /* If set, this subtable will be applied to |
907 | * both horizontal and vertical text (i.e. |
908 | * the state of bit 0x80000000 is ignored). */ |
909 | Logical = 0x10, /* If set, this subtable will process glyphs |
910 | * in logical order (or reverse logical order, |
911 | * depending on the value of bit 0x80000000). */ |
912 | }; |
913 | enum Type |
914 | { |
915 | Rearrangement = 0, |
916 | Contextual = 1, |
917 | Ligature = 2, |
918 | Noncontextual = 4, |
919 | Insertion = 5 |
920 | }; |
921 | |
922 | template <typename context_t, typename ...Ts> |
923 | typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const |
924 | { |
925 | unsigned int subtable_type = get_type (); |
926 | TRACE_DISPATCH (this, subtable_type); |
927 | switch (subtable_type) { |
928 | case Rearrangement: return_trace (c->dispatch (u.rearrangement, std::forward<Ts> (ds)...)); |
929 | case Contextual: return_trace (c->dispatch (u.contextual, std::forward<Ts> (ds)...)); |
930 | case Ligature: return_trace (c->dispatch (u.ligature, std::forward<Ts> (ds)...)); |
931 | case Noncontextual: return_trace (c->dispatch (u.noncontextual, std::forward<Ts> (ds)...)); |
932 | case Insertion: return_trace (c->dispatch (u.insertion, std::forward<Ts> (ds)...)); |
933 | default: return_trace (c->default_return_value ()); |
934 | } |
935 | } |
936 | |
937 | bool apply (hb_aat_apply_context_t *c) const |
938 | { |
939 | TRACE_APPLY (this); |
940 | hb_sanitize_with_object_t with (&c->sanitizer, this); |
941 | return_trace (dispatch (c)); |
942 | } |
943 | |
944 | bool sanitize (hb_sanitize_context_t *c) const |
945 | { |
946 | TRACE_SANITIZE (this); |
947 | if (!length.sanitize (c) || |
948 | length <= min_size || |
949 | !c->check_range (this, length)) |
950 | return_trace (false); |
951 | |
952 | hb_sanitize_with_object_t with (c, this); |
953 | return_trace (dispatch (c)); |
954 | } |
955 | |
956 | protected: |
957 | HBUINT length; /* Total subtable length, including this header. */ |
958 | HBUINT coverage; /* Coverage flags and subtable type. */ |
959 | HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */ |
960 | union { |
961 | RearrangementSubtable<Types> rearrangement; |
962 | ContextualSubtable<Types> contextual; |
963 | LigatureSubtable<Types> ligature; |
964 | NoncontextualSubtable<Types> noncontextual; |
965 | InsertionSubtable<Types> insertion; |
966 | } u; |
967 | public: |
968 | DEFINE_SIZE_MIN (2 * sizeof (HBUINT) + 4); |
969 | }; |
970 | |
971 | template <typename Types> |
972 | struct Chain |
973 | { |
974 | typedef typename Types::HBUINT HBUINT; |
975 | |
976 | hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const |
977 | { |
978 | hb_mask_t flags = defaultFlags; |
979 | { |
980 | unsigned int count = featureCount; |
981 | for (unsigned i = 0; i < count; i++) |
982 | { |
983 | const Feature &feature = featureZ[i]; |
984 | hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType; |
985 | hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting; |
986 | retry: |
987 | // Check whether this type/setting pair was requested in the map, and if so, apply its flags. |
988 | // (The search here only looks at the type and setting fields of feature_info_t.) |
989 | hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 }; |
990 | if (map->current_features.bsearch (info)) |
991 | { |
992 | flags &= feature.disableFlags; |
993 | flags |= feature.enableFlags; |
994 | } |
995 | else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE && setting == HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS) |
996 | { |
997 | /* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */ |
998 | type = HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE; |
999 | setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS; |
1000 | goto retry; |
1001 | } |
1002 | #ifndef HB_NO_AAT |
1003 | else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE && setting && |
1004 | /* TODO: Rudimentary language matching. */ |
1005 | hb_language_matches (map->face->table.ltag->get_language (setting - 1), map->props.language)) |
1006 | { |
1007 | flags &= feature.disableFlags; |
1008 | flags |= feature.enableFlags; |
1009 | } |
1010 | #endif |
1011 | } |
1012 | } |
1013 | return flags; |
1014 | } |
1015 | |
1016 | void apply (hb_aat_apply_context_t *c) const |
1017 | { |
1018 | const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount)); |
1019 | unsigned int count = subtableCount; |
1020 | for (unsigned int i = 0; i < count; i++) |
1021 | { |
1022 | bool reverse; |
1023 | |
1024 | if (hb_none (hb_iter (c->range_flags) | |
1025 | hb_map ([&subtable] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable->subFeatureFlags & (_.flags); }))) |
1026 | goto skip; |
1027 | c->subtable_flags = subtable->subFeatureFlags; |
1028 | |
1029 | if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) && |
1030 | HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != |
1031 | bool (subtable->get_coverage() & ChainSubtable<Types>::Vertical)) |
1032 | goto skip; |
1033 | |
1034 | /* Buffer contents is always in logical direction. Determine if |
1035 | * we need to reverse before applying this subtable. We reverse |
1036 | * back after if we did reverse indeed. |
1037 | * |
1038 | * Quoting the spac: |
1039 | * """ |
1040 | * Bits 28 and 30 of the coverage field control the order in which |
1041 | * glyphs are processed when the subtable is run by the layout engine. |
1042 | * Bit 28 is used to indicate if the glyph processing direction is |
1043 | * the same as logical order or layout order. Bit 30 is used to |
1044 | * indicate whether glyphs are processed forwards or backwards within |
1045 | * that order. |
1046 | |
1047 | Bit 30 Bit 28 Interpretation for Horizontal Text |
1048 | 0 0 The subtable is processed in layout order |
1049 | (the same order as the glyphs, which is |
1050 | always left-to-right). |
1051 | 1 0 The subtable is processed in reverse layout order |
1052 | (the order opposite that of the glyphs, which is |
1053 | always right-to-left). |
1054 | 0 1 The subtable is processed in logical order |
1055 | (the same order as the characters, which may be |
1056 | left-to-right or right-to-left). |
1057 | 1 1 The subtable is processed in reverse logical order |
1058 | (the order opposite that of the characters, which |
1059 | may be right-to-left or left-to-right). |
1060 | */ |
1061 | reverse = subtable->get_coverage () & ChainSubtable<Types>::Logical ? |
1062 | bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) : |
1063 | bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) != |
1064 | HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); |
1065 | |
1066 | if (!c->buffer->message (c->font, "start chainsubtable %u" , c->lookup_index)) |
1067 | goto skip; |
1068 | |
1069 | if (reverse) |
1070 | c->buffer->reverse (); |
1071 | |
1072 | subtable->apply (c); |
1073 | |
1074 | if (reverse) |
1075 | c->buffer->reverse (); |
1076 | |
1077 | (void) c->buffer->message (c->font, "end chainsubtable %u" , c->lookup_index); |
1078 | |
1079 | if (unlikely (!c->buffer->successful)) return; |
1080 | |
1081 | skip: |
1082 | subtable = &StructAfter<ChainSubtable<Types>> (*subtable); |
1083 | c->set_lookup_index (c->lookup_index + 1); |
1084 | } |
1085 | } |
1086 | |
1087 | unsigned int get_size () const { return length; } |
1088 | |
1089 | bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const |
1090 | { |
1091 | TRACE_SANITIZE (this); |
1092 | if (!length.sanitize (c) || |
1093 | length < min_size || |
1094 | !c->check_range (this, length)) |
1095 | return_trace (false); |
1096 | |
1097 | if (!c->check_array (featureZ.arrayZ, featureCount)) |
1098 | return_trace (false); |
1099 | |
1100 | const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount)); |
1101 | unsigned int count = subtableCount; |
1102 | for (unsigned int i = 0; i < count; i++) |
1103 | { |
1104 | if (!subtable->sanitize (c)) |
1105 | return_trace (false); |
1106 | subtable = &StructAfter<ChainSubtable<Types>> (*subtable); |
1107 | } |
1108 | |
1109 | return_trace (true); |
1110 | } |
1111 | |
1112 | protected: |
1113 | HBUINT32 defaultFlags; /* The default specification for subtables. */ |
1114 | HBUINT32 length; /* Total byte count, including this header. */ |
1115 | HBUINT featureCount; /* Number of feature subtable entries. */ |
1116 | HBUINT subtableCount; /* The number of subtables in the chain. */ |
1117 | |
1118 | UnsizedArrayOf<Feature> featureZ; /* Features. */ |
1119 | /*ChainSubtable firstSubtable;*//* Subtables. */ |
1120 | /*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ |
1121 | |
1122 | public: |
1123 | DEFINE_SIZE_MIN (8 + 2 * sizeof (HBUINT)); |
1124 | }; |
1125 | |
1126 | |
1127 | /* |
1128 | * The 'mort'/'morx' Table |
1129 | */ |
1130 | |
1131 | template <typename Types, hb_tag_t TAG> |
1132 | struct mortmorx |
1133 | { |
1134 | static constexpr hb_tag_t tableTag = TAG; |
1135 | |
1136 | bool has_data () const { return version != 0; } |
1137 | |
1138 | void compile_flags (const hb_aat_map_builder_t *mapper, |
1139 | hb_aat_map_t *map) const |
1140 | { |
1141 | const Chain<Types> *chain = &firstChain; |
1142 | unsigned int count = chainCount; |
1143 | if (unlikely (!map->chain_flags.resize (count))) |
1144 | return; |
1145 | for (unsigned int i = 0; i < count; i++) |
1146 | { |
1147 | map->chain_flags[i].push (hb_aat_map_t::range_flags_t {chain->compile_flags (mapper), |
1148 | mapper->range_first, |
1149 | mapper->range_last}); |
1150 | chain = &StructAfter<Chain<Types>> (*chain); |
1151 | } |
1152 | } |
1153 | |
1154 | void apply (hb_aat_apply_context_t *c, |
1155 | const hb_aat_map_t &map) const |
1156 | { |
1157 | if (unlikely (!c->buffer->successful)) return; |
1158 | |
1159 | c->buffer->unsafe_to_concat (); |
1160 | |
1161 | c->set_lookup_index (0); |
1162 | const Chain<Types> *chain = &firstChain; |
1163 | unsigned int count = chainCount; |
1164 | for (unsigned int i = 0; i < count; i++) |
1165 | { |
1166 | c->range_flags = &map.chain_flags[i]; |
1167 | chain->apply (c); |
1168 | if (unlikely (!c->buffer->successful)) return; |
1169 | chain = &StructAfter<Chain<Types>> (*chain); |
1170 | } |
1171 | } |
1172 | |
1173 | bool sanitize (hb_sanitize_context_t *c) const |
1174 | { |
1175 | TRACE_SANITIZE (this); |
1176 | if (!version.sanitize (c) || !version || !chainCount.sanitize (c)) |
1177 | return_trace (false); |
1178 | |
1179 | const Chain<Types> *chain = &firstChain; |
1180 | unsigned int count = chainCount; |
1181 | for (unsigned int i = 0; i < count; i++) |
1182 | { |
1183 | if (!chain->sanitize (c, version)) |
1184 | return_trace (false); |
1185 | chain = &StructAfter<Chain<Types>> (*chain); |
1186 | } |
1187 | |
1188 | return_trace (true); |
1189 | } |
1190 | |
1191 | protected: |
1192 | HBUINT16 version; /* Version number of the glyph metamorphosis table. |
1193 | * 1, 2, or 3. */ |
1194 | HBUINT16 unused; /* Set to 0. */ |
1195 | HBUINT32 chainCount; /* Number of metamorphosis chains contained in this |
1196 | * table. */ |
1197 | Chain<Types> firstChain; /* Chains. */ |
1198 | |
1199 | public: |
1200 | DEFINE_SIZE_MIN (8); |
1201 | }; |
1202 | |
1203 | struct morx : mortmorx<ExtendedTypes, HB_AAT_TAG_morx> {}; |
1204 | struct mort : mortmorx<ObsoleteTypes, HB_AAT_TAG_mort> {}; |
1205 | |
1206 | |
1207 | } /* namespace AAT */ |
1208 | |
1209 | |
1210 | #endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */ |
1211 | |