/src/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh
Line | Count | Source |
1 | | #ifndef OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH |
2 | | #define OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH |
3 | | |
4 | | #include "Anchor.hh" |
5 | | #include "../../../hb-limits.hh" |
6 | | |
7 | | namespace OT { |
8 | | namespace Layout { |
9 | | namespace GPOS_impl { |
10 | | |
11 | | struct EntryExitRecord |
12 | | { |
13 | | friend struct CursivePosFormat1; |
14 | | |
15 | | bool sanitize (hb_sanitize_context_t *c, const struct CursivePosFormat1 *base) const |
16 | 0 | { |
17 | 0 | TRACE_SANITIZE (this); |
18 | 0 | return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base)); |
19 | 0 | } |
20 | | |
21 | | void collect_variation_indices (hb_collect_variation_indices_context_t *c, |
22 | | const struct CursivePosFormat1 *src_base) const |
23 | 0 | { |
24 | 0 | (src_base+entryAnchor).collect_variation_indices (c); |
25 | 0 | (src_base+exitAnchor).collect_variation_indices (c); |
26 | 0 | } |
27 | | |
28 | | bool subset (hb_subset_context_t *c, |
29 | | const struct CursivePosFormat1 *src_base) const |
30 | 0 | { |
31 | 0 | TRACE_SERIALIZE (this); |
32 | 0 | auto *out = c->serializer->embed (this); |
33 | 0 | if (unlikely (!out)) return_trace (false); |
34 | 0 |
|
35 | 0 | bool ret = false; |
36 | 0 | ret |= out->entryAnchor.serialize_subset (c, entryAnchor, src_base); |
37 | 0 | ret |= out->exitAnchor.serialize_subset (c, exitAnchor, src_base); |
38 | 0 | return_trace (ret); |
39 | 0 | } |
40 | | |
41 | | protected: |
42 | | Offset16To<Anchor, struct CursivePosFormat1> |
43 | | entryAnchor; /* Offset to EntryAnchor table--from |
44 | | * beginning of CursivePos |
45 | | * subtable--may be NULL */ |
46 | | Offset16To<Anchor, struct CursivePosFormat1> |
47 | | exitAnchor; /* Offset to ExitAnchor table--from |
48 | | * beginning of CursivePos |
49 | | * subtable--may be NULL */ |
50 | | public: |
51 | | DEFINE_SIZE_STATIC (4); |
52 | | }; |
53 | | |
54 | | static inline void |
55 | | reverse_cursive_minor_offset (hb_glyph_position_t *pos, |
56 | | unsigned int len, |
57 | | unsigned int i, |
58 | | hb_direction_t direction, |
59 | | unsigned int new_parent, |
60 | | unsigned nesting_level = 0) |
61 | 0 | { |
62 | 0 | if (nesting_level > HB_MAX_NESTING_LEVEL) |
63 | 0 | return; |
64 | | |
65 | 0 | int chain = pos[i].attach_chain(), type = pos[i].attach_type(); |
66 | 0 | if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE))) |
67 | 0 | return; |
68 | | |
69 | 0 | pos[i].attach_chain() = 0; |
70 | |
|
71 | 0 | unsigned int j = (int) i + chain; |
72 | |
|
73 | 0 | if (unlikely (j >= len)) |
74 | 0 | return; |
75 | | |
76 | | /* Stop if we see new parent in the chain. */ |
77 | 0 | if (j == new_parent) |
78 | 0 | return; |
79 | | |
80 | 0 | int16_t reversed_chain = -chain; |
81 | | /* The old edge was cleared above; if the reversed distance truncates, |
82 | | * keep it detached instead of storing a poisoned chain. |
83 | | */ |
84 | 0 | if (unlikely (reversed_chain != -chain)) |
85 | 0 | return; |
86 | | |
87 | 0 | reverse_cursive_minor_offset (pos, len, j, direction, new_parent, nesting_level + 1); |
88 | |
|
89 | 0 | if (HB_DIRECTION_IS_HORIZONTAL (direction)) |
90 | 0 | pos[j].y_offset = hb_saturate_neg (pos[i].y_offset); |
91 | 0 | else |
92 | 0 | pos[j].x_offset = hb_saturate_neg (pos[i].x_offset); |
93 | |
|
94 | 0 | pos[j].attach_chain() = reversed_chain; |
95 | 0 | pos[j].attach_type() = type; |
96 | 0 | } Unexecuted instantiation: hb-ot-face.cc:OT::Layout::GPOS_impl::reverse_cursive_minor_offset(hb_glyph_position_t*, unsigned int, unsigned int, hb_direction_t, unsigned int, unsigned int) Unexecuted instantiation: hb-aat-layout.cc:OT::Layout::GPOS_impl::reverse_cursive_minor_offset(hb_glyph_position_t*, unsigned int, unsigned int, hb_direction_t, unsigned int, unsigned int) Unexecuted instantiation: hb-ot-layout.cc:OT::Layout::GPOS_impl::reverse_cursive_minor_offset(hb_glyph_position_t*, unsigned int, unsigned int, hb_direction_t, unsigned int, unsigned int) Unexecuted instantiation: hb-ot-shape-fallback.cc:OT::Layout::GPOS_impl::reverse_cursive_minor_offset(hb_glyph_position_t*, unsigned int, unsigned int, hb_direction_t, unsigned int, unsigned int) |
97 | | |
98 | | |
99 | | struct CursivePosFormat1 |
100 | | { |
101 | | protected: |
102 | | HBUINT16 format; /* Format identifier--format = 1 */ |
103 | | Offset16To<Coverage> |
104 | | coverage; /* Offset to Coverage table--from |
105 | | * beginning of subtable */ |
106 | | Array16Of<EntryExitRecord> |
107 | | entryExitRecord; /* Array of EntryExit records--in |
108 | | * Coverage Index order */ |
109 | | public: |
110 | | DEFINE_SIZE_ARRAY (6, entryExitRecord); |
111 | | |
112 | | bool sanitize (hb_sanitize_context_t *c) const |
113 | 0 | { |
114 | 0 | TRACE_SANITIZE (this); |
115 | 0 | if (unlikely (!coverage.sanitize (c, this))) |
116 | 0 | return_trace (false); |
117 | | |
118 | 0 | if (c->lazy_some_gpos) |
119 | 0 | return_trace (entryExitRecord.sanitize_shallow (c)); |
120 | 0 | else |
121 | 0 | return_trace (entryExitRecord.sanitize (c, this)); |
122 | 0 | } |
123 | | |
124 | | bool intersects (const hb_set_t *glyphs) const |
125 | 0 | { return (this+coverage).intersects (glyphs); } |
126 | | |
127 | 0 | void closure_lookups (hb_closure_lookups_context_t *c) const {} |
128 | | |
129 | | void collect_variation_indices (hb_collect_variation_indices_context_t *c) const |
130 | 0 | { |
131 | 0 | + hb_zip (this+coverage, entryExitRecord) |
132 | 0 | | hb_filter (c->glyph_set, hb_first) |
133 | 0 | | hb_map (hb_second) |
134 | 0 | | hb_apply ([&] (const EntryExitRecord& record) { record.collect_variation_indices (c, this); }) |
135 | 0 | ; |
136 | 0 | } |
137 | | |
138 | | void collect_glyphs (hb_collect_glyphs_context_t *c) const |
139 | 0 | { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } |
140 | | |
141 | 0 | const Coverage &get_coverage () const { return this+coverage; } |
142 | | |
143 | | bool apply (hb_ot_apply_context_t *c) const |
144 | 0 | { |
145 | 0 | TRACE_APPLY (this); |
146 | 0 | hb_buffer_t *buffer = c->buffer; |
147 | |
|
148 | 0 | const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)]; |
149 | 0 | if (!this_record.entryAnchor || |
150 | 0 | unlikely (!this_record.entryAnchor.sanitize (&c->sanitizer, this))) return_trace (false); |
151 | 0 | hb_barrier (); |
152 | |
|
153 | 0 | auto &skippy_iter = c->iter_input; |
154 | 0 | skippy_iter.reset_fast (buffer->idx); |
155 | 0 | unsigned unsafe_from; |
156 | 0 | if (unlikely (!skippy_iter.prev (&unsafe_from))) |
157 | 0 | { |
158 | 0 | buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); |
159 | 0 | return_trace (false); |
160 | 0 | } |
161 | | |
162 | 0 | const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)]; |
163 | 0 | if (!prev_record.exitAnchor || |
164 | 0 | unlikely (!prev_record.exitAnchor.sanitize (&c->sanitizer, this))) |
165 | 0 | { |
166 | 0 | buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); |
167 | 0 | return_trace (false); |
168 | 0 | } |
169 | 0 | hb_barrier (); |
170 | |
|
171 | 0 | unsigned int i = skippy_iter.idx; |
172 | 0 | unsigned int j = buffer->idx; |
173 | |
|
174 | 0 | if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) |
175 | 0 | { |
176 | 0 | c->buffer->message (c->font, |
177 | 0 | "cursive attaching glyph at %u to glyph at %u", |
178 | 0 | i, j); |
179 | 0 | } |
180 | |
|
181 | 0 | buffer->unsafe_to_break (i, j + 1); |
182 | 0 | float entry_x, entry_y, exit_x, exit_y; |
183 | 0 | (this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y); |
184 | 0 | (this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y); |
185 | |
|
186 | 0 | hb_glyph_position_t *pos = buffer->pos; |
187 | |
|
188 | 0 | hb_position_t d; |
189 | | /* Main-direction adjustment */ |
190 | 0 | switch (c->direction) { |
191 | 0 | case HB_DIRECTION_LTR: |
192 | 0 | pos[i].x_advance = hb_saturate_add ((hb_position_t) roundf (exit_x), pos[i].x_offset); |
193 | |
|
194 | 0 | d = hb_saturate_add ((hb_position_t) roundf (entry_x), pos[j].x_offset); |
195 | 0 | pos[j].x_advance = hb_saturate_sub (pos[j].x_advance, d); |
196 | 0 | pos[j].x_offset = hb_saturate_sub (pos[j].x_offset, d); |
197 | 0 | break; |
198 | 0 | case HB_DIRECTION_RTL: |
199 | 0 | d = hb_saturate_add ((hb_position_t) roundf (exit_x), pos[i].x_offset); |
200 | 0 | pos[i].x_advance = hb_saturate_sub (pos[i].x_advance, d); |
201 | 0 | pos[i].x_offset = hb_saturate_sub (pos[i].x_offset, d); |
202 | |
|
203 | 0 | pos[j].x_advance = hb_saturate_add ((hb_position_t) roundf (entry_x), pos[j].x_offset); |
204 | 0 | break; |
205 | 0 | case HB_DIRECTION_TTB: |
206 | 0 | pos[i].y_advance = hb_saturate_add ((hb_position_t) roundf (exit_y), pos[i].y_offset); |
207 | |
|
208 | 0 | d = hb_saturate_add ((hb_position_t) roundf (entry_y), pos[j].y_offset); |
209 | 0 | pos[j].y_advance = hb_saturate_sub (pos[j].y_advance, d); |
210 | 0 | pos[j].y_offset = hb_saturate_sub (pos[j].y_offset, d); |
211 | 0 | break; |
212 | 0 | case HB_DIRECTION_BTT: |
213 | 0 | d = hb_saturate_add ((hb_position_t) roundf (exit_y), pos[i].y_offset); |
214 | 0 | pos[i].y_advance = hb_saturate_sub (pos[i].y_advance, d); |
215 | 0 | pos[i].y_offset = hb_saturate_sub (pos[i].y_offset, d); |
216 | |
|
217 | 0 | pos[j].y_advance = (hb_position_t) roundf (entry_y); |
218 | 0 | break; |
219 | 0 | case HB_DIRECTION_INVALID: |
220 | 0 | default: |
221 | 0 | break; |
222 | 0 | } |
223 | | |
224 | | /* Cross-direction adjustment */ |
225 | | |
226 | | /* We attach child to parent (think graph theory and rooted trees whereas |
227 | | * the root stays on baseline and each node aligns itself against its |
228 | | * parent. |
229 | | * |
230 | | * Optimize things for the case of RightToLeft, as that's most common in |
231 | | * Arabic. */ |
232 | 0 | unsigned int child = i; |
233 | 0 | unsigned int parent = j; |
234 | 0 | hb_position_t x_offset = (hb_position_t) roundf (entry_x - exit_x); |
235 | 0 | hb_position_t y_offset = (hb_position_t) roundf (entry_y - exit_y); |
236 | 0 | if (!(c->lookup_props & LookupFlag::RightToLeft)) |
237 | 0 | { |
238 | 0 | unsigned int k = child; |
239 | 0 | child = parent; |
240 | 0 | parent = k; |
241 | 0 | x_offset = -x_offset; |
242 | 0 | y_offset = -y_offset; |
243 | 0 | } |
244 | | |
245 | | /* If child was already connected to someone else, walk through its old |
246 | | * chain and reverse the link direction, such that the whole tree of its |
247 | | * previous connection now attaches to new parent. Watch out for case |
248 | | * where new parent is on the path from old chain... |
249 | | */ |
250 | 0 | reverse_cursive_minor_offset (pos, buffer->len, child, c->direction, parent); |
251 | |
|
252 | 0 | pos[child].attach_chain() = (int) parent - (int) child; |
253 | 0 | if (pos[child].attach_chain() != (int) parent - (int) child) |
254 | 0 | { |
255 | 0 | pos[child].attach_chain() = 0; |
256 | 0 | goto overflow; |
257 | 0 | } |
258 | 0 | pos[child].attach_type() = ATTACH_TYPE_CURSIVE; |
259 | 0 | buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; |
260 | 0 | if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) |
261 | 0 | pos[child].y_offset = y_offset; |
262 | 0 | else |
263 | 0 | pos[child].x_offset = x_offset; |
264 | | |
265 | | /* If parent was attached to child, separate them. |
266 | | * https://github.com/harfbuzz/harfbuzz/issues/2469 |
267 | | */ |
268 | 0 | if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain())) |
269 | 0 | { |
270 | 0 | pos[parent].attach_chain() = 0; |
271 | 0 | if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) |
272 | 0 | pos[parent].y_offset = 0; |
273 | 0 | else |
274 | 0 | pos[parent].x_offset = 0; |
275 | 0 | } |
276 | |
|
277 | 0 | if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) |
278 | 0 | { |
279 | 0 | c->buffer->message (c->font, |
280 | 0 | "cursive attached glyph at %u to glyph at %u", |
281 | 0 | i, j); |
282 | 0 | } |
283 | |
|
284 | 0 | overflow: |
285 | 0 | buffer->idx++; |
286 | 0 | return_trace (true); |
287 | 0 | } |
288 | | |
289 | | template <typename Iterator, |
290 | | hb_requires (hb_is_iterator (Iterator))> |
291 | | void serialize (hb_subset_context_t *c, |
292 | | Iterator it, |
293 | | const struct CursivePosFormat1 *src_base) |
294 | 0 | { |
295 | 0 | if (unlikely (!c->serializer->extend_min ((*this)))) return; |
296 | 0 | this->format = 1; |
297 | 0 | this->entryExitRecord.len = it.len (); |
298 | 0 |
|
299 | 0 | for (const EntryExitRecord& entry_record : + it |
300 | 0 | | hb_map (hb_second)) |
301 | 0 | entry_record.subset (c, src_base); |
302 | 0 |
|
303 | 0 | auto glyphs = |
304 | 0 | + it |
305 | 0 | | hb_map_retains_sorting (hb_first) |
306 | 0 | ; |
307 | 0 |
|
308 | 0 | coverage.serialize_serialize (c->serializer, glyphs); |
309 | 0 | } Unexecuted instantiation: hb-ot-face.cc:_ZN2OT6Layout9GPOS_impl17CursivePosFormat19serializeI13hb_map_iter_tI16hb_filter_iter_tI13hb_zip_iter_tINS0_6Common8Coverage6iter_tE10hb_array_tIKNS1_15EntryExitRecordEEERK8hb_set_tRK3$_6LPv0EEZNKS2_6subsetEP19hb_subset_context_tEUl9hb_pair_tIjRSC_EE_L24hb_function_sortedness_t1ELSL_0EETnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT_NSW_6item_tEEE5valueEvE4typeELSL_0EEEvSO_SW_PKS2_ Unexecuted instantiation: hb-aat-layout.cc:_ZN2OT6Layout9GPOS_impl17CursivePosFormat19serializeI13hb_map_iter_tI16hb_filter_iter_tI13hb_zip_iter_tINS0_6Common8Coverage6iter_tE10hb_array_tIKNS1_15EntryExitRecordEEERK8hb_set_tRK3$_6LPv0EEZNKS2_6subsetEP19hb_subset_context_tEUl9hb_pair_tIjRSC_EE_L24hb_function_sortedness_t1ELSL_0EETnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT_NSW_6item_tEEE5valueEvE4typeELSL_0EEEvSO_SW_PKS2_ Unexecuted instantiation: hb-ot-layout.cc:_ZN2OT6Layout9GPOS_impl17CursivePosFormat19serializeI13hb_map_iter_tI16hb_filter_iter_tI13hb_zip_iter_tINS0_6Common8Coverage6iter_tE10hb_array_tIKNS1_15EntryExitRecordEEERK8hb_set_tRK3$_6LPv0EEZNKS2_6subsetEP19hb_subset_context_tEUl9hb_pair_tIjRSC_EE_L24hb_function_sortedness_t1ELSL_0EETnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT_NSW_6item_tEEE5valueEvE4typeELSL_0EEEvSO_SW_PKS2_ Unexecuted instantiation: hb-ot-shape-fallback.cc:_ZN2OT6Layout9GPOS_impl17CursivePosFormat19serializeI13hb_map_iter_tI16hb_filter_iter_tI13hb_zip_iter_tINS0_6Common8Coverage6iter_tE10hb_array_tIKNS1_15EntryExitRecordEEERK8hb_set_tRK3$_6LPv0EEZNKS2_6subsetEP19hb_subset_context_tEUl9hb_pair_tIjRSC_EE_L24hb_function_sortedness_t1ELSL_0EETnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT_NSW_6item_tEEE5valueEvE4typeELSL_0EEEvSO_SW_PKS2_ |
310 | | |
311 | | bool subset (hb_subset_context_t *c) const |
312 | 0 | { |
313 | 0 | TRACE_SUBSET (this); |
314 | 0 | const hb_set_t &glyphset = *c->plan->glyphset_gsub (); |
315 | 0 | const hb_map_t &glyph_map = *c->plan->glyph_map; |
316 | 0 |
|
317 | 0 | auto *out = c->serializer->start_embed (*this); |
318 | 0 |
|
319 | 0 | auto it = |
320 | 0 | + hb_zip (this+coverage, entryExitRecord) |
321 | 0 | | hb_filter (glyphset, hb_first) |
322 | 0 | | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const EntryExitRecord&> p) -> hb_pair_t<hb_codepoint_t, const EntryExitRecord&> |
323 | 0 | { return hb_pair (glyph_map[p.first], p.second);}) |
324 | 0 | ; |
325 | 0 |
|
326 | 0 | bool ret = bool (it); |
327 | 0 | out->serialize (c, it, this); |
328 | 0 | return_trace (ret); |
329 | 0 | } |
330 | | }; |
331 | | |
332 | | |
333 | | } |
334 | | } |
335 | | } |
336 | | |
337 | | #endif /* OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH */ |