Repository avatar
AI Tools
v0.3.2
active

opendockit

io.github.hyeonsang010716/opendockit

Read local PDF, Office, HWP, OpenDocument, RTF, EPUB, HTML and email files as Markdown. Offline.

Documentation

OpenDocKit

Turn any document into clean, LLM-ready Markdown.
PDF · DOCX · PPTX · XLSX · HWP/HWPX · ODF · RTF · EPUB · HTML · email · images — one function, no GPU, MIT licensed.

PyPI Python CI License: MIT

English · 한국어 · 简体中文


A Korean HWP report, an Excel sheet and a PDF page turn into Markdown: merged cells are repeated per row, numbers appear as the sheet shows them, and a table without borders is still a table

from opendockit import convert_file

result = convert_file("quarterly-report.hwp")

result.text        # Markdown: headings, tables, lists, footnotes, LaTeX equations
result.sections    # per page / slide / sheet — for chunking and citations ("page 3")
result.warnings    # what was skipped or approximated, in plain sentences

Why OpenDocKit

Most converters handle the happy path. OpenDocKit is built for the files people actually upload.

  • Structure, not just text. Merged table cells, multi-column PDFs, borderless tables, nested lists, footnotes, speaker notes, native charts as data tables, SmartArt as nested lists, Word / PowerPoint / Hancom equations as LaTeX, links and bold / italic as Markdown.
  • First-class Korean documents. A from-scratch HWP 5.x parser, HWPX, CP949 detection, NFC normalization, and the form-heavy tables Korean offices produce. Most general-purpose converters cannot read these at all.
  • Detects by content, not extension. An HTML table saved as .xls, a CSV with a title block, an MHTML page named .doc — each is read as what it really is, and a warning tells you.
  • Safe on untrusted uploads. Zip bombs, runaway merged-cell counts, recursive PDF objects and multi-gigabyte images are bounded. IsolatedConverter runs every file in a disposable worker process with memory and time limits.
  • Images in place. Pictures, charts and scanned pages are extracted, filtered, and — if you plug in a vision model — described right where they appeared in the document.
  • A hard character budget. max_chars stops work instead of truncating afterwards, so a file that will be rejected anyway costs neither parsing time nor vision-model calls.
  • Permissive by default. MIT licensed; everything a plain pip install opendockit brings in is MIT / BSD / Apache-style (no PyMuPDF, no pyhwp), with no ML models and no GPU. The only copyleft code is behind the opt-in heic extra.

Installation

pip install opendockit                # conversion only — never touches the network
pip install "opendockit[openai]"      # + image description with a vision model
pip install "opendockit[heic]"        # + HEIC/HEIF images (pulls in GPL/LGPL-licensed codec libraries)
pip install "opendockit[langchain]"   # + LangChain loader
pip install "opendockit[llamaindex]"  # + LlamaIndex reader
pip install "opendockit[mcp]"         # + MCP server for agents

Requires Python 3.12 or newer on Linux or macOS. Windows is not supported yet for HTML, email and EPUB (why).

Optional: install LibreOffice (brew install --cask libreoffice, apt install libreoffice) to read legacy .doc and .ppt files. Every other format is read by OpenDocKit itself.

Quick start

Convert

from opendockit import convert, convert_file

result = convert_file("report.pdf")              # from a path
result = convert(data, "report.pdf")             # from bytes (the filename is only a hint)

print(result.text)
print(result.char_count, result.exceeded)        # size of the output, and whether the limit stopped it

convert is synchronous, offline and deterministic (the one exception: .doc and .ppt start a local LibreOffice process). Pictures are collected in result.images and the text keeps an [Image] placeholder where each one was.

Describe images with a vision model

from opendockit import OpenAIImageDescriber, aconvert

result = await aconvert(data, "report.pdf", describer=OpenAIImageDescriber())   # reads OPENAI_API_KEY

Charts become data tables, diagrams become lists, scanned pages are transcribed, and decorative images are dropped. OpenAIImageDescriber(model) accepts any LangChain chat model, and anything with an async describe(image) -> str method works as a describer. See docs/images.md.

Serve it

from opendockit import ConversionError, IsolatedConverter

async with IsolatedConverter(workers=2) as converter:      # 2 GB and 2 minutes per file by default
    try:
        result = await converter.convert(data, filename)
    except ConversionError as exc:
        print(exc.code, exc)                               # stable code + a message safe to show users

One bad file ends one worker; your server and the next upload carry on. In a script, run this under if __name__ == "__main__": — the workers import the main module again. See docs/server.md.

Command line

opendockit report.pdf                 # Markdown on stdout, warnings on stderr
opendockit report.pdf --json          # sections, metadata, warnings, images
opendockit report.pdf --count         # just the character count
opendockit scan.pdf --describe        # describe images with a vision model (uses OPENAI_API_KEY)

LangChain, LlamaIndex and MCP

from opendockit.integrations.langchain import OpenDocKitLoader
from opendockit.integrations.llama_index import OpenDocKitReader

docs = OpenDocKitLoader("report.pdf", mode="sections").load()       # one Document per page / slide / sheet
docs = OpenDocKitReader(mode="sections").load_data("report.pdf")
opendockit-mcp --root ~/Documents     # MCP server: Claude, Cursor and other agents read your documents as Markdown

To let the client start the server itself, with nothing installed beforehand, add it to the client's MCP settings:

{
  "mcpServers": {
    "opendockit": {
      "command": "uvx",
      "args": ["opendockit-mcp", "--root", "/path/to/documents"]
    }
  }
}

With mode="sections" each page, slide or sheet is its own Document, with its number in the metadata for citations. See docs/integrations.md.

Supported formats

FormatSectionsHighlights
PDFpagereading order across columns, ruled and borderless tables, form field values, headings from font size, repeated header/footer removal, scanned pages via vision model
DOCXdocumentheading styles, list numbering, merged cells, charts → tables, SmartArt, footnotes/endnotes, equations → LaTeX, comments
PPTXslideon-screen order, bullet levels, tables, charts → tables, SmartArt, equations, speaker notes
XLSX · XLS · Excel 2003 XML · CSVsheetseveral tables per sheet, merged cells, number and date formats, charts, cell comments
HWP · HWPXdocumentoutline headings, numbering, merged cells, captions, footnotes, equations → LaTeX
ODT · ODS · ODPdocument / sheet / slideread directly from the package: headings, lists, merged cells, charts, formulas → LaTeX, speaker notes
RTFdocumentbuilt-in parser: legacy code pages (CP949), tables with merged cells, lists, footnotes, pictures
EPUBdocumentchapters in reading order, pictures stored in the book, metadata
DOC · PPTdocument / slideconverted through LibreOffice when it is installed, then read like DOCX / PPTX
HTML · MHTMLdocumenttables with rowspan/colspan, layout tables unwrapped, MathML → LaTeX, hidden elements removed
Email (.eml, mbox)documentheaders, HTML or plain body, forwarded messages, calendar invites
TXT · Markdowndocumentencoding detection (UTF-8, CP949, UTF-16, Windows-1252)
Imagesdocument / pageJPEG, PNG, WebP, GIF, BMP, TIFF, AVIF, and HEIC with the heic extra — described by a vision model

Legacy .doc and .ppt need LibreOffice installed; everything else is read by OpenDocKit itself. The full list of what each format restores is in docs/formats.md; the honest list of what it cannot do is in docs/limitations.md.

How it compares

Structure checks passed: OpenDocKit 49 of 49, Docling 36, MarkItDown 31, Kreuzberg 29, Unstructured 12

We ran OpenDocKit, MarkItDown, Kreuzberg, Unstructured and Docling on the same files on a standard GitHub runner (method, raw results and how to rerun it):

  • Structure — OpenDocKit keeps the most: 49 of 49 checks such as "is the merged cell still attached to its row", against 36 for the next tool. The checks are ours, and 0.2.0 was written with them known; the report says what that means.
  • Formats — it reads 26 of the 31 formats tested without any model or service, the most of the five, and is the only one that reads HWP and HWPX.
  • Not the fastest, and no OCR — scans and images are read only through a vision model, where Docling and Unstructured read them offline. Kreuzberg is about thirty times faster, and legacy .doc / .ppt cost about a second each because they go through LibreOffice.

Examples

examples/ has one sample document per format and the Markdown OpenDocKit produces from each — with and without a vision model — so you can judge the output before installing anything.

Documentation

FormatsWhat is extracted from each format and how it is rendered
OptionsConvertOptions, exceptions and error codes
ImagesExtraction, filtering, vision models, custom describers
Running on a serverIsolatedConverter, resource limits, the character budget
IntegrationsLangChain, LlamaIndex and the MCP server
Integration guideWhat an upload UI should tell its users
DesignWhy it is built this way, and how it was verified
LimitationsKnown gaps, stated plainly
Decision recordsEvery deliberate trade-off, with the test that pins it

Contributing

Contributions are welcome — bug reports with a minimal reproducing document are especially valuable. Start with CONTRIBUTING.md.

git clone https://github.com/hyeonsang010716/OpenDocKit.git && cd OpenDocKit
uv sync
uv run pytest -n auto -m "not timing"   # the bulk of the suite, in parallel
uv run pytest -m timing                  # tests that measure time, one at a time
uv run ruff check

License

MIT

PYPI
opendockit-mcp
Install Command
pip install opendockit-mcp
Runtime: uvx