lightweight-pdfv0.3.0

← All examples

Tagged PDF / PDF/UA-1

Document::pdf_ua() writes a structure tree, marked content and /Lang, marks the watermark and footer as artifacts, and requires alt text for the image. Also PDF/A-3b.

Tagged PDF / PDF/UA-1

examples/demo_pdf_ua.rs
//! Example: Tagged PDF/PDF-UA output (issue #27) — `Document::pdf_ua()`
//! writes a structure tree (`/StructTreeRoot`, one `/StructElem` per
//! heading/paragraph/table/list/figure), marked content (`BDC`/`EMC` with
//! MCIDs) in every content stream, and marks the watermark/footer as
//! artifacts (pagination decoration, excluded from reading order) rather
//! than structure. Implies `.pdf_a3b()` (ADR-019) — this document is both
//! PDF/A-3b and PDF/UA-1 conformant.
//!
//! Run: `cargo run -p lightweight-pdf --example demo_pdf_ua --features tagged-pdf,png`
//! Verify: `verapdf --flavour ua1 examples/demo_pdf_ua.pdf` (also passes
//! `--flavour 3b`) — see `docs.verapdf.org/install`, or `docker run --rm
//! -v "$PWD/examples:/data" verapdf/cli --flavour ua1 /data/demo_pdf_ua.pdf`

use lightweight_pdf::*;

const LOGO: &[u8] = include_bytes!("../test-fixtures/images/logo_rgba.png");

fn main() {
    let mut doc = Document::new(PageFormat::A4)
        .margin(Margin::all(40.0))
        .pdf_ua()
        .lang("en-US")
        .watermark(Watermark::new("SAMPLE"))
        .footer(Footer::new(20.0, |ctx| {
            Text::new(format!("Page {} of {}", ctx.page, ctx.total_pages)).into()
        }));
    doc.metadata.title = Some("Tagged PDF / PDF-UA Demo".to_string());
    doc.metadata.author = Some("lightweight-pdf".to_string());

    doc.add(Text::new("Tagged PDF / PDF-UA Demo").heading1());
    doc.add(Text::new(
        "This document has a real structure tree: headings, this paragraph, the table and \
         list below, and the image all have their own tagged structure element and reading \
         order that follows document order, not render order. The watermark and page-number \
         footer are marked as artifacts, excluded from that reading order entirely.",
    ));

    doc.add(Text::new("Accessibility checklist").heading2());
    doc.add(
        Table::new()
            .columns([TableColumn::flex(1.0), TableColumn::fixed(70.0).align(Align::End)])
            .header(["Check", "Status"])
            .rows(vec![
                vec![Element::from("Structure tree"), Element::from("yes")],
                vec![Element::from("Reading order"), Element::from("yes")],
                vec![Element::from("Alt text"), Element::from("yes")],
            ]),
    );

    doc.add(Text::new("Highlights").heading2());
    doc.add(
        List::new()
            .bullet(Text::new("Headings tagged H1/H2"))
            .bullet(Text::new("Table tagged Table/TR/TH/TD"))
            .numbered(Text::new("Image tagged Figure with Alt text")),
    );

    doc.add(
        Image::new(LOGO)
            .expect("valid PNG fixture")
            .width(100.0)
            .alt("lightweight-pdf logo"),
    );

    let (bytes, warnings) = doc.render_with_diagnostics().expect("render should succeed");
    assert!(warnings.is_empty(), "unexpected warnings: {warnings:?}");
    std::fs::write("examples/demo_pdf_ua.pdf", &bytes).expect("write examples/demo_pdf_ua.pdf");
    println!("wrote examples/demo_pdf_ua.pdf ({} bytes)", bytes.len());
}
  • Rust
  • tagged-pdf
  • Accessibility