1#ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH
2#define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH
3
4#include "Common.hh"
5
6namespace OT {
7namespace Layout {
8namespace GSUB_impl {
9
10template <typename Types>
11struct SingleSubstFormat1_3
12{
13 protected:
14 HBUINT16 format; /* Format identifier--format = 1 */
15 typename Types::template OffsetTo<Coverage>
16 coverage; /* Offset to Coverage table--from
17 * beginning of Substitution table */
18 typename Types::HBUINT
19 deltaGlyphID; /* Add to original GlyphID to get
20 * substitute GlyphID, modulo 0x10000 */
21
22 public:
23 DEFINE_SIZE_STATIC (2 + 2 * Types::size);
24
25 bool sanitize (hb_sanitize_context_t *c) const
26 {
27 TRACE_SANITIZE (this);
28 return_trace (c->check_struct (this) &&
29 coverage.sanitize (c, this) &&
30 /* The coverage table may use a range to represent a set
31 * of glyphs, which means a small number of bytes can
32 * generate a large glyph set. Manually modify the
33 * sanitizer max ops to take this into account.
34 *
35 * Note: This check *must* be right after coverage sanitize. */
36 c->check_ops ((this + coverage).get_population () >> 1));
37 }
38
39 hb_codepoint_t get_mask () const
40 { return (1 << (8 * Types::size)) - 1; }
41
42 bool intersects (const hb_set_t *glyphs) const
43 { return (this+coverage).intersects (glyphs); }
44
45 bool may_have_non_1to1 () const
46 { return false; }
47
48 void closure (hb_closure_context_t *c) const
49 {
50 hb_codepoint_t d = deltaGlyphID;
51 hb_codepoint_t mask = get_mask ();
52
53 /* Help fuzzer avoid this function as much. */
54 unsigned pop = (this+coverage).get_population ();
55 if (pop >= mask)
56 return;
57
58 hb_set_t intersection;
59 (this+coverage).intersect_set (c->parent_active_glyphs (), intersection);
60
61 /* In degenerate fuzzer-found fonts, but not real fonts,
62 * this table can keep adding new glyphs in each round of closure.
63 * Refuse to close-over, if it maps glyph range to overlapping range. */
64 hb_codepoint_t min_before = intersection.get_min ();
65 hb_codepoint_t max_before = intersection.get_max ();
66 hb_codepoint_t min_after = (min_before + d) & mask;
67 hb_codepoint_t max_after = (max_before + d) & mask;
68 if (intersection.get_population () == max_before - min_before + 1 &&
69 ((min_before <= min_after && min_after <= max_before) ||
70 (min_before <= max_after && max_after <= max_before)))
71 return;
72
73 + hb_iter (intersection)
74 | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; })
75 | hb_sink (c->output)
76 ;
77 }
78
79 void closure_lookups (hb_closure_lookups_context_t *c) const {}
80
81 void collect_glyphs (hb_collect_glyphs_context_t *c) const
82 {
83 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
84 hb_codepoint_t d = deltaGlyphID;
85 hb_codepoint_t mask = get_mask ();
86
87 + hb_iter (this+coverage)
88 | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; })
89 | hb_sink (c->output)
90 ;
91 }
92
93 const Coverage &get_coverage () const { return this+coverage; }
94
95 bool would_apply (hb_would_apply_context_t *c) const
96 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
97
98 unsigned
99 get_glyph_alternates (hb_codepoint_t glyph_id,
100 unsigned start_offset,
101 unsigned *alternate_count /* IN/OUT. May be NULL. */,
102 hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
103 {
104 unsigned int index = (this+coverage).get_coverage (glyph_id);
105 if (likely (index == NOT_COVERED))
106 {
107 if (alternate_count)
108 *alternate_count = 0;
109 return 0;
110 }
111
112 if (alternate_count && *alternate_count)
113 {
114 hb_codepoint_t d = deltaGlyphID;
115 hb_codepoint_t mask = get_mask ();
116
117 glyph_id = (glyph_id + d) & mask;
118
119 *alternate_glyphs = glyph_id;
120 *alternate_count = 1;
121 }
122
123 return 1;
124 }
125
126 bool apply (hb_ot_apply_context_t *c) const
127 {
128 TRACE_APPLY (this);
129 hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
130 unsigned int index = (this+coverage).get_coverage (glyph_id);
131 if (likely (index == NOT_COVERED)) return_trace (false);
132
133 hb_codepoint_t d = deltaGlyphID;
134 hb_codepoint_t mask = get_mask ();
135
136 glyph_id = (glyph_id + d) & mask;
137
138 if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
139 {
140 c->buffer->sync_so_far ();
141 c->buffer->message (c->font,
142 "replacing glyph at %u (single substitution)",
143 c->buffer->idx);
144 }
145
146 c->replace_glyph (glyph_id);
147
148 if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
149 {
150 c->buffer->message (c->font,
151 "replaced glyph at %u (single substitution)",
152 c->buffer->idx - 1u);
153 }
154
155 return_trace (true);
156 }
157
158 template<typename Iterator,
159 hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
160 bool serialize (hb_serialize_context_t *c,
161 Iterator glyphs,
162 unsigned delta)
163 {
164 TRACE_SERIALIZE (this);
165 if (unlikely (!c->extend_min (this))) return_trace (false);
166 if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false);
167 c->check_assign (deltaGlyphID, delta, HB_SERIALIZE_ERROR_INT_OVERFLOW);
168 return_trace (true);
169 }
170
171 bool subset (hb_subset_context_t *c) const
172 {
173 TRACE_SUBSET (this);
174 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
175 const hb_map_t &glyph_map = *c->plan->glyph_map;
176
177 hb_codepoint_t d = deltaGlyphID;
178 hb_codepoint_t mask = get_mask ();
179
180 hb_set_t intersection;
181 (this+coverage).intersect_set (glyphset, intersection);
182
183 auto it =
184 + hb_iter (intersection)
185 | hb_map_retains_sorting ([d, mask] (hb_codepoint_t g) {
186 return hb_codepoint_pair_t (g,
187 (g + d) & mask); })
188 | hb_filter (glyphset, hb_second)
189 | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t
190 { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
191 ;
192
193 bool ret = bool (it);
194 SingleSubst_serialize (c->serializer, it);
195 return_trace (ret);
196 }
197};
198
199}
200}
201}
202
203
204#endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH */
205