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.

Not in the current release. This capability is not part of the published PDFluent SDK; the example below does not compile against the current release. See the changelog for what ships today.
rust
use pdfluent::PdfDocument;

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::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(())
}

Step by step

1

Open the PDF in read mode

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

rust
let doc = PdfDocument::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