SuperAPI AI Directory

pdfium-impl-fonts

Office & productivity

View repository

Updated 10-15 22:18

About this skill

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 use true for 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. Use false for 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 via fonts_mut().
  • Wrong is_cid_font flag: 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 Copy token.
  • 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 the PdfPageTextObject that uses a PdfFontToken (PdfPageTextObject::new takes impl ToPdfFontToken).
  • pdfium-syntax-text: reading existing page text and per-character font info.
  • pdfium-syntax-page-objects: narrowing PdfPageObject with as_text_object().
  • pdfium-impl-wasm: the load_*_from_fetch / load_*_from_blob async 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: PdfiumError variants 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).