Docmax 3.3.1__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 (175) hide show
  1. docmax-3.3.1/LICENSE +21 -0
  2. docmax-3.3.1/PKG-INFO +371 -0
  3. docmax-3.3.1/README.md +301 -0
  4. docmax-3.3.1/pyproject.toml +321 -0
  5. docmax-3.3.1/setup.cfg +4 -0
  6. docmax-3.3.1/src/Docmax.egg-info/PKG-INFO +371 -0
  7. docmax-3.3.1/src/Docmax.egg-info/SOURCES.txt +173 -0
  8. docmax-3.3.1/src/Docmax.egg-info/dependency_links.txt +1 -0
  9. docmax-3.3.1/src/Docmax.egg-info/entry_points.txt +2 -0
  10. docmax-3.3.1/src/Docmax.egg-info/requires.txt +52 -0
  11. docmax-3.3.1/src/Docmax.egg-info/top_level.txt +1 -0
  12. docmax-3.3.1/src/docmax/__init__.py +23 -0
  13. docmax-3.3.1/src/docmax/cli/__init__.py +8 -0
  14. docmax-3.3.1/src/docmax/cli/cloud.py +338 -0
  15. docmax-3.3.1/src/docmax/cli/commands.py +1286 -0
  16. docmax-3.3.1/src/docmax/cli/execution.py +310 -0
  17. docmax-3.3.1/src/docmax/cli/interactive.py +67 -0
  18. docmax-3.3.1/src/docmax/cli/json_output.py +151 -0
  19. docmax-3.3.1/src/docmax/cli/main.py +508 -0
  20. docmax-3.3.1/src/docmax/cli/mcp_group.py +438 -0
  21. docmax-3.3.1/src/docmax/cli/progress.py +114 -0
  22. docmax-3.3.1/src/docmax/cli/render.py +323 -0
  23. docmax-3.3.1/src/docmax/cli/workflows.py +409 -0
  24. docmax-3.3.1/src/docmax/cloud_client/__init__.py +35 -0
  25. docmax-3.3.1/src/docmax/cloud_client/client.py +414 -0
  26. docmax-3.3.1/src/docmax/cloud_client/config.py +136 -0
  27. docmax-3.3.1/src/docmax/cloud_client/errors.py +95 -0
  28. docmax-3.3.1/src/docmax/cloud_client/models.py +189 -0
  29. docmax-3.3.1/src/docmax/core/__init__.py +12 -0
  30. docmax-3.3.1/src/docmax/core/atomic.py +282 -0
  31. docmax-3.3.1/src/docmax/core/branding.py +53 -0
  32. docmax-3.3.1/src/docmax/core/cancellation.py +235 -0
  33. docmax-3.3.1/src/docmax/core/config.py +377 -0
  34. docmax-3.3.1/src/docmax/core/consent.py +278 -0
  35. docmax-3.3.1/src/docmax/core/errors.py +544 -0
  36. docmax-3.3.1/src/docmax/core/models.py +251 -0
  37. docmax-3.3.1/src/docmax/core/protocols.py +207 -0
  38. docmax-3.3.1/src/docmax/core/registry.py +374 -0
  39. docmax-3.3.1/src/docmax/core/router.py +383 -0
  40. docmax-3.3.1/src/docmax/core/ui_state.py +98 -0
  41. docmax-3.3.1/src/docmax/mcp/__init__.py +91 -0
  42. docmax-3.3.1/src/docmax/mcp/policy.py +134 -0
  43. docmax-3.3.1/src/docmax/mcp/schema.py +19 -0
  44. docmax-3.3.1/src/docmax/mcp/server.py +359 -0
  45. docmax-3.3.1/src/docmax/mcpschema/__init__.py +14 -0
  46. docmax-3.3.1/src/docmax/mcpschema/schema.py +179 -0
  47. docmax-3.3.1/src/docmax/pickers/__init__.py +61 -0
  48. docmax-3.3.1/src/docmax/pickers/_pages.py +370 -0
  49. docmax-3.3.1/src/docmax/pickers/box.py +106 -0
  50. docmax-3.3.1/src/docmax/pickers/order.py +95 -0
  51. docmax-3.3.1/src/docmax/pickers/server.py +281 -0
  52. docmax-3.3.1/src/docmax/py.typed +0 -0
  53. docmax-3.3.1/src/docmax/runners/__init__.py +49 -0
  54. docmax-3.3.1/src/docmax/runners/_progress.py +50 -0
  55. docmax-3.3.1/src/docmax/runners/batch.py +255 -0
  56. docmax-3.3.1/src/docmax/runners/pipeline.py +474 -0
  57. docmax-3.3.1/src/docmax/runners/watch.py +317 -0
  58. docmax-3.3.1/src/docmax/tools/__init__.py +15 -0
  59. docmax-3.3.1/src/docmax/tools/_archive.py +63 -0
  60. docmax-3.3.1/src/docmax/tools/_binaries.py +338 -0
  61. docmax-3.3.1/src/docmax/tools/_box.py +142 -0
  62. docmax-3.3.1/src/docmax/tools/_cloud.py +223 -0
  63. docmax-3.3.1/src/docmax/tools/_deskew.py +165 -0
  64. docmax-3.3.1/src/docmax/tools/_dpi.py +59 -0
  65. docmax-3.3.1/src/docmax/tools/_formats.py +395 -0
  66. docmax-3.3.1/src/docmax/tools/_install.py +379 -0
  67. docmax-3.3.1/src/docmax/tools/_pagespec.py +164 -0
  68. docmax-3.3.1/src/docmax/tools/_pdf.py +266 -0
  69. docmax-3.3.1/src/docmax/tools/_permissions.py +151 -0
  70. docmax-3.3.1/src/docmax/tools/_position.py +136 -0
  71. docmax-3.3.1/src/docmax/tools/compress/__init__.py +9 -0
  72. docmax-3.3.1/src/docmax/tools/compress/cloud.py +51 -0
  73. docmax-3.3.1/src/docmax/tools/compress/local.py +186 -0
  74. docmax-3.3.1/src/docmax/tools/compress/tool.py +44 -0
  75. docmax-3.3.1/src/docmax/tools/compress/validators.py +62 -0
  76. docmax-3.3.1/src/docmax/tools/compress_image/__init__.py +3 -0
  77. docmax-3.3.1/src/docmax/tools/compress_image/local.py +202 -0
  78. docmax-3.3.1/src/docmax/tools/compress_image/tool.py +35 -0
  79. docmax-3.3.1/src/docmax/tools/compress_image/validators.py +43 -0
  80. docmax-3.3.1/src/docmax/tools/convert/__init__.py +8 -0
  81. docmax-3.3.1/src/docmax/tools/convert/cloud.py +58 -0
  82. docmax-3.3.1/src/docmax/tools/convert/local.py +231 -0
  83. docmax-3.3.1/src/docmax/tools/convert/tool.py +63 -0
  84. docmax-3.3.1/src/docmax/tools/convert/validators.py +104 -0
  85. docmax-3.3.1/src/docmax/tools/convert_image/__init__.py +3 -0
  86. docmax-3.3.1/src/docmax/tools/convert_image/local.py +234 -0
  87. docmax-3.3.1/src/docmax/tools/convert_image/tool.py +109 -0
  88. docmax-3.3.1/src/docmax/tools/convert_image/validators.py +43 -0
  89. docmax-3.3.1/src/docmax/tools/crop/__init__.py +1 -0
  90. docmax-3.3.1/src/docmax/tools/crop/local.py +145 -0
  91. docmax-3.3.1/src/docmax/tools/crop/tool.py +51 -0
  92. docmax-3.3.1/src/docmax/tools/crop/validators.py +105 -0
  93. docmax-3.3.1/src/docmax/tools/from_images/__init__.py +8 -0
  94. docmax-3.3.1/src/docmax/tools/from_images/local.py +216 -0
  95. docmax-3.3.1/src/docmax/tools/from_images/tool.py +34 -0
  96. docmax-3.3.1/src/docmax/tools/from_images/validators.py +71 -0
  97. docmax-3.3.1/src/docmax/tools/get_info/__init__.py +3 -0
  98. docmax-3.3.1/src/docmax/tools/get_info/local.py +135 -0
  99. docmax-3.3.1/src/docmax/tools/get_info/tool.py +28 -0
  100. docmax-3.3.1/src/docmax/tools/merge/__init__.py +9 -0
  101. docmax-3.3.1/src/docmax/tools/merge/local.py +201 -0
  102. docmax-3.3.1/src/docmax/tools/merge/tool.py +35 -0
  103. docmax-3.3.1/src/docmax/tools/merge/validators.py +63 -0
  104. docmax-3.3.1/src/docmax/tools/metadata/__init__.py +3 -0
  105. docmax-3.3.1/src/docmax/tools/metadata/local.py +174 -0
  106. docmax-3.3.1/src/docmax/tools/metadata/tool.py +48 -0
  107. docmax-3.3.1/src/docmax/tools/metadata/validators.py +67 -0
  108. docmax-3.3.1/src/docmax/tools/ocr/__init__.py +15 -0
  109. docmax-3.3.1/src/docmax/tools/ocr/cloud.py +68 -0
  110. docmax-3.3.1/src/docmax/tools/ocr/local.py +518 -0
  111. docmax-3.3.1/src/docmax/tools/ocr/tool.py +49 -0
  112. docmax-3.3.1/src/docmax/tools/ocr/validators.py +139 -0
  113. docmax-3.3.1/src/docmax/tools/pages/__init__.py +3 -0
  114. docmax-3.3.1/src/docmax/tools/pages/local.py +130 -0
  115. docmax-3.3.1/src/docmax/tools/pages/tool.py +41 -0
  116. docmax-3.3.1/src/docmax/tools/pages/validators.py +67 -0
  117. docmax-3.3.1/src/docmax/tools/permissions/__init__.py +7 -0
  118. docmax-3.3.1/src/docmax/tools/permissions/local.py +151 -0
  119. docmax-3.3.1/src/docmax/tools/permissions/tool.py +35 -0
  120. docmax-3.3.1/src/docmax/tools/protect/__init__.py +7 -0
  121. docmax-3.3.1/src/docmax/tools/protect/local.py +284 -0
  122. docmax-3.3.1/src/docmax/tools/protect/tool.py +69 -0
  123. docmax-3.3.1/src/docmax/tools/protect/validators.py +94 -0
  124. docmax-3.3.1/src/docmax/tools/remove_bg/__init__.py +3 -0
  125. docmax-3.3.1/src/docmax/tools/remove_bg/local.py +202 -0
  126. docmax-3.3.1/src/docmax/tools/remove_bg/tool.py +40 -0
  127. docmax-3.3.1/src/docmax/tools/remove_bg/validators.py +46 -0
  128. docmax-3.3.1/src/docmax/tools/reorder/__init__.py +3 -0
  129. docmax-3.3.1/src/docmax/tools/reorder/local.py +130 -0
  130. docmax-3.3.1/src/docmax/tools/reorder/tool.py +36 -0
  131. docmax-3.3.1/src/docmax/tools/reorder/validators.py +67 -0
  132. docmax-3.3.1/src/docmax/tools/resize/__init__.py +3 -0
  133. docmax-3.3.1/src/docmax/tools/resize/local.py +393 -0
  134. docmax-3.3.1/src/docmax/tools/resize/tool.py +126 -0
  135. docmax-3.3.1/src/docmax/tools/resize/validators.py +43 -0
  136. docmax-3.3.1/src/docmax/tools/rotate/__init__.py +3 -0
  137. docmax-3.3.1/src/docmax/tools/rotate/local.py +130 -0
  138. docmax-3.3.1/src/docmax/tools/rotate/tool.py +41 -0
  139. docmax-3.3.1/src/docmax/tools/rotate/validators.py +67 -0
  140. docmax-3.3.1/src/docmax/tools/sanitize/__init__.py +7 -0
  141. docmax-3.3.1/src/docmax/tools/sanitize/local.py +136 -0
  142. docmax-3.3.1/src/docmax/tools/sanitize/tool.py +27 -0
  143. docmax-3.3.1/src/docmax/tools/sanitize/validators.py +67 -0
  144. docmax-3.3.1/src/docmax/tools/split/__init__.py +8 -0
  145. docmax-3.3.1/src/docmax/tools/split/local.py +168 -0
  146. docmax-3.3.1/src/docmax/tools/split/tool.py +43 -0
  147. docmax-3.3.1/src/docmax/tools/stamp/__init__.py +8 -0
  148. docmax-3.3.1/src/docmax/tools/stamp/local.py +195 -0
  149. docmax-3.3.1/src/docmax/tools/stamp/tool.py +51 -0
  150. docmax-3.3.1/src/docmax/tools/stamp/validators.py +67 -0
  151. docmax-3.3.1/src/docmax/tools/to_images/__init__.py +7 -0
  152. docmax-3.3.1/src/docmax/tools/to_images/cloud.py +74 -0
  153. docmax-3.3.1/src/docmax/tools/to_images/local.py +210 -0
  154. docmax-3.3.1/src/docmax/tools/to_images/tool.py +58 -0
  155. docmax-3.3.1/src/docmax/tools/to_images/validators.py +124 -0
  156. docmax-3.3.1/src/docmax/tools/unlock/__init__.py +6 -0
  157. docmax-3.3.1/src/docmax/tools/unlock/local.py +154 -0
  158. docmax-3.3.1/src/docmax/tools/unlock/tool.py +34 -0
  159. docmax-3.3.1/src/docmax/tools/unlock/validators.py +74 -0
  160. docmax-3.3.1/src/docmax/tools/watermark/__init__.py +8 -0
  161. docmax-3.3.1/src/docmax/tools/watermark/local.py +369 -0
  162. docmax-3.3.1/src/docmax/tools/watermark/tool.py +66 -0
  163. docmax-3.3.1/src/docmax/tools/watermark/validators.py +67 -0
  164. docmax-3.3.1/src/docmax/tools/watermark_image/__init__.py +3 -0
  165. docmax-3.3.1/src/docmax/tools/watermark_image/local.py +327 -0
  166. docmax-3.3.1/src/docmax/tools/watermark_image/tool.py +62 -0
  167. docmax-3.3.1/src/docmax/tools/watermark_image/validators.py +43 -0
  168. docmax-3.3.1/src/docmax/tui/__init__.py +96 -0
  169. docmax-3.3.1/src/docmax/tui/app.py +2577 -0
  170. docmax-3.3.1/src/docmax/tui/browser.py +347 -0
  171. docmax-3.3.1/src/docmax/tui/catalog.py +97 -0
  172. docmax-3.3.1/src/docmax/tui/content.py +74 -0
  173. docmax-3.3.1/src/docmax/tui/forms.py +318 -0
  174. docmax-3.3.1/src/docmax/tui/runner.py +199 -0
  175. docmax-3.3.1/src/docmax/tui/status.py +114 -0
docmax-3.3.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Punith Naidu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
docmax-3.3.1/PKG-INFO ADDED
@@ -0,0 +1,371 @@
1
+ Metadata-Version: 2.4
2
+ Name: Docmax
3
+ Version: 3.3.1
4
+ Summary: Terminal-native document toolkit. Local-first, dual-engine, no server required.
5
+ Author: Punith Naidu
6
+ Maintainer-email: Punith Naidu <punithmedaramitta@gmail.com>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/megabyte44/docmax
9
+ Project-URL: Repository, https://github.com/megabyte44/docmax
10
+ Project-URL: Issues, https://github.com/megabyte44/docmax/issues
11
+ Project-URL: Changelog, https://github.com/megabyte44/docmax/blob/main/CHANGELOG.md
12
+ Keywords: pdf,ocr,document,cli,tui,terminal,conversion,batch
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Office/Business
22
+ Classifier: Topic :: Utilities
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.11
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: typer>=0.12.0
28
+ Requires-Dist: rich>=13.7.0
29
+ Requires-Dist: pypdf>=4.2.0
30
+ Requires-Dist: platformdirs>=4.2.0
31
+ Requires-Dist: httpx>=0.27.0
32
+ Provides-Extra: ocr
33
+ Requires-Dist: opencv-python-headless>=4.9.0; extra == "ocr"
34
+ Requires-Dist: numpy>=1.26.0; extra == "ocr"
35
+ Provides-Extra: tables
36
+ Requires-Dist: pdfplumber>=0.11.0; extra == "tables"
37
+ Requires-Dist: pandas>=2.2.0; extra == "tables"
38
+ Requires-Dist: openpyxl>=3.1.0; extra == "tables"
39
+ Provides-Extra: images
40
+ Requires-Dist: Pillow>=10.3.0; extra == "images"
41
+ Requires-Dist: img2pdf>=0.5.0; extra == "images"
42
+ Provides-Extra: remove-bg
43
+ Requires-Dist: rembg[cpu]>=2.0.0; extra == "remove-bg"
44
+ Provides-Extra: tui
45
+ Requires-Dist: textual>=1.0.0; extra == "tui"
46
+ Provides-Extra: crypto
47
+ Requires-Dist: cryptography>=42.0; extra == "crypto"
48
+ Provides-Extra: server
49
+ Requires-Dist: Docmax[all]; extra == "server"
50
+ Requires-Dist: fastapi>=0.111.0; extra == "server"
51
+ Requires-Dist: uvicorn[standard]>=0.30.0; extra == "server"
52
+ Requires-Dist: python-multipart>=0.0.9; extra == "server"
53
+ Requires-Dist: mcp<3,>=2.1; extra == "server"
54
+ Provides-Extra: mcp
55
+ Requires-Dist: mcp<3,>=2.1; extra == "mcp"
56
+ Provides-Extra: all
57
+ Requires-Dist: Docmax[crypto,images,ocr,remove-bg,tables,tui]; extra == "all"
58
+ Provides-Extra: dev
59
+ Requires-Dist: pytest>=8.2.0; extra == "dev"
60
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
61
+ Requires-Dist: pytest-timeout>=2.3.0; extra == "dev"
62
+ Requires-Dist: hypothesis>=6.100.0; extra == "dev"
63
+ Requires-Dist: respx>=0.21.0; extra == "dev"
64
+ Requires-Dist: ruff>=0.5.0; extra == "dev"
65
+ Requires-Dist: mypy>=1.10.0; extra == "dev"
66
+ Requires-Dist: import-linter>=2.0; extra == "dev"
67
+ Requires-Dist: pre-commit>=3.7.0; extra == "dev"
68
+ Requires-Dist: mcp<3,>=2.1; extra == "dev"
69
+ Dynamic: license-file
70
+
71
+ # DocMax
72
+
73
+ **A document toolkit that lives in your terminal.** Merge, split, OCR, compress,
74
+ convert, redact — locally, privately, with no server to run and no browser tab
75
+ to open.
76
+
77
+ ```bash
78
+ pip install Docmax
79
+ docmax merge a.pdf b.pdf -o combined.pdf
80
+ ```
81
+
82
+ > **Status: early development (M0).** The architecture and safety mechanisms are
83
+ > in place; the tools are being rebuilt on top of them one at a time. For a
84
+ > working tool today, use [`docmax` 2.x](https://pypi.org/project/docmax/).
85
+ > See [the roadmap](#roadmap) for what lands when.
86
+
87
+ ---
88
+
89
+ ## Why another PDF tool
90
+
91
+ The good self-hosted options — Stirling PDF and friends — are excellent, and
92
+ they all assume a browser. That means Docker, a running server, a port, and no
93
+ sensible way to use them over SSH or from a script.
94
+
95
+ DocMax assumes a terminal instead.
96
+
97
+ | | DocMax | Self-hosted web tools |
98
+ |---|---|---|
99
+ | Install | `pip install Docmax` | Docker + a container |
100
+ | Interface | CLI and TUI | browser |
101
+ | Over SSH | works | needs port forwarding |
102
+ | Scripting | argv | HTTP against a running server |
103
+ | Your documents | stay on your machine | stay on your machine |
104
+
105
+ ## Two engines, one interface
106
+
107
+ Every operation can run two ways, and the choice is yours per tool:
108
+
109
+ - **Local** — offline and private. Needs the relevant dependencies installed.
110
+ - **Cloud** — no local install at all. For the handful of tools whose
111
+ dependencies are genuinely painful.
112
+
113
+ ```bash
114
+ docmax ocr scan.pdf # picks whichever is available
115
+ docmax ocr scan.pdf --engine local # force local
116
+ docmax ocr scan.pdf --engine cloud # skip installing Tesseract
117
+ ```
118
+
119
+ Cloud exists for exactly one reason — to let you use a tool without installing
120
+ its heavy dependencies. Only a handful of tools have it — **`compress` and
121
+ `convert` today** — because for a pure-Python operation like `merge`, uploading
122
+ your document would be slower, less private, and pointless. OCR's cloud engine
123
+ arrives with OCR itself, at M8.
124
+
125
+ ```bash
126
+ docmax cloud login # store an API key
127
+ docmax cloud status # endpoint, key, and what you have agreed to send
128
+ docmax compress big.pdf -o small.pdf --engine cloud
129
+ ```
130
+
131
+ **Nothing is ever uploaded without asking.** Consent is per-tool and remembered;
132
+ `offline = true` in your config disables cloud entirely regardless of flags; and
133
+ every upload tells you what it is sending before it sends it. The cloud endpoint
134
+ is configurable, so you can point DocMax at your own server instead.
135
+
136
+ ## Your files are safe
137
+
138
+ This is the part most tools get wrong, so it is worth being specific.
139
+
140
+ - **Atomic writes.** Output goes to a temp file, gets validated, and is only then
141
+ swapped into place. A crash or Ctrl-C mid-operation leaves your destination
142
+ either untouched or absent — never half-written.
143
+ - **Your input is never the output.** `docmax merge a.pdf b.pdf -o a.pdf` is
144
+ refused, not silently obeyed.
145
+ - **Nothing is overwritten by accident.** Existing files need `--force`.
146
+ - **No tracebacks.** Every anticipated failure gives you a plain message and the
147
+ next step to take.
148
+
149
+ These are enforced by tests that run on every commit across Linux, macOS, and
150
+ Windows — not by good intentions. See
151
+ [architecture.md](docs/architecture/overview.md#the-structural-guarantees).
152
+
153
+ ## Install
154
+
155
+ ```bash
156
+ pip install Docmax # the shell and the cloud client
157
+ pip install "Docmax[ocr]" # local OCR
158
+ pip install "Docmax[crypto]" # AES encryption for `protect`
159
+ pip install "Docmax[all]" # everything
160
+ ```
161
+
162
+ The base install is deliberately small. Heavy dependencies arrive only when you
163
+ first ask for a local engine that needs them.
164
+
165
+ Some local engines also need external programs. `compress` needs
166
+ **Ghostscript**; OCR and conversion will need Tesseract, Poppler and Pandoc.
167
+
168
+ `protect` defaults to AES-256, which needs the `crypto` extra. It says so and
169
+ names the install line rather than quietly falling back to RC4 — a tool called
170
+ `protect` should not hand you broken encryption without mentioning it.
171
+
172
+ `convert` needs **Pandoc**, and `to-images` needs **Poppler**.
173
+
174
+ ```bash
175
+ docmax formats # what every tool can read and write
176
+ ```
177
+
178
+ **`convert` does not handle PDF in either direction.** Pandoc has no PDF reader,
179
+ and writing PDF needs a LaTeX distribution DocMax does not install — so
180
+ `convert report.pdf --to docx` is refused with an explanation rather than a bad
181
+ answer. It converts between Markdown, HTML, Word, OpenDocument,
182
+ reStructuredText, LaTeX source, EPUB and plain text. To turn a PDF into images,
183
+ use `to-images`. See
184
+ [ADR 0011](docs/adr/0011-convert-is-pandoc-only.md).
185
+
186
+ ```bash
187
+ docmax doctor # what's installed, what's missing, and the command to fix it
188
+ ```
189
+
190
+ ## Many documents, several steps, or a folder that fills up
191
+
192
+ ```bash
193
+ docmax batch scans/*.pdf --output-dir out --tool ocr
194
+ docmax pipeline scan.pdf --pipeline clean.toml -o clean.pdf
195
+ docmax watch inbox --output-dir done --tool ocr
196
+ ```
197
+
198
+ A **pipeline** chains operations over one document. The stages live in a TOML
199
+ file, so a workflow is something you save and re-run rather than retype:
200
+
201
+ ```toml
202
+ name = "scan-cleanup"
203
+
204
+ [[stage]]
205
+ tool = "ocr"
206
+ params = { lang = "eng", dpi = 300 }
207
+
208
+ [[stage]]
209
+ tool = "compress"
210
+ params = { preset = "ebook" }
211
+ ```
212
+
213
+ **Only the last stage writes your file.** The intermediate documents live in one
214
+ temporary directory and are gone whether the run succeeded, failed or was
215
+ interrupted — so a failure at stage three leaves your destination exactly as it
216
+ was, and nothing is ever left lying beside your documents.
217
+
218
+ A **batch** runs one operation over many documents, naming each output after its
219
+ input. One corrupt file does not cost you the other hundred and ninety-nine: it
220
+ is reported and the rest carry on. Two things are refused before any work
221
+ starts, because neither can be undone afterwards — two inputs whose names would
222
+ collide in the output directory, and any output that would land on an input.
223
+
224
+ A **watch** processes documents as they arrive in a folder. A file is picked up
225
+ only once it has stopped changing, so a document still being copied in is left
226
+ alone until it is whole, and each one is handled exactly once.
227
+
228
+ **`--output-dir` may not be inside the folder you are watching.** v2's watcher
229
+ wrote its output beside its input, saw that output as new input, and fed on
230
+ itself. That is now refused rather than survived. See
231
+ [ADR 0026](docs/adr/0026-the-watcher-polls-and-never-watches-its-own-output.md).
232
+
233
+ **There is no `--resume` yet.** The roadmap says "resumable batch"; a resume
234
+ journal is a persistent file format that deserves deciding on its own, so it was
235
+ deferred rather than improvised. Re-running an interrupted batch repeats what
236
+ already succeeded, safely — the outputs exist, and DocMax refuses to overwrite
237
+ them without `--force`.
238
+
239
+ ## Drive it from an AI agent
240
+
241
+ ```bash
242
+ pip install "Docmax[mcp]"
243
+ docmax mcp --root ~/Documents
244
+ ```
245
+
246
+ Serves every tool over the Model Context Protocol on stdio, so an assistant can
247
+ merge, split, compress or OCR your documents — **on your machine, with nothing
248
+ uploaded**. Point your MCP client at it — automatically:
249
+
250
+ ```bash
251
+ docmax mcp connect
252
+ ```
253
+
254
+ Detects Claude Desktop, Claude Code and Cursor on this machine and merges a
255
+ `docmax` entry into each one's own config, leaving everything else in that file
256
+ untouched. `--dry-run` shows the plan first; `--remote` wires up the cloud
257
+ bridge instead, using whatever `docmax cloud login` already stored. If nothing
258
+ is detected (or you use something else), it prints the same snippet to paste in
259
+ by hand:
260
+
261
+ ```json
262
+ {
263
+ "mcpServers": {
264
+ "docmax": { "command": "docmax", "args": ["mcp", "--root", "/home/you/Documents"] }
265
+ }
266
+ }
267
+ ```
268
+
269
+ The tool list is generated from the same registry the CLI reads, so an agent sees
270
+ exactly what you can run, with the same parameters and the same validation.
271
+
272
+ **An agent is not a person, and it is not trusted like one.**
273
+
274
+ - **It can only touch `--root`.** Reads and writes outside it are refused before
275
+ anything runs — `..`, symlinks and lookalike directory names included. The
276
+ default is the directory you started the server in.
277
+ - **It cannot overwrite your files.** There is no `--force` to give it; an
278
+ existing destination is an error.
279
+ - **It cannot upload anything.** Cloud engines are off unless you pass
280
+ `--allow-cloud`, and even then only for tools *you* already agreed to with
281
+ `docmax cloud agree`. An agent cannot consent on your behalf, and a configured
282
+ `offline = true` cannot be overridden by a flag.
283
+ - **It gets no shell, no filesystem browsing, and no tracebacks.**
284
+
285
+ Cancelling a request cancels the underlying operation, and the atomic writes mean
286
+ a cancelled run leaves your destination exactly as it was. See
287
+ [docs/implementation/mcp.md](docs/implementation/mcp.md).
288
+
289
+ ## An interface for when you are not scripting
290
+
291
+ ```bash
292
+ pip install "Docmax[tui]"
293
+ docmax tui # or just `docmax`, at a terminal
294
+ ```
295
+
296
+ Every tool, the same router, the same engines — a second way in, not a second
297
+ implementation. Pick a tool, fill in the form, watch the progress, press
298
+ `ctrl+c` to stop. It is generated from the tool registry, so it always offers
299
+ exactly what the CLI does.
300
+
301
+ Two operations need a value a terminal cannot ask for — where to crop, and what
302
+ order pages go in. Those get a browser tab:
303
+
304
+ ```bash
305
+ docmax crop scan.pdf -o trimmed.pdf --box 36,36,540,720 # scriptable
306
+ docmax crop scan.pdf -o trimmed.pdf --interactive # drag a box instead
307
+
308
+ docmax reorder in.pdf -o out.pdf --order 3,1,2
309
+ docmax reorder in.pdf -o out.pdf --interactive
310
+ ```
311
+
312
+ **The picker returns the parameter and nothing else.** It never opens your
313
+ document for writing and has no route to an output file. The flag form is the
314
+ one that is tested, works over SSH, and is what `--interactive` fills in — so
315
+ nothing you can do in a browser is something you cannot do in a script.
316
+
317
+ `doctor` prints the install line for your platform — `apt install ghostscript`,
318
+ `brew install ghostscript`, or the winget package on Windows. It only reports;
319
+ nothing is installed for you.
320
+
321
+ ## Roadmap
322
+
323
+ | | | |
324
+ |---|---|---|
325
+ | **M0** | Foundation — architecture, CI, safety mechanisms | ✅ done |
326
+ | **M1** | Core engine + `merge` as the reference implementation | ✅ complete |
327
+ | **M2** | `split`, `rotate`, `reorder`, `pages`, `metadata`, `sanitize`, `get-info` | ✅ done |
328
+ | **M3** | `compress` + external-binary support in `doctor` | ✅ done |
329
+ | **M4** | `watermark`, `stamp`, `protect`, `unlock`, `permissions` | ✅ done |
330
+ | **M5** | `convert`, `to-images`, `from-images` | ✅ done |
331
+ | **M6** | Cloud engines, `--json` everywhere, published benchmarks | ✅ done |
332
+ | **M7** | Textual TUI + visual pickers for crop and reorder | ✅ done |
333
+ | **M8** | OCR, done properly | ✅ done |
334
+ | **M9** | Pipelines, batch, folder watch — `--resume` [deferred](#many-documents-several-steps-or-a-folder-that-fills-up) | ✅ |
335
+ | **M10** | Local MCP server — drive DocMax from an AI agent, nothing leaves your machine | ✅ |
336
+ | **M11** | Remote MCP — network-reachable tool server, for clients that can't spawn a local process | ✅ |
337
+
338
+ Benchmarks live in [`benchmarks/`](benchmarks/METHODOLOGY.md) with the method
339
+ written down. Run them with `python -m benchmarks`. No numbers appear in this
340
+ README until they are measured — and none have been yet.
341
+
342
+ ## Documentation
343
+
344
+ [**docs/**](docs/README.md) is the index. The short version:
345
+
346
+ - [architecture/overview.md](docs/architecture/overview.md) — how DocMax is put
347
+ together, and why
348
+ - [adr/](docs/adr/README.md) — the decisions, and what they cost
349
+ - [planning/current-status.md](docs/planning/current-status.md) — what is done,
350
+ what is next, what is missing
351
+
352
+ ## Contributing
353
+
354
+ ```bash
355
+ git clone https://github.com/megabyte44/docmax
356
+ cd docmax
357
+ python -m venv .venv && .venv/bin/pip install -e ".[dev]"
358
+ pre-commit install
359
+
360
+ pytest && ruff check . && mypy && lint-imports
361
+ ```
362
+
363
+ Start with [docs/architecture/overview.md](docs/architecture/overview.md) and the
364
+ [ADRs](docs/adr/) — they explain the constraints, most of which exist for a
365
+ specific reason.
366
+
367
+ ## Licence
368
+
369
+ MIT. Every document operation is free and always will be — see
370
+ [ADR 0004](docs/adr/0004-open-core-boundary.md) for where the open-core line
371
+ sits and why.
docmax-3.3.1/README.md ADDED
@@ -0,0 +1,301 @@
1
+ # DocMax
2
+
3
+ **A document toolkit that lives in your terminal.** Merge, split, OCR, compress,
4
+ convert, redact — locally, privately, with no server to run and no browser tab
5
+ to open.
6
+
7
+ ```bash
8
+ pip install Docmax
9
+ docmax merge a.pdf b.pdf -o combined.pdf
10
+ ```
11
+
12
+ > **Status: early development (M0).** The architecture and safety mechanisms are
13
+ > in place; the tools are being rebuilt on top of them one at a time. For a
14
+ > working tool today, use [`docmax` 2.x](https://pypi.org/project/docmax/).
15
+ > See [the roadmap](#roadmap) for what lands when.
16
+
17
+ ---
18
+
19
+ ## Why another PDF tool
20
+
21
+ The good self-hosted options — Stirling PDF and friends — are excellent, and
22
+ they all assume a browser. That means Docker, a running server, a port, and no
23
+ sensible way to use them over SSH or from a script.
24
+
25
+ DocMax assumes a terminal instead.
26
+
27
+ | | DocMax | Self-hosted web tools |
28
+ |---|---|---|
29
+ | Install | `pip install Docmax` | Docker + a container |
30
+ | Interface | CLI and TUI | browser |
31
+ | Over SSH | works | needs port forwarding |
32
+ | Scripting | argv | HTTP against a running server |
33
+ | Your documents | stay on your machine | stay on your machine |
34
+
35
+ ## Two engines, one interface
36
+
37
+ Every operation can run two ways, and the choice is yours per tool:
38
+
39
+ - **Local** — offline and private. Needs the relevant dependencies installed.
40
+ - **Cloud** — no local install at all. For the handful of tools whose
41
+ dependencies are genuinely painful.
42
+
43
+ ```bash
44
+ docmax ocr scan.pdf # picks whichever is available
45
+ docmax ocr scan.pdf --engine local # force local
46
+ docmax ocr scan.pdf --engine cloud # skip installing Tesseract
47
+ ```
48
+
49
+ Cloud exists for exactly one reason — to let you use a tool without installing
50
+ its heavy dependencies. Only a handful of tools have it — **`compress` and
51
+ `convert` today** — because for a pure-Python operation like `merge`, uploading
52
+ your document would be slower, less private, and pointless. OCR's cloud engine
53
+ arrives with OCR itself, at M8.
54
+
55
+ ```bash
56
+ docmax cloud login # store an API key
57
+ docmax cloud status # endpoint, key, and what you have agreed to send
58
+ docmax compress big.pdf -o small.pdf --engine cloud
59
+ ```
60
+
61
+ **Nothing is ever uploaded without asking.** Consent is per-tool and remembered;
62
+ `offline = true` in your config disables cloud entirely regardless of flags; and
63
+ every upload tells you what it is sending before it sends it. The cloud endpoint
64
+ is configurable, so you can point DocMax at your own server instead.
65
+
66
+ ## Your files are safe
67
+
68
+ This is the part most tools get wrong, so it is worth being specific.
69
+
70
+ - **Atomic writes.** Output goes to a temp file, gets validated, and is only then
71
+ swapped into place. A crash or Ctrl-C mid-operation leaves your destination
72
+ either untouched or absent — never half-written.
73
+ - **Your input is never the output.** `docmax merge a.pdf b.pdf -o a.pdf` is
74
+ refused, not silently obeyed.
75
+ - **Nothing is overwritten by accident.** Existing files need `--force`.
76
+ - **No tracebacks.** Every anticipated failure gives you a plain message and the
77
+ next step to take.
78
+
79
+ These are enforced by tests that run on every commit across Linux, macOS, and
80
+ Windows — not by good intentions. See
81
+ [architecture.md](docs/architecture/overview.md#the-structural-guarantees).
82
+
83
+ ## Install
84
+
85
+ ```bash
86
+ pip install Docmax # the shell and the cloud client
87
+ pip install "Docmax[ocr]" # local OCR
88
+ pip install "Docmax[crypto]" # AES encryption for `protect`
89
+ pip install "Docmax[all]" # everything
90
+ ```
91
+
92
+ The base install is deliberately small. Heavy dependencies arrive only when you
93
+ first ask for a local engine that needs them.
94
+
95
+ Some local engines also need external programs. `compress` needs
96
+ **Ghostscript**; OCR and conversion will need Tesseract, Poppler and Pandoc.
97
+
98
+ `protect` defaults to AES-256, which needs the `crypto` extra. It says so and
99
+ names the install line rather than quietly falling back to RC4 — a tool called
100
+ `protect` should not hand you broken encryption without mentioning it.
101
+
102
+ `convert` needs **Pandoc**, and `to-images` needs **Poppler**.
103
+
104
+ ```bash
105
+ docmax formats # what every tool can read and write
106
+ ```
107
+
108
+ **`convert` does not handle PDF in either direction.** Pandoc has no PDF reader,
109
+ and writing PDF needs a LaTeX distribution DocMax does not install — so
110
+ `convert report.pdf --to docx` is refused with an explanation rather than a bad
111
+ answer. It converts between Markdown, HTML, Word, OpenDocument,
112
+ reStructuredText, LaTeX source, EPUB and plain text. To turn a PDF into images,
113
+ use `to-images`. See
114
+ [ADR 0011](docs/adr/0011-convert-is-pandoc-only.md).
115
+
116
+ ```bash
117
+ docmax doctor # what's installed, what's missing, and the command to fix it
118
+ ```
119
+
120
+ ## Many documents, several steps, or a folder that fills up
121
+
122
+ ```bash
123
+ docmax batch scans/*.pdf --output-dir out --tool ocr
124
+ docmax pipeline scan.pdf --pipeline clean.toml -o clean.pdf
125
+ docmax watch inbox --output-dir done --tool ocr
126
+ ```
127
+
128
+ A **pipeline** chains operations over one document. The stages live in a TOML
129
+ file, so a workflow is something you save and re-run rather than retype:
130
+
131
+ ```toml
132
+ name = "scan-cleanup"
133
+
134
+ [[stage]]
135
+ tool = "ocr"
136
+ params = { lang = "eng", dpi = 300 }
137
+
138
+ [[stage]]
139
+ tool = "compress"
140
+ params = { preset = "ebook" }
141
+ ```
142
+
143
+ **Only the last stage writes your file.** The intermediate documents live in one
144
+ temporary directory and are gone whether the run succeeded, failed or was
145
+ interrupted — so a failure at stage three leaves your destination exactly as it
146
+ was, and nothing is ever left lying beside your documents.
147
+
148
+ A **batch** runs one operation over many documents, naming each output after its
149
+ input. One corrupt file does not cost you the other hundred and ninety-nine: it
150
+ is reported and the rest carry on. Two things are refused before any work
151
+ starts, because neither can be undone afterwards — two inputs whose names would
152
+ collide in the output directory, and any output that would land on an input.
153
+
154
+ A **watch** processes documents as they arrive in a folder. A file is picked up
155
+ only once it has stopped changing, so a document still being copied in is left
156
+ alone until it is whole, and each one is handled exactly once.
157
+
158
+ **`--output-dir` may not be inside the folder you are watching.** v2's watcher
159
+ wrote its output beside its input, saw that output as new input, and fed on
160
+ itself. That is now refused rather than survived. See
161
+ [ADR 0026](docs/adr/0026-the-watcher-polls-and-never-watches-its-own-output.md).
162
+
163
+ **There is no `--resume` yet.** The roadmap says "resumable batch"; a resume
164
+ journal is a persistent file format that deserves deciding on its own, so it was
165
+ deferred rather than improvised. Re-running an interrupted batch repeats what
166
+ already succeeded, safely — the outputs exist, and DocMax refuses to overwrite
167
+ them without `--force`.
168
+
169
+ ## Drive it from an AI agent
170
+
171
+ ```bash
172
+ pip install "Docmax[mcp]"
173
+ docmax mcp --root ~/Documents
174
+ ```
175
+
176
+ Serves every tool over the Model Context Protocol on stdio, so an assistant can
177
+ merge, split, compress or OCR your documents — **on your machine, with nothing
178
+ uploaded**. Point your MCP client at it — automatically:
179
+
180
+ ```bash
181
+ docmax mcp connect
182
+ ```
183
+
184
+ Detects Claude Desktop, Claude Code and Cursor on this machine and merges a
185
+ `docmax` entry into each one's own config, leaving everything else in that file
186
+ untouched. `--dry-run` shows the plan first; `--remote` wires up the cloud
187
+ bridge instead, using whatever `docmax cloud login` already stored. If nothing
188
+ is detected (or you use something else), it prints the same snippet to paste in
189
+ by hand:
190
+
191
+ ```json
192
+ {
193
+ "mcpServers": {
194
+ "docmax": { "command": "docmax", "args": ["mcp", "--root", "/home/you/Documents"] }
195
+ }
196
+ }
197
+ ```
198
+
199
+ The tool list is generated from the same registry the CLI reads, so an agent sees
200
+ exactly what you can run, with the same parameters and the same validation.
201
+
202
+ **An agent is not a person, and it is not trusted like one.**
203
+
204
+ - **It can only touch `--root`.** Reads and writes outside it are refused before
205
+ anything runs — `..`, symlinks and lookalike directory names included. The
206
+ default is the directory you started the server in.
207
+ - **It cannot overwrite your files.** There is no `--force` to give it; an
208
+ existing destination is an error.
209
+ - **It cannot upload anything.** Cloud engines are off unless you pass
210
+ `--allow-cloud`, and even then only for tools *you* already agreed to with
211
+ `docmax cloud agree`. An agent cannot consent on your behalf, and a configured
212
+ `offline = true` cannot be overridden by a flag.
213
+ - **It gets no shell, no filesystem browsing, and no tracebacks.**
214
+
215
+ Cancelling a request cancels the underlying operation, and the atomic writes mean
216
+ a cancelled run leaves your destination exactly as it was. See
217
+ [docs/implementation/mcp.md](docs/implementation/mcp.md).
218
+
219
+ ## An interface for when you are not scripting
220
+
221
+ ```bash
222
+ pip install "Docmax[tui]"
223
+ docmax tui # or just `docmax`, at a terminal
224
+ ```
225
+
226
+ Every tool, the same router, the same engines — a second way in, not a second
227
+ implementation. Pick a tool, fill in the form, watch the progress, press
228
+ `ctrl+c` to stop. It is generated from the tool registry, so it always offers
229
+ exactly what the CLI does.
230
+
231
+ Two operations need a value a terminal cannot ask for — where to crop, and what
232
+ order pages go in. Those get a browser tab:
233
+
234
+ ```bash
235
+ docmax crop scan.pdf -o trimmed.pdf --box 36,36,540,720 # scriptable
236
+ docmax crop scan.pdf -o trimmed.pdf --interactive # drag a box instead
237
+
238
+ docmax reorder in.pdf -o out.pdf --order 3,1,2
239
+ docmax reorder in.pdf -o out.pdf --interactive
240
+ ```
241
+
242
+ **The picker returns the parameter and nothing else.** It never opens your
243
+ document for writing and has no route to an output file. The flag form is the
244
+ one that is tested, works over SSH, and is what `--interactive` fills in — so
245
+ nothing you can do in a browser is something you cannot do in a script.
246
+
247
+ `doctor` prints the install line for your platform — `apt install ghostscript`,
248
+ `brew install ghostscript`, or the winget package on Windows. It only reports;
249
+ nothing is installed for you.
250
+
251
+ ## Roadmap
252
+
253
+ | | | |
254
+ |---|---|---|
255
+ | **M0** | Foundation — architecture, CI, safety mechanisms | ✅ done |
256
+ | **M1** | Core engine + `merge` as the reference implementation | ✅ complete |
257
+ | **M2** | `split`, `rotate`, `reorder`, `pages`, `metadata`, `sanitize`, `get-info` | ✅ done |
258
+ | **M3** | `compress` + external-binary support in `doctor` | ✅ done |
259
+ | **M4** | `watermark`, `stamp`, `protect`, `unlock`, `permissions` | ✅ done |
260
+ | **M5** | `convert`, `to-images`, `from-images` | ✅ done |
261
+ | **M6** | Cloud engines, `--json` everywhere, published benchmarks | ✅ done |
262
+ | **M7** | Textual TUI + visual pickers for crop and reorder | ✅ done |
263
+ | **M8** | OCR, done properly | ✅ done |
264
+ | **M9** | Pipelines, batch, folder watch — `--resume` [deferred](#many-documents-several-steps-or-a-folder-that-fills-up) | ✅ |
265
+ | **M10** | Local MCP server — drive DocMax from an AI agent, nothing leaves your machine | ✅ |
266
+ | **M11** | Remote MCP — network-reachable tool server, for clients that can't spawn a local process | ✅ |
267
+
268
+ Benchmarks live in [`benchmarks/`](benchmarks/METHODOLOGY.md) with the method
269
+ written down. Run them with `python -m benchmarks`. No numbers appear in this
270
+ README until they are measured — and none have been yet.
271
+
272
+ ## Documentation
273
+
274
+ [**docs/**](docs/README.md) is the index. The short version:
275
+
276
+ - [architecture/overview.md](docs/architecture/overview.md) — how DocMax is put
277
+ together, and why
278
+ - [adr/](docs/adr/README.md) — the decisions, and what they cost
279
+ - [planning/current-status.md](docs/planning/current-status.md) — what is done,
280
+ what is next, what is missing
281
+
282
+ ## Contributing
283
+
284
+ ```bash
285
+ git clone https://github.com/megabyte44/docmax
286
+ cd docmax
287
+ python -m venv .venv && .venv/bin/pip install -e ".[dev]"
288
+ pre-commit install
289
+
290
+ pytest && ruff check . && mypy && lint-imports
291
+ ```
292
+
293
+ Start with [docs/architecture/overview.md](docs/architecture/overview.md) and the
294
+ [ADRs](docs/adr/) — they explain the constraints, most of which exist for a
295
+ specific reason.
296
+
297
+ ## Licence
298
+
299
+ MIT. Every document operation is free and always will be — see
300
+ [ADR 0004](docs/adr/0004-open-core-boundary.md) for where the open-core line
301
+ sits and why.