Markdown to PDF

Embed in Rust

The PDF generator is a small Rust crate, markdown2pdf-core. The CLI and the WASM module both wrap it. Use it directly in your own Rust project to skip the binary entirely.

Add the dependency

Until the crate is on crates.io, depend on it via git:

[dependencies]
markdown2pdf-core = { git = "https://github.com/woodyjon/markdown2pdf", package = "markdown2pdf-core" }

Convert markdown to PDF bytes

use markdown2pdf_core::{markdown_to_pdf, Options};

fn main() -> anyhow::Result<()> {
    let markdown = std::fs::read_to_string("README.md")?;
    let opts = Options {
        title: Some("My Document".into()),
    };
    let pdf_bytes: Vec<u8> = markdown_to_pdf(&markdown, &opts)
        .map_err(|e| anyhow::anyhow!("{e}"))?;
    std::fs::write("README.pdf", pdf_bytes)?;
    Ok(())
}

That’s the entire API. The function returns a Vec<u8> containing the rendered PDF — write it to a file, stream it over HTTP, attach it to an email, anything.

Inspect the intermediate Typst source

Useful for debugging conversion issues:

use markdown2pdf_core::markdown_to_typst;

let typst_source: String = markdown_to_typst("# Hello\n\nWorld");
println!("{}", typst_source);

Use Typst directly

If you want to skip the markdown step and feed Typst markup directly:

use markdown2pdf_core::{typst_source_to_pdf, Options};

let typst = r#"
= My Heading

Some *bold* text in Typst.
"#;
let pdf = typst_source_to_pdf(typst, &Options::default())
    .map_err(|e| anyhow::anyhow!("{e}"))?;

Embedded fonts

Inter and JetBrains Mono ship inside the crate via include_bytes!. The compiled binary is ~20 MB but has zero runtime dependencies — copy it anywhere and it works.

Build for WebAssembly

The same crate compiles to WASM via markdown2pdf-wasm. The build script rust/build-wasm.sh runs cargo build --target wasm32-unknown-unknown, then wasm-bindgen --target web, then wasm-opt -Oz if available. Output lands in src/lib/wasm/.

The WASM exports a single function: markdown_to_pdf(md: string) -> Uint8Array.