Scan every page resource dictionary and identify fonts that are referenced but not embedded in the file.
use pdfluent::Document;
fn main() -> pdfluent::Result<()> {
let doc = Document::open("input.pdf")?;
let missing: Vec<_> = doc
.fonts()
.filter(|f| !f.is_embedded() && !f.is_standard_14())
.collect();
if missing.is_empty() {
println!("All fonts are embedded.");
} else {
for font in &missing {
println!("Missing: {} ({:?})", font.name(), font.font_type());
}
std::process::exit(1);
}
Ok(())
}For an audit task you only need a read-only Document.
let doc = Document::open("input.pdf")?;doc.fonts() returns an iterator over all font dictionaries referenced from any page resource dictionary in the document.
for font in doc.fonts() {
println!(
"name={} type={:?} embedded={} standard14={}",
font.name(),
font.font_type(),
font.is_embedded(),
font.is_standard_14(),
);
}Standard 14 fonts (Helvetica, Times-Roman, Courier, etc.) are provided by PDF viewers and do not need embedding. All other fonts should be embedded for reliable rendering.
let unembedded: Vec<_> = doc
.fonts()
.filter(|f| !f.is_embedded() && !f.is_standard_14())
.collect();To know which page each font appears on, iterate page by page.
for (i, page) in doc.pages().enumerate() {
for font in page.fonts() {
if !font.is_embedded() && !font.is_standard_14() {
println!("Page {}: unembedded font {}", i + 1, font.name());
}
}
}Use the missing font list to fail a CI pipeline when required fonts are absent.
if !unembedded.is_empty() {
eprintln!("{} unembedded font(s) found", unembedded.len());
std::process::exit(1);
}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.