Read standard document information fields and XMP metadata from any PDF. Covers title, author, creation date, subject, and custom XMP properties.
use pdfluent::PdfDocument;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let doc = PdfDocument::open("document.pdf")?;
let meta = doc.metadata()?;
println!("Title: {}", meta.title.as_deref().unwrap_or("(none)"));
println!("Author: {}", meta.author.as_deref().unwrap_or("(none)"));
println!("Created: {:?}", meta.creation_date);
Ok(())
}Open the document. Metadata is read from the Info dictionary, which is loaded during the initial parse.
use pdfluent::PdfDocument;
let doc = PdfDocument::open("report.pdf")?;Call doc.metadata() to get a PdfMetadata struct. All standard fields are Option<String> because they may not be present in every PDF.
let meta = doc.metadata()?;
println!("Title: {}", meta.title.as_deref().unwrap_or(""));
println!("Author: {}", meta.author.as_deref().unwrap_or(""));
println!("Subject: {}", meta.subject.as_deref().unwrap_or(""));
println!("Keywords: {}", meta.keywords.as_deref().unwrap_or(""));
println!("Creator: {}", meta.creator.as_deref().unwrap_or(""));
println!("Producer: {}", meta.producer.as_deref().unwrap_or(""));Dates in the Info dictionary are stored as PDF date strings. PDFluent parses them into chrono::DateTime<FixedOffset> values.
if let Some(created) = meta.creation_date {
println!("Created: {}", created.format("%Y-%m-%d %H:%M:%S %Z"));
}
if let Some(modified) = meta.modification_date {
println!("Modified: {}", modified.format("%Y-%m-%d %H:%M:%S %Z"));
}If the PDF has an XMP metadata stream, access it via doc.xmp_metadata(). This returns the raw XMP XML string.
if let Some(xmp) = doc.xmp_metadata()? {
println!("XMP length: {} bytes", xmp.len());
// Parse with your preferred XML library
println!("{}", &xmp[..xmp.len().min(500)]);
}The Info dictionary may contain non-standard keys added by the authoring tool. Access them via meta.custom.
for (key, value) in &meta.custom {
println!("Custom[{}] = {}", key, value);
}No JVM, no runtime, no DLL dependencies. Ships as a single native binary or WASM module.
Rust's ownership model prevents buffer overflows and use-after-free. No segfaults in PDF parsing.
Same code runs server-side, in Docker, on AWS Lambda, on Cloudflare Workers, or in the browser via WASM.