1#ifndef OT_LAYOUT_GSUB_LIGATURESET_HH
2#define OT_LAYOUT_GSUB_LIGATURESET_HH
3
4#include "Common.hh"
5#include "Ligature.hh"
6
7namespace OT {
8namespace Layout {
9namespace GSUB_impl {
10
11template <typename Types>
12struct LigatureSet
13{
14 protected:
15 Array16OfOffset16To<Ligature<Types>>
16 ligature; /* Array LigatureSet tables
17 * ordered by preference */
18 public:
19 DEFINE_SIZE_ARRAY (2, ligature);
20
21 bool sanitize (hb_sanitize_context_t *c) const
22 {
23 TRACE_SANITIZE (this);
24 return_trace (ligature.sanitize (c, this));
25 }
26
27 bool intersects (const hb_set_t *glyphs) const
28 {
29 return
30 + hb_iter (ligature)
31 | hb_map (hb_add (this))
32 | hb_map ([glyphs] (const Ligature<Types> &_) { return _.intersects (glyphs); })
33 | hb_any
34 ;
35 }
36
37 bool intersects_lig_glyph (const hb_set_t *glyphs) const
38 {
39 return
40 + hb_iter (ligature)
41 | hb_map (hb_add (this))
42 | hb_map ([glyphs] (const Ligature<Types> &_) {
43 return _.intersects_lig_glyph (glyphs) && _.intersects (glyphs);
44 })
45 | hb_any
46 ;
47 }
48
49 void closure (hb_closure_context_t *c) const
50 {
51 + hb_iter (ligature)
52 | hb_map (hb_add (this))
53 | hb_apply ([c] (const Ligature<Types> &_) { _.closure (c); })
54 ;
55 }
56
57 void collect_glyphs (hb_collect_glyphs_context_t *c) const
58 {
59 + hb_iter (ligature)
60 | hb_map (hb_add (this))
61 | hb_apply ([c] (const Ligature<Types> &_) { _.collect_glyphs (c); })
62 ;
63 }
64
65 bool would_apply (hb_would_apply_context_t *c) const
66 {
67 return
68 + hb_iter (ligature)
69 | hb_map (hb_add (this))
70 | hb_map ([c] (const Ligature<Types> &_) { return _.would_apply (c); })
71 | hb_any
72 ;
73 }
74
75 bool apply (hb_ot_apply_context_t *c) const
76 {
77 TRACE_APPLY (this);
78
79 unsigned int num_ligs = ligature.len;
80
81#ifndef HB_NO_OT_RULESETS_FAST_PATH
82 if (HB_OPTIMIZE_SIZE_VAL || num_ligs <= 4)
83#endif
84 {
85 slow:
86 for (unsigned int i = 0; i < num_ligs; i++)
87 {
88 const auto &lig = this+ligature.arrayZ[i];
89 if (lig.apply (c)) return_trace (true);
90 }
91 return_trace (false);
92 }
93
94 /* This version is optimized for speed by matching the first component
95 * of the ligature here, instead of calling into the ligation code.
96 *
97 * This is replicated in ChainRuleSet and RuleSet. */
98
99 hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
100 skippy_iter.reset (c->buffer->idx);
101 skippy_iter.set_match_func (match_always, nullptr);
102 skippy_iter.set_glyph_data ((HBUINT16 *) nullptr);
103 unsigned unsafe_to;
104 hb_codepoint_t first = (unsigned) -1;
105 bool matched = skippy_iter.next (&unsafe_to);
106 if (likely (matched))
107 {
108 first = c->buffer->info[skippy_iter.idx].codepoint;
109 unsafe_to = skippy_iter.idx + 1;
110
111 if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx]))
112 {
113 /* Can't use the fast path if eg. the next char is a default-ignorable
114 * or other skippable. */
115 goto slow;
116 }
117 }
118 else
119 goto slow;
120
121 bool unsafe_to_concat = false;
122
123 for (unsigned int i = 0; i < num_ligs; i++)
124 {
125 const auto &lig = this+ligature.arrayZ[i];
126 if (unlikely (lig.component.lenP1 <= 1) ||
127 lig.component.arrayZ[0] == first)
128 {
129 if (lig.apply (c))
130 {
131 if (unsafe_to_concat)
132 c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to);
133 return_trace (true);
134 }
135 }
136 else if (likely (lig.component.lenP1 > 1))
137 unsafe_to_concat = true;
138 }
139 if (likely (unsafe_to_concat))
140 c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to);
141
142 return_trace (false);
143 }
144
145 bool serialize (hb_serialize_context_t *c,
146 hb_array_t<const HBGlyphID16> ligatures,
147 hb_array_t<const unsigned int> component_count_list,
148 hb_array_t<const HBGlyphID16> &component_list /* Starting from second for each ligature */)
149 {
150 TRACE_SERIALIZE (this);
151 if (unlikely (!c->extend_min (this))) return_trace (false);
152 if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false);
153 for (unsigned int i = 0; i < ligatures.length; i++)
154 {
155 unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0);
156 if (unlikely (!ligature[i].serialize_serialize (c,
157 ligatures[i],
158 component_list.sub_array (0, component_count))))
159 return_trace (false);
160 component_list += component_count;
161 }
162 return_trace (true);
163 }
164
165 bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
166 {
167 TRACE_SUBSET (this);
168 auto *out = c->serializer->start_embed (*this);
169 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
170
171 + hb_iter (ligature)
172 | hb_filter (subset_offset_array (c, out->ligature, this, coverage_idx))
173 | hb_drain
174 ;
175
176 if (bool (out->ligature))
177 // Ensure Coverage table is always packed after this.
178 c->serializer->add_virtual_link (coverage_idx);
179
180 return_trace (bool (out->ligature));
181 }
182};
183
184}
185}
186}
187
188#endif /* OT_LAYOUT_GSUB_LIGATURESET_HH */
189