1#ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH
2#define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH
3
4#include "Common.hh"
5
6namespace OT {
7namespace Layout {
8namespace GSUB_impl {
9
10template <typename Types>
11struct SingleSubstFormat2_4
12{
13 protected:
14 HBUINT16 format; /* Format identifier--format = 2 */
15 typename Types::template OffsetTo<Coverage>
16 coverage; /* Offset to Coverage table--from
17 * beginning of Substitution table */
18 Array16Of<typename Types::HBGlyphID>
19 substitute; /* Array of substitute
20 * GlyphIDs--ordered by Coverage Index */
21
22 public:
23 DEFINE_SIZE_ARRAY (4 + Types::size, substitute);
24
25 bool sanitize (hb_sanitize_context_t *c) const
26 {
27 TRACE_SANITIZE (this);
28 return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
29 }
30
31 bool intersects (const hb_set_t *glyphs) const
32 { return (this+coverage).intersects (glyphs); }
33
34 bool may_have_non_1to1 () const
35 { return false; }
36
37 void closure (hb_closure_context_t *c) const
38 {
39 auto &cov = this+coverage;
40 auto &glyph_set = c->parent_active_glyphs ();
41
42 if (substitute.len > glyph_set.get_population () * 4)
43 {
44 for (auto g : glyph_set)
45 {
46 unsigned i = cov.get_coverage (g);
47 if (i == NOT_COVERED || i >= substitute.len)
48 continue;
49 c->output->add (substitute.arrayZ[i]);
50 }
51
52 return;
53 }
54
55 + hb_zip (cov, substitute)
56 | hb_filter (glyph_set, hb_first)
57 | hb_map (hb_second)
58 | hb_sink (c->output)
59 ;
60 }
61
62 void closure_lookups (hb_closure_lookups_context_t *c) const {}
63
64 void collect_glyphs (hb_collect_glyphs_context_t *c) const
65 {
66 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
67 + hb_zip (this+coverage, substitute)
68 | hb_map (hb_second)
69 | hb_sink (c->output)
70 ;
71 }
72
73 const Coverage &get_coverage () const { return this+coverage; }
74
75 bool would_apply (hb_would_apply_context_t *c) const
76 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
77
78 unsigned
79 get_glyph_alternates (hb_codepoint_t glyph_id,
80 unsigned start_offset,
81 unsigned *alternate_count /* IN/OUT. May be NULL. */,
82 hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
83 {
84 unsigned int index = (this+coverage).get_coverage (glyph_id);
85 if (likely (index == NOT_COVERED))
86 {
87 if (alternate_count)
88 *alternate_count = 0;
89 return 0;
90 }
91
92 if (alternate_count && *alternate_count)
93 {
94 glyph_id = substitute[index];
95
96 *alternate_glyphs = glyph_id;
97 *alternate_count = 1;
98 }
99
100 return 1;
101 }
102
103 bool apply (hb_ot_apply_context_t *c) const
104 {
105 TRACE_APPLY (this);
106 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
107 if (likely (index == NOT_COVERED)) return_trace (false);
108
109 if (unlikely (index >= substitute.len)) return_trace (false);
110
111 if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
112 {
113 c->buffer->sync_so_far ();
114 c->buffer->message (c->font,
115 "replacing glyph at %u (single substitution)",
116 c->buffer->idx);
117 }
118
119 c->replace_glyph (substitute[index]);
120
121 if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
122 {
123 c->buffer->message (c->font,
124 "replaced glyph at %u (single substitution)",
125 c->buffer->idx - 1u);
126 }
127
128 return_trace (true);
129 }
130
131 template<typename Iterator,
132 hb_requires (hb_is_sorted_source_of (Iterator,
133 hb_codepoint_pair_t))>
134 bool serialize (hb_serialize_context_t *c,
135 Iterator it)
136 {
137 TRACE_SERIALIZE (this);
138 auto substitutes =
139 + it
140 | hb_map (hb_second)
141 ;
142 auto glyphs =
143 + it
144 | hb_map_retains_sorting (hb_first)
145 ;
146 if (unlikely (!c->extend_min (this))) return_trace (false);
147 if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false);
148 if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false);
149 return_trace (true);
150 }
151
152 bool subset (hb_subset_context_t *c) const
153 {
154 TRACE_SUBSET (this);
155 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
156 const hb_map_t &glyph_map = *c->plan->glyph_map;
157
158 auto it =
159 + hb_zip (this+coverage, substitute)
160 | hb_filter (glyphset, hb_first)
161 | hb_filter (glyphset, hb_second)
162 | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const typename Types::HBGlyphID &> p) -> hb_codepoint_pair_t
163 { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
164 ;
165
166 bool ret = bool (it);
167 SingleSubst_serialize (c->serializer, it);
168 return_trace (ret);
169 }
170};
171
172}
173}
174}
175
176#endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH */
177