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"
37 typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> hb_codepoint_pair_t;
39 template<typename Iterator>
40 static void SingleSubst_serialize (hb_serialize_context_t *c,
44 struct SingleSubstFormat1
46 bool intersects (const hb_set_t *glyphs) const
47 { return (this+coverage).intersects (glyphs); }
49 bool may_have_non_1to1 () const
52 void closure (hb_closure_context_t *c) const
54 unsigned d = deltaGlyphID;
56 + hb_iter (this+coverage)
57 | hb_filter (c->parent_active_glyphs ())
58 | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; })
64 void closure_lookups (hb_closure_lookups_context_t *c) const {}
66 void collect_glyphs (hb_collect_glyphs_context_t *c) const
68 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
69 unsigned d = deltaGlyphID;
70 + hb_iter (this+coverage)
71 | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; })
76 const Coverage &get_coverage () const { return this+coverage; }
78 bool would_apply (hb_would_apply_context_t *c) const
79 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
81 bool apply (hb_ot_apply_context_t *c) const
84 hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
85 unsigned int index = (this+coverage).get_coverage (glyph_id);
86 if (likely (index == NOT_COVERED)) return_trace (false);
88 /* According to the Adobe Annotated OpenType Suite, result is always
89 * limited to 16bit. */
90 glyph_id = (glyph_id + deltaGlyphID) & 0xFFFFu;
91 c->replace_glyph (glyph_id);
96 template<typename Iterator,
97 hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
98 bool serialize (hb_serialize_context_t *c,
102 TRACE_SERIALIZE (this);
103 if (unlikely (!c->extend_min (this))) return_trace (false);
104 if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false);
105 c->check_assign (deltaGlyphID, delta, HB_SERIALIZE_ERROR_INT_OVERFLOW);
109 bool subset (hb_subset_context_t *c) const
112 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
113 const hb_map_t &glyph_map = *c->plan->glyph_map;
115 hb_codepoint_t delta = deltaGlyphID;
118 + hb_iter (this+coverage)
119 | hb_filter (glyphset)
120 | hb_map_retains_sorting ([&] (hb_codepoint_t g) {
121 return hb_codepoint_pair_t (g,
122 (g + delta) & 0xFFFF); })
123 | hb_filter (glyphset, hb_second)
124 | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t
125 { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
128 bool ret = bool (it);
129 SingleSubst_serialize (c->serializer, it);
133 bool sanitize (hb_sanitize_context_t *c) const
135 TRACE_SANITIZE (this);
136 return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
140 HBUINT16 format; /* Format identifier--format = 1 */
142 coverage; /* Offset to Coverage table--from
143 * beginning of Substitution table */
144 HBUINT16 deltaGlyphID; /* Add to original GlyphID to get
145 * substitute GlyphID, modulo 0x10000 */
147 DEFINE_SIZE_STATIC (6);
150 struct SingleSubstFormat2
152 bool intersects (const hb_set_t *glyphs) const
153 { return (this+coverage).intersects (glyphs); }
155 bool may_have_non_1to1 () const
158 void closure (hb_closure_context_t *c) const
160 + hb_zip (this+coverage, substitute)
161 | hb_filter (c->parent_active_glyphs (), hb_first)
163 | hb_sink (c->output)
168 void closure_lookups (hb_closure_lookups_context_t *c) const {}
170 void collect_glyphs (hb_collect_glyphs_context_t *c) const
172 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
173 + hb_zip (this+coverage, substitute)
175 | hb_sink (c->output)
179 const Coverage &get_coverage () const { return this+coverage; }
181 bool would_apply (hb_would_apply_context_t *c) const
182 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
184 bool apply (hb_ot_apply_context_t *c) const
187 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
188 if (likely (index == NOT_COVERED)) return_trace (false);
190 if (unlikely (index >= substitute.len)) return_trace (false);
192 c->replace_glyph (substitute[index]);
197 template<typename Iterator,
198 hb_requires (hb_is_sorted_source_of (Iterator,
199 hb_codepoint_pair_t))>
200 bool serialize (hb_serialize_context_t *c,
203 TRACE_SERIALIZE (this);
210 | hb_map_retains_sorting (hb_first)
212 if (unlikely (!c->extend_min (this))) return_trace (false);
213 if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false);
214 if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false);
218 bool subset (hb_subset_context_t *c) const
221 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
222 const hb_map_t &glyph_map = *c->plan->glyph_map;
225 + hb_zip (this+coverage, substitute)
226 | hb_filter (glyphset, hb_first)
227 | hb_filter (glyphset, hb_second)
228 | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const HBGlyphID16 &> p) -> hb_codepoint_pair_t
229 { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
232 bool ret = bool (it);
233 SingleSubst_serialize (c->serializer, it);
237 bool sanitize (hb_sanitize_context_t *c) const
239 TRACE_SANITIZE (this);
240 return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
244 HBUINT16 format; /* Format identifier--format = 2 */
246 coverage; /* Offset to Coverage table--from
247 * beginning of Substitution table */
248 Array16Of<HBGlyphID16>
249 substitute; /* Array of substitute
250 * GlyphIDs--ordered by Coverage Index */
252 DEFINE_SIZE_ARRAY (6, substitute);
258 template<typename Iterator,
259 hb_requires (hb_is_sorted_source_of (Iterator,
260 const hb_codepoint_pair_t))>
261 bool serialize (hb_serialize_context_t *c,
264 TRACE_SERIALIZE (this);
265 if (unlikely (!c->extend_min (u.format))) return_trace (false);
271 auto get_delta = [=] (hb_codepoint_pair_t _)
272 { return (unsigned) (_.second - _.first) & 0xFFFF; };
273 delta = get_delta (*glyphs);
274 if (!hb_all (++(+glyphs), delta, get_delta)) format = 2;
278 case 1: return_trace (u.format1.serialize (c,
280 | hb_map_retains_sorting (hb_first),
282 case 2: return_trace (u.format2.serialize (c, glyphs));
283 default:return_trace (false);
287 template <typename context_t, typename ...Ts>
288 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
290 TRACE_DISPATCH (this, u.format);
291 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
293 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
294 case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
295 default:return_trace (c->default_return_value ());
301 HBUINT16 format; /* Format identifier */
302 SingleSubstFormat1 format1;
303 SingleSubstFormat2 format2;
307 template<typename Iterator>
309 SingleSubst_serialize (hb_serialize_context_t *c,
311 { c->start_embed<SingleSubst> ()->serialize (c, it); }
315 bool intersects (const hb_set_t *glyphs) const
316 { return hb_all (substitute, glyphs); }
318 void closure (hb_closure_context_t *c) const
319 { c->output->add_array (substitute.arrayZ, substitute.len); }
321 void collect_glyphs (hb_collect_glyphs_context_t *c) const
322 { c->output->add_array (substitute.arrayZ, substitute.len); }
324 bool apply (hb_ot_apply_context_t *c) const
327 unsigned int count = substitute.len;
329 /* Special-case to make it in-place and not consider this
330 * as a "multiplied" substitution. */
331 if (unlikely (count == 1))
333 c->replace_glyph (substitute.arrayZ[0]);
336 /* Spec disallows this, but Uniscribe allows it.
337 * https://github.com/harfbuzz/harfbuzz/issues/253 */
338 else if (unlikely (count == 0))
340 c->buffer->delete_glyph ();
344 unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
345 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
346 unsigned lig_id = _hb_glyph_info_get_lig_id (&c->buffer->cur());
348 for (unsigned int i = 0; i < count; i++)
350 /* If is attached to a ligature, don't disturb that.
351 * https://github.com/harfbuzz/harfbuzz/issues/3069 */
353 _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i);
354 c->output_glyph_for_component (substitute.arrayZ[i], klass);
356 c->buffer->skip_glyph ();
361 template <typename Iterator,
362 hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
363 bool serialize (hb_serialize_context_t *c,
366 TRACE_SERIALIZE (this);
367 return_trace (substitute.serialize (c, subst));
370 bool subset (hb_subset_context_t *c) const
373 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
374 const hb_map_t &glyph_map = *c->plan->glyph_map;
376 if (!intersects (&glyphset)) return_trace (false);
379 + hb_iter (substitute)
383 auto *out = c->serializer->start_embed (*this);
384 return_trace (out->serialize (c->serializer, it));
387 bool sanitize (hb_sanitize_context_t *c) const
389 TRACE_SANITIZE (this);
390 return_trace (substitute.sanitize (c));
394 Array16Of<HBGlyphID16>
395 substitute; /* String of GlyphIDs to substitute */
397 DEFINE_SIZE_ARRAY (2, substitute);
400 struct MultipleSubstFormat1
402 bool intersects (const hb_set_t *glyphs) const
403 { return (this+coverage).intersects (glyphs); }
405 bool may_have_non_1to1 () const
408 void closure (hb_closure_context_t *c) const
410 + hb_zip (this+coverage, sequence)
411 | hb_filter (c->parent_active_glyphs (), hb_first)
413 | hb_map (hb_add (this))
414 | hb_apply ([c] (const Sequence &_) { _.closure (c); })
418 void closure_lookups (hb_closure_lookups_context_t *c) const {}
420 void collect_glyphs (hb_collect_glyphs_context_t *c) const
422 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
423 + hb_zip (this+coverage, sequence)
425 | hb_map (hb_add (this))
426 | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); })
430 const Coverage &get_coverage () const { return this+coverage; }
432 bool would_apply (hb_would_apply_context_t *c) const
433 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
435 bool apply (hb_ot_apply_context_t *c) const
439 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
440 if (likely (index == NOT_COVERED)) return_trace (false);
442 return_trace ((this+sequence[index]).apply (c));
445 bool serialize (hb_serialize_context_t *c,
446 hb_sorted_array_t<const HBGlyphID16> glyphs,
447 hb_array_t<const unsigned int> substitute_len_list,
448 hb_array_t<const HBGlyphID16> substitute_glyphs_list)
450 TRACE_SERIALIZE (this);
451 if (unlikely (!c->extend_min (this))) return_trace (false);
452 if (unlikely (!sequence.serialize (c, glyphs.length))) return_trace (false);
453 for (unsigned int i = 0; i < glyphs.length; i++)
455 unsigned int substitute_len = substitute_len_list[i];
456 if (unlikely (!sequence[i]
457 .serialize_serialize (c, substitute_glyphs_list.sub_array (0, substitute_len))))
458 return_trace (false);
459 substitute_glyphs_list += substitute_len;
461 return_trace (coverage.serialize_serialize (c, glyphs));
464 bool subset (hb_subset_context_t *c) const
467 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
468 const hb_map_t &glyph_map = *c->plan->glyph_map;
470 auto *out = c->serializer->start_embed (*this);
471 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
472 out->format = format;
474 hb_sorted_vector_t<hb_codepoint_t> new_coverage;
475 + hb_zip (this+coverage, sequence)
476 | hb_filter (glyphset, hb_first)
477 | hb_filter (subset_offset_array (c, out->sequence, this), hb_second)
480 | hb_sink (new_coverage)
482 out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
483 return_trace (bool (new_coverage));
486 bool sanitize (hb_sanitize_context_t *c) const
488 TRACE_SANITIZE (this);
489 return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
493 HBUINT16 format; /* Format identifier--format = 1 */
495 coverage; /* Offset to Coverage table--from
496 * beginning of Substitution table */
497 Array16OfOffset16To<Sequence>
498 sequence; /* Array of Sequence tables
499 * ordered by Coverage Index */
501 DEFINE_SIZE_ARRAY (6, sequence);
506 bool serialize (hb_serialize_context_t *c,
507 hb_sorted_array_t<const HBGlyphID16> glyphs,
508 hb_array_t<const unsigned int> substitute_len_list,
509 hb_array_t<const HBGlyphID16> substitute_glyphs_list)
511 TRACE_SERIALIZE (this);
512 if (unlikely (!c->extend_min (u.format))) return_trace (false);
513 unsigned int format = 1;
516 case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list));
517 default:return_trace (false);
521 template <typename context_t, typename ...Ts>
522 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
524 TRACE_DISPATCH (this, u.format);
525 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
527 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
528 default:return_trace (c->default_return_value ());
534 HBUINT16 format; /* Format identifier */
535 MultipleSubstFormat1 format1;
541 bool intersects (const hb_set_t *glyphs) const
542 { return hb_any (alternates, glyphs); }
544 void closure (hb_closure_context_t *c) const
545 { c->output->add_array (alternates.arrayZ, alternates.len); }
547 void collect_glyphs (hb_collect_glyphs_context_t *c) const
548 { c->output->add_array (alternates.arrayZ, alternates.len); }
550 bool apply (hb_ot_apply_context_t *c) const
553 unsigned int count = alternates.len;
555 if (unlikely (!count)) return_trace (false);
557 hb_mask_t glyph_mask = c->buffer->cur().mask;
558 hb_mask_t lookup_mask = c->lookup_mask;
560 /* Note: This breaks badly if two features enabled this lookup together. */
561 unsigned int shift = hb_ctz (lookup_mask);
562 unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
564 /* If alt_index is MAX_VALUE, randomize feature if it is the rand feature. */
565 if (alt_index == HB_OT_MAP_MAX_VALUE && c->random)
567 /* Maybe we can do better than unsafe-to-break all; but since we are
568 * changing random state, it would be hard to track that. Good 'nough. */
569 c->buffer->unsafe_to_break (0, c->buffer->len);
570 alt_index = c->random_number () % count + 1;
573 if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
575 c->replace_glyph (alternates[alt_index - 1]);
581 get_alternates (unsigned start_offset,
582 unsigned *alternate_count /* IN/OUT. May be NULL. */,
583 hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
585 if (alternates.len && alternate_count)
587 + alternates.sub_array (start_offset, alternate_count)
588 | hb_sink (hb_array (alternate_glyphs, *alternate_count))
591 return alternates.len;
594 template <typename Iterator,
595 hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
596 bool serialize (hb_serialize_context_t *c,
599 TRACE_SERIALIZE (this);
600 return_trace (alternates.serialize (c, alts));
603 bool subset (hb_subset_context_t *c) const
606 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
607 const hb_map_t &glyph_map = *c->plan->glyph_map;
610 + hb_iter (alternates)
611 | hb_filter (glyphset)
615 auto *out = c->serializer->start_embed (*this);
616 return_trace (out->serialize (c->serializer, it) &&
620 bool sanitize (hb_sanitize_context_t *c) const
622 TRACE_SANITIZE (this);
623 return_trace (alternates.sanitize (c));
627 Array16Of<HBGlyphID16>
628 alternates; /* Array of alternate GlyphIDs--in
631 DEFINE_SIZE_ARRAY (2, alternates);
634 struct AlternateSubstFormat1
636 bool intersects (const hb_set_t *glyphs) const
637 { return (this+coverage).intersects (glyphs); }
639 bool may_have_non_1to1 () const
642 void closure (hb_closure_context_t *c) const
644 + hb_zip (this+coverage, alternateSet)
645 | hb_filter (c->parent_active_glyphs (), hb_first)
647 | hb_map (hb_add (this))
648 | hb_apply ([c] (const AlternateSet &_) { _.closure (c); })
653 void closure_lookups (hb_closure_lookups_context_t *c) const {}
655 void collect_glyphs (hb_collect_glyphs_context_t *c) const
657 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
658 + hb_zip (this+coverage, alternateSet)
660 | hb_map (hb_add (this))
661 | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); })
665 const Coverage &get_coverage () const { return this+coverage; }
667 bool would_apply (hb_would_apply_context_t *c) const
668 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
671 get_glyph_alternates (hb_codepoint_t gid,
672 unsigned start_offset,
673 unsigned *alternate_count /* IN/OUT. May be NULL. */,
674 hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
675 { return (this+alternateSet[(this+coverage).get_coverage (gid)])
676 .get_alternates (start_offset, alternate_count, alternate_glyphs); }
678 bool apply (hb_ot_apply_context_t *c) const
682 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
683 if (likely (index == NOT_COVERED)) return_trace (false);
685 return_trace ((this+alternateSet[index]).apply (c));
688 bool serialize (hb_serialize_context_t *c,
689 hb_sorted_array_t<const HBGlyphID16> glyphs,
690 hb_array_t<const unsigned int> alternate_len_list,
691 hb_array_t<const HBGlyphID16> alternate_glyphs_list)
693 TRACE_SERIALIZE (this);
694 if (unlikely (!c->extend_min (this))) return_trace (false);
695 if (unlikely (!alternateSet.serialize (c, glyphs.length))) return_trace (false);
696 for (unsigned int i = 0; i < glyphs.length; i++)
698 unsigned int alternate_len = alternate_len_list[i];
699 if (unlikely (!alternateSet[i]
700 .serialize_serialize (c, alternate_glyphs_list.sub_array (0, alternate_len))))
701 return_trace (false);
702 alternate_glyphs_list += alternate_len;
704 return_trace (coverage.serialize_serialize (c, glyphs));
707 bool subset (hb_subset_context_t *c) const
710 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
711 const hb_map_t &glyph_map = *c->plan->glyph_map;
713 auto *out = c->serializer->start_embed (*this);
714 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
715 out->format = format;
717 hb_sorted_vector_t<hb_codepoint_t> new_coverage;
718 + hb_zip (this+coverage, alternateSet)
719 | hb_filter (glyphset, hb_first)
720 | hb_filter (subset_offset_array (c, out->alternateSet, this), hb_second)
723 | hb_sink (new_coverage)
725 out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
726 return_trace (bool (new_coverage));
729 bool sanitize (hb_sanitize_context_t *c) const
731 TRACE_SANITIZE (this);
732 return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
736 HBUINT16 format; /* Format identifier--format = 1 */
738 coverage; /* Offset to Coverage table--from
739 * beginning of Substitution table */
740 Array16OfOffset16To<AlternateSet>
741 alternateSet; /* Array of AlternateSet tables
742 * ordered by Coverage Index */
744 DEFINE_SIZE_ARRAY (6, alternateSet);
747 struct AlternateSubst
749 bool serialize (hb_serialize_context_t *c,
750 hb_sorted_array_t<const HBGlyphID16> glyphs,
751 hb_array_t<const unsigned int> alternate_len_list,
752 hb_array_t<const HBGlyphID16> alternate_glyphs_list)
754 TRACE_SERIALIZE (this);
755 if (unlikely (!c->extend_min (u.format))) return_trace (false);
756 unsigned int format = 1;
759 case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list));
760 default:return_trace (false);
764 template <typename context_t, typename ...Ts>
765 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
767 TRACE_DISPATCH (this, u.format);
768 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
770 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
771 default:return_trace (c->default_return_value ());
777 HBUINT16 format; /* Format identifier */
778 AlternateSubstFormat1 format1;
785 bool intersects (const hb_set_t *glyphs) const
786 { return hb_all (component, glyphs); }
788 void closure (hb_closure_context_t *c) const
790 if (!intersects (c->glyphs)) return;
791 c->output->add (ligGlyph);
794 void collect_glyphs (hb_collect_glyphs_context_t *c) const
796 c->input->add_array (component.arrayZ, component.get_length ());
797 c->output->add (ligGlyph);
800 bool would_apply (hb_would_apply_context_t *c) const
802 if (c->len != component.lenP1)
805 for (unsigned int i = 1; i < c->len; i++)
806 if (likely (c->glyphs[i] != component[i]))
812 bool apply (hb_ot_apply_context_t *c) const
815 unsigned int count = component.lenP1;
817 if (unlikely (!count)) return_trace (false);
819 /* Special-case to make it in-place and not consider this
820 * as a "ligated" substitution. */
821 if (unlikely (count == 1))
823 c->replace_glyph (ligGlyph);
827 unsigned int total_component_count = 0;
829 unsigned int match_end = 0;
830 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
832 if (likely (!match_input (c, count,
838 &total_component_count)))
840 c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
841 return_trace (false);
849 total_component_count);
854 template <typename Iterator,
855 hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
856 bool serialize (hb_serialize_context_t *c,
857 hb_codepoint_t ligature,
858 Iterator components /* Starting from second */)
860 TRACE_SERIALIZE (this);
861 if (unlikely (!c->extend_min (this))) return_trace (false);
863 if (unlikely (!component.serialize (c, components))) return_trace (false);
867 bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
870 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
871 const hb_map_t &glyph_map = *c->plan->glyph_map;
873 if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
874 // Ensure Coverage table is always packed after this.
875 c->serializer->add_virtual_link (coverage_idx);
878 + hb_iter (component)
882 auto *out = c->serializer->start_embed (*this);
883 return_trace (out->serialize (c->serializer,
889 bool sanitize (hb_sanitize_context_t *c) const
891 TRACE_SANITIZE (this);
892 return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
896 HBGlyphID16 ligGlyph; /* GlyphID of ligature to substitute */
897 HeadlessArrayOf<HBGlyphID16>
898 component; /* Array of component GlyphIDs--start
899 * with the second component--ordered
900 * in writing direction */
902 DEFINE_SIZE_ARRAY (4, component);
907 bool intersects (const hb_set_t *glyphs) const
911 | hb_map (hb_add (this))
912 | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); })
917 void closure (hb_closure_context_t *c) const
920 | hb_map (hb_add (this))
921 | hb_apply ([c] (const Ligature &_) { _.closure (c); })
925 void collect_glyphs (hb_collect_glyphs_context_t *c) const
928 | hb_map (hb_add (this))
929 | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); })
933 bool would_apply (hb_would_apply_context_t *c) const
937 | hb_map (hb_add (this))
938 | hb_map ([c] (const Ligature &_) { return _.would_apply (c); })
943 bool apply (hb_ot_apply_context_t *c) const
946 unsigned int num_ligs = ligature.len;
947 for (unsigned int i = 0; i < num_ligs; i++)
949 const Ligature &lig = this+ligature[i];
950 if (lig.apply (c)) return_trace (true);
953 return_trace (false);
956 bool serialize (hb_serialize_context_t *c,
957 hb_array_t<const HBGlyphID16> ligatures,
958 hb_array_t<const unsigned int> component_count_list,
959 hb_array_t<const HBGlyphID16> &component_list /* Starting from second for each ligature */)
961 TRACE_SERIALIZE (this);
962 if (unlikely (!c->extend_min (this))) return_trace (false);
963 if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false);
964 for (unsigned int i = 0; i < ligatures.length; i++)
966 unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0);
967 if (unlikely (!ligature[i].serialize_serialize (c,
969 component_list.sub_array (0, component_count))))
970 return_trace (false);
971 component_list += component_count;
976 bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
979 auto *out = c->serializer->start_embed (*this);
980 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
983 | hb_filter (subset_offset_array (c, out->ligature, this, coverage_idx))
987 if (bool (out->ligature))
988 // Ensure Coverage table is always packed after this.
989 c->serializer->add_virtual_link (coverage_idx);
991 return_trace (bool (out->ligature));
994 bool sanitize (hb_sanitize_context_t *c) const
996 TRACE_SANITIZE (this);
997 return_trace (ligature.sanitize (c, this));
1001 Array16OfOffset16To<Ligature>
1002 ligature; /* Array LigatureSet tables
1003 * ordered by preference */
1005 DEFINE_SIZE_ARRAY (2, ligature);
1008 struct LigatureSubstFormat1
1010 bool intersects (const hb_set_t *glyphs) const
1013 + hb_zip (this+coverage, ligatureSet)
1014 | hb_filter (*glyphs, hb_first)
1015 | hb_map (hb_second)
1016 | hb_map ([this, glyphs] (const Offset16To<LigatureSet> &_)
1017 { return (this+_).intersects (glyphs); })
1022 bool may_have_non_1to1 () const
1025 void closure (hb_closure_context_t *c) const
1027 + hb_zip (this+coverage, ligatureSet)
1028 | hb_filter (c->parent_active_glyphs (), hb_first)
1029 | hb_map (hb_second)
1030 | hb_map (hb_add (this))
1031 | hb_apply ([c] (const LigatureSet &_) { _.closure (c); })
1036 void closure_lookups (hb_closure_lookups_context_t *c) const {}
1038 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1040 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
1042 + hb_zip (this+coverage, ligatureSet)
1043 | hb_map (hb_second)
1044 | hb_map (hb_add (this))
1045 | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); })
1049 const Coverage &get_coverage () const { return this+coverage; }
1051 bool would_apply (hb_would_apply_context_t *c) const
1053 unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
1054 if (likely (index == NOT_COVERED)) return false;
1056 const LigatureSet &lig_set = this+ligatureSet[index];
1057 return lig_set.would_apply (c);
1060 bool apply (hb_ot_apply_context_t *c) const
1064 unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
1065 if (likely (index == NOT_COVERED)) return_trace (false);
1067 const LigatureSet &lig_set = this+ligatureSet[index];
1068 return_trace (lig_set.apply (c));
1071 bool serialize (hb_serialize_context_t *c,
1072 hb_sorted_array_t<const HBGlyphID16> first_glyphs,
1073 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1074 hb_array_t<const HBGlyphID16> ligatures_list,
1075 hb_array_t<const unsigned int> component_count_list,
1076 hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
1078 TRACE_SERIALIZE (this);
1079 if (unlikely (!c->extend_min (this))) return_trace (false);
1080 if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false);
1081 for (unsigned int i = 0; i < first_glyphs.length; i++)
1083 unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
1084 if (unlikely (!ligatureSet[i]
1085 .serialize_serialize (c,
1086 ligatures_list.sub_array (0, ligature_count),
1087 component_count_list.sub_array (0, ligature_count),
1088 component_list))) return_trace (false);
1089 ligatures_list += ligature_count;
1090 component_count_list += ligature_count;
1092 return_trace (coverage.serialize_serialize (c, first_glyphs));
1095 bool subset (hb_subset_context_t *c) const
1097 TRACE_SUBSET (this);
1098 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
1099 const hb_map_t &glyph_map = *c->plan->glyph_map;
1101 auto *out = c->serializer->start_embed (*this);
1102 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
1103 out->format = format;
1105 // Due to a bug in some older versions of windows 7 the Coverage table must be
1106 // packed after the LigatureSet and Ligature tables, so serialize Coverage first
1107 // which places it last in the packed order.
1108 hb_set_t new_coverage;
1109 + hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this)))
1110 | hb_filter (glyphset, hb_first)
1111 | hb_filter ([&] (const LigatureSet& _) {
1112 return _.intersects (&glyphset);
1115 | hb_sink (new_coverage);
1117 if (!c->serializer->push<Coverage> ()
1118 ->serialize (c->serializer,
1119 + new_coverage.iter () | hb_map_retains_sorting (glyph_map)))
1121 c->serializer->pop_discard ();
1122 return_trace (false);
1125 unsigned coverage_idx = c->serializer->pop_pack ();
1126 c->serializer->add_link (out->coverage, coverage_idx);
1128 + hb_zip (this+coverage, ligatureSet)
1129 | hb_filter (new_coverage, hb_first)
1130 | hb_map (hb_second)
1131 // to ensure that the repacker always orders the coverage table after the LigatureSet
1132 // and LigatureSubtable's they will be linked to the Coverage table via a virtual link
1133 // the coverage table object idx is passed down to facilitate this.
1134 | hb_apply (subset_offset_array (c, out->ligatureSet, this, coverage_idx))
1137 return_trace (bool (new_coverage));
1140 bool sanitize (hb_sanitize_context_t *c) const
1142 TRACE_SANITIZE (this);
1143 return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
1147 HBUINT16 format; /* Format identifier--format = 1 */
1148 Offset16To<Coverage>
1149 coverage; /* Offset to Coverage table--from
1150 * beginning of Substitution table */
1151 Array16OfOffset16To<LigatureSet>
1152 ligatureSet; /* Array LigatureSet tables
1153 * ordered by Coverage Index */
1155 DEFINE_SIZE_ARRAY (6, ligatureSet);
1158 struct LigatureSubst
1160 bool serialize (hb_serialize_context_t *c,
1161 hb_sorted_array_t<const HBGlyphID16> first_glyphs,
1162 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1163 hb_array_t<const HBGlyphID16> ligatures_list,
1164 hb_array_t<const unsigned int> component_count_list,
1165 hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
1167 TRACE_SERIALIZE (this);
1168 if (unlikely (!c->extend_min (u.format))) return_trace (false);
1169 unsigned int format = 1;
1172 case 1: return_trace (u.format1.serialize (c,
1174 ligature_per_first_glyph_count_list,
1176 component_count_list,
1178 default:return_trace (false);
1182 template <typename context_t, typename ...Ts>
1183 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1185 TRACE_DISPATCH (this, u.format);
1186 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1188 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
1189 default:return_trace (c->default_return_value ());
1195 HBUINT16 format; /* Format identifier */
1196 LigatureSubstFormat1 format1;
1201 struct ContextSubst : Context {};
1203 struct ChainContextSubst : ChainContext {};
1205 struct ExtensionSubst : Extension<ExtensionSubst>
1207 typedef struct SubstLookupSubTable SubTable;
1208 bool is_reverse () const;
1212 struct ReverseChainSingleSubstFormat1
1214 bool intersects (const hb_set_t *glyphs) const
1216 if (!(this+coverage).intersects (glyphs))
1219 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1223 count = backtrack.len;
1224 for (unsigned int i = 0; i < count; i++)
1225 if (!(this+backtrack[i]).intersects (glyphs))
1228 count = lookahead.len;
1229 for (unsigned int i = 0; i < count; i++)
1230 if (!(this+lookahead[i]).intersects (glyphs))
1236 bool may_have_non_1to1 () const
1239 void closure (hb_closure_context_t *c) const
1241 if (!intersects (c->glyphs)) return;
1243 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1244 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1246 + hb_zip (this+coverage, substitute)
1247 | hb_filter (c->parent_active_glyphs (), hb_first)
1248 | hb_map (hb_second)
1249 | hb_sink (c->output)
1253 void closure_lookups (hb_closure_lookups_context_t *c) const {}
1255 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1257 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
1261 count = backtrack.len;
1262 for (unsigned int i = 0; i < count; i++)
1263 if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return;
1265 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1266 count = lookahead.len;
1267 for (unsigned int i = 0; i < count; i++)
1268 if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return;
1270 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1271 count = substitute.len;
1272 c->output->add_array (substitute.arrayZ, substitute.len);
1275 const Coverage &get_coverage () const { return this+coverage; }
1277 bool would_apply (hb_would_apply_context_t *c) const
1278 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
1280 bool apply (hb_ot_apply_context_t *c) const
1283 if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
1284 return_trace (false); /* No chaining to this type */
1286 unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
1287 if (likely (index == NOT_COVERED)) return_trace (false);
1289 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1290 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1292 if (unlikely (index >= substitute.len)) return_trace (false);
1294 unsigned int start_index = 0, end_index = 0;
1295 if (match_backtrack (c,
1296 backtrack.len, (HBUINT16 *) backtrack.arrayZ,
1297 match_coverage, this,
1300 lookahead.len, (HBUINT16 *) lookahead.arrayZ,
1301 match_coverage, this,
1302 c->buffer->idx + 1, &end_index))
1304 c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
1305 c->replace_glyph_inplace (substitute[index]);
1306 /* Note: We DON'T decrease buffer->idx. The main loop does it
1307 * for us. This is useful for preventing surprises if someone
1308 * calls us through a Context lookup. */
1309 return_trace (true);
1313 c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index);
1314 return_trace (false);
1318 template<typename Iterator,
1319 hb_requires (hb_is_iterator (Iterator))>
1320 bool serialize_coverage_offset_array (hb_subset_context_t *c, Iterator it) const
1322 TRACE_SERIALIZE (this);
1323 auto *out = c->serializer->start_embed<Array16OfOffset16To<Coverage>> ();
1325 if (unlikely (!c->serializer->allocate_size<HBUINT16> (HBUINT16::static_size)))
1326 return_trace (false);
1328 for (auto& offset : it) {
1329 auto *o = out->serialize_append (c->serializer);
1330 if (unlikely (!o) || !o->serialize_subset (c, offset, this))
1331 return_trace (false);
1334 return_trace (true);
1337 template<typename Iterator, typename BacktrackIterator, typename LookaheadIterator,
1338 hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_pair_t)),
1339 hb_requires (hb_is_iterator (BacktrackIterator)),
1340 hb_requires (hb_is_iterator (LookaheadIterator))>
1341 bool serialize (hb_subset_context_t *c,
1342 Iterator coverage_subst_iter,
1343 BacktrackIterator backtrack_iter,
1344 LookaheadIterator lookahead_iter) const
1346 TRACE_SERIALIZE (this);
1348 auto *out = c->serializer->start_embed (this);
1349 if (unlikely (!c->serializer->check_success (out))) return_trace (false);
1350 if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
1351 if (unlikely (!c->serializer->embed (this->coverage))) return_trace (false);
1353 if (!serialize_coverage_offset_array (c, backtrack_iter)) return_trace (false);
1354 if (!serialize_coverage_offset_array (c, lookahead_iter)) return_trace (false);
1356 auto *substitute_out = c->serializer->start_embed<Array16Of<HBGlyphID16>> ();
1358 + coverage_subst_iter
1359 | hb_map (hb_second)
1363 + coverage_subst_iter
1364 | hb_map_retains_sorting (hb_first)
1366 if (unlikely (! c->serializer->check_success (substitute_out->serialize (c->serializer, substitutes))))
1367 return_trace (false);
1369 if (unlikely (!out->coverage.serialize_serialize (c->serializer, glyphs)))
1370 return_trace (false);
1371 return_trace (true);
1374 bool subset (hb_subset_context_t *c) const
1376 TRACE_SUBSET (this);
1377 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
1378 const hb_map_t &glyph_map = *c->plan->glyph_map;
1380 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1381 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1384 + hb_zip (this+coverage, substitute)
1385 | hb_filter (glyphset, hb_first)
1386 | hb_filter (glyphset, hb_second)
1387 | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const HBGlyphID16 &> p) -> hb_codepoint_pair_t
1388 { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
1391 return_trace (bool (it) && serialize (c, it, backtrack.iter (), lookahead.iter ()));
1394 bool sanitize (hb_sanitize_context_t *c) const
1396 TRACE_SANITIZE (this);
1397 if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
1398 return_trace (false);
1399 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1400 if (!lookahead.sanitize (c, this))
1401 return_trace (false);
1402 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1403 return_trace (substitute.sanitize (c));
1407 HBUINT16 format; /* Format identifier--format = 1 */
1408 Offset16To<Coverage>
1409 coverage; /* Offset to Coverage table--from
1410 * beginning of table */
1411 Array16OfOffset16To<Coverage>
1412 backtrack; /* Array of coverage tables
1413 * in backtracking sequence, in glyph
1415 Array16OfOffset16To<Coverage>
1416 lookaheadX; /* Array of coverage tables
1417 * in lookahead sequence, in glyph
1419 Array16Of<HBGlyphID16>
1420 substituteX; /* Array of substitute
1421 * GlyphIDs--ordered by Coverage Index */
1423 DEFINE_SIZE_MIN (10);
1426 struct ReverseChainSingleSubst
1428 template <typename context_t, typename ...Ts>
1429 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1431 TRACE_DISPATCH (this, u.format);
1432 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1434 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
1435 default:return_trace (c->default_return_value ());
1441 HBUINT16 format; /* Format identifier */
1442 ReverseChainSingleSubstFormat1 format1;
1452 struct SubstLookupSubTable
1454 friend struct Lookup;
1455 friend struct SubstLookup;
1465 ReverseChainSingle = 8
1468 template <typename context_t, typename ...Ts>
1469 typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const
1471 TRACE_DISPATCH (this, lookup_type);
1472 switch (lookup_type) {
1473 case Single: return_trace (u.single.dispatch (c, std::forward<Ts> (ds)...));
1474 case Multiple: return_trace (u.multiple.dispatch (c, std::forward<Ts> (ds)...));
1475 case Alternate: return_trace (u.alternate.dispatch (c, std::forward<Ts> (ds)...));
1476 case Ligature: return_trace (u.ligature.dispatch (c, std::forward<Ts> (ds)...));
1477 case Context: return_trace (u.context.dispatch (c, std::forward<Ts> (ds)...));
1478 case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward<Ts> (ds)...));
1479 case Extension: return_trace (u.extension.dispatch (c, std::forward<Ts> (ds)...));
1480 case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, std::forward<Ts> (ds)...));
1481 default: return_trace (c->default_return_value ());
1485 bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const
1487 hb_intersects_context_t c (glyphs);
1488 return dispatch (&c, lookup_type);
1494 MultipleSubst multiple;
1495 AlternateSubst alternate;
1496 LigatureSubst ligature;
1497 ContextSubst context;
1498 ChainContextSubst chainContext;
1499 ExtensionSubst extension;
1500 ReverseChainSingleSubst reverseChainContextSingle;
1503 DEFINE_SIZE_MIN (0);
1507 struct SubstLookup : Lookup
1509 typedef SubstLookupSubTable SubTable;
1511 const SubTable& get_subtable (unsigned int i) const
1512 { return Lookup::get_subtable<SubTable> (i); }
1514 static inline bool lookup_type_is_reverse (unsigned int lookup_type)
1515 { return lookup_type == SubTable::ReverseChainSingle; }
1517 bool is_reverse () const
1519 unsigned int type = get_type ();
1520 if (unlikely (type == SubTable::Extension))
1521 return reinterpret_cast<const ExtensionSubst &> (get_subtable (0)).is_reverse ();
1522 return lookup_type_is_reverse (type);
1525 bool may_have_non_1to1 () const
1527 hb_have_non_1to1_context_t c;
1528 return dispatch (&c);
1531 bool apply (hb_ot_apply_context_t *c) const
1534 return_trace (dispatch (c));
1537 bool intersects (const hb_set_t *glyphs) const
1539 hb_intersects_context_t c (glyphs);
1540 return dispatch (&c);
1543 hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
1545 if (!c->should_visit_lookup (this_index))
1546 return hb_closure_context_t::default_return_value ();
1548 c->set_recurse_func (dispatch_closure_recurse_func);
1550 hb_closure_context_t::return_t ret = dispatch (c);
1557 hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const
1559 if (c->is_lookup_visited (this_index))
1560 return hb_closure_lookups_context_t::default_return_value ();
1562 c->set_lookup_visited (this_index);
1563 if (!intersects (c->glyphs))
1565 c->set_lookup_inactive (this_index);
1566 return hb_closure_lookups_context_t::default_return_value ();
1569 c->set_recurse_func (dispatch_closure_lookups_recurse_func);
1571 hb_closure_lookups_context_t::return_t ret = dispatch (c);
1575 hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
1577 c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
1578 return dispatch (c);
1581 template <typename set_t>
1582 void collect_coverage (set_t *glyphs) const
1584 hb_collect_coverage_context_t<set_t> c (glyphs);
1588 bool would_apply (hb_would_apply_context_t *c,
1589 const hb_ot_layout_lookup_accelerator_t *accel) const
1591 if (unlikely (!c->len)) return false;
1592 if (!accel->may_have (c->glyphs[0])) return false;
1593 return dispatch (c);
1596 static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
1598 bool serialize_single (hb_serialize_context_t *c,
1599 uint32_t lookup_props,
1600 hb_sorted_array_t<const HBGlyphID16> glyphs,
1601 hb_array_t<const HBGlyphID16> substitutes)
1603 TRACE_SERIALIZE (this);
1604 if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
1605 if (c->push<SubTable> ()->u.single.serialize (c, hb_zip (glyphs, substitutes)))
1607 c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
1608 return_trace (true);
1611 return_trace (false);
1614 bool serialize_multiple (hb_serialize_context_t *c,
1615 uint32_t lookup_props,
1616 hb_sorted_array_t<const HBGlyphID16> glyphs,
1617 hb_array_t<const unsigned int> substitute_len_list,
1618 hb_array_t<const HBGlyphID16> substitute_glyphs_list)
1620 TRACE_SERIALIZE (this);
1621 if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
1622 if (c->push<SubTable> ()->u.multiple.
1625 substitute_len_list,
1626 substitute_glyphs_list))
1628 c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
1629 return_trace (true);
1632 return_trace (false);
1635 bool serialize_alternate (hb_serialize_context_t *c,
1636 uint32_t lookup_props,
1637 hb_sorted_array_t<const HBGlyphID16> glyphs,
1638 hb_array_t<const unsigned int> alternate_len_list,
1639 hb_array_t<const HBGlyphID16> alternate_glyphs_list)
1641 TRACE_SERIALIZE (this);
1642 if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
1644 if (c->push<SubTable> ()->u.alternate.
1648 alternate_glyphs_list))
1650 c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
1651 return_trace (true);
1654 return_trace (false);
1657 bool serialize_ligature (hb_serialize_context_t *c,
1658 uint32_t lookup_props,
1659 hb_sorted_array_t<const HBGlyphID16> first_glyphs,
1660 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1661 hb_array_t<const HBGlyphID16> ligatures_list,
1662 hb_array_t<const unsigned int> component_count_list,
1663 hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
1665 TRACE_SERIALIZE (this);
1666 if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
1667 if (c->push<SubTable> ()->u.ligature.
1670 ligature_per_first_glyph_count_list,
1672 component_count_list,
1675 c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
1676 return_trace (true);
1679 return_trace (false);
1682 template <typename context_t>
1683 static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
1685 static inline typename hb_closure_context_t::return_t closure_glyphs_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index);
1687 static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index)
1689 if (!c->should_visit_lookup (lookup_index))
1690 return hb_empty_t ();
1692 hb_closure_context_t::return_t ret = closure_glyphs_recurse_func (c, lookup_index, covered_seq_indices, seq_index, end_index);
1694 /* While in theory we should flush here, it will cause timeouts because a recursive
1695 * lookup can keep growing the glyph set. Skip, and outer loop will retry up to
1696 * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */
1702 HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned lookup_index);
1704 template <typename context_t, typename ...Ts>
1705 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1706 { return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); }
1708 bool subset (hb_subset_context_t *c) const
1709 { return Lookup::subset<SubTable> (c); }
1711 bool sanitize (hb_sanitize_context_t *c) const
1712 { return Lookup::sanitize<SubTable> (c); }
1716 * GSUB -- Glyph Substitution
1717 * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
1720 struct GSUB : GSUBGPOS
1722 static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB;
1724 const SubstLookup& get_lookup (unsigned int i) const
1725 { return static_cast<const SubstLookup &> (GSUBGPOS::get_lookup (i)); }
1727 bool subset (hb_subset_context_t *c) const
1729 hb_subset_layout_context_t l (c, tableTag, c->plan->gsub_lookups, c->plan->gsub_langsys, c->plan->gsub_features);
1730 return GSUBGPOS::subset<SubstLookup> (&l);
1733 bool sanitize (hb_sanitize_context_t *c) const
1734 { return GSUBGPOS::sanitize<SubstLookup> (c); }
1736 HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
1737 hb_face_t *face) const;
1739 void closure_lookups (hb_face_t *face,
1740 const hb_set_t *glyphs,
1741 hb_set_t *lookup_indexes /* IN/OUT */) const
1742 { GSUBGPOS::closure_lookups<SubstLookup> (face, glyphs, lookup_indexes); }
1744 typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
1748 struct GSUB_accelerator_t : GSUB::accelerator_t {
1749 GSUB_accelerator_t (hb_face_t *face) : GSUB::accelerator_t (face) {}
1753 /* Out-of-class implementation for methods recursing */
1755 #ifndef HB_NO_OT_LAYOUT
1756 /*static*/ inline bool ExtensionSubst::is_reverse () const
1758 return SubstLookup::lookup_type_is_reverse (get_type ());
1760 template <typename context_t>
1761 /*static*/ typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
1763 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1764 return l.dispatch (c);
1767 /*static*/ typename hb_closure_context_t::return_t SubstLookup::closure_glyphs_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index)
1769 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1770 if (l.may_have_non_1to1 ())
1771 hb_set_add_range (covered_seq_indices, seq_index, end_index);
1772 return l.dispatch (c);
1775 /*static*/ inline hb_closure_lookups_context_t::return_t SubstLookup::dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index)
1777 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (this_index);
1778 return l.closure_lookups (c, this_index);
1781 /*static*/ bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
1783 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1784 unsigned int saved_lookup_props = c->lookup_props;
1785 unsigned int saved_lookup_index = c->lookup_index;
1786 c->set_lookup_index (lookup_index);
1787 c->set_lookup_props (l.get_props ());
1788 bool ret = l.dispatch (c);
1789 c->set_lookup_index (saved_lookup_index);
1790 c->set_lookup_props (saved_lookup_props);
1796 } /* namespace OT */
1799 #endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */