pdfium-render: Fonts
Registering, embedding, inspecting, and extracting fonts with pdfium-render. A font
must be added to a document's PdfFonts collection before any text object can use
it. This skill owns the font lifecycle. Creating the text objects that consume a
font is pdfium-impl-page-objects-edit; reading page text is pdfium-syntax-text.
All API names below are verified against docs.rs/pdfium-render 0.9.1. The default
target is the 0.9.x API; 0.8.x differences are flagged inline.
Quick Reference
| Goal | API | Returns |
|---|---|---|
| Get the font collection | PdfDocument::fonts() / fonts_mut() |
&PdfFonts / &mut PdfFonts |
| Use a built-in font | PdfFonts::helvetica() etc. |
PdfFontToken |
| Use a built-in by enum | PdfFonts::new_built_in(PdfFontBuiltin) |
PdfFontToken |
| Embed a TrueType font | PdfFonts::load_true_type_from_bytes(data, is_cid) |
Result<PdfFontToken, _> |
| Embed a Type1 font | PdfFonts::load_type1_from_bytes(data, is_cid) |
Result<PdfFontToken, _> |
| Look up a registered font | PdfFonts::get(token) |
Option<&PdfFont> |
| Inspect a font | PdfFont::name/family/weight/is_embedded |
see methods.md |
| Read raw font program | PdfFont::data() |
Result<Vec<u8>, _> |
| Walk glyphs | PdfFont::glyphs() |
&PdfFontGlyphs |
PdfFontBuiltin variants (14): TimesRoman, TimesBold, TimesItalic,
TimesBoldItalic, Helvetica, HelveticaBold, HelveticaOblique,
HelveticaBoldOblique, Courier, CourierBold, CourierOblique,
CourierBoldOblique, Symbol, ZapfDingbats.
Full signatures with version annotations: see references/methods.md.
The Token Model
PdfFonts constructors do NOT return a PdfFont. They return a PdfFontToken: a
small Copy handle that references a font registered in the document. The flow is
always:
fonts_mut() -> load/built-in constructor -> PdfFontToken (Copy)
-> pass token to PdfPageTextObject::new()
-> fonts().get(token) -> Option<&PdfFont> for inspection
PdfFontToken is Copy, Eq, and Hash, so registering a font ONCE and reusing
the token everywhere is correct and cheap. PdfFontToken is NOT Send/Sync; do
not move it across threads.
ALWAYS register a font once per document and reuse its token. NEVER call a load function repeatedly for the same font: each call embeds another copy of the font program and inflates the file.
Version Trap: removed PdfFont constructors
Direct PdfFont constructors (PdfFont::new_*) were deprecated in 0.8.3 and
REMOVED in 0.9.0. Code that constructs a PdfFont directly does not compile against
0.9.x.
// WRONG: no such constructor on 0.9.x.
let font = PdfFont::new_true_type_from_bytes(&document, &bytes, true)?;
// CORRECT: register through the document's PdfFonts collection.
let token = document.fonts_mut().load_true_type_from_bytes(&bytes, true)?;
ALWAYS obtain fonts through document.fonts_mut(). A PdfFont reference is only
ever read back with fonts().get(token) or from a text object's font().
Decision Tree: which font
Need a standard Latin font, no embedding, smallest file?
-> PdfFonts::helvetica() / times_roman() / courier() (+ bold/italic variants)
These are the 14 built-in PDF fonts; viewers supply the glyphs.
Need a specific brand / non-Latin / Unicode font in the output?
-> embed it: load_true_type_from_bytes / load_type1_from_bytes
The font program is stored IN the PDF.
Embedding a CJK or large-Unicode font (multi-byte character codes)?
-> is_cid_font = true
Embedding a simple single-byte Latin font?
-> is_cid_font = false
Need to read or save a font already inside a PDF?
-> text_object.font() -> is_embedded()? -> data()
Pattern: Built-In Font
The 14 built-in fonts need no font file. The constructor takes &mut self, so call
it through fonts_mut():
use pdfium_render::prelude::*;
let pdfium = Pdfium::default();
let mut document = pdfium.create_new_pdf()?;
let helvetica = document.fonts_mut().helvetica(); // PdfFontToken
// `helvetica` is now usable as the `font` argument of PdfPageTextObject::new.
new_built_in(PdfFontBuiltin) is the enum-driven equivalent:
let token = document.fonts_mut().new_built_in(PdfFontBuiltin::TimesBold);
Built-in fonts cover Latin text only. They CANNOT render emoji, CJK, or other scripts outside their character set. For anything else, embed a font.
Pattern: Embed a Custom TrueType Font
use pdfium_render::prelude::*;
let pdfium = Pdfium::default();
let mut document = pdfium.create_new_pdf()?;
// Embed from bytes already in memory.
let font_bytes = std::fs::read("Inter-Regular.ttf")?;
let inter = document.fonts_mut()
.load_true_type_from_bytes(&font_bytes, false)?; // simple Latin font
// Or embed straight from a path.
let other = document.fonts_mut()
.load_true_type_from_file("NotoSansJP-Regular.ttf", true)?; // CID font
let _ = (inter, other);
load_true_type_from_file, load_true_type_from_reader, and
load_true_type_from_bytes all take (source, is_cid_font: bool) and return
Result<PdfFontToken, PdfiumError>. Type1 fonts use the matching
load_type1_from_* functions. The _from_fetch and _from_blob async variants
exist for WASM targets only (see pdfium-impl-wasm).
The is_cid_font Flag
is_cid_font is the single most important font-embedding decision and a documented
cause of garbled text (issues #84, #226).
is_cid_font = true: the font is registered as a CID-keyed font, using multi-byte character codes. ALWAYS usetruefor CJK fonts and for large-Unicode TrueType fonts that must cover characters beyond a single-byte range.is_cid_font = false: the font is registered as a simple font with single-byte character codes. Usefalsefor simple Latin fonts.
A mismatch between the flag and the font's actual encoding maps character codes to
the wrong glyphs: the rendered text looks like garbage, boxes, or ÿ characters,
and copied text comes back wrong. See references/anti-patterns.md.
Pattern: Inspect a Font
Look a registered font back up with get(token), or take it from a text object:
if let Some(font) = document.fonts().get(token) {
println!("name: {}", font.name());
println!("family: {}", font.family());
println!("embedded: {:?}", font.is_embedded()); // Result<bool, _>
println!("serif: {}", font.is_serif());
println!("italic: {}", font.is_italic());
println!("weight: {:?}", font.weight()); // Result<PdfFontWeight, _>
}
PdfFont::name() and family() return String. The is_* predicate methods
return bool directly. is_embedded(), weight(), italic_angle(), ascent(),
and descent() return a Result because they query the PDFium font program.
ascent(font_size) and descent(font_size) take a PdfPoints size.
Pattern: Extract an Embedded Font
PdfFont::data() returns the raw font program. Combined with is_embedded() this
extracts fonts from an existing PDF:
let pdfium = Pdfium::default();
let document = pdfium.load_pdf_from_file("input.pdf", None)?;
for page in document.pages().iter() {
for object in page.objects().iter() {
if let Some(text_object) = object.as_text_object() {
let font = text_object.font(); // PdfFont<'_>
if font.is_embedded()? {
let program = font.data()?; // Vec<u8>
std::fs::write(format!("{}.fontdata", font.name()), program)?;
}
}
}
}
text_object.font() returns a PdfFont by value. Narrowing a PdfPageObject to
PdfPageTextObject with as_text_object() is covered by pdfium-syntax-page-objects.
NEVER call data() on a non-embedded font expecting the font file: built-in fonts
are supplied by the viewer, not stored in the PDF.
Glyphs
PdfFont::glyphs() returns &PdfFontGlyphs, the font's glyph collection:
let glyphs = font.glyphs();
println!("glyph count: {}", glyphs.len());
for glyph in glyphs.iter() {
let width = glyph.width_at_font_size(PdfPoints::new(12.0));
let _ = width;
}
PdfFontGlyphs exposes len(), is_empty(), as_range(), as_range_inclusive(),
get(index), and iter(). A PdfFontGlyph gives width_at_font_size(PdfPoints)
and segments_at_font_size(PdfPoints) for the glyph outline path. Glyph indices use
the PdfFontGlyphIndex type alias.
Version Traps
| Item | 0.8.x | 0.9.x | Action |
|---|---|---|---|
PdfFont::new_* direct constructors |
deprecated 0.8.3 | REMOVED | use PdfFonts load/built-in functions |
PdfFonts collection |
present | present | reach it via fonts() / fonts_mut() |
_from_fetch / _from_blob loaders |
WASM only | WASM only | native code uses _from_file/_bytes/_reader |
Anti-Patterns (summary)
- Calling a removed
PdfFont::new_*constructor: register viafonts_mut(). - Wrong
is_cid_fontflag: garbled, boxed, or unselectable text. - Expecting a built-in font to render emoji or CJK: it cannot; embed a font.
- Re-loading the same font per text object: bloats the file; register once, reuse
the
Copytoken. - Calling
data()on a non-embedded font and expecting the font program.
Each anti-pattern with the failure detail and fix: see references/anti-patterns.md.
Cross-References
pdfium-impl-page-objects-edit: building thePdfPageTextObjectthat uses aPdfFontToken(PdfPageTextObject::newtakesimpl ToPdfFontToken).pdfium-syntax-text: reading existing page text and per-character font info.pdfium-syntax-page-objects: narrowingPdfPageObjectwithas_text_object().pdfium-impl-wasm: theload_*_from_fetch/load_*_from_blobasync loaders.pdfium-core-memory: the self-referential trap when storing a document with its fonts in one struct (0.9.0 relaxed this).pdfium-errors-runtime:PdfiumErrorvariants from font loading.
Reference Files
references/methods.md: complete API signatures with version annotations.references/examples.md: verified, runnable Rust programs.references/anti-patterns.md: real failures, why they fail, the fix.
Source
Verified 2026-05-20 against https://docs.rs/pdfium-render/latest/pdfium_render/
(prelude struct pages for PdfFonts, PdfFont, PdfFontToken, PdfFontGlyphs,
PdfFontGlyph, PdfPageTextObject, PdfDocument, and the PdfFontBuiltin enum).