Building the Shared Library
cd rust
cargo build --release
# Output locations:
# Linux: target/release/libmarkdown_academic.so
# macOS: target/release/libmarkdown_academic.dylib
# Windows: target/release/markdown_academic.dll
For PDF support, add the feature flag:
cargo build --release --features pdf
Python Package
The recommended way to use markdown-academic from Python:
Installation
# Build the Rust library first
cd rust && cargo build --release --features pdf
# Install the Python package
cd ../python && pip install -e .
Basic Usage
import markdown_academic as mda
# Render to HTML fragment
html = mda.render("# Hello\n\nThe equation $E=mc^2$ is famous.")
print(html)
# Render complete HTML document
html = mda.render("# Hello", standalone=True)
# Choose math backend
html = mda.render("$x^2$", math_backend=mda.MathBackend.MATHJAX)
PDF Generation
import markdown_academic as mda
# Check if PDF support is available
if mda.has_pdf_support():
# Render to PDF bytes
pdf_bytes = mda.render_pdf("# My Document\n\n$E=mc^2$")
with open("output.pdf", "wb") as f:
f.write(pdf_bytes)
# Or write directly to file with options
mda.render_pdf_to_file(
"# My Document",
"output.pdf",
paper_size=mda.PaperSize.A4,
title="My Document"
)
Document Class
Parse once, render multiple times:
import markdown_academic as mda
# Using context manager for automatic cleanup
with mda.Document("# Hello $E=mc^2$") as doc:
html_fragment = doc.render()
html_full = doc.render(standalone=True)
# Access metadata
print(doc.title)
print(doc.authors)
Configuration Options
import markdown_academic as mda
# Math backends
mda.MathBackend.KATEX # Fast, client-side (default)
mda.MathBackend.MATHJAX # Full LaTeX support
mda.MathBackend.MATHML # Native browser rendering
# Paper sizes (for PDF)
mda.PaperSize.LETTER # US Letter (default)
mda.PaperSize.A4 # ISO A4
# Full render options
html = mda.render(
source,
standalone=True,
math_backend=mda.MathBackend.KATEX,
title="Document Title",
custom_css="body { font-family: Georgia; }",
strict_citations=False,
strict_references=False,
base_path="/path/to/document"
)
Error Handling
import markdown_academic as mda
try:
html = mda.render(source, strict_citations=True)
except mda.ParseError as e:
print(f"Parse error: {e}")
except mda.ResolutionError as e:
print(f"Resolution error: {e}")
except mda.RenderError as e:
print(f"Render error: {e}")
C API
Header File
// markdown_academic.h
#ifndef MARKDOWN_ACADEMIC_H
#define MARKDOWN_ACADEMIC_H
#include <stdint.h>
// Opaque document handle
typedef struct MdAcademicDocument MdAcademicDocument;
// Configuration options
typedef struct {
int math_backend; // 0 = KaTeX, 1 = MathJax, 2 = MathML
int standalone; // 0 = fragment, 1 = full HTML document
const char* base_path;
const char* title;
int strict_citations;
int strict_references;
} MdAcademicConfig;
// Result type (check error field first)
typedef struct {
char* data; // Result string (NULL on error)
char* error; // Error message (NULL on success)
} MdAcademicResult;
// === One-shot API ===
// Parse and render in one step
MdAcademicResult mdacademic_parse_and_render(
const char* input,
const MdAcademicConfig* config // NULL for defaults
);
// === Two-step API ===
// Parse input into a document
MdAcademicDocument* mdacademic_parse(const char* input);
// Render document to HTML
MdAcademicResult mdacademic_render_html(
const MdAcademicDocument* doc,
const MdAcademicConfig* config
);
// === Memory Management ===
void mdacademic_free_string(char* s);
void mdacademic_free_document(MdAcademicDocument* doc);
void mdacademic_free_result(MdAcademicResult result);
// === Utility ===
const char* mdacademic_version(void);
int mdacademic_has_pdf_support(void);
#endif
Usage Example (C)
#include "markdown_academic.h"
#include <stdio.h>
int main() {
const char* input = "# Hello\n\nThe equation $E=mc^2$ is famous.";
MdAcademicConfig config = {
.math_backend = 0, // KaTeX
.standalone = 1, // Full HTML document
.base_path = NULL,
.title = "My Document",
.strict_citations = 0,
.strict_references = 0
};
MdAcademicResult result = mdacademic_parse_and_render(input, &config);
if (result.error != NULL) {
fprintf(stderr, "Error: %s\n", result.error);
mdacademic_free_result(result);
return 1;
}
printf("%s\n", result.data);
mdacademic_free_result(result); // Always free!
return 0;
}
Low-level ctypes (Python)
For reference, here's how to use the C API directly with ctypes:
import ctypes
from ctypes import c_char_p, c_int, POINTER, Structure
# Load the library
lib = ctypes.CDLL("./libmarkdown_academic.so")
class MdAcademicConfig(Structure):
_fields_ = [
("math_backend", c_int),
("standalone", c_int),
("base_path", c_char_p),
("title", c_char_p),
("strict_citations", c_int),
("strict_references", c_int),
]
class MdAcademicResult(Structure):
_fields_ = [
("data", c_char_p),
("error", c_char_p),
]
# Set up function signatures
lib.mdacademic_parse_and_render.argtypes = [c_char_p, POINTER(MdAcademicConfig)]
lib.mdacademic_parse_and_render.restype = MdAcademicResult
lib.mdacademic_free_result.argtypes = [MdAcademicResult]
lib.mdacademic_version.restype = c_char_p
def render_markdown(text: str, standalone: bool = False) -> str:
config = MdAcademicConfig(
math_backend=0,
standalone=1 if standalone else 0,
base_path=None,
title=None,
strict_citations=0,
strict_references=0
)
result = lib.mdacademic_parse_and_render(
text.encode('utf-8'),
ctypes.byref(config)
)
if result.error:
error_msg = result.error.decode('utf-8')
lib.mdacademic_free_result(result)
raise ValueError(error_msg)
html = result.data.decode('utf-8')
lib.mdacademic_free_result(result)
return html
# Usage
html = render_markdown("# Hello\n\n$E=mc^2$", standalone=True)
print(html)
WebAssembly
Build with the wasm feature for browser/Node.js usage:
# Install wasm-pack if needed
cargo install wasm-pack
# Build the WASM package
cd rust
wasm-pack build --target web --features wasm
JavaScript Usage
import init, { renderMarkdown, RenderOptions } from './pkg/markdown_academic.js';
async function main() {
// Initialize the WASM module
await init();
// Simple usage
const html = renderMarkdown('# Hello $x^2$');
console.log(html);
// With options
const options = new RenderOptions();
options.setMathBackend('katex');
options.setStandalone(true);
options.setTitle('My Document');
const fullHtml = renderMarkdown('# Hello', options);
document.body.innerHTML = fullHtml;
}
main();
Node.js Usage
const { renderMarkdown, RenderOptions } = require('./pkg/markdown_academic.js');
const input = `
# Introduction {#sec:intro}
The equation $E = mc^2$ is famous.
See @sec:intro for more details.
`;
const options = new RenderOptions();
options.setStandalone(true);
const html = renderMarkdown(input, options);
console.log(html);
Browser Script Tag
<script type="module">
import init, { renderMarkdown } from './pkg/markdown_academic.js';
init().then(() => {
const input = document.getElementById('editor').value;
const html = renderMarkdown(input);
document.getElementById('preview').innerHTML = html;
});
</script>