pdf-to-markdown · git:20260610.f3f3ec8 · 2026-06-10 · sha256 6c11b2c8b5ed6e7b
pdf-to-markdown git:20260610.f3f3ec8A
Immutable. This exact content never changes and is served at /api/v1/blob/6c11b2c8b5ed6e7b.
--- name: pdf-to-markdown description: > Convert documents (PDF, DOCX, PPTX, XLSX, EPUB, HTML, images) to Markdown using a tiered backend strategy: MinerU first, then marker-pdf, then a pure-python pypdf fallback. Trigger on: "convert PDF to markdown", "PDF to markdown", "DOCX/Word to markdown", "PPTX to markdown", "XLSX/Excel to markdown", "convert document to markdown", "transform to text", "mineru", "marker-pdf", "extract markdown from PDF", or any mention of a .pdf/.docx/.pptx/.xlsx/.epub/.html/image file when markdown output is the desired result. --- # Document to Markdown ## Overview Converts documents to high-quality Markdown using a **tiered backend strategy** — the best available tool wins, and the script degrades gracefully when a tool is missing or fails: | Tier | Backend | Command | When it runs | Strength | |---|---|---|---|---| | 1 | **MinerU** | `mineru` | Used first whenever on `PATH` | Highest-fidelity layout, tables, formulas, OCR | | 2 | **marker-pdf** | `marker_single` | Fallback if MinerU missing/fails | Strong layout + optional Gemini LLM cleanup | | 3 | **pure-python** | `pypdf` | Last resort | Plain text-layer extraction (PDF only, no OCR) | With the default `--backend auto`, the script tries tiers **1 → 2 → 3** in order, moving to the next one when a backend is **unavailable** (not installed) *or* **fails** (non-zero exit / no output). It prints which backend produced the result on `stderr` (`[done] backend=...`) and the output `.md` path on `stdout`. Force a single tier with `--backend mineru|marker|python`. ## Supported Input Formats | Category | Extensions | MinerU | marker | python | |---|---|:--:|:--:|:--:| | PDF | `.pdf` | ✅ | ✅ | ✅ | | Word | `.docx` | ✅ | ✅ | — | | PowerPoint | `.pptx` | ✅ | ✅ | — | | Excel | `.xlsx` | ✅ | ✅ | — | | E-book | `.epub` | ✅ | ✅ | — | | Web | `.html`, `.htm` | ✅ | ✅ | — | | Images | `.png`, `.jpg`, `.jpeg`, `.webp`, `.tif`, `.tiff`, `.bmp`, `.gif` | ✅ | ✅ | — | The pure-python tier only handles PDFs **with a text layer** (no OCR). A scanned PDF with no text layer fails the python tier and requires MinerU or marker. ## Quick Start ```bash python scripts/convert.py /path/to/document.pdf # auto: mineru -> marker -> python python scripts/convert.py /path/to/report.docx python scripts/convert.py /path/to/slides.pptx ``` The output `.md` path is printed on `stdout`; progress/backend logs go to `stderr`. ## Setup ### Tier 1 — MinerU (preferred) ```bash pip install -U "mineru[core]" ``` MinerU downloads its models on first run. CLI: `mineru -p <input> -o <output_dir>`. ### Tier 2 — marker-pdf (fallback) ```bash pip install marker-pdf ``` (Despite the name, `marker-pdf` also handles DOCX, PPTX, XLSX, EPUB, HTML, and images.) Optional LLM enhancement (marker tier only) — set a Gemini key and the script adds `--use_llm` automatically; **without a key it runs marker without LLM enhancement** so the fallback still works: ``` GEMINI_API_KEY=your_key_here GEMINI_MODEL=gemini-flash-latest # optional; default model ``` `.env` is searched from the current working directory upward to the filesystem root; `GEMINI_API_KEY` / `GEMINI_MODEL` may also be exported as environment variables. ### Tier 3 — pure-python (last resort) ```bash pip install pypdf ``` ## Usage ``` python scripts/convert.py <input_path> [options] Arguments: input_path Path to the input document Backend selection: --backend CHOICE auto (default) | mineru | marker | python Page range (backend-agnostic, 0-based inclusive): --start N First page (0-based) --end N Last page (0-based, inclusive) MinerU-specific: --method CHOICE auto (default) | txt | ocr --lang LANG OCR language hint, e.g. "ch", "en" --mineru-backend NAME MinerU -b backend, e.g. "pipeline", "vlm-auto-engine" marker-specific: --page-range RANGE marker page syntax, e.g. "0,5-10,20" --disable-ocr Use text layer only, skip OCR --disable-images Do not extract images --model MODEL Gemini model (default: gemini-flash-latest) ``` ### Examples Convert with automatic tiering (recommended): ```bash python scripts/convert.py ~/Documents/report.pdf ``` Force MinerU with Chinese OCR: ```bash python scripts/convert.py ~/Documents/report.pdf --backend mineru --lang ch ``` Force the marker fallback explicitly: ```bash python scripts/convert.py ~/Documents/report.pdf --backend marker ``` Convert only pages 1–5: ```bash python scripts/convert.py ~/Documents/report.pdf --start 0 --end 4 ``` Quick, dependency-light text dump from a born-digital PDF: ```bash python scripts/convert.py ~/Documents/report.pdf --backend python ``` ## Output Location Every backend writes into a subdirectory named after the input stem, inside the input file's parent directory. The script locates the produced `.md` by globbing that subtree (backends nest differently — MinerU uses `<stem>/<method>/<stem>.md`, marker uses `<stem>/<stem>.md`) and prints the resolved path: ``` <input_parent>/<stem>/.../<stem>.md ``` Example: `/home/user/docs/paper.pdf` → `/home/user/docs/paper/.../paper.md` ## Error Handling The script only exits non-zero when **all** eligible tiers are exhausted; it then lists why each tier was skipped. Common per-tier causes: | Message | Tier | Cause | Fix | |---|---|---|---| | `'mineru' not found on PATH` | mineru | Not installed | `pip install -U "mineru[core]"` | | `'marker_single' not found on PATH` | marker | Not installed | `pip install marker-pdf` | | `pypdf not installed` | python | Not installed | `pip install pypdf` | | `no extractable text (scanned PDF?...)` | python | Scanned PDF, no text layer | Use mineru/marker (OCR) | | `pure-python fallback only supports PDF` | python | Non-PDF input | Use mineru/marker | | `file not found` / `unsupported file type` | — | Bad path/extension | Check path / use a supported format | ## scripts/ - **`convert.py`** — Tiered conversion driver. Validates the input, loads `.env`, then attempts MinerU → marker-pdf → pure-python (or the single `--backend` chosen), falling back on missing/failed tiers. Locates the produced markdown by globbing the per-backend output subtree and prints its path. Self-contained (no third-party imports beyond the backend tools themselves).