1use crate::Engine;
40use crate::nodes::{NodeData, NodeRef, id, kern_sub};
41
42pub const LEFT_SIDE: u16 = 0;
44pub const RIGHT_SIDE: u16 = 1;
46
47enum Found {
49 Char(NodeRef),
51 Blocked,
53 End,
55}
56
57pub(crate) fn round_div(a: i64, b: i64) -> i64 {
59 if a >= 0 {
60 (2 * a + b) / (2 * b)
61 } else {
62 -((-2 * a + b) / (2 * b))
63 }
64}
65
66impl Engine {
67 pub fn char_protrusion(&self, p: NodeRef, left: bool) -> i32 {
71 let Some(g) = self.nodes.glyph(p) else {
72 return 0;
73 };
74 let fi = self.font(g.font);
75 let code = fi.char_codes[usize::from(!left)]
76 .get(&g.chr)
77 .copied()
78 .unwrap_or(0);
79 if code == 0 {
80 return 0;
81 }
82 let quad = i64::from(self.quad(g.font));
83 round_div(i64::from(code) * quad, 1000) as i32
84 }
85
86 fn protrusion_skippable(&self, p: NodeRef) -> bool {
89 match self.nodes.data(p) {
90 NodeData::Glue { spec, .. } => spec.width == 0 && spec.stretch == 0 && spec.shrink == 0,
91 NodeData::Penalty { .. } => true,
92 NodeData::Disc {
93 pre, post, replace, ..
94 } => pre.is_null() && post.is_null() && replace.is_null(),
95 NodeData::Kern { kern, .. } => *kern == 0 || self.nodes.subtype(p) == kern_sub::FONT,
96 NodeData::Rule {
97 width,
98 height,
99 depth,
100 ..
101 } => *width == 0 && *height == 0 && *depth == 0,
102 NodeData::Math { surround, glue } => {
103 *surround == 0 && glue.width == 0 && glue.stretch == 0 && glue.shrink == 0
104 }
105 NodeData::Dir { .. }
106 | NodeData::LocalPar { .. }
107 | NodeData::Ins { .. }
108 | NodeData::Mark { .. }
109 | NodeData::Adjust { .. }
110 | NodeData::Boundary { .. }
111 | NodeData::Whatsit(_) => true,
112 NodeData::Box(b) => {
113 self.nodes.id(p) == id::HLIST
114 && b.head.is_null()
115 && b.width == 0
116 && b.height == 0
117 && b.depth == 0
118 }
119 _ => false,
120 }
121 }
122
123 fn pruned_after_break(&self, p: NodeRef) -> bool {
125 match self.nodes.id(p) {
126 id::GLUE | id::PENALTY | id::MATH => true,
127 id::KERN => matches!(self.nodes.subtype(p), kern_sub::USER | kern_sub::ITALIC),
128 _ => false,
129 }
130 }
131
132 fn protchar_left_from(&self, mut p: NodeRef) -> Found {
134 while !p.is_null() {
135 match self.nodes.id(p) {
136 id::GLYPH => return Found::Char(p),
137 id::HLIST if !self.nodes.list_ptr(p).is_null() => {
138 match self.protchar_left_from(self.nodes.list_ptr(p)) {
139 Found::End => {}
140 f => return f,
141 }
142 }
143 _ if self.protrusion_skippable(p) => {}
144 _ => return Found::Blocked,
145 }
146 p = self.nodes.next(p);
147 }
148 Found::End
149 }
150
151 fn protchar_right_in(&self, head: NodeRef) -> Found {
153 let mut v = Vec::new();
156 let mut p = head;
157 while !p.is_null() {
158 v.push(p);
159 p = self.nodes.next(p);
160 }
161 self.protchar_right_rev(v.into_iter().rev())
162 }
163
164 fn protchar_right_rev(&self, it: impl Iterator<Item = NodeRef>) -> Found {
165 for p in it {
166 match self.nodes.id(p) {
167 id::GLYPH => return Found::Char(p),
168 id::HLIST if !self.nodes.list_ptr(p).is_null() => {
169 match self.protchar_right_in(self.nodes.list_ptr(p)) {
170 Found::End => {}
171 f => return f,
172 }
173 }
174 _ if self.protrusion_skippable(p) => {}
175 _ => return Found::Blocked,
176 }
177 }
178 Found::End
179 }
180
181 fn protchar_right_back(&self, p: NodeRef, stop: NodeRef) -> Found {
184 let mut q = p;
185 let it = std::iter::from_fn(move || {
186 if q.is_null() || q == stop {
187 return None;
188 }
189 let r = q;
190 q = self.nodes.prev(q);
191 Some(r)
192 });
193 self.protchar_right_rev(it)
194 }
195
196 fn found_left(&self, f: Found) -> i32 {
197 match f {
198 Found::Char(g) => self.char_protrusion(g, true),
199 _ => 0,
200 }
201 }
202
203 fn found_right(&self, f: Found) -> i32 {
204 match f {
205 Found::Char(g) => self.char_protrusion(g, false),
206 _ => 0,
207 }
208 }
209
210 pub(crate) fn lb_left_protrusion(&self, brk: Option<NodeRef>, first: NodeRef) -> i32 {
214 let mut p = match brk {
215 None => return self.found_left(self.protchar_left_from(first)),
216 Some(b) if b.is_null() => return 0,
217 Some(b) => b,
218 };
219 if self.nodes.id(p) == id::DISC {
220 let (_, post, _) = self.nodes.disc_lists(p);
221 if !post.is_null() {
222 return if self.nodes.id(post) == id::GLYPH {
223 self.char_protrusion(post, true)
224 } else {
225 0
226 };
227 }
228 }
229 while !p.is_null() && self.pruned_after_break(p) {
230 p = self.nodes.next(p);
231 }
232 self.found_left(self.protchar_left_from(p))
233 }
234
235 pub(crate) fn lb_right_protrusion(&self, cur_p: NodeRef, head: NodeRef) -> i32 {
239 if cur_p.is_null() {
240 return 0;
243 }
244 if self.nodes.id(cur_p) == id::DISC {
245 let (pre, _, _) = self.nodes.disc_lists(cur_p);
246 if !pre.is_null() {
247 let t = self.nodes.tail_of(pre);
248 return if self.nodes.id(t) == id::GLYPH {
249 self.char_protrusion(t, false)
250 } else {
251 0
252 };
253 }
254 }
255 let p = self.nodes.prev(cur_p);
256 self.found_right(self.protchar_right_back(p, head))
257 }
258
259 pub(crate) fn fix_back_links(&mut self, head: NodeRef) {
261 let mut prev = head;
262 let mut p = self.nodes.next(head);
263 while !p.is_null() {
264 self.nodes.set_prev(p, prev);
265 prev = p;
266 p = self.nodes.next(p);
267 }
268 }
269
270 fn new_margin_kern(&mut self, width: i32, side: u16) -> NodeRef {
271 self.new_node(
272 id::MARGIN_KERN,
273 side,
274 NodeData::MarginKern {
275 width,
276 glyph: NodeRef::NULL,
277 },
278 )
279 }
280
281 pub(crate) fn left_margin_kern(&mut self, line: NodeRef) -> NodeRef {
284 let w = self.found_left(self.protchar_left_from(line));
285 if w == 0 {
286 return NodeRef::NULL;
287 }
288 self.new_margin_kern(-w, LEFT_SIDE)
289 }
290
291 pub(crate) fn insert_right_margin_kern(&mut self, pred: NodeRef, q: NodeRef) {
295 let mut v = Vec::new();
296 let mut p = self.nodes.next(pred);
297 while !p.is_null() && p != q {
298 v.push(p);
299 p = self.nodes.next(p);
300 }
301 if p != q || q.is_null() {
302 return;
303 }
304 let w = self.found_right(self.protchar_right_rev(v.iter().rev().copied()));
305 if w == 0 {
306 return;
307 }
308 let k = self.new_margin_kern(-w, RIGHT_SIDE);
309 let before = v.last().copied().unwrap_or(pred);
310 self.nodes.link(before, k);
311 self.nodes.link(k, q);
312 }
313
314 pub fn margin_kern(&mut self, n: u32, left: bool) -> Option<i32> {
319 let b = self
320 .eqtb
321 .boxes
322 .get(n as usize)
323 .map_or(NodeRef::NULL, |b| b.val);
324 if b.is_null() || self.nodes.id(b) != id::HLIST {
325 self.luatex_fatal("marginkern", "a non-empty hbox expected");
326 }
327 let mut v = Vec::new();
328 let mut p = self.nodes.list_ptr(b);
329 while !p.is_null() {
330 v.push(p);
331 p = self.nodes.next(p);
332 }
333 if !left {
334 v.reverse();
335 }
336 let want = if left { LEFT_SIDE } else { RIGHT_SIDE };
337 for p in v {
338 match self.nodes.data(p) {
339 NodeData::MarginKern { width, .. } => {
340 return (self.nodes.subtype(p) == want).then_some(*width);
341 }
342 NodeData::Glue { .. } | NodeData::Penalty { .. } => {}
343 _ => return None,
344 }
345 }
346 None
347 }
348
349 pub fn strip_margin_kerns(&mut self, mut head: NodeRef) -> NodeRef {
352 while !head.is_null() && self.nodes.id(head) == id::MARGIN_KERN {
353 let n = self.nodes.next(head);
354 self.nodes.free(head);
355 head = n;
356 }
357 if head.is_null() {
358 return head;
359 }
360 self.nodes.set_prev(head, NodeRef::NULL);
361 let mut prev = head;
362 let mut p = self.nodes.next(head);
363 while !p.is_null() {
364 let n = self.nodes.next(p);
365 if self.nodes.id(p) == id::MARGIN_KERN {
366 self.nodes.free(p);
367 self.nodes.set_next(prev, n);
368 if !n.is_null() {
369 self.nodes.set_prev(n, prev);
370 }
371 } else {
372 prev = p;
373 }
374 p = n;
375 }
376 head
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::round_div;
383
384 #[test]
385 fn rounding_halves_away_from_zero() {
386 assert_eq!(round_div(3_276_805, 10), 327_681);
387 assert_eq!(round_div(-3_276_805, 10), -327_681);
388 assert_eq!(round_div(1_966_083, 10), 196_608);
389 assert_eq!(round_div(7, 2), 4);
390 assert_eq!(round_div(-7, 2), -4);
391 }
392}