1#![allow(clippy::needless_range_loop)]
19
20use std::rc::Rc;
21
22use crate::Engine;
23use crate::build::PackSpec;
24use crate::glue::Glue;
25use crate::nodes::{NodeData, NodeRef, glue_sub, id, kern_sub};
26use crate::primitives_gen::{
27 assign_dimen as dp, assign_glue as gp, assign_int as ip, set_etex_shape as es,
28};
29use crate::typeset::{AWFUL_BAD, EJECT_PENALTY, INF_BAD, INF_PENALTY};
30
31const VERY_LOOSE_FIT: usize = 0;
32const LOOSE_FIT: usize = 1;
33const DECENT_FIT: usize = 2;
34const TIGHT_FIT: usize = 3;
35
36type Widths = [i32; NW];
39
40const NW: usize = 9;
42const FONT_STRETCH: usize = 7;
44const FONT_SHRINK: usize = 8;
46
47#[inline]
48fn add_glue(w: &mut Widths, g: &Glue) {
49 w[0] += g.width;
50 w[1 + (g.stretch_order as usize).min(4)] += g.stretch;
51 w[6] += g.shrink;
52}
53
54#[derive(Clone, Debug)]
56enum Entry {
57 Sentinel,
59 Active {
61 passive: Option<usize>,
62 line_number: i32,
63 fitness: usize,
64 hyphenated: bool,
65 total_demerits: i32,
66 short: i32,
67 glue: i32,
68 },
69 Delta(Widths),
71}
72
73struct Item {
74 next: usize,
75 entry: Entry,
76}
77
78struct Passive {
80 cur_break: NodeRef,
81 prev_break: Option<usize>,
82 serial: i32,
83 left_pw: i32,
85}
86
87struct Breaker {
89 items: Vec<Item>,
90 free: Vec<usize>,
91 passive: Vec<Passive>,
92 active_width: Widths,
93 cur_active_width: Widths,
94 background: Widths,
95 break_width: Widths,
96 minimal_demerits: [i32; 4],
97 minimum_demerits: i32,
98 best_place: [Option<usize>; 4],
99 best_pl_line: [i32; 4],
100 best_pl_short: [i32; 4],
101 best_pl_glue: [i32; 4],
102 threshold: i32,
103 second_pass: bool,
104 final_pass: bool,
105 disc_width: i32,
106 disc_fs: crate::expansion::FontStretch,
108 easy_line: i32,
109 last_special_line: i32,
110 first_width: i32,
111 second_width: i32,
112 first_indent: i32,
113 second_indent: i32,
114 par_shape: Option<std::rc::Rc<[i32]>>,
115 printed_node: NodeRef,
116 pass_number: i32,
117 do_last_line_fit: bool,
118 fill_width: [i32; 4],
119 last_line_fit: i32,
120 adj_demerits: i32,
121 line_penalty: i32,
122 double_hyphen_demerits: i32,
123 final_hyphen_demerits: i32,
124 tracing: bool,
125 no_shrink_error_yet: bool,
126 protrude: bool,
128 first_left_pw: i32,
130 head: NodeRef,
132 expand: bool,
134 expand_kerns: bool,
136}
137
138const ACTIVE: usize = 0;
139
140impl Breaker {
141 fn alloc(&mut self, e: Entry, next: usize) -> usize {
142 if let Some(i) = self.free.pop() {
143 self.items[i] = Item { next, entry: e };
144 i
145 } else {
146 self.items.push(Item { next, entry: e });
147 self.items.len() - 1
148 }
149 }
150
151 fn release(&mut self, i: usize) {
152 self.free.push(i);
153 }
154
155 fn is_delta(&self, i: usize) -> bool {
156 matches!(self.items[i].entry, Entry::Delta(_))
157 }
158
159 fn delta(&self, i: usize) -> Widths {
160 match self.items[i].entry {
161 Entry::Delta(d) => d,
162 _ => [0; NW],
163 }
164 }
165
166 fn line_number(&self, i: usize) -> i32 {
167 match self.items[i].entry {
168 Entry::Active { line_number, .. } => line_number,
169 Entry::Sentinel => i32::MAX,
170 Entry::Delta(_) => 0,
171 }
172 }
173
174 fn clean_up(&mut self) {
176 self.items.truncate(1);
177 self.items[ACTIVE].next = ACTIVE;
178 self.free.clear();
179 self.passive.clear();
180 }
181}
182
183#[derive(Clone, Debug)]
186pub struct ParParams {
187 pub pretolerance: i32,
189 pub tolerance: i32,
191 pub looseness: i32,
193 pub adj_demerits: i32,
195 pub line_penalty: i32,
197 pub last_line_fit: i32,
199 pub double_hyphen_demerits: i32,
201 pub final_hyphen_demerits: i32,
203 pub hang_after: i32,
205 pub hang_indent: i32,
207 pub hsize: i32,
209 pub emergency_stretch: i32,
211 pub broken_penalty: i32,
213 pub protrude_chars: i32,
215 pub adjust_spacing: i32,
217 pub tracing_paragraphs: i32,
219 pub interline_penalty: i32,
221 pub club_penalty: i32,
223 pub widow_penalty: i32,
226 pub interline_penalties: Option<Rc<[i32]>>,
228 pub club_penalties: Option<Rc<[i32]>>,
230 pub widow_penalties: Option<Rc<[i32]>>,
232 pub left_skip: Glue,
234 pub right_skip: Glue,
236 pub par_shape: Option<Rc<[i32]>>,
238 pub par_dir: i8,
240}
241
242#[derive(Clone, Copy, Debug)]
244pub struct LineBreakInfo {
245 pub prev_depth: i32,
247 pub prev_graf: i32,
249 pub looseness: i32,
251 pub demerits: i32,
253}
254
255impl Engine {
256 fn disc_node_width(&self, s: NodeRef) -> i32 {
258 match self.nodes.data(s) {
259 NodeData::Glyph(_) => self.glyph_dimensions(s).0,
260 _ => self.nodes.width(s),
261 }
262 }
263
264 fn list_width(&self, mut s: NodeRef) -> i32 {
265 let mut w = 0;
266 while !s.is_null() {
267 w += self.disc_node_width(s);
268 s = self.nodes.next(s);
269 }
270 w
271 }
272
273 fn finite_shrink(&mut self, br: &mut Breaker, g: Glue) -> Glue {
275 if br.no_shrink_error_yet {
276 br.no_shrink_error_yet = false;
277 if br.tracing {
278 self.end_diagnostic(true);
279 }
280 self.print_err("Infinite glue shrinkage found in a paragraph");
281 self.help(&[
282 "The paragraph just ended includes some glue that has",
283 "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.",
284 "Such glue doesn't belong there---it allows a paragraph",
285 "of any length to fit on one line. But it's safe to proceed,",
286 "since the offensive shrinkability has been made finite.",
287 ]);
288 self.error();
289 if br.tracing {
290 self.begin_diagnostic();
291 }
292 }
293 Glue {
294 shrink_order: 0,
295 ..g
296 }
297 }
298
299 pub fn line_break(&mut self, final_widow_penalty: i32) {
304 self.fonts.sync_dense();
305 let display = self.cur_cmd == crate::cmd::Cmd::MathShift;
308 let group = if display { "math_shift" } else { "" };
309 self.ts.pack_begin_line = self.cur_list().mode_line;
310 let temp_head = self.nodes.new_temp();
313 let head = self.cur_list().head;
314 let first = self.nodes.next(head);
315 self.nodes.set_next(head, NodeRef::NULL);
316 let first = self.node_process_list(first, crate::nodeproc::Context::Paragraph);
320 self.nodes.link(temp_head, first);
321 if first.is_null() {
322 self.nodes.set_next(temp_head, NodeRef::NULL);
323 }
324 let mut tail = if first.is_null() {
325 temp_head
326 } else {
327 self.nodes.tail_of(first)
328 };
329 if self.nodes.id(tail) == id::GLUE {
330 let p = self.find_prev_in(temp_head, tail);
332 self.nodes.set_next(p, NodeRef::NULL);
333 self.nodes.flush_list(tail);
334 tail = p;
335 }
336 let pen = self.new_penalty_node(INF_PENALTY);
337 self.nodes.set_subtype(pen, 2);
339 self.nodes.link(tail, pen);
340 let pfs = self.new_param_glue(glue_sub::PAR_FILL_SKIP);
341 self.nodes.link(pen, pfs);
342 let last_line_fill = pfs;
343 self.pop_nest();
344 let first = self.nodes.next(temp_head);
346 let had_list = !first.is_null();
347 let first = self.filter_list("pre_linebreak_filter", first, group);
348 if had_list && first.is_null() {
349 self.confusion("weird par dir");
352 }
353 self.nodes.link(temp_head, first);
354 if first.is_null() {
355 self.nodes.set_next(temp_head, NodeRef::NULL);
356 }
357 let old_tail = self.cur_list().tail;
358 let replaced = match self.lua.clone() {
359 Some(h) if h.has_callback("linebreak_filter") => {
360 let first = self.nodes.next(temp_head);
361 h.linebreak_filter(self, first, display)
362 }
363 _ => None,
364 };
365 if let Some(list) = replaced {
366 let mut p = list;
369 while !p.is_null() && self.nodes.id(p) != id::HLIST {
370 p = self.nodes.next(p);
371 }
372 if p.is_null() {
373 self.print_err("Invalid linebreak_filter");
374 self.help(&[
375 "A linebreaking routine should return a non-empty list of nodes",
376 "and at least one of those has to be a \\hbox.",
377 "Sorry, I cannot recover from this.",
378 ]);
379 self.succumb();
380 }
381 self.nodes.link(old_tail, list);
382 let t = self.nodes.tail_of(list);
383 self.cur_list_mut().tail = t;
384 } else {
385 let params = self.par_params(final_widow_penalty, display);
386 self.break_paragraph(temp_head, Some(last_line_fill), ¶ms);
387 }
388 if self.has_callback("post_linebreak_filter") {
389 let first = self.nodes.next(old_tail);
390 self.nodes.set_next(old_tail, NodeRef::NULL);
391 let first = self.filter_list("post_linebreak_filter", first, group);
392 self.cur_list_mut().tail = old_tail;
393 if !first.is_null() {
394 self.nodes.link(old_tail, first);
395 let t = self.nodes.tail_of(first);
396 self.cur_list_mut().tail = t;
397 }
398 }
399 self.nodes.free(temp_head);
400 self.ts.pack_begin_line = 0;
401 }
402
403 pub fn par_params(&self, widow_penalty: i32, display: bool) -> ParParams {
407 let array = |k: u32| self.eqtb.etex_shape[k as usize].val.clone();
408 ParParams {
409 pretolerance: self.eqtb.int(ip::PRETOLERANCE),
410 tolerance: self.eqtb.int(ip::TOLERANCE),
411 looseness: self.eqtb.int(ip::LOOSENESS),
412 adj_demerits: self.eqtb.int(ip::ADJDEMERITS),
413 line_penalty: self.eqtb.int(ip::LINEPENALTY),
414 last_line_fit: self.eqtb.int(ip::LASTLINEFIT),
415 double_hyphen_demerits: self.eqtb.int(ip::DOUBLEHYPHENDEMERITS),
416 final_hyphen_demerits: self.eqtb.int(ip::FINALHYPHENDEMERITS),
417 hang_after: self.eqtb.int(ip::HANGAFTER),
418 hang_indent: self.eqtb.dimen(dp::HANGINDENT),
419 hsize: self.eqtb.dimen(dp::HSIZE),
420 emergency_stretch: self.eqtb.dimen(dp::EMERGENCYSTRETCH),
421 broken_penalty: self.eqtb.int(ip::BROKENPENALTY),
422 protrude_chars: self.eqtb.int(ip::PROTRUDECHARS),
423 adjust_spacing: self.eqtb.int(ip::ADJUSTSPACING),
424 tracing_paragraphs: self.eqtb.int(ip::TRACINGPARAGRAPHS),
425 interline_penalty: self.eqtb.int(ip::INTERLINEPENALTY),
426 club_penalty: self.eqtb.int(ip::CLUBPENALTY),
427 widow_penalty,
428 interline_penalties: array(es::INTERLINEPENALTIES),
429 club_penalties: array(es::CLUBPENALTIES),
430 widow_penalties: array(if display {
431 es::DISPLAYWIDOWPENALTIES
432 } else {
433 es::WIDOWPENALTIES
434 }),
435 left_skip: self.eqtb.glue(gp::LEFTSKIP),
436 right_skip: self.eqtb.glue(gp::RIGHTSKIP),
437 par_shape: self.eqtb.par_shape.val.clone(),
438 par_dir: self.dir_param(crate::primitives_gen::assign_dir::PARDIR) as i8,
439 }
440 }
441
442 pub fn lua_line_break(&mut self, head: NodeRef, p: &ParParams) -> (NodeRef, LineBreakInfo) {
450 if self.nodes.id(self.nodes.tail_of(head)) == id::GLYPH {
451 self.luatex_fatal("linebreak", "invalid list tail, probably missing glue");
453 }
454 self.fonts.sync_dense();
455 let prev_depth = self.cur_list().prev_depth;
456 self.push_nest(crate::maincontrol::Mode::InternalVertical);
457 self.cur_list_mut().prev_depth = prev_depth;
458 let temp_head = self.nodes.new_temp();
459 self.nodes.link(temp_head, head);
460 let (looseness, demerits) = self.break_paragraph(temp_head, None, p);
461 self.nodes.free(temp_head);
462 let list_head = self.cur_list().head;
463 let lines = self.nodes.next(list_head);
464 self.nodes.set_next(list_head, NodeRef::NULL);
465 if !lines.is_null() {
466 self.nodes.set_prev(lines, NodeRef::NULL);
467 }
468 let info = LineBreakInfo {
469 prev_depth: self.cur_list().prev_depth,
470 prev_graf: self.cur_list().prev_graf,
471 looseness,
472 demerits,
473 };
474 self.pop_nest();
475 (lines, info)
476 }
477
478 fn break_paragraph(
483 &mut self,
484 temp_head: NodeRef,
485 last_line_fill: Option<NodeRef>,
486 p: &ParParams,
487 ) -> (i32, i32) {
488 let mut br = Breaker {
489 items: vec![Item {
490 next: ACTIVE,
491 entry: Entry::Sentinel,
492 }],
493 free: Vec::new(),
494 passive: Vec::new(),
495 active_width: [0; NW],
496 cur_active_width: [0; NW],
497 background: [0; NW],
498 break_width: [0; NW],
499 minimal_demerits: [AWFUL_BAD; 4],
500 minimum_demerits: AWFUL_BAD,
501 best_place: [None; 4],
502 best_pl_line: [0; 4],
503 best_pl_short: [0; 4],
504 best_pl_glue: [0; 4],
505 threshold: 0,
506 second_pass: false,
507 final_pass: false,
508 disc_width: 0,
509 disc_fs: Default::default(),
510 easy_line: 0,
511 last_special_line: 0,
512 first_width: 0,
513 second_width: 0,
514 first_indent: 0,
515 second_indent: 0,
516 par_shape: p.par_shape.clone(),
517 printed_node: temp_head,
518 pass_number: 0,
519 do_last_line_fit: false,
520 fill_width: [0; 4],
521 last_line_fit: p.last_line_fit,
522 adj_demerits: p.adj_demerits,
523 line_penalty: p.line_penalty,
524 double_hyphen_demerits: p.double_hyphen_demerits,
525 final_hyphen_demerits: p.final_hyphen_demerits,
526 tracing: p.tracing_paragraphs > 0,
527 no_shrink_error_yet: true,
528 protrude: p.protrude_chars > 1,
529 first_left_pw: 0,
530 head: temp_head,
531 expand: p.adjust_spacing > 1,
532 expand_kerns: p.adjust_spacing == 2,
533 };
534 if br.expand && !br.protrude {
535 self.fix_back_links(temp_head);
536 }
537 if br.protrude {
538 self.fix_back_links(temp_head);
539 let first = self.nodes.next(temp_head);
540 br.first_left_pw = self.lb_left_protrusion(None, first);
541 }
542 let mut ls = p.left_skip;
543 let mut rs = p.right_skip;
544 if ls.shrink_order != 0 && ls.shrink != 0 {
545 ls = self.finite_shrink(&mut br, ls);
546 }
547 if rs.shrink_order != 0 && rs.shrink != 0 {
548 rs = self.finite_shrink(&mut br, rs);
549 }
550 br.background[0] = ls.width + rs.width;
551 br.background[1 + (ls.stretch_order as usize).min(4)] += ls.stretch;
552 br.background[1 + (rs.stretch_order as usize).min(4)] += rs.stretch;
553 br.background[6] = ls.shrink + rs.shrink;
554 if let Some(last_line_fill) = last_line_fill
556 && br.last_line_fit > 0
557 {
558 let q = self.nodes.glue_spec(last_line_fill);
559 if q.stretch > 0 && q.stretch_order > 0 && br.background[2..6].iter().all(|&v| v == 0) {
560 br.do_last_line_fit = true;
561 br.fill_width = [0; 4];
562 br.fill_width[(q.stretch_order as usize - 1).min(3)] = q.stretch;
563 }
564 }
565 let hsize = p.hsize;
567 let hang_indent = p.hang_indent;
568 let hang_after = p.hang_after;
569 match &br.par_shape {
570 None => {
571 if hang_indent == 0 {
572 br.last_special_line = 0;
573 br.second_width = hsize;
574 br.second_indent = 0;
575 } else {
576 br.last_special_line = hang_after.abs();
577 if hang_after < 0 {
578 br.first_width = hsize - hang_indent.abs();
579 br.first_indent = hang_indent.max(0);
580 br.second_width = hsize;
581 br.second_indent = 0;
582 } else {
583 br.first_width = hsize;
584 br.first_indent = 0;
585 br.second_width = hsize - hang_indent.abs();
586 br.second_indent = hang_indent.max(0);
587 }
588 }
589 }
590 Some(s) => {
591 let n = (s.len() / 2) as i32;
592 br.last_special_line = n - 1;
593 br.second_indent = s[2 * (n as usize - 1)];
594 br.second_width = s[2 * (n as usize - 1) + 1];
595 }
596 }
597 br.easy_line = if p.looseness == 0 {
598 br.last_special_line
599 } else {
600 i32::MAX - 1
601 };
602 br.threshold = p.pretolerance;
604 let emergency = p.emergency_stretch;
605 if br.threshold >= 0 {
606 if br.tracing {
607 self.begin_diagnostic();
608 self.printer.print_nl("@firstpass");
609 }
610 br.second_pass = false;
611 br.final_pass = false;
612 } else {
613 br.threshold = p.tolerance;
614 br.second_pass = true;
615 br.final_pass = emergency <= 0;
616 if br.tracing {
617 self.begin_diagnostic();
618 }
619 }
620 let prev_graf = self.cur_list().prev_graf;
621 let looseness = p.looseness;
622 let best_bet: usize;
623 let best_line: i32;
624 let mut actual_looseness = 0;
625 loop {
626 if br.threshold > INF_BAD {
627 br.threshold = INF_BAD;
628 }
629 let q = br.alloc(
631 Entry::Active {
632 passive: None,
633 line_number: prev_graf + 1,
634 fitness: DECENT_FIT,
635 hyphenated: false,
636 total_demerits: 0,
637 short: 0,
638 glue: 0,
639 },
640 ACTIVE,
641 );
642 br.items[ACTIVE].next = q;
643 br.active_width = br.background;
644 br.passive.clear();
645 br.printed_node = temp_head;
646 br.pass_number = 0;
647 self.ts.font_in_short_display = 0;
648 let mut cur_p = self.nodes.next(temp_head);
649 let mut auto_breaking = true;
650 let mut prev_p = cur_p;
651 while !cur_p.is_null() && br.items[ACTIVE].next != ACTIVE {
652 let t = self.nodes.id(cur_p);
653 match t {
654 id::GLYPH => {
655 prev_p = cur_p;
657 while !cur_p.is_null() && self.nodes.id(cur_p) == id::GLYPH {
658 self.check_lost_char(cur_p);
659 br.active_width[0] += self.glyph_dimensions(cur_p).0;
660 if br.expand {
661 let f = self.glyph_font_stretch(cur_p);
662 br.active_width[FONT_STRETCH] += f.stretch;
663 br.active_width[FONT_SHRINK] += f.shrink;
664 }
665 cur_p = self.nodes.next(cur_p);
666 }
667 continue;
668 }
669 id::HLIST | id::VLIST | id::RULE => {
670 br.active_width[0] += self.nodes.width(cur_p);
671 }
672 id::GLUE => {
673 if auto_breaking {
674 let pt = self.nodes.id(prev_p);
675 let legal = pt == id::GLYPH
676 || crate::page::precedes_break(pt)
677 || (pt == id::KERN
678 && !matches!(
679 self.nodes.subtype(prev_p),
680 kern_sub::USER | kern_sub::ITALIC
681 ));
682 if legal {
683 self.try_break(&mut br, cur_p, 0, false);
684 }
685 }
686 let mut g = self.nodes.glue_spec(cur_p);
687 if g.shrink_order != 0 && g.shrink != 0 {
688 g = self.finite_shrink(&mut br, g);
689 if let NodeData::Glue { spec, .. } = self.nodes.data_mut(cur_p) {
690 *spec = g;
691 }
692 }
693 add_glue(&mut br.active_width, &g);
694 }
695 id::KERN => {
696 if matches!(self.nodes.subtype(cur_p), kern_sub::USER | kern_sub::ITALIC) {
697 self.kern_break(&mut br, cur_p, auto_breaking);
698 } else {
699 br.active_width[0] += self.nodes.width(cur_p);
700 if br.expand_kerns {
701 let (pv, nx) = (self.nodes.prev(cur_p), self.nodes.next(cur_p));
702 let f = self.kern_font_stretch(pv, cur_p, nx);
703 br.active_width[FONT_STRETCH] += f.stretch;
704 br.active_width[FONT_SHRINK] += f.shrink;
705 }
706 }
707 }
708 id::DISC => {
709 let (pre, _post, rep) = self.nodes.disc_lists(cur_p);
710 let pen = self.nodes.penalty(cur_p);
711 let from_hyphenation = self.nodes.subtype(cur_p) >= 3;
716 if br.second_pass || !from_hyphenation {
717 br.disc_width = self.list_width(pre);
718 br.active_width[0] += br.disc_width;
719 if br.expand {
720 br.disc_fs = self.list_font_stretch(pre, br.expand_kerns);
721 br.active_width[FONT_STRETCH] += br.disc_fs.stretch;
722 br.active_width[FONT_SHRINK] += br.disc_fs.shrink;
723 }
724 self.try_break(&mut br, cur_p, pen, true);
725 br.active_width[0] -= br.disc_width;
726 if br.expand {
727 br.active_width[FONT_STRETCH] -= br.disc_fs.stretch;
728 br.active_width[FONT_SHRINK] -= br.disc_fs.shrink;
729 }
730 }
731 br.active_width[0] += self.list_width(rep);
732 if br.expand {
733 let f = self.list_font_stretch(rep, br.expand_kerns);
734 br.active_width[FONT_STRETCH] += f.stretch;
735 br.active_width[FONT_SHRINK] += f.shrink;
736 }
737 }
738 id::MATH => {
739 auto_breaking = self.nodes.subtype(cur_p) == 1;
740 self.kern_break(&mut br, cur_p, auto_breaking);
741 }
742 id::PENALTY => {
743 let pen = self.nodes.penalty(cur_p);
744 self.try_break(&mut br, cur_p, pen, false);
745 }
746 id::LOCAL_PAR
747 | id::DIR
748 | id::BOUNDARY
749 | id::MARK
750 | id::INS
751 | id::ADJUST
752 | id::WHATSIT => {}
753 id::GLUE_SPEC => {
754 self.luatex_warning("parbuilder", "found a glue_spec in a paragraph")
755 }
756 _ => self.luatex_fatal("parbuilder", &format!("weird node {t} in paragraph")),
760 }
761 prev_p = cur_p;
762 cur_p = self.nodes.next(cur_p);
763 }
764 if cur_p.is_null() {
765 self.try_break(&mut br, NodeRef::NULL, EJECT_PENALTY, true);
767 if br.items[ACTIVE].next != ACTIVE {
768 let mut r = br.items[ACTIVE].next;
770 let mut fewest = AWFUL_BAD;
771 let mut bb = r;
772 while r != ACTIVE {
773 if let Entry::Active { total_demerits, .. } = br.items[r].entry
774 && total_demerits < fewest
775 {
776 fewest = total_demerits;
777 bb = r;
778 }
779 r = br.items[r].next;
780 }
781 let mut bl = br.line_number(bb);
782 if looseness == 0 {
783 best_bet = bb;
784 best_line = bl;
785 break;
786 }
787 let mut r = br.items[ACTIVE].next;
789 let mut actual = 0;
790 let mut bb2 = bb;
791 while r != ACTIVE {
792 if let Entry::Active {
793 line_number,
794 total_demerits,
795 ..
796 } = br.items[r].entry
797 {
798 let line_diff = line_number - bl;
799 if (line_diff < actual && looseness <= line_diff)
800 || (line_diff > actual && looseness >= line_diff)
801 {
802 bb2 = r;
803 actual = line_diff;
804 fewest = total_demerits;
805 } else if line_diff == actual && total_demerits < fewest {
806 bb2 = r;
807 fewest = total_demerits;
808 }
809 }
810 r = br.items[r].next;
811 }
812 bl = br.line_number(bb2);
813 if actual == looseness || br.final_pass {
814 actual_looseness = actual;
815 best_bet = bb2;
816 best_line = bl;
817 break;
818 }
819 }
820 }
821 br.clean_up();
823 if !br.second_pass {
824 if br.tracing {
825 self.printer.print_nl("@secondpass");
826 }
827 br.threshold = p.tolerance;
828 br.second_pass = true;
829 br.final_pass = emergency <= 0;
830 } else {
831 if br.tracing {
832 self.printer.print_nl("@emergencypass");
833 }
834 br.background[1] += emergency;
835 br.final_pass = true;
836 }
837 }
838 if br.tracing {
839 self.end_diagnostic(true);
840 self.normalize_selector();
841 }
842 let demerits = match br.items[best_bet].entry {
843 Entry::Active { total_demerits, .. } => total_demerits,
844 _ => 0,
845 };
846 if let Some(last_line_fill) = last_line_fill
847 && br.do_last_line_fit
848 {
849 if let Entry::Active { short, glue, .. } = br.items[best_bet].entry {
851 if short == 0 {
852 br.do_last_line_fit = false;
853 } else if let NodeData::Glue { spec, .. } = self.nodes.data_mut(last_line_fill) {
854 spec.width += short - glue;
855 spec.stretch = 0;
856 }
857 }
858 }
859 self.post_line_break(&mut br, best_bet, best_line, temp_head, p);
861 br.clean_up();
862 (actual_looseness, demerits)
863 }
864
865 fn find_prev_in(&self, head: NodeRef, q: NodeRef) -> NodeRef {
866 let mut p = head;
867 loop {
868 let n = self.nodes.next(p);
869 if n == q || n.is_null() {
870 return p;
871 }
872 p = n;
873 }
874 }
875
876 fn kern_break(&mut self, br: &mut Breaker, cur_p: NodeRef, auto_breaking: bool) {
879 let n = self.nodes.next(cur_p);
880 if !n.is_null() && auto_breaking && self.nodes.id(n) == id::GLUE {
881 self.try_break(br, cur_p, 0, false);
882 }
883 br.active_width[0] += self.nodes.width(cur_p);
884 }
885
886 fn try_break(&mut self, br: &mut Breaker, cur_p: NodeRef, pi: i32, hyphenated: bool) {
888 let mut pi = pi;
889 if pi.abs() >= INF_PENALTY {
890 if pi > 0 {
891 return;
892 }
893 pi = EJECT_PENALTY;
894 }
895 let mut no_break_yet = true;
896 let mut prev_r = ACTIVE;
897 let mut prev_prev_r = ACTIVE;
898 let mut old_l = 0;
899 let mut line_width = 0;
900 let mut right_pw: Option<i32> = None;
901 br.cur_active_width = br.active_width;
902 loop {
903 let r = br.items[prev_r].next;
904 if let Entry::Delta(d) = br.items[r].entry {
906 for k in 0..NW {
907 br.cur_active_width[k] += d[k];
908 }
909 prev_prev_r = prev_r;
910 prev_r = r;
911 continue;
912 }
913 let l = br.line_number(r);
916 if l > old_l {
917 if br.minimum_demerits < AWFUL_BAD && (old_l != br.easy_line || r == ACTIVE) {
918 if no_break_yet {
919 no_break_yet = false;
920 self.compute_break_width(br, cur_p, hyphenated);
921 }
922 if br.is_delta(prev_r) {
924 let mut d = br.delta(prev_r);
925 for k in 0..NW {
926 d[k] = d[k] - br.cur_active_width[k] + br.break_width[k];
927 }
928 br.items[prev_r].entry = Entry::Delta(d);
929 } else if prev_r == ACTIVE {
930 br.active_width = br.break_width;
931 } else {
932 let mut d = [0; NW];
933 for k in 0..NW {
934 d[k] = br.break_width[k] - br.cur_active_width[k];
935 }
936 let q = br.alloc(Entry::Delta(d), r);
937 br.items[prev_r].next = q;
938 prev_prev_r = prev_r;
939 prev_r = q;
940 }
941 if br.adj_demerits.abs() >= AWFUL_BAD - br.minimum_demerits {
942 br.minimum_demerits = AWFUL_BAD - 1;
943 } else {
944 br.minimum_demerits += br.adj_demerits.abs();
945 }
946 for fit_class in VERY_LOOSE_FIT..=TIGHT_FIT {
947 if br.minimal_demerits[fit_class] <= br.minimum_demerits {
948 br.pass_number += 1;
950 let left_pw = if br.protrude {
951 self.lb_left_protrusion(Some(cur_p), NodeRef::NULL)
952 } else {
953 0
954 };
955 br.passive.push(Passive {
956 cur_break: cur_p,
957 prev_break: br.best_place[fit_class],
958 serial: br.pass_number,
959 left_pw,
960 });
961 let pidx = br.passive.len() - 1;
962 let (short, glue) = if br.do_last_line_fit {
963 (br.best_pl_short[fit_class], br.best_pl_glue[fit_class])
964 } else {
965 (0, 0)
966 };
967 let q = br.alloc(
968 Entry::Active {
969 passive: Some(pidx),
970 line_number: br.best_pl_line[fit_class] + 1,
971 fitness: fit_class,
972 hyphenated,
973 total_demerits: br.minimal_demerits[fit_class],
974 short,
975 glue,
976 },
977 r,
978 );
979 br.items[prev_r].next = q;
980 prev_r = q;
981 if br.tracing {
982 self.printer.print_nl("@@");
983 self.printer.print_int(i64::from(br.pass_number));
984 self.printer.print_str(": line ");
985 self.printer
986 .print_int(i64::from(br.best_pl_line[fit_class]));
987 self.printer.print_char('.' as u32);
988 self.printer.print_int(fit_class as i64);
989 if hyphenated {
990 self.printer.print_char('-' as u32);
991 }
992 self.printer.print_str(" t=");
993 self.printer
994 .print_int(i64::from(br.minimal_demerits[fit_class]));
995 if br.do_last_line_fit {
996 self.printer.print_str(" s=");
997 self.printer.print_scaled(short);
998 self.printer.print_str(if cur_p.is_null() {
999 " a="
1000 } else {
1001 " g="
1002 });
1003 self.printer.print_scaled(glue);
1004 }
1005 self.printer.print_str(" -> @");
1006 match br.best_place[fit_class] {
1007 None => self.printer.print_char('0' as u32),
1008 Some(pp) => {
1009 self.printer.print_int(i64::from(br.passive[pp].serial))
1010 }
1011 }
1012 }
1013 }
1014 br.minimal_demerits[fit_class] = AWFUL_BAD;
1015 }
1016 br.minimum_demerits = AWFUL_BAD;
1017 if r != ACTIVE {
1019 let mut d = [0; NW];
1020 for k in 0..NW {
1021 d[k] = br.cur_active_width[k] - br.break_width[k];
1022 }
1023 let q = br.alloc(Entry::Delta(d), r);
1024 br.items[prev_r].next = q;
1025 prev_prev_r = prev_r;
1026 prev_r = q;
1027 }
1028 }
1029 if r == ACTIVE {
1030 return;
1031 }
1032 if l > br.easy_line {
1034 line_width = br.second_width;
1035 old_l = i32::MAX - 1;
1036 } else {
1037 old_l = l;
1038 line_width = if l > br.last_special_line {
1039 br.second_width
1040 } else {
1041 match &br.par_shape {
1042 None => br.first_width,
1043 Some(s) => s[2 * (l as usize - 1) + 1],
1044 }
1045 };
1046 }
1047 }
1048 let (r_passive, r_fitness, r_hyph, r_total, r_short, r_glue) = match br.items[r].entry {
1050 Entry::Active {
1051 passive,
1052 fitness,
1053 hyphenated,
1054 total_demerits,
1055 short,
1056 glue,
1057 ..
1058 } => (passive, fitness, hyphenated, total_demerits, short, glue),
1059 _ => (None, DECENT_FIT, false, 0, 0, 0),
1060 };
1061 let mut artificial_demerits = false;
1062 let mut shortfall = line_width - br.cur_active_width[0];
1063 if br.protrude {
1064 let rp = *right_pw.get_or_insert_with(|| self.lb_right_protrusion(cur_p, br.head));
1065 let lp = match r_passive {
1066 Some(i) => br.passive[i].left_pw,
1067 None => br.first_left_pw,
1068 };
1069 shortfall += lp + rp;
1070 }
1071 if br.expand {
1072 let caw = &br.cur_active_width;
1074 if shortfall > 0 && caw[FONT_STRETCH] > 0 {
1075 shortfall = (shortfall - caw[FONT_STRETCH]).max(0);
1076 } else if shortfall < 0 && caw[FONT_SHRINK] > 0 {
1077 shortfall = (shortfall + caw[FONT_SHRINK]).min(0);
1078 }
1079 }
1080 let mut g = 0i32;
1081 let b: i32;
1082 let fit_class: usize;
1083 let caw = br.cur_active_width;
1084 'found: {
1085 if shortfall > 0 {
1086 if caw[2] != 0 || caw[3] != 0 || caw[4] != 0 || caw[5] != 0 {
1087 if br.do_last_line_fit {
1088 if cur_p.is_null() {
1089 'not_found: {
1091 if r_short == 0 || r_glue <= 0 {
1092 break 'not_found;
1093 }
1094 if caw[2] != br.fill_width[0]
1095 || caw[3] != br.fill_width[1]
1096 || caw[4] != br.fill_width[2]
1097 || caw[5] != br.fill_width[3]
1098 {
1099 break 'not_found;
1100 }
1101 g = if r_short > 0 { caw[1] } else { caw[6] };
1102 if g <= 0 {
1103 break 'not_found;
1104 }
1105 let md = retex_arith::MAX_DIMEN;
1106 g = match retex_arith::fract(g, r_short, r_glue, md) {
1107 Ok(v) => v,
1108 Err(_) => {
1109 if r_short > 0 {
1110 md
1111 } else {
1112 -md
1113 }
1114 }
1115 };
1116 if br.last_line_fit < 1000 {
1117 g = retex_arith::fract(g, br.last_line_fit, 1000, md)
1118 .unwrap_or(if r_short > 0 { md } else { -md });
1119 }
1120 if g > 0 {
1121 if g > shortfall {
1122 g = shortfall;
1123 }
1124 if g > 7_230_584 && caw[1] < 1_663_497 {
1125 b = INF_BAD;
1126 fit_class = VERY_LOOSE_FIT;
1127 break 'found;
1128 }
1129 b = retex_arith::badness(g, caw[1]);
1130 fit_class = if b > 12 {
1131 if b > 99 { VERY_LOOSE_FIT } else { LOOSE_FIT }
1132 } else {
1133 DECENT_FIT
1134 };
1135 break 'found;
1136 } else if g < 0 {
1137 if -g > caw[6] {
1138 g = -caw[6];
1139 }
1140 b = retex_arith::badness(-g, caw[6]);
1141 fit_class = if b > 12 { TIGHT_FIT } else { DECENT_FIT };
1142 break 'found;
1143 }
1144 }
1145 }
1146 shortfall = 0;
1147 }
1148 b = 0;
1149 fit_class = DECENT_FIT;
1150 } else if shortfall > 7_230_584 && caw[1] < 1_663_497 {
1151 b = INF_BAD;
1152 fit_class = VERY_LOOSE_FIT;
1153 } else {
1154 b = retex_arith::badness(shortfall, caw[1]);
1155 fit_class = if b > 12 {
1156 if b > 99 { VERY_LOOSE_FIT } else { LOOSE_FIT }
1157 } else {
1158 DECENT_FIT
1159 };
1160 }
1161 } else {
1162 b = if -shortfall > caw[6] {
1163 INF_BAD + 1
1164 } else {
1165 retex_arith::badness(-shortfall, caw[6])
1166 };
1167 fit_class = if b > 12 { TIGHT_FIT } else { DECENT_FIT };
1168 }
1169 if br.do_last_line_fit {
1170 if cur_p.is_null() {
1172 shortfall = 0;
1173 }
1174 g = if shortfall > 0 {
1175 caw[1]
1176 } else if shortfall < 0 {
1177 caw[6]
1178 } else {
1179 0
1180 };
1181 }
1182 }
1183 let node_r_stays_active;
1184 let mut deactivate = false;
1185 if b > INF_BAD || pi == EJECT_PENALTY {
1186 if br.final_pass
1187 && br.minimum_demerits == AWFUL_BAD
1188 && br.items[r].next == ACTIVE
1189 && prev_r == ACTIVE
1190 {
1191 artificial_demerits = true;
1192 } else if b > br.threshold {
1193 deactivate = true;
1194 }
1195 node_r_stays_active = false;
1196 } else {
1197 prev_r = r;
1198 if b > br.threshold {
1199 continue;
1200 }
1201 node_r_stays_active = true;
1202 }
1203 if !deactivate {
1204 let mut d: i32;
1206 if artificial_demerits {
1207 d = 0;
1208 } else {
1209 d = br.line_penalty + b;
1210 d = if d.abs() >= 10000 { 100_000_000 } else { d * d };
1211 if pi != 0 {
1212 if pi > 0 {
1213 d += pi * pi;
1214 } else if pi > EJECT_PENALTY {
1215 d -= pi * pi;
1216 }
1217 }
1218 if hyphenated && r_hyph {
1219 if !cur_p.is_null() {
1220 d += br.double_hyphen_demerits;
1221 } else {
1222 d += br.final_hyphen_demerits;
1223 }
1224 }
1225 if (fit_class as i32 - r_fitness as i32).abs() > 1 {
1226 d += br.adj_demerits;
1227 }
1228 }
1229 if br.tracing {
1230 self.print_feasible_break(br, cur_p, r_passive, b, pi, d, artificial_demerits);
1231 }
1232 d += r_total;
1233 if d <= br.minimal_demerits[fit_class] {
1234 br.minimal_demerits[fit_class] = d;
1235 br.best_place[fit_class] = r_passive;
1236 br.best_pl_line[fit_class] = l;
1237 if br.do_last_line_fit {
1238 br.best_pl_short[fit_class] = shortfall;
1239 br.best_pl_glue[fit_class] = g;
1240 }
1241 if d < br.minimum_demerits {
1242 br.minimum_demerits = d;
1243 }
1244 }
1245 if node_r_stays_active {
1246 continue;
1247 }
1248 }
1249 let rn = br.items[r].next;
1251 br.items[prev_r].next = rn;
1252 br.release(r);
1253 if prev_r == ACTIVE {
1254 let r = br.items[ACTIVE].next;
1257 if let Entry::Delta(d) = br.items[r].entry {
1258 for k in 0..NW {
1259 br.active_width[k] += d[k];
1260 }
1261 br.cur_active_width = br.active_width;
1262 let rn = br.items[r].next;
1263 br.items[ACTIVE].next = rn;
1264 br.release(r);
1265 }
1266 } else if br.is_delta(prev_r) {
1267 let r = br.items[prev_r].next;
1268 if r == ACTIVE {
1269 let d = br.delta(prev_r);
1270 for k in 0..NW {
1271 br.cur_active_width[k] -= d[k];
1272 }
1273 br.items[prev_prev_r].next = ACTIVE;
1274 br.release(prev_r);
1275 prev_r = prev_prev_r;
1276 } else if let Entry::Delta(dr) = br.items[r].entry {
1277 let mut dp_ = br.delta(prev_r);
1278 for k in 0..NW {
1279 br.cur_active_width[k] += dr[k];
1280 dp_[k] += dr[k];
1281 }
1282 br.items[prev_r].entry = Entry::Delta(dp_);
1283 let rn = br.items[r].next;
1284 br.items[prev_r].next = rn;
1285 br.release(r);
1286 }
1287 }
1288 }
1289 }
1290
1291 fn compute_break_width(&mut self, br: &mut Breaker, cur_p: NodeRef, hyphenated: bool) {
1293 br.break_width = br.background;
1294 let mut s = cur_p;
1295 if hyphenated && !cur_p.is_null() && self.nodes.id(cur_p) == id::DISC {
1296 let (_pre, post, rep) = self.nodes.disc_lists(cur_p);
1297 br.break_width[0] -= self.list_width(rep);
1298 br.break_width[0] += self.list_width(post);
1299 br.break_width[0] += br.disc_width;
1300 if br.expand {
1301 let r = self.list_font_stretch(rep, br.expand_kerns);
1302 let p = self.list_font_stretch(post, br.expand_kerns);
1303 br.break_width[FONT_STRETCH] += p.stretch - r.stretch + br.disc_fs.stretch;
1304 br.break_width[FONT_SHRINK] += p.shrink - r.shrink + br.disc_fs.shrink;
1305 }
1306 s = if post.is_null() {
1307 self.nodes.next(cur_p)
1308 } else {
1309 NodeRef::NULL
1310 };
1311 }
1312 while !s.is_null() {
1313 match self.nodes.data(s) {
1314 NodeData::Glue { spec, .. } => {
1315 let g = *spec;
1316 br.break_width[0] -= g.width;
1317 br.break_width[1 + (g.stretch_order as usize).min(4)] -= g.stretch;
1318 br.break_width[6] -= g.shrink;
1319 }
1320 NodeData::Penalty { .. } => {}
1321 NodeData::Math { .. } => br.break_width[0] -= self.nodes.width(s),
1322 NodeData::Kern { kern, .. } => {
1323 if !matches!(self.nodes.subtype(s), kern_sub::USER | kern_sub::ITALIC) {
1324 break;
1325 }
1326 br.break_width[0] -= *kern;
1327 }
1328 _ => break,
1329 }
1330 s = self.nodes.next(s);
1331 }
1332 }
1333
1334 #[allow(clippy::too_many_arguments)]
1336 fn print_feasible_break(
1337 &mut self,
1338 br: &mut Breaker,
1339 cur_p: NodeRef,
1340 r_passive: Option<usize>,
1341 b: i32,
1342 pi: i32,
1343 d: i32,
1344 artificial: bool,
1345 ) {
1346 if br.printed_node != cur_p {
1347 self.printer.print_nl("");
1349 let start = self.nodes.next(br.printed_node);
1350 if cur_p.is_null() {
1351 self.short_display(start);
1352 } else {
1353 let save = self.nodes.next(cur_p);
1354 self.nodes.set_next(cur_p, NodeRef::NULL);
1355 self.printer.print_nl("");
1356 self.short_display(start);
1357 self.nodes.set_next(cur_p, save);
1358 }
1359 br.printed_node = cur_p;
1360 }
1361 self.printer.print_nl("@");
1362 if cur_p.is_null() {
1363 self.print_esc_str("par");
1364 } else {
1365 match self.nodes.id(cur_p) {
1366 id::GLUE => {}
1367 id::PENALTY => self.print_esc_str("penalty"),
1368 id::DISC => self.print_esc_str("discretionary"),
1369 id::KERN => self.print_esc_str("kern"),
1370 _ => self.print_esc_str("math"),
1371 }
1372 }
1373 self.printer.print_str(" via @");
1374 match r_passive {
1375 None => self.printer.print_char('0' as u32),
1376 Some(p) => self.printer.print_int(i64::from(br.passive[p].serial)),
1377 }
1378 self.printer.print_str(" b=");
1379 if b > INF_BAD {
1380 self.printer.print_char('*' as u32);
1381 } else {
1382 self.printer.print_int(i64::from(b));
1383 }
1384 self.printer.print_str(" p=");
1385 self.printer.print_int(i64::from(pi));
1386 self.printer.print_str(" d=");
1387 if artificial {
1388 self.printer.print_char('*' as u32);
1389 } else {
1390 self.printer.print_int(i64::from(d));
1391 }
1392 }
1393
1394 fn post_line_break(
1397 &mut self,
1398 br: &mut Breaker,
1399 best_bet: usize,
1400 best_line: i32,
1401 temp_head: NodeRef,
1402 p: &ParParams,
1403 ) {
1404 let mut breaks: Vec<NodeRef> = Vec::new();
1406 let mut q = match br.items[best_bet].entry {
1407 Entry::Active { passive, .. } => passive,
1408 _ => None,
1409 };
1410 while let Some(i) = q {
1411 breaks.push(br.passive[i].cur_break);
1412 q = br.passive[i].prev_break;
1413 }
1414 breaks.reverse();
1415 let prev_graf = self.cur_list().prev_graf;
1416 let mut cur_line = prev_graf + 1;
1417 let ls = p.left_skip;
1418 let ls_nonzero = ls.width != 0 || ls.stretch != 0 || ls.shrink != 0;
1419 let first = self.nodes.next(temp_head);
1422 let pardir = if first.is_null() {
1423 p.par_dir
1424 } else if let NodeData::LocalPar { dir, .. } = self.nodes.data(first) {
1425 *dir
1426 } else {
1427 p.par_dir
1428 };
1429 let protrude = p.protrude_chars > 0;
1430 let adjust_spacing = p.adjust_spacing > 0;
1431 let mut local_left = NodeRef::NULL;
1434 let mut local_right = NodeRef::NULL;
1435 let mut post_disc_break = false;
1436 let mut open_dirs: Vec<i8> = Vec::new();
1438 for (bi, &cb) in breaks.iter().enumerate() {
1439 let line_first = self.nodes.next(temp_head);
1441 if let Some((h, t)) = self.begin_dir_nodes(&open_dirs) {
1442 self.nodes.link(temp_head, h);
1443 self.nodes.link(t, line_first);
1444 if line_first.is_null() {
1445 self.nodes.set_next(t, NodeRef::NULL);
1446 }
1447 }
1448 self.track_open_dirs(line_first, cb, &mut open_dirs);
1449 {
1451 let mut s = self.nodes.next(temp_head);
1452 while !s.is_null() && s != cb {
1453 if let NodeData::LocalPar {
1454 box_left,
1455 box_right,
1456 ..
1457 } = self.nodes.data(s)
1458 {
1459 local_left = *box_left;
1460 local_right = *box_right;
1461 }
1462 s = self.nodes.next(s);
1463 }
1464 }
1465 let mut q = cb;
1468 let mut disc_break = false;
1469 post_disc_break = false;
1470 let mut rightskip_done = false;
1471 if !q.is_null() {
1472 match self.nodes.id(q) {
1473 id::GLUE => {
1474 let rs = p.right_skip;
1475 if let NodeData::Glue { spec, leader } = self.nodes.data_mut(q) {
1476 *spec = rs;
1477 let l = std::mem::take(leader);
1478 self.nodes.flush_list(l);
1479 }
1480 self.nodes.set_subtype(q, glue_sub::RIGHT_SKIP);
1481 rightskip_done = true;
1482 }
1483 id::DISC => {
1484 let (pre, post, rep) = self.nodes.disc_lists(q);
1488 self.nodes
1489 .set_disc_lists(q, (NodeRef::NULL, NodeRef::NULL, NodeRef::NULL));
1490 self.nodes.flush_list(rep);
1491 let r = self.nodes.next(q);
1492 if !post.is_null() {
1493 let t = self.nodes.tail_of(post);
1494 self.nodes.link(q, post);
1495 self.nodes.link(t, r);
1496 if r.is_null() {
1497 self.nodes.set_next(t, NodeRef::NULL);
1498 }
1499 post_disc_break = true;
1500 }
1501 if !pre.is_null() {
1502 let before = self.find_prev_in(temp_head, q);
1503 let t = self.nodes.tail_of(pre);
1504 self.nodes.link(before, pre);
1505 self.nodes.link(t, q);
1506 }
1507 disc_break = true;
1508 }
1509 id::KERN | id::MATH => match self.nodes.data_mut(q) {
1510 NodeData::Kern { kern, .. } => *kern = 0,
1511 NodeData::Math { surround, glue } => {
1512 *surround = 0;
1513 *glue = Glue::ZERO;
1514 }
1515 _ => {}
1516 },
1517 _ => {}
1518 }
1519 } else {
1520 q = self.nodes.tail_of(temp_head);
1521 }
1522 let q0 = q;
1523 if !rightskip_done {
1524 let r = self.new_glue_node(p.right_skip, glue_sub::RIGHT_SKIP);
1525 if !q.is_null() && q != temp_head {
1527 let a = self.nodes.attr(q).cloned();
1528 self.nodes.set_attr(r, a);
1529 }
1530 let n = self.nodes.next(q);
1531 self.nodes.link(q, r);
1532 self.nodes.link(r, n);
1533 if n.is_null() {
1534 self.nodes.set_next(r, NodeRef::NULL);
1535 }
1536 q = r;
1537 }
1538 if let Some((h, t)) = self.end_dir_nodes(&open_dirs) {
1539 let mut at = q;
1543 if bi + 1 == breaks.len() {
1544 let p = self.find_prev_in(temp_head, q);
1545 if self.nodes.id(p) == id::GLUE
1546 && self.nodes.subtype(p) == glue_sub::PAR_FILL_SKIP
1547 {
1548 at = p;
1549 }
1550 }
1551 let p = self.find_prev_in(temp_head, at);
1552 self.nodes.link(p, h);
1553 self.nodes.link(t, at);
1554 }
1555 if !local_right.is_null() {
1556 let mut target = q;
1559 if bi + 1 == breaks.len() {
1560 let p = self.find_prev_in(temp_head, q);
1561 if self.nodes.id(p) == id::GLUE
1562 && self.nodes.subtype(p) == glue_sub::PAR_FILL_SKIP
1563 {
1564 target = p;
1565 }
1566 }
1567 let b = self.nodes.copy_node(local_right);
1568 let p = self.find_prev_in(temp_head, target);
1569 self.nodes.link(p, b);
1570 self.nodes.link(b, target);
1571 }
1572 if protrude {
1573 self.insert_right_margin_kern(temp_head, q0);
1574 }
1575 let r = self.nodes.next(q);
1577 self.nodes.set_next(q, NodeRef::NULL);
1578 let mut line = self.nodes.next(temp_head);
1579 self.nodes.set_next(temp_head, r);
1580 if !r.is_null() {
1581 self.nodes.set_prev(r, temp_head);
1582 }
1583 if !local_left.is_null() {
1584 let b = self.nodes.copy_node(local_left);
1585 self.nodes.link(b, line);
1586 line = b;
1587 }
1588 if protrude {
1589 let k = self.left_margin_kern(line);
1590 if !k.is_null() {
1591 self.nodes.link(k, line);
1592 line = k;
1593 }
1594 }
1595 if ls_nonzero {
1596 let g = self.new_glue_node(ls, glue_sub::LEFT_SKIP);
1597 if !line.is_null() {
1599 let a = self.nodes.attr(line).cloned();
1600 self.nodes.set_attr(g, a);
1601 }
1602 self.nodes.link(g, line);
1603 line = g;
1604 }
1605 if !line.is_null() {
1606 self.nodes.set_prev(line, NodeRef::NULL);
1607 }
1608 let (cur_width, cur_indent) = if cur_line > br.last_special_line {
1610 (br.second_width, br.second_indent)
1611 } else {
1612 match &br.par_shape {
1613 None => (br.first_width, br.first_indent),
1614 Some(s) => (
1615 s[2 * (cur_line as usize - 1) + 1],
1616 s[2 * (cur_line as usize - 1)],
1617 ),
1618 }
1619 };
1620 self.start_adjust_lists();
1621 let natural = adjust_spacing && self.expand_line(line, cur_width);
1624 self.pack_dir = pardir;
1627 let just_box = if natural {
1628 let b = self.hpack(line, PackSpec::Additional(0));
1629 if let Some(bd) = self.nodes.box_data_mut(b) {
1630 bd.width = cur_width;
1631 }
1632 b
1633 } else {
1634 self.hpack(line, PackSpec::Exactly(cur_width))
1635 };
1636 self.ts.just_box = just_box;
1637 self.nodes.set_subtype(just_box, 1);
1638 if let Some(b) = self.nodes.box_data_mut(just_box) {
1639 b.shift = cur_indent;
1640 b.dir = pardir;
1641 }
1642 let pre_h = self.ts.pre_adjust_head;
1646 if !pre_h.is_null() && !self.nodes.next(pre_h).is_null() {
1647 self.notify_callback("contribute_filter", "pre_adjust");
1648 }
1649 self.append_adjust_material(pre_h);
1650 self.notify_callback("contribute_filter", "box");
1651 self.append_to_vlist(just_box);
1652 let adj_h = self.ts.adjust_head;
1653 if !adj_h.is_null() && !self.nodes.next(adj_h).is_null() {
1654 self.notify_callback("contribute_filter", "adjust");
1655 }
1656 self.append_adjust_material(adj_h);
1657 self.ts.adjust_tail = None;
1658 self.ts.pre_adjust_tail = None;
1659 if cur_line + 1 != best_line {
1661 let mut pen = match &p.interline_penalties {
1662 Some(a) if !a.is_empty() => {
1663 let n = a.len() as i32;
1664 a[(cur_line.min(n) - 1).max(0) as usize]
1665 }
1666 _ => p.interline_penalty,
1667 };
1668 match &p.club_penalties {
1669 Some(a) if !a.is_empty() => {
1670 let n = a.len() as i32;
1671 let r = (cur_line - prev_graf).min(n);
1672 pen += a[(r - 1).max(0) as usize];
1673 }
1674 _ => {
1675 if cur_line == prev_graf + 1 {
1676 pen += p.club_penalty;
1677 }
1678 }
1679 }
1680 match &p.widow_penalties {
1681 Some(a) if !a.is_empty() => {
1682 let n = a.len() as i32;
1683 let r = (best_line - cur_line - 1).min(n);
1684 pen += a[(r - 1).max(0) as usize];
1685 }
1686 _ => {
1687 if cur_line + 2 == best_line {
1688 pen += p.widow_penalty;
1689 }
1690 }
1691 }
1692 if disc_break {
1693 pen += p.broken_penalty;
1694 }
1695 if pen != 0 {
1696 let r = self.new_penalty_node(pen);
1697 self.nodes.set_subtype(r, 1);
1699 self.tail_append(r);
1700 }
1701 }
1702 cur_line += 1;
1703 if bi + 1 < breaks.len() && !post_disc_break {
1705 let next_break = breaks[bi + 1];
1706 let mut r = temp_head;
1707 loop {
1708 let q = self.nodes.next(r);
1709 if q.is_null() || q == next_break {
1710 break;
1711 }
1712 let t = self.nodes.id(q);
1713 if t == id::GLYPH || crate::page::precedes_break(t) {
1714 break;
1715 }
1716 if t == id::KERN
1717 && !matches!(self.nodes.subtype(q), kern_sub::USER | kern_sub::ITALIC)
1718 {
1719 break;
1720 }
1721 if !matches!(t, id::GLUE | id::KERN | id::MATH | id::PENALTY) {
1722 break;
1723 }
1724 r = q;
1725 }
1726 if r != temp_head {
1727 let q = self.nodes.next(r);
1728 self.nodes.set_next(r, NodeRef::NULL);
1729 let first = self.nodes.next(temp_head);
1730 self.nodes.flush_list(first);
1731 self.nodes.link(temp_head, q);
1732 if q.is_null() {
1733 self.nodes.set_next(temp_head, NodeRef::NULL);
1734 }
1735 }
1736 }
1737 }
1738 let _ = post_disc_break;
1739 if !self.nodes.next(temp_head).is_null() {
1740 let rest = self.nodes.next(temp_head);
1742 self.nodes.set_next(temp_head, NodeRef::NULL);
1743 self.nodes.flush_list(rest);
1744 }
1745 self.cur_list_mut().prev_graf = best_line - 1;
1746 }
1747
1748 fn append_adjust_material(&mut self, head: NodeRef) {
1750 if head.is_null() {
1751 return;
1752 }
1753 let first = self.nodes.next(head);
1754 if first.is_null() {
1755 return;
1756 }
1757 let tail = self.cur_list().tail;
1758 self.nodes.link(tail, first);
1759 let t = self.nodes.tail_of(first);
1760 self.cur_list_mut().tail = t;
1761 self.nodes.set_next(head, NodeRef::NULL);
1762 }
1763}