use zpdf::{PdfDocument, StructKid, StructRole};
fn build(objects: &[&str]) -> Vec<u8> {
let mut buf = Vec::from(&b"%PDF-1.7\n"[..]);
let mut offsets = Vec::new();
for (i, body) in objects.iter().enumerate() {
offsets.push(buf.len());
buf.extend_from_slice(format!("{} 0 obj\n{body}\nendobj\n", i + 1).as_bytes());
}
let xref = buf.len();
buf.extend_from_slice(
format!("xref\n0 {}\n0000000000 65535 f \n", objects.len() + 1).as_bytes(),
);
for off in &offsets {
buf.extend_from_slice(format!("{off:010} 00000 n \n").as_bytes());
}
buf.extend_from_slice(
format!(
"trailer\n<< /Size {} /Root 1 0 R >>\nstartxref\n{xref}\n%%EOF\n",
objects.len() + 1
)
.as_bytes(),
);
buf
}
const PAGES: &str = "<< /Type /Pages /Kids [3 0 R] /Count 1 >>";
const PAGE: &str = "<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>";
#[test]
fn tagged_structure_tree_through_facade() {
let data = build(&[
"<< /Type /Catalog /Pages 2 0 R /StructTreeRoot 4 0 R \
/MarkInfo << /Marked true >> >>", PAGES, PAGE, "<< /Type /StructTreeRoot /K 5 0 R /RoleMap << /Heading1 /H1 >> >>", "<< /Type /StructElem /S /Document /P 4 0 R /K [6 0 R 7 0 R 8 0 R] >>", "<< /Type /StructElem /S /Heading1 /P 5 0 R /Pg 3 0 R /K 0 >>", "<< /Type /StructElem /S /P /P 5 0 R /Pg 3 0 R /K [1] >>", "<< /Type /StructElem /S /Figure /P 5 0 R /Pg 3 0 R /Alt (A diagram) \
/K << /Type /OBJR /Obj 9 0 R /Pg 3 0 R >> >>", "<< /Type /Annot /Subtype /Link >>", ]);
let doc = PdfDocument::open(data).expect("open");
assert!(doc.is_tagged());
let tree = doc.struct_tree().expect("structure tree");
assert!(tree.marked);
assert_eq!(tree.children.len(), 1);
assert_eq!(tree.element_count(), 4);
let document = &tree.children[0];
assert_eq!(document.role, StructRole::Document);
let kids: Vec<&zpdf::StructElem> = document.child_elements().collect();
assert_eq!(kids.len(), 3);
let heading = kids[0];
assert_eq!(heading.role, StructRole::H1);
assert!(heading.role.is_heading());
assert_eq!(heading.raw_type, "Heading1");
assert_eq!(heading.page, Some(0));
assert_eq!(
heading.kids,
vec![StructKid::MarkedContent {
page: Some(0),
mcid: 0
}]
);
assert_eq!(kids[1].role, StructRole::P);
let figure = kids[2];
assert_eq!(figure.role, StructRole::Figure);
assert_eq!(figure.accessible_text(), Some("A diagram"));
match &figure.kids[0] {
StructKid::Object { page, obj } => {
assert_eq!(*page, Some(0));
assert_eq!(obj.0, 9);
}
other => panic!("expected OBJR kid, got {other:?}"),
}
}
#[test]
fn untagged_document_has_no_structure_tree() {
let data = build(&["<< /Type /Catalog /Pages 2 0 R >>", PAGES, PAGE]);
let doc = PdfDocument::open(data).expect("open");
assert!(!doc.is_tagged());
assert!(doc.struct_tree().is_none());
}