Skip to main content

this_file: README.md

vexy-pdf3md

Convert one PDF or an entire folder tree to Markdown. Choose a converter, let the package select one, or run several strategies side by side. Failed conversions try another eligible tool; results record every attempt and the actual winner.

Install

uv sync
uv run vexy-pdf3md --help
# Install this checkout as a CLI available outside the repository:
uv tool install .

Python 3.12+ is required. PyMuPDF provides a working text-layer backend immediately. Optional tools are discovered on PATH:

  • markit: install from upstream instructions. Preferred for fast text-layer conversion and multi-column layouts.
  • pdf22md: build/install pdf22md on macOS for PDFKit extraction and Apple Vision OCR.
  • Tesseract: brew install tesseract on macOS; install the corresponding package on other systems. Extra languages: brew install tesseract-lang.
  • Docling: uv sync --extra docling, then use uv run --extra docling .... The adapter uses Tesseract for OCR and may download layout/table models on first use. For an installed CLI, use uv tool install '.[docling]' from this checkout.

Convert

# book.pdf -> book.md, beside the PDF
uv run vexy-pdf3md --input book.pdf

# Explicit output file, with images in ./results/renamed/
uv run vexy-pdf3md --input book.pdf --output results/renamed.md --media

# Recursively map PDFs to Markdown, preserving relative subfolders
uv run vexy-pdf3md --input ./pdfs --output ./markdown --workers=4

# Omit --output to write Markdown alongside PDFs throughout the input tree
uv run vexy-pdf3md --input ./pdfs

# Run independent strategies concurrently
uv run vexy-pdf3md --input book.pdf --strategies=markit,pymupdf --media

# OCR, selected pages, chosen language, and full-page images (including vectors)
uv run vexy-pdf3md --input scan.pdf --strategies=tesseract \
  --pages=1-3,5 --language=eng+pol --media=pages --dpi=200

# Use Docling for layout/table reconstruction
uv run --extra docling vexy-pdf3md --input book.pdf --strategies=docling

pdf3md and python -m vexy_pdf3md expose the same Fire CLI. Single-file input accepts an explicit .md filename or an existing output directory. Directory input always treats --output as a directory, even if its name ends in .md.

Output contract

pdfs/part/book.PDF
  --output markdown
    -> markdown/part/book.md
  --output markdown --strategies=markit,pymupdf --media
    -> markdown/part/book.markit.md
       markdown/part/book.markit/page-0001-001.png
    -> markdown/part/book.pymupdf.md
       markdown/part/book.pymupdf/page-0001-001.png

Suffixes are added only when multiple strategies are requested. They identify the requested strategy, even if a fallback produces that output; JSON reports identify the actual backend. Use --fallback=False for strict backend comparisons.

--media / --media=images exports displayed raster-image regions as PNGs, appended under a Media heading in page order. --media=pages renders each complete selected page, including vector artwork. Media links are relative and URL-escaped. Original embedded image formats, editable vectors, and exact inline placement are not preserved. The media directory uses the complete Markdown stem and is created even if no images are found; .vexy-pdf3md marks it as generated.

Existing Markdown is skipped by default. --overwrite=True replaces it only after conversion passes quality checks. Existing media directories are replaceable only if they carry this package's ownership marker. Keep curated files outside generated media directories. Case-insensitive destination collisions are rejected before processing. Directory symlinks are not followed and PDF symlinks within trees are skipped; an explicit single-file symlink is resolved to its target.

Selection and fallback

  • Text-layer PDFs: markit → pymupdf → pdf22md → tesseract → docling.
  • Suspected scanned/mixed PDFs: pdf22md → tesseract → docling.
  • Only installed candidates participate in automatic selection. An explicitly named missing backend produces a failed attempt before fallback.
  • Native crashes, timeout, nonzero exit, empty output, replacement-character corruption, and substantial text loss trigger fallback. The default retention threshold is 50% of the PDF's alphanumeric text layer, before media is appended.
  • All selected pages are inspected. Detection and length checks are heuristics; they cannot prove complete or correct OCR. Use --ocr=always when automatic routing misses scanned content. --ocr=never deliberately permits text-layer-only extraction.

PyMuPDF's basic fallback preserves text blocks and infers larger-font headings. It does not reconstruct sophisticated tables. For difficult layouts, compare outputs or request Docling. Image-only documents still require usable OCR text: exporting page images alone does not count as successful Markdown conversion.

Options

Option Default Meaning
--strategies auto One tool or comma-separated independent variants
--workers 2 Maximum simultaneous isolated jobs, 1–64
--timeout 600 Seconds per inspection, backend attempt, or media export
--fallback True Try alternative eligible converters
--media False True/images, pages, or False/none
--overwrite False Replace Markdown and owned generated media
--pages all One-based ranges, e.g. 1-3,5, sorted and deduplicated
--password empty Password for encrypted PDFs; omitted from reports
--ocr auto auto, always, or never
--language eng Tesseract/Docling codes joined with +
--vision-languages en pdf22md Vision codes joined with commas
--dpi 150 OCR and media resolution, 72–600
--min-text-ratio 0.5 Minimum retained text-layer fraction, 0–1
--verbose False Backend diagnostics on stderr

Timeouts apply per operation, so fallback can extend total time. POSIX timeout and Ctrl-C terminate the worker process group, including converter subprocesses. On Windows only the direct worker is terminated. Separate invocations should not overwrite the same destinations concurrently.

Python API and exit codes

from vexy_pdf3md import convert

batch = convert("pdfs", "markdown", strategies="auto", media=True, workers=4)
for item in batch.results:
    print(item.input, item.output, item.status, item.strategy)
    for attempt in item.attempts:
        print(attempt.strategy, attempt.status, attempt.error)
assert batch.ok

The CLI prints a JSON batch report to stdout. Exit status 0 means every output succeeded or was skipped, 1 means at least one conversion failed, and 2 means invalid configuration/input arguments. A bad PDF does not stop other jobs. API argument and path errors raise; individual document failures are returned in BatchResult. No cloud OCR or AI service is invoked by this package.

Develop

./test.sh                                    # lint, types, 80% coverage gate, smoke
uv run --extra docling pytest -m optional    # real Docling + model integration
uvx hatch test                              # isolated package test environment
uv build                                    # Git-derived version, wheel and sdist

Publish

./publish.sh                         # commit changes, tag, push, build, uv publish
PUBLISH_SKIP_UPLOAD=1 ./publish.sh    # same Git release, skip only the PyPI upload

hatch-vcs derives versions from Git tags and generates the gitignored src/vexy_pdf3md/__version__.py during builds/installs. Rebuild or reinstall after changing tags to refresh editable-install versions; never commit the generated file.

publish.sh validates before and after uvx gitnextver, which stages and commits all nonignored changes, creates the next vX.Y.Z tag, and pushes configured remotes. Generated version files are cleaned; source changes are committed, not discarded. The script checks the clean tree, exact tag, and remote push, then builds fresh artifacts and compares wheel/sdist/generated versions to the tag before calling uv publish. Only the newly built wheel and sdist are uploaded; copies remain in dist/.

Configure uv publishing credentials, e.g. UV_PUBLISH_TOKEN, through your environment. Arguments to publish.sh are forwarded to uv publish. A clean tagged checkout can retry a failed upload. A clean untagged checkout is rejected because gitnextver requires changes to create a tag. Validation installs all extras but does not run the optional model-download integration by default. A failed push, test, build, or version check stops the upload.

See ANALYSIS.md for both source-project investigations and routing tradeoffs, DEPENDENCIES.md for dependency roles/licenses, and WORK.md for verification evidence. The package's own code is Apache-2.0; dependencies have their own licenses, including PyMuPDF's AGPL/commercial terms.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vexy_pdf3md-1.0.0.tar.gz (196.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vexy_pdf3md-1.0.0-py3-none-any.whl (24.4 kB view details)

Uploaded Python 3

File details

Details for the file vexy_pdf3md-1.0.0.tar.gz.

File metadata

  • Download URL: vexy_pdf3md-1.0.0.tar.gz
  • Upload date:
  • Size: 196.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for vexy_pdf3md-1.0.0.tar.gz
Algorithm Hash digest
SHA256 71654b42ffece6e3aae6f4b76a7dfe753cb340944458d33e557026ea9303c8ac
MD5 a7d2aac1aa301182b49b4c6bddeab2f3
BLAKE2b-256 4b33052e1d8bec379709adc55709eca810a294091dac66c1a650abe17149ced7

See more details on using hashes here.

File details

Details for the file vexy_pdf3md-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: vexy_pdf3md-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 24.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for vexy_pdf3md-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 095f503ce516cb7de51ef6ae8e27976a8320a3cab8efee50b573115ac05b3788
MD5 c15d8232032d7b55022e5e7f23c1528e
BLAKE2b-256 498e7cbf108ce0908b1e07f790affb153e211e8247d3bba87f4587ed9640049d

See more details on using hashes here.

Release history Release notifications | RSS feed

1.0.4

2 files

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

This release

1.0.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page