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