use crate::ir::{Block, Line, PageLayout};
pub trait Output {
fn render(&self, pages: &[PageLayout]) -> String;
}
pub struct Text;
impl Output for Text {
fn render(&self, pages: &[PageLayout]) -> String {
let mut out = String::new();
for (page_index, page) in pages.iter().enumerate() {
if page_index > 0 {
out.push('\u{c}');
}
let mut written = 0usize;
for block in &page.blocks {
match block {
Block::Heading { lines, .. } | Block::Paragraph { lines, .. } => {
for line in lines {
open_line(&mut out, &mut written);
push_line(&mut out, line);
}
}
Block::List { items, .. } => {
for line in items.iter().flat_map(|item| &item.lines) {
open_line(&mut out, &mut written);
push_line(&mut out, line);
}
}
Block::Table { rows, .. } => {
for row in rows {
open_line(&mut out, &mut written);
push_row(&mut out, row);
}
}
}
}
}
out
}
}
fn open_line(out: &mut String, written: &mut usize) {
if *written > 0 {
out.push('\n');
}
*written += 1;
}
pub(crate) fn line_text(line: &Line) -> String {
let mut out = String::new();
push_line(&mut out, line);
out
}
fn push_line(out: &mut String, line: &Line) {
for inline in &line.inlines {
out.push_str(&inline.text);
}
}
fn push_row(out: &mut String, row: &[crate::ir::Cell]) {
let row_start = out.len();
for line in row.iter().filter_map(|cell| cell.line.as_ref()) {
let text = line_text(line);
let separated = out.len() == row_start
|| out.ends_with(char::is_whitespace)
|| text.starts_with(char::is_whitespace);
if !separated {
out.push(' ');
}
out.push_str(&text);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::{BBox, Cell, Inline};
fn cell(text: &str) -> Cell {
Cell {
line: Some(Line {
inlines: vec![Inline {
text: text.to_string(),
bold: false,
italic: false,
}],
y: 700.0,
x: 72.0,
end_x: 100.0,
size: 10.0,
}),
colspan: 1,
rowspan: 1,
}
}
#[test]
fn a_table_row_joins_cells_with_one_space() {
let page = PageLayout {
blocks: vec![Block::Table {
rows: vec![vec![cell("0.933 "), cell("0.215"), cell(" 0.216")]],
bbox: BBox {
x0: 72.0,
y0: 700.0,
x1: 300.0,
y1: 710.0,
},
}],
};
assert_eq!(Text.render(&[page]), "0.933 0.215 0.216");
}
}