All formats

pdf (pdf-rs)

LanguageRust
LicenseMIT
Repositorygithub.com/pdf-rs/pdf
Documentationdocs.rs/pdf
Depends onpure Rust crates; optional cache feature (on by default) for object caching
Read / writeyes / experimental

The pdf crate, developed as pdf-rs, maps PDF objects to typed Rust structs with a derive macro: Catalog, PageTree, Page, Resources, Font, XObject, InfoDict, FieldDictionary and many more. Instead of looking up dictionary keys by name, you access fields like page.media_box or info.title. It decodes streams (Flate, LZW, DCT passthrough, CCITT and others), parses content streams into operations, and gives access to embedded fonts and images. Companion projects render pages (pdf_render) and inspect objects.

It’s a good base for tools that analyze PDFs. Writing and modifying files is still experimental, it has no built-in text extraction, and some unusual files fail to parse (encrypted files in particular); for robust reading use lopdf or pdfium-render.

Installation#

Shell
cargo add pdf

Basic usage: open a PDF and read metadata#

Rust
use pdf::file::FileOptions;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file = FileOptions::cached().open("in.pdf")?;
    println!("{} pages", file.num_pages());

    if let Some(ref info) = file.trailer.info_dict {
        if let Some(ref title) = info.title {
            println!("Title: {}", title.to_string_lossy());
        }
        if let Some(ref producer) = info.producer {
            println!("Producer: {}", producer.to_string_lossy());
        }
    }
    Ok(())
}

How to list pages, page sizes and fonts#

Rust
for (i, page) in file.pages().enumerate() {
    let page = page?;
    let mb = page.media_box()?; // inherited from the page tree if needed
    println!("page {}: {} x {} pt, rotate {:?}", i + 1, mb.right - mb.left, mb.top - mb.bottom, page.rotate);
    let resources = page.resources()?;
    for name in resources.fonts.keys() {
        println!("  font {}", name.as_str());
    }
}

How to extract embedded images#

Rust
use pdf::enc::StreamFilter;
use pdf::object::{Resolve, XObject};

let resolver = file.resolver();
let page = file.get_page(0)?; // pages are numbered from 0
let resources = page.resources()?;
for (name, &r) in resources.xobjects.iter() {
    if let XObject::Image(ref img) = *resolver.get(r)? {
        let (data, filter) = img.raw_image_data(&resolver)?;
        let ext = match filter {
            Some(StreamFilter::DCTDecode(_)) => "jpg",
            Some(StreamFilter::JPXDecode) => "jp2",
            _ => "raw",
        };
        std::fs::write(format!("{}.{}", name.as_str(), ext), &data)?;
        println!("{}: {}x{}", name.as_str(), img.width, img.height);
    }
}

How to list form fields#

Rust
let catalog = file.get_root();
if let Some(ref forms) = catalog.forms {
    for field in forms.fields.iter() {
        println!("{:?} = {:?}", field.name, field.value);
    }
}

Tips#