Detect missing or unembedded fonts in a PDF in Rust

Scan every page resource dictionary and identify fonts that are referenced but not embedded in the file.

rust
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(())
}
Install:cargo add pdfluentDownload SDK →

Step by step

1

Open the PDF in read mode

For an audit task you only need a read-only Document.

rust
let doc = Document::open("input.pdf")?;
2

Iterate over all font references

doc.fonts() returns an iterator over all font dictionaries referenced from any page resource dictionary in the document.

rust
for font in doc.fonts() {
    println!(
        "name={} type={:?} embedded={} standard14={}",
        font.name(),
        font.font_type(),
        font.is_embedded(),
        font.is_standard_14(),
    );
}
3

Filter for unembedded non-standard fonts

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.

rust
let unembedded: Vec<_> = doc
    .fonts()
    .filter(|f| !f.is_embedded() && !f.is_standard_14())
    .collect();
4

Print a per-page report

To know which page each font appears on, iterate page by page.

rust
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());
        }
    }
}
5

Return a non-zero exit code for CI gating

Use the missing font list to fail a CI pipeline when required fonts are absent.

rust
if !unembedded.is_empty() {
    eprintln!("{} unembedded font(s) found", unembedded.len());
    std::process::exit(1);
}

Notes and tips

  • A font can be present in the resource dictionary but have an empty or missing font program stream. is_embedded() checks for the stream, not just the dictionary entry.
  • Subset fonts are still considered embedded. The 6-character prefix tag does not affect the is_embedded check.
  • Type3 fonts (custom glyph shapes) are always "embedded" by definition since the glyph procedures are in the PDF itself.
  • Combine this check with the PDF/A validator for a full compliance audit.

Why PDFluent for this

Pure Rust

No JVM, no runtime, no DLL dependencies. Ships as a single native binary or WASM module.

Memory safe

Rust's ownership model prevents buffer overflows and use-after-free. No segfaults in PDF parsing.

Runs anywhere

Same code runs server-side, in Docker, on AWS Lambda, on Cloudflare Workers, or in the browser via WASM.

Frequently asked questions