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