PDF to Markdown
Convert PDFs to clean Markdown and RAG-ready JSONL chunks, locally:
- Reading order for one- and multi-column pages (column gutters are found before rows, so two columns are read column by column), rotated pages, and sideways text on a normal page (kept as its own paragraph after the page text).
- Headings from font size and weight, nested by section numbers (
3.2.1sits under3.2); lists (bullets and numbers); code blocks from monospaced text; links ([text](url)); footnotes ([^1]references with the notes at the end). - Tables: ruled tables, tables ruled only between columns or every few rows, and rule-bounded tables without vertical lines (booktabs style). Plain tables become Markdown tables; merged cells become HTML tables with
rowspan/colspan. - Clean text: repeated headers/footers, page numbers and running heads removed; line-end hyphenation repaired (
com-pilebecomescompile,English-to-Germankeeps its hyphen); ligatures (fi,fl, ...) expanded. - Images optionally extracted as PNGs (deduplicated) and linked from the Markdown in a ZIP.
- Page ranges (
1-5,9,12-). - Chunks for retrieval: JSONL with
id,source,text,tokens,pages,heading_path,start,end. Each chunk is an exact slicemarkdown[start:end]; chunks start at headings, never exceed the token limit, repeat aboutoverlaptokens when a section is split, and together cover all non-whitespace text.
Runs offline. Scanned PDFs without a text layer are reported clearly ("no text layer"); there is no OCR.
Install (no administrator)
irm https://raw.githubusercontent.com/chayprabs/localtools/main/pdf-to-markdown/install.ps1 | iex
curl -fsSL https://raw.githubusercontent.com/chayprabs/localtools/main/pdf-to-markdown/install.sh | bash
# Or, with Python 3.12+ on any OS:
pipx install localtools-pdf-to-markdown
Requires Windows 10/11, macOS 13+, or Linux (glibc).
Portable ZIPs: Windows, macOS and Linux releases.
Use
Run localtools-pdf-to-markdown and the UI opens at http://127.0.0.1:47104. Choose a PDF, optionally set pages, chunk size and overlap, tick "Extract images", and Convert. Preview the rendered Markdown, the raw Markdown or the chunks table, then download the .md, the ZIP with images, or the .jsonl.
localtools-pdf-to-markdown convert paper.pdf -o paper.md
localtools-pdf-to-markdown convert paper.pdf -o paper.zip # Markdown + images/page-N-K.png
localtools-pdf-to-markdown convert book.pdf --pages 23-30 > ch1.md
localtools-pdf-to-markdown chunks paper.pdf --max-tokens 512 --overlap 64 -o chunks.jsonl
Options: --pages, --images (with -o FILE.zip), --max-tokens 64-8192 (default 512), --overlap (0 to half of max, default 64). Exit code 0 on success, 2 on error (one line on stderr); warnings (e.g. pages without a text layer) go to stderr. Outputs are written to a temporary file and renamed, so a failure never leaves a partial file.
tokens is a conservative estimate without a tokenizer download: the larger of characters/4 and 4/3 per word or symbol. Measured against OpenAI's cl100k tokenizer on the test PDFs it averages 1.26-1.30x the real count (range 0.89-1.6x per chunk), so keep about 10% headroom below a hard model limit.
API: POST /api/convert (multipart: file; fields pages, max_tokens, overlap, images = 1/0, format). With format=markdown, jsonl or zip the response is that file. The default (json) returns the summary, the Markdown (first 2 million characters), the first 500 chunks and a job id; GET /api/file?job=<id>&name=document.md|chunks.jsonl|document.zip|images/page-N-K.png serves the full files of the last three jobs while the tool runs.
curl -F file=@paper.pdf -F format=markdown http://127.0.0.1:47104/api/convert
Limits
64 MiB and 1,000 converted pages per PDF (use a page range for longer documents), 200,000 characters and 100,000 drawing objects per page, 2,000 images (40 megapixels each, 400 in total), 20 million characters of Markdown, 120 seconds per conversion, two conversions at a time (more get HTTP 503). Each conversion runs in a separate worker process that is killed on timeout, when the browser cancels, or when the tool exits. Workers get a 2 GiB memory limit on Windows (job object) and Linux (address space); macOS does not allow that limit, so there the page and object caps bound memory. Temporary folders are deleted after each job; leftovers from a crash are swept at the next start.
Engine and why
PDFium (Chrome's PDF engine) through pypdfium2 5.14.0 (Apache-2.0/BSD-3-Clause) supplies characters with positions, font size and weight, link annotations, vector paths and images, and repairs broken PDFs. The layout analysis on top (XY-cut reading order, lines, paragraphs, headings, lists, footnotes, header/footer removal, chunking) is this tool's own code; ruled-table detection adapts pdfplumber's algorithm (MIT) onto PDFium's paths.
Alternatives considered: docling (MIT) has ML layout and table models, but pulls PyTorch and model weights (gigabytes, no macOS Intel builds for current PyTorch), far too heavy for a small offline tool. pdfplumber/pdfminer.six (MIT) were the first candidate; pdfminer's pure-Python parsing measured 0.3-1.7 s per page on these test PDFs against 0.02-0.04 s with PDFium, so PDFium does the parsing. PyMuPDF / pymupdf4llm are AGPL and are used only in the tests, as an independent reference.
Measured against PyMuPDF's text (share of its words we keep / reading-order similarity): ResNet (CVPR two-column) 0.993 / 0.910, TraceMonkey (ACM two-column) 0.997 / 0.969, Attention Is All You Need 0.989 / 0.937, IRS Form W-9 0.995 / 0.979, Think Python chapter 1 0.980 / 0.989. The 64 data rows of the IRS Pub 15-T weekly wage-bracket table match PyMuPDF's table cells exactly.
Not included
- Right-to-left scripts (Arabic, Hebrew) are read in logical order line by line; mixed left-to-right runs inside an RTL line (numbers, Latin words) may come out in the wrong order. Arabic presentation forms are expanded to their letters.
- OCR: scanned pages have no text layer and are reported as such.
- Equations: math is kept as the text the PDF contains (often imperfect), not LaTeX.
- Tables without any rules (aligned by whitespace only) stay as text lines; booktabs tables (horizontal rules only) are detected.
- Inline bold/italic are not marked (only headings use font weight).
- Password-protected PDFs: remove the password first.
- Text drawn inside figures (chart labels) is kept as paragraphs, and fonts without a Unicode map produce dropped characters (a warning names the pages).
Update / uninstall
localtools-pdf-to-markdown update / localtools-pdf-to-markdown uninstall. If you installed with pipx: pipx upgrade localtools-pdf-to-markdown / pipx uninstall localtools-pdf-to-markdown.
MIT license. Bundled components: see THIRD_PARTY_NOTICES.md.
Metadata
Release files for localtools-pdf-to-markdown 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| localtools_pdf_to_markdown-1.0.1.tar.gz | 150.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| localtools_pdf_to_markdown-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 306.9 kB
Release files / localtools_pdf_to_markdown-1.0.1.tar.gz
| Download URL | localtools_pdf_to_markdown-1.0.1.tar.gz |
|---|---|
| Size | 150.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
04261a9abed92a10b43fe1f802a38880e1e201ca86d6eeec6c82b46477e9a9ff
|
|
BLAKE2b-256 checksum How to use checksums |
4ba825338a9e4229f029b0b284c02611a54413173b81d84929e48290c057e9c7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Release files / localtools_pdf_to_markdown-1.0.1-py3-none-any.whl
| Download URL | localtools_pdf_to_markdown-1.0.1-py3-none-any.whl |
|---|---|
| Size | 156.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
45039a476a8a1401157e6064a2a8122303edf92cb31df0c3bd8b051f9d0e3406
|
|
BLAKE2b-256 checksum How to use checksums |
6535a99a20d852d7e6670c8fb48ba72224d60c9e87cc9ee258335318c05ee988
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Security for localtools-pdf-to-markdown 1.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.