docling-rs 0.15.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 (91) hide show
  1. docling_rs-0.15.1/PKG-INFO +248 -0
  2. docling_rs-0.15.1/README.md +233 -0
  3. docling_rs-0.15.1/docling/Cargo.toml +57 -0
  4. docling_rs-0.15.1/docling/README.md +597 -0
  5. docling_rs-0.15.1/docling/examples/convert.rs +17 -0
  6. docling_rs-0.15.1/docling/examples/stream.rs +38 -0
  7. docling_rs-0.15.1/docling/sample.html +42 -0
  8. docling_rs-0.15.1/docling/sample.md +13 -0
  9. docling_rs-0.15.1/docling/src/backend/asciidoc.rs +326 -0
  10. docling_rs-0.15.1/docling/src/backend/browser.rs +138 -0
  11. docling_rs-0.15.1/docling/src/backend/csv.rs +109 -0
  12. docling_rs-0.15.1/docling/src/backend/deepseek.rs +203 -0
  13. docling_rs-0.15.1/docling/src/backend/docling_json.rs +280 -0
  14. docling_rs-0.15.1/docling/src/backend/docx.rs +1371 -0
  15. docling_rs-0.15.1/docling/src/backend/email.rs +120 -0
  16. docling_rs-0.15.1/docling/src/backend/epub.rs +241 -0
  17. docling_rs-0.15.1/docling/src/backend/html.rs +1510 -0
  18. docling_rs-0.15.1/docling/src/backend/images.rs +258 -0
  19. docling_rs-0.15.1/docling/src/backend/jats.rs +952 -0
  20. docling_rs-0.15.1/docling/src/backend/latex.rs +469 -0
  21. docling_rs-0.15.1/docling/src/backend/markdown.rs +667 -0
  22. docling_rs-0.15.1/docling/src/backend/mhtml.rs +198 -0
  23. docling_rs-0.15.1/docling/src/backend/mod.rs +101 -0
  24. docling_rs-0.15.1/docling/src/backend/odf.rs +1378 -0
  25. docling_rs-0.15.1/docling/src/backend/omml.rs +507 -0
  26. docling_rs-0.15.1/docling/src/backend/ooxml.rs +229 -0
  27. docling_rs-0.15.1/docling/src/backend/pptx.rs +504 -0
  28. docling_rs-0.15.1/docling/src/backend/uspto.rs +1075 -0
  29. docling_rs-0.15.1/docling/src/backend/uspto_entities.rs +2178 -0
  30. docling_rs-0.15.1/docling/src/backend/webvtt.rs +191 -0
  31. docling_rs-0.15.1/docling/src/backend/xbrl.rs +110 -0
  32. docling_rs-0.15.1/docling/src/backend/xlsx.rs +305 -0
  33. docling_rs-0.15.1/docling/src/converter.rs +373 -0
  34. docling_rs-0.15.1/docling/src/dclx.rs +58 -0
  35. docling_rs-0.15.1/docling/src/error.rs +60 -0
  36. docling_rs-0.15.1/docling/src/format.rs +102 -0
  37. docling_rs-0.15.1/docling/src/lib.rs +51 -0
  38. docling_rs-0.15.1/docling/src/result.rs +27 -0
  39. docling_rs-0.15.1/docling/src/source.rs +73 -0
  40. docling_rs-0.15.1/docling/src/stream.rs +193 -0
  41. docling_rs-0.15.1/docling-asr/Cargo.toml +22 -0
  42. docling_rs-0.15.1/docling-asr/README.md +597 -0
  43. docling_rs-0.15.1/docling-asr/src/audio.rs +167 -0
  44. docling_rs-0.15.1/docling-asr/src/lib.rs +99 -0
  45. docling_rs-0.15.1/docling-asr/src/mel.rs +197 -0
  46. docling_rs-0.15.1/docling-asr/src/tokenizer.rs +170 -0
  47. docling_rs-0.15.1/docling-asr/src/whisper.rs +513 -0
  48. docling_rs-0.15.1/docling-core/Cargo.toml +21 -0
  49. docling_rs-0.15.1/docling-core/README.md +597 -0
  50. docling_rs-0.15.1/docling-core/src/base64.rs +85 -0
  51. docling_rs-0.15.1/docling-core/src/doclang.rs +1679 -0
  52. docling_rs-0.15.1/docling-core/src/document.rs +415 -0
  53. docling_rs-0.15.1/docling-core/src/json.rs +732 -0
  54. docling_rs-0.15.1/docling-core/src/labels.rs +43 -0
  55. docling_rs-0.15.1/docling-core/src/lib.rs +26 -0
  56. docling_rs-0.15.1/docling-core/src/markdown.rs +876 -0
  57. docling_rs-0.15.1/docling-pdf/Cargo.toml +27 -0
  58. docling_rs-0.15.1/docling-pdf/README.md +597 -0
  59. docling_rs-0.15.1/docling-pdf/examples/dump_chars.rs +24 -0
  60. docling_rs-0.15.1/docling-pdf/examples/dump_regions.rs +54 -0
  61. docling_rs-0.15.1/docling-pdf/examples/dump_render_modes.rs +48 -0
  62. docling_rs-0.15.1/docling-pdf/examples/dump_stages.rs +20 -0
  63. docling_rs-0.15.1/docling-pdf/examples/extract.rs +26 -0
  64. docling_rs-0.15.1/docling-pdf/examples/layout.rs +32 -0
  65. docling_rs-0.15.1/docling-pdf/examples/probe_page.rs +67 -0
  66. docling_rs-0.15.1/docling-pdf/examples/snapshot.rs +88 -0
  67. docling_rs-0.15.1/docling-pdf/examples/textparse_dump.rs +44 -0
  68. docling_rs-0.15.1/docling-pdf/examples/textparse_glyphs.rs +18 -0
  69. docling_rs-0.15.1/docling-pdf/examples/tf_otsl.rs +73 -0
  70. docling_rs-0.15.1/docling-pdf/examples/word_cells.rs +17 -0
  71. docling_rs-0.15.1/docling-pdf/src/assemble.rs +1559 -0
  72. docling_rs-0.15.1/docling-pdf/src/dp_lines.rs +485 -0
  73. docling_rs-0.15.1/docling-pdf/src/layout.rs +154 -0
  74. docling_rs-0.15.1/docling-pdf/src/lib.rs +814 -0
  75. docling_rs-0.15.1/docling-pdf/src/mets.rs +164 -0
  76. docling_rs-0.15.1/docling-pdf/src/ocr.rs +223 -0
  77. docling_rs-0.15.1/docling-pdf/src/pdfium_backend.rs +978 -0
  78. docling_rs-0.15.1/docling-pdf/src/resample.rs +70 -0
  79. docling_rs-0.15.1/docling-pdf/src/tableformer.rs +633 -0
  80. docling_rs-0.15.1/docling-pdf/src/textparse.rs +1456 -0
  81. docling_rs-0.15.1/docling-pdf/src/timing.rs +65 -0
  82. docling_rs-0.15.1/docling-py/Cargo.lock +2914 -0
  83. docling_rs-0.15.1/docling-py/Cargo.toml +31 -0
  84. docling_rs-0.15.1/docling-py/README.md +233 -0
  85. docling_rs-0.15.1/docling-py/src/lib.rs +254 -0
  86. docling_rs-0.15.1/docling-py/tests/test_config.py +165 -0
  87. docling_rs-0.15.1/docling-py/tests/test_dropin.py +60 -0
  88. docling_rs-0.15.1/pyproject.toml +35 -0
  89. docling_rs-0.15.1/python/docling_rs/__init__.py +237 -0
  90. docling_rs-0.15.1/python/docling_rs/models.py +130 -0
  91. docling_rs-0.15.1/python/docling_rs/options.py +122 -0
@@ -0,0 +1,248 @@
1
+ Metadata-Version: 2.4
2
+ Name: docling-rs
3
+ Version: 0.15.1
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Programming Language :: Rust
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Requires-Dist: docling-core>=2.0
9
+ Summary: Rust port of docling (docling.rs): convert PDF/DOCX/HTML/... to Markdown or docling JSON. Python bindings.
10
+ License: MIT
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
13
+ Project-URL: Repository, https://github.com/docling-project/docling.rs
14
+
15
+ # docling-py — Python bindings (PyO3)
16
+
17
+ A **strangler-fig drop-in** for Python docling's common conversion path,
18
+ backed by the Rust [docling.rs](https://github.com/docling-project/docling.rs) engine:
19
+ same call shape, no torch, ~4× faster PDF conversion at a fraction of the
20
+ memory (see [`PDF_PERFORMANCE.md`](../../PDF_PERFORMANCE.md)).
21
+
22
+ ```python
23
+ # was: from docling.document_converter import DocumentConverter
24
+ from docling_rs import DocumentConverter
25
+
26
+ result = DocumentConverter().convert("document.pdf")
27
+ print(result.document.export_to_markdown())
28
+ data = result.document.export_to_dict() # docling-core JSON wire format (schema 1.10.0)
29
+ ```
30
+
31
+ **Only the document processor is Rust.** The engine parses the input and returns
32
+ docling-core's JSON wire format; this package validates it into a genuine
33
+ [`docling_core.types.doc.DoclingDocument`](https://github.com/docling-project/docling-core).
34
+ So `result.document` **is** the docling object — `export_to_markdown()`,
35
+ `export_to_dict()`, `export_to_doctags()`, the serializers, and the
36
+ [chunkers](https://github.com/docling-project/docling-core) are docling's own
37
+ Python code, unchanged. `docling-core` is a runtime dependency; nothing else from
38
+ docling is required for the declarative path.
39
+
40
+ > **Status: experimental.** The PyPI distribution name is `docling-rs`.
41
+ > Releases are cut manually (like the npm package) via the
42
+ > [`pypi-publish`](../../.github/workflows/pypi-publish.yml) workflow — see
43
+ > [Publishing](#publishing) below. The crate is intentionally outside the repo's
44
+ > Cargo workspace and its crates.io publish flow. For development, build and
45
+ > install locally as shown next.
46
+
47
+ ## Try it locally
48
+
49
+ Needs a Rust toolchain (1.82+) and Python ≥ 3.9.
50
+
51
+ ```bash
52
+ cd crates/docling-py
53
+
54
+ # 1. Build + install into the CURRENT virtualenv (create one first):
55
+ python -m venv .venv && source .venv/bin/activate
56
+ pip install maturin
57
+ maturin develop --release # compiles the Rust engine, installs `docling.rs`
58
+
59
+ # 2. One-time model download (~700 MB → ~/.cache/docling.rs), pure Python —
60
+ # fetched from the repo's models-v1 GitHub release, like docling fetches
61
+ # its artifacts. Declarative formats (DOCX/HTML/XLSX/…) skip this entirely.
62
+ python -c "import docling_rs; docling_rs.download_models()"
63
+
64
+ # 3. Convert:
65
+ python - <<'PY'
66
+ from docling_rs import DocumentConverter
67
+
68
+ conv = DocumentConverter()
69
+ result = conv.convert("../../tests/data/pdf/sources/2305.03393v1-pg9.pdf")
70
+ print(result.status) # "success"
71
+ print(result.document.export_to_markdown()[:400])
72
+ PY
73
+ ```
74
+
75
+ ## API surface (docling-shaped)
76
+
77
+ | docling.rs | docling counterpart | notes |
78
+ |---|---|---|
79
+ | `DocumentConverter(format_options=None, *, allowed_formats=None, do_ocr=True, do_table_structure=True, fetch_images=False, use_web_browser=False, artifacts_path=None)` | `DocumentConverter(allowed_formats=…, format_options=…)` | Pass `{InputFormat.PDF: PdfFormatOption(pipeline_options=PdfPipelineOptions(…))}` or the shorthand kwargs; `allowed_formats` restricts conversion; `artifacts_path` overrides the model cache dir. |
80
+ | `.convert(path \| DocumentStream) -> ConversionResult` | `.convert(source)` | str / `pathlib.Path` / `DocumentStream`. Releases the GIL during conversion. |
81
+ | `.convert_all(sources, raises_on_error=True) -> Iterator[ConversionResult]` | same | lazily converts many sources; `raises_on_error=False` yields a `failure` result instead of raising |
82
+ | `.initialize_pipeline(format=None)` | same | pre-loads the PDF/image ML models so the first conversion isn't slow and later PDFs reuse the warm pipeline (no-op for non-ML formats; needs the models available) |
83
+ | `.convert_bytes(name, data)` | `DocumentStream` | extension of `name` drives format detection |
84
+ | `InputFormat`, `PdfPipelineOptions`, `PdfFormatOption`, `AcceleratorOptions`, `TableFormerMode`, `DocumentStream`, `ImageRefMode` | same modules | docling-shaped config re-exported from `docling_rs` (see below) |
85
+ | `ConversionError` | `docling.exceptions.ConversionError` | raised on a failed conversion; caught by `convert_all(..., raises_on_error=False)` |
86
+ | `result.status` / `result.document` / `result.input.file` | same | `.status` is a `ConversionStatus` str-enum (`"success" / "partial_success" / "failure"`); `.document` is a genuine `docling_core` `DoclingDocument` |
87
+ | `document.export_to_markdown(...)` | same | docling-core's own method — all of docling's params (`image_placeholder`, `page_break_placeholder`, …) apply |
88
+ | `document.export_to_dict()` / `export_to_json()` / `export_to_doctags()` | same | docling-core's own serializers over the wire format |
89
+ | `document.save_as_markdown(p)` / `save_as_json(p)` / chunkers | same | anything `docling_core` offers on a `DoclingDocument` works, since it *is* one |
90
+ | `docling_rs.download_models()` | `docling-tools models download` | idempotent; `~/.cache/docling.rs` or `$DOCLING_RS_CACHE_DIR`; INT8 models fetched when hosted and preferred automatically (`DOCLING_RS_FP32=1` opts out) |
91
+
92
+ Model/env resolution order: explicit `DOCLING_*` env vars → the cache dir set
93
+ by `ensure_env()` (called by the constructor) → the process CWD (`models/`,
94
+ `.pdfium/`, matching the CLI). pdfium is Linux x64 from the release; on other
95
+ platforms set `PDFIUM_DYNAMIC_LIB_PATH` to a local build.
96
+
97
+ ## Configuration (docling-shaped)
98
+
99
+ `docling_rs` re-exports docling-shaped config objects — same names and fields, so
100
+ docling code reads unchanged:
101
+
102
+ ```python
103
+ from docling_rs import DocumentConverter, InputFormat, PdfFormatOption, PdfPipelineOptions, AcceleratorOptions
104
+
105
+ opts = PdfPipelineOptions(
106
+ do_ocr=False, # skip OCR on scanned pages
107
+ do_table_structure=True, # TableFormer table recovery
108
+ accelerator_options=AcceleratorOptions(num_threads=4),
109
+ )
110
+ conv = DocumentConverter(format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=opts)})
111
+ # shorthand: DocumentConverter(do_ocr=False, do_table_structure=True)
112
+ ```
113
+
114
+ The Rust engine acts on `do_ocr`, `do_table_structure`, and
115
+ `accelerator_options.num_threads` (→ ONNX Runtime intra-op threads via
116
+ `DOCLING_RS_PDF_THREADS`). The remaining `PdfPipelineOptions` fields
117
+ (`images_scale`, `generate_page_images`, `table_structure_options.mode`, …) are
118
+ accepted for API compatibility but do not change the pipeline. `InputFormat`,
119
+ `DocumentStream` and `ImageRefMode` are re-exported too (the last straight from
120
+ `docling_core`, for `export_to_markdown(image_mode=…)`). A GPU
121
+ `accelerator_options.device` (`CUDA`/`MPS`) is accepted but warns and falls back
122
+ to CPU — the engine runs ONNX Runtime on the CPU execution provider.
123
+
124
+ ## Not covered (yet)
125
+
126
+ VLM/enrichment pipelines, GPU accelerator devices (the engine is ONNX Runtime on
127
+ CPU), and per-format *backend* selection. Chunkers **are** available — the
128
+ returned object is a real `docling_core` `DoclingDocument`, so
129
+ `docling_core.transforms.chunker`'s `HierarchicalChunker` / `HybridChunker`
130
+ operate on it directly (install docling-core's own extras for those:
131
+ `pip install "docling-core[chunking]"`). The document carries rendered text for
132
+ inline formatting rather than structured `formatting` fields — see
133
+ `MIGRATION.md` §4 for the documented divergences.
134
+
135
+ ## Publishing
136
+
137
+ Releases are **manual**, mirroring the npm package: the
138
+ [`pypi-publish`](../../.github/workflows/pypi-publish.yml) GitHub Actions workflow
139
+ (`workflow_dispatch`) builds an `abi3` wheel per platform (Linux x86-64/arm64 as
140
+ `manylinux_2_28`, Windows x86-64 — one wheel covers every Python ≥ 3.9) plus an
141
+ sdist, and uploads them to PyPI.
142
+
143
+ ```bash
144
+ # From the Actions tab, or:
145
+ gh workflow run pypi-publish.yml # version from pyproject.toml
146
+ gh workflow run pypi-publish.yml -f version=0.16.0
147
+ ```
148
+
149
+ No secrets: it publishes via PyPI **Trusted Publishing** (OIDC), like
150
+ docling-core — no API token is stored or rotated. This requires a **one-time
151
+ setup on PyPI by the project owner** (below); re-runs are idempotent
152
+ (`skip-existing`). macOS wheels are omitted (no hosted runners here); macOS users
153
+ install the sdist, which compiles from source. The ONNX runtime is bundled in the
154
+ wheel; pdfium is fetched at runtime by `download_models()`.
155
+
156
+ ### First-time PyPI setup (project owner)
157
+
158
+ Trusted Publishing lets this repo's workflow upload to PyPI without any password
159
+ or API token — GitHub mints a short-lived OIDC token per run and PyPI verifies it
160
+ against a *trusted publisher* you register once. Until that publisher exists, the
161
+ `publish` job fails with `invalid-publisher: valid token, but no corresponding
162
+ publisher`.
163
+
164
+ Because the `docling-rs` project does not exist on PyPI yet, register a **pending
165
+ publisher** (it both authorizes the workflow and lets the first run create the
166
+ project):
167
+
168
+ 1. Sign in to PyPI as the account that will own `docling-rs`, then open
169
+ **<https://pypi.org/manage/account/publishing/>**.
170
+ 2. Under **“Add a new pending publisher”**, choose **GitHub** and fill in
171
+ **exactly** these values:
172
+
173
+ | Field | Value |
174
+ |---|---|
175
+ | PyPI Project Name | `docling-rs` |
176
+ | Owner | `docling-project` |
177
+ | Repository name | `docling.rs` |
178
+ | Workflow name | `pypi-publish.yml` |
179
+ | Environment name | `pypi` |
180
+
181
+ 3. Click **Add**. Then re-run the **pypi publish** workflow (Actions tab → Run
182
+ workflow) — the `publish` job will now succeed and create the project on its
183
+ first upload.
184
+
185
+ Notes:
186
+ - The Git branch does not matter: PyPI matches on repository + workflow +
187
+ environment, not the branch, so publishing works from any branch once the
188
+ publisher is registered.
189
+ - **Environment name must be `pypi`** — it has to match the `environment: pypi`
190
+ the `publish` job runs in.
191
+ - After the first successful publish the project exists; the *pending* publisher
192
+ automatically becomes a regular trusted publisher (manage it later at
193
+ *Project → Manage → Publishing*). To rotate/add publishers there, use the same
194
+ five values above.
195
+ - To rehearse without touching production PyPI, register the equivalent pending
196
+ publisher on **TestPyPI** (<https://test.pypi.org/manage/account/publishing/>)
197
+ and point the workflow's upload at `https://test.pypi.org/legacy/`.
198
+
199
+ ### Test the release build locally
200
+
201
+ Reproduce what CI does — build the wheel + sdist and verify both install and run
202
+ — before (or instead of) triggering the workflow. Needs a Rust toolchain and
203
+ Python ≥ 3.9.
204
+
205
+ ```bash
206
+ cd crates/docling-py
207
+ python -m venv .venv && source .venv/bin/activate
208
+ pip install maturin
209
+
210
+ # 1. Build the same two artifacts the workflow builds.
211
+ maturin build --release --out dist # dist/docling_rs-<v>-cp39-abi3-<platform>.whl
212
+ maturin sdist --out dist # dist/docling_rs-<v>.tar.gz (vendors all crates)
213
+
214
+ # 2. Smoke-test the WHEEL in a clean env — pip pulls docling-core from the
215
+ # wheel's declared dependency, exactly as an end user would get it.
216
+ python -m venv /tmp/wheel-test
217
+ /tmp/wheel-test/bin/pip install dist/docling_rs-*.whl
218
+ /tmp/wheel-test/bin/python - <<'PY'
219
+ from docling_rs import DocumentConverter
220
+ r = DocumentConverter().convert("../../tests/data/html/sources/hyperlink_03.html")
221
+ assert r.status == "success"
222
+ assert type(r.document).__module__.startswith("docling_core") # the real DoclingDocument
223
+ print("wheel OK:", len(r.document.export_to_markdown()), "md chars")
224
+ PY
225
+
226
+ # 3. Verify the SDIST is self-contained: pip compiles the Rust engine from source
227
+ # (this is the exact unpack-and-build path cibuildwheel runs in the manylinux
228
+ # containers, so a green result here means the CI wheel build will work too).
229
+ python -m venv /tmp/sdist-test
230
+ /tmp/sdist-test/bin/pip install dist/docling_rs-*.tar.gz
231
+ /tmp/sdist-test/bin/python -c "import docling_rs; print('sdist build OK')"
232
+
233
+ # 4. Run the declarative-path test suite (no ML models needed).
234
+ pip install pytest docling-core
235
+ pytest tests/
236
+ ```
237
+
238
+ To exercise the full manylinux wheel build (what `pypa/cibuildwheel` runs) you
239
+ need Docker; with a daemon available:
240
+
241
+ ```bash
242
+ pipx run cibuildwheel==2.21.3 --platform linux --output-dir wheelhouse .
243
+ # env: CIBW_BUILD=cp39-* CIBW_SKIP=*-musllinux* CIBW_BEFORE_ALL_LINUX="curl … rustup … -y"
244
+ ```
245
+
246
+ An optional final rehearsal uploads to **TestPyPI** (needs a TestPyPI token or a
247
+ pending publisher there): `pip install twine && twine upload --repository testpypi dist/*`.
248
+
@@ -0,0 +1,233 @@
1
+ # docling-py — Python bindings (PyO3)
2
+
3
+ A **strangler-fig drop-in** for Python docling's common conversion path,
4
+ backed by the Rust [docling.rs](https://github.com/docling-project/docling.rs) engine:
5
+ same call shape, no torch, ~4× faster PDF conversion at a fraction of the
6
+ memory (see [`PDF_PERFORMANCE.md`](../../PDF_PERFORMANCE.md)).
7
+
8
+ ```python
9
+ # was: from docling.document_converter import DocumentConverter
10
+ from docling_rs import DocumentConverter
11
+
12
+ result = DocumentConverter().convert("document.pdf")
13
+ print(result.document.export_to_markdown())
14
+ data = result.document.export_to_dict() # docling-core JSON wire format (schema 1.10.0)
15
+ ```
16
+
17
+ **Only the document processor is Rust.** The engine parses the input and returns
18
+ docling-core's JSON wire format; this package validates it into a genuine
19
+ [`docling_core.types.doc.DoclingDocument`](https://github.com/docling-project/docling-core).
20
+ So `result.document` **is** the docling object — `export_to_markdown()`,
21
+ `export_to_dict()`, `export_to_doctags()`, the serializers, and the
22
+ [chunkers](https://github.com/docling-project/docling-core) are docling's own
23
+ Python code, unchanged. `docling-core` is a runtime dependency; nothing else from
24
+ docling is required for the declarative path.
25
+
26
+ > **Status: experimental.** The PyPI distribution name is `docling-rs`.
27
+ > Releases are cut manually (like the npm package) via the
28
+ > [`pypi-publish`](../../.github/workflows/pypi-publish.yml) workflow — see
29
+ > [Publishing](#publishing) below. The crate is intentionally outside the repo's
30
+ > Cargo workspace and its crates.io publish flow. For development, build and
31
+ > install locally as shown next.
32
+
33
+ ## Try it locally
34
+
35
+ Needs a Rust toolchain (1.82+) and Python ≥ 3.9.
36
+
37
+ ```bash
38
+ cd crates/docling-py
39
+
40
+ # 1. Build + install into the CURRENT virtualenv (create one first):
41
+ python -m venv .venv && source .venv/bin/activate
42
+ pip install maturin
43
+ maturin develop --release # compiles the Rust engine, installs `docling.rs`
44
+
45
+ # 2. One-time model download (~700 MB → ~/.cache/docling.rs), pure Python —
46
+ # fetched from the repo's models-v1 GitHub release, like docling fetches
47
+ # its artifacts. Declarative formats (DOCX/HTML/XLSX/…) skip this entirely.
48
+ python -c "import docling_rs; docling_rs.download_models()"
49
+
50
+ # 3. Convert:
51
+ python - <<'PY'
52
+ from docling_rs import DocumentConverter
53
+
54
+ conv = DocumentConverter()
55
+ result = conv.convert("../../tests/data/pdf/sources/2305.03393v1-pg9.pdf")
56
+ print(result.status) # "success"
57
+ print(result.document.export_to_markdown()[:400])
58
+ PY
59
+ ```
60
+
61
+ ## API surface (docling-shaped)
62
+
63
+ | docling.rs | docling counterpart | notes |
64
+ |---|---|---|
65
+ | `DocumentConverter(format_options=None, *, allowed_formats=None, do_ocr=True, do_table_structure=True, fetch_images=False, use_web_browser=False, artifacts_path=None)` | `DocumentConverter(allowed_formats=…, format_options=…)` | Pass `{InputFormat.PDF: PdfFormatOption(pipeline_options=PdfPipelineOptions(…))}` or the shorthand kwargs; `allowed_formats` restricts conversion; `artifacts_path` overrides the model cache dir. |
66
+ | `.convert(path \| DocumentStream) -> ConversionResult` | `.convert(source)` | str / `pathlib.Path` / `DocumentStream`. Releases the GIL during conversion. |
67
+ | `.convert_all(sources, raises_on_error=True) -> Iterator[ConversionResult]` | same | lazily converts many sources; `raises_on_error=False` yields a `failure` result instead of raising |
68
+ | `.initialize_pipeline(format=None)` | same | pre-loads the PDF/image ML models so the first conversion isn't slow and later PDFs reuse the warm pipeline (no-op for non-ML formats; needs the models available) |
69
+ | `.convert_bytes(name, data)` | `DocumentStream` | extension of `name` drives format detection |
70
+ | `InputFormat`, `PdfPipelineOptions`, `PdfFormatOption`, `AcceleratorOptions`, `TableFormerMode`, `DocumentStream`, `ImageRefMode` | same modules | docling-shaped config re-exported from `docling_rs` (see below) |
71
+ | `ConversionError` | `docling.exceptions.ConversionError` | raised on a failed conversion; caught by `convert_all(..., raises_on_error=False)` |
72
+ | `result.status` / `result.document` / `result.input.file` | same | `.status` is a `ConversionStatus` str-enum (`"success" / "partial_success" / "failure"`); `.document` is a genuine `docling_core` `DoclingDocument` |
73
+ | `document.export_to_markdown(...)` | same | docling-core's own method — all of docling's params (`image_placeholder`, `page_break_placeholder`, …) apply |
74
+ | `document.export_to_dict()` / `export_to_json()` / `export_to_doctags()` | same | docling-core's own serializers over the wire format |
75
+ | `document.save_as_markdown(p)` / `save_as_json(p)` / chunkers | same | anything `docling_core` offers on a `DoclingDocument` works, since it *is* one |
76
+ | `docling_rs.download_models()` | `docling-tools models download` | idempotent; `~/.cache/docling.rs` or `$DOCLING_RS_CACHE_DIR`; INT8 models fetched when hosted and preferred automatically (`DOCLING_RS_FP32=1` opts out) |
77
+
78
+ Model/env resolution order: explicit `DOCLING_*` env vars → the cache dir set
79
+ by `ensure_env()` (called by the constructor) → the process CWD (`models/`,
80
+ `.pdfium/`, matching the CLI). pdfium is Linux x64 from the release; on other
81
+ platforms set `PDFIUM_DYNAMIC_LIB_PATH` to a local build.
82
+
83
+ ## Configuration (docling-shaped)
84
+
85
+ `docling_rs` re-exports docling-shaped config objects — same names and fields, so
86
+ docling code reads unchanged:
87
+
88
+ ```python
89
+ from docling_rs import DocumentConverter, InputFormat, PdfFormatOption, PdfPipelineOptions, AcceleratorOptions
90
+
91
+ opts = PdfPipelineOptions(
92
+ do_ocr=False, # skip OCR on scanned pages
93
+ do_table_structure=True, # TableFormer table recovery
94
+ accelerator_options=AcceleratorOptions(num_threads=4),
95
+ )
96
+ conv = DocumentConverter(format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=opts)})
97
+ # shorthand: DocumentConverter(do_ocr=False, do_table_structure=True)
98
+ ```
99
+
100
+ The Rust engine acts on `do_ocr`, `do_table_structure`, and
101
+ `accelerator_options.num_threads` (→ ONNX Runtime intra-op threads via
102
+ `DOCLING_RS_PDF_THREADS`). The remaining `PdfPipelineOptions` fields
103
+ (`images_scale`, `generate_page_images`, `table_structure_options.mode`, …) are
104
+ accepted for API compatibility but do not change the pipeline. `InputFormat`,
105
+ `DocumentStream` and `ImageRefMode` are re-exported too (the last straight from
106
+ `docling_core`, for `export_to_markdown(image_mode=…)`). A GPU
107
+ `accelerator_options.device` (`CUDA`/`MPS`) is accepted but warns and falls back
108
+ to CPU — the engine runs ONNX Runtime on the CPU execution provider.
109
+
110
+ ## Not covered (yet)
111
+
112
+ VLM/enrichment pipelines, GPU accelerator devices (the engine is ONNX Runtime on
113
+ CPU), and per-format *backend* selection. Chunkers **are** available — the
114
+ returned object is a real `docling_core` `DoclingDocument`, so
115
+ `docling_core.transforms.chunker`'s `HierarchicalChunker` / `HybridChunker`
116
+ operate on it directly (install docling-core's own extras for those:
117
+ `pip install "docling-core[chunking]"`). The document carries rendered text for
118
+ inline formatting rather than structured `formatting` fields — see
119
+ `MIGRATION.md` §4 for the documented divergences.
120
+
121
+ ## Publishing
122
+
123
+ Releases are **manual**, mirroring the npm package: the
124
+ [`pypi-publish`](../../.github/workflows/pypi-publish.yml) GitHub Actions workflow
125
+ (`workflow_dispatch`) builds an `abi3` wheel per platform (Linux x86-64/arm64 as
126
+ `manylinux_2_28`, Windows x86-64 — one wheel covers every Python ≥ 3.9) plus an
127
+ sdist, and uploads them to PyPI.
128
+
129
+ ```bash
130
+ # From the Actions tab, or:
131
+ gh workflow run pypi-publish.yml # version from pyproject.toml
132
+ gh workflow run pypi-publish.yml -f version=0.16.0
133
+ ```
134
+
135
+ No secrets: it publishes via PyPI **Trusted Publishing** (OIDC), like
136
+ docling-core — no API token is stored or rotated. This requires a **one-time
137
+ setup on PyPI by the project owner** (below); re-runs are idempotent
138
+ (`skip-existing`). macOS wheels are omitted (no hosted runners here); macOS users
139
+ install the sdist, which compiles from source. The ONNX runtime is bundled in the
140
+ wheel; pdfium is fetched at runtime by `download_models()`.
141
+
142
+ ### First-time PyPI setup (project owner)
143
+
144
+ Trusted Publishing lets this repo's workflow upload to PyPI without any password
145
+ or API token — GitHub mints a short-lived OIDC token per run and PyPI verifies it
146
+ against a *trusted publisher* you register once. Until that publisher exists, the
147
+ `publish` job fails with `invalid-publisher: valid token, but no corresponding
148
+ publisher`.
149
+
150
+ Because the `docling-rs` project does not exist on PyPI yet, register a **pending
151
+ publisher** (it both authorizes the workflow and lets the first run create the
152
+ project):
153
+
154
+ 1. Sign in to PyPI as the account that will own `docling-rs`, then open
155
+ **<https://pypi.org/manage/account/publishing/>**.
156
+ 2. Under **“Add a new pending publisher”**, choose **GitHub** and fill in
157
+ **exactly** these values:
158
+
159
+ | Field | Value |
160
+ |---|---|
161
+ | PyPI Project Name | `docling-rs` |
162
+ | Owner | `docling-project` |
163
+ | Repository name | `docling.rs` |
164
+ | Workflow name | `pypi-publish.yml` |
165
+ | Environment name | `pypi` |
166
+
167
+ 3. Click **Add**. Then re-run the **pypi publish** workflow (Actions tab → Run
168
+ workflow) — the `publish` job will now succeed and create the project on its
169
+ first upload.
170
+
171
+ Notes:
172
+ - The Git branch does not matter: PyPI matches on repository + workflow +
173
+ environment, not the branch, so publishing works from any branch once the
174
+ publisher is registered.
175
+ - **Environment name must be `pypi`** — it has to match the `environment: pypi`
176
+ the `publish` job runs in.
177
+ - After the first successful publish the project exists; the *pending* publisher
178
+ automatically becomes a regular trusted publisher (manage it later at
179
+ *Project → Manage → Publishing*). To rotate/add publishers there, use the same
180
+ five values above.
181
+ - To rehearse without touching production PyPI, register the equivalent pending
182
+ publisher on **TestPyPI** (<https://test.pypi.org/manage/account/publishing/>)
183
+ and point the workflow's upload at `https://test.pypi.org/legacy/`.
184
+
185
+ ### Test the release build locally
186
+
187
+ Reproduce what CI does — build the wheel + sdist and verify both install and run
188
+ — before (or instead of) triggering the workflow. Needs a Rust toolchain and
189
+ Python ≥ 3.9.
190
+
191
+ ```bash
192
+ cd crates/docling-py
193
+ python -m venv .venv && source .venv/bin/activate
194
+ pip install maturin
195
+
196
+ # 1. Build the same two artifacts the workflow builds.
197
+ maturin build --release --out dist # dist/docling_rs-<v>-cp39-abi3-<platform>.whl
198
+ maturin sdist --out dist # dist/docling_rs-<v>.tar.gz (vendors all crates)
199
+
200
+ # 2. Smoke-test the WHEEL in a clean env — pip pulls docling-core from the
201
+ # wheel's declared dependency, exactly as an end user would get it.
202
+ python -m venv /tmp/wheel-test
203
+ /tmp/wheel-test/bin/pip install dist/docling_rs-*.whl
204
+ /tmp/wheel-test/bin/python - <<'PY'
205
+ from docling_rs import DocumentConverter
206
+ r = DocumentConverter().convert("../../tests/data/html/sources/hyperlink_03.html")
207
+ assert r.status == "success"
208
+ assert type(r.document).__module__.startswith("docling_core") # the real DoclingDocument
209
+ print("wheel OK:", len(r.document.export_to_markdown()), "md chars")
210
+ PY
211
+
212
+ # 3. Verify the SDIST is self-contained: pip compiles the Rust engine from source
213
+ # (this is the exact unpack-and-build path cibuildwheel runs in the manylinux
214
+ # containers, so a green result here means the CI wheel build will work too).
215
+ python -m venv /tmp/sdist-test
216
+ /tmp/sdist-test/bin/pip install dist/docling_rs-*.tar.gz
217
+ /tmp/sdist-test/bin/python -c "import docling_rs; print('sdist build OK')"
218
+
219
+ # 4. Run the declarative-path test suite (no ML models needed).
220
+ pip install pytest docling-core
221
+ pytest tests/
222
+ ```
223
+
224
+ To exercise the full manylinux wheel build (what `pypa/cibuildwheel` runs) you
225
+ need Docker; with a daemon available:
226
+
227
+ ```bash
228
+ pipx run cibuildwheel==2.21.3 --platform linux --output-dir wheelhouse .
229
+ # env: CIBW_BUILD=cp39-* CIBW_SKIP=*-musllinux* CIBW_BEFORE_ALL_LINUX="curl … rustup … -y"
230
+ ```
231
+
232
+ An optional final rehearsal uploads to **TestPyPI** (needs a TestPyPI token or a
233
+ pending publisher there): `pip install twine && twine upload --repository testpypi dist/*`.
@@ -0,0 +1,57 @@
1
+ [package]
2
+ name = "docling"
3
+ description = "DocumentConverter and format backends for docling.rs (a Rust port of docling)."
4
+ version = "0.30.0"
5
+ edition = "2021"
6
+ license = "MIT"
7
+ repository = "https://github.com/docling-project/docling.rs"
8
+ homepage = "https://github.com/docling-project/docling.rs"
9
+ readme = "README.md"
10
+ keywords = ["docling", "document", "pdf", "markdown", "converter"]
11
+ categories = ["text-processing", "parser-implementations"]
12
+ rust-version = "1.74.0"
13
+ # The 25 MB output-regression corpus under tests/ is dev-only — keep it out of
14
+ # the published crate (and under crates.io's size limit). benches/ reference that
15
+ # corpus by path, so they're dev-only too.
16
+ exclude = ["tests", "benches"]
17
+
18
+ [features]
19
+ # Optional headless-browser HTML pre-render (the `--use-web-browser` flag /
20
+ # `DocumentConverter::use_web_browser`). Off by default so the standard build
21
+ # stays pure-Rust with no browser/runtime dependency; enable with
22
+ # `--features web-browser`. Drives the system Chromium over the DevTools
23
+ # protocol purely from Rust (no Node/Playwright) via `headless_chrome`.
24
+ web-browser = ["dep:headless_chrome"]
25
+
26
+ [dependencies]
27
+ calamine = { version = "0.26", features = ["dates"] }
28
+ csv = "1.4.0"
29
+ headless_chrome = { version = "1.0", optional = true }
30
+ docling-asr = { path = "../docling-asr", version = "0.30.0" }
31
+ docling-core = { path = "../docling-core", version = "0.30.0" }
32
+ docling-pdf = { path = "../docling-pdf", version = "0.30.0" }
33
+ image = { version = "0.25", default-features = false, features = ["png", "jpeg", "gif", "bmp", "tiff", "webp"] }
34
+ mail-parser = "0.11"
35
+ pulldown-cmark = { version = "0.13.4", default-features = false }
36
+ quick-xml = "0.37"
37
+ regex = "1.11"
38
+ roxmltree = "0.20"
39
+ scraper = "0.27.0"
40
+ serde_json = "1"
41
+ # Blocking HTTP client for fetching remote <img src> images when image fetching
42
+ # is enabled (DocumentConverter::fetch_images). 3.x matches the version `ort`
43
+ # already pulls in (so the graph keeps a single ureq); it needs Rust 1.85, which
44
+ # this crate already requires transitively via docling-pdf → ort.
45
+ ureq = "3"
46
+ zip = { version = "2.2", default-features = false, features = ["deflate"] }
47
+
48
+ [[example]]
49
+ name = "convert"
50
+ path = "examples/convert.rs"
51
+
52
+ [[example]]
53
+ name = "stream"
54
+ path = "examples/stream.rs"
55
+
56
+ [dev-dependencies]
57
+ criterion = "0.5"