cli_justify/pdf_hybrid/
engine.rs1use super::alignment::TocAlignmentState;
2use super::figure_labels::normalize_sparse_figure_label_blocks;
3use super::wrapping::{flush_pending_aligned_toc_row, flush_pending_pdf_block};
4
5pub(super) enum PendingPdfBlock {
6 Paragraph { indent: String, lines: Vec<String> },
7 ListItem { indent: String, marker: String, lines: Vec<String> },
8}
9
10pub(super) struct PendingAlignedTocRow {
11 pub(super) indent: String,
12 pub(super) entry_prefix: String,
13 pub(super) title: String,
14}
15
16pub(super) struct FormatterEngine {
17 pub(super) line_width: usize,
18 pub(super) out: Vec<String>,
19 pub(super) pending: Option<PendingPdfBlock>,
20 pub(super) pending_toc_row: Option<PendingAlignedTocRow>,
21 pub(super) in_aligned_toc: bool,
22 pub(super) in_code_block: bool,
23 pub(super) alignment_state: TocAlignmentState,
24 pub(super) shell_session_indent: Option<String>,
25 pub(super) pending_deep_callout_bottom_margin: bool,
26 pub(super) pending_code_block_parent_callout_indent: Option<usize>,
27 pub(super) code_block_source_base_indent: Option<usize>,
28 pub(super) code_block_target_base_indent: Option<usize>,
29}
30
31impl FormatterEngine {
32 pub(super) fn new(line_width: usize) -> Self {
33 Self {
34 line_width,
35 out: Vec::new(),
36 pending: None,
37 pending_toc_row: None,
38 in_aligned_toc: false,
39 in_code_block: false,
40 alignment_state: TocAlignmentState::new(),
41 shell_session_indent: None,
42 pending_deep_callout_bottom_margin: false,
43 pending_code_block_parent_callout_indent: None,
44 code_block_source_base_indent: None,
45 code_block_target_base_indent: None,
46 }
47 }
48
49 pub(super) fn process_line(&mut self, line: &str) {
50 if self.pending_deep_callout_bottom_margin && !line.trim().is_empty() {
51 self.apply_pending_deep_callout_bottom_margin();
52 }
53
54 if self.handle_aligned_toc_row_start(line)
55 || self.handle_pending_aligned_toc_row(line)
56 || self.handle_plain_aligned_toc_row(line)
57 || self.handle_shell_session_line(line)
58 || self.handle_list_item_start(line)
59 || self.handle_list_item_continuation(line)
60 || line.trim().is_empty() && self.handle_blank_line()
61 || self.handle_preserved_pdf_layout_line(line)
62 {
63 return;
64 }
65
66 self.handle_paragraph_line(line);
67 }
68
69 pub(super) fn finish(mut self) -> Vec<String> {
70 flush_pending_aligned_toc_row(
71 &mut self.pending_toc_row,
72 &mut self.out,
73 self.line_width,
74 &mut self.alignment_state,
75 );
76 let _ = flush_pending_pdf_block(
77 &mut self.pending,
78 &mut self.out,
79 self.line_width,
80 );
81 normalize_sparse_figure_label_blocks(&mut self.out, self.line_width);
82 self.out
83 }
84}
85
86pub fn justify_pdf_hybrid(text: &str, line_width: usize) -> Vec<String> {
87 let mut engine = FormatterEngine::new(line_width);
88 for line in text.split('\n') {
89 engine.process_line(line);
90 }
91 engine.finish()
92}