Python API Workflows
all2md exposes a small set of top-level functions, all importable directly from all2md
(from all2md import to_markdown, convert, to_ast, from_ast). They share one pipeline —
the difference is only which end of it you drive. This guide walks through those entry points
and the patterns you’ll use when building conversion workflows.
Core Workflow
Every conversion flows through all2md’s AST (Abstract Syntax Tree) architecture:
Input Format → Parser → AST → Renderer → Output Format
Each public function drives some span of this flow — from a one-liner that covers the whole pipeline to helpers that stop at the AST so you can inspect or transform it yourself.
Choosing an entry point
All of these are first-class, supported, public functions (see all2md.__all__). Pick the
one that matches your target:
Function |
Use it when |
Returns |
|---|---|---|
|
Converting anything → Markdown (the most common case) |
|
|
Converting anything → anything (DOCX, PDF, HTML, EPUB, …) |
|
|
You want the parsed |
|
|
You already hold a |
|
|
Rendering Markdown → another format (thin wrapper over |
|
Splitting a document into provenance-carrying chunks for RAG/LLM pipelines |
|
Note
to_markdown() and convert() are peer entry points, not one wrapping the other.
to_markdown(src) is the ergonomic shortcut for the Markdown target and is equivalent to
convert(src, target_format="markdown") for file/bytes/stream inputs (it additionally
accepts an already-parsed Document and normalizes line endings). Reach for convert()
the moment your target isn’t Markdown.
Shortcut for Markdown output: to_markdown()
For the library’s headline use case — turning any supported document into Markdown —
to_markdown() is the shortest path. It auto-detects the source format and always returns a
Markdown str.
from all2md import to_markdown
markdown = to_markdown("report.docx") # auto-detects DOCX
markdown = to_markdown("scanned.pdf") # auto-detects PDF
# Already have a parsed AST? to_markdown renders it directly.
from all2md import to_ast
doc = to_ast("report.docx")
markdown = to_markdown(doc)
General-purpose conversion: convert()
Use convert() for any conversion whose target isn’t Markdown, and for the reverse and
cross-format directions all2md supports.
Automatically detects the source format when possible and infers the target format from the
outputname.Accepts file paths, in-memory strings/bytes, or file-like objects.
Exposes parser/renderer options, transform pipelines, hooks, and progress callbacks.
from all2md import convert
# Convert a DOCX report to Markdown text (equivalent to to_markdown("report.docx"))
markdown = convert("report.docx", target_format="markdown")
# Produce a PDF from Markdown (returns bytes when output is omitted)
pdf_bytes = convert("summary.md", target_format="pdf")
# Write directly to a file; target format inferred from extension
convert("slides.md", output="slides.pptx")
Note
convert() returns str for text formats and bytes for binary formats when
output is None. Provide a path or file-like object to stream the result instead.
Quick Start
Markdown to Other Formats
from all2md import convert
# Core formats
convert("document.md", target_format="docx", output="document.docx")
convert("document.md", target_format="html", output="document.html")
convert("document.md", target_format="pdf", output="document.pdf")
# Additional outputs
convert("book.md", target_format="epub", output="book.epub")
convert("slides.md", target_format="pptx", output="presentation.pptx")
Other Formats to Markdown
from all2md import convert
markdown_from_docx = convert("meeting-notes.docx", target_format="markdown")
markdown_from_pdf = convert("scanned.pdf", target_format="markdown")
markdown_from_html = convert("<h1>Title</h1>", source_format="html", target_format="markdown")
# Persist the result
convert("slides.pptx", output="slides.md", target_format="markdown")
Convenience Helpers and AST Access
Specialized helpers remain available when you need more control.
from all2md import from_ast, to_ast, from_markdown
# Direct AST access for custom pipelines
doc_ast = to_ast("document.md")
transformed = from_ast(doc_ast, target_format="html")
# ``from_markdown`` is a convenience wrapper that forwards to ``convert()``
html = from_markdown("document.md", target_format="html")
Markdown to Word (DOCX)
Basic Conversion
from all2md import convert
from all2md.renderers.docx import DocxRendererOptions
# Simple conversion
convert('report.md', target_format='docx', output='report.docx')
# With options
options = DocxRendererOptions(
default_font='Arial',
default_font_size=12,
use_styles=True
)
convert('report.md', target_format='docx', output='report.docx', renderer_options=options)
Preserving Markdown Features
The DOCX renderer supports:
Headings (H1-H6) → Word heading styles
Bold/Italic/Strikethrough → Character formatting
Lists (ordered, unordered, nested) → Word list styles
Tables → Word tables with borders
Code blocks → Monospace font with background
Links → Hyperlinks
Images → Embedded images (if paths are accessible)
Block quotes → Indented paragraphs
from all2md import convert
markdown_content = """
# Annual Report 2024
## Executive Summary
This report covers **fiscal year 2024** with the following highlights:
- Revenue increased by *15%*
- Customer base grew to **10,000 users**
- New product launch successful
### Key Metrics
| Metric | 2023 | 2024 | Change |
|--------|------|------|--------|
| Revenue | $1M | $1.15M | +15% |
| Users | 8,500 | 10,000 | +17.6% |
> "Our best year yet!" - CEO
"""
convert(
markdown_content,
target_format='docx',
output='annual_report.docx'
)
Advanced DOCX Customization
from all2md import convert
from all2md.renderers.docx import DocxRendererOptions
# Customize rendering
options = DocxRendererOptions(
default_font='Calibri',
default_font_size=11,
code_font='Courier New',
code_font_size=10,
table_style='Light Grid Accent 1',
use_styles=True
)
# Render to DOCX (returns bytes directly)
docx_bytes = convert('document.md', target_format='docx', renderer_options=options)
# Write to file
with open('output.docx', 'wb') as f:
f.write(docx_bytes)
Round-Trip Editing with Template Preservation
A frequent use case — especially for LLM-driven editing — is parsing an
existing .docx, transforming it via the AST or its Markdown projection,
and writing it back to a .docx that still looks like the original
document. The plain round-trip is lossy (page setup, theme, custom styles
all collapse to defaults), so all2md exposes two related mechanisms to
preserve formatting via template-based rendering:
The renderer option
template_pathalready loaded the template’s styles, theme, headers/footers, and section properties (margins, page size). This existed before — it’s the foundation.A new
clear_template_bodyflag (defaultFalse) controls whether the template’s existing body content is kept and the AST appended (letterhead-style, the historical behavior) or cleared and replaced by the AST (round-trip-style).A
preserve_formattingkwarg onfrom_ast(),from_markdown(), andconvert()is a one-flag shortcut that picks up the source pathto_astautomatically stashes onDocument.metadata['source_path']and applies bothtemplate_pathandclear_template_body=True.
The simplest LLM-edit workflow:
from all2md import to_ast, from_ast
# Parse the original document. to_ast stashes the absolute file path on
# the AST when given a file path (skipped for streams or bytes).
doc = to_ast("report.docx")
assert doc.metadata["source_path"].endswith("report.docx")
# ... apply transforms, hand the markdown form to an LLM and replace
# the AST, run a NodeTransformer, etc. ...
# Render back. preserve_formatting=True means the original is used as
# the rendering template and its body is replaced by the new AST.
from_ast(doc, "docx", output="report.docx", preserve_formatting=True)
The same flag works on convert for a direct .docx → .docx pass
that has to round-trip through the AST:
from all2md import convert
convert("input.docx", "output.docx", preserve_formatting=True)
If you need finer control — for example, you parsed from a stream and want to use a different docx as the template — pass the renderer options explicitly instead:
from all2md import from_ast
from all2md.options.docx import DocxRendererOptions
options = DocxRendererOptions(
template_path="corporate_template.docx",
clear_template_body=True, # replace template body with AST content
)
from_ast(doc, "docx", output="report.docx", renderer_options=options)
Set clear_template_body=False (the default) to keep the template’s
body content and append the AST after it — the natural fit for a
letterhead, cover page, or boilerplate-prefix template:
options = DocxRendererOptions(
template_path="letterhead.docx",
# clear_template_body=False (default; template body is preserved)
)
from_ast(doc, "docx", output="letter.docx", renderer_options=options)
What round-trips and what doesn’t. When preserve_formatting=True
or template_path is set, the parser also stashes the originating Word
style name on AST nodes via metadata['source_style']. The renderer
re-applies that style on output if the template defines it, so a
paragraph with style "Chapter Title" round-trips as "Chapter
Title" rather than collapsing to "Heading 1". Run-level character
styles (e.g. "Quote Char") are not yet preserved; only paragraph-
level styles. Direct/manual run formatting outside named styles, content
controls, drawings/SmartArt, and run-anchored comments are likewise lost
on round-trip.
Markdown to HTML
Basic Conversion
from all2md import convert
from all2md.renderers.html import HtmlRendererOptions
# Simple conversion
html = convert('document.md', target_format='html')
# With options
options = HtmlRendererOptions(
standalone=True,
css_style='embedded',
syntax_highlighting=True,
include_toc=False
)
html = convert('document.md', target_format='html', renderer_options=options)
# Write to file
with open('document.html', 'w', encoding='utf-8') as f:
f.write(html)
HTML Templates and Styling
from all2md import convert
from all2md.renderers.html import HtmlRendererOptions
# Custom CSS file
# Create a custom.css file with your styles:
# body { font-family: 'Georgia', serif; max-width: 800px; }
# h1 { color: #2c3e50; }
# h2 { color: #34495e; }
# code { background-color: #f4f4f4; }
options = HtmlRendererOptions(
standalone=True,
css_style='external',
css_file='custom.css',
syntax_highlighting=True,
escape_html=True
)
html = convert('document.md', target_format='html', renderer_options=options)
Standalone HTML Documents
from all2md import convert
from all2md.renderers.html import HtmlRendererOptions
options = HtmlRendererOptions(
standalone=True,
css_style='embedded',
syntax_highlighting=True,
include_toc=True,
math_renderer='mathjax'
)
html = convert('article.md', target_format='html', renderer_options=options)
# Result is a complete HTML document
with open('article.html', 'w', encoding='utf-8') as f:
f.write(html)
Warning
When using math_renderer='mathjax', you must also enable remote script loading
by setting allow_remote_scripts=True in the HTML options. This is required for
MathJax CDN access but disabled by default for security.
options = HtmlRendererOptions(
math_renderer='mathjax',
allow_remote_scripts=True # Required for MathJax CDN
)
Note
all2md supports two approaches for static sites:
Static Site Generation: Use the
generate-sitecommand for turnkey Hugo, Jekyll, MkDocs, Zola, or Eleventy site creation with scaffolding and frontmatter. See Static Site Generation.Custom HTML Templates: Use HTML renderer template modes (inject/replace/jinja) for full control over HTML output. See all2md.renderers.html.
Markdown to PDF
Basic Conversion
from all2md import convert
from all2md.renderers.pdf import PdfRendererOptions
# Simple conversion
convert('document.md', target_format='pdf', output='document.pdf')
# With options
options = PdfRendererOptions(
page_size='a4',
margin_top=72.0, # 1 inch (72 points)
margin_bottom=72.0,
font_name='Helvetica',
font_size=11,
include_page_numbers=True
)
convert('document.md', target_format='pdf', output='document.pdf', renderer_options=options)
PDF Styling and Layout
from all2md import to_ast, from_ast
from all2md.renderers.pdf import PdfRendererOptions
# Parse Markdown
doc_ast = to_ast('report.md')
# Configure PDF rendering
options = PdfRendererOptions(
page_size='letter',
margin_top=72.0, # 1 inch in points
margin_bottom=72.0,
margin_left=72.0,
margin_right=72.0,
font_name='Times-Roman',
font_size=12,
line_spacing=1.5,
include_page_numbers=True,
include_toc=False
)
# Render to PDF (returns bytes directly)
pdf_bytes = from_ast(doc_ast, target_format='pdf', renderer_options=options)
# Save
with open('formatted_report.pdf', 'wb') as f:
f.write(pdf_bytes)
Advanced Workflows
Round-Trip Conversion
Convert a document through multiple formats while preserving content:
from pathlib import Path
from all2md import convert, to_ast, from_ast
# Start with PDF → Markdown for editing
markdown = convert('original.pdf', target_format='markdown')
Path('editable.md').write_text(markdown, encoding='utf-8')
# Edit editable.md manually...
# Convert edited Markdown back to DOCX
docx_bytes = convert('editable.md', target_format='docx')
Path('final.docx').write_bytes(docx_bytes)
# Optional: inspect AST differences when needed
original_ast = to_ast('original.pdf')
edited_ast = to_ast('final.docx')
# ...run custom comparisons with from_ast/to_ast
Multi-Format Publishing
Generate multiple output formats from a single Markdown source:
from pathlib import Path
from all2md import to_ast, from_ast
def publish_multiformat(markdown_path: str, output_dir: str):
"""Publish a Markdown document to multiple formats."""
# Parse once
doc_ast = to_ast(markdown_path)
output_path = Path(output_dir)
output_path.mkdir(exist_ok=True)
stem = Path(markdown_path).stem
# Generate DOCX (returns bytes directly)
docx_bytes = from_ast(doc_ast, target_format='docx')
(output_path / f'{stem}.docx').write_bytes(docx_bytes)
# Generate HTML (returns str directly)
html_text = from_ast(doc_ast, target_format='html')
(output_path / f'{stem}.html').write_text(html_text, encoding='utf-8')
# Generate PDF (returns bytes directly)
pdf_bytes = from_ast(doc_ast, target_format='pdf')
(output_path / f'{stem}.pdf').write_bytes(pdf_bytes)
# Generate EPUB
from all2md.renderers.epub import EpubRendererOptions
epub_options = EpubRendererOptions(title=stem, generate_toc=True)
from_ast(doc_ast, target_format='epub', output=output_path / f'{stem}.epub', renderer_options=epub_options)
# Generate PPTX
from_ast(doc_ast, target_format='pptx', output=output_path / f'{stem}.pptx')
print(f"Published {stem} to DOCX, HTML, PDF, EPUB, and PPTX")
# Usage
publish_multiformat('article.md', './output')
Transform Before Rendering
Apply AST transforms before rendering to any format:
from all2md import convert, to_ast, from_ast
from all2md.transforms import (
HeadingOffsetTransform,
RemoveImagesTransform,
AddHeadingIdsTransform
)
transforms = [
HeadingOffsetTransform(offset=1), # H1 → H2, etc.
RemoveImagesTransform(), # Strip images
AddHeadingIdsTransform() # Add IDs for TOC
]
# Apply transforms inline with convert()
docx_bytes = convert('document.md', target_format='docx', transforms=transforms)
html = convert('document.md', target_format='html', transforms=transforms)
# Or manipulate the AST manually for custom workflows
doc_ast = to_ast('document.md')
for transform in transforms:
doc_ast = transform.transform(doc_ast)
docx_bytes = from_ast(doc_ast, target_format='docx')
html = from_ast(doc_ast, target_format='html')
Content Aggregation
Combine multiple Markdown files into a single output document:
from pathlib import Path
from all2md import to_ast, from_ast
from all2md.ast import Document
from all2md.transforms import HeadingOffsetTransform
def combine_markdown_files(md_files: list[str], output_format: str) -> bytes | str:
"""Combine multiple Markdown files into one document."""
combined_children = []
for md_file in md_files:
# Parse each file
doc_ast = to_ast(md_file)
# Offset headings to make them subsections
transformer = HeadingOffsetTransform(offset=1)
doc_ast = transformer.transform(doc_ast)
# Add to combined document
combined_children.extend(doc_ast.children)
# Create combined document
combined_doc = Document(children=combined_children)
# Render to desired format (returns bytes for binary, str for text)
result = from_ast(combined_doc, target_format=output_format)
return result
# Usage
chapters = ['chapter1.md', 'chapter2.md', 'chapter3.md']
book_pdf = combine_markdown_files(chapters, 'pdf')
with open('book.pdf', 'wb') as f:
f.write(book_pdf)
Supported Features by Format
Feature Comparison
Feature |
DOCX |
HTML |
EPUB |
PPTX |
ODT |
ODP |
RST |
AsciiDoc |
LaTeX |
MediaWiki |
Org-Mode |
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
Headings (H1-H6) |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
Bold/Italic/Strike |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
Lists (ordered/unordered) |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
Tables |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
Code blocks |
✓ |
✓ (highlighting) |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
Inline code |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
Links |
✓ |
✓ |
✓ |
✓ |
Partial |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
Images |
✓ |
✓ |
✓ |
✓ |
Partial |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
Block quotes |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
Horizontal rules |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
Footnotes |
✓ |
✓ |
✓ |
✓ |
✗ |
✓ |
✓ |
✓ |
✓ |
✓ |
Partial |
✓ |
Task lists |
✓ |
✓ |
Partial |
✓ |
✗ |
✓ |
✓ |
Partial |
Partial |
Partial |
Partial |
✓ |
Limitations
DOCX: - Nested tables may have layout issues - Some advanced Markdown extensions not supported - Image paths must be accessible at render time
HTML:
- No automatic styling (unless CSS provided)
- JavaScript not included
- Images embedded as <img> tags with original URLs
PDF: - Limited font embedding support - No interactive elements - Fixed page layout (not responsive) - Images must be accessible at render time
EPUB: - Images must be accessible at render time - Limited styling control (depends on ereader) - Chapter splitting required for logical structure
PPTX: - Images not yet fully supported - Links rendered as plain text - Limited layout customization without templates - Font availability depends on system
RST: - Some advanced Markdown extensions may not have RST equivalents - Directive syntax may differ from original
ODT: - Images must be accessible at render time - Complex formatting may require manual adjustment - Font availability depends on LibreOffice installation
ODP: - Images must be accessible at render time - Slide layouts are basic without custom templates - Font availability depends on LibreOffice installation
Best Practices
Use Relative Paths for Images
Ensure images can be found during rendering:
# Good: relative to current directory markdown = "" # Better: absolute paths or base64 embedding from all2md import to_ast doc_ast = to_ast(markdown) # Resolve paths during parsing
Validate AST Before Rendering
Check the AST structure before rendering:
from all2md import to_ast from all2md.ast import ValidationVisitor doc_ast = to_ast('document.md') # Validate validator = ValidationVisitor() doc_ast.accept(validator) # Raises if invalid
Handle Rendering Errors Gracefully
from all2md import convert from all2md.exceptions import RenderingError try: convert('document.md', target_format='pdf', output='output.pdf') except RenderingError as e: print(f"Rendering failed: {e}") print(f"Stage: {e.rendering_stage}")
Test Round-Trip Fidelity
For critical documents, test round-trip conversion:
from all2md import to_markdown, to_ast, from_ast # Original original_md = Path('document.md').read_text() # Round-trip ast1 = to_ast('document.md') docx_bytes = from_ast(ast1, target_format='docx') ast2 = to_ast(docx_bytes, source_format='docx') roundtrip_md = from_ast(ast2, target_format='markdown') # Compare (note: formatting may differ) assert len(original_md) > 0 assert len(roundtrip_md) > 0
For lossless
.docx→.docxround-trips (page setup, theme, and named paragraph styles preserved), usepreserve_formatting=True— see Round-Trip Editing with Template Preservation below.
Markdown to EPUB
Basic Conversion
from all2md import convert
from all2md.renderers.epub import EpubRendererOptions
# Simple conversion
convert('book.md', target_format='epub', output='book.epub')
# With metadata
options = EpubRendererOptions(
title="My Book",
author="John Doe",
language="en",
generate_toc=True
)
convert('book.md', target_format='epub', output='book.epub', renderer_options=options)
Chapter Splitting Strategies
EPUB documents are organized into chapters. all2md supports three strategies for splitting content into chapters:
from all2md import convert
from all2md.renderers.epub import EpubRendererOptions
# Strategy 1: Split by thematic breaks (---)
# Best for: Markdown with explicit separators between chapters
options = EpubRendererOptions(
chapter_split_mode="separator",
title="My Novel"
)
convert('novel.md', target_format='epub', output='novel.epub', renderer_options=options)
# Strategy 2: Split by heading level
# Best for: Structured documents with H1 or H2 chapter markers
options = EpubRendererOptions(
chapter_split_mode="heading",
chapter_split_heading_level=1, # Split on H1 headings
use_heading_as_chapter_title=True
)
convert('book.md', target_format='epub', output='book.epub', renderer_options=options)
# Strategy 3: Auto-detect (default)
# Prefers separators if present, falls back to headings
options = EpubRendererOptions(chapter_split_mode="auto")
convert('content.md', target_format='epub', output='content.epub', renderer_options=options)
Example with Multiple Chapters
from all2md import convert
from all2md.renderers.epub import EpubRendererOptions
# Markdown with explicit chapter separators
markdown_content = """
# Chapter 1: The Beginning
It was a dark and stormy night...
---
# Chapter 2: The Journey
The next morning, our hero set out on their quest...
---
# Chapter 3: The End
And they lived happily ever after.
"""
options = EpubRendererOptions(
title="My Short Story",
author="Jane Author",
chapter_split_mode="separator", # Split on ---
generate_toc=True
)
convert(markdown_content, target_format='epub', output='story.epub', renderer_options=options)
Advanced EPUB Options
from all2md import to_ast, from_ast
from all2md.renderers.epub import EpubRendererOptions
# Parse Markdown
doc_ast = to_ast('manuscript.md')
# Configure EPUB rendering
options = EpubRendererOptions(
title="Technical Manual",
author="Expert Team",
language="en",
identifier="urn:isbn:978-0-123456-78-9", # ISBN or unique ID
chapter_split_mode="heading",
chapter_split_heading_level=1,
chapter_title_template="Chapter {num}",
use_heading_as_chapter_title=True,
generate_toc=True
)
# Render to EPUB
from_ast(doc_ast, target_format='epub', output='manual.epub', renderer_options=options)
EPUB Features
The EPUB renderer supports:
Chapter Organization - Automatic chapter splitting with configurable strategies
Table of Contents - Auto-generated navigation (NCX and nav.xhtml)
Metadata - Title, author, language, ISBN, and Dublin Core fields
Rich Content - Tables, code blocks, lists, images, formatting
EPUB3 Standard - Modern EPUB3 format with proper structure
Markdown to PowerPoint (PPTX)
Basic Conversion
from all2md import convert
from all2md.renderers.pptx import PptxRendererOptions
# Simple conversion
convert('presentation.md', target_format='pptx', output='presentation.pptx')
# With custom fonts
options = PptxRendererOptions(
default_font="Arial",
default_font_size=20,
title_font_size=36
)
convert('slides.md', target_format='pptx', output='slides.pptx', renderer_options=options)
Slide Splitting Strategies
PowerPoint presentations are organized into slides. all2md supports three strategies for splitting content into slides:
from all2md import convert
from all2md.renderers.pptx import PptxRendererOptions
# Strategy 1: Split by thematic breaks (---)
# Best for: Markdown with explicit separators between slides
options = PptxRendererOptions(slide_split_mode="separator")
convert('deck.md', target_format='pptx', output='deck.pptx', renderer_options=options)
# Strategy 2: Split by heading level
# Best for: Structured presentations with H2 slide markers
options = PptxRendererOptions(
slide_split_mode="heading",
slide_split_heading_level=2, # Split on H2 headings (common pattern)
use_heading_as_slide_title=True
)
convert('presentation.md', target_format='pptx', output='presentation.pptx', renderer_options=options)
# Strategy 3: Auto-detect (default)
# Prefers separators if present, falls back to headings
options = PptxRendererOptions(slide_split_mode="auto")
convert('slides.md', target_format='pptx', output='slides.pptx', renderer_options=options)
Example Presentation
from all2md import convert
from all2md.renderers.pptx import PptxRendererOptions
# Markdown for a presentation
markdown_content = """
# Welcome to Our Product
A revolutionary new solution
---
## Key Features
- Fast performance
- Easy to use
- Secure by default
---
## Technical Specs
| Feature | Value |
|---------|-------|
| Speed | 10x faster |
| Memory | 50% less |
---
## Get Started Today
Visit our website for more information
"""
options = PptxRendererOptions(
slide_split_mode="separator", # Split on ---
use_heading_as_slide_title=True,
default_font_size=24,
title_font_size=44
)
convert(markdown_content, target_format='pptx', output='product.pptx', renderer_options=options)
Advanced PPTX Options
from all2md import to_ast, from_ast
from all2md.renderers.pptx import PptxRendererOptions
# Parse Markdown
doc_ast = to_ast('quarterly_review.md')
# Configure PPTX rendering
options = PptxRendererOptions(
slide_split_mode="heading",
slide_split_heading_level=2,
default_layout="Title and Content",
title_slide_layout="Title Slide",
use_heading_as_slide_title=True,
template_path="corporate_template.pptx", # Use custom template
default_font="Calibri",
default_font_size=18,
title_font_size=36
)
# Render to PPTX
from_ast(doc_ast, target_format='pptx', output='review.pptx', renderer_options=options)
PPTX Features
The PPTX renderer supports:
Slide Organization - Automatic slide splitting with configurable strategies
Text Formatting - Bold, italic, code, inline formatting
Lists - Bullet points and numbered lists
Tables - Data tables with headers
Code Blocks - Syntax highlighting with monospace font
Custom Layouts - Support for PowerPoint templates
Font Customization - Configure fonts and sizes
Markdown to reStructuredText (RST)
Basic Conversion
from all2md import convert
from all2md.renderers.rst import RestructuredTextRenderer, RstRendererOptions
# Simple conversion
convert('document.md', target_format='rst', output='document.rst')
# With options
options = RstRendererOptions(
heading_chars="=-~^*", # Customize heading underlines
table_style="grid", # Use grid tables
code_directive_style="directive" # Use .. code-block:: directives
)
convert('document.md', target_format='rst', output='document.rst', renderer_options=options)
Bidirectional RST Support
RST has full bidirectional support, enabling round-trip conversions:
from all2md.parsers.rst import RestructuredTextParser
from all2md.renderers.rst import RestructuredTextRenderer
from all2md.renderers.markdown import MarkdownRenderer
# RST → Markdown → RST round-trip
parser = RestructuredTextParser()
rst_renderer = RestructuredTextRenderer()
md_renderer = MarkdownRenderer()
# Parse RST to AST
doc = parser.parse('input.rst')
# Convert to Markdown
markdown = md_renderer.render_to_string(doc)
# Convert back to RST
output_rst = rst_renderer.render_to_string(doc)
Features Preserved
The RST renderer preserves:
Headings → RST section underlines with configurable characters
Inline Formatting → Emphasis (text), strong (text), literal (
text)Lists → Bullet and enumerated lists with nesting
Tables → Grid or simple table styles
Code Blocks → Literal blocks (::) or code-block directives with language
Definition Lists → Term/definition structures
Links → Inline and reference-style links
Images → .. image:: directives with alt text
Block Quotes → Indented blocks
Math → Inline (:math:) and block (.. math::) directives
Metadata → Docinfo blocks for author, date, etc.
Customizing RST Output
from all2md.renderers.rst import RestructuredTextRenderer, RstRendererOptions
# Custom heading characters (level 1-5)
options = RstRendererOptions(
heading_chars="#*+=", # Different underline chars
table_style="simple", # Simple tables instead of grid
code_directive_style="double_colon", # Use :: for code blocks
line_length=100 # Target line wrapping
)
renderer = RestructuredTextRenderer(options)
rst_output = renderer.render_to_string(doc_ast)
Round-Trip Example
Complete round-trip conversion example:
from all2md.parsers.rst import RestructuredTextParser
from all2md.renderers.rst import RestructuredTextRenderer, RstRendererOptions
from pathlib import Path
# Read original RST
original_rst = Path('documentation.rst').read_text()
# Parse to AST
parser = RestructuredTextParser()
doc = parser.parse(original_rst)
# Apply transforms if needed
# (e.g., modify headings, add content, etc.)
# Render back to RST with custom options
options = RstRendererOptions(
heading_chars="=-~^*",
table_style="grid"
)
renderer = RestructuredTextRenderer(options)
modified_rst = renderer.render_to_string(doc)
# Save output
Path('documentation_modified.rst').write_text(modified_rst)
Custom Formats with Jinja2 Templates
Note
Custom Templates with Jinja2 is the canonical reference for the Jinja2 renderer — the full template context, filters, helper functions, and feature flags live there. This section is a Python-API-focused summary; if anything here and the template guide ever disagree, the template guide is authoritative.
The Jinja2 template renderer allows you to create any text-based output format using templates, without writing Python code. This is perfect for custom formats like DocBook XML, YAML metadata extraction, ANSI terminal output, or proprietary markup languages.
Quick Start
from all2md import convert
from all2md.options.jinja import JinjaRendererOptions
# Convert Markdown to custom format using a template
options = JinjaRendererOptions(
template_file='templates/docbook.xml.jinja2',
escape_strategy='xml',
enable_escape_filters=True,
enable_traversal_helpers=True
)
convert('document.md', target_format='jinja', output='document.xml', renderer_options=options)
Template Context
Every Jinja2 template receives a rich context with access to:
Core Variables:
document- The Document AST node (Python object)ast- The document as a dictionary (template-friendly)metadata- Document metadata dictionarytitle- Document title (shorthand for metadata.title)
Pre-computed Collections (when enable_traversal_helpers=True):
headings- List of all heading nodes with level, text, and nodelinks- List of all links with url, title, text, and nodeimages- List of all images with url, alt_text, width, height, and nodefootnotes- List of all footnote definitions
Custom Context:
Add your own variables via extra_context option.
Built-in Filters
Templates have access to powerful filters:
Rendering Filters:
render- Render a node with default logic (markdown/html/plain)render_inline- Render inline content as textto_dict- Convert AST Node to dictionary
Escaping Filters:
escape_xml/escape_html- XML/HTML entity escapingescape_latex- LaTeX special character escapingescape_yaml- YAML string escapingescape_markdown- Markdown special character escaping
Utility Filters:
node_type- Get node type name as string
Helper Functions
When enable_traversal_helpers=True, templates can use:
get_headings(doc)- Extract all heading nodesget_links(doc)- Extract all link nodesget_images(doc)- Extract all image nodesget_footnotes(doc)- Extract footnote definitionsfind_nodes(doc, node_type)- Find all nodes of specified type
Example: DocBook XML Template
Create custom DocBook XML output:
<?xml version="1.0" encoding="UTF-8"?>
<article>
<title>{{ metadata.title|escape_xml }}</title>
{% if metadata.author %}
<articleinfo>
<author>
<firstname>{{ metadata.author|escape_xml }}</firstname>
</author>
</articleinfo>
{% endif %}
{% for node in document.children %}
{% if node|node_type == "Heading" %}
{% if node.level == 1 %}
<sect1>
<title>{{ node.content|map('render')|join('')|escape_xml }}</title>
{% elif node.level == 2 %}
<sect2>
<title>{{ node.content|map('render')|join('')|escape_xml }}</title>
{% endif %}
{% elif node|node_type == "Paragraph" %}
<para>{{ node.content|map('render')|join('')|escape_xml }}</para>
{% elif node|node_type == "CodeBlock" %}
<programlisting language="{{ node.language or 'text' }}">
{{- node.content|escape_xml -}}
</programlisting>
{% endif %}
{% endfor %}
</article>
Example: YAML Metadata Extraction
Extract document structure as YAML:
---
title: {{ metadata.title|escape_yaml }}
{% if metadata.author -%}
author: {{ metadata.author|escape_yaml }}
{% endif -%}
structure:
sections: {{ headings|length }}
links: {{ links|length }}
images: {{ images|length }}
headings:
{%- for h in headings %}
- level: {{ h.level }}
text: {{ h.text|escape_yaml }}
{%- endfor %}
{% if links -%}
links:
{%- for link in links %}
- url: {{ link.url|escape_yaml }}
text: {{ link.text|escape_yaml }}
{%- endfor %}
{% endif %}
Example: ANSI Terminal Output
Create colorful terminal output with box drawing:
{# Define ANSI color codes #}
{%- set BOLD = "\033[1m" -%}
{%- set CYAN = "\033[36m" -%}
{%- set RESET = "\033[0m" -%}
{{ BOLD }}{{ CYAN }}╔══════════════════════════════╗{{ RESET }}
{{ BOLD }}{{ CYAN }}║{{ RESET }} {{ metadata.title.center(28) }} {{ BOLD }}{{ CYAN }}║{{ RESET }}
{{ BOLD }}{{ CYAN }}╚══════════════════════════════╝{{ RESET }}
{% for node in document.children %}
{% if node|node_type == "Heading" %}
{{ BOLD }}{{ node.content|map('render')|join('') }}{{ RESET }}
{{ "=" * 30 }}
{% elif node|node_type == "Paragraph" %}
{{ node.content|map('render')|join('') }}
{% endif %}
{% endfor %}
Inline Template Strings
For simple templates, use inline template strings:
from all2md.options.jinja import JinjaRendererOptions
from all2md.renderers.jinja import JinjaRenderer
template = """
# {{ title }}
## Table of Contents
{%- for h in headings %}
{{ " " * (h.level - 1) }}- {{ h.text }}
{%- endfor %}
Statistics: {{ headings|length }} sections, {{ links|length }} links
"""
options = JinjaRendererOptions(
template_string=template,
enable_traversal_helpers=True
)
renderer = JinjaRenderer(options)
output = renderer.render_to_string(document)
Custom Escape Functions
Provide your own escape logic:
def my_custom_escape(text: str) -> str:
# Your custom escaping logic
return text.replace('&', '&').replace('<', '<')
options = JinjaRendererOptions(
template_file='template.jinja2',
escape_strategy='custom',
custom_escape_function=my_custom_escape
)
Extra Context Variables
Add custom variables to templates:
options = JinjaRendererOptions(
template_file='report.jinja2',
extra_context={
'version': '1.0.0',
'generated_by': 'all2md',
'timestamp': datetime.now().isoformat()
}
)
Then use in template:
Generated by {{ generated_by }} v{{ version }}
Timestamp: {{ timestamp }}
Example Templates Gallery
The examples/templates/jinja-templates/ directory contains production-ready templates:
docbook.xml.jinja2- DocBook XML for technical documentationmetadata.yaml.jinja2- YAML metadata and structure extractioncustom-outline.txt.jinja2- Human-readable document outlineansi-terminal.txt.jinja2- Colorful terminal output with Unicode box drawing
Full Documentation
For complete template reference, see Custom Templates with Jinja2.
Document Linting
The all2md.linter package exposes a rule-based linter that operates on
the AST. The public entry points mirror the all2md lint subcommand and
support both “give me a parsed document” and “give me a file path” workflows:
from all2md import to_ast
from all2md.linter import (
LintConfig,
LintRunner,
Severity,
lint_document,
lint_file,
rule_registry,
)
# Fastest path: hand in a file, get a LintResult back
result = lint_file("whitepaper.pdf")
# Or parse once and lint with a custom config
doc = to_ast("handbook.md")
config = LintConfig(
severity_threshold=Severity.WARNING,
disabled_rules=frozenset({"TYP003", "HDG004"}),
severity_overrides={"STR005": Severity.ERROR},
rule_options={"HDG002": {"max_length": 100}},
)
result = lint_document(doc, config=config, file_path="handbook.md")
for v in result.violations:
print(f"{v.rule_code} {v.severity.label}: {v.message}")
LintConfig is a frozen dataclass — use create_updated(...) to derive
modified copies in place of mutating. The LintRunner class wraps the same
pipeline when you need to lint many documents with a shared config:
runner = LintRunner(config=config)
results = runner.lint_files(["a.md", "b.md", "c.md"])
The rule_registry singleton lists the 47 built-in rules and accepts
third-party rules either by direct registration or via the
all2md.lint_rules entry-point group (see Plugin Development Guide). A custom rule
is any subclass of all2md.linter.LintRule that implements check(ctx)
and sets the class-level code, name, category, description,
and default_severity attributes.
See Also
Working with the AST - Complete AST documentation
Supported Formats - Supported input formats
Configuration Options - Document parsing/rendering options
AST Transforms and Hooks - Document transformation guide
Recipes and Cookbook - Real-world conversion patterns
API Reference
all2md.renderers - Renderers API
all2md.parsers - Parsers API
all2md.options - Parser and renderer Options
Linter - Document linter (rules, runner, reporters)