Skip to main content

cli_justify/pdf_hybrid/
engine.rs

1use 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}