1#ifndef OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH
2#define OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH
3
4#include "MarkArray.hh"
5
6namespace OT {
7namespace Layout {
8namespace GPOS_impl {
9
10typedef AnchorMatrix BaseArray; /* base-major--
11 * in order of BaseCoverage Index--,
12 * mark-minor--
13 * ordered by class--zero-based. */
14
15template <typename Types>
16struct MarkBasePosFormat1_2
17{
18 protected:
19 HBUINT16 format; /* Format identifier--format = 1 */
20 typename Types::template OffsetTo<Coverage>
21 markCoverage; /* Offset to MarkCoverage table--from
22 * beginning of MarkBasePos subtable */
23 typename Types::template OffsetTo<Coverage>
24 baseCoverage; /* Offset to BaseCoverage table--from
25 * beginning of MarkBasePos subtable */
26 HBUINT16 classCount; /* Number of classes defined for marks */
27 typename Types::template OffsetTo<MarkArray>
28 markArray; /* Offset to MarkArray table--from
29 * beginning of MarkBasePos subtable */
30 typename Types::template OffsetTo<BaseArray>
31 baseArray; /* Offset to BaseArray table--from
32 * beginning of MarkBasePos subtable */
33
34 public:
35 DEFINE_SIZE_STATIC (4 + 4 * Types::size);
36
37 bool sanitize (hb_sanitize_context_t *c) const
38 {
39 TRACE_SANITIZE (this);
40 return_trace (c->check_struct (this) &&
41 markCoverage.sanitize (c, this) &&
42 baseCoverage.sanitize (c, this) &&
43 markArray.sanitize (c, this) &&
44 baseArray.sanitize (c, this, (unsigned int) classCount));
45 }
46
47 bool intersects (const hb_set_t *glyphs) const
48 {
49 return (this+markCoverage).intersects (glyphs) &&
50 (this+baseCoverage).intersects (glyphs);
51 }
52
53 void closure_lookups (hb_closure_lookups_context_t *c) const {}
54
55 void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
56 {
57 + hb_zip (this+markCoverage, this+markArray)
58 | hb_filter (c->glyph_set, hb_first)
59 | hb_map (hb_second)
60 | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); })
61 ;
62
63 hb_map_t klass_mapping;
64 Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping);
65
66 unsigned basecount = (this+baseArray).rows;
67 auto base_iter =
68 + hb_zip (this+baseCoverage, hb_range (basecount))
69 | hb_filter (c->glyph_set, hb_first)
70 | hb_map (hb_second)
71 ;
72
73 hb_sorted_vector_t<unsigned> base_indexes;
74 for (const unsigned row : base_iter)
75 {
76 + hb_range ((unsigned) classCount)
77 | hb_filter (klass_mapping)
78 | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
79 | hb_sink (base_indexes)
80 ;
81 }
82 (this+baseArray).collect_variation_indices (c, base_indexes.iter ());
83 }
84
85 void collect_glyphs (hb_collect_glyphs_context_t *c) const
86 {
87 if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return;
88 if (unlikely (!(this+baseCoverage).collect_coverage (c->input))) return;
89 }
90
91 const Coverage &get_coverage () const { return this+markCoverage; }
92
93 static inline bool accept (hb_buffer_t *buffer, unsigned idx)
94 {
95 /* We only want to attach to the first of a MultipleSubst sequence.
96 * https://github.com/harfbuzz/harfbuzz/issues/740
97 * Reject others...
98 * ...but stop if we find a mark in the MultipleSubst sequence:
99 * https://github.com/harfbuzz/harfbuzz/issues/1020 */
100 return !_hb_glyph_info_multiplied (&buffer->info[idx]) ||
101 0 == _hb_glyph_info_get_lig_comp (&buffer->info[idx]) ||
102 (idx == 0 ||
103 _hb_glyph_info_is_mark (&buffer->info[idx - 1]) ||
104 !_hb_glyph_info_multiplied (&buffer->info[idx - 1]) ||
105 _hb_glyph_info_get_lig_id (&buffer->info[idx]) !=
106 _hb_glyph_info_get_lig_id (&buffer->info[idx - 1]) ||
107 _hb_glyph_info_get_lig_comp (&buffer->info[idx]) !=
108 _hb_glyph_info_get_lig_comp (&buffer->info[idx - 1]) + 1
109 );
110 }
111
112 bool apply (hb_ot_apply_context_t *c) const
113 {
114 TRACE_APPLY (this);
115 hb_buffer_t *buffer = c->buffer;
116 unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint);
117 if (likely (mark_index == NOT_COVERED)) return_trace (false);
118
119 /* Now we search backwards for a non-mark glyph.
120 * We don't use skippy_iter.prev() to avoid O(n^2) behavior. */
121
122 hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
123 skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
124
125 if (c->last_base_until > buffer->idx)
126 {
127 c->last_base_until = 0;
128 c->last_base = -1;
129 }
130 unsigned j;
131 for (j = buffer->idx; j > c->last_base_until; j--)
132 {
133 auto match = skippy_iter.match (buffer->info[j - 1]);
134 if (match == skippy_iter.MATCH)
135 {
136 // https://github.com/harfbuzz/harfbuzz/issues/4124
137 if (!accept (buffer, j - 1) &&
138 NOT_COVERED == (this+baseCoverage).get_coverage (buffer->info[j - 1].codepoint))
139 match = skippy_iter.SKIP;
140 }
141 if (match == skippy_iter.MATCH)
142 {
143 c->last_base = (signed) j - 1;
144 break;
145 }
146 }
147 c->last_base_until = buffer->idx;
148 if (c->last_base == -1)
149 {
150 buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1);
151 return_trace (false);
152 }
153
154 unsigned idx = (unsigned) c->last_base;
155
156 /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
157 //if (!_hb_glyph_info_is_base_glyph (&buffer->info[idx])) { return_trace (false); }
158
159 unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[idx].codepoint);
160 if (base_index == NOT_COVERED)
161 {
162 buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1);
163 return_trace (false);
164 }
165
166 return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, idx));
167 }
168
169 bool subset (hb_subset_context_t *c) const
170 {
171 TRACE_SUBSET (this);
172 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
173 const hb_map_t &glyph_map = *c->plan->glyph_map;
174
175 auto *out = c->serializer->start_embed (*this);
176 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
177 out->format = format;
178
179 hb_map_t klass_mapping;
180 Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping);
181
182 if (!klass_mapping.get_population ()) return_trace (false);
183 out->classCount = klass_mapping.get_population ();
184
185 auto mark_iter =
186 + hb_zip (this+markCoverage, this+markArray)
187 | hb_filter (glyphset, hb_first)
188 ;
189
190 hb_sorted_vector_t<hb_codepoint_t> new_coverage;
191 + mark_iter
192 | hb_map (hb_first)
193 | hb_map (glyph_map)
194 | hb_sink (new_coverage)
195 ;
196
197 if (!out->markCoverage.serialize_serialize (c->serializer, new_coverage.iter ()))
198 return_trace (false);
199
200 out->markArray.serialize_subset (c, markArray, this,
201 (this+markCoverage).iter (),
202 &klass_mapping);
203
204 unsigned basecount = (this+baseArray).rows;
205 auto base_iter =
206 + hb_zip (this+baseCoverage, hb_range (basecount))
207 | hb_filter (glyphset, hb_first)
208 ;
209
210 new_coverage.reset ();
211 + base_iter
212 | hb_map (hb_first)
213 | hb_map (glyph_map)
214 | hb_sink (new_coverage)
215 ;
216
217 if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ()))
218 return_trace (false);
219
220 hb_sorted_vector_t<unsigned> base_indexes;
221 for (const unsigned row : + base_iter
222 | hb_map (hb_second))
223 {
224 + hb_range ((unsigned) classCount)
225 | hb_filter (klass_mapping)
226 | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
227 | hb_sink (base_indexes)
228 ;
229 }
230
231 out->baseArray.serialize_subset (c, baseArray, this,
232 base_iter.len (),
233 base_indexes.iter ());
234
235 return_trace (true);
236 }
237};
238
239
240}
241}
242}
243
244#endif /* OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH */
245