use lightweight_pdf_writer::PdfStructNode;
pub(super) struct StructTreeBuilder {
stack: Vec<Vec<PdfStructNode>>,
next_mcid: u32,
}
impl StructTreeBuilder {
pub(super) fn new() -> Self {
StructTreeBuilder {
stack: vec![Vec::new()],
next_mcid: 0,
}
}
pub(super) fn start_page(&mut self) {
self.next_mcid = 0;
}
pub(super) fn enter(&mut self) {
self.stack.push(Vec::new());
}
pub(super) fn exit(&mut self, tag: &'static str, alt: Option<String>) {
let children = self.stack.pop().expect("exit without a matching enter");
let attrs = (tag == "TH").then_some("/O /Table /Scope /Column");
let elem = PdfStructNode::Elem { tag, alt, attrs, children };
self.stack.last_mut().expect("Document root accumulator is never popped").push(elem);
}
pub(super) fn next_content_ref(&mut self, page_index: usize) -> u32 {
let mcid = self.next_mcid;
self.next_mcid += 1;
self.stack
.last_mut()
.expect("Document root accumulator is never popped")
.push(PdfStructNode::ContentRef { page_index, mcid });
mcid
}
pub(super) fn finish(mut self) -> PdfStructNode {
let children = self.stack.pop().expect("Document root accumulator");
assert!(self.stack.is_empty(), "unbalanced enter/exit calls building the structure tree");
PdfStructNode::Elem {
tag: "Document",
alt: None,
attrs: None,
children,
}
}
}
pub(super) fn find_image_alt(node: &lightweight_pdf_layout::RenderNode) -> Option<String> {
use lightweight_pdf_layout::RenderNode;
match node {
RenderNode::Image { alt, .. } => alt.clone(),
RenderNode::Group { children, .. } => children.iter().find_map(find_image_alt),
RenderNode::Tagged { inner, .. } => find_image_alt(inner),
_ => None,
}
}