1/*
2 * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
3 * Copyright © 2010,2012,2013 Google, Inc.
4 *
5 * This is part of HarfBuzz, a text shaping library.
6 *
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
12 *
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 * DAMAGE.
18 *
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 *
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
27 */
28
29#ifndef HB_OT_LAYOUT_GSUB_TABLE_HH
30#define HB_OT_LAYOUT_GSUB_TABLE_HH
31
32#include "hb-ot-layout-gsubgpos.hh"
33
34
35namespace OT {
36
37
38static inline void SingleSubst_serialize (hb_serialize_context_t *c,
39 hb_array_t<const GlyphID> glyphs,
40 hb_array_t<const GlyphID> substitutes);
41
42struct SingleSubstFormat1
43{
44 bool intersects (const hb_set_t *glyphs) const
45 { return (this+coverage).intersects (glyphs); }
46
47 void closure (hb_closure_context_t *c) const
48 {
49 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
50 {
51 /* TODO Switch to range-based API to work around malicious fonts.
52 * https://github.com/harfbuzz/harfbuzz/issues/363 */
53 hb_codepoint_t glyph_id = iter.get_glyph ();
54 if (c->glyphs->has (glyph_id))
55 c->out->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
56 }
57 }
58
59 void collect_glyphs (hb_collect_glyphs_context_t *c) const
60 {
61 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
62 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
63 {
64 /* TODO Switch to range-based API to work around malicious fonts.
65 * https://github.com/harfbuzz/harfbuzz/issues/363 */
66 hb_codepoint_t glyph_id = iter.get_glyph ();
67 c->output->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
68 }
69 }
70
71 const Coverage &get_coverage () const { return this+coverage; }
72
73 bool would_apply (hb_would_apply_context_t *c) const
74 {
75 TRACE_WOULD_APPLY (this);
76 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
77 }
78
79 bool apply (hb_ot_apply_context_t *c) const
80 {
81 TRACE_APPLY (this);
82 hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
83 unsigned int index = (this+coverage).get_coverage (glyph_id);
84 if (likely (index == NOT_COVERED)) return_trace (false);
85
86 /* According to the Adobe Annotated OpenType Suite, result is always
87 * limited to 16bit. */
88 glyph_id = (glyph_id + deltaGlyphID) & 0xFFFFu;
89 c->replace_glyph (glyph_id);
90
91 return_trace (true);
92 }
93
94 bool serialize (hb_serialize_context_t *c,
95 hb_array_t<const GlyphID> glyphs,
96 int delta)
97 {
98 TRACE_SERIALIZE (this);
99 if (unlikely (!c->extend_min (*this))) return_trace (false);
100 if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
101 deltaGlyphID.set (delta); /* TODO(serialize) overflow? */
102 return_trace (true);
103 }
104
105 bool subset (hb_subset_context_t *c) const
106 {
107 TRACE_SUBSET (this);
108 const hb_set_t &glyphset = *c->plan->glyphset;
109 const hb_map_t &glyph_map = *c->plan->glyph_map;
110 hb_vector_t<GlyphID> from;
111 hb_vector_t<GlyphID> to;
112 hb_codepoint_t delta = deltaGlyphID;
113 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
114 {
115 if (!glyphset.has (iter.get_glyph ())) continue;
116 from.push ()->set (glyph_map[iter.get_glyph ()]);
117 to.push ()->set (glyph_map[(iter.get_glyph () + delta) & 0xFFFF]);
118 }
119 c->serializer->propagate_error (from, to);
120 SingleSubst_serialize (c->serializer, from, to);
121 return_trace (from.length);
122 }
123
124 bool sanitize (hb_sanitize_context_t *c) const
125 {
126 TRACE_SANITIZE (this);
127 return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
128 }
129
130 protected:
131 HBUINT16 format; /* Format identifier--format = 1 */
132 OffsetTo<Coverage>
133 coverage; /* Offset to Coverage table--from
134 * beginning of Substitution table */
135 HBINT16 deltaGlyphID; /* Add to original GlyphID to get
136 * substitute GlyphID */
137 public:
138 DEFINE_SIZE_STATIC (6);
139};
140
141struct SingleSubstFormat2
142{
143 bool intersects (const hb_set_t *glyphs) const
144 { return (this+coverage).intersects (glyphs); }
145
146 void closure (hb_closure_context_t *c) const
147 {
148 unsigned int count = substitute.len;
149 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
150 {
151 if (unlikely (iter.get_coverage () >= count))
152 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
153 if (c->glyphs->has (iter.get_glyph ()))
154 c->out->add (substitute[iter.get_coverage ()]);
155 }
156 }
157
158 void collect_glyphs (hb_collect_glyphs_context_t *c) const
159 {
160 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
161 unsigned int count = substitute.len;
162 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
163 {
164 if (unlikely (iter.get_coverage () >= count))
165 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
166 c->output->add (substitute[iter.get_coverage ()]);
167 }
168 }
169
170 const Coverage &get_coverage () const { return this+coverage; }
171
172 bool would_apply (hb_would_apply_context_t *c) const
173 {
174 TRACE_WOULD_APPLY (this);
175 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
176 }
177
178 bool apply (hb_ot_apply_context_t *c) const
179 {
180 TRACE_APPLY (this);
181 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
182 if (likely (index == NOT_COVERED)) return_trace (false);
183
184 if (unlikely (index >= substitute.len)) return_trace (false);
185
186 c->replace_glyph (substitute[index]);
187
188 return_trace (true);
189 }
190
191 bool serialize (hb_serialize_context_t *c,
192 hb_array_t<const GlyphID> glyphs,
193 hb_array_t<const GlyphID> substitutes)
194 {
195 TRACE_SERIALIZE (this);
196 if (unlikely (!c->extend_min (*this))) return_trace (false);
197 if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false);
198 if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
199 return_trace (true);
200 }
201
202 bool subset (hb_subset_context_t *c) const
203 {
204 TRACE_SUBSET (this);
205 const hb_set_t &glyphset = *c->plan->glyphset;
206 const hb_map_t &glyph_map = *c->plan->glyph_map;
207 hb_vector_t<GlyphID> from;
208 hb_vector_t<GlyphID> to;
209 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
210 {
211 if (!glyphset.has (iter.get_glyph ())) continue;
212 from.push ()->set (glyph_map[iter.get_glyph ()]);
213 to.push ()->set (glyph_map[substitute[iter.get_coverage ()]]);
214 }
215 c->serializer->propagate_error (from, to);
216 SingleSubst_serialize (c->serializer, from, to);
217 return_trace (from.length);
218 }
219
220 bool sanitize (hb_sanitize_context_t *c) const
221 {
222 TRACE_SANITIZE (this);
223 return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
224 }
225
226 protected:
227 HBUINT16 format; /* Format identifier--format = 2 */
228 OffsetTo<Coverage>
229 coverage; /* Offset to Coverage table--from
230 * beginning of Substitution table */
231 ArrayOf<GlyphID>
232 substitute; /* Array of substitute
233 * GlyphIDs--ordered by Coverage Index */
234 public:
235 DEFINE_SIZE_ARRAY (6, substitute);
236};
237
238struct SingleSubst
239{
240 bool serialize (hb_serialize_context_t *c,
241 hb_array_t<const GlyphID> glyphs,
242 hb_array_t<const GlyphID> substitutes)
243 {
244 TRACE_SERIALIZE (this);
245 if (unlikely (!c->extend_min (u.format))) return_trace (false);
246 unsigned int format = 2;
247 int delta = 0;
248 if (glyphs.length)
249 {
250 format = 1;
251 /* TODO(serialize) check for wrap-around */
252 delta = substitutes[0] - glyphs[0];
253 for (unsigned int i = 1; i < glyphs.length; i++)
254 if (delta != (int) (substitutes[i] - glyphs[i])) {
255 format = 2;
256 break;
257 }
258 }
259 u.format.set (format);
260 switch (u.format) {
261 case 1: return_trace (u.format1.serialize (c, glyphs, delta));
262 case 2: return_trace (u.format2.serialize (c, glyphs, substitutes));
263 default:return_trace (false);
264 }
265 }
266
267 template <typename context_t>
268 typename context_t::return_t dispatch (context_t *c) const
269 {
270 TRACE_DISPATCH (this, u.format);
271 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
272 switch (u.format) {
273 case 1: return_trace (c->dispatch (u.format1));
274 case 2: return_trace (c->dispatch (u.format2));
275 default:return_trace (c->default_return_value ());
276 }
277 }
278
279 protected:
280 union {
281 HBUINT16 format; /* Format identifier */
282 SingleSubstFormat1 format1;
283 SingleSubstFormat2 format2;
284 } u;
285};
286
287static inline void
288SingleSubst_serialize (hb_serialize_context_t *c,
289 hb_array_t<const GlyphID> glyphs,
290 hb_array_t<const GlyphID> substitutes)
291{ c->start_embed<SingleSubst> ()->serialize (c, glyphs, substitutes); }
292
293struct Sequence
294{
295 void closure (hb_closure_context_t *c) const
296 {
297 unsigned int count = substitute.len;
298 for (unsigned int i = 0; i < count; i++)
299 c->out->add (substitute[i]);
300 }
301
302 void collect_glyphs (hb_collect_glyphs_context_t *c) const
303 { c->output->add_array (substitute.arrayZ, substitute.len); }
304
305 bool apply (hb_ot_apply_context_t *c) const
306 {
307 TRACE_APPLY (this);
308 unsigned int count = substitute.len;
309
310 /* Special-case to make it in-place and not consider this
311 * as a "multiplied" substitution. */
312 if (unlikely (count == 1))
313 {
314 c->replace_glyph (substitute.arrayZ[0]);
315 return_trace (true);
316 }
317 /* Spec disallows this, but Uniscribe allows it.
318 * https://github.com/harfbuzz/harfbuzz/issues/253 */
319 else if (unlikely (count == 0))
320 {
321 c->buffer->delete_glyph ();
322 return_trace (true);
323 }
324
325 unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
326 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
327
328 for (unsigned int i = 0; i < count; i++) {
329 _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i);
330 c->output_glyph_for_component (substitute.arrayZ[i], klass);
331 }
332 c->buffer->skip_glyph ();
333
334 return_trace (true);
335 }
336
337 bool serialize (hb_serialize_context_t *c,
338 hb_array_t<const GlyphID> glyphs)
339 {
340 TRACE_SERIALIZE (this);
341 return_trace (substitute.serialize (c, glyphs));
342 }
343
344 bool sanitize (hb_sanitize_context_t *c) const
345 {
346 TRACE_SANITIZE (this);
347 return_trace (substitute.sanitize (c));
348 }
349
350 protected:
351 ArrayOf<GlyphID>
352 substitute; /* String of GlyphIDs to substitute */
353 public:
354 DEFINE_SIZE_ARRAY (2, substitute);
355};
356
357struct MultipleSubstFormat1
358{
359 bool intersects (const hb_set_t *glyphs) const
360 { return (this+coverage).intersects (glyphs); }
361
362 void closure (hb_closure_context_t *c) const
363 {
364 unsigned int count = sequence.len;
365 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
366 {
367 if (unlikely (iter.get_coverage () >= count))
368 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
369 if (c->glyphs->has (iter.get_glyph ()))
370 (this+sequence[iter.get_coverage ()]).closure (c);
371 }
372 }
373
374 void collect_glyphs (hb_collect_glyphs_context_t *c) const
375 {
376 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
377 unsigned int count = sequence.len;
378 for (unsigned int i = 0; i < count; i++)
379 (this+sequence[i]).collect_glyphs (c);
380 }
381
382 const Coverage &get_coverage () const { return this+coverage; }
383
384 bool would_apply (hb_would_apply_context_t *c) const
385 {
386 TRACE_WOULD_APPLY (this);
387 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
388 }
389
390 bool apply (hb_ot_apply_context_t *c) const
391 {
392 TRACE_APPLY (this);
393
394 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
395 if (likely (index == NOT_COVERED)) return_trace (false);
396
397 return_trace ((this+sequence[index]).apply (c));
398 }
399
400 bool serialize (hb_serialize_context_t *c,
401 hb_array_t<const GlyphID> glyphs,
402 hb_array_t<const unsigned int> substitute_len_list,
403 hb_array_t<const GlyphID> substitute_glyphs_list)
404 {
405 TRACE_SERIALIZE (this);
406 if (unlikely (!c->extend_min (*this))) return_trace (false);
407 if (unlikely (!sequence.serialize (c, glyphs.length))) return_trace (false);
408 for (unsigned int i = 0; i < glyphs.length; i++)
409 {
410 unsigned int substitute_len = substitute_len_list[i];
411 if (unlikely (!sequence[i].serialize (c, this)
412 .serialize (c, substitute_glyphs_list.sub_array (0, substitute_len))))
413 return_trace (false);
414 substitute_glyphs_list += substitute_len;
415 }
416 return_trace (coverage.serialize (c, this).serialize (c, glyphs));
417 }
418
419 bool subset (hb_subset_context_t *c) const
420 {
421 TRACE_SUBSET (this);
422 // TODO(subset)
423 return_trace (false);
424 }
425
426 bool sanitize (hb_sanitize_context_t *c) const
427 {
428 TRACE_SANITIZE (this);
429 return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
430 }
431
432 protected:
433 HBUINT16 format; /* Format identifier--format = 1 */
434 OffsetTo<Coverage>
435 coverage; /* Offset to Coverage table--from
436 * beginning of Substitution table */
437 OffsetArrayOf<Sequence>
438 sequence; /* Array of Sequence tables
439 * ordered by Coverage Index */
440 public:
441 DEFINE_SIZE_ARRAY (6, sequence);
442};
443
444struct MultipleSubst
445{
446 bool serialize (hb_serialize_context_t *c,
447 hb_array_t<const GlyphID> glyphs,
448 hb_array_t<const unsigned int> substitute_len_list,
449 hb_array_t<const GlyphID> substitute_glyphs_list)
450 {
451 TRACE_SERIALIZE (this);
452 if (unlikely (!c->extend_min (u.format))) return_trace (false);
453 unsigned int format = 1;
454 u.format.set (format);
455 switch (u.format) {
456 case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list));
457 default:return_trace (false);
458 }
459 }
460
461 template <typename context_t>
462 typename context_t::return_t dispatch (context_t *c) const
463 {
464 TRACE_DISPATCH (this, u.format);
465 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
466 switch (u.format) {
467 case 1: return_trace (c->dispatch (u.format1));
468 default:return_trace (c->default_return_value ());
469 }
470 }
471
472 protected:
473 union {
474 HBUINT16 format; /* Format identifier */
475 MultipleSubstFormat1 format1;
476 } u;
477};
478
479struct AlternateSet
480{
481 void closure (hb_closure_context_t *c) const
482 {
483 unsigned int count = alternates.len;
484 for (unsigned int i = 0; i < count; i++)
485 c->out->add (alternates[i]);
486 }
487
488 void collect_glyphs (hb_collect_glyphs_context_t *c) const
489 { c->output->add_array (alternates.arrayZ, alternates.len); }
490
491 bool apply (hb_ot_apply_context_t *c) const
492 {
493 TRACE_APPLY (this);
494 unsigned int count = alternates.len;
495
496 if (unlikely (!count)) return_trace (false);
497
498 hb_mask_t glyph_mask = c->buffer->cur().mask;
499 hb_mask_t lookup_mask = c->lookup_mask;
500
501 /* Note: This breaks badly if two features enabled this lookup together. */
502 unsigned int shift = hb_ctz (lookup_mask);
503 unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
504
505 /* If alt_index is MAX, randomize feature if it is the rand feature. */
506 if (alt_index == HB_OT_MAP_MAX_VALUE && c->random)
507 alt_index = c->random_number () % count + 1;
508
509 if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
510
511 c->replace_glyph (alternates[alt_index - 1]);
512
513 return_trace (true);
514 }
515
516 bool serialize (hb_serialize_context_t *c,
517 hb_array_t<const GlyphID> glyphs)
518 {
519 TRACE_SERIALIZE (this);
520 return_trace (alternates.serialize (c, glyphs));
521 }
522
523 bool sanitize (hb_sanitize_context_t *c) const
524 {
525 TRACE_SANITIZE (this);
526 return_trace (alternates.sanitize (c));
527 }
528
529 protected:
530 ArrayOf<GlyphID>
531 alternates; /* Array of alternate GlyphIDs--in
532 * arbitrary order */
533 public:
534 DEFINE_SIZE_ARRAY (2, alternates);
535};
536
537struct AlternateSubstFormat1
538{
539 bool intersects (const hb_set_t *glyphs) const
540 { return (this+coverage).intersects (glyphs); }
541
542 void closure (hb_closure_context_t *c) const
543 {
544 unsigned int count = alternateSet.len;
545 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
546 {
547 if (unlikely (iter.get_coverage () >= count))
548 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
549 if (c->glyphs->has (iter.get_glyph ()))
550 (this+alternateSet[iter.get_coverage ()]).closure (c);
551 }
552 }
553
554 void collect_glyphs (hb_collect_glyphs_context_t *c) const
555 {
556 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
557 unsigned int count = alternateSet.len;
558 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
559 {
560 if (unlikely (iter.get_coverage () >= count))
561 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
562 (this+alternateSet[iter.get_coverage ()]).collect_glyphs (c);
563 }
564 }
565
566 const Coverage &get_coverage () const { return this+coverage; }
567
568 bool would_apply (hb_would_apply_context_t *c) const
569 {
570 TRACE_WOULD_APPLY (this);
571 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
572 }
573
574 bool apply (hb_ot_apply_context_t *c) const
575 {
576 TRACE_APPLY (this);
577
578 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
579 if (likely (index == NOT_COVERED)) return_trace (false);
580
581 return_trace ((this+alternateSet[index]).apply (c));
582 }
583
584 bool serialize (hb_serialize_context_t *c,
585 hb_array_t<const GlyphID> glyphs,
586 hb_array_t<const unsigned int> alternate_len_list,
587 hb_array_t<const GlyphID> alternate_glyphs_list)
588 {
589 TRACE_SERIALIZE (this);
590 if (unlikely (!c->extend_min (*this))) return_trace (false);
591 if (unlikely (!alternateSet.serialize (c, glyphs.length))) return_trace (false);
592 for (unsigned int i = 0; i < glyphs.length; i++)
593 {
594 unsigned int alternate_len = alternate_len_list[i];
595 if (unlikely (!alternateSet[i].serialize (c, this)
596 .serialize (c, alternate_glyphs_list.sub_array (0, alternate_len))))
597 return_trace (false);
598 alternate_glyphs_list += alternate_len;
599 }
600 return_trace (coverage.serialize (c, this).serialize (c, glyphs));
601 }
602
603 bool subset (hb_subset_context_t *c) const
604 {
605 TRACE_SUBSET (this);
606 // TODO(subset)
607 return_trace (false);
608 }
609
610 bool sanitize (hb_sanitize_context_t *c) const
611 {
612 TRACE_SANITIZE (this);
613 return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
614 }
615
616 protected:
617 HBUINT16 format; /* Format identifier--format = 1 */
618 OffsetTo<Coverage>
619 coverage; /* Offset to Coverage table--from
620 * beginning of Substitution table */
621 OffsetArrayOf<AlternateSet>
622 alternateSet; /* Array of AlternateSet tables
623 * ordered by Coverage Index */
624 public:
625 DEFINE_SIZE_ARRAY (6, alternateSet);
626};
627
628struct AlternateSubst
629{
630 bool serialize (hb_serialize_context_t *c,
631 hb_array_t<const GlyphID> glyphs,
632 hb_array_t<const unsigned int> alternate_len_list,
633 hb_array_t<const GlyphID> alternate_glyphs_list)
634 {
635 TRACE_SERIALIZE (this);
636 if (unlikely (!c->extend_min (u.format))) return_trace (false);
637 unsigned int format = 1;
638 u.format.set (format);
639 switch (u.format) {
640 case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list));
641 default:return_trace (false);
642 }
643 }
644
645 template <typename context_t>
646 typename context_t::return_t dispatch (context_t *c) const
647 {
648 TRACE_DISPATCH (this, u.format);
649 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
650 switch (u.format) {
651 case 1: return_trace (c->dispatch (u.format1));
652 default:return_trace (c->default_return_value ());
653 }
654 }
655
656 protected:
657 union {
658 HBUINT16 format; /* Format identifier */
659 AlternateSubstFormat1 format1;
660 } u;
661};
662
663
664struct Ligature
665{
666 bool intersects (const hb_set_t *glyphs) const
667 {
668 unsigned int count = component.lenP1;
669 for (unsigned int i = 1; i < count; i++)
670 if (!glyphs->has (component[i]))
671 return false;
672 return true;
673 }
674
675 void closure (hb_closure_context_t *c) const
676 {
677 unsigned int count = component.lenP1;
678 for (unsigned int i = 1; i < count; i++)
679 if (!c->glyphs->has (component[i]))
680 return;
681 c->out->add (ligGlyph);
682 }
683
684 void collect_glyphs (hb_collect_glyphs_context_t *c) const
685 {
686 c->input->add_array (component.arrayZ, component.lenP1 ? component.lenP1 - 1 : 0);
687 c->output->add (ligGlyph);
688 }
689
690 bool would_apply (hb_would_apply_context_t *c) const
691 {
692 TRACE_WOULD_APPLY (this);
693 if (c->len != component.lenP1)
694 return_trace (false);
695
696 for (unsigned int i = 1; i < c->len; i++)
697 if (likely (c->glyphs[i] != component[i]))
698 return_trace (false);
699
700 return_trace (true);
701 }
702
703 bool apply (hb_ot_apply_context_t *c) const
704 {
705 TRACE_APPLY (this);
706 unsigned int count = component.lenP1;
707
708 if (unlikely (!count)) return_trace (false);
709
710 /* Special-case to make it in-place and not consider this
711 * as a "ligated" substitution. */
712 if (unlikely (count == 1))
713 {
714 c->replace_glyph (ligGlyph);
715 return_trace (true);
716 }
717
718 unsigned int total_component_count = 0;
719
720 unsigned int match_length = 0;
721 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
722
723 if (likely (!match_input (c, count,
724 &component[1],
725 match_glyph,
726 nullptr,
727 &match_length,
728 match_positions,
729 &total_component_count)))
730 return_trace (false);
731
732 ligate_input (c,
733 count,
734 match_positions,
735 match_length,
736 ligGlyph,
737 total_component_count);
738
739 return_trace (true);
740 }
741
742 bool serialize (hb_serialize_context_t *c,
743 GlyphID ligature,
744 hb_array_t<const GlyphID> components /* Starting from second */)
745 {
746 TRACE_SERIALIZE (this);
747 if (unlikely (!c->extend_min (*this))) return_trace (false);
748 ligGlyph = ligature;
749 if (unlikely (!component.serialize (c, components))) return_trace (false);
750 return_trace (true);
751 }
752
753 public:
754 bool sanitize (hb_sanitize_context_t *c) const
755 {
756 TRACE_SANITIZE (this);
757 return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
758 }
759
760 protected:
761 GlyphID ligGlyph; /* GlyphID of ligature to substitute */
762 HeadlessArrayOf<GlyphID>
763 component; /* Array of component GlyphIDs--start
764 * with the second component--ordered
765 * in writing direction */
766 public:
767 DEFINE_SIZE_ARRAY (4, component);
768};
769
770struct LigatureSet
771{
772 bool intersects (const hb_set_t *glyphs) const
773 {
774 unsigned int num_ligs = ligature.len;
775 for (unsigned int i = 0; i < num_ligs; i++)
776 if ((this+ligature[i]).intersects (glyphs))
777 return true;
778 return false;
779 }
780
781 void closure (hb_closure_context_t *c) const
782 {
783 unsigned int num_ligs = ligature.len;
784 for (unsigned int i = 0; i < num_ligs; i++)
785 (this+ligature[i]).closure (c);
786 }
787
788 void collect_glyphs (hb_collect_glyphs_context_t *c) const
789 {
790 unsigned int num_ligs = ligature.len;
791 for (unsigned int i = 0; i < num_ligs; i++)
792 (this+ligature[i]).collect_glyphs (c);
793 }
794
795 bool would_apply (hb_would_apply_context_t *c) const
796 {
797 TRACE_WOULD_APPLY (this);
798 unsigned int num_ligs = ligature.len;
799 for (unsigned int i = 0; i < num_ligs; i++)
800 {
801 const Ligature &lig = this+ligature[i];
802 if (lig.would_apply (c))
803 return_trace (true);
804 }
805 return_trace (false);
806 }
807
808 bool apply (hb_ot_apply_context_t *c) const
809 {
810 TRACE_APPLY (this);
811 unsigned int num_ligs = ligature.len;
812 for (unsigned int i = 0; i < num_ligs; i++)
813 {
814 const Ligature &lig = this+ligature[i];
815 if (lig.apply (c)) return_trace (true);
816 }
817
818 return_trace (false);
819 }
820
821 bool serialize (hb_serialize_context_t *c,
822 hb_array_t<const GlyphID> ligatures,
823 hb_array_t<const unsigned int> component_count_list,
824 hb_array_t<const GlyphID> &component_list /* Starting from second for each ligature */)
825 {
826 TRACE_SERIALIZE (this);
827 if (unlikely (!c->extend_min (*this))) return_trace (false);
828 if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false);
829 for (unsigned int i = 0; i < ligatures.length; i++)
830 {
831 unsigned int component_count = MAX<int> (component_count_list[i] - 1, 0);
832 if (unlikely (!ligature[i].serialize (c, this)
833 .serialize (c,
834 ligatures[i],
835 component_list.sub_array (0, component_count))))
836 return_trace (false);
837 component_list += component_count;
838 }
839 return_trace (true);
840 }
841
842 bool sanitize (hb_sanitize_context_t *c) const
843 {
844 TRACE_SANITIZE (this);
845 return_trace (ligature.sanitize (c, this));
846 }
847
848 protected:
849 OffsetArrayOf<Ligature>
850 ligature; /* Array LigatureSet tables
851 * ordered by preference */
852 public:
853 DEFINE_SIZE_ARRAY (2, ligature);
854};
855
856struct LigatureSubstFormat1
857{
858 bool intersects (const hb_set_t *glyphs) const
859 {
860 unsigned int count = ligatureSet.len;
861 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
862 {
863 if (unlikely (iter.get_coverage () >= count))
864 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
865 if (glyphs->has (iter.get_glyph ()) &&
866 (this+ligatureSet[iter.get_coverage ()]).intersects (glyphs))
867 return true;
868 }
869 return false;
870 }
871
872 void closure (hb_closure_context_t *c) const
873 {
874 unsigned int count = ligatureSet.len;
875 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
876 {
877 if (unlikely (iter.get_coverage () >= count))
878 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
879 if (c->glyphs->has (iter.get_glyph ()))
880 (this+ligatureSet[iter.get_coverage ()]).closure (c);
881 }
882 }
883
884 void collect_glyphs (hb_collect_glyphs_context_t *c) const
885 {
886 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
887 unsigned int count = ligatureSet.len;
888 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
889 {
890 if (unlikely (iter.get_coverage () >= count))
891 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
892 (this+ligatureSet[iter.get_coverage ()]).collect_glyphs (c);
893 }
894 }
895
896 const Coverage &get_coverage () const { return this+coverage; }
897
898 bool would_apply (hb_would_apply_context_t *c) const
899 {
900 TRACE_WOULD_APPLY (this);
901 unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
902 if (likely (index == NOT_COVERED)) return_trace (false);
903
904 const LigatureSet &lig_set = this+ligatureSet[index];
905 return_trace (lig_set.would_apply (c));
906 }
907
908 bool apply (hb_ot_apply_context_t *c) const
909 {
910 TRACE_APPLY (this);
911
912 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
913 if (likely (index == NOT_COVERED)) return_trace (false);
914
915 const LigatureSet &lig_set = this+ligatureSet[index];
916 return_trace (lig_set.apply (c));
917 }
918
919 bool serialize (hb_serialize_context_t *c,
920 hb_array_t<const GlyphID> first_glyphs,
921 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
922 hb_array_t<const GlyphID> ligatures_list,
923 hb_array_t<const unsigned int> component_count_list,
924 hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
925 {
926 TRACE_SERIALIZE (this);
927 if (unlikely (!c->extend_min (*this))) return_trace (false);
928 if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false);
929 for (unsigned int i = 0; i < first_glyphs.length; i++)
930 {
931 unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
932 if (unlikely (!ligatureSet[i].serialize (c, this)
933 .serialize (c,
934 ligatures_list.sub_array (0, ligature_count),
935 component_count_list.sub_array (0, ligature_count),
936 component_list))) return_trace (false);
937 ligatures_list += ligature_count;
938 component_count_list += ligature_count;
939 }
940 return_trace (coverage.serialize (c, this).serialize (c, first_glyphs));
941 }
942
943 bool subset (hb_subset_context_t *c) const
944 {
945 TRACE_SUBSET (this);
946 // TODO(subset)
947 return_trace (false);
948 }
949
950 bool sanitize (hb_sanitize_context_t *c) const
951 {
952 TRACE_SANITIZE (this);
953 return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
954 }
955
956 protected:
957 HBUINT16 format; /* Format identifier--format = 1 */
958 OffsetTo<Coverage>
959 coverage; /* Offset to Coverage table--from
960 * beginning of Substitution table */
961 OffsetArrayOf<LigatureSet>
962 ligatureSet; /* Array LigatureSet tables
963 * ordered by Coverage Index */
964 public:
965 DEFINE_SIZE_ARRAY (6, ligatureSet);
966};
967
968struct LigatureSubst
969{
970 bool serialize (hb_serialize_context_t *c,
971 hb_array_t<const GlyphID> first_glyphs,
972 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
973 hb_array_t<const GlyphID> ligatures_list,
974 hb_array_t<const unsigned int> component_count_list,
975 hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
976 {
977 TRACE_SERIALIZE (this);
978 if (unlikely (!c->extend_min (u.format))) return_trace (false);
979 unsigned int format = 1;
980 u.format.set (format);
981 switch (u.format) {
982 case 1: return_trace (u.format1.serialize (c,
983 first_glyphs,
984 ligature_per_first_glyph_count_list,
985 ligatures_list,
986 component_count_list,
987 component_list));
988 default:return_trace (false);
989 }
990 }
991
992 template <typename context_t>
993 typename context_t::return_t dispatch (context_t *c) const
994 {
995 TRACE_DISPATCH (this, u.format);
996 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
997 switch (u.format) {
998 case 1: return_trace (c->dispatch (u.format1));
999 default:return_trace (c->default_return_value ());
1000 }
1001 }
1002
1003 protected:
1004 union {
1005 HBUINT16 format; /* Format identifier */
1006 LigatureSubstFormat1 format1;
1007 } u;
1008};
1009
1010
1011struct ContextSubst : Context {};
1012
1013struct ChainContextSubst : ChainContext {};
1014
1015struct ExtensionSubst : Extension<ExtensionSubst>
1016{
1017 typedef struct SubstLookupSubTable SubTable;
1018
1019 bool is_reverse () const;
1020};
1021
1022
1023struct ReverseChainSingleSubstFormat1
1024{
1025 bool intersects (const hb_set_t *glyphs) const
1026 {
1027 if (!(this+coverage).intersects (glyphs))
1028 return false;
1029
1030 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1031
1032 unsigned int count;
1033
1034 count = backtrack.len;
1035 for (unsigned int i = 0; i < count; i++)
1036 if (!(this+backtrack[i]).intersects (glyphs))
1037 return false;
1038
1039 count = lookahead.len;
1040 for (unsigned int i = 0; i < count; i++)
1041 if (!(this+lookahead[i]).intersects (glyphs))
1042 return false;
1043
1044 return true;
1045 }
1046
1047 void closure (hb_closure_context_t *c) const
1048 {
1049 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1050
1051 unsigned int count;
1052
1053 count = backtrack.len;
1054 for (unsigned int i = 0; i < count; i++)
1055 if (!(this+backtrack[i]).intersects (c->glyphs))
1056 return;
1057
1058 count = lookahead.len;
1059 for (unsigned int i = 0; i < count; i++)
1060 if (!(this+lookahead[i]).intersects (c->glyphs))
1061 return;
1062
1063 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
1064 count = substitute.len;
1065 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
1066 {
1067 if (unlikely (iter.get_coverage () >= count))
1068 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
1069 if (c->glyphs->has (iter.get_glyph ()))
1070 c->out->add (substitute[iter.get_coverage ()]);
1071 }
1072 }
1073
1074 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1075 {
1076 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
1077
1078 unsigned int count;
1079
1080 count = backtrack.len;
1081 for (unsigned int i = 0; i < count; i++)
1082 if (unlikely (!(this+backtrack[i]).add_coverage (c->before))) return;
1083
1084 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1085 count = lookahead.len;
1086 for (unsigned int i = 0; i < count; i++)
1087 if (unlikely (!(this+lookahead[i]).add_coverage (c->after))) return;
1088
1089 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
1090 count = substitute.len;
1091 c->output->add_array (substitute.arrayZ, substitute.len);
1092 }
1093
1094 const Coverage &get_coverage () const { return this+coverage; }
1095
1096 bool would_apply (hb_would_apply_context_t *c) const
1097 {
1098 TRACE_WOULD_APPLY (this);
1099 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
1100 }
1101
1102 bool apply (hb_ot_apply_context_t *c) const
1103 {
1104 TRACE_APPLY (this);
1105 if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
1106 return_trace (false); /* No chaining to this type */
1107
1108 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
1109 if (likely (index == NOT_COVERED)) return_trace (false);
1110
1111 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1112 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
1113
1114 unsigned int start_index = 0, end_index = 0;
1115 if (match_backtrack (c,
1116 backtrack.len, (HBUINT16 *) backtrack.arrayZ,
1117 match_coverage, this,
1118 &start_index) &&
1119 match_lookahead (c,
1120 lookahead.len, (HBUINT16 *) lookahead.arrayZ,
1121 match_coverage, this,
1122 1, &end_index))
1123 {
1124 c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
1125 c->replace_glyph_inplace (substitute[index]);
1126 /* Note: We DON'T decrease buffer->idx. The main loop does it
1127 * for us. This is useful for preventing surprises if someone
1128 * calls us through a Context lookup. */
1129 return_trace (true);
1130 }
1131
1132 return_trace (false);
1133 }
1134
1135 bool subset (hb_subset_context_t *c) const
1136 {
1137 TRACE_SUBSET (this);
1138 // TODO(subset)
1139 return_trace (false);
1140 }
1141
1142 bool sanitize (hb_sanitize_context_t *c) const
1143 {
1144 TRACE_SANITIZE (this);
1145 if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
1146 return_trace (false);
1147 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1148 if (!lookahead.sanitize (c, this))
1149 return_trace (false);
1150 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
1151 return_trace (substitute.sanitize (c));
1152 }
1153
1154 protected:
1155 HBUINT16 format; /* Format identifier--format = 1 */
1156 OffsetTo<Coverage>
1157 coverage; /* Offset to Coverage table--from
1158 * beginning of table */
1159 OffsetArrayOf<Coverage>
1160 backtrack; /* Array of coverage tables
1161 * in backtracking sequence, in glyph
1162 * sequence order */
1163 OffsetArrayOf<Coverage>
1164 lookaheadX; /* Array of coverage tables
1165 * in lookahead sequence, in glyph
1166 * sequence order */
1167 ArrayOf<GlyphID>
1168 substituteX; /* Array of substitute
1169 * GlyphIDs--ordered by Coverage Index */
1170 public:
1171 DEFINE_SIZE_MIN (10);
1172};
1173
1174struct ReverseChainSingleSubst
1175{
1176 template <typename context_t>
1177 typename context_t::return_t dispatch (context_t *c) const
1178 {
1179 TRACE_DISPATCH (this, u.format);
1180 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1181 switch (u.format) {
1182 case 1: return_trace (c->dispatch (u.format1));
1183 default:return_trace (c->default_return_value ());
1184 }
1185 }
1186
1187 protected:
1188 union {
1189 HBUINT16 format; /* Format identifier */
1190 ReverseChainSingleSubstFormat1 format1;
1191 } u;
1192};
1193
1194
1195
1196/*
1197 * SubstLookup
1198 */
1199
1200struct SubstLookupSubTable
1201{
1202 friend struct Lookup;
1203 friend struct SubstLookup;
1204
1205 enum Type {
1206 Single = 1,
1207 Multiple = 2,
1208 Alternate = 3,
1209 Ligature = 4,
1210 Context = 5,
1211 ChainContext = 6,
1212 Extension = 7,
1213 ReverseChainSingle = 8
1214 };
1215
1216 template <typename context_t>
1217 typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
1218 {
1219 TRACE_DISPATCH (this, lookup_type);
1220 switch (lookup_type) {
1221 case Single: return_trace (u.single.dispatch (c));
1222 case Multiple: return_trace (u.multiple.dispatch (c));
1223 case Alternate: return_trace (u.alternate.dispatch (c));
1224 case Ligature: return_trace (u.ligature.dispatch (c));
1225 case Context: return_trace (u.context.dispatch (c));
1226 case ChainContext: return_trace (u.chainContext.dispatch (c));
1227 case Extension: return_trace (u.extension.dispatch (c));
1228 case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c));
1229 default: return_trace (c->default_return_value ());
1230 }
1231 }
1232
1233 protected:
1234 union {
1235 SingleSubst single;
1236 MultipleSubst multiple;
1237 AlternateSubst alternate;
1238 LigatureSubst ligature;
1239 ContextSubst context;
1240 ChainContextSubst chainContext;
1241 ExtensionSubst extension;
1242 ReverseChainSingleSubst reverseChainContextSingle;
1243 } u;
1244 public:
1245 DEFINE_SIZE_MIN (0);
1246};
1247
1248
1249struct SubstLookup : Lookup
1250{
1251 typedef SubstLookupSubTable SubTable;
1252
1253 const SubTable& get_subtable (unsigned int i) const
1254 { return Lookup::get_subtable<SubTable> (i); }
1255
1256 static bool lookup_type_is_reverse (unsigned int lookup_type)
1257 { return lookup_type == SubTable::ReverseChainSingle; }
1258
1259 bool is_reverse () const
1260 {
1261 unsigned int type = get_type ();
1262 if (unlikely (type == SubTable::Extension))
1263 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
1264 return lookup_type_is_reverse (type);
1265 }
1266
1267 bool apply (hb_ot_apply_context_t *c) const
1268 {
1269 TRACE_APPLY (this);
1270 return_trace (dispatch (c));
1271 }
1272
1273 bool intersects (const hb_set_t *glyphs) const
1274 {
1275 hb_intersects_context_t c (glyphs);
1276 return dispatch (&c);
1277 }
1278
1279 hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
1280 {
1281 if (!c->should_visit_lookup (this_index))
1282 return hb_closure_context_t::default_return_value ();
1283
1284 c->set_recurse_func (dispatch_closure_recurse_func);
1285
1286 hb_closure_context_t::return_t ret = dispatch (c);
1287
1288 c->flush ();
1289
1290 return ret;
1291 }
1292
1293 hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
1294 {
1295 c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
1296 return dispatch (c);
1297 }
1298
1299 template <typename set_t>
1300 void add_coverage (set_t *glyphs) const
1301 {
1302 hb_add_coverage_context_t<set_t> c (glyphs);
1303 dispatch (&c);
1304 }
1305
1306 bool would_apply (hb_would_apply_context_t *c,
1307 const hb_ot_layout_lookup_accelerator_t *accel) const
1308 {
1309 TRACE_WOULD_APPLY (this);
1310 if (unlikely (!c->len)) return_trace (false);
1311 if (!accel->may_have (c->glyphs[0])) return_trace (false);
1312 return_trace (dispatch (c));
1313 }
1314
1315 static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
1316
1317 SubTable& serialize_subtable (hb_serialize_context_t *c,
1318 unsigned int i)
1319 { return get_subtables<SubTable> ()[i].serialize (c, this); }
1320
1321 bool serialize_single (hb_serialize_context_t *c,
1322 uint32_t lookup_props,
1323 hb_array_t<const GlyphID> glyphs,
1324 hb_array_t<const GlyphID> substitutes)
1325 {
1326 TRACE_SERIALIZE (this);
1327 if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
1328 return_trace (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes));
1329 }
1330
1331 bool serialize_multiple (hb_serialize_context_t *c,
1332 uint32_t lookup_props,
1333 hb_array_t<const GlyphID> glyphs,
1334 hb_array_t<const unsigned int> substitute_len_list,
1335 hb_array_t<const GlyphID> substitute_glyphs_list)
1336 {
1337 TRACE_SERIALIZE (this);
1338 if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
1339 return_trace (serialize_subtable (c, 0).u.multiple.serialize (c,
1340 glyphs,
1341 substitute_len_list,
1342 substitute_glyphs_list));
1343 }
1344
1345 bool serialize_alternate (hb_serialize_context_t *c,
1346 uint32_t lookup_props,
1347 hb_array_t<const GlyphID> glyphs,
1348 hb_array_t<const unsigned int> alternate_len_list,
1349 hb_array_t<const GlyphID> alternate_glyphs_list)
1350 {
1351 TRACE_SERIALIZE (this);
1352 if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
1353 return_trace (serialize_subtable (c, 0).u.alternate.serialize (c,
1354 glyphs,
1355 alternate_len_list,
1356 alternate_glyphs_list));
1357 }
1358
1359 bool serialize_ligature (hb_serialize_context_t *c,
1360 uint32_t lookup_props,
1361 hb_array_t<const GlyphID> first_glyphs,
1362 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1363 hb_array_t<const GlyphID> ligatures_list,
1364 hb_array_t<const unsigned int> component_count_list,
1365 hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
1366 {
1367 TRACE_SERIALIZE (this);
1368 if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
1369 return_trace (serialize_subtable (c, 0).u.ligature.serialize (c,
1370 first_glyphs,
1371 ligature_per_first_glyph_count_list,
1372 ligatures_list,
1373 component_count_list,
1374 component_list));
1375 }
1376
1377 template <typename context_t>
1378 static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
1379
1380 static hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index)
1381 {
1382 if (!c->should_visit_lookup (lookup_index))
1383 return HB_VOID;
1384
1385 hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index);
1386
1387 /* While in theory we should flush here, it will cause timeouts because a recursive
1388 * lookup can keep growing the glyph set. Skip, and outer loop will retry up to
1389 * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */
1390 //c->flush ();
1391
1392 return ret;
1393 }
1394
1395 template <typename context_t>
1396 typename context_t::return_t dispatch (context_t *c) const
1397 { return Lookup::dispatch<SubTable> (c); }
1398
1399 bool subset (hb_subset_context_t *c) const
1400 { return Lookup::subset<SubTable> (c); }
1401
1402 bool sanitize (hb_sanitize_context_t *c) const
1403 { return Lookup::sanitize<SubTable> (c); }
1404};
1405
1406/*
1407 * GSUB -- Glyph Substitution
1408 * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
1409 */
1410
1411struct GSUB : GSUBGPOS
1412{
1413 static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB;
1414
1415 const SubstLookup& get_lookup (unsigned int i) const
1416 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
1417
1418 bool subset (hb_subset_context_t *c) const
1419 { return GSUBGPOS::subset<SubstLookup> (c); }
1420
1421 bool sanitize (hb_sanitize_context_t *c) const
1422 { return GSUBGPOS::sanitize<SubstLookup> (c); }
1423
1424 HB_INTERNAL bool is_blacklisted (hb_blob_t *blob,
1425 hb_face_t *face) const;
1426
1427 typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
1428};
1429
1430
1431struct GSUB_accelerator_t : GSUB::accelerator_t {};
1432
1433
1434/* Out-of-class implementation for methods recursing */
1435
1436/*static*/ inline bool ExtensionSubst::is_reverse () const
1437{
1438 unsigned int type = get_type ();
1439 if (unlikely (type == SubTable::Extension))
1440 return CastR<ExtensionSubst> (get_subtable<SubTable>()).is_reverse ();
1441 return SubstLookup::lookup_type_is_reverse (type);
1442}
1443
1444template <typename context_t>
1445/*static*/ inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
1446{
1447 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1448 return l.dispatch (c);
1449}
1450
1451/*static*/ inline bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
1452{
1453 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1454 unsigned int saved_lookup_props = c->lookup_props;
1455 unsigned int saved_lookup_index = c->lookup_index;
1456 c->set_lookup_index (lookup_index);
1457 c->set_lookup_props (l.get_props ());
1458 bool ret = l.dispatch (c);
1459 c->set_lookup_index (saved_lookup_index);
1460 c->set_lookup_props (saved_lookup_props);
1461 return ret;
1462}
1463
1464} /* namespace OT */
1465
1466
1467#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
1468