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