1use crate::Engine;
7use crate::build::PackSpec;
8use crate::nodes::{BoxData, NodeData, NodeRef, glue_sub, id};
9use crate::primitives_gen::{assign_dimen as dp, assign_int as ip};
10
11#[derive(Default, Clone, Copy)]
14struct Totals {
15 stretch: [i32; 5],
16 shrink: [i32; 5],
17}
18
19impl Totals {
20 #[inline]
21 fn add(&mut self, g: &crate::glue::Glue) {
22 let so = (g.stretch_order as usize).min(4);
23 let ho = (g.shrink_order as usize).min(4);
24 self.stretch[so] = self.stretch[so].wrapping_add(g.stretch);
25 self.shrink[ho] = self.shrink[ho].wrapping_add(g.shrink);
26 }
27
28 fn stretch_order(&self) -> u8 {
29 (1..5).rev().find(|&o| self.stretch[o] != 0).unwrap_or(0) as u8
30 }
31
32 fn shrink_order(&self) -> u8 {
33 (1..5).rev().find(|&o| self.shrink[o] != 0).unwrap_or(0) as u8
34 }
35}
36
37impl Engine {
38 #[inline]
40 pub fn glyph_dimensions(&self, p: NodeRef) -> (i32, i32, i32) {
41 match self.nodes.data(p) {
42 NodeData::Glyph(g) => match self.fonts.char_metrics(g.font, g.chr) {
43 Some(m) => (
44 crate::expansion::expanded_width(m.width, g.expansion_factor),
45 m.height + g.yoffset,
46 m.depth - g.yoffset,
47 ),
48 None => (0, 0, 0),
49 },
50 _ => (0, 0, 0),
51 }
52 }
53
54 fn take_pack_dir(&mut self, r: NodeRef) {
56 let dir = std::mem::replace(&mut self.pack_dir, -1);
57 if dir >= 0
58 && let Some(b) = self.nodes.box_data_mut(r)
59 {
60 b.dir = dir;
61 }
62 }
63
64 pub fn hpack(&mut self, p: NodeRef, spec: PackSpec) -> NodeRef {
66 self.fonts.sync_dense();
67 self.last_badness = 0;
68 let r = self.nodes.new_null_box(true);
69 self.take_pack_dir(r);
70 let (mut h, mut d, mut x) = (0i32, 0i32, 0i32);
71 let mut tot = Totals::default();
72 let head = self.nodes.new_temp();
75 self.nodes.link(head, p);
76 let mut q = head;
77 let mut p = p;
78 while !p.is_null() {
79 let next = self.nodes.next(p);
80 match self.nodes.data(p) {
81 NodeData::Glyph(_) => {
82 let (w, gh, gd) = self.glyph_dimensions(p);
83 x += w;
84 if gh > h {
85 h = gh;
86 }
87 if gd > d {
88 d = gd;
89 }
90 }
91 NodeData::Box(b) => {
92 x += b.width;
93 let s = b.shift;
94 if b.height - s > h {
95 h = b.height - s;
96 }
97 if b.depth + s > d {
98 d = b.depth + s;
99 }
100 }
101 NodeData::Unset { b, .. } => {
102 x += b.width;
103 if b.height > h {
104 h = b.height;
105 }
106 if b.depth > d {
107 d = b.depth;
108 }
109 }
110 NodeData::Rule {
111 width,
112 height,
113 depth,
114 ..
115 } => {
116 x += *width;
117 if *height > h {
118 h = *height;
119 }
120 if *depth > d {
121 d = *depth;
122 }
123 }
124 NodeData::Ins { .. } | NodeData::Mark { .. } | NodeData::Adjust { .. } => {
125 if let Some(at) = self.ts.adjust_tail {
126 self.nodes.set_next(q, next);
128 if !next.is_null() {
129 self.nodes.set_prev(next, q);
130 }
131 let is_pre = self.nodes.id(p) == id::ADJUST && self.nodes.subtype(p) == 1;
132 if self.nodes.id(p) == id::ADJUST {
133 let l = self.nodes.list_ptr(p);
134 self.nodes.set_list_ptr(p, NodeRef::NULL);
135 self.nodes.free(p);
136 if !l.is_null() {
137 let target = if is_pre {
138 self.ts.pre_adjust_tail.unwrap_or(at)
139 } else {
140 at
141 };
142 self.nodes.link(target, l);
143 let t = self.nodes.tail_of(l);
144 if is_pre && self.ts.pre_adjust_tail.is_some() {
145 self.ts.pre_adjust_tail = Some(t);
146 } else {
147 self.ts.adjust_tail = Some(t);
148 }
149 }
150 } else {
151 self.nodes.link(at, p);
152 self.nodes.set_next(p, NodeRef::NULL);
153 self.ts.adjust_tail = Some(p);
154 }
155 p = next;
156 continue;
157 }
158 }
159 NodeData::Glue { spec, leader } => {
160 x += spec.width;
161 tot.add(spec);
162 let (leader, sub) = (*leader, self.nodes.subtype(p));
163 if sub >= glue_sub::A_LEADERS && !leader.is_null() {
164 let (lh, ld) = (self.node_height(leader), self.node_depth(leader));
165 if lh > h {
166 h = lh;
167 }
168 if ld > d {
169 d = ld;
170 }
171 }
172 }
173 NodeData::Kern {
174 kern,
175 expansion_factor,
176 } => x += *kern + *expansion_factor,
177 NodeData::MarginKern { width, .. } => x += *width,
178 NodeData::Math { surround, glue } => {
179 if glue.width != 0 || glue.stretch != 0 || glue.shrink != 0 {
180 x += glue.width;
181 let g = *glue;
182 tot.add(&g);
183 } else {
184 x += *surround;
185 }
186 }
187 NodeData::Disc { replace, .. } => {
188 let mut s = *replace;
190 while !s.is_null() {
191 let (w, sh, sd) = self.simple_dimensions(s);
192 x += w;
193 if sh > h {
194 h = sh;
195 }
196 if sd > d {
197 d = sd;
198 }
199 s = self.nodes.next(s);
200 }
201 }
202 _ => {}
203 }
204 q = p;
205 p = next;
206 }
207 if let Some(at) = self.ts.adjust_tail {
208 self.nodes.set_next(at, NodeRef::NULL);
209 }
210 if let Some(at) = self.ts.pre_adjust_tail {
211 self.nodes.set_next(at, NodeRef::NULL);
212 }
213 let first = self.nodes.next(head);
214 self.nodes.free(head);
215 if !first.is_null() {
216 self.nodes.set_prev(first, NodeRef::NULL);
217 }
218 let w = match spec {
219 PackSpec::Exactly(w) => w,
220 PackSpec::Additional(w) => x + w,
221 };
222 {
223 let b = self.box_mut(r);
224 b.head = first;
225 b.height = h;
226 b.depth = d;
227 b.width = w;
228 }
229 self.ts.last_totals = (tot.stretch, tot.shrink);
230 let x = w - x;
231 let report: Option<()> = if x == 0 {
232 None
233 } else if x > 0 {
234 let o = tot.stretch_order();
235 let ts = tot.stretch[o as usize];
236 {
237 let b = self.box_mut(r);
238 b.glue_order = o;
239 b.glue_sign = 1;
240 if ts != 0 {
241 b.glue_set = crate::nodes::glue_ratio(x, ts);
242 } else {
243 b.glue_sign = 0;
244 b.glue_set = 0.0;
245 }
246 }
247 if o == 0 && !first.is_null() {
248 self.last_badness = retex_arith::badness(x, tot.stretch[0]);
249 if self.last_badness > self.eqtb.int(ip::HBADNESS) {
250 self.printer.print_ln();
251 if self.last_badness > 100 {
252 self.diag_capture_start();
253 self.printer.print_nl("Underfull");
254 } else {
255 self.printer.print_nl("Loose");
256 }
257 self.printer.print_str(" \\hbox (badness ");
258 self.printer.print_int(i64::from(self.last_badness));
259 Some(())
260 } else {
261 None
262 }
263 } else {
264 None
265 }
266 } else {
267 let o = tot.shrink_order();
268 let ts = tot.shrink[o as usize];
269 {
270 let b = self.box_mut(r);
271 b.glue_order = o;
272 b.glue_sign = 2;
273 if ts != 0 {
274 b.glue_set = crate::nodes::glue_ratio(-x, ts);
275 } else {
276 b.glue_sign = 0;
277 b.glue_set = 0.0;
278 }
279 }
280 if ts < -x && o == 0 && !first.is_null() {
281 self.last_badness = 1_000_000;
282 self.box_mut(r).glue_set = 1.0;
283 let excess = -x - tot.shrink[0];
284 let hfuzz = self.eqtb.dimen(dp::HFUZZ);
285 if excess > hfuzz || self.eqtb.int(ip::HBADNESS) < 100 {
286 let ofr = self.eqtb.dimen(dp::OVERFULLRULE);
287 if ofr > 0 && excess > hfuzz {
288 let rule = self.nodes.new_rule(ofr, NULL_RULE, NULL_RULE);
289 if first.is_null() {
290 self.box_mut(r).head = rule;
291 } else {
292 let t = self.nodes.tail_of(first);
293 self.nodes.link(t, rule);
294 }
295 }
296 self.printer.print_ln();
297 self.diag_capture_start();
298 self.printer.print_nl("Overfull \\hbox (");
299 self.printer.print_scaled(excess);
300 self.printer.print_str("pt too wide");
301 Some(())
302 } else {
303 None
304 }
305 } else if o == 0 && !first.is_null() {
306 self.last_badness = retex_arith::badness(-x, tot.shrink[0]);
307 if self.last_badness > self.eqtb.int(ip::HBADNESS) {
308 self.printer.print_ln();
309 self.diag_capture_start();
310 self.printer.print_nl("Tight \\hbox (badness ");
311 self.printer.print_int(i64::from(self.last_badness));
312 Some(())
313 } else {
314 None
315 }
316 } else {
317 None
318 }
319 };
320 if report.is_some() {
321 self.finish_hbox_diagnostic(r);
322 }
323 r
324 }
325
326 pub fn natural_dimensions(&self, mut p: NodeRef, stop: NodeRef) -> (i32, i32, i32) {
330 let (mut w, mut h, mut d) = (0i32, 0i32, 0i32);
331 while !p.is_null() && p != stop {
332 let (pw, ph, pd) = match self.nodes.data(p) {
333 NodeData::Glyph(_) => self.glyph_dimensions(p),
334 NodeData::Box(b) => (b.width, b.height - b.shift, b.depth + b.shift),
335 NodeData::Rule {
336 width,
337 height,
338 depth,
339 ..
340 } => (*width, *height, *depth),
341 NodeData::Disc { replace, .. } => {
342 let r = *replace;
343 self.natural_dimensions(r, NodeRef::NULL)
344 }
345 _ => (self.nodes.width(p), 0, 0),
346 };
347 w += pw;
348 h = h.max(ph);
349 d = d.max(pd);
350 p = self.nodes.next(p);
351 }
352 (w, h, d)
353 }
354
355 fn simple_dimensions(&self, p: NodeRef) -> (i32, i32, i32) {
357 match self.nodes.data(p) {
358 NodeData::Glyph(_) => self.glyph_dimensions(p),
359 NodeData::Box(b) => (b.width, b.height - b.shift, b.depth + b.shift),
360 NodeData::Rule {
361 width,
362 height,
363 depth,
364 ..
365 } => (*width, *height, *depth),
366 NodeData::Kern {
367 kern,
368 expansion_factor,
369 } => (*kern + *expansion_factor, 0, 0),
370 _ => (0, 0, 0),
371 }
372 }
373
374 fn box_mut(&mut self, r: NodeRef) -> &mut BoxData {
375 match self.nodes.data_mut(r) {
376 NodeData::Box(b) => b,
377 _ => unreachable!("box_mut on a non-box node"),
378 }
379 }
380
381 fn finish_hbox_diagnostic(&mut self, r: NodeRef) {
383 if self.ts.output_active {
384 self.printer
385 .print_str(") has occurred while \\output is active");
386 } else {
387 let pbl = self.ts.pack_begin_line;
388 if pbl != 0 {
389 if pbl > 0 {
390 self.printer.print_str(") in paragraph at lines ");
391 } else {
392 self.printer.print_str(") in alignment at lines ");
393 }
394 self.printer.print_int(i64::from(pbl.abs()));
395 self.printer.print_str("--");
396 } else {
397 self.printer.print_str(") detected at line ");
398 }
399 self.printer.print_int(i64::from(self.input.line()));
400 }
401 let at = (self.ts.pack_begin_line != 0).then(|| self.ts.pack_begin_line.unsigned_abs());
402 self.diag_capture_warning(at);
403 self.printer.print_ln();
404 self.ts.font_in_short_display = 0;
405 let l = self.nodes.list_ptr(r);
406 self.short_display(l);
407 self.printer.print_ln();
408 self.begin_diagnostic();
409 self.show_box(r);
410 self.end_diagnostic(true);
411 }
412
413 pub fn vpack(&mut self, p: NodeRef, spec: PackSpec) -> NodeRef {
415 self.vpackage(p, spec, retex_arith::MAX_DIMEN)
416 }
417
418 pub fn vpackage(&mut self, p: NodeRef, spec: PackSpec, l: i32) -> NodeRef {
421 self.last_badness = 0;
422 let r = self.nodes.new_null_box(false);
423 self.take_pack_dir(r);
424 let (mut w, mut d, mut x) = (0i32, 0i32, 0i32);
425 let mut tot = Totals::default();
426 let mut q = p;
427 while !q.is_null() {
428 match self.nodes.data(q) {
429 NodeData::Box(b) => {
430 x += d + b.height;
431 d = b.depth;
432 if b.width + b.shift > w {
433 w = b.width + b.shift;
434 }
435 }
436 NodeData::Unset { b, .. } => {
437 x += d + b.height;
438 d = b.depth;
439 if b.width > w {
440 w = b.width;
441 }
442 }
443 NodeData::Rule {
444 width,
445 height,
446 depth,
447 ..
448 } => {
449 x += d + *height;
450 d = *depth;
451 if *width > w {
452 w = *width;
453 }
454 }
455 NodeData::Glue { spec, leader } => {
456 x += d;
457 d = 0;
458 x += spec.width;
459 tot.add(spec);
460 let leader = *leader;
461 if self.nodes.subtype(q) >= glue_sub::A_LEADERS && !leader.is_null() {
462 let lw = self.nodes.width(leader);
463 if lw > w {
464 w = lw;
465 }
466 }
467 }
468 NodeData::Kern { kern, .. } => {
469 x += d + *kern;
470 d = 0;
471 }
472 _ => {}
473 }
474 q = self.nodes.next(q);
475 }
476 {
477 let b = self.box_mut(r);
478 b.width = w;
479 b.head = p;
480 }
481 if d > l {
482 x += d - l;
483 self.box_mut(r).depth = l;
484 } else {
485 self.box_mut(r).depth = d;
486 }
487 let h = match spec {
488 PackSpec::Exactly(h) => h,
489 PackSpec::Additional(h) => x + h,
490 };
491 self.box_mut(r).height = h;
492 self.ts.last_totals = (tot.stretch, tot.shrink);
493 let x = h - x;
494 let report = if x == 0 {
495 false
496 } else if x > 0 {
497 let o = tot.stretch_order();
498 let ts = tot.stretch[o as usize];
499 {
500 let b = self.box_mut(r);
501 b.glue_order = o;
502 b.glue_sign = 1;
503 if ts != 0 {
504 b.glue_set = crate::nodes::glue_ratio(x, ts);
505 } else {
506 b.glue_sign = 0;
507 b.glue_set = 0.0;
508 }
509 }
510 if o == 0 && !p.is_null() {
511 self.last_badness = retex_arith::badness(x, tot.stretch[0]);
512 if self.last_badness > self.eqtb.int(ip::VBADNESS) {
513 self.printer.print_ln();
514 if self.last_badness > 100 {
515 self.diag_capture_start();
516 self.printer.print_nl("Underfull");
517 } else {
518 self.printer.print_nl("Loose");
519 }
520 self.printer.print_str(" \\vbox (badness ");
521 self.printer.print_int(i64::from(self.last_badness));
522 true
523 } else {
524 false
525 }
526 } else {
527 false
528 }
529 } else {
530 let o = tot.shrink_order();
531 let ts = tot.shrink[o as usize];
532 {
533 let b = self.box_mut(r);
534 b.glue_order = o;
535 b.glue_sign = 2;
536 if ts != 0 {
537 b.glue_set = crate::nodes::glue_ratio(-x, ts);
538 } else {
539 b.glue_sign = 0;
540 b.glue_set = 0.0;
541 }
542 }
543 if ts < -x && o == 0 && !p.is_null() {
544 self.last_badness = 1_000_000;
545 self.box_mut(r).glue_set = 1.0;
546 let excess = -x - tot.shrink[0];
547 if excess > self.eqtb.dimen(dp::VFUZZ) || self.eqtb.int(ip::VBADNESS) < 100 {
548 self.printer.print_ln();
549 self.diag_capture_start();
550 self.printer.print_nl("Overfull \\vbox (");
551 self.printer.print_scaled(excess);
552 self.printer.print_str("pt too high");
553 true
554 } else {
555 false
556 }
557 } else if o == 0 && !p.is_null() {
558 self.last_badness = retex_arith::badness(-x, tot.shrink[0]);
559 if self.last_badness > self.eqtb.int(ip::VBADNESS) {
560 self.printer.print_ln();
561 self.diag_capture_start();
562 self.printer.print_nl("Tight \\vbox (badness ");
563 self.printer.print_int(i64::from(self.last_badness));
564 true
565 } else {
566 false
567 }
568 } else {
569 false
570 }
571 };
572 if report {
573 if self.ts.output_active {
574 self.printer
575 .print_str(") has occurred while \\output is active");
576 } else {
577 let pbl = self.ts.pack_begin_line;
578 if pbl != 0 {
579 self.printer.print_str(") in alignment at lines ");
580 self.printer.print_int(i64::from(pbl.abs()));
581 self.printer.print_str("--");
582 } else {
583 self.printer.print_str(") detected at line ");
584 }
585 self.printer.print_int(i64::from(self.input.line()));
586 }
587 let at = (self.ts.pack_begin_line != 0).then(|| self.ts.pack_begin_line.unsigned_abs());
588 self.diag_capture_warning(at);
589 if !self.ts.output_active {
590 self.printer.print_ln();
591 }
592 self.begin_diagnostic();
593 self.show_box(r);
594 self.end_diagnostic(true);
595 }
596 r
597 }
598}
599
600const NULL_RULE: i32 = crate::typeset::NULL_FLAG;