anytomd
A pure Rust tool and library that converts various document formats into Markdown — designed for LLM consumption.
Why?
MarkItDown is a great Python library for converting documents to Markdown. But integrating Python into Rust applications means bundling a Python runtime (~50 MB), dealing with cross-platform compatibility issues, and managing dependency hell.
anytomd solves this with a single cargo add anytomd — zero external runtime, no C bindings, no subprocess calls. Just pure Rust.
Supported Formats
| Format | Extensions | Notes |
|---|---|---|
| DOCX | .docx |
Headings, tables, lists, bold/italic, hyperlinks, images |
| PPTX | .pptx |
Slides, tables, speaker notes, images |
| XLSX | .xlsx |
Multi-sheet, date/time handling, images |
| XLS | .xls |
Legacy Excel (via calamine) |
| HTML | .html, .htm |
Full DOM: headings, tables, lists, links, blockquotes, code blocks |
| CSV | .csv |
Converted to Markdown tables |
| Jupyter Notebook | .ipynb |
Markdown cells preserved, code cells in fenced blocks with language detection |
| JSON | .json |
Pretty-printed in fenced code blocks |
| XML | .xml |
Pretty-printed in fenced code blocks |
| Images | .png, .jpg, .gif, .webp, .bmp, .tiff, .svg, .heic, .avif |
Optional LLM-based alt text via ImageDescriber |
| Code | .py, .rs, .js, .ts, .c, .cpp, .go, .java, .rb, .swift, .sh, ... |
Fenced code blocks with language identifier |
| Plain Text | .txt, .md, .rst, .log, .toml, .yaml, .ini, etc. |
Passthrough with encoding detection (UTF-8, UTF-16, Windows-1252) |
Note on PDF: PDF conversion is intentionally out of scope. Gemini, ChatGPT, and Claude already provide native PDF support (with plan/model-specific limits), so anytomd focuses on formats that still benefit from dedicated Markdown conversion.
Format is auto-detected from magic bytes and file extension. ZIP-based formats (DOCX/PPTX/XLSX) are distinguished by inspecting internal archive structure.
Installation
Feature Flags
| Feature | Dependencies | Description |
|---|---|---|
| (default) | async-gemini |
Async API + AsyncGeminiDescriber — all async features enabled out of the box |
async |
futures-util |
Async API (convert_file_async, convert_bytes_async, AsyncImageDescriber trait) |
async-gemini |
async + reqwest |
AsyncGeminiDescriber for concurrent image descriptions via Gemini |
Async features are included by default. To opt out:
= { = "0.11", = false }
CLI
Install
Usage
# Convert a single file
# Convert multiple files (separated by <!-- source: path --> comments)
# Write output to a file
# Read from stdin (--format is required)
|
# Override format detection
# Strict mode: treat recoverable errors as hard errors
# Plain text output (Markdown formatting stripped)
# Plain text from stdin
|
# Auto image descriptions (just set GEMINI_API_KEY)
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Conversion failure |
| 2 | Invalid arguments |
Quick Start (Library)
use ;
// Convert a file (format auto-detected from extension and magic bytes)
let options = default;
let result = convert_file.unwrap;
println!;
// Convert raw bytes with an explicit format
let csv_data = b"Name,Age\nAlice,30\nBob,25";
let result = convert_bytes.unwrap;
println!;
Plain Text Output
Every conversion produces both Markdown and plain text output. The plain text is extracted directly from the source document — no post-processing or markdown stripping — so source characters like **kwargs or # comment are preserved exactly.
use ;
let result = convert_file.unwrap;
// Markdown output
println!;
// Plain text output (no headings, bold, tables, code fences, etc.)
println!;
Extracting Embedded Images
use ;
let options = ConversionOptions ;
let result = convert_file.unwrap;
for in &result.images
LLM-Based Image Descriptions
anytomd can generate alt text for images using any LLM backend via the ImageDescriber trait. A built-in Google Gemini implementation is included.
use Arc;
use ;
use GeminiDescriber;
// Option 1: Use the built-in Gemini describer
let describer = from_env // reads GEMINI_API_KEY
.unwrap
.with_model;
let options = ConversionOptions ;
let result = convert_file.unwrap;
// Images now have LLM-generated alt text: 
// Option 2: Implement your own describer for any backend
;
Async Image Descriptions
For documents with many images, the async API resolves all descriptions concurrently. Included by default since v0.11.0.
use Arc;
use ;
use AsyncGeminiDescriber;
async
The library has no tokio dependency — the caller provides the async runtime. Any runtime (tokio, async-std, etc.) works.
API
convert_file
/// Convert a file at the given path to Markdown.
/// Format is auto-detected from magic bytes and file extension.
convert_bytes
/// Convert raw bytes to Markdown with an explicit format extension.
convert_file_async
Included by default (requires the async feature if default features are disabled).
/// Convert a file at the given path to Markdown with async image description.
/// If an async_image_describer is set, all image descriptions are resolved concurrently.
pub async
convert_bytes_async
Included by default (requires the async feature if default features are disabled).
/// Convert raw bytes to Markdown with async image description.
pub async
ConversionOptions
| Field | Type | Default | Description |
|---|---|---|---|
extract_images |
bool |
false |
Extract embedded images into result.images |
max_total_image_bytes |
usize |
50 MB | Hard cap for total extracted image bytes |
max_input_bytes |
usize |
100 MB | Maximum input file size |
max_uncompressed_zip_bytes |
usize |
500 MB | ZIP bomb guard |
strict |
bool |
false |
Error on recoverable failures instead of warnings |
image_describer |
Option<Arc<dyn ImageDescriber>> |
None |
LLM backend for image alt text generation |
ConversionResult
Error Handling
Conversion is best-effort by default. If a single element fails to parse (e.g., a corrupted table), it is skipped and a warning is added to result.warnings. The rest of the document is still converted.
Set strict: true in ConversionOptions to turn recoverable failures into errors instead.
Warning codes: SkippedElement, UnsupportedFeature, ResourceLimitReached, MalformedSegment.
Development
Build and Test
&& &&
Docker
A Docker environment is available for reproducible Linux builds:
License
Apache-2.0