harfrust/hb/ot/gpos/
mark.rs1use crate::hb::buffer::{hb_buffer_t, HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT};
2use crate::hb::ot_layout_common::lookup_flags;
3use crate::hb::ot_layout_gpos_table::attach_type;
4use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t;
5use crate::hb::ot_layout_gsubgpos::{match_t, skipping_iterator_t, Apply, MatchSource};
6use read_fonts::tables::gpos::{
7 AnchorTable, MarkArray, MarkBasePosFormat1, MarkLigPosFormat1, MarkMarkPosFormat1,
8};
9
10trait MarkArrayExt {
11 fn apply(
12 &self,
13 ctx: &mut hb_ot_apply_context_t,
14 base_anchor: &AnchorTable,
15 mark_anchor: &AnchorTable,
16 glyph_pos: usize,
17 ) -> Option<()>;
18}
19
20impl MarkArrayExt for MarkArray<'_> {
21 fn apply(
22 &self,
23 ctx: &mut hb_ot_apply_context_t,
24 base_anchor: &AnchorTable,
25 mark_anchor: &AnchorTable,
26 glyph_pos: usize,
27 ) -> Option<()> {
28 let (base_x, base_y) = ctx.face.ot_tables.resolve_anchor(base_anchor);
32 let (mark_x, mark_y) = ctx.face.ot_tables.resolve_anchor(mark_anchor);
33 let x_offset = ctx.scale_x(base_x - mark_x);
34 let y_offset = ctx.scale_y(base_y - mark_y);
35
36 ctx.buffer
37 .unsafe_to_break(Some(glyph_pos), Some(ctx.buffer.idx + 1));
38
39 let idx = ctx.buffer.idx;
40 let pos = ctx.buffer.cur_pos_mut();
41 pos.x_offset = x_offset;
42 pos.y_offset = y_offset;
43 pos.set_attach_type(attach_type::MARK);
44 pos.set_attach_chain((glyph_pos as isize - idx as isize) as i16);
45
46 ctx.buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
47 ctx.buffer.idx += 1;
48
49 Some(())
50 }
51}
52
53impl Apply for MarkBasePosFormat1<'_> {
54 fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
55 let mark_glyph = ctx.buffer.cur(0).as_glyph();
56 let mark_index = self.mark_coverage().ok()?.get(mark_glyph)?;
57
58 let base_coverage = self.base_coverage().ok()?;
59 let last_base_until = ctx.last_base_until;
60 let mut last_base = ctx.last_base;
61
62 if ctx.last_base_until > ctx.buffer.idx as u32 {
65 ctx.last_base_until = 0;
66 ctx.last_base = -1;
67 }
68
69 let mut iter = skipping_iterator_t::new(ctx, false);
72 iter.set_lookup_props(u32::from(lookup_flags::IGNORE_MARKS));
73
74 let mut j = iter.buffer.idx;
75 while j > last_base_until as usize {
76 let mut _match = iter.match_at(j - 1, MatchSource::Info);
77 if _match == match_t::MATCH {
78 if !accept(iter.buffer, j - 1)
80 && base_coverage
81 .get(iter.buffer.info[j - 1].as_glyph())
82 .is_none()
83 {
84 _match = match_t::SKIP;
85 }
86 }
87
88 if _match == match_t::MATCH {
89 last_base = j as i32 - 1;
90 break;
91 }
92
93 j -= 1;
94 }
95 ctx.last_base_until = ctx.buffer.idx as u32;
96 ctx.last_base = last_base;
97
98 if ctx.last_base == -1 {
99 ctx.buffer
100 .unsafe_to_concat_from_outbuffer(Some(0), Some(ctx.buffer.idx + 1));
101 return None;
102 }
103
104 let idx = ctx.last_base as u32;
105
106 let info = &ctx.buffer.info;
107
108 let base_glyph = info[idx as usize].as_glyph();
110 let Some(base_index) = self.base_coverage().ok()?.get(base_glyph) else {
111 ctx.buffer
112 .unsafe_to_concat_from_outbuffer(Some(idx as usize), Some(ctx.buffer.idx + 1));
113 return None;
114 };
115
116 let mark_array = self.mark_array().ok()?;
117 let mark_record = mark_array.mark_records().get(mark_index as usize)?;
118 let mark_anchor = mark_record.mark_anchor(mark_array.offset_data()).ok()?;
119
120 let base_array = self.base_array().ok()?;
121 let base_record = base_array.base_records().get(base_index as usize).ok()?;
122 let base_anchor = base_record
123 .base_anchors(base_array.offset_data())
124 .get(mark_record.mark_class() as usize)?
125 .ok()?;
126
127 mark_array.apply(ctx, &base_anchor, &mark_anchor, idx as usize)
128 }
129}
130
131fn accept(buffer: &hb_buffer_t, idx: usize) -> bool {
132 !buffer.info[idx].multiplied()
138 || 0 == buffer.info[idx].lig_comp()
139 || (idx == 0
140 || buffer.info[idx - 1].is_mark()
141 || !buffer.info[idx - 1].multiplied()
142 || buffer.info[idx].lig_id() != buffer.info[idx - 1].lig_id()
143 || buffer.info[idx].lig_comp() != buffer.info[idx - 1].lig_comp() + 1)
144}
145
146impl Apply for MarkMarkPosFormat1<'_> {
147 fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
148 let mark1_glyph = ctx.buffer.cur(0).as_glyph();
149 let mark1_index = self.mark1_coverage().ok()?.get(mark1_glyph)?;
150 let lookup_props = ctx.lookup_props;
151 let mut iter = skipping_iterator_t::new(ctx, false);
153 iter.reset_fast(iter.buffer.idx);
154 iter.set_lookup_props(lookup_props & !u32::from(lookup_flags::IGNORE_FLAGS));
155
156 let mut unsafe_from = 0;
157 if !iter.prev(Some(&mut unsafe_from)) {
158 iter.buffer
159 .unsafe_to_concat_from_outbuffer(Some(unsafe_from), Some(iter.buffer.idx + 1));
160 return None;
161 }
162
163 let iter_idx = iter.index();
164 if !ctx.buffer.info[iter_idx].is_mark() {
165 ctx.buffer
166 .unsafe_to_concat_from_outbuffer(Some(iter_idx), Some(ctx.buffer.idx + 1));
167 return None;
168 }
169
170 let id1 = ctx.buffer.cur(0).lig_id();
171 let id2 = ctx.buffer.info[iter_idx].lig_id();
172 let comp1 = ctx.buffer.cur(0).lig_comp();
173 let comp2 = ctx.buffer.info[iter_idx].lig_comp();
174
175 let matches = if id1 == id2 {
176 id1 == 0 || comp1 == comp2
179 } else {
180 (id1 > 0 && comp1 == 0) || (id2 > 0 && comp2 == 0)
183 };
184
185 if !matches {
186 ctx.buffer
187 .unsafe_to_concat_from_outbuffer(Some(iter_idx), Some(ctx.buffer.idx + 1));
188 return None;
189 }
190
191 let mark2_glyph = ctx.buffer.info[iter_idx].as_glyph();
192 let mark2_index = self.mark2_coverage().ok()?.get(mark2_glyph)?;
193
194 let mark1_array = self.mark1_array().ok()?;
195 let mark1_record = mark1_array.mark_records().get(mark1_index as usize)?;
196 let mark1_anchor = mark1_record.mark_anchor(mark1_array.offset_data()).ok()?;
197
198 let base_array = self.mark2_array().ok()?;
199 let base_record = base_array.mark2_records().get(mark2_index as usize).ok()?;
200 let base_anchor = base_record
201 .mark2_anchors(base_array.offset_data())
202 .get(mark1_record.mark_class() as usize)?
203 .ok()?;
204
205 mark1_array.apply(ctx, &base_anchor, &mark1_anchor, iter_idx)
206 }
207}
208
209impl Apply for MarkLigPosFormat1<'_> {
210 fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
211 let mark_glyph = ctx.buffer.cur(0).as_glyph();
212 let mark_index = self.mark_coverage().ok()?.get(mark_glyph)? as usize;
213
214 if ctx.last_base_until > ctx.buffer.idx as u32 {
217 ctx.last_base_until = 0;
218 ctx.last_base = -1;
219 }
220
221 let last_base_until = ctx.last_base_until;
222 let mut last_base = ctx.last_base;
223
224 let mut iter = skipping_iterator_t::new(ctx, false);
226 iter.set_lookup_props(u32::from(lookup_flags::IGNORE_MARKS));
227
228 let mut j = iter.buffer.idx;
229 while j > last_base_until as usize {
230 let mut _match = iter.match_at(j - 1, MatchSource::Info);
231 if _match == match_t::MATCH {
232 last_base = j as i32 - 1;
233 break;
234 }
235 j -= 1;
236 }
237
238 ctx.last_base_until = ctx.buffer.idx as u32;
239 ctx.last_base = last_base;
240
241 if ctx.last_base == -1 {
242 ctx.buffer
243 .unsafe_to_concat_from_outbuffer(Some(0), Some(ctx.buffer.idx + 1));
244 return None;
245 }
246
247 let idx = ctx.last_base as usize;
248
249 let lig_glyph = ctx.buffer.info[idx].as_glyph();
252 let Some(lig_index) = self.ligature_coverage().ok()?.get(lig_glyph) else {
253 ctx.buffer
254 .unsafe_to_concat_from_outbuffer(Some(idx), Some(ctx.buffer.idx + 1));
255 return None;
256 };
257 let lig_attach = self
258 .ligature_array()
259 .ok()?
260 .ligature_attaches()
261 .get(lig_index as usize)
262 .ok()?;
263
264 let comp_count = lig_attach.component_count();
266 if comp_count == 0 {
267 ctx.buffer
268 .unsafe_to_concat_from_outbuffer(Some(idx), Some(ctx.buffer.idx + 1));
269 return None;
270 }
271
272 let lig_id = ctx.buffer.info[idx].lig_id();
277 let mark_id = ctx.buffer.cur(0).lig_id();
278 let mark_comp = u16::from(ctx.buffer.cur(0).lig_comp());
279 let matches = lig_id != 0 && lig_id == mark_id && mark_comp > 0;
280 let comp_index = if matches {
281 mark_comp.min(comp_count)
282 } else {
283 comp_count
284 } - 1;
285
286 let mark_array = self.mark_array().ok()?;
287 let mark_record = mark_array.mark_records().get(mark_index)?;
288 let mark_anchor = mark_record.mark_anchor(mark_array.offset_data()).ok()?;
289
290 let base_record = lig_attach
291 .component_records()
292 .get(comp_index as usize)
293 .ok()?;
294 let base_anchor = base_record
295 .ligature_anchors(lig_attach.offset_data())
296 .get(mark_record.mark_class() as usize)?
297 .ok()?;
298
299 mark_array.apply(ctx, &base_anchor, &mark_anchor, idx)
300 }
301}