This guide shows developers how to use PDFluent to manage fonts in PDF documents, ensuring compatibility and optimising file size.
Load font files from disk and embed them into a PDF so it renders correctly on any system.
use pdfluent::{PdfDocument, Font};
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("input.pdf")?;
// Load a font from disk and embed it
let font = Font::from_file("fonts/NotoSans-Regular.ttf")?;
doc.embed_font(&font)?;
doc.save("with_embedded_font.pdf")?;
Ok(())
}PDFluent accepts TrueType (.ttf), OpenType (.otf), and TrueType Collection (.ttc) files. Pass the path to Font::from_file.
use pdfluent::Font;
let font = Font::from_file("fonts/NotoSans-Regular.ttf")?;embed_font adds the font program to the PDF as a stream object and registers it in the document font resources.
doc.embed_font(&font)?;After embedding, use the font handle when adding text to a page. The font name in the content stream references the embedded resource.
use pdfluent::content::{TextBuilder, Color};
let mut page = doc.add_page(pdfluent::PageSize::A4);
page.draw_text(
TextBuilder::new("Hello, world!")
.font(&font)
.size(14.0)
.position(72.0, 700.0)
.color(Color::black()),
)?;To re-embed a font that a page already references by name but is not embedded, look it up by its PDF resource name.
let font_data = std::fs::read("fonts/Arial.ttf")?;
doc.embed_font_data("Arial", &font_data)?;List fonts after the operation to confirm each font shows is_embedded = true.
for font in doc.fonts() {
assert!(font.is_embedded(), "Font {} not embedded", font.name());
}
doc.save("with_embedded_font.pdf")?;Strip unused glyphs from embedded fonts so the PDF only carries the characters that actually appear in the document.
use pdfluent::PdfDocument;
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("report.pdf")?;
let _report = doc.subset_fonts()?;
doc.save("subset.pdf")?;
Ok(())
}Before subsetting, you can list embedded fonts and their sizes to understand what will change.
use pdfluent::PdfDocument;
let mut doc = PdfDocument::open("input.pdf")?;
println!("{} pages", doc.page_count());FontSubsetOptions controls which font types are processed. Type 1 fonts often have limited glyph sets already; focus on TrueType and OpenType where gains are largest.
// subset_fonts() removes unused glyphs from embedded fonts.
// It is core-tier and takes no configuration in 1.0.PDFluent scans every page content stream, collects the Unicode codepoints actually used, then rewrites each embedded font to contain only those glyphs.
let report = doc.subset_fonts()?;
println!(
"Subsetted {} of {} fonts, saved {} bytes",
report.fonts_subsetted, report.fonts_processed, report.bytes_saved,
);Check the font sizes again after subsetting to measure the reduction.
// The FontSubsetReport summarises the result:
// fonts_processed - fonts_subsetted - bytes_saved
println!("bytes saved: {}", report.bytes_saved);Write the subsetted file. Combine with compress_streams() for maximum size reduction.
use pdfluent::CompressOptions;
doc.compress(CompressOptions::strict())?;
doc.save("subsetted.pdf")?;Scan every page resource dictionary and identify fonts that are referenced but not embedded in the file.
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(())
}For an audit task you only need a read-only Document.
let doc = PdfDocument::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);
}