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

source code of qtbase/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-gsub-table.hh