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.work_begin(crate::profile::Work::LineBreak);
305 self.line_break_inner(final_widow_penalty);
306 self.work_end();
307 }
308
309 fn line_break_inner(&mut self, final_widow_penalty: i32) {
310 self.fonts.sync_dense();
311 let display = self.cur_cmd == crate::cmd::Cmd::MathShift;
314 let group = if display { "math_shift" } else { "" };
315 self.ts.pack_begin_line = self.cur_list().mode_line;
316 let temp_head = self.nodes.new_temp();
319 let head = self.cur_list().head;
320 let first = self.nodes.next(head);
321 self.nodes.set_next(head, NodeRef::NULL);
322 let first = self.node_process_list(first, crate::nodeproc::Context::Paragraph);
326 self.nodes.link(temp_head, first);
327 if first.is_null() {
328 self.nodes.set_next(temp_head, NodeRef::NULL);
329 }
330 let mut tail = if first.is_null() {
331 temp_head
332 } else {
333 self.nodes.tail_of(first)
334 };
335 if self.nodes.id(tail) == id::GLUE {
336 let p = self.find_prev_in(temp_head, tail);
338 self.nodes.set_next(p, NodeRef::NULL);
339 self.nodes.flush_list(tail);
340 tail = p;
341 }
342 let pen = self.new_penalty_node(INF_PENALTY);
343 self.nodes.set_subtype(pen, 2);
345 self.nodes.link(tail, pen);
346 let pfs = self.new_param_glue(glue_sub::PAR_FILL_SKIP);
347 self.nodes.link(pen, pfs);
348 let last_line_fill = pfs;
349 self.pop_nest();
350 let first = self.nodes.next(temp_head);
352 let had_list = !first.is_null();
353 let first = self.filter_list("pre_linebreak_filter", first, group);
354 if had_list && first.is_null() {
355 self.confusion("weird par dir");
358 }
359 self.nodes.link(temp_head, first);
360 if first.is_null() {
361 self.nodes.set_next(temp_head, NodeRef::NULL);
362 }
363 let old_tail = self.cur_list().tail;
364 let replaced = match self.lua.clone() {
365 Some(h) if h.has_callback("linebreak_filter") => {
366 let first = self.nodes.next(temp_head);
367 h.linebreak_filter(self, first, display)
368 }
369 _ => None,
370 };
371 if let Some(list) = replaced {
372 let mut p = list;
375 while !p.is_null() && self.nodes.id(p) != id::HLIST {
376 p = self.nodes.next(p);
377 }
378 if p.is_null() {
379 self.print_err("Invalid linebreak_filter");
380 self.help(&[
381 "A linebreaking routine should return a non-empty list of nodes",
382 "and at least one of those has to be a \\hbox.",
383 "Sorry, I cannot recover from this.",
384 ]);
385 self.succumb();
386 }
387 self.nodes.link(old_tail, list);
388 let t = self.nodes.tail_of(list);
389 self.cur_list_mut().tail = t;
390 } else {
391 let params = self.par_params(final_widow_penalty, display);
392 self.break_paragraph(temp_head, Some(last_line_fill), ¶ms);
393 }
394 if self.has_callback("post_linebreak_filter") {
395 let first = self.nodes.next(old_tail);
396 self.nodes.set_next(old_tail, NodeRef::NULL);
397 let first = self.filter_list("post_linebreak_filter", first, group);
398 self.cur_list_mut().tail = old_tail;
399 if !first.is_null() {
400 self.nodes.link(old_tail, first);
401 let t = self.nodes.tail_of(first);
402 self.cur_list_mut().tail = t;
403 }
404 }
405 self.nodes.free(temp_head);
406 self.ts.pack_begin_line = 0;
407 }
408
409 pub fn par_params(&self, widow_penalty: i32, display: bool) -> ParParams {
413 let array = |k: u32| self.eqtb.etex_shape[k as usize].val.clone();
414 ParParams {
415 pretolerance: self.eqtb.int(ip::PRETOLERANCE),
416 tolerance: self.eqtb.int(ip::TOLERANCE),
417 looseness: self.eqtb.int(ip::LOOSENESS),
418 adj_demerits: self.eqtb.int(ip::ADJDEMERITS),
419 line_penalty: self.eqtb.int(ip::LINEPENALTY),
420 last_line_fit: self.eqtb.int(ip::LASTLINEFIT),
421 double_hyphen_demerits: self.eqtb.int(ip::DOUBLEHYPHENDEMERITS),
422 final_hyphen_demerits: self.eqtb.int(ip::FINALHYPHENDEMERITS),
423 hang_after: self.eqtb.int(ip::HANGAFTER),
424 hang_indent: self.eqtb.dimen(dp::HANGINDENT),
425 hsize: self.eqtb.dimen(dp::HSIZE),
426 emergency_stretch: self.eqtb.dimen(dp::EMERGENCYSTRETCH),
427 broken_penalty: self.eqtb.int(ip::BROKENPENALTY),
428 protrude_chars: self.eqtb.int(ip::PROTRUDECHARS),
429 adjust_spacing: self.eqtb.int(ip::ADJUSTSPACING),
430 tracing_paragraphs: self.eqtb.int(ip::TRACINGPARAGRAPHS),
431 interline_penalty: self.eqtb.int(ip::INTERLINEPENALTY),
432 club_penalty: self.eqtb.int(ip::CLUBPENALTY),
433 widow_penalty,
434 interline_penalties: array(es::INTERLINEPENALTIES),
435 club_penalties: array(es::CLUBPENALTIES),
436 widow_penalties: array(if display {
437 es::DISPLAYWIDOWPENALTIES
438 } else {
439 es::WIDOWPENALTIES
440 }),
441 left_skip: self.eqtb.glue(gp::LEFTSKIP),
442 right_skip: self.eqtb.glue(gp::RIGHTSKIP),
443 par_shape: self.eqtb.par_shape.val.clone(),
444 par_dir: self.dir_param(crate::primitives_gen::assign_dir::PARDIR) as i8,
445 }
446 }
447
448 pub fn lua_line_break(&mut self, head: NodeRef, p: &ParParams) -> (NodeRef, LineBreakInfo) {
456 if self.nodes.id(self.nodes.tail_of(head)) == id::GLYPH {
457 self.luatex_fatal("linebreak", "invalid list tail, probably missing glue");
459 }
460 self.fonts.sync_dense();
461 let prev_depth = self.cur_list().prev_depth;
462 self.push_nest(crate::maincontrol::Mode::InternalVertical);
463 self.cur_list_mut().prev_depth = prev_depth;
464 let temp_head = self.nodes.new_temp();
465 self.nodes.link(temp_head, head);
466 let (looseness, demerits) = self.break_paragraph(temp_head, None, p);
467 self.nodes.free(temp_head);
468 let list_head = self.cur_list().head;
469 let lines = self.nodes.next(list_head);
470 self.nodes.set_next(list_head, NodeRef::NULL);
471 if !lines.is_null() {
472 self.nodes.set_prev(lines, NodeRef::NULL);
473 }
474 let info = LineBreakInfo {
475 prev_depth: self.cur_list().prev_depth,
476 prev_graf: self.cur_list().prev_graf,
477 looseness,
478 demerits,
479 };
480 self.pop_nest();
481 (lines, info)
482 }
483
484 fn break_paragraph(
489 &mut self,
490 temp_head: NodeRef,
491 last_line_fill: Option<NodeRef>,
492 p: &ParParams,
493 ) -> (i32, i32) {
494 let mut br = Breaker {
495 items: vec![Item {
496 next: ACTIVE,
497 entry: Entry::Sentinel,
498 }],
499 free: Vec::new(),
500 passive: Vec::new(),
501 active_width: [0; NW],
502 cur_active_width: [0; NW],
503 background: [0; NW],
504 break_width: [0; NW],
505 minimal_demerits: [AWFUL_BAD; 4],
506 minimum_demerits: AWFUL_BAD,
507 best_place: [None; 4],
508 best_pl_line: [0; 4],
509 best_pl_short: [0; 4],
510 best_pl_glue: [0; 4],
511 threshold: 0,
512 second_pass: false,
513 final_pass: false,
514 disc_width: 0,
515 disc_fs: Default::default(),
516 easy_line: 0,
517 last_special_line: 0,
518 first_width: 0,
519 second_width: 0,
520 first_indent: 0,
521 second_indent: 0,
522 par_shape: p.par_shape.clone(),
523 printed_node: temp_head,
524 pass_number: 0,
525 do_last_line_fit: false,
526 fill_width: [0; 4],
527 last_line_fit: p.last_line_fit,
528 adj_demerits: p.adj_demerits,
529 line_penalty: p.line_penalty,
530 double_hyphen_demerits: p.double_hyphen_demerits,
531 final_hyphen_demerits: p.final_hyphen_demerits,
532 tracing: p.tracing_paragraphs > 0,
533 no_shrink_error_yet: true,
534 protrude: p.protrude_chars > 1,
535 first_left_pw: 0,
536 head: temp_head,
537 expand: p.adjust_spacing > 1,
538 expand_kerns: p.adjust_spacing == 2,
539 };
540 if br.expand && !br.protrude {
541 self.fix_back_links(temp_head);
542 }
543 if br.protrude {
544 self.fix_back_links(temp_head);
545 let first = self.nodes.next(temp_head);
546 br.first_left_pw = self.lb_left_protrusion(None, first);
547 }
548 let mut ls = p.left_skip;
549 let mut rs = p.right_skip;
550 if ls.shrink_order != 0 && ls.shrink != 0 {
551 ls = self.finite_shrink(&mut br, ls);
552 }
553 if rs.shrink_order != 0 && rs.shrink != 0 {
554 rs = self.finite_shrink(&mut br, rs);
555 }
556 br.background[0] = ls.width + rs.width;
557 br.background[1 + (ls.stretch_order as usize).min(4)] += ls.stretch;
558 br.background[1 + (rs.stretch_order as usize).min(4)] += rs.stretch;
559 br.background[6] = ls.shrink + rs.shrink;
560 if let Some(last_line_fill) = last_line_fill
562 && br.last_line_fit > 0
563 {
564 let q = self.nodes.glue_spec(last_line_fill);
565 if q.stretch > 0 && q.stretch_order > 0 && br.background[2..6].iter().all(|&v| v == 0) {
566 br.do_last_line_fit = true;
567 br.fill_width = [0; 4];
568 br.fill_width[(q.stretch_order as usize - 1).min(3)] = q.stretch;
569 }
570 }
571 let hsize = p.hsize;
573 let hang_indent = p.hang_indent;
574 let hang_after = p.hang_after;
575 match &br.par_shape {
576 None => {
577 if hang_indent == 0 {
578 br.last_special_line = 0;
579 br.second_width = hsize;
580 br.second_indent = 0;
581 } else {
582 br.last_special_line = hang_after.abs();
583 if hang_after < 0 {
584 br.first_width = hsize - hang_indent.abs();
585 br.first_indent = hang_indent.max(0);
586 br.second_width = hsize;
587 br.second_indent = 0;
588 } else {
589 br.first_width = hsize;
590 br.first_indent = 0;
591 br.second_width = hsize - hang_indent.abs();
592 br.second_indent = hang_indent.max(0);
593 }
594 }
595 }
596 Some(s) => {
597 let n = (s.len() / 2) as i32;
598 br.last_special_line = n - 1;
599 br.second_indent = s[2 * (n as usize - 1)];
600 br.second_width = s[2 * (n as usize - 1) + 1];
601 }
602 }
603 br.easy_line = if p.looseness == 0 {
604 br.last_special_line
605 } else {
606 i32::MAX - 1
607 };
608 br.threshold = p.pretolerance;
610 let emergency = p.emergency_stretch;
611 if br.threshold >= 0 {
612 if br.tracing {
613 self.begin_diagnostic();
614 self.printer.print_nl("@firstpass");
615 }
616 br.second_pass = false;
617 br.final_pass = false;
618 } else {
619 br.threshold = p.tolerance;
620 br.second_pass = true;
621 br.final_pass = emergency <= 0;
622 if br.tracing {
623 self.begin_diagnostic();
624 }
625 }
626 let prev_graf = self.cur_list().prev_graf;
627 let looseness = p.looseness;
628 let best_bet: usize;
629 let best_line: i32;
630 let mut actual_looseness = 0;
631 loop {
632 if br.threshold > INF_BAD {
633 br.threshold = INF_BAD;
634 }
635 let q = br.alloc(
637 Entry::Active {
638 passive: None,
639 line_number: prev_graf + 1,
640 fitness: DECENT_FIT,
641 hyphenated: false,
642 total_demerits: 0,
643 short: 0,
644 glue: 0,
645 },
646 ACTIVE,
647 );
648 br.items[ACTIVE].next = q;
649 br.active_width = br.background;
650 br.passive.clear();
651 br.printed_node = temp_head;
652 br.pass_number = 0;
653 self.ts.font_in_short_display = 0;
654 let mut cur_p = self.nodes.next(temp_head);
655 let mut auto_breaking = true;
656 let mut prev_p = cur_p;
657 while !cur_p.is_null() && br.items[ACTIVE].next != ACTIVE {
658 let t = self.nodes.id(cur_p);
659 match t {
660 id::GLYPH => {
661 prev_p = cur_p;
663 while !cur_p.is_null() && self.nodes.id(cur_p) == id::GLYPH {
664 self.check_lost_char(cur_p);
665 br.active_width[0] += self.glyph_dimensions(cur_p).0;
666 if br.expand {
667 let f = self.glyph_font_stretch(cur_p);
668 br.active_width[FONT_STRETCH] += f.stretch;
669 br.active_width[FONT_SHRINK] += f.shrink;
670 }
671 cur_p = self.nodes.next(cur_p);
672 }
673 continue;
674 }
675 id::HLIST | id::VLIST | id::RULE => {
676 br.active_width[0] += self.nodes.width(cur_p);
677 }
678 id::GLUE => {
679 if auto_breaking {
680 let pt = self.nodes.id(prev_p);
681 let legal = pt == id::GLYPH
685 || (crate::page::precedes_break(pt) && pt != id::LOCAL_PAR)
686 || (pt == id::KERN
687 && !matches!(
688 self.nodes.subtype(prev_p),
689 kern_sub::USER | kern_sub::ITALIC
690 ));
691 if legal {
692 self.try_break(&mut br, cur_p, 0, false);
693 }
694 }
695 let mut g = self.nodes.glue_spec(cur_p);
696 if g.shrink_order != 0 && g.shrink != 0 {
697 g = self.finite_shrink(&mut br, g);
698 if let NodeData::Glue { spec, .. } = self.nodes.data_mut(cur_p) {
699 *spec = g;
700 }
701 }
702 add_glue(&mut br.active_width, &g);
703 }
704 id::KERN => {
705 if matches!(self.nodes.subtype(cur_p), kern_sub::USER | kern_sub::ITALIC) {
706 self.kern_break(&mut br, cur_p, auto_breaking);
707 } else {
708 br.active_width[0] += self.nodes.width(cur_p);
709 if br.expand_kerns {
710 let (pv, nx) = (self.nodes.prev(cur_p), self.nodes.next(cur_p));
711 let f = self.kern_font_stretch(pv, cur_p, nx);
712 br.active_width[FONT_STRETCH] += f.stretch;
713 br.active_width[FONT_SHRINK] += f.shrink;
714 }
715 }
716 }
717 id::DISC => {
718 let (pre, _post, rep) = self.nodes.disc_lists(cur_p);
719 let pen = self.nodes.penalty(cur_p);
720 let from_hyphenation = self.nodes.subtype(cur_p) >= 3;
725 if br.second_pass || !from_hyphenation {
726 br.disc_width = self.list_width(pre);
727 br.active_width[0] += br.disc_width;
728 if br.expand {
729 br.disc_fs = self.list_font_stretch(pre, br.expand_kerns);
730 br.active_width[FONT_STRETCH] += br.disc_fs.stretch;
731 br.active_width[FONT_SHRINK] += br.disc_fs.shrink;
732 }
733 self.try_break(&mut br, cur_p, pen, true);
734 br.active_width[0] -= br.disc_width;
735 if br.expand {
736 br.active_width[FONT_STRETCH] -= br.disc_fs.stretch;
737 br.active_width[FONT_SHRINK] -= br.disc_fs.shrink;
738 }
739 }
740 br.active_width[0] += self.list_width(rep);
741 if br.expand {
742 let f = self.list_font_stretch(rep, br.expand_kerns);
743 br.active_width[FONT_STRETCH] += f.stretch;
744 br.active_width[FONT_SHRINK] += f.shrink;
745 }
746 }
747 id::MATH => {
748 auto_breaking = self.nodes.subtype(cur_p) == 1;
749 self.kern_break(&mut br, cur_p, auto_breaking);
750 }
751 id::PENALTY => {
752 let pen = self.nodes.penalty(cur_p);
753 self.try_break(&mut br, cur_p, pen, false);
754 }
755 id::LOCAL_PAR
756 | id::DIR
757 | id::BOUNDARY
758 | id::MARK
759 | id::INS
760 | id::ADJUST
761 | id::WHATSIT => {}
762 id::GLUE_SPEC => {
763 self.luatex_warning("parbuilder", "found a glue_spec in a paragraph")
764 }
765 _ => self.luatex_fatal("parbuilder", &format!("weird node {t} in paragraph")),
769 }
770 prev_p = cur_p;
771 cur_p = self.nodes.next(cur_p);
772 }
773 if cur_p.is_null() {
774 self.try_break(&mut br, NodeRef::NULL, EJECT_PENALTY, true);
776 if br.items[ACTIVE].next != ACTIVE {
777 let mut r = br.items[ACTIVE].next;
779 let mut fewest = AWFUL_BAD;
780 let mut bb = r;
781 while r != ACTIVE {
782 if let Entry::Active { total_demerits, .. } = br.items[r].entry
783 && total_demerits < fewest
784 {
785 fewest = total_demerits;
786 bb = r;
787 }
788 r = br.items[r].next;
789 }
790 let mut bl = br.line_number(bb);
791 if looseness == 0 {
792 best_bet = bb;
793 best_line = bl;
794 break;
795 }
796 let mut r = br.items[ACTIVE].next;
798 let mut actual = 0;
799 let mut bb2 = bb;
800 while r != ACTIVE {
801 if let Entry::Active {
802 line_number,
803 total_demerits,
804 ..
805 } = br.items[r].entry
806 {
807 let line_diff = line_number - bl;
808 if (line_diff < actual && looseness <= line_diff)
809 || (line_diff > actual && looseness >= line_diff)
810 {
811 bb2 = r;
812 actual = line_diff;
813 fewest = total_demerits;
814 } else if line_diff == actual && total_demerits < fewest {
815 bb2 = r;
816 fewest = total_demerits;
817 }
818 }
819 r = br.items[r].next;
820 }
821 bl = br.line_number(bb2);
822 if actual == looseness || br.final_pass {
823 actual_looseness = actual;
824 best_bet = bb2;
825 best_line = bl;
826 break;
827 }
828 }
829 }
830 br.clean_up();
832 if !br.second_pass {
833 if br.tracing {
834 self.printer.print_nl("@secondpass");
835 }
836 br.threshold = p.tolerance;
837 br.second_pass = true;
838 br.final_pass = emergency <= 0;
839 } else {
840 if br.tracing {
841 self.printer.print_nl("@emergencypass");
842 }
843 br.background[1] += emergency;
844 br.final_pass = true;
845 }
846 }
847 if br.tracing {
848 self.end_diagnostic(true);
849 self.normalize_selector();
850 }
851 let demerits = match br.items[best_bet].entry {
852 Entry::Active { total_demerits, .. } => total_demerits,
853 _ => 0,
854 };
855 if let Some(last_line_fill) = last_line_fill
856 && br.do_last_line_fit
857 {
858 if let Entry::Active { short, glue, .. } = br.items[best_bet].entry {
860 if short == 0 {
861 br.do_last_line_fit = false;
862 } else if let NodeData::Glue { spec, .. } = self.nodes.data_mut(last_line_fill) {
863 spec.width += short - glue;
864 spec.stretch = 0;
865 }
866 }
867 }
868 self.post_line_break(&mut br, best_bet, best_line, temp_head, p);
870 br.clean_up();
871 (actual_looseness, demerits)
872 }
873
874 fn find_prev_in(&self, head: NodeRef, q: NodeRef) -> NodeRef {
875 let mut p = head;
876 loop {
877 let n = self.nodes.next(p);
878 if n == q || n.is_null() {
879 return p;
880 }
881 p = n;
882 }
883 }
884
885 fn kern_break(&mut self, br: &mut Breaker, cur_p: NodeRef, auto_breaking: bool) {
888 let n = self.nodes.next(cur_p);
889 if !n.is_null() && auto_breaking && self.nodes.id(n) == id::GLUE {
890 self.try_break(br, cur_p, 0, false);
891 }
892 br.active_width[0] += self.nodes.width(cur_p);
893 }
894
895 fn try_break(&mut self, br: &mut Breaker, cur_p: NodeRef, pi: i32, hyphenated: bool) {
897 let mut pi = pi;
898 if pi.abs() >= INF_PENALTY {
899 if pi > 0 {
900 return;
901 }
902 pi = EJECT_PENALTY;
903 }
904 let mut no_break_yet = true;
905 let mut prev_r = ACTIVE;
906 let mut prev_prev_r = ACTIVE;
907 let mut old_l = 0;
908 let mut line_width = 0;
909 let mut right_pw: Option<i32> = None;
910 br.cur_active_width = br.active_width;
911 loop {
912 let r = br.items[prev_r].next;
913 if let Entry::Delta(d) = br.items[r].entry {
915 for k in 0..NW {
916 br.cur_active_width[k] += d[k];
917 }
918 prev_prev_r = prev_r;
919 prev_r = r;
920 continue;
921 }
922 let l = br.line_number(r);
925 if l > old_l {
926 if br.minimum_demerits < AWFUL_BAD && (old_l != br.easy_line || r == ACTIVE) {
927 if no_break_yet {
928 no_break_yet = false;
929 self.compute_break_width(br, cur_p, hyphenated);
930 }
931 if br.is_delta(prev_r) {
933 let mut d = br.delta(prev_r);
934 for k in 0..NW {
935 d[k] = d[k] - br.cur_active_width[k] + br.break_width[k];
936 }
937 br.items[prev_r].entry = Entry::Delta(d);
938 } else if prev_r == ACTIVE {
939 br.active_width = br.break_width;
940 } else {
941 let mut d = [0; NW];
942 for k in 0..NW {
943 d[k] = br.break_width[k] - br.cur_active_width[k];
944 }
945 let q = br.alloc(Entry::Delta(d), r);
946 br.items[prev_r].next = q;
947 prev_prev_r = prev_r;
948 prev_r = q;
949 }
950 if br.adj_demerits.abs() >= AWFUL_BAD - br.minimum_demerits {
951 br.minimum_demerits = AWFUL_BAD - 1;
952 } else {
953 br.minimum_demerits += br.adj_demerits.abs();
954 }
955 for fit_class in VERY_LOOSE_FIT..=TIGHT_FIT {
956 if br.minimal_demerits[fit_class] <= br.minimum_demerits {
957 br.pass_number += 1;
959 let left_pw = if br.protrude {
960 self.lb_left_protrusion(Some(cur_p), NodeRef::NULL)
961 } else {
962 0
963 };
964 br.passive.push(Passive {
965 cur_break: cur_p,
966 prev_break: br.best_place[fit_class],
967 serial: br.pass_number,
968 left_pw,
969 });
970 let pidx = br.passive.len() - 1;
971 let (short, glue) = if br.do_last_line_fit {
972 (br.best_pl_short[fit_class], br.best_pl_glue[fit_class])
973 } else {
974 (0, 0)
975 };
976 let q = br.alloc(
977 Entry::Active {
978 passive: Some(pidx),
979 line_number: br.best_pl_line[fit_class] + 1,
980 fitness: fit_class,
981 hyphenated,
982 total_demerits: br.minimal_demerits[fit_class],
983 short,
984 glue,
985 },
986 r,
987 );
988 br.items[prev_r].next = q;
989 prev_r = q;
990 if br.tracing {
991 self.printer.print_nl("@@");
992 self.printer.print_int(i64::from(br.pass_number));
993 self.printer.print_str(": line ");
994 self.printer
995 .print_int(i64::from(br.best_pl_line[fit_class]));
996 self.printer.print_char('.' as u32);
997 self.printer.print_int(fit_class as i64);
998 if hyphenated {
999 self.printer.print_char('-' as u32);
1000 }
1001 self.printer.print_str(" t=");
1002 self.printer
1003 .print_int(i64::from(br.minimal_demerits[fit_class]));
1004 if br.do_last_line_fit {
1005 self.printer.print_str(" s=");
1006 self.printer.print_scaled(short);
1007 self.printer.print_str(if cur_p.is_null() {
1008 " a="
1009 } else {
1010 " g="
1011 });
1012 self.printer.print_scaled(glue);
1013 }
1014 self.printer.print_str(" -> @");
1015 match br.best_place[fit_class] {
1016 None => self.printer.print_char('0' as u32),
1017 Some(pp) => {
1018 self.printer.print_int(i64::from(br.passive[pp].serial))
1019 }
1020 }
1021 }
1022 }
1023 br.minimal_demerits[fit_class] = AWFUL_BAD;
1024 }
1025 br.minimum_demerits = AWFUL_BAD;
1026 if r != ACTIVE {
1028 let mut d = [0; NW];
1029 for k in 0..NW {
1030 d[k] = br.cur_active_width[k] - br.break_width[k];
1031 }
1032 let q = br.alloc(Entry::Delta(d), r);
1033 br.items[prev_r].next = q;
1034 prev_prev_r = prev_r;
1035 prev_r = q;
1036 }
1037 }
1038 if r == ACTIVE {
1039 return;
1040 }
1041 if l > br.easy_line {
1043 line_width = br.second_width;
1044 old_l = i32::MAX - 1;
1045 } else {
1046 old_l = l;
1047 line_width = if l > br.last_special_line {
1048 br.second_width
1049 } else {
1050 match &br.par_shape {
1051 None => br.first_width,
1052 Some(s) => s[2 * (l as usize - 1) + 1],
1053 }
1054 };
1055 }
1056 }
1057 let (r_passive, r_fitness, r_hyph, r_total, r_short, r_glue) = match br.items[r].entry {
1059 Entry::Active {
1060 passive,
1061 fitness,
1062 hyphenated,
1063 total_demerits,
1064 short,
1065 glue,
1066 ..
1067 } => (passive, fitness, hyphenated, total_demerits, short, glue),
1068 _ => (None, DECENT_FIT, false, 0, 0, 0),
1069 };
1070 let mut artificial_demerits = false;
1071 let mut shortfall = line_width - br.cur_active_width[0];
1072 if br.protrude {
1073 let rp = *right_pw.get_or_insert_with(|| self.lb_right_protrusion(cur_p, br.head));
1074 let lp = match r_passive {
1075 Some(i) => br.passive[i].left_pw,
1076 None => br.first_left_pw,
1077 };
1078 shortfall += lp + rp;
1079 }
1080 if br.expand {
1081 let caw = &br.cur_active_width;
1083 if shortfall > 0 && caw[FONT_STRETCH] > 0 {
1084 shortfall = (shortfall - caw[FONT_STRETCH]).max(0);
1085 } else if shortfall < 0 && caw[FONT_SHRINK] > 0 {
1086 shortfall = (shortfall + caw[FONT_SHRINK]).min(0);
1087 }
1088 }
1089 let mut g = 0i32;
1090 let b: i32;
1091 let fit_class: usize;
1092 let caw = br.cur_active_width;
1093 'found: {
1094 if shortfall > 0 {
1095 if caw[2] != 0 || caw[3] != 0 || caw[4] != 0 || caw[5] != 0 {
1096 if br.do_last_line_fit {
1097 if cur_p.is_null() {
1098 'not_found: {
1100 if r_short == 0 || r_glue <= 0 {
1101 break 'not_found;
1102 }
1103 if caw[2] != br.fill_width[0]
1104 || caw[3] != br.fill_width[1]
1105 || caw[4] != br.fill_width[2]
1106 || caw[5] != br.fill_width[3]
1107 {
1108 break 'not_found;
1109 }
1110 g = if r_short > 0 { caw[1] } else { caw[6] };
1111 if g <= 0 {
1112 break 'not_found;
1113 }
1114 let md = retex_arith::MAX_DIMEN;
1115 g = match retex_arith::fract(g, r_short, r_glue, md) {
1116 Ok(v) => v,
1117 Err(_) => {
1118 if r_short > 0 {
1119 md
1120 } else {
1121 -md
1122 }
1123 }
1124 };
1125 if br.last_line_fit < 1000 {
1126 g = retex_arith::fract(g, br.last_line_fit, 1000, md)
1127 .unwrap_or(if r_short > 0 { md } else { -md });
1128 }
1129 if g > 0 {
1130 if g > shortfall {
1131 g = shortfall;
1132 }
1133 if g > 7_230_584 && caw[1] < 1_663_497 {
1134 b = INF_BAD;
1135 fit_class = VERY_LOOSE_FIT;
1136 break 'found;
1137 }
1138 b = retex_arith::badness(g, caw[1]);
1139 fit_class = if b > 12 {
1140 if b > 99 { VERY_LOOSE_FIT } else { LOOSE_FIT }
1141 } else {
1142 DECENT_FIT
1143 };
1144 break 'found;
1145 } else if g < 0 {
1146 if -g > caw[6] {
1147 g = -caw[6];
1148 }
1149 b = retex_arith::badness(-g, caw[6]);
1150 fit_class = if b > 12 { TIGHT_FIT } else { DECENT_FIT };
1151 break 'found;
1152 }
1153 }
1154 }
1155 shortfall = 0;
1156 }
1157 b = 0;
1158 fit_class = DECENT_FIT;
1159 } else if shortfall > 7_230_584 && caw[1] < 1_663_497 {
1160 b = INF_BAD;
1161 fit_class = VERY_LOOSE_FIT;
1162 } else {
1163 b = retex_arith::badness(shortfall, caw[1]);
1164 fit_class = if b > 12 {
1165 if b > 99 { VERY_LOOSE_FIT } else { LOOSE_FIT }
1166 } else {
1167 DECENT_FIT
1168 };
1169 }
1170 } else {
1171 b = if -shortfall > caw[6] {
1172 INF_BAD + 1
1173 } else {
1174 retex_arith::badness(-shortfall, caw[6])
1175 };
1176 fit_class = if b > 12 { TIGHT_FIT } else { DECENT_FIT };
1177 }
1178 if br.do_last_line_fit {
1179 if cur_p.is_null() {
1181 shortfall = 0;
1182 }
1183 g = if shortfall > 0 {
1184 caw[1]
1185 } else if shortfall < 0 {
1186 caw[6]
1187 } else {
1188 0
1189 };
1190 }
1191 }
1192 let node_r_stays_active;
1193 let mut deactivate = false;
1194 if b > INF_BAD || pi == EJECT_PENALTY {
1195 if br.final_pass
1196 && br.minimum_demerits == AWFUL_BAD
1197 && br.items[r].next == ACTIVE
1198 && prev_r == ACTIVE
1199 {
1200 artificial_demerits = true;
1201 } else if b > br.threshold {
1202 deactivate = true;
1203 }
1204 node_r_stays_active = false;
1205 } else {
1206 prev_r = r;
1207 if b > br.threshold {
1208 continue;
1209 }
1210 node_r_stays_active = true;
1211 }
1212 if !deactivate {
1213 let mut d: i32;
1215 if artificial_demerits {
1216 d = 0;
1217 } else {
1218 d = br.line_penalty + b;
1219 d = if d.abs() >= 10000 { 100_000_000 } else { d * d };
1220 if pi != 0 {
1221 if pi > 0 {
1222 d += pi * pi;
1223 } else if pi > EJECT_PENALTY {
1224 d -= pi * pi;
1225 }
1226 }
1227 if hyphenated && r_hyph {
1228 if !cur_p.is_null() {
1229 d += br.double_hyphen_demerits;
1230 } else {
1231 d += br.final_hyphen_demerits;
1232 }
1233 }
1234 if (fit_class as i32 - r_fitness as i32).abs() > 1 {
1235 d += br.adj_demerits;
1236 }
1237 }
1238 if br.tracing {
1239 self.print_feasible_break(br, cur_p, r_passive, b, pi, d, artificial_demerits);
1240 }
1241 d += r_total;
1242 if d <= br.minimal_demerits[fit_class] {
1243 br.minimal_demerits[fit_class] = d;
1244 br.best_place[fit_class] = r_passive;
1245 br.best_pl_line[fit_class] = l;
1246 if br.do_last_line_fit {
1247 br.best_pl_short[fit_class] = shortfall;
1248 br.best_pl_glue[fit_class] = g;
1249 }
1250 if d < br.minimum_demerits {
1251 br.minimum_demerits = d;
1252 }
1253 }
1254 if node_r_stays_active {
1255 continue;
1256 }
1257 }
1258 let rn = br.items[r].next;
1260 br.items[prev_r].next = rn;
1261 br.release(r);
1262 if prev_r == ACTIVE {
1263 let r = br.items[ACTIVE].next;
1266 if let Entry::Delta(d) = br.items[r].entry {
1267 for k in 0..NW {
1268 br.active_width[k] += d[k];
1269 }
1270 br.cur_active_width = br.active_width;
1271 let rn = br.items[r].next;
1272 br.items[ACTIVE].next = rn;
1273 br.release(r);
1274 }
1275 } else if br.is_delta(prev_r) {
1276 let r = br.items[prev_r].next;
1277 if r == ACTIVE {
1278 let d = br.delta(prev_r);
1279 for k in 0..NW {
1280 br.cur_active_width[k] -= d[k];
1281 }
1282 br.items[prev_prev_r].next = ACTIVE;
1283 br.release(prev_r);
1284 prev_r = prev_prev_r;
1285 } else if let Entry::Delta(dr) = br.items[r].entry {
1286 let mut dp_ = br.delta(prev_r);
1287 for k in 0..NW {
1288 br.cur_active_width[k] += dr[k];
1289 dp_[k] += dr[k];
1290 }
1291 br.items[prev_r].entry = Entry::Delta(dp_);
1292 let rn = br.items[r].next;
1293 br.items[prev_r].next = rn;
1294 br.release(r);
1295 }
1296 }
1297 }
1298 }
1299
1300 fn compute_break_width(&mut self, br: &mut Breaker, cur_p: NodeRef, hyphenated: bool) {
1302 br.break_width = br.background;
1303 let mut s = cur_p;
1304 if hyphenated && !cur_p.is_null() && self.nodes.id(cur_p) == id::DISC {
1305 let (_pre, post, rep) = self.nodes.disc_lists(cur_p);
1306 br.break_width[0] -= self.list_width(rep);
1307 br.break_width[0] += self.list_width(post);
1308 br.break_width[0] += br.disc_width;
1309 if br.expand {
1310 let r = self.list_font_stretch(rep, br.expand_kerns);
1311 let p = self.list_font_stretch(post, br.expand_kerns);
1312 br.break_width[FONT_STRETCH] += p.stretch - r.stretch + br.disc_fs.stretch;
1313 br.break_width[FONT_SHRINK] += p.shrink - r.shrink + br.disc_fs.shrink;
1314 }
1315 s = if post.is_null() {
1316 self.nodes.next(cur_p)
1317 } else {
1318 NodeRef::NULL
1319 };
1320 }
1321 while !s.is_null() {
1322 match self.nodes.data(s) {
1323 NodeData::Glue { spec, .. } => {
1324 let g = *spec;
1325 br.break_width[0] -= g.width;
1326 br.break_width[1 + (g.stretch_order as usize).min(4)] -= g.stretch;
1327 br.break_width[6] -= g.shrink;
1328 }
1329 NodeData::Penalty { .. } => {}
1330 NodeData::Math { .. } => br.break_width[0] -= self.nodes.width(s),
1331 NodeData::Kern { kern, .. } => {
1332 if !matches!(self.nodes.subtype(s), kern_sub::USER | kern_sub::ITALIC) {
1333 break;
1334 }
1335 br.break_width[0] -= *kern;
1336 }
1337 _ => break,
1338 }
1339 s = self.nodes.next(s);
1340 }
1341 }
1342
1343 #[allow(clippy::too_many_arguments)]
1345 fn print_feasible_break(
1346 &mut self,
1347 br: &mut Breaker,
1348 cur_p: NodeRef,
1349 r_passive: Option<usize>,
1350 b: i32,
1351 pi: i32,
1352 d: i32,
1353 artificial: bool,
1354 ) {
1355 if br.printed_node != cur_p {
1356 self.printer.print_nl("");
1358 let start = self.nodes.next(br.printed_node);
1359 if cur_p.is_null() {
1360 self.short_display(start);
1361 } else {
1362 let save = self.nodes.next(cur_p);
1363 self.nodes.set_next(cur_p, NodeRef::NULL);
1364 self.printer.print_nl("");
1365 self.short_display(start);
1366 self.nodes.set_next(cur_p, save);
1367 }
1368 br.printed_node = cur_p;
1369 }
1370 self.printer.print_nl("@");
1371 if cur_p.is_null() {
1372 self.print_esc_str("par");
1373 } else {
1374 match self.nodes.id(cur_p) {
1375 id::GLUE => {}
1376 id::PENALTY => self.print_esc_str("penalty"),
1377 id::DISC => self.print_esc_str("discretionary"),
1378 id::KERN => self.print_esc_str("kern"),
1379 _ => self.print_esc_str("math"),
1380 }
1381 }
1382 self.printer.print_str(" via @");
1383 match r_passive {
1384 None => self.printer.print_char('0' as u32),
1385 Some(p) => self.printer.print_int(i64::from(br.passive[p].serial)),
1386 }
1387 self.printer.print_str(" b=");
1388 if b > INF_BAD {
1389 self.printer.print_char('*' as u32);
1390 } else {
1391 self.printer.print_int(i64::from(b));
1392 }
1393 self.printer.print_str(" p=");
1394 self.printer.print_int(i64::from(pi));
1395 self.printer.print_str(" d=");
1396 if artificial {
1397 self.printer.print_char('*' as u32);
1398 } else {
1399 self.printer.print_int(i64::from(d));
1400 }
1401 }
1402
1403 fn post_line_break(
1406 &mut self,
1407 br: &mut Breaker,
1408 best_bet: usize,
1409 best_line: i32,
1410 temp_head: NodeRef,
1411 p: &ParParams,
1412 ) {
1413 let mut breaks: Vec<NodeRef> = Vec::new();
1415 let mut q = match br.items[best_bet].entry {
1416 Entry::Active { passive, .. } => passive,
1417 _ => None,
1418 };
1419 while let Some(i) = q {
1420 breaks.push(br.passive[i].cur_break);
1421 q = br.passive[i].prev_break;
1422 }
1423 breaks.reverse();
1424 let prev_graf = self.cur_list().prev_graf;
1425 let mut cur_line = prev_graf + 1;
1426 let ls = p.left_skip;
1427 let ls_nonzero = ls.width != 0 || ls.stretch != 0 || ls.shrink != 0;
1428 let first = self.nodes.next(temp_head);
1431 let pardir = if first.is_null() {
1432 p.par_dir
1433 } else if let NodeData::LocalPar { dir, .. } = self.nodes.data(first) {
1434 *dir
1435 } else {
1436 p.par_dir
1437 };
1438 let protrude = p.protrude_chars > 0;
1439 let adjust_spacing = p.adjust_spacing > 0;
1440 let mut local_left = NodeRef::NULL;
1443 let mut local_right = NodeRef::NULL;
1444 let mut post_disc_break = false;
1445 let mut open_dirs: Vec<i8> = Vec::new();
1447 for (bi, &cb) in breaks.iter().enumerate() {
1448 let line_first = self.nodes.next(temp_head);
1450 if let Some((h, t)) = self.begin_dir_nodes(&open_dirs) {
1451 self.nodes.link(temp_head, h);
1452 self.nodes.link(t, line_first);
1453 if line_first.is_null() {
1454 self.nodes.set_next(t, NodeRef::NULL);
1455 }
1456 }
1457 self.track_open_dirs(line_first, cb, &mut open_dirs);
1458 {
1460 let mut s = self.nodes.next(temp_head);
1461 while !s.is_null() && s != cb {
1462 if let NodeData::LocalPar {
1463 box_left,
1464 box_right,
1465 ..
1466 } = self.nodes.data(s)
1467 {
1468 local_left = *box_left;
1469 local_right = *box_right;
1470 }
1471 s = self.nodes.next(s);
1472 }
1473 }
1474 let mut q = cb;
1477 let mut disc_break = false;
1478 post_disc_break = false;
1479 let mut rightskip_done = false;
1480 if !q.is_null() {
1481 match self.nodes.id(q) {
1482 id::GLUE => {
1483 let rs = p.right_skip;
1484 if let NodeData::Glue { spec, leader } = self.nodes.data_mut(q) {
1485 *spec = rs;
1486 let l = std::mem::take(leader);
1487 self.nodes.flush_list(l);
1488 }
1489 self.nodes.set_subtype(q, glue_sub::RIGHT_SKIP);
1490 rightskip_done = true;
1491 }
1492 id::DISC => {
1493 let (pre, post, rep) = self.nodes.disc_lists(q);
1497 self.nodes
1498 .set_disc_lists(q, (NodeRef::NULL, NodeRef::NULL, NodeRef::NULL));
1499 self.nodes.flush_list(rep);
1500 let r = self.nodes.next(q);
1501 if !post.is_null() {
1502 let t = self.nodes.tail_of(post);
1503 self.nodes.link(q, post);
1504 self.nodes.link(t, r);
1505 if r.is_null() {
1506 self.nodes.set_next(t, NodeRef::NULL);
1507 }
1508 post_disc_break = true;
1509 }
1510 if !pre.is_null() {
1511 let before = self.find_prev_in(temp_head, q);
1512 let t = self.nodes.tail_of(pre);
1513 self.nodes.link(before, pre);
1514 self.nodes.link(t, q);
1515 }
1516 disc_break = true;
1517 }
1518 id::KERN | id::MATH => match self.nodes.data_mut(q) {
1519 NodeData::Kern { kern, .. } => *kern = 0,
1520 NodeData::Math { surround, glue } => {
1521 *surround = 0;
1522 *glue = Glue::ZERO;
1523 }
1524 _ => {}
1525 },
1526 _ => {}
1527 }
1528 } else {
1529 q = self.nodes.tail_of(temp_head);
1530 }
1531 let q0 = q;
1532 if !rightskip_done {
1533 let r = self.new_glue_node(p.right_skip, glue_sub::RIGHT_SKIP);
1534 if !q.is_null() && q != temp_head {
1536 let a = self.nodes.attr(q).cloned();
1537 self.nodes.set_attr(r, a);
1538 }
1539 let n = self.nodes.next(q);
1540 self.nodes.link(q, r);
1541 self.nodes.link(r, n);
1542 if n.is_null() {
1543 self.nodes.set_next(r, NodeRef::NULL);
1544 }
1545 q = r;
1546 }
1547 if let Some((h, t)) = self.end_dir_nodes(&open_dirs) {
1548 let mut at = q;
1552 if bi + 1 == breaks.len() {
1553 let p = self.find_prev_in(temp_head, q);
1554 if self.nodes.id(p) == id::GLUE
1555 && self.nodes.subtype(p) == glue_sub::PAR_FILL_SKIP
1556 {
1557 at = p;
1558 }
1559 }
1560 let p = self.find_prev_in(temp_head, at);
1561 self.nodes.link(p, h);
1562 self.nodes.link(t, at);
1563 }
1564 if !local_right.is_null() {
1565 let mut target = q;
1568 if bi + 1 == breaks.len() {
1569 let p = self.find_prev_in(temp_head, q);
1570 if self.nodes.id(p) == id::GLUE
1571 && self.nodes.subtype(p) == glue_sub::PAR_FILL_SKIP
1572 {
1573 target = p;
1574 }
1575 }
1576 let b = self.nodes.copy_node(local_right);
1577 let p = self.find_prev_in(temp_head, target);
1578 self.nodes.link(p, b);
1579 self.nodes.link(b, target);
1580 }
1581 if protrude {
1582 self.insert_right_margin_kern(temp_head, q0);
1583 }
1584 let r = self.nodes.next(q);
1586 self.nodes.set_next(q, NodeRef::NULL);
1587 let mut line = self.nodes.next(temp_head);
1588 self.nodes.set_next(temp_head, r);
1589 if !r.is_null() {
1590 self.nodes.set_prev(r, temp_head);
1591 }
1592 if !local_left.is_null() {
1593 let b = self.nodes.copy_node(local_left);
1594 self.nodes.link(b, line);
1595 line = b;
1596 }
1597 if protrude {
1598 let k = self.left_margin_kern(line);
1599 if !k.is_null() {
1600 self.nodes.link(k, line);
1601 line = k;
1602 }
1603 }
1604 if ls_nonzero {
1605 let g = self.new_glue_node(ls, glue_sub::LEFT_SKIP);
1606 if !line.is_null() {
1608 let a = self.nodes.attr(line).cloned();
1609 self.nodes.set_attr(g, a);
1610 }
1611 self.nodes.link(g, line);
1612 line = g;
1613 }
1614 if !line.is_null() {
1615 self.nodes.set_prev(line, NodeRef::NULL);
1616 }
1617 let (cur_width, cur_indent) = if cur_line > br.last_special_line {
1619 (br.second_width, br.second_indent)
1620 } else {
1621 match &br.par_shape {
1622 None => (br.first_width, br.first_indent),
1623 Some(s) => (
1624 s[2 * (cur_line as usize - 1) + 1],
1625 s[2 * (cur_line as usize - 1)],
1626 ),
1627 }
1628 };
1629 self.start_adjust_lists();
1630 let natural = adjust_spacing && self.expand_line(line, cur_width);
1633 self.pack_dir = pardir;
1636 let just_box = if natural {
1637 let b = self.hpack(line, PackSpec::Additional(0));
1638 if let Some(bd) = self.nodes.box_data_mut(b) {
1639 bd.width = cur_width;
1640 }
1641 b
1642 } else {
1643 self.hpack(line, PackSpec::Exactly(cur_width))
1644 };
1645 self.ts.just_box = just_box;
1646 self.nodes.set_subtype(just_box, 1);
1647 if let Some(b) = self.nodes.box_data_mut(just_box) {
1648 b.shift = cur_indent;
1649 b.dir = pardir;
1650 }
1651 let pre_h = self.ts.pre_adjust_head;
1655 if !pre_h.is_null() && !self.nodes.next(pre_h).is_null() {
1656 self.notify_callback("contribute_filter", "pre_adjust");
1657 }
1658 self.append_adjust_material(pre_h);
1659 self.notify_callback("contribute_filter", "box");
1660 self.append_to_vlist(just_box);
1661 let adj_h = self.ts.adjust_head;
1662 if !adj_h.is_null() && !self.nodes.next(adj_h).is_null() {
1663 self.notify_callback("contribute_filter", "adjust");
1664 }
1665 self.append_adjust_material(adj_h);
1666 self.ts.adjust_tail = None;
1667 self.ts.pre_adjust_tail = None;
1668 if cur_line + 1 != best_line {
1670 let mut pen = match &p.interline_penalties {
1671 Some(a) if !a.is_empty() => {
1672 let n = a.len() as i32;
1673 a[(cur_line.min(n) - 1).max(0) as usize]
1674 }
1675 _ => p.interline_penalty,
1676 };
1677 match &p.club_penalties {
1678 Some(a) if !a.is_empty() => {
1679 let n = a.len() as i32;
1680 let r = (cur_line - prev_graf).min(n);
1681 pen += a[(r - 1).max(0) as usize];
1682 }
1683 _ => {
1684 if cur_line == prev_graf + 1 {
1685 pen += p.club_penalty;
1686 }
1687 }
1688 }
1689 match &p.widow_penalties {
1690 Some(a) if !a.is_empty() => {
1691 let n = a.len() as i32;
1692 let r = (best_line - cur_line - 1).min(n);
1693 pen += a[(r - 1).max(0) as usize];
1694 }
1695 _ => {
1696 if cur_line + 2 == best_line {
1697 pen += p.widow_penalty;
1698 }
1699 }
1700 }
1701 if disc_break {
1702 pen += p.broken_penalty;
1703 }
1704 if pen != 0 {
1705 let r = self.new_penalty_node(pen);
1706 self.nodes.set_subtype(r, 1);
1708 self.tail_append(r);
1709 }
1710 }
1711 cur_line += 1;
1712 if bi + 1 < breaks.len() && !post_disc_break {
1714 let next_break = breaks[bi + 1];
1715 let mut r = temp_head;
1716 loop {
1717 let q = self.nodes.next(r);
1718 if q.is_null() || q == next_break {
1719 break;
1720 }
1721 let t = self.nodes.id(q);
1722 if t == id::GLYPH || crate::page::precedes_break(t) {
1723 break;
1724 }
1725 if t == id::KERN
1726 && !matches!(self.nodes.subtype(q), kern_sub::USER | kern_sub::ITALIC)
1727 {
1728 break;
1729 }
1730 if !matches!(t, id::GLUE | id::KERN | id::MATH | id::PENALTY) {
1731 break;
1732 }
1733 r = q;
1734 }
1735 if r != temp_head {
1736 let q = self.nodes.next(r);
1737 self.nodes.set_next(r, NodeRef::NULL);
1738 let first = self.nodes.next(temp_head);
1739 self.nodes.flush_list(first);
1740 self.nodes.link(temp_head, q);
1741 if q.is_null() {
1742 self.nodes.set_next(temp_head, NodeRef::NULL);
1743 }
1744 }
1745 }
1746 }
1747 let _ = post_disc_break;
1748 if !self.nodes.next(temp_head).is_null() {
1749 let rest = self.nodes.next(temp_head);
1751 self.nodes.set_next(temp_head, NodeRef::NULL);
1752 self.nodes.flush_list(rest);
1753 }
1754 self.cur_list_mut().prev_graf = best_line - 1;
1755 }
1756
1757 fn append_adjust_material(&mut self, head: NodeRef) {
1759 if head.is_null() {
1760 return;
1761 }
1762 let first = self.nodes.next(head);
1763 if first.is_null() {
1764 return;
1765 }
1766 let tail = self.cur_list().tail;
1767 self.nodes.link(tail, first);
1768 let t = self.nodes.tail_of(first);
1769 self.cur_list_mut().tail = t;
1770 self.nodes.set_next(head, NodeRef::NULL);
1771 }
1772}