2 * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
3 * Copyright © 2010,2012,2013 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
29 #ifndef HB_OT_LAYOUT_GSUB_TABLE_HH
30 #define HB_OT_LAYOUT_GSUB_TABLE_HH
32 #include "hb-ot-layout-gsubgpos.hh"
38 static inline void SingleSubst_serialize (hb_serialize_context_t *c,
39 hb_array_t<const GlyphID> glyphs,
40 hb_array_t<const GlyphID> substitutes);
42 struct SingleSubstFormat1
44 bool intersects (const hb_set_t *glyphs) const
45 { return (this+coverage).intersects (glyphs); }
47 void closure (hb_closure_context_t *c) const
49 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
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);
59 void collect_glyphs (hb_collect_glyphs_context_t *c) const
61 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
62 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
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);
71 const Coverage &get_coverage () const { return this+coverage; }
73 bool would_apply (hb_would_apply_context_t *c) const
75 TRACE_WOULD_APPLY (this);
76 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
79 bool apply (hb_ot_apply_context_t *c) const
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);
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);
94 bool serialize (hb_serialize_context_t *c,
95 hb_array_t<const GlyphID> glyphs,
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? */
105 bool subset (hb_subset_context_t *c) const
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 ())
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]);
119 c->serializer->propagate_error (from, to);
120 SingleSubst_serialize (c->serializer, from, to);
121 return_trace (from.length);
124 bool sanitize (hb_sanitize_context_t *c) const
126 TRACE_SANITIZE (this);
127 return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
131 HBUINT16 format; /* Format identifier--format = 1 */
133 coverage; /* Offset to Coverage table--from
134 * beginning of Substitution table */
135 HBINT16 deltaGlyphID; /* Add to original GlyphID to get
136 * substitute GlyphID */
138 DEFINE_SIZE_STATIC (6);
141 struct SingleSubstFormat2
143 bool intersects (const hb_set_t *glyphs) const
144 { return (this+coverage).intersects (glyphs); }
146 void closure (hb_closure_context_t *c) const
148 unsigned int count = substitute.len;
149 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
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 ()]);
158 void collect_glyphs (hb_collect_glyphs_context_t *c) const
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 ())
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 ()]);
170 const Coverage &get_coverage () const { return this+coverage; }
172 bool would_apply (hb_would_apply_context_t *c) const
174 TRACE_WOULD_APPLY (this);
175 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
178 bool apply (hb_ot_apply_context_t *c) const
181 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
182 if (likely (index == NOT_COVERED)) return_trace (false);
184 if (unlikely (index >= substitute.len)) return_trace (false);
186 c->replace_glyph (substitute[index]);
191 bool serialize (hb_serialize_context_t *c,
192 hb_array_t<const GlyphID> glyphs,
193 hb_array_t<const GlyphID> substitutes)
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);
202 bool subset (hb_subset_context_t *c) const
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 ())
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 ()]]);
215 c->serializer->propagate_error (from, to);
216 SingleSubst_serialize (c->serializer, from, to);
217 return_trace (from.length);
220 bool sanitize (hb_sanitize_context_t *c) const
222 TRACE_SANITIZE (this);
223 return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
227 HBUINT16 format; /* Format identifier--format = 2 */
229 coverage; /* Offset to Coverage table--from
230 * beginning of Substitution table */
232 substitute; /* Array of substitute
233 * GlyphIDs--ordered by Coverage Index */
235 DEFINE_SIZE_ARRAY (6, substitute);
240 bool serialize (hb_serialize_context_t *c,
241 hb_array_t<const GlyphID> glyphs,
242 hb_array_t<const GlyphID> substitutes)
244 TRACE_SERIALIZE (this);
245 if (unlikely (!c->extend_min (u.format))) return_trace (false);
246 unsigned int format = 2;
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])) {
259 u.format.set (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);
267 template <typename context_t>
268 typename context_t::return_t dispatch (context_t *c) const
270 TRACE_DISPATCH (this, u.format);
271 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
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 ());
281 HBUINT16 format; /* Format identifier */
282 SingleSubstFormat1 format1;
283 SingleSubstFormat2 format2;
288 SingleSubst_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); }
295 void closure (hb_closure_context_t *c) const
297 unsigned int count = substitute.len;
298 for (unsigned int i = 0; i < count; i++)
299 c->out->add (substitute[i]);
302 void collect_glyphs (hb_collect_glyphs_context_t *c) const
303 { c->output->add_array (substitute.arrayZ, substitute.len); }
305 bool apply (hb_ot_apply_context_t *c) const
308 unsigned int count = substitute.len;
310 /* Special-case to make it in-place and not consider this
311 * as a "multiplied" substitution. */
312 if (unlikely (count == 1))
314 c->replace_glyph (substitute.arrayZ[0]);
317 /* Spec disallows this, but Uniscribe allows it.
318 * https://github.com/harfbuzz/harfbuzz/issues/253 */
319 else if (unlikely (count == 0))
321 c->buffer->delete_glyph ();
325 unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
326 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
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);
332 c->buffer->skip_glyph ();
337 bool serialize (hb_serialize_context_t *c,
338 hb_array_t<const GlyphID> glyphs)
340 TRACE_SERIALIZE (this);
341 return_trace (substitute.serialize (c, glyphs));
344 bool sanitize (hb_sanitize_context_t *c) const
346 TRACE_SANITIZE (this);
347 return_trace (substitute.sanitize (c));
352 substitute; /* String of GlyphIDs to substitute */
354 DEFINE_SIZE_ARRAY (2, substitute);
357 struct MultipleSubstFormat1
359 bool intersects (const hb_set_t *glyphs) const
360 { return (this+coverage).intersects (glyphs); }
362 void closure (hb_closure_context_t *c) const
364 unsigned int count = sequence.len;
365 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
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);
374 void collect_glyphs (hb_collect_glyphs_context_t *c) const
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);
382 const Coverage &get_coverage () const { return this+coverage; }
384 bool would_apply (hb_would_apply_context_t *c) const
386 TRACE_WOULD_APPLY (this);
387 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
390 bool apply (hb_ot_apply_context_t *c) const
394 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
395 if (likely (index == NOT_COVERED)) return_trace (false);
397 return_trace ((this+sequence[index]).apply (c));
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)
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++)
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;
416 return_trace (coverage.serialize (c, this).serialize (c, glyphs));
419 bool subset (hb_subset_context_t *c) const
423 return_trace (false);
426 bool sanitize (hb_sanitize_context_t *c) const
428 TRACE_SANITIZE (this);
429 return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
433 HBUINT16 format; /* Format identifier--format = 1 */
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 */
441 DEFINE_SIZE_ARRAY (6, sequence);
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)
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);
456 case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list));
457 default:return_trace (false);
461 template <typename context_t>
462 typename context_t::return_t dispatch (context_t *c) const
464 TRACE_DISPATCH (this, u.format);
465 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
467 case 1: return_trace (c->dispatch (u.format1));
468 default:return_trace (c->default_return_value ());
474 HBUINT16 format; /* Format identifier */
475 MultipleSubstFormat1 format1;
481 void closure (hb_closure_context_t *c) const
483 unsigned int count = alternates.len;
484 for (unsigned int i = 0; i < count; i++)
485 c->out->add (alternates[i]);
488 void collect_glyphs (hb_collect_glyphs_context_t *c) const
489 { c->output->add_array (alternates.arrayZ, alternates.len); }
491 bool apply (hb_ot_apply_context_t *c) const
494 unsigned int count = alternates.len;
496 if (unlikely (!count)) return_trace (false);
498 hb_mask_t glyph_mask = c->buffer->cur().mask;
499 hb_mask_t lookup_mask = c->lookup_mask;
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);
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;
509 if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
511 c->replace_glyph (alternates[alt_index - 1]);
516 bool serialize (hb_serialize_context_t *c,
517 hb_array_t<const GlyphID> glyphs)
519 TRACE_SERIALIZE (this);
520 return_trace (alternates.serialize (c, glyphs));
523 bool sanitize (hb_sanitize_context_t *c) const
525 TRACE_SANITIZE (this);
526 return_trace (alternates.sanitize (c));
531 alternates; /* Array of alternate GlyphIDs--in
534 DEFINE_SIZE_ARRAY (2, alternates);
537 struct AlternateSubstFormat1
539 bool intersects (const hb_set_t *glyphs) const
540 { return (this+coverage).intersects (glyphs); }
542 void closure (hb_closure_context_t *c) const
544 unsigned int count = alternateSet.len;
545 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
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);
554 void collect_glyphs (hb_collect_glyphs_context_t *c) const
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 ())
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);
566 const Coverage &get_coverage () const { return this+coverage; }
568 bool would_apply (hb_would_apply_context_t *c) const
570 TRACE_WOULD_APPLY (this);
571 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
574 bool apply (hb_ot_apply_context_t *c) const
578 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
579 if (likely (index == NOT_COVERED)) return_trace (false);
581 return_trace ((this+alternateSet[index]).apply (c));
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)
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++)
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;
600 return_trace (coverage.serialize (c, this).serialize (c, glyphs));
603 bool subset (hb_subset_context_t *c) const
607 return_trace (false);
610 bool sanitize (hb_sanitize_context_t *c) const
612 TRACE_SANITIZE (this);
613 return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
617 HBUINT16 format; /* Format identifier--format = 1 */
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 */
625 DEFINE_SIZE_ARRAY (6, alternateSet);
628 struct AlternateSubst
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)
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);
640 case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list));
641 default:return_trace (false);
645 template <typename context_t>
646 typename context_t::return_t dispatch (context_t *c) const
648 TRACE_DISPATCH (this, u.format);
649 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
651 case 1: return_trace (c->dispatch (u.format1));
652 default:return_trace (c->default_return_value ());
658 HBUINT16 format; /* Format identifier */
659 AlternateSubstFormat1 format1;
666 bool intersects (const hb_set_t *glyphs) const
668 unsigned int count = component.lenP1;
669 for (unsigned int i = 1; i < count; i++)
670 if (!glyphs->has (component[i]))
675 void closure (hb_closure_context_t *c) const
677 unsigned int count = component.lenP1;
678 for (unsigned int i = 1; i < count; i++)
679 if (!c->glyphs->has (component[i]))
681 c->out->add (ligGlyph);
684 void collect_glyphs (hb_collect_glyphs_context_t *c) const
686 c->input->add_array (component.arrayZ, component.lenP1 ? component.lenP1 - 1 : 0);
687 c->output->add (ligGlyph);
690 bool would_apply (hb_would_apply_context_t *c) const
692 TRACE_WOULD_APPLY (this);
693 if (c->len != component.lenP1)
694 return_trace (false);
696 for (unsigned int i = 1; i < c->len; i++)
697 if (likely (c->glyphs[i] != component[i]))
698 return_trace (false);
703 bool apply (hb_ot_apply_context_t *c) const
706 unsigned int count = component.lenP1;
708 if (unlikely (!count)) return_trace (false);
710 /* Special-case to make it in-place and not consider this
711 * as a "ligated" substitution. */
712 if (unlikely (count == 1))
714 c->replace_glyph (ligGlyph);
718 unsigned int total_component_count = 0;
720 unsigned int match_length = 0;
721 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
723 if (likely (!match_input (c, count,
729 &total_component_count)))
730 return_trace (false);
737 total_component_count);
742 bool serialize (hb_serialize_context_t *c,
744 hb_array_t<const GlyphID> components /* Starting from second */)
746 TRACE_SERIALIZE (this);
747 if (unlikely (!c->extend_min (*this))) return_trace (false);
749 if (unlikely (!component.serialize (c, components))) return_trace (false);
754 bool sanitize (hb_sanitize_context_t *c) const
756 TRACE_SANITIZE (this);
757 return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
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 */
767 DEFINE_SIZE_ARRAY (4, component);
772 bool intersects (const hb_set_t *glyphs) const
774 unsigned int num_ligs = ligature.len;
775 for (unsigned int i = 0; i < num_ligs; i++)
776 if ((this+ligature[i]).intersects (glyphs))
781 void closure (hb_closure_context_t *c) const
783 unsigned int num_ligs = ligature.len;
784 for (unsigned int i = 0; i < num_ligs; i++)
785 (this+ligature[i]).closure (c);
788 void collect_glyphs (hb_collect_glyphs_context_t *c) const
790 unsigned int num_ligs = ligature.len;
791 for (unsigned int i = 0; i < num_ligs; i++)
792 (this+ligature[i]).collect_glyphs (c);
795 bool would_apply (hb_would_apply_context_t *c) const
797 TRACE_WOULD_APPLY (this);
798 unsigned int num_ligs = ligature.len;
799 for (unsigned int i = 0; i < num_ligs; i++)
801 const Ligature &lig = this+ligature[i];
802 if (lig.would_apply (c))
805 return_trace (false);
808 bool apply (hb_ot_apply_context_t *c) const
811 unsigned int num_ligs = ligature.len;
812 for (unsigned int i = 0; i < num_ligs; i++)
814 const Ligature &lig = this+ligature[i];
815 if (lig.apply (c)) return_trace (true);
818 return_trace (false);
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 */)
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++)
831 unsigned int component_count = MAX<int> (component_count_list[i] - 1, 0);
832 if (unlikely (!ligature[i].serialize (c, this)
835 component_list.sub_array (0, component_count))))
836 return_trace (false);
837 component_list += component_count;
842 bool sanitize (hb_sanitize_context_t *c) const
844 TRACE_SANITIZE (this);
845 return_trace (ligature.sanitize (c, this));
849 OffsetArrayOf<Ligature>
850 ligature; /* Array LigatureSet tables
851 * ordered by preference */
853 DEFINE_SIZE_ARRAY (2, ligature);
856 struct LigatureSubstFormat1
858 bool intersects (const hb_set_t *glyphs) const
860 unsigned int count = ligatureSet.len;
861 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
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))
872 void closure (hb_closure_context_t *c) const
874 unsigned int count = ligatureSet.len;
875 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
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);
884 void collect_glyphs (hb_collect_glyphs_context_t *c) const
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 ())
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);
896 const Coverage &get_coverage () const { return this+coverage; }
898 bool would_apply (hb_would_apply_context_t *c) const
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);
904 const LigatureSet &lig_set = this+ligatureSet[index];
905 return_trace (lig_set.would_apply (c));
908 bool apply (hb_ot_apply_context_t *c) const
912 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
913 if (likely (index == NOT_COVERED)) return_trace (false);
915 const LigatureSet &lig_set = this+ligatureSet[index];
916 return_trace (lig_set.apply (c));
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 */)
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++)
931 unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
932 if (unlikely (!ligatureSet[i].serialize (c, this)
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;
940 return_trace (coverage.serialize (c, this).serialize (c, first_glyphs));
943 bool subset (hb_subset_context_t *c) const
947 return_trace (false);
950 bool sanitize (hb_sanitize_context_t *c) const
952 TRACE_SANITIZE (this);
953 return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
957 HBUINT16 format; /* Format identifier--format = 1 */
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 */
965 DEFINE_SIZE_ARRAY (6, ligatureSet);
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 */)
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);
982 case 1: return_trace (u.format1.serialize (c,
984 ligature_per_first_glyph_count_list,
986 component_count_list,
988 default:return_trace (false);
992 template <typename context_t>
993 typename context_t::return_t dispatch (context_t *c) const
995 TRACE_DISPATCH (this, u.format);
996 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
998 case 1: return_trace (c->dispatch (u.format1));
999 default:return_trace (c->default_return_value ());
1005 HBUINT16 format; /* Format identifier */
1006 LigatureSubstFormat1 format1;
1011 struct ContextSubst : Context {};
1013 struct ChainContextSubst : ChainContext {};
1015 struct ExtensionSubst : Extension<ExtensionSubst>
1017 typedef struct SubstLookupSubTable SubTable;
1019 bool is_reverse () const;
1023 struct ReverseChainSingleSubstFormat1
1025 bool intersects (const hb_set_t *glyphs) const
1027 if (!(this+coverage).intersects (glyphs))
1030 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1034 count = backtrack.len;
1035 for (unsigned int i = 0; i < count; i++)
1036 if (!(this+backtrack[i]).intersects (glyphs))
1039 count = lookahead.len;
1040 for (unsigned int i = 0; i < count; i++)
1041 if (!(this+lookahead[i]).intersects (glyphs))
1047 void closure (hb_closure_context_t *c) const
1049 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1053 count = backtrack.len;
1054 for (unsigned int i = 0; i < count; i++)
1055 if (!(this+backtrack[i]).intersects (c->glyphs))
1058 count = lookahead.len;
1059 for (unsigned int i = 0; i < count; i++)
1060 if (!(this+lookahead[i]).intersects (c->glyphs))
1063 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
1064 count = substitute.len;
1065 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
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 ()]);
1074 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1076 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
1080 count = backtrack.len;
1081 for (unsigned int i = 0; i < count; i++)
1082 if (unlikely (!(this+backtrack[i]).add_coverage (c->before))) return;
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;
1089 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
1090 count = substitute.len;
1091 c->output->add_array (substitute.arrayZ, substitute.len);
1094 const Coverage &get_coverage () const { return this+coverage; }
1096 bool would_apply (hb_would_apply_context_t *c) const
1098 TRACE_WOULD_APPLY (this);
1099 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
1102 bool apply (hb_ot_apply_context_t *c) const
1105 if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
1106 return_trace (false); /* No chaining to this type */
1108 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
1109 if (likely (index == NOT_COVERED)) return_trace (false);
1111 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1112 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
1114 unsigned int start_index = 0, end_index = 0;
1115 if (match_backtrack (c,
1116 backtrack.len, (HBUINT16 *) backtrack.arrayZ,
1117 match_coverage, this,
1120 lookahead.len, (HBUINT16 *) lookahead.arrayZ,
1121 match_coverage, this,
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);
1132 return_trace (false);
1135 bool subset (hb_subset_context_t *c) const
1137 TRACE_SUBSET (this);
1139 return_trace (false);
1142 bool sanitize (hb_sanitize_context_t *c) const
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));
1155 HBUINT16 format; /* Format identifier--format = 1 */
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
1163 OffsetArrayOf<Coverage>
1164 lookaheadX; /* Array of coverage tables
1165 * in lookahead sequence, in glyph
1168 substituteX; /* Array of substitute
1169 * GlyphIDs--ordered by Coverage Index */
1171 DEFINE_SIZE_MIN (10);
1174 struct ReverseChainSingleSubst
1176 template <typename context_t>
1177 typename context_t::return_t dispatch (context_t *c) const
1179 TRACE_DISPATCH (this, u.format);
1180 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1182 case 1: return_trace (c->dispatch (u.format1));
1183 default:return_trace (c->default_return_value ());
1189 HBUINT16 format; /* Format identifier */
1190 ReverseChainSingleSubstFormat1 format1;
1200 struct SubstLookupSubTable
1202 friend struct Lookup;
1203 friend struct SubstLookup;
1213 ReverseChainSingle = 8
1216 template <typename context_t>
1217 typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
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 ());
1236 MultipleSubst multiple;
1237 AlternateSubst alternate;
1238 LigatureSubst ligature;
1239 ContextSubst context;
1240 ChainContextSubst chainContext;
1241 ExtensionSubst extension;
1242 ReverseChainSingleSubst reverseChainContextSingle;
1245 DEFINE_SIZE_MIN (0);
1249 struct SubstLookup : Lookup
1251 typedef SubstLookupSubTable SubTable;
1253 const SubTable& get_subtable (unsigned int i) const
1254 { return Lookup::get_subtable<SubTable> (i); }
1256 static bool lookup_type_is_reverse (unsigned int lookup_type)
1257 { return lookup_type == SubTable::ReverseChainSingle; }
1259 bool is_reverse () const
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);
1267 bool apply (hb_ot_apply_context_t *c) const
1270 return_trace (dispatch (c));
1273 bool intersects (const hb_set_t *glyphs) const
1275 hb_intersects_context_t c (glyphs);
1276 return dispatch (&c);
1279 hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
1281 if (!c->should_visit_lookup (this_index))
1282 return hb_closure_context_t::default_return_value ();
1284 c->set_recurse_func (dispatch_closure_recurse_func);
1286 hb_closure_context_t::return_t ret = dispatch (c);
1293 hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
1295 c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
1296 return dispatch (c);
1299 template <typename set_t>
1300 void add_coverage (set_t *glyphs) const
1302 hb_add_coverage_context_t<set_t> c (glyphs);
1306 bool would_apply (hb_would_apply_context_t *c,
1307 const hb_ot_layout_lookup_accelerator_t *accel) const
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));
1315 static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
1317 SubTable& serialize_subtable (hb_serialize_context_t *c,
1319 { return get_subtables<SubTable> ()[i].serialize (c, this); }
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)
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));
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)
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,
1341 substitute_len_list,
1342 substitute_glyphs_list));
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)
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,
1356 alternate_glyphs_list));
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 */)
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,
1371 ligature_per_first_glyph_count_list,
1373 component_count_list,
1377 template <typename context_t>
1378 static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
1380 static hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index)
1382 if (!c->should_visit_lookup (lookup_index))
1385 hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index);
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. */
1395 template <typename context_t>
1396 typename context_t::return_t dispatch (context_t *c) const
1397 { return Lookup::dispatch<SubTable> (c); }
1399 bool subset (hb_subset_context_t *c) const
1400 { return Lookup::subset<SubTable> (c); }
1402 bool sanitize (hb_sanitize_context_t *c) const
1403 { return Lookup::sanitize<SubTable> (c); }
1407 * GSUB -- Glyph Substitution
1408 * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
1411 struct GSUB : GSUBGPOS
1413 static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB;
1415 const SubstLookup& get_lookup (unsigned int i) const
1416 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
1418 bool subset (hb_subset_context_t *c) const
1419 { return GSUBGPOS::subset<SubstLookup> (c); }
1421 bool sanitize (hb_sanitize_context_t *c) const
1422 { return GSUBGPOS::sanitize<SubstLookup> (c); }
1424 HB_INTERNAL bool is_blacklisted (hb_blob_t *blob,
1425 hb_face_t *face) const;
1427 typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
1431 struct GSUB_accelerator_t : GSUB::accelerator_t {};
1434 /* Out-of-class implementation for methods recursing */
1436 /*static*/ inline bool ExtensionSubst::is_reverse () const
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);
1444 template <typename context_t>
1445 /*static*/ inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
1447 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1448 return l.dispatch (c);
1451 /*static*/ inline bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
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);
1464 } /* namespace OT */
1467 #endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */