pro-ledin-ocr 0.1.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.
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ *.egg-info/
7
+ build/
8
+ dist/
9
+ .venv/
10
+ venv/
11
+ .env
12
+ *.env
13
+
14
+ uv.lock
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mxl
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.
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.4
2
+ Name: pro-ledin-ocr
3
+ Version: 0.1.0
4
+ Summary: Layered OCR workhorse: extract text from scanned PDFs and images (tesseract, PyMuPDF, easyocr, paddleocr, vision-api).
5
+ Project-URL: Homepage, https://github.com/ledin-pro/ocr
6
+ Project-URL: Repository, https://github.com/ledin-pro/ocr
7
+ Author: mxl
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: cyrillic,ocr,pdf,tesseract,text-extraction,vision
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Image Recognition
20
+ Classifier: Topic :: Text Processing
21
+ Requires-Python: >=3.10
22
+ Provides-Extra: all
23
+ Requires-Dist: easyocr; extra == 'all'
24
+ Requires-Dist: numpy; extra == 'all'
25
+ Requires-Dist: openai>=1.0; extra == 'all'
26
+ Requires-Dist: opencv-python; extra == 'all'
27
+ Requires-Dist: paddleocr; extra == 'all'
28
+ Requires-Dist: paddlepaddle; extra == 'all'
29
+ Requires-Dist: pymupdf; extra == 'all'
30
+ Requires-Dist: pytesseract; extra == 'all'
31
+ Provides-Extra: cv
32
+ Requires-Dist: numpy; extra == 'cv'
33
+ Requires-Dist: opencv-python; extra == 'cv'
34
+ Provides-Extra: dev
35
+ Requires-Dist: pillow; extra == 'dev'
36
+ Requires-Dist: pytest>=8.0; extra == 'dev'
37
+ Provides-Extra: easyocr
38
+ Requires-Dist: easyocr; extra == 'easyocr'
39
+ Provides-Extra: paddle
40
+ Requires-Dist: paddleocr; extra == 'paddle'
41
+ Requires-Dist: paddlepaddle; extra == 'paddle'
42
+ Provides-Extra: pdf
43
+ Requires-Dist: pymupdf; extra == 'pdf'
44
+ Provides-Extra: pytesseract
45
+ Requires-Dist: pytesseract; extra == 'pytesseract'
46
+ Provides-Extra: vision
47
+ Requires-Dist: openai>=1.0; extra == 'vision'
48
+ Description-Content-Type: text/markdown
49
+
50
+ # pro-ledin-ocr
51
+
52
+ Layered OCR workhorse: extract text from scanned PDFs and images (PNG/JPG/TIFF/
53
+ HEIC/WEBP) using a tiered engine stack. The baseline path (poppler + tesseract)
54
+ needs zero extra Python installs; heavier engines are opt-in extras.
55
+
56
+ - Import name: `pro.ledin.ocr`
57
+ - Console scripts: `ocr`, `ocr-probe`
58
+ - PyPI: `pro-ledin-ocr`
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pip install pro-ledin-ocr # baseline
64
+ pip install "pro-ledin-ocr[vision]" # + OpenAI-compatible vision-api engine
65
+ pip install "pro-ledin-ocr[all]" # + pymupdf, opencv, easyocr, paddleocr
66
+ ```
67
+
68
+ System binaries required for the local path: `poppler` (pdftoppm, pdftotext,
69
+ pdfinfo) and `tesseract` (with language packs).
70
+
71
+ ```bash
72
+ brew install poppler tesseract tesseract-lang # macOS
73
+ sudo apt install poppler-utils tesseract-ocr-all # Debian/Ubuntu
74
+ ```
75
+
76
+ ## CLI
77
+
78
+ ```bash
79
+ ocr-probe myfile.pdf # triage: does it need OCR?
80
+ ocr myfile.pdf --format all # md + txt + json
81
+ ocr scan.png --format md
82
+ ocr russian_doc.pdf --lang rus+eng --format md
83
+ ocr scan.pdf --preprocess full # deskew + denoise
84
+ ocr slides.pdf --engine vision --pages 9,12 # hand pages to a multimodal agent
85
+ ocr slides.pdf --engine vision-api \
86
+ --vision-api-url https://api.example.com/v1 \
87
+ --vision-api-key "$KEY" --vision-model my-vision-model
88
+ ```
89
+
90
+ See `ocr --help` for the full flag reference.
91
+
92
+ ## Library
93
+
94
+ ```python
95
+ from pro.ledin import ocr
96
+
97
+ pages = ocr.recognize("scan.pdf", ocr.RecognizeOptions(engine="tesseract", lang="rus+eng"))
98
+ markdown = ocr.to_markdown(pages, "scan.pdf")
99
+ ```
100
+
101
+ `recognize()` never calls `sys.exit()`; catch `ocr.OcrError` for recoverable
102
+ failures (unsupported input, missing binaries/packages, vision-api config).
103
+
104
+ ## Engine tiers
105
+
106
+ | Tier | Engine | Best for | Cost |
107
+ |------|--------|----------|------|
108
+ | 0 | pdftotext / PyMuPDF | Real text layers | Free, instant |
109
+ | 1 | tesseract (default) | Clean scans, typed text, 160+ languages | Free |
110
+ | 2 | easyocr | Handwriting, degraded scans | Free, heavy |
111
+ | 2.5 | paddleocr | CJK, multilingual, angled text | Free |
112
+ | 3 | vision (agent reads PNGs) | Tables, charts, complex layouts | Agent tokens |
113
+ | 3.5 | vision-api (OpenAI-compatible) | Headless batch, complex layouts | API cost |
114
+
115
+ Full docs: `SKILL.md`, `references/engines.md`, `references/troubleshooting.md`.
116
+
117
+ ## Development
118
+
119
+ ```bash
120
+ uv sync --extra dev
121
+ uv run --extra dev pytest
122
+ uv build
123
+ ```
124
+
125
+ ## License
126
+
127
+ MIT
@@ -0,0 +1,78 @@
1
+ # pro-ledin-ocr
2
+
3
+ Layered OCR workhorse: extract text from scanned PDFs and images (PNG/JPG/TIFF/
4
+ HEIC/WEBP) using a tiered engine stack. The baseline path (poppler + tesseract)
5
+ needs zero extra Python installs; heavier engines are opt-in extras.
6
+
7
+ - Import name: `pro.ledin.ocr`
8
+ - Console scripts: `ocr`, `ocr-probe`
9
+ - PyPI: `pro-ledin-ocr`
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install pro-ledin-ocr # baseline
15
+ pip install "pro-ledin-ocr[vision]" # + OpenAI-compatible vision-api engine
16
+ pip install "pro-ledin-ocr[all]" # + pymupdf, opencv, easyocr, paddleocr
17
+ ```
18
+
19
+ System binaries required for the local path: `poppler` (pdftoppm, pdftotext,
20
+ pdfinfo) and `tesseract` (with language packs).
21
+
22
+ ```bash
23
+ brew install poppler tesseract tesseract-lang # macOS
24
+ sudo apt install poppler-utils tesseract-ocr-all # Debian/Ubuntu
25
+ ```
26
+
27
+ ## CLI
28
+
29
+ ```bash
30
+ ocr-probe myfile.pdf # triage: does it need OCR?
31
+ ocr myfile.pdf --format all # md + txt + json
32
+ ocr scan.png --format md
33
+ ocr russian_doc.pdf --lang rus+eng --format md
34
+ ocr scan.pdf --preprocess full # deskew + denoise
35
+ ocr slides.pdf --engine vision --pages 9,12 # hand pages to a multimodal agent
36
+ ocr slides.pdf --engine vision-api \
37
+ --vision-api-url https://api.example.com/v1 \
38
+ --vision-api-key "$KEY" --vision-model my-vision-model
39
+ ```
40
+
41
+ See `ocr --help` for the full flag reference.
42
+
43
+ ## Library
44
+
45
+ ```python
46
+ from pro.ledin import ocr
47
+
48
+ pages = ocr.recognize("scan.pdf", ocr.RecognizeOptions(engine="tesseract", lang="rus+eng"))
49
+ markdown = ocr.to_markdown(pages, "scan.pdf")
50
+ ```
51
+
52
+ `recognize()` never calls `sys.exit()`; catch `ocr.OcrError` for recoverable
53
+ failures (unsupported input, missing binaries/packages, vision-api config).
54
+
55
+ ## Engine tiers
56
+
57
+ | Tier | Engine | Best for | Cost |
58
+ |------|--------|----------|------|
59
+ | 0 | pdftotext / PyMuPDF | Real text layers | Free, instant |
60
+ | 1 | tesseract (default) | Clean scans, typed text, 160+ languages | Free |
61
+ | 2 | easyocr | Handwriting, degraded scans | Free, heavy |
62
+ | 2.5 | paddleocr | CJK, multilingual, angled text | Free |
63
+ | 3 | vision (agent reads PNGs) | Tables, charts, complex layouts | Agent tokens |
64
+ | 3.5 | vision-api (OpenAI-compatible) | Headless batch, complex layouts | API cost |
65
+
66
+ Full docs: `SKILL.md`, `references/engines.md`, `references/troubleshooting.md`.
67
+
68
+ ## Development
69
+
70
+ ```bash
71
+ uv sync --extra dev
72
+ uv run --extra dev pytest
73
+ uv build
74
+ ```
75
+
76
+ ## License
77
+
78
+ MIT
@@ -0,0 +1,211 @@
1
+ ---
2
+ name: ocr
3
+ description: >
4
+ Extract text from scanned PDFs and images (PNG/JPG/TIFF/HEIC) using OCR. Use
5
+ this skill whenever a PDF's text cannot be selected or copied, the document is a
6
+ scan or photo, text is rendered as images rather than a selectable layer, or the
7
+ file is a receipt, screenshot, fax, ID card, form, or presentation slide image.
8
+ Also use for non-English and Cyrillic-language scans, when pdftotext or pypdf
9
+ return empty or garbled output, or when a user says "this PDF has no text" or
10
+ "I can't copy from this file". Handles language auto-detection, deskew and
11
+ denoise for messy scans, tables and charts via vision escalation, and produces
12
+ Markdown plus plain-text output. Always reach for this skill before giving up on
13
+ a document that appears to have no readable text.
14
+ ---
15
+
16
+ # OCR Skill
17
+
18
+ Extracts text from scanned PDFs and images using a layered engine stack.
19
+ The baseline path runs with zero additional installs (poppler + tesseract are
20
+ assumed present). Heavier tools are added only when a page needs them.
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ pip install pro-ledin-ocr # baseline (tesseract + poppler on PATH)
26
+ pip install "pro-ledin-ocr[vision]" # + OpenAI-compatible vision-api engine
27
+ pip install "pro-ledin-ocr[all]" # + pymupdf, opencv, easyocr, paddleocr
28
+ ```
29
+
30
+ System binaries still required for the local path: `poppler` (pdftoppm,
31
+ pdftotext, pdfinfo) and `tesseract`. Installs the `ocr` and `ocr-probe`
32
+ console scripts.
33
+
34
+ ## When to use this skill
35
+
36
+ - PDF where text cannot be selected/copied in a viewer
37
+ - PDF produced by scanning a paper document or photographing a page
38
+ - Office-generated PDF where text is rasterized into image masks (common with
39
+ some Word/PowerPoint exports — `pdftotext` returns near-empty output)
40
+ - Any standalone image file containing text (PNG, JPG, TIFF, HEIC, WEBP)
41
+ - Non-English documents, especially Cyrillic/Russian
42
+ - Receipts, invoices, forms, ID cards, screenshots of documents
43
+ - When a previous attempt with `pdftotext`, `pypdf`, or `pdfplumber` failed
44
+
45
+ ## Decision tree
46
+
47
+ Work through this in order. Stop at the first successful step.
48
+
49
+ ```
50
+ 1. Is input an image (png/jpg/tiff/heic/webp)?
51
+ └─ Yes → OCR directly (skip probe). Go to step 3.
52
+
53
+ 2. PDF input: run ocr-probe FILE
54
+ ├─ needs_ocr = false → real text layer exists.
55
+ │ Run: pdftotext -layout FILE - (or PyMuPDF get_text())
56
+ │ Done — fast, free, no OCR needed.
57
+ └─ needs_ocr = true → continue to step 3.
58
+
59
+ 3. Baseline OCR:
60
+ ocr FILE --format all
61
+ (auto-detects language via OSD, DPI from page size, preprocessing level)
62
+ Emits: FILE.md FILE.txt FILE_ocr.json
63
+ quality report on stderr: per-page confidence, flagged pages.
64
+
65
+ 4. Review quality report. Escalate flagged pages only:
66
+ ├─ low confidence OR tables/charts/forms
67
+ │ → ocr FILE --engine vision --pages <flagged>
68
+ │ Renders persistent PNGs and hands them to the current multimodal agent
69
+ │ to read (Claude, GPT, or another model with image/file reading).
70
+ │ Agent produces Markdown (use Markdown table syntax for tables).
71
+ ├─ CJK / multilingual / angled dense text
72
+ │ → ocr FILE --engine paddleocr
73
+ │ (opt-in; installs paddleocr+paddlepaddle, downloads models on first run)
74
+ ├─ handwriting detected (very low conf, cursive)
75
+ │ → ocr FILE --engine easyocr
76
+ └─ skewed/noisy scan (scanned paper, phone photo)
77
+ → ocr FILE --preprocess full
78
+
79
+ 5. Need a selectable/searchable PDF?
80
+ → ocr FILE --searchable-pdf OUT.pdf
81
+ (requires: brew install ocrmypdf)
82
+
83
+ 6. Processing a folder or re-running repeatedly?
84
+ → ocr FILE1 FILE2 … --cache ocr_cache.json
85
+ Add --skip-ocr to triage-only mode (skip OCR, text-layer files only).
86
+ Add --force to ignore cache and re-process.
87
+ ```
88
+
89
+ ## Quick start (90% of cases)
90
+
91
+ ```bash
92
+ # Probe first to confirm OCR is needed
93
+ ocr-probe myfile.pdf
94
+
95
+ # Extract everything (md + txt + json quality report)
96
+ ocr myfile.pdf --format all
97
+
98
+ # Image input
99
+ ocr scan.png --format all
100
+
101
+ # Russian/Cyrillic doc — language auto-detected, but can be forced
102
+ ocr russian_doc.pdf --lang rus+eng --format md
103
+
104
+ # Messy scan (skewed, noisy)
105
+ ocr scan.pdf --preprocess full --format all
106
+
107
+ # Table-heavy slide / complex layout → vision tier
108
+ ocr slides.pdf --engine vision
109
+
110
+ # CJK / multilingual doc → PaddleOCR (opt-in)
111
+ ocr doc.png --engine paddleocr
112
+
113
+ # Headless vision via an OpenAI-compatible endpoint (all config via flags)
114
+ ocr slides.pdf --engine vision-api \
115
+ --vision-api-url https://api.example.com/v1 \
116
+ --vision-api-key "$MY_KEY" --vision-model my-vision-model
117
+ ```
118
+
119
+ For `vision-api`, page images over ~7.5 MB are auto-re-encoded to JPEG (full
120
+ resolution first, dimensions reduced only if still too large) to stay under
121
+ the API's 10 MB per-image cap; this re-encode path requires Pillow. Images
122
+ already under the limit are sent untouched with their detected media type.
123
+
124
+ ## Engine tiers (summary)
125
+
126
+ | Tier | Engine | Best for | Cost |
127
+ |------|--------|----------|------|
128
+ | 0 | pdftotext / PyMuPDF | Real text layers | Free, instant |
129
+ | 1 | tesseract (default) | Clean scans, typed text, 160+ languages | Free, ~3–4s/page |
130
+ | 2 | easyocr | Handwriting, degraded scans | Free, heavy (~2 GB) |
131
+ | 2.5 | paddleocr (opt-in) | CJK, multilingual (100+), angled text | Free, models on first run |
132
+ | 3 | vision (agent reads PNGs) | Tables, charts, complex layouts | Agent/model tokens |
133
+ | 4 | cloud APIs | High-volume, max accuracy | Paid + key |
134
+
135
+ See `references/engines.md` for full details, escalation thresholds, language
136
+ maps, DPI guidance, preprocessing levels, and install commands.
137
+
138
+ ## Output formats
139
+
140
+ | Flag | Output |
141
+ |------|--------|
142
+ | `--format md` | `# filename` + `## Page N` headers, prose text |
143
+ | `--format txt` | pages separated by `----- Page N -----` |
144
+ | `--format json` | per-page text + word confidence + bboxes + quality report |
145
+ | `--format all` | all three formats written to disk |
146
+ | `--searchable-pdf OUT` | invisible text layer overlaid on original PDF |
147
+
148
+ ## All CLI flags
149
+
150
+ ```
151
+ ocr INPUT [INPUT ...]
152
+ --engine auto|tesseract|easyocr|paddleocr|vision|vision-api default: auto
153
+ --lang auto|<tesseract codes> default: auto (OSD detection)
154
+ --format md|txt|json|all default: md
155
+ --out PATH default: stdout (md/txt) or ./
156
+ --dpi N|auto default: auto (300 A4, 150 wide)
157
+ --preprocess none|basic|enhanced|full|auto default: auto
158
+ --pages RANGE (e.g. 1-3,5)
159
+ --max-pages N
160
+ --psm N (default 3; use 6 for dense single-block pages)
161
+ --min-conf F (default 60.0 — flag pages below this for review)
162
+ --cache PATH --force --skip-ocr
163
+ --no-cleanup (skip whitespace / ligature cleanup)
164
+ --vision-api-url URL (OpenAI-compatible base URL for vision-api)
165
+ --vision-api-key KEY (required for vision-api; env vars are NOT read)
166
+ --vision-model NAME (required for vision-api; no default)
167
+ --searchable-pdf OUT.pdf
168
+ --json-report PATH
169
+ --verbose
170
+ ```
171
+
172
+ ## Library usage
173
+
174
+ The package also works as an importable library for other skills/scripts that
175
+ want structured OCR results in-process instead of shelling out to the CLI:
176
+
177
+ ```python
178
+ from pro.ledin import ocr
179
+
180
+ pages = ocr.recognize("scan.pdf", ocr.RecognizeOptions(engine="tesseract", lang="rus+eng"))
181
+ markdown = ocr.to_markdown(pages, "scan.pdf")
182
+ ```
183
+
184
+ - `recognize(path, options=None, *, caps=None, cache=None)` is the entry
185
+ point: it manages a throwaway render directory and returns the same
186
+ per-page dicts the CLI builds internally. Format the result with
187
+ `to_markdown()`, `to_text()`, or `to_json()`.
188
+ - `RecognizeOptions` mirrors the CLI's recognition flags (`engine`, `lang`,
189
+ `dpi`, `preprocess`, `pages`, `max_pages`, `psm`, `min_conf`, `no_cleanup`,
190
+ `force`, `vision_api_url`, `vision_api_key`, `vision_model`, `timeout`,
191
+ `verbose`). Output-only flags (`--out`, `--format`, `--json-report`,
192
+ `--searchable-pdf`) are CLI-only and have no library equivalent.
193
+ - `--engine vision` is an interactive agent handoff (renders pages and prints
194
+ a manifest for a multimodal agent to read) and is not usable via
195
+ `recognize()`; it raises `OcrError` if requested. Use another engine or the
196
+ CLI directly.
197
+ - Catch `OcrError` for recoverable failures (unsupported input, missing
198
+ binaries/packages, vision-api config/request errors) — the library never
199
+ calls `sys.exit()`, unlike the CLI.
200
+ - For `engine="vision-api"`, `RecognizeOptions.timeout` (seconds) bounds the
201
+ HTTP request via the openai SDK's own client timeout. Local engines
202
+ (tesseract/easyocr/paddleocr) run in-process with no external kill switch,
203
+ since there is no longer a subprocess to terminate on timeout.
204
+ - The `healthos` skill uses this API (via `import`) to recognize family medical
205
+ documents without spawning a subprocess per file.
206
+
207
+ ## Troubleshooting
208
+
209
+ See `references/troubleshooting.md` for: rasterized-text PDFs, garbled Cyrillic,
210
+ rotated pages, table/chart handling, handwriting, multi-column layouts, and
211
+ large-folder batch jobs.
@@ -0,0 +1,65 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pro-ledin-ocr"
7
+ version = "0.1.0"
8
+ description = "Layered OCR workhorse: extract text from scanned PDFs and images (tesseract, PyMuPDF, easyocr, paddleocr, vision-api)."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "mxl" }]
14
+ keywords = ["ocr", "pdf", "tesseract", "vision", "text-extraction", "cyrillic"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Environment :: Console",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: Scientific/Engineering :: Image Recognition",
25
+ "Topic :: Text Processing",
26
+ ]
27
+ dependencies = []
28
+
29
+ [project.optional-dependencies]
30
+ vision = ["openai>=1.0"]
31
+ pdf = ["pymupdf"]
32
+ cv = ["opencv-python", "numpy"]
33
+ pytesseract = ["pytesseract"]
34
+ easyocr = ["easyocr"]
35
+ paddle = ["paddleocr", "paddlepaddle"]
36
+ all = ["openai>=1.0", "pymupdf", "opencv-python", "numpy", "pytesseract", "easyocr", "paddleocr", "paddlepaddle"]
37
+ dev = ["pytest>=8.0", "pillow"]
38
+
39
+ [project.scripts]
40
+ ocr = "pro.ledin.ocr.cli:main"
41
+ ocr-probe = "pro.ledin.ocr.cli:probe_main"
42
+
43
+ [project.urls]
44
+ Homepage = "https://github.com/ledin-pro/ocr"
45
+ Repository = "https://github.com/ledin-pro/ocr"
46
+
47
+ [tool.hatch.build.targets.wheel]
48
+ packages = ["src/pro"]
49
+
50
+ [tool.hatch.build.targets.wheel.force-include]
51
+ "src/pro/ledin/ocr/probe.sh" = "pro/ledin/ocr/probe.sh"
52
+
53
+ [tool.hatch.build.targets.sdist]
54
+ include = [
55
+ "src/pro",
56
+ "tests",
57
+ "references",
58
+ "SKILL.md",
59
+ "README.md",
60
+ "LICENSE",
61
+ ]
62
+
63
+ [tool.pytest.ini_options]
64
+ testpaths = ["tests"]
65
+ pythonpath = ["src"]