use super::Line;
use crate::Paragraph;
const PARAGRAPH_BREAK_GAP_RATIO: f32 = 1.8;
pub(super) fn consume_paragraph(lines: &[Line]) -> (Paragraph, usize) {
let mut text = String::new();
let mut consumed = 0;
let mut prev_line: Option<&Line> = None;
for line in lines {
if let Some(prev) = prev_line {
let gap = prev.y - line.y;
if gap > prev.font_size * PARAGRAPH_BREAK_GAP_RATIO {
break;
}
}
append_line(&mut text, &line.text);
consumed += 1;
prev_line = Some(line);
}
(Paragraph { text }, consumed.max(1))
}
fn append_line(text: &mut String, line: &str) {
let line = line.trim();
let ends_with_hyphen = text.ends_with('-');
let continues_word = line.chars().next().is_some_and(|c| c.is_lowercase());
if ends_with_hyphen && continues_word {
text.pop();
text.push_str(line);
return;
}
if !text.is_empty() {
text.push(' ');
}
text.push_str(line);
}
#[cfg(test)]
mod tests {
use super::*;
fn line(text: &str, y: f32, font_size: f32) -> Line {
Line {
text: text.to_string(),
y,
font_size,
cells: Vec::new(),
}
}
#[test]
fn repairs_end_of_line_hyphenation() {
let lines = vec![
line("develop-", 100.0, 10.0),
line("ment continues.", 88.0, 10.0),
];
let (paragraph, consumed) = consume_paragraph(&lines);
assert_eq!(paragraph.text, "development continues.");
assert_eq!(consumed, 2);
}
#[test]
fn does_not_dehyphenate_across_a_capitalized_word() {
let lines = vec![
line("Anglo-", 100.0, 10.0),
line("Saxon history.", 88.0, 10.0),
];
let (paragraph, _) = consume_paragraph(&lines);
assert_eq!(paragraph.text, "Anglo- Saxon history.");
}
#[test]
fn stops_at_a_large_vertical_gap() {
let lines = vec![
line("First paragraph.", 100.0, 10.0),
line("Still first paragraph.", 88.0, 10.0),
line("Second paragraph after a big gap.", 50.0, 10.0),
];
let (paragraph, consumed) = consume_paragraph(&lines);
assert_eq!(paragraph.text, "First paragraph. Still first paragraph.");
assert_eq!(consumed, 2);
}
}