pdf-to-markdown-cli 0.5.2__tar.gz → 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. pdf_to_markdown_cli-1.0.0/CHANGELOG.md +133 -0
  2. pdf_to_markdown_cli-1.0.0/CONTRIBUTING.md +77 -0
  3. {pdf_to_markdown_cli-0.5.2 → pdf_to_markdown_cli-1.0.0}/LICENSE +0 -0
  4. pdf_to_markdown_cli-1.0.0/MANIFEST.in +7 -0
  5. pdf_to_markdown_cli-1.0.0/PKG-INFO +226 -0
  6. pdf_to_markdown_cli-1.0.0/README.md +182 -0
  7. pdf_to_markdown_cli-1.0.0/examples/alice_in_wonderland_sample.pdf +0 -0
  8. pdf_to_markdown_cli-1.0.0/examples/equations.pdf +0 -0
  9. {pdf_to_markdown_cli-0.5.2 → pdf_to_markdown_cli-1.0.0}/pyproject.toml +32 -14
  10. pdf_to_markdown_cli-1.0.0/src/docs_to_md/__init__.py +8 -0
  11. pdf_to_markdown_cli-1.0.0/src/docs_to_md/__main__.py +3 -0
  12. pdf_to_markdown_cli-1.0.0/src/docs_to_md/assemble.py +212 -0
  13. pdf_to_markdown_cli-1.0.0/src/docs_to_md/cli.py +297 -0
  14. pdf_to_markdown_cli-1.0.0/src/docs_to_md/client.py +198 -0
  15. pdf_to_markdown_cli-1.0.0/src/docs_to_md/config.py +81 -0
  16. pdf_to_markdown_cli-1.0.0/src/docs_to_md/console.py +107 -0
  17. pdf_to_markdown_cli-1.0.0/src/docs_to_md/discovery.py +173 -0
  18. pdf_to_markdown_cli-1.0.0/src/docs_to_md/errors.py +50 -0
  19. pdf_to_markdown_cli-1.0.0/src/docs_to_md/markdown.py +90 -0
  20. pdf_to_markdown_cli-1.0.0/src/docs_to_md/models.py +124 -0
  21. pdf_to_markdown_cli-1.0.0/src/docs_to_md/pdf.py +108 -0
  22. pdf_to_markdown_cli-1.0.0/src/docs_to_md/pipeline.py +295 -0
  23. pdf_to_markdown_cli-1.0.0/src/pdf_to_markdown_cli.egg-info/PKG-INFO +226 -0
  24. pdf_to_markdown_cli-1.0.0/src/pdf_to_markdown_cli.egg-info/SOURCES.txt +38 -0
  25. {pdf_to_markdown_cli-0.5.2 → pdf_to_markdown_cli-1.0.0}/src/pdf_to_markdown_cli.egg-info/dependency_links.txt +0 -0
  26. pdf_to_markdown_cli-1.0.0/src/pdf_to_markdown_cli.egg-info/entry_points.txt +2 -0
  27. pdf_to_markdown_cli-1.0.0/src/pdf_to_markdown_cli.egg-info/requires.txt +13 -0
  28. {pdf_to_markdown_cli-0.5.2 → pdf_to_markdown_cli-1.0.0}/src/pdf_to_markdown_cli.egg-info/top_level.txt +0 -0
  29. {pdf_to_markdown_cli-0.5.2/src/docs_to_md → pdf_to_markdown_cli-1.0.0/tests}/__init__.py +0 -0
  30. pdf_to_markdown_cli-1.0.0/tests/conftest.py +93 -0
  31. pdf_to_markdown_cli-1.0.0/tests/test_assemble.py +194 -0
  32. pdf_to_markdown_cli-1.0.0/tests/test_cli.py +261 -0
  33. pdf_to_markdown_cli-1.0.0/tests/test_client.py +214 -0
  34. pdf_to_markdown_cli-1.0.0/tests/test_console.py +85 -0
  35. pdf_to_markdown_cli-1.0.0/tests/test_discovery.py +159 -0
  36. pdf_to_markdown_cli-1.0.0/tests/test_markdown.py +53 -0
  37. pdf_to_markdown_cli-1.0.0/tests/test_models.py +66 -0
  38. pdf_to_markdown_cli-1.0.0/tests/test_pdf.py +76 -0
  39. pdf_to_markdown_cli-1.0.0/tests/test_pipeline.py +297 -0
  40. pdf_to_markdown_cli-0.5.2/CHANGELOG.md +0 -25
  41. pdf_to_markdown_cli-0.5.2/CONTRIBUTING.md +0 -38
  42. pdf_to_markdown_cli-0.5.2/MANIFEST.in +0 -13
  43. pdf_to_markdown_cli-0.5.2/PKG-INFO +0 -143
  44. pdf_to_markdown_cli-0.5.2/README.md +0 -95
  45. pdf_to_markdown_cli-0.5.2/src/docs_to_md/__main__.py +0 -5
  46. pdf_to_markdown_cli-0.5.2/src/docs_to_md/api/__init__.py +0 -0
  47. pdf_to_markdown_cli-0.5.2/src/docs_to_md/api/client.py +0 -221
  48. pdf_to_markdown_cli-0.5.2/src/docs_to_md/api/models.py +0 -112
  49. pdf_to_markdown_cli-0.5.2/src/docs_to_md/config/__init__.py +0 -0
  50. pdf_to_markdown_cli-0.5.2/src/docs_to_md/config/cli.py +0 -95
  51. pdf_to_markdown_cli-0.5.2/src/docs_to_md/config/settings.py +0 -99
  52. pdf_to_markdown_cli-0.5.2/src/docs_to_md/core/__init__.py +0 -0
  53. pdf_to_markdown_cli-0.5.2/src/docs_to_md/core/paths.py +0 -76
  54. pdf_to_markdown_cli-0.5.2/src/docs_to_md/core/processor.py +0 -346
  55. pdf_to_markdown_cli-0.5.2/src/docs_to_md/core/result_handler.py +0 -635
  56. pdf_to_markdown_cli-0.5.2/src/docs_to_md/main.py +0 -64
  57. pdf_to_markdown_cli-0.5.2/src/docs_to_md/storage/__init__.py +0 -0
  58. pdf_to_markdown_cli-0.5.2/src/docs_to_md/storage/cache.py +0 -123
  59. pdf_to_markdown_cli-0.5.2/src/docs_to_md/storage/models.py +0 -104
  60. pdf_to_markdown_cli-0.5.2/src/docs_to_md/utils/__init__.py +0 -0
  61. pdf_to_markdown_cli-0.5.2/src/docs_to_md/utils/exceptions.py +0 -33
  62. pdf_to_markdown_cli-0.5.2/src/docs_to_md/utils/file_utils.py +0 -220
  63. pdf_to_markdown_cli-0.5.2/src/docs_to_md/utils/logging.py +0 -151
  64. pdf_to_markdown_cli-0.5.2/src/docs_to_md/utils/pdf_splitter.py +0 -167
  65. pdf_to_markdown_cli-0.5.2/src/pdf_to_markdown_cli.egg-info/PKG-INFO +0 -143
  66. pdf_to_markdown_cli-0.5.2/src/pdf_to_markdown_cli.egg-info/SOURCES.txt +0 -38
  67. pdf_to_markdown_cli-0.5.2/src/pdf_to_markdown_cli.egg-info/entry_points.txt +0 -2
  68. pdf_to_markdown_cli-0.5.2/src/pdf_to_markdown_cli.egg-info/requires.txt +0 -18
  69. pdf_to_markdown_cli-0.5.2/tests/test_cli.py +0 -52
  70. pdf_to_markdown_cli-0.5.2/tests/test_cli_equations.py +0 -127
  71. pdf_to_markdown_cli-0.5.2/tests/test_paths.py +0 -25
  72. pdf_to_markdown_cli-0.5.2/tests/test_settings.py +0 -91
  73. pdf_to_markdown_cli-0.5.2/tests/test_utils.py +0 -118
  74. {pdf_to_markdown_cli-0.5.2 → pdf_to_markdown_cli-1.0.0}/setup.cfg +0 -0
@@ -0,0 +1,133 @@
1
+ # Changelog
2
+
3
+ <!-- markdownlint-disable MD024 -->
4
+
5
+ All notable changes to `pdf-to-markdown-cli` are documented here.
6
+
7
+ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html), and this file follows the spirit of [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
8
+
9
+ ## [Unreleased]
10
+
11
+ ## [1.0.0] - 2026-09-29
12
+
13
+ First stable release: a rewrite on Datalab's current Convert API with a scriptable, re-runnable CLI. See "Upgrading from 0.x" in the README.
14
+
15
+ ### Added
16
+
17
+ - `--mode {fast,balanced,accurate}`, `--page-range`, `--no-image-captions`, `--skip-cache`, and `--api-option KEY=VALUE` for any other Convert API field.
18
+ - Multiple inputs per command, e.g. `pdf-to-md a.pdf b.docx docs/`.
19
+ - `--dry-run` previews files, pages, and chunks without calling the API or needing a key.
20
+ - `--overwrite`: existing outputs are now skipped by default, so re-runs only convert what's new.
21
+ - `-j/--concurrency`: chunks and files convert in parallel (default 5).
22
+ - `--timeout`, `-q/--quiet`, `--keep-line-breaks`, and `--api-key`.
23
+ - The `DATALAB_API_KEY` environment variable, matching Datalab's official SDK. `MARKER_PDF_KEY` still works.
24
+ - Input types: `.xlsm`, `.xltx`, `.csv`, `.htm`, `.tif`.
25
+ - A per-file result line and an end-of-run summary with page count and cost. Converted paths are printed to stdout.
26
+ - Markdown paragraphs that were hard-wrapped are joined (disable with `--keep-line-breaks`).
27
+ - Markdown no longer repeats each generated image description as a paragraph under the image. The description stays in the alt text.
28
+
29
+ ### Changed
30
+
31
+ - Uses `POST /api/v1/convert`; the `/api/v1/marker` endpoint is deprecated upstream.
32
+ - Deterministic output names: `report.md` + `report_images/` instead of UUID-keyed names. Same-name inputs become `a.pdf.md` / `a.docx.md`, and other clashes get a numeric suffix. Names are compared case-insensitively, and an output never overwrites an input file.
33
+ - `-o/--output-dir` accepts relative paths and mirrors the input folder tree.
34
+ - `--llm` now means `--mode balanced` and `--max` means `--mode accurate`. `--strip`, `--force`, and `--langs` are ignored with a warning because the API removed them. `--pages`, `--noimg`, `-cs`, and `-mp` are still accepted as aliases of the new flag names.
35
+ - Exit codes: `0` success, `1` some files failed, `2` usage/auth error, `130` interrupted.
36
+ - Quieter default output. Timestamps and debug logs only appear with `-v`.
37
+ - Dependencies trimmed to `requests`, `pikepdf`, and `tqdm`.
38
+ - Flat package layout and a pytest suite with 97% branch coverage. CI runs ruff, tests on Python 3.10-3.14, and a packaging check.
39
+
40
+ ### Fixed
41
+
42
+ - `--html` and `--json` produced no output, because the response fields were misnamed.
43
+ - Failures (including an invalid API key) exited `0` and printed "Conversion completed successfully".
44
+ - `--max-pages` was applied to every chunk instead of the whole document.
45
+ - Retries never happened because the client swallowed exceptions. 429/5xx/network errors now retry with backoff and honor `Retry-After`.
46
+ - Long conversions were marked failed after about 5 minutes of polling.
47
+ - A 30-second request timeout broke uploads of large files.
48
+ - Re-running on a directory re-converted previously extracted images, and `--html` could overwrite `.html` inputs.
49
+ - Page separators, JSON block ids, and image names restarted in every chunk. Image links broke on filenames with spaces.
50
+ - Ctrl-C printed a traceback and left temporary files behind.
51
+
52
+ ### Removed
53
+
54
+ - The on-disk request cache (`~/.docs_to_md/`), which was never reused between runs.
55
+ - The copied documentation for the deprecated Marker endpoint. The docs now link to the live Datalab API reference.
56
+
57
+ ## [0.5.2] - 2026-05-13
58
+
59
+ ### Added
60
+
61
+ - Fallback handling when the default cache or temp directories are not writable.
62
+ - Test coverage for HTML output CLI configuration.
63
+ - Mocked test coverage for processing both bundled example PDFs.
64
+ - CI and dependency automation configuration in `.github/workflows/ci.yml` and `.github/dependabot.yml`.
65
+
66
+ ### Changed
67
+
68
+ - Expanded supported input extension and MIME mappings for documents, spreadsheets, presentations, web/ebook files, and images.
69
+ - Improved result polling with per-chunk exponential backoff and cleaner progress handling.
70
+ - Modernized packaging and project metadata in `pyproject.toml`.
71
+ - Refreshed public and contributor documentation.
72
+
73
+ ### Fixed
74
+
75
+ - Avoided false "unsupported file type" failures by adding MIME detection fallbacks.
76
+ - Fixed a cache retrieval edge case where empty payloads were treated as missing.
77
+ - Improved request/chunk state tracking for completion and failure handling.
78
+
79
+ ## [0.5.1] - 2025-09-29
80
+
81
+ ### Fixed
82
+
83
+ - Prevented creation of output image directories when no images were returned.
84
+ - Corrected generated filename and image path handling for output assets.
85
+
86
+ ## [0.5.0] - 2025-05-19
87
+
88
+ ### Added
89
+
90
+ - Unit test coverage for CLI processing paths.
91
+
92
+ ### Changed
93
+
94
+ - Updated project docs for Datalab Marker API usage and contributor workflow.
95
+ - Refactored processor internals into clearer chunking and submission helper flows.
96
+ - Switched test execution to `unittest`.
97
+
98
+ ### Fixed
99
+
100
+ - Fixed mutable default list behavior in request/chunk models.
101
+
102
+ ## [0.4.0] - 2025-04-21
103
+
104
+ ### Changed
105
+
106
+ - Polished post-refactor processing behavior and output handling for the package layout.
107
+
108
+ ### Fixed
109
+
110
+ - Improved image path deduplication and related output consistency.
111
+
112
+ ## [0.3.0] - 2025-04-19 (unpublished)
113
+
114
+ ### Changed
115
+
116
+ - Refactored processing, caching, and result handling internals.
117
+
118
+ ## [0.2.1] - 2025-04-10
119
+
120
+ ### Fixed
121
+
122
+ - Fixed packaging and distribution issues from the first PyPI release.
123
+
124
+ ## [0.2.0] - 2025-04-10
125
+
126
+ ### Added
127
+
128
+ - First public PyPI release of `pdf-to-markdown-cli`.
129
+ - Package and distribution scaffolding for pip installation and the `pdf-to-md` CLI entrypoint.
130
+
131
+ ### Changed
132
+
133
+ - Reorganized the project into a publishable Python package.
@@ -0,0 +1,77 @@
1
+ # Contributing
2
+
3
+ Thanks for helping improve `pdf-to-markdown-cli`. The most valuable contributions keep conversions reliable and predictable, and keep the CLI pleasant to script.
4
+
5
+ ## Development setup
6
+
7
+ ```bash
8
+ git clone https://github.com/SokolskyNikita/pdf-to-markdown-cli.git
9
+ cd pdf-to-markdown-cli
10
+ python -m venv .venv && source .venv/bin/activate
11
+ pip install -e ".[dev]"
12
+ ```
13
+
14
+ Run the checks CI runs:
15
+
16
+ ```bash
17
+ pytest --cov
18
+ ruff check .
19
+ ruff format --check .
20
+ ```
21
+
22
+ For manual testing against the live API, set a key and use the bundled samples. They are small, so they cost fractions of a cent:
23
+
24
+ ```bash
25
+ export DATALAB_API_KEY="your_api_key"
26
+ pdf-to-md examples/alice_in_wonderland_sample.pdf -o /tmp/out --chunk-size 1 --paginate
27
+ ```
28
+
29
+ ## Project layout
30
+
31
+ ```text
32
+ src/docs_to_md/
33
+ cli.py argument parsing, entry point, exit codes, run summary
34
+ config.py validated runtime configuration
35
+ discovery.py input discovery and deterministic output planning
36
+ pipeline.py splitting, concurrent submission, polling, per-file results
37
+ client.py Datalab Convert API client (retries, timeouts, error types)
38
+ models.py API request/response types and supported formats
39
+ pdf.py page-range parsing and PDF splitting (pikepdf)
40
+ assemble.py merging chunk outputs, image renaming, atomic writes
41
+ markdown.py Markdown line-break normalization
42
+ console.py terminal output, progress bar, logging setup
43
+ errors.py exception hierarchy
44
+ tests/ pytest suite; no test calls the live API
45
+ ```
46
+
47
+ ## Conventions
48
+
49
+ - Target Python 3.10+. Use type hints and `from __future__ import annotations`.
50
+ - Keep the stdout/stderr split: only converted file paths go to stdout.
51
+ - Raise exceptions from `errors.py`. Raise `FatalAPIError` only for problems that affect every request, such as a bad key or no credits.
52
+ - Keep CLI behavior backward-compatible unless a breaking change is intentional and documented in `CHANGELOG.md`.
53
+ - Never commit API keys, private documents, or build artifacts.
54
+
55
+ ## Tests
56
+
57
+ Add or update tests whenever you change CLI options, output naming, discovery rules, API request/response handling, chunk merging, or error handling. Use `tests/conftest.py`'s `FakeClient` for pipeline tests, and a fake session (see `tests/test_client.py`) for HTTP behavior.
58
+
59
+ ## Documentation
60
+
61
+ Update `README.md` for user-facing changes, `CHANGELOG.md` for anything release-visible, and `AGENTS.md` when the module map or conventions change.
62
+
63
+ `examples/` holds the sample PDFs used by the tests, next to their outputs from the current version. If output behavior changes, regenerate the outputs with `pdf-to-md examples --overwrite`. The Datalab API reference lives at <https://documentation.datalab.to/api-reference/>.
64
+
65
+ ## Releasing
66
+
67
+ 1. Bump `version` in `pyproject.toml` and move the `Unreleased` changelog notes under a `## [X.Y.Z] - YYYY-MM-DD` heading.
68
+ 2. Commit, push `main`, and wait for CI to pass.
69
+ 3. Tag and push: `git tag vX.Y.Z && git push origin vX.Y.Z`.
70
+
71
+ The [release workflow](.github/workflows/release.yml) checks that the tag matches the package version, runs the tests, builds the sdist and wheel, and creates the GitHub release with those files attached and the changelog section as notes. It then publishes to PyPI through [Trusted Publishing](https://docs.pypi.org/trusted-publishers/).
72
+
73
+ PyPI publishing needs a one-time setup. On pypi.org, go to the project's **Settings → Publishing** and add a GitHub publisher (`SokolskyNikita/pdf-to-markdown-cli`, workflow `release.yml`, environment `pypi`). Then set the repository variable `PYPI_PUBLISH=true`. Until then, upload manually with `twine upload dist/*`, using the files attached to the GitHub release.
74
+
75
+ ## Reporting issues
76
+
77
+ Open a GitHub issue with the command you ran, the input file type, what you expected, and the output of the same command with `-v`.
@@ -0,0 +1,7 @@
1
+ include README.md LICENSE CHANGELOG.md CONTRIBUTING.md
2
+
3
+ # The test suite, with the sample PDFs it uses, so the sdist is testable.
4
+ recursive-include tests *.py
5
+ include examples/alice_in_wonderland_sample.pdf examples/equations.pdf
6
+
7
+ global-exclude *.py[cod] __pycache__ .DS_Store
@@ -0,0 +1,226 @@
1
+ Metadata-Version: 2.4
2
+ Name: pdf-to-markdown-cli
3
+ Version: 1.0.0
4
+ Summary: Convert PDFs, Office documents, ebooks, and images to Markdown, HTML, or JSON with the Datalab API.
5
+ Author-email: Nikita Sokolsky <sokolx@gmail.com>
6
+ Maintainer-email: Nikita Sokolsky <sokolx@gmail.com>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/SokolskyNikita/pdf-to-markdown-cli
9
+ Project-URL: Repository, https://github.com/SokolskyNikita/pdf-to-markdown-cli
10
+ Project-URL: Documentation, https://github.com/SokolskyNikita/pdf-to-markdown-cli#readme
11
+ Project-URL: Issues, https://github.com/SokolskyNikita/pdf-to-markdown-cli/issues
12
+ Project-URL: Changelog, https://github.com/SokolskyNikita/pdf-to-markdown-cli/blob/main/CHANGELOG.md
13
+ Keywords: pdf,markdown,converter,cli,document,ocr,datalab,marker
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: End Users/Desktop
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Operating System :: OS Independent
24
+ Classifier: Environment :: Console
25
+ Classifier: Topic :: Office/Business
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Classifier: Topic :: Text Processing
28
+ Classifier: Topic :: Utilities
29
+ Requires-Python: >=3.10
30
+ Description-Content-Type: text/markdown
31
+ License-File: LICENSE
32
+ Requires-Dist: pikepdf>=8.0
33
+ Requires-Dist: requests>=2.28
34
+ Requires-Dist: tqdm>=4.60
35
+ Provides-Extra: test
36
+ Requires-Dist: pytest>=8.3.0; extra == "test"
37
+ Requires-Dist: pytest-cov>=5.0.0; extra == "test"
38
+ Provides-Extra: dev
39
+ Requires-Dist: pdf-to-markdown-cli[test]; extra == "dev"
40
+ Requires-Dist: build>=1.2.0; extra == "dev"
41
+ Requires-Dist: twine>=5.1.0; extra == "dev"
42
+ Requires-Dist: ruff>=0.11.0; extra == "dev"
43
+ Dynamic: license-file
44
+
45
+ # PDF to Markdown CLI
46
+
47
+ [![PyPI](https://img.shields.io/pypi/v/pdf-to-markdown-cli.svg)](https://pypi.org/project/pdf-to-markdown-cli/)
48
+ [![GitHub release](https://img.shields.io/github/v/release/SokolskyNikita/pdf-to-markdown-cli)](https://github.com/SokolskyNikita/pdf-to-markdown-cli/releases)
49
+ [![Python versions](https://img.shields.io/pypi/pyversions/pdf-to-markdown-cli.svg)](https://pypi.org/project/pdf-to-markdown-cli/)
50
+ [![CI](https://github.com/SokolskyNikita/pdf-to-markdown-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/SokolskyNikita/pdf-to-markdown-cli/actions/workflows/ci.yml)
51
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
52
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
53
+
54
+ `pdf-to-md` converts PDFs, Office documents, ebooks, and images to Markdown, HTML, or JSON using the [Datalab](https://www.datalab.to/) Convert API (the hosted version of [Marker](https://github.com/datalab-to/marker)).
55
+
56
+ It is built for large documents and whole folders:
57
+
58
+ - **Big PDFs, fast.** PDFs are split into page chunks that convert in parallel and are stitched back into one file, with page numbers and image links fixed up across chunks.
59
+ - **Safe to re-run.** Output names are deterministic (`report.pdf` → `report.md`) and existing outputs are skipped, so re-running a folder only converts what is new. You never pay twice by accident.
60
+ - **Scriptable.** Converted file paths go to stdout, progress to stderr, and exit codes tell you what happened.
61
+ - **Resilient.** Rate limits, server errors, and dropped connections are retried with backoff. A bad API key stops the run immediately instead of failing file by file.
62
+
63
+ ## Quick start
64
+
65
+ ```bash
66
+ pip install pdf-to-markdown-cli # or: pipx install pdf-to-markdown-cli
67
+ export DATALAB_API_KEY="your_api_key" # https://www.datalab.to/app/keys
68
+ pdf-to-md report.pdf
69
+ ```
70
+
71
+ ```text
72
+ ✓ report.pdf → report.md (48 pages, $0.14)
73
+ report.md
74
+ ```
75
+
76
+ ## Usage
77
+
78
+ ```bash
79
+ pdf-to-md INPUT [INPUT ...] [options]
80
+ ```
81
+
82
+ `INPUT` can be any mix of files and directories. Directories are searched recursively. Hidden files and image folders this tool generated are skipped.
83
+
84
+ ```bash
85
+ pdf-to-md report.pdf # → report.md + report_images/
86
+ pdf-to-md docs/ -o converted/ # mirror a folder tree into converted/
87
+ pdf-to-md *.docx --html # HTML output
88
+ pdf-to-md scan.pdf --mode accurate # highest quality: slower and more expensive
89
+ pdf-to-md book.pdf --page-range 0-9 # first ten pages (0-based, like the API)
90
+ pdf-to-md docs/ --dry-run # preview files, pages, and chunks; no API calls
91
+ pdf-to-md docs/ --overwrite # redo everything, replacing old outputs
92
+ ```
93
+
94
+ The package can also run as a module: `python -m docs_to_md report.pdf`.
95
+
96
+ Sample outputs live in [`examples/`](examples/): each PDF sits next to its converted `.md` file and images folder.
97
+
98
+ ## Options
99
+
100
+ ### Output
101
+
102
+ | Option | Description |
103
+ | --- | --- |
104
+ | `-f`, `--format {markdown,html,json}` | Output format. Default: `markdown`. `--html` and `--json` are shortcuts. |
105
+ | `-o`, `--output-dir DIR` | Write outputs here instead of next to each input. Subfolders of directory inputs are mirrored. |
106
+ | `--overwrite` | Replace existing outputs. Without it, inputs whose output already exists are skipped. |
107
+ | `--keep-line-breaks` | Keep hard-wrapped paragraph lines in Markdown. By default they are joined. |
108
+
109
+ ### Conversion
110
+
111
+ | Option | Description |
112
+ | --- | --- |
113
+ | `-m`, `--mode {fast,balanced,accurate}` | Quality versus speed and cost. When omitted, the API default (`fast`) is used. |
114
+ | `--paginate` | Insert page separators. Page numbers stay correct across chunks. |
115
+ | `--no-images` | Don't extract images. |
116
+ | `--no-image-captions` | Don't generate text descriptions of images. |
117
+ | `--skip-cache` | Ignore Datalab's server-side result cache. |
118
+ | `--api-option KEY=VALUE` | Pass any other [Convert API](https://documentation.datalab.to/api-reference/convert-document) field, e.g. `--api-option extras=extract_links`. Repeatable. |
119
+
120
+ ### Pages and chunking
121
+
122
+ | Option | Description |
123
+ | --- | --- |
124
+ | `--page-range RANGE` | 0-based pages to convert, e.g. `0,5-10`. |
125
+ | `--max-pages N` | Convert at most `N` pages of each document. |
126
+ | `--chunk-size N` | PDF pages per API request. Default: `25`. |
127
+ | `--no-chunk` | Send each PDF as a single request. |
128
+
129
+ For PDFs, page selection happens locally before splitting, so only the requested pages are uploaded and billed.
130
+
131
+ ### Run control
132
+
133
+ | Option | Description |
134
+ | --- | --- |
135
+ | `--api-key KEY` | API key. Prefer the `DATALAB_API_KEY` environment variable so the key stays out of shell history. |
136
+ | `-j`, `--concurrency N` | Requests in flight at once. Default: `5`, the free tier's concurrency limit. |
137
+ | `--timeout SECONDS` | Give up on a single request after this long. Default: `3600`. |
138
+ | `-n`, `--dry-run` | List what would be converted, with page and chunk counts. Makes no API calls and needs no key. |
139
+ | `-q`, `--quiet` / `-v`, `--verbose` | Show only errors, or show debug logs. |
140
+ | `--version` | Print the version. |
141
+
142
+ ## Output files
143
+
144
+ ```text
145
+ docs/report.pdf
146
+ docs/report.md # converted document
147
+ docs/report_images/ # extracted images, created only if there are any
148
+ ```
149
+
150
+ - Image links in the output point into the images folder and are URL-encoded, so names with spaces work.
151
+ - If two inputs share a name (`a.pdf` and `a.docx`), both keep their source extension: `a.pdf.md` and `a.docx.md`.
152
+ - Outputs are written atomically. An interrupted or failed conversion never leaves a half-written file behind.
153
+
154
+ ## Supported inputs
155
+
156
+ | Type | Extensions |
157
+ | --- | --- |
158
+ | PDF | `.pdf` |
159
+ | Word processing | `.doc`, `.docx`, `.odt` |
160
+ | Presentations | `.ppt`, `.pptx`, `.odp` |
161
+ | Spreadsheets | `.xls`, `.xlsx`, `.xlsm`, `.xltx`, `.ods`, `.csv` |
162
+ | Web and ebooks | `.html`, `.htm`, `.epub` |
163
+ | Images | `.png`, `.jpg`, `.jpeg`, `.webp`, `.gif`, `.tiff`, `.tif` |
164
+
165
+ Only PDFs are split into chunks. Other files are uploaded as-is. The API accepts uploads of up to 200 MB.
166
+
167
+ ## Scripting
168
+
169
+ Converted paths are printed to stdout, one per line. Everything else goes to stderr:
170
+
171
+ ```bash
172
+ pdf-to-md papers/ -q | xargs wc -w
173
+ ```
174
+
175
+ | Exit status | Meaning |
176
+ | --- | --- |
177
+ | `0` | Every file was converted or skipped. |
178
+ | `1` | At least one file failed. The others were still converted. |
179
+ | `2` | Usage or configuration error, including a rejected API key or no credits. |
180
+ | `130` | Interrupted with Ctrl-C. |
181
+
182
+ Set `NO_COLOR=1` to disable colored output.
183
+
184
+ ## Configuration
185
+
186
+ The API key is read from, in order: `--api-key`, `DATALAB_API_KEY`, and `MARKER_PDF_KEY` (the name used before 1.0). Get a key at [datalab.to/app/keys](https://www.datalab.to/app/keys).
187
+
188
+ The CLI keeps no state between runs. Temporary chunk files live in the system temp directory and are removed when the run ends.
189
+
190
+ ## Upgrading from 0.x
191
+
192
+ Version 1.0 moves to Datalab's current `/api/v1/convert` endpoint and fixes `--html` and `--json` output, which failed in 0.5.x. Behavior changes:
193
+
194
+ - **Output names are deterministic.** You now get `report.md` and `report_images/` instead of `report_<random>.md` and `images_<random>/`. Existing outputs are skipped unless you pass `--overwrite`.
195
+ - **`-o` accepts relative paths** and mirrors subfolders when converting a directory.
196
+ - **Quality flags follow the API.** `--llm` maps to `--mode balanced` and `--max` to `--mode accurate`. Datalab removed the options behind `--strip`, `--force`, and `--langs`, so those flags are accepted but ignored, with a warning.
197
+ - **Renamed flags:** `--pages` → `--paginate`, `--noimg` → `--no-images`, `-cs` → `--chunk-size`, `-mp` → `--max-pages`. The old spellings still work.
198
+ - **Exit codes are meaningful.** Failures now exit non-zero instead of reporting success.
199
+ - **No local cache.** `~/.docs_to_md/` is no longer used and can be deleted.
200
+
201
+ ## Troubleshooting
202
+
203
+ - **`No API key found`**: set `DATALAB_API_KEY`.
204
+ - **`Authentication failed`**: the key was rejected. Check it on the Datalab dashboard.
205
+ - **`Payment required`**: your Datalab account is out of credits.
206
+ - **`... exists (use --overwrite)`**: the output is already there. Pass `--overwrite` to redo it.
207
+ - **`timed out`**: a request took longer than `--timeout`. Raise it, or lower `--chunk-size`.
208
+ - **Frequent rate limiting on the free tier**: lower `-j/--concurrency`. The free tier allows 10 requests per minute.
209
+ - Run with `-v` to see every API call and retry.
210
+
211
+ ## Development
212
+
213
+ See [CONTRIBUTING.md](CONTRIBUTING.md). In short:
214
+
215
+ ```bash
216
+ git clone https://github.com/SokolskyNikita/pdf-to-markdown-cli.git
217
+ cd pdf-to-markdown-cli
218
+ python -m venv .venv && source .venv/bin/activate
219
+ pip install -e ".[dev]"
220
+ pytest --cov
221
+ ruff check . && ruff format --check .
222
+ ```
223
+
224
+ ## License
225
+
226
+ [MIT](LICENSE)
@@ -0,0 +1,182 @@
1
+ # PDF to Markdown CLI
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/pdf-to-markdown-cli.svg)](https://pypi.org/project/pdf-to-markdown-cli/)
4
+ [![GitHub release](https://img.shields.io/github/v/release/SokolskyNikita/pdf-to-markdown-cli)](https://github.com/SokolskyNikita/pdf-to-markdown-cli/releases)
5
+ [![Python versions](https://img.shields.io/pypi/pyversions/pdf-to-markdown-cli.svg)](https://pypi.org/project/pdf-to-markdown-cli/)
6
+ [![CI](https://github.com/SokolskyNikita/pdf-to-markdown-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/SokolskyNikita/pdf-to-markdown-cli/actions/workflows/ci.yml)
7
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
9
+
10
+ `pdf-to-md` converts PDFs, Office documents, ebooks, and images to Markdown, HTML, or JSON using the [Datalab](https://www.datalab.to/) Convert API (the hosted version of [Marker](https://github.com/datalab-to/marker)).
11
+
12
+ It is built for large documents and whole folders:
13
+
14
+ - **Big PDFs, fast.** PDFs are split into page chunks that convert in parallel and are stitched back into one file, with page numbers and image links fixed up across chunks.
15
+ - **Safe to re-run.** Output names are deterministic (`report.pdf` → `report.md`) and existing outputs are skipped, so re-running a folder only converts what is new. You never pay twice by accident.
16
+ - **Scriptable.** Converted file paths go to stdout, progress to stderr, and exit codes tell you what happened.
17
+ - **Resilient.** Rate limits, server errors, and dropped connections are retried with backoff. A bad API key stops the run immediately instead of failing file by file.
18
+
19
+ ## Quick start
20
+
21
+ ```bash
22
+ pip install pdf-to-markdown-cli # or: pipx install pdf-to-markdown-cli
23
+ export DATALAB_API_KEY="your_api_key" # https://www.datalab.to/app/keys
24
+ pdf-to-md report.pdf
25
+ ```
26
+
27
+ ```text
28
+ ✓ report.pdf → report.md (48 pages, $0.14)
29
+ report.md
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ```bash
35
+ pdf-to-md INPUT [INPUT ...] [options]
36
+ ```
37
+
38
+ `INPUT` can be any mix of files and directories. Directories are searched recursively. Hidden files and image folders this tool generated are skipped.
39
+
40
+ ```bash
41
+ pdf-to-md report.pdf # → report.md + report_images/
42
+ pdf-to-md docs/ -o converted/ # mirror a folder tree into converted/
43
+ pdf-to-md *.docx --html # HTML output
44
+ pdf-to-md scan.pdf --mode accurate # highest quality: slower and more expensive
45
+ pdf-to-md book.pdf --page-range 0-9 # first ten pages (0-based, like the API)
46
+ pdf-to-md docs/ --dry-run # preview files, pages, and chunks; no API calls
47
+ pdf-to-md docs/ --overwrite # redo everything, replacing old outputs
48
+ ```
49
+
50
+ The package can also run as a module: `python -m docs_to_md report.pdf`.
51
+
52
+ Sample outputs live in [`examples/`](examples/): each PDF sits next to its converted `.md` file and images folder.
53
+
54
+ ## Options
55
+
56
+ ### Output
57
+
58
+ | Option | Description |
59
+ | --- | --- |
60
+ | `-f`, `--format {markdown,html,json}` | Output format. Default: `markdown`. `--html` and `--json` are shortcuts. |
61
+ | `-o`, `--output-dir DIR` | Write outputs here instead of next to each input. Subfolders of directory inputs are mirrored. |
62
+ | `--overwrite` | Replace existing outputs. Without it, inputs whose output already exists are skipped. |
63
+ | `--keep-line-breaks` | Keep hard-wrapped paragraph lines in Markdown. By default they are joined. |
64
+
65
+ ### Conversion
66
+
67
+ | Option | Description |
68
+ | --- | --- |
69
+ | `-m`, `--mode {fast,balanced,accurate}` | Quality versus speed and cost. When omitted, the API default (`fast`) is used. |
70
+ | `--paginate` | Insert page separators. Page numbers stay correct across chunks. |
71
+ | `--no-images` | Don't extract images. |
72
+ | `--no-image-captions` | Don't generate text descriptions of images. |
73
+ | `--skip-cache` | Ignore Datalab's server-side result cache. |
74
+ | `--api-option KEY=VALUE` | Pass any other [Convert API](https://documentation.datalab.to/api-reference/convert-document) field, e.g. `--api-option extras=extract_links`. Repeatable. |
75
+
76
+ ### Pages and chunking
77
+
78
+ | Option | Description |
79
+ | --- | --- |
80
+ | `--page-range RANGE` | 0-based pages to convert, e.g. `0,5-10`. |
81
+ | `--max-pages N` | Convert at most `N` pages of each document. |
82
+ | `--chunk-size N` | PDF pages per API request. Default: `25`. |
83
+ | `--no-chunk` | Send each PDF as a single request. |
84
+
85
+ For PDFs, page selection happens locally before splitting, so only the requested pages are uploaded and billed.
86
+
87
+ ### Run control
88
+
89
+ | Option | Description |
90
+ | --- | --- |
91
+ | `--api-key KEY` | API key. Prefer the `DATALAB_API_KEY` environment variable so the key stays out of shell history. |
92
+ | `-j`, `--concurrency N` | Requests in flight at once. Default: `5`, the free tier's concurrency limit. |
93
+ | `--timeout SECONDS` | Give up on a single request after this long. Default: `3600`. |
94
+ | `-n`, `--dry-run` | List what would be converted, with page and chunk counts. Makes no API calls and needs no key. |
95
+ | `-q`, `--quiet` / `-v`, `--verbose` | Show only errors, or show debug logs. |
96
+ | `--version` | Print the version. |
97
+
98
+ ## Output files
99
+
100
+ ```text
101
+ docs/report.pdf
102
+ docs/report.md # converted document
103
+ docs/report_images/ # extracted images, created only if there are any
104
+ ```
105
+
106
+ - Image links in the output point into the images folder and are URL-encoded, so names with spaces work.
107
+ - If two inputs share a name (`a.pdf` and `a.docx`), both keep their source extension: `a.pdf.md` and `a.docx.md`.
108
+ - Outputs are written atomically. An interrupted or failed conversion never leaves a half-written file behind.
109
+
110
+ ## Supported inputs
111
+
112
+ | Type | Extensions |
113
+ | --- | --- |
114
+ | PDF | `.pdf` |
115
+ | Word processing | `.doc`, `.docx`, `.odt` |
116
+ | Presentations | `.ppt`, `.pptx`, `.odp` |
117
+ | Spreadsheets | `.xls`, `.xlsx`, `.xlsm`, `.xltx`, `.ods`, `.csv` |
118
+ | Web and ebooks | `.html`, `.htm`, `.epub` |
119
+ | Images | `.png`, `.jpg`, `.jpeg`, `.webp`, `.gif`, `.tiff`, `.tif` |
120
+
121
+ Only PDFs are split into chunks. Other files are uploaded as-is. The API accepts uploads of up to 200 MB.
122
+
123
+ ## Scripting
124
+
125
+ Converted paths are printed to stdout, one per line. Everything else goes to stderr:
126
+
127
+ ```bash
128
+ pdf-to-md papers/ -q | xargs wc -w
129
+ ```
130
+
131
+ | Exit status | Meaning |
132
+ | --- | --- |
133
+ | `0` | Every file was converted or skipped. |
134
+ | `1` | At least one file failed. The others were still converted. |
135
+ | `2` | Usage or configuration error, including a rejected API key or no credits. |
136
+ | `130` | Interrupted with Ctrl-C. |
137
+
138
+ Set `NO_COLOR=1` to disable colored output.
139
+
140
+ ## Configuration
141
+
142
+ The API key is read from, in order: `--api-key`, `DATALAB_API_KEY`, and `MARKER_PDF_KEY` (the name used before 1.0). Get a key at [datalab.to/app/keys](https://www.datalab.to/app/keys).
143
+
144
+ The CLI keeps no state between runs. Temporary chunk files live in the system temp directory and are removed when the run ends.
145
+
146
+ ## Upgrading from 0.x
147
+
148
+ Version 1.0 moves to Datalab's current `/api/v1/convert` endpoint and fixes `--html` and `--json` output, which failed in 0.5.x. Behavior changes:
149
+
150
+ - **Output names are deterministic.** You now get `report.md` and `report_images/` instead of `report_<random>.md` and `images_<random>/`. Existing outputs are skipped unless you pass `--overwrite`.
151
+ - **`-o` accepts relative paths** and mirrors subfolders when converting a directory.
152
+ - **Quality flags follow the API.** `--llm` maps to `--mode balanced` and `--max` to `--mode accurate`. Datalab removed the options behind `--strip`, `--force`, and `--langs`, so those flags are accepted but ignored, with a warning.
153
+ - **Renamed flags:** `--pages` → `--paginate`, `--noimg` → `--no-images`, `-cs` → `--chunk-size`, `-mp` → `--max-pages`. The old spellings still work.
154
+ - **Exit codes are meaningful.** Failures now exit non-zero instead of reporting success.
155
+ - **No local cache.** `~/.docs_to_md/` is no longer used and can be deleted.
156
+
157
+ ## Troubleshooting
158
+
159
+ - **`No API key found`**: set `DATALAB_API_KEY`.
160
+ - **`Authentication failed`**: the key was rejected. Check it on the Datalab dashboard.
161
+ - **`Payment required`**: your Datalab account is out of credits.
162
+ - **`... exists (use --overwrite)`**: the output is already there. Pass `--overwrite` to redo it.
163
+ - **`timed out`**: a request took longer than `--timeout`. Raise it, or lower `--chunk-size`.
164
+ - **Frequent rate limiting on the free tier**: lower `-j/--concurrency`. The free tier allows 10 requests per minute.
165
+ - Run with `-v` to see every API call and retry.
166
+
167
+ ## Development
168
+
169
+ See [CONTRIBUTING.md](CONTRIBUTING.md). In short:
170
+
171
+ ```bash
172
+ git clone https://github.com/SokolskyNikita/pdf-to-markdown-cli.git
173
+ cd pdf-to-markdown-cli
174
+ python -m venv .venv && source .venv/bin/activate
175
+ pip install -e ".[dev]"
176
+ pytest --cov
177
+ ruff check . && ruff format --check .
178
+ ```
179
+
180
+ ## License
181
+
182
+ [MIT](LICENSE)