Embed document in PDF with Rust pdf-writer crate
19:25 15 Feb 2026

I would like to use the low-level pdf-writer crate in version 0.14.0 (latest) to add the liberation sans font to my PDF.

So far, I have

use pdf_writer::{Finish, Name, Pdf, Rect, Ref, Str, Obj};
use pdf_writer::types::{CidFontType, FontFlags, SystemInfo, UnicodeCmap};
use ttf_parser::{Face, GlyphId};

pub const LIBERATION_SANS_REGULAR_TTF: &[u8] =
    include_bytes!("../../assets/fonts/LiberationSansRegular.ttf");

pub const LIBERATION_SANS_BOLD_TTF: &[u8] =
    include_bytes!("../../assets/fonts/LiberationSansBold.ttf");

const SYSTEM_INFO: SystemInfo = SystemInfo {
    registry: Str(b"Adobe"),
    ordering: Str(b"Identity"),
    supplement: 0,
};

pub struct EmbeddedFont {
    pub type0_ref: Ref,
}

pub fn embed_ttf_font(pdf: &mut Pdf, next_id: &mut Ref, font_data: &[u8]) -> EmbeddedFont {

    let face = Face::parse(font_data, 0).expect("Invalid font data");

    // Allocate IDs
    let type0_ref = next_id.bump();
    let cid_ref = next_id.bump();
    let desc_ref = next_id.bump();
    let stream_ref = next_id.bump();
    let cmap_ref = next_id.bump();

    // Extract unicode cmap from .ttf
    let mut cmap = UnicodeCmap::::new(Name(b"Custom"), SYSTEM_INFO);
    for subtable in face
        .tables()
        .cmap
        .iter()
        .flat_map(|c| c.subtables) {
            if subtable.is_unicode() {
                subtable.codepoints(|cp| {
                    if let (Some(c), Some(gid)) = (char::from_u32(cp), subtable.glyph_index(cp)) {
                        cmap.pair(gid.0, c);
                    }
                });
                break;
            }
    }
    pdf.stream(cmap_ref, &cmap.finish()).finish();

    // Width
    // PDF CIDFont widths are: [first_gid [w1 w2 w3]]
    let mut widths = Vec::new();
    for gid in 0..face.number_of_glyphs() {
        let width = face.glyph_hor_advance(GlyphId(gid)).unwrap_or(0);
        widths.push(width as f32);
    }
    
    // Here I am stuck


    EmbeddedFont {
        type0_ref
    }

Where I am stuck now is that I have to add that to the pdf stream using something like:

    pdf.font_stream(stream_ref, font_data)
        .pair(Name(b"Length1"), font_data.len() as i32)
        .finish();

But the API complains that neither does font_stream exist nor does the normal stream contain pair() or insert(). There seems to have been some API change which is undocumented. At the moment, I am thinking about moving to a more high-level crate, but I would love to have as few dependencies as possible.

pdf rust cargo pdf-writer font-embedding