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 | |
34 | /* |
35 | * morx -- Extended Glyph Metamorphosis |
36 | * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html |
37 | */ |
38 | #define HB_AAT_TAG_morx HB_TAG('m','o','r','x') |
39 | |
40 | |
41 | namespace AAT { |
42 | |
43 | using namespace OT; |
44 | |
45 | |
46 | struct RearrangementSubtable |
47 | { |
48 | typedef void EntryData; |
49 | |
50 | struct driver_context_t |
51 | { |
52 | static const bool in_place = true; |
53 | enum Flags { |
54 | MarkFirst = 0x8000, /* If set, make the current glyph the first |
55 | * glyph to be rearranged. */ |
56 | DontAdvance = 0x4000, /* If set, don't advance to the next glyph |
57 | * before going to the new state. This means |
58 | * that the glyph index doesn't change, even |
59 | * if the glyph at that index has changed. */ |
60 | MarkLast = 0x2000, /* If set, make the current glyph the last |
61 | * glyph to be rearranged. */ |
62 | Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */ |
63 | Verb = 0x000F, /* The type of rearrangement specified. */ |
64 | }; |
65 | |
66 | inline driver_context_t (const RearrangementSubtable *table) : |
67 | ret (false), |
68 | start (0), end (0) {} |
69 | |
70 | inline bool is_actionable (StateTableDriver<EntryData> *driver, |
71 | const Entry<EntryData> *entry) |
72 | { |
73 | return (entry->flags & Verb) && start < end; |
74 | } |
75 | inline bool transition (StateTableDriver<EntryData> *driver, |
76 | const Entry<EntryData> *entry) |
77 | { |
78 | hb_buffer_t *buffer = driver->buffer; |
79 | unsigned int flags = entry->flags; |
80 | |
81 | if (flags & MarkFirst) |
82 | start = buffer->idx; |
83 | |
84 | if (flags & MarkLast) |
85 | end = MIN (buffer->idx + 1, buffer->len); |
86 | |
87 | if ((flags & Verb) && start < end) |
88 | { |
89 | /* The following map has two nibbles, for start-side |
90 | * and end-side. Values of 0,1,2 mean move that many |
91 | * to the other side. Value of 3 means move 2 and |
92 | * flip them. */ |
93 | const unsigned char map[16] = |
94 | { |
95 | 0x00, /* 0 no change */ |
96 | 0x10, /* 1 Ax => xA */ |
97 | 0x01, /* 2 xD => Dx */ |
98 | 0x11, /* 3 AxD => DxA */ |
99 | 0x20, /* 4 ABx => xAB */ |
100 | 0x30, /* 5 ABx => xBA */ |
101 | 0x02, /* 6 xCD => CDx */ |
102 | 0x03, /* 7 xCD => DCx */ |
103 | 0x12, /* 8 AxCD => CDxA */ |
104 | 0x13, /* 9 AxCD => DCxA */ |
105 | 0x21, /* 10 ABxD => DxAB */ |
106 | 0x31, /* 11 ABxD => DxBA */ |
107 | 0x22, /* 12 ABxCD => CDxAB */ |
108 | 0x32, /* 13 ABxCD => CDxBA */ |
109 | 0x23, /* 14 ABxCD => DCxAB */ |
110 | 0x33, /* 15 ABxCD => DCxBA */ |
111 | }; |
112 | |
113 | unsigned int m = map[flags & Verb]; |
114 | unsigned int l = MIN<unsigned int> (2, m >> 4); |
115 | unsigned int r = MIN<unsigned int> (2, m & 0x0F); |
116 | bool reverse_l = 3 == (m >> 4); |
117 | bool reverse_r = 3 == (m & 0x0F); |
118 | |
119 | if (end - start >= l + r) |
120 | { |
121 | buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len)); |
122 | buffer->merge_clusters (start, end); |
123 | |
124 | hb_glyph_info_t *info = buffer->info; |
125 | hb_glyph_info_t buf[4]; |
126 | |
127 | memcpy (buf, info + start, l * sizeof (buf[0])); |
128 | memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); |
129 | |
130 | if (l != r) |
131 | memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0])); |
132 | |
133 | memcpy (info + start, buf + 2, r * sizeof (buf[0])); |
134 | memcpy (info + end - l, buf, l * sizeof (buf[0])); |
135 | if (reverse_l) |
136 | { |
137 | buf[0] = info[end - 1]; |
138 | info[end - 1] = info[end - 2]; |
139 | info[end - 2] = buf[0]; |
140 | } |
141 | if (reverse_r) |
142 | { |
143 | buf[0] = info[start]; |
144 | info[start] = info[start + 1]; |
145 | info[start + 1] = buf[0]; |
146 | } |
147 | } |
148 | } |
149 | |
150 | return true; |
151 | } |
152 | |
153 | public: |
154 | bool ret; |
155 | private: |
156 | unsigned int start; |
157 | unsigned int end; |
158 | }; |
159 | |
160 | inline bool apply (hb_aat_apply_context_t *c) const |
161 | { |
162 | TRACE_APPLY (this); |
163 | |
164 | driver_context_t dc (this); |
165 | |
166 | StateTableDriver<void> driver (machine, c->buffer, c->face); |
167 | driver.drive (&dc); |
168 | |
169 | return_trace (dc.ret); |
170 | } |
171 | |
172 | inline bool sanitize (hb_sanitize_context_t *c) const |
173 | { |
174 | TRACE_SANITIZE (this); |
175 | return_trace (machine.sanitize (c)); |
176 | } |
177 | |
178 | protected: |
179 | StateTable<EntryData> machine; |
180 | public: |
181 | DEFINE_SIZE_STATIC (16); |
182 | }; |
183 | |
184 | struct ContextualSubtable |
185 | { |
186 | struct EntryData |
187 | { |
188 | HBUINT16 markIndex; /* Index of the substitution table for the |
189 | * marked glyph (use 0xFFFF for none). */ |
190 | HBUINT16 currentIndex; /* Index of the substitution table for the |
191 | * current glyph (use 0xFFFF for none). */ |
192 | public: |
193 | DEFINE_SIZE_STATIC (4); |
194 | }; |
195 | |
196 | struct driver_context_t |
197 | { |
198 | static const bool in_place = true; |
199 | enum Flags { |
200 | SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */ |
201 | DontAdvance = 0x4000, /* If set, don't advance to the next glyph before |
202 | * going to the new state. */ |
203 | Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */ |
204 | }; |
205 | |
206 | inline driver_context_t (const ContextualSubtable *table) : |
207 | ret (false), |
208 | mark_set (false), |
209 | mark (0), |
210 | subs (table+table->substitutionTables) {} |
211 | |
212 | inline bool is_actionable (StateTableDriver<EntryData> *driver, |
213 | const Entry<EntryData> *entry) |
214 | { |
215 | hb_buffer_t *buffer = driver->buffer; |
216 | |
217 | if (buffer->idx == buffer->len && !mark_set) |
218 | return false; |
219 | |
220 | return entry->data.markIndex != 0xFFFF || entry->data.currentIndex != 0xFFFF; |
221 | } |
222 | inline bool transition (StateTableDriver<EntryData> *driver, |
223 | const Entry<EntryData> *entry) |
224 | { |
225 | hb_buffer_t *buffer = driver->buffer; |
226 | |
227 | /* Looks like CoreText applies neither mark nor current substitution for |
228 | * end-of-text if mark was not explicitly set. */ |
229 | if (buffer->idx == buffer->len && !mark_set) |
230 | return true; |
231 | |
232 | if (entry->data.markIndex != 0xFFFF) |
233 | { |
234 | const Lookup<GlyphID> &lookup = subs[entry->data.markIndex]; |
235 | hb_glyph_info_t *info = buffer->info; |
236 | const GlyphID *replacement = lookup.get_value (info[mark].codepoint, driver->num_glyphs); |
237 | if (replacement) |
238 | { |
239 | buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len)); |
240 | info[mark].codepoint = *replacement; |
241 | ret = true; |
242 | } |
243 | } |
244 | if (entry->data.currentIndex != 0xFFFF) |
245 | { |
246 | unsigned int idx = MIN (buffer->idx, buffer->len - 1); |
247 | const Lookup<GlyphID> &lookup = subs[entry->data.currentIndex]; |
248 | hb_glyph_info_t *info = buffer->info; |
249 | const GlyphID *replacement = lookup.get_value (info[idx].codepoint, driver->num_glyphs); |
250 | if (replacement) |
251 | { |
252 | info[idx].codepoint = *replacement; |
253 | ret = true; |
254 | } |
255 | } |
256 | |
257 | if (entry->flags & SetMark) |
258 | { |
259 | mark_set = true; |
260 | mark = buffer->idx; |
261 | } |
262 | |
263 | return true; |
264 | } |
265 | |
266 | public: |
267 | bool ret; |
268 | private: |
269 | bool mark_set; |
270 | unsigned int mark; |
271 | const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> &subs; |
272 | }; |
273 | |
274 | inline bool apply (hb_aat_apply_context_t *c) const |
275 | { |
276 | TRACE_APPLY (this); |
277 | |
278 | driver_context_t dc (this); |
279 | |
280 | StateTableDriver<EntryData> driver (machine, c->buffer, c->face); |
281 | driver.drive (&dc); |
282 | |
283 | return_trace (dc.ret); |
284 | } |
285 | |
286 | inline bool sanitize (hb_sanitize_context_t *c) const |
287 | { |
288 | TRACE_SANITIZE (this); |
289 | |
290 | unsigned int num_entries = 0; |
291 | if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false); |
292 | |
293 | unsigned int num_lookups = 0; |
294 | |
295 | const Entry<EntryData> *entries = machine.get_entries (); |
296 | for (unsigned int i = 0; i < num_entries; i++) |
297 | { |
298 | const EntryData &data = entries[i].data; |
299 | |
300 | if (data.markIndex != 0xFFFF) |
301 | num_lookups = MAX<unsigned int> (num_lookups, 1 + data.markIndex); |
302 | if (data.currentIndex != 0xFFFF) |
303 | num_lookups = MAX<unsigned int> (num_lookups, 1 + data.currentIndex); |
304 | } |
305 | |
306 | return_trace (substitutionTables.sanitize (c, this, num_lookups)); |
307 | } |
308 | |
309 | protected: |
310 | StateTable<EntryData> |
311 | machine; |
312 | LOffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> > |
313 | substitutionTables; |
314 | public: |
315 | DEFINE_SIZE_STATIC (20); |
316 | }; |
317 | |
318 | struct LigatureSubtable |
319 | { |
320 | struct EntryData |
321 | { |
322 | HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry |
323 | * for processing this group, if indicated |
324 | * by the flags. */ |
325 | public: |
326 | DEFINE_SIZE_STATIC (2); |
327 | }; |
328 | |
329 | struct driver_context_t |
330 | { |
331 | static const bool in_place = false; |
332 | enum Flags { |
333 | SetComponent = 0x8000, /* Push this glyph onto the component stack for |
334 | * eventual processing. */ |
335 | DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the |
336 | next iteration. */ |
337 | PerformAction = 0x2000, /* Use the ligActionIndex to process a ligature |
338 | * group. */ |
339 | Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */ |
340 | }; |
341 | enum LigActionFlags { |
342 | LigActionLast = 0x80000000, /* This is the last action in the list. This also |
343 | * implies storage. */ |
344 | LigActionStore = 0x40000000, /* Store the ligature at the current cumulated index |
345 | * in the ligature table in place of the marked |
346 | * (i.e. currently-popped) glyph. */ |
347 | LigActionOffset = 0x3FFFFFFF, /* A 30-bit value which is sign-extended to 32-bits |
348 | * and added to the glyph ID, resulting in an index |
349 | * into the component table. */ |
350 | }; |
351 | |
352 | inline driver_context_t (const LigatureSubtable *table, |
353 | hb_aat_apply_context_t *c_) : |
354 | ret (false), |
355 | c (c_), |
356 | ligAction (table+table->ligAction), |
357 | component (table+table->component), |
358 | ligature (table+table->ligature), |
359 | match_length (0) {} |
360 | |
361 | inline bool is_actionable (StateTableDriver<EntryData> *driver, |
362 | const Entry<EntryData> *entry) |
363 | { |
364 | return !!(entry->flags & PerformAction); |
365 | } |
366 | inline bool transition (StateTableDriver<EntryData> *driver, |
367 | const Entry<EntryData> *entry) |
368 | { |
369 | hb_buffer_t *buffer = driver->buffer; |
370 | unsigned int flags = entry->flags; |
371 | |
372 | if (flags & SetComponent) |
373 | { |
374 | if (unlikely (match_length >= ARRAY_LENGTH (match_positions))) |
375 | return false; |
376 | |
377 | /* Never mark same index twice, in case DontAdvance was used... */ |
378 | if (match_length && match_positions[match_length - 1] == buffer->out_len) |
379 | match_length--; |
380 | |
381 | match_positions[match_length++] = buffer->out_len; |
382 | } |
383 | |
384 | if (flags & PerformAction) |
385 | { |
386 | unsigned int end = buffer->out_len; |
387 | unsigned int action_idx = entry->data.ligActionIndex; |
388 | unsigned int action; |
389 | unsigned int ligature_idx = 0; |
390 | do |
391 | { |
392 | if (unlikely (!match_length)) |
393 | return false; |
394 | |
395 | buffer->move_to (match_positions[--match_length]); |
396 | |
397 | const HBUINT32 &actionData = ligAction[action_idx]; |
398 | if (unlikely (!actionData.sanitize (&c->sanitizer))) return false; |
399 | action = actionData; |
400 | |
401 | uint32_t uoffset = action & LigActionOffset; |
402 | if (uoffset & 0x20000000) |
403 | uoffset += 0xC0000000; |
404 | int32_t offset = (int32_t) uoffset; |
405 | unsigned int component_idx = buffer->cur().codepoint + offset; |
406 | |
407 | const HBUINT16 &componentData = component[component_idx]; |
408 | if (unlikely (!componentData.sanitize (&c->sanitizer))) return false; |
409 | ligature_idx += componentData; |
410 | |
411 | if (action & (LigActionStore | LigActionLast)) |
412 | { |
413 | const GlyphID &ligatureData = ligature[ligature_idx]; |
414 | if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false; |
415 | hb_codepoint_t lig = ligatureData; |
416 | |
417 | match_positions[match_length++] = buffer->out_len; |
418 | buffer->replace_glyph (lig); |
419 | |
420 | //ligature_idx = 0; // XXX Yes or no? |
421 | } |
422 | else |
423 | { |
424 | buffer->skip_glyph (); |
425 | end--; |
426 | } |
427 | /* TODO merge_clusters / unsafe_to_break */ |
428 | |
429 | action_idx++; |
430 | } |
431 | while (!(action & LigActionLast)); |
432 | buffer->move_to (end); |
433 | } |
434 | |
435 | return true; |
436 | } |
437 | |
438 | public: |
439 | bool ret; |
440 | private: |
441 | hb_aat_apply_context_t *c; |
442 | const UnsizedArrayOf<HBUINT32> &ligAction; |
443 | const UnsizedArrayOf<HBUINT16> &component; |
444 | const UnsizedArrayOf<GlyphID> &ligature; |
445 | unsigned int match_length; |
446 | unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; |
447 | }; |
448 | |
449 | inline bool apply (hb_aat_apply_context_t *c) const |
450 | { |
451 | TRACE_APPLY (this); |
452 | |
453 | driver_context_t dc (this, c); |
454 | |
455 | StateTableDriver<EntryData> driver (machine, c->buffer, c->face); |
456 | driver.drive (&dc); |
457 | |
458 | return_trace (dc.ret); |
459 | } |
460 | |
461 | inline bool sanitize (hb_sanitize_context_t *c) const |
462 | { |
463 | TRACE_SANITIZE (this); |
464 | /* The rest of array sanitizations are done at run-time. */ |
465 | return_trace (c->check_struct (this) && machine.sanitize (c) && |
466 | ligAction && component && ligature); |
467 | } |
468 | |
469 | protected: |
470 | StateTable<EntryData> |
471 | machine; |
472 | LOffsetTo<UnsizedArrayOf<HBUINT32> > |
473 | ligAction; /* Offset to the ligature action table. */ |
474 | LOffsetTo<UnsizedArrayOf<HBUINT16> > |
475 | component; /* Offset to the component table. */ |
476 | LOffsetTo<UnsizedArrayOf<GlyphID> > |
477 | ligature; /* Offset to the actual ligature lists. */ |
478 | public: |
479 | DEFINE_SIZE_STATIC (28); |
480 | }; |
481 | |
482 | struct NoncontextualSubtable |
483 | { |
484 | inline bool apply (hb_aat_apply_context_t *c) const |
485 | { |
486 | TRACE_APPLY (this); |
487 | |
488 | bool ret = false; |
489 | unsigned int num_glyphs = c->face->get_num_glyphs (); |
490 | |
491 | hb_glyph_info_t *info = c->buffer->info; |
492 | unsigned int count = c->buffer->len; |
493 | for (unsigned int i = 0; i < count; i++) |
494 | { |
495 | const GlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs); |
496 | if (replacement) |
497 | { |
498 | info[i].codepoint = *replacement; |
499 | ret = true; |
500 | } |
501 | } |
502 | |
503 | return_trace (ret); |
504 | } |
505 | |
506 | inline bool sanitize (hb_sanitize_context_t *c) const |
507 | { |
508 | TRACE_SANITIZE (this); |
509 | return_trace (substitute.sanitize (c)); |
510 | } |
511 | |
512 | protected: |
513 | Lookup<GlyphID> substitute; |
514 | public: |
515 | DEFINE_SIZE_MIN (2); |
516 | }; |
517 | |
518 | struct InsertionSubtable |
519 | { |
520 | inline bool apply (hb_aat_apply_context_t *c) const |
521 | { |
522 | TRACE_APPLY (this); |
523 | /* TODO */ |
524 | return_trace (false); |
525 | } |
526 | |
527 | inline bool sanitize (hb_sanitize_context_t *c) const |
528 | { |
529 | TRACE_SANITIZE (this); |
530 | /* TODO */ |
531 | return_trace (true); |
532 | } |
533 | }; |
534 | |
535 | |
536 | struct Feature |
537 | { |
538 | inline bool sanitize (hb_sanitize_context_t *c) const |
539 | { |
540 | TRACE_SANITIZE (this); |
541 | return_trace (c->check_struct (this)); |
542 | } |
543 | |
544 | public: |
545 | HBUINT16 featureType; /* The type of feature. */ |
546 | HBUINT16 featureSetting; /* The feature's setting (aka selector). */ |
547 | HBUINT32 enableFlags; /* Flags for the settings that this feature |
548 | * and setting enables. */ |
549 | HBUINT32 disableFlags; /* Complement of flags for the settings that this |
550 | * feature and setting disable. */ |
551 | |
552 | public: |
553 | DEFINE_SIZE_STATIC (12); |
554 | }; |
555 | |
556 | |
557 | struct ChainSubtable |
558 | { |
559 | friend struct Chain; |
560 | |
561 | inline unsigned int get_size (void) const { return length; } |
562 | inline unsigned int get_type (void) const { return coverage & 0xFF; } |
563 | |
564 | enum Type { |
565 | Rearrangement = 0, |
566 | Contextual = 1, |
567 | Ligature = 2, |
568 | Noncontextual = 4, |
569 | Insertion = 5 |
570 | }; |
571 | |
572 | inline void apply (hb_aat_apply_context_t *c) const |
573 | { |
574 | dispatch (c); |
575 | } |
576 | |
577 | template <typename context_t> |
578 | inline typename context_t::return_t dispatch (context_t *c) const |
579 | { |
580 | unsigned int subtable_type = get_type (); |
581 | TRACE_DISPATCH (this, subtable_type); |
582 | switch (subtable_type) { |
583 | case Rearrangement: return_trace (c->dispatch (u.rearrangement)); |
584 | case Contextual: return_trace (c->dispatch (u.contextual)); |
585 | case Ligature: return_trace (c->dispatch (u.ligature)); |
586 | case Noncontextual: return_trace (c->dispatch (u.noncontextual)); |
587 | case Insertion: return_trace (c->dispatch (u.insertion)); |
588 | default: return_trace (c->default_return_value ()); |
589 | } |
590 | } |
591 | |
592 | inline bool sanitize (hb_sanitize_context_t *c) const |
593 | { |
594 | TRACE_SANITIZE (this); |
595 | if (!length.sanitize (c) || |
596 | length < min_size || |
597 | !c->check_range (this, length)) |
598 | return_trace (false); |
599 | |
600 | return_trace (dispatch (c)); |
601 | } |
602 | |
603 | protected: |
604 | HBUINT32 length; /* Total subtable length, including this header. */ |
605 | HBUINT32 coverage; /* Coverage flags and subtable type. */ |
606 | HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */ |
607 | union { |
608 | RearrangementSubtable rearrangement; |
609 | ContextualSubtable contextual; |
610 | LigatureSubtable ligature; |
611 | NoncontextualSubtable noncontextual; |
612 | InsertionSubtable insertion; |
613 | } u; |
614 | public: |
615 | DEFINE_SIZE_MIN (12); |
616 | }; |
617 | |
618 | struct Chain |
619 | { |
620 | inline void apply (hb_aat_apply_context_t *c) const |
621 | { |
622 | const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount); |
623 | unsigned int count = subtableCount; |
624 | for (unsigned int i = 0; i < count; i++) |
625 | { |
626 | if (!c->buffer->message (c->font, "start chain subtable %d" , c->lookup_index)) |
627 | { |
628 | c->set_lookup_index (c->lookup_index + 1); |
629 | continue; |
630 | } |
631 | |
632 | subtable->apply (c); |
633 | subtable = &StructAfter<ChainSubtable> (*subtable); |
634 | |
635 | (void) c->buffer->message (c->font, "end chain subtable %d" , c->lookup_index); |
636 | |
637 | c->set_lookup_index (c->lookup_index + 1); |
638 | } |
639 | } |
640 | |
641 | inline unsigned int get_size (void) const { return length; } |
642 | |
643 | inline bool sanitize (hb_sanitize_context_t *c, unsigned int major) const |
644 | { |
645 | TRACE_SANITIZE (this); |
646 | if (!length.sanitize (c) || |
647 | length < min_size || |
648 | !c->check_range (this, length)) |
649 | return_trace (false); |
650 | |
651 | if (!c->check_array (featureZ, featureZ[0].static_size, featureCount)) |
652 | return_trace (false); |
653 | |
654 | const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount); |
655 | unsigned int count = subtableCount; |
656 | for (unsigned int i = 0; i < count; i++) |
657 | { |
658 | if (!subtable->sanitize (c)) |
659 | return_trace (false); |
660 | subtable = &StructAfter<ChainSubtable> (*subtable); |
661 | } |
662 | |
663 | return_trace (true); |
664 | } |
665 | |
666 | protected: |
667 | HBUINT32 defaultFlags; /* The default specification for subtables. */ |
668 | HBUINT32 length; /* Total byte count, including this header. */ |
669 | HBUINT32 featureCount; /* Number of feature subtable entries. */ |
670 | HBUINT32 subtableCount; /* The number of subtables in the chain. */ |
671 | |
672 | Feature featureZ[VAR]; /* Features. */ |
673 | /*ChainSubtable subtableX[VAR];*//* Subtables. */ |
674 | /*subtableGlyphCoverageArray*/ /* Only if major == 3. */ |
675 | |
676 | public: |
677 | DEFINE_SIZE_MIN (16); |
678 | }; |
679 | |
680 | |
681 | /* |
682 | * The 'mort'/'morx' Tables |
683 | */ |
684 | |
685 | struct morx |
686 | { |
687 | static const hb_tag_t tableTag = HB_AAT_TAG_morx; |
688 | |
689 | inline void apply (hb_aat_apply_context_t *c) const |
690 | { |
691 | c->set_lookup_index (0); |
692 | const Chain *chain = chainsZ; |
693 | unsigned int count = chainCount; |
694 | for (unsigned int i = 0; i < count; i++) |
695 | { |
696 | chain->apply (c); |
697 | chain = &StructAfter<Chain> (*chain); |
698 | } |
699 | } |
700 | |
701 | inline bool sanitize (hb_sanitize_context_t *c) const |
702 | { |
703 | TRACE_SANITIZE (this); |
704 | if (!version.sanitize (c) || |
705 | (version.major >> (sizeof (HBUINT32) == 4 ? 1 : 0)) != 1 || |
706 | !chainCount.sanitize (c)) |
707 | return_trace (false); |
708 | |
709 | const Chain *chain = chainsZ; |
710 | unsigned int count = chainCount; |
711 | for (unsigned int i = 0; i < count; i++) |
712 | { |
713 | if (!chain->sanitize (c, version.major)) |
714 | return_trace (false); |
715 | chain = &StructAfter<Chain> (*chain); |
716 | } |
717 | |
718 | return_trace (true); |
719 | } |
720 | |
721 | protected: |
722 | FixedVersion<>version; /* Version number of the glyph metamorphosis table. |
723 | * 1 for mort, 2 or 3 for morx. */ |
724 | HBUINT32 chainCount; /* Number of metamorphosis chains contained in this |
725 | * table. */ |
726 | Chain chainsZ[VAR]; /* Chains. */ |
727 | |
728 | public: |
729 | DEFINE_SIZE_MIN (8); |
730 | }; |
731 | |
732 | } /* namespace AAT */ |
733 | |
734 | |
735 | #endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */ |
736 | |