Order
Text in reading order
Columns, sidebars and footnotes come out in the order a person reads them, never interleaved line by line.
PDFs, Office files, emails and images become clean Markdown for LLMs and RAG, on your own hardware, in 3.7 milliseconds a page.
Start with the CLI, the REST API, .NET or MCP.
The open benchmark for PDF-to-Markdown engines grades reading order, tables and headings against human ground truth. LM-Kit leads all 14 engines, on each one.
The mean of reading order, table and heading fidelity against human-made ground truth.
Whether the text comes out in the order a person reads it (normalized indel distance).
Whether each table is rebuilt cell for cell, structure and text (tree edit distance).
Whether the heading hierarchy survives, level for level.
Seconds per page, lower is better. Bars use a log scale.
The official opendataloader-bench evaluator and its 200-document dataset, unmodified; other engines as published on its leaderboard.
RAG, search and agents are only as good as the text they receive. The engine keeps what flat extraction loses, and tells you how far to trust each page.
Order
Columns, sidebars and footnotes come out in the order a person reads them, never interleaved line by line.
Tables
Merged cells kept as HTML spans; simple tables as Markdown pipes when you prefer them.
Headings
Heading levels follow the document's structure, and false headings, like chart legends, stay body text.
Formats
PDF, DOCX, PPTX, XLSX, EML, MBOX, HTML and images, each routed to a converter built for it.
Trust
Every page and document gets a calibrated score from 0 to 1, so review goes only where it is needed.
Pipeline
Page separators, optional front matter with provenance, normalized whitespace: drop it straight into RAG.
One setting picks the path. The text engine that tops the leaderboard needs no GPU; scans and complex layouts can go to a vision model.
TextExtraction
Reads the document's own text layer, with built-in OCR for scans and images. CPU only, milliseconds a page.
Hybrid
Clean pages take the text path; scanned or image-heavy pages go to a vision model. The default.
VlmOcr
Each page is rendered and transcribed by a vision-language model of your choice, for photos and hard layouts.
Need charts read as data, element boxes and JSON? The document parser returns typed, positioned elements.
Each page carries a certainty from 0 to 1, calibrated on text quality, OCR share, table and heading structure, and how much cleanup the page needed. Route by it.
LM-Kit One converts over a command line, REST and MCP. LM-Kit.NET runs the same engine inside your .NET application.
LM-Kit One
The server, for any language and agent
LM-Kit.NET
The SDK, inside your .NET application
# Every document in ./inbox, as Markdown in ./markdown: the benchmarked engine
lmkit run document-to-markdown --in ./inbox --out ./markdown --strategy text-extraction --enable-ocr true
import base64, requests doc = base64.b64encode(open("contract.pdf", "rb").read()).decode() r = requests.post( "http://localhost:5189/lmkit/v1/document-to-markdown", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={"input": doc, "strategy": "TextExtraction", "enable_ocr": True}, ) print(r.json())
{
"mcpServers": {
"lmkit": {
"url": "http://localhost:5189/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}
// Agents ingest a file, then call document_convert_to_markdown.
using LMKit.Document.Conversion; using LMKit.Extraction.Ocr; var converter = new DocumentToMarkdown(); var options = new DocumentToMarkdownOptions { Strategy = DocumentToMarkdownStrategy.TextExtraction, OcrEngine = new LMKitOcr() }; var result = await converter.ConvertAsync("report.pdf", options); Console.WriteLine(result.Markdown); Console.WriteLine($"certainty {result.Certainty:F2}");
var review = new List<int>(); converter.PageCompleted += (_, e) => { var page = e.PageResult; if (page != null && page.Certainty < 0.70) review.Add(page.PageNumber); // only these pages need a person }; var result = await converter.ConvertAsync("scanned-batch.pdf");
var options = new DocumentToMarkdownOptions { EmitFrontMatter = true, // provenance on every file PreferMarkdownTablesForNonNested = true }; foreach (var path in Directory.EnumerateFiles("corpus")) { var r = await converter.ConvertAsync(path, options); if (r.Certainty < 0.70) { reviewQueue.Add(path); continue; } rag.ImportText(r.Markdown, chunking, "corpus", path); }
At milliseconds a page on a CPU, a million-page archive is an overnight job on hardware you already own, with no per-page API charge and no document leaving it.
RAG
Markdown is the normalized input before chunking and embedding, for RAG over any document mix.
Document RAGEML and MBOX archives become one Markdown file per message, headers kept, quoted replies optional.
Email processingEdge
No GPU and no model for the text engine: convert on laptops, scanning stations and air-gapped servers.
Edge deploymentWorking console demos on GitHub, step-by-step how-to guides on the docs site, and the API reference for the classes used on this page.
Console demo: a folder of PDFs, DOCX and HTML to clean Markdown.
Open on GitHub → SamplePrerequisites, setup, code path and expected output.
Read on docs → How-to guideStrategies, per-format behavior, tables and layout-aware output.
Read the guide → API referenceEvery option, with its default.
Open the reference →Document to Markdown