Basic Usage
The library processes documents in three stages: parse, resolve, and render.
use markdown_academic::{parse, resolve, render_html, ResolveConfig, HtmlConfig};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let input = r#"
# Introduction {#sec:intro}
The equation $E = mc^2$ is famous. See @sec:intro for more.
::: theorem {#thm:main}
Every natural number is interesting.
:::
"#;
// Stage 1: Parse the document into an AST
let doc = parse(input)?;
// Stage 2: Resolve references, citations, and numbering
let resolved = resolve(doc, &ResolveConfig::default())?;
// Stage 3: Render to HTML
let html = render_html(&resolved, &HtmlConfig::default())?;
println!("{}", html);
Ok(())
}
Convenience Functions
For simple use cases, the render function combines all three stages:
use markdown_academic::render;
// Minimal usage
let html = render("# Hello *world*", None, None)?;
// With configuration
let html = render(
input,
Some(&ResolveConfig::default()),
Some(&HtmlConfig { standalone: true, ..Default::default() })
)?;
Configuration
ResolveConfig
Controls reference resolution and citation handling:
use markdown_academic::ResolveConfig;
let config = ResolveConfig {
// Base path for resolving relative bibliography paths
base_path: Some("/path/to/document".to_string()),
// Error on unknown citations (default: false)
// When false, unknown citations render as [?key]
strict_citations: true,
// Error on unknown references (default: false)
// When false, unknown refs render as ??
strict_references: true,
};
HtmlConfig
Controls HTML output generation:
use markdown_academic::{HtmlConfig, MathBackend};
let config = HtmlConfig {
// Math rendering backend
math_backend: MathBackend::KaTeX, // or MathJax, MathML
// Generate complete HTML document vs fragment
standalone: true,
// Document title (used in standalone mode)
title: Some("My Document".to_string()),
// Custom CSS to include (standalone mode)
custom_css: Some("body { max-width: 800px; }".to_string()),
// Include table of contents
include_toc: true,
// CSS class prefix (default: "mda")
class_prefix: "mda".to_string(),
};
MathBackend Options
| Backend | Description |
|---|---|
MathBackend::KaTeX |
Fast client-side rendering (default) |
MathBackend::MathJax |
Comprehensive LaTeX support |
MathBackend::MathML |
Native browser rendering (requires mathml feature) |
Core Types
Document Structure
pub struct Document {
pub metadata: Metadata,
pub blocks: Vec<Block>,
}
pub struct Metadata {
pub title: Option<String>,
pub subtitle: Option<String>,
pub authors: Vec<String>,
pub date: Option<String>,
pub keywords: Vec<String>,
pub institution: Option<String>,
pub macros: HashMap<String, Macro>,
pub bibliography_path: Option<String>,
}
pub struct ResolvedDocument {
pub document: Document,
pub labels: HashMap<String, LabelInfo>,
pub bibliography: Vec<BibEntry>,
pub footnotes: Vec<Footnote>,
}
Block Elements
pub enum Block {
Paragraph(Vec<Inline>),
Heading {
level: u8,
content: Vec<Inline>,
label: Option<String>,
number: Option<String>,
},
CodeBlock {
language: Option<String>,
content: String,
},
BlockQuote(Vec<Block>),
List {
ordered: bool,
start: Option<u32>,
items: Vec<ListItem>,
},
DisplayMath {
content: String,
label: Option<String>,
number: Option<u32>,
},
Environment {
kind: EnvironmentKind,
label: Option<String>,
content: Vec<Block>,
caption: Option<Vec<Inline>>,
number: Option<u32>,
},
Table {
headers: Vec<Vec<Inline>>,
alignments: Vec<Alignment>,
rows: Vec<Vec<Vec<Inline>>>,
caption: Option<Vec<Inline>>,
label: Option<String>,
},
TableOfContents,
ThematicBreak,
PageBreak,
AppendixMarker,
}
Inline Elements
pub enum Inline {
Text(String),
Emphasis(Vec<Inline>),
Strong(Vec<Inline>),
Code(String),
Link {
url: String,
title: Option<String>,
content: Vec<Inline>,
},
Image {
url: String,
alt: String,
title: Option<String>,
},
InlineMath(String),
Citation(Citation),
Reference {
label: String,
resolved: Option<String>, // Filled after resolution
},
Footnote(FootnoteKind),
Subscript(Vec<Inline>),
Superscript(Vec<Inline>),
SmallCaps(Vec<Inline>),
SoftBreak,
HardBreak,
}
Environment Kinds
pub enum EnvironmentKind {
Theorem,
Lemma,
Proposition,
Corollary,
Definition,
Example,
Remark,
Proof,
Figure,
Table,
Algorithm,
Abstract,
Note,
Warning,
}
Error Handling
use markdown_academic::{Error, ParseError, ResolutionError, RenderError};
fn process(input: &str) -> Result<String, Error> {
let doc = parse(input)?;
let resolved = resolve(doc, &ResolveConfig::default())?;
render_html(&resolved, &HtmlConfig::default())
}
match process(input) {
Ok(html) => println!("{}", html),
Err(Error::Parse(ParseError::FrontMatter(msg))) => {
eprintln!("Invalid TOML front matter: {}", msg);
}
Err(Error::Parse(ParseError::Syntax { line, message })) => {
eprintln!("Syntax error on line {}: {}", line, message);
}
Err(Error::Resolution(ResolutionError::UnknownReference(label))) => {
eprintln!("Unknown reference: @{}", label);
}
Err(Error::Resolution(ResolutionError::UnknownCitation(key))) => {
eprintln!("Unknown citation: [@{}]", key);
}
Err(Error::Resolution(ResolutionError::BibliographyLoad(path, msg))) => {
eprintln!("Failed to load {}: {}", path, msg);
}
Err(Error::Render(RenderError::Io(e))) => {
eprintln!("I/O error: {}", e);
}
Err(e) => eprintln!("Error: {}", e),
}
PDF Generation
Enable the pdf feature for PDF output:
# Cargo.toml
[dependencies]
markdown-academic = { version = "0.1", features = ["pdf"] }
Basic PDF Usage
use markdown_academic::{parse, resolve, render_pdf, ResolveConfig, PdfConfig};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let input = "# My Document\n\nContent with math: $E=mc^2$";
let doc = parse(input)?;
let resolved = resolve(doc, &ResolveConfig::default())?;
// Render to PDF bytes
let pdf_bytes = render_pdf(&resolved, &PdfConfig::default())?;
std::fs::write("output.pdf", pdf_bytes)?;
Ok(())
}
PdfConfig Options
use markdown_academic::{PdfConfig, PaperSize, PageMargins};
let config = PdfConfig {
// Paper size: Letter (default) or A4
paper_size: PaperSize::A4,
// Font size in points
font_size: 11,
// Page margins
margins: PageMargins {
top: 72.0, // 1 inch
bottom: 72.0,
left: 72.0,
right: 72.0,
},
// Include title page
title_page: true,
// Include page numbers
page_numbers: true,
// Document title (overrides metadata)
title: Some("My Document".to_string()),
..Default::default()
};
Convenience Functions
use markdown_academic::{render_to_pdf, render_to_pdf_file};
// Get PDF bytes directly (all-in-one)
let pdf = render_to_pdf(input, None, None)?;
// Write to file directly
render_to_pdf_file(input, None, None, "output.pdf")?;