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