markitdown-ocr 0.1.0__py3-none-any.whl
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.
- markitdown_ocr/__about__.py +4 -0
- markitdown_ocr/__init__.py +31 -0
- markitdown_ocr/_docx_converter_with_ocr.py +189 -0
- markitdown_ocr/_ocr_service.py +110 -0
- markitdown_ocr/_pdf_converter_with_ocr.py +422 -0
- markitdown_ocr/_plugin.py +68 -0
- markitdown_ocr/_pptx_converter_with_ocr.py +249 -0
- markitdown_ocr/_xlsx_converter_with_ocr.py +225 -0
- markitdown_ocr-0.1.0.dist-info/METADATA +233 -0
- markitdown_ocr-0.1.0.dist-info/RECORD +13 -0
- markitdown_ocr-0.1.0.dist-info/WHEEL +4 -0
- markitdown_ocr-0.1.0.dist-info/entry_points.txt +2 -0
- markitdown_ocr-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Enhanced PDF Converter with OCR support for embedded images.
|
|
3
|
+
Extracts images from PDFs and performs OCR while maintaining document context.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import io
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Any, BinaryIO, Optional
|
|
9
|
+
|
|
10
|
+
from markitdown import DocumentConverter, DocumentConverterResult, StreamInfo
|
|
11
|
+
from markitdown._exceptions import (
|
|
12
|
+
MissingDependencyException,
|
|
13
|
+
MISSING_DEPENDENCY_MESSAGE,
|
|
14
|
+
)
|
|
15
|
+
from ._ocr_service import LLMVisionOCRService
|
|
16
|
+
|
|
17
|
+
# Import dependencies
|
|
18
|
+
_dependency_exc_info = None
|
|
19
|
+
try:
|
|
20
|
+
import pdfminer
|
|
21
|
+
import pdfminer.high_level
|
|
22
|
+
import pdfplumber
|
|
23
|
+
from PIL import Image
|
|
24
|
+
except ImportError:
|
|
25
|
+
_dependency_exc_info = sys.exc_info()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _extract_images_from_page(page: Any) -> list[dict]:
|
|
29
|
+
"""
|
|
30
|
+
Extract images from a PDF page by rendering page regions.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
List of dicts with 'stream', 'bbox', 'name', 'y_pos' keys
|
|
34
|
+
"""
|
|
35
|
+
images_info = []
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
# Try multiple methods to detect images
|
|
39
|
+
images = []
|
|
40
|
+
|
|
41
|
+
# Method 1: Use page.images (standard approach)
|
|
42
|
+
if hasattr(page, "images") and page.images:
|
|
43
|
+
images = page.images
|
|
44
|
+
|
|
45
|
+
# Method 2: If no images found, try underlying PDF objects
|
|
46
|
+
if not images and hasattr(page, "objects") and "image" in page.objects:
|
|
47
|
+
images = page.objects.get("image", [])
|
|
48
|
+
|
|
49
|
+
# Method 3: Try filtering all objects for image types
|
|
50
|
+
if not images and hasattr(page, "objects"):
|
|
51
|
+
all_objs = page.objects
|
|
52
|
+
for obj_type in all_objs.keys():
|
|
53
|
+
if "image" in obj_type.lower() or "xobject" in obj_type.lower():
|
|
54
|
+
potential_imgs = all_objs.get(obj_type, [])
|
|
55
|
+
if potential_imgs:
|
|
56
|
+
images = potential_imgs
|
|
57
|
+
break
|
|
58
|
+
|
|
59
|
+
for i, img_dict in enumerate(images):
|
|
60
|
+
try:
|
|
61
|
+
# Try to get the actual image stream from the PDF
|
|
62
|
+
img_stream = None
|
|
63
|
+
y_pos = 0
|
|
64
|
+
|
|
65
|
+
# Method A: If img_dict has 'stream' key, use it directly
|
|
66
|
+
if "stream" in img_dict and hasattr(img_dict["stream"], "get_data"):
|
|
67
|
+
try:
|
|
68
|
+
img_bytes = img_dict["stream"].get_data()
|
|
69
|
+
|
|
70
|
+
# Try to open as PIL Image to validate/decode
|
|
71
|
+
pil_img = Image.open(io.BytesIO(img_bytes))
|
|
72
|
+
|
|
73
|
+
# Convert to RGB if needed (handle CMYK, etc.)
|
|
74
|
+
if pil_img.mode not in ("RGB", "L"):
|
|
75
|
+
pil_img = pil_img.convert("RGB")
|
|
76
|
+
|
|
77
|
+
# Save to stream as PNG
|
|
78
|
+
img_stream = io.BytesIO()
|
|
79
|
+
pil_img.save(img_stream, format="PNG")
|
|
80
|
+
img_stream.seek(0)
|
|
81
|
+
|
|
82
|
+
y_pos = img_dict.get("top", 0)
|
|
83
|
+
except Exception:
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
# Method B: Fallback to rendering page region
|
|
87
|
+
if img_stream is None:
|
|
88
|
+
x0 = img_dict.get("x0", 0)
|
|
89
|
+
y0 = img_dict.get("top", 0)
|
|
90
|
+
x1 = img_dict.get("x1", 0)
|
|
91
|
+
y1 = img_dict.get("bottom", 0)
|
|
92
|
+
y_pos = y0
|
|
93
|
+
|
|
94
|
+
# Check if dimensions are valid
|
|
95
|
+
if x1 <= x0 or y1 <= y0:
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
# Use pdfplumber's within_bbox to crop, then render
|
|
99
|
+
# This preserves coordinate system correctly
|
|
100
|
+
bbox = (x0, y0, x1, y1)
|
|
101
|
+
cropped_page = page.within_bbox(bbox)
|
|
102
|
+
|
|
103
|
+
# Render at 150 DPI (balance between quality and size)
|
|
104
|
+
page_img = cropped_page.to_image(resolution=150)
|
|
105
|
+
|
|
106
|
+
# Save to stream
|
|
107
|
+
img_stream = io.BytesIO()
|
|
108
|
+
page_img.original.save(img_stream, format="PNG")
|
|
109
|
+
img_stream.seek(0)
|
|
110
|
+
|
|
111
|
+
if img_stream:
|
|
112
|
+
images_info.append(
|
|
113
|
+
{
|
|
114
|
+
"stream": img_stream,
|
|
115
|
+
"name": f"page_{page.page_number}_img_{i}",
|
|
116
|
+
"y_pos": y_pos,
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
except Exception:
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
except Exception:
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
return images_info
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class PdfConverterWithOCR(DocumentConverter):
|
|
130
|
+
"""
|
|
131
|
+
Enhanced PDF Converter with OCR support for embedded images.
|
|
132
|
+
Maintains document structure while extracting text from images inline.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
def __init__(self, ocr_service: Optional[LLMVisionOCRService] = None):
|
|
136
|
+
super().__init__()
|
|
137
|
+
self.ocr_service = ocr_service
|
|
138
|
+
|
|
139
|
+
def accepts(
|
|
140
|
+
self,
|
|
141
|
+
file_stream: BinaryIO,
|
|
142
|
+
stream_info: StreamInfo,
|
|
143
|
+
**kwargs: Any,
|
|
144
|
+
) -> bool:
|
|
145
|
+
mimetype = (stream_info.mimetype or "").lower()
|
|
146
|
+
extension = (stream_info.extension or "").lower()
|
|
147
|
+
|
|
148
|
+
if extension == ".pdf":
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
if mimetype.startswith("application/pdf") or mimetype.startswith(
|
|
152
|
+
"application/x-pdf"
|
|
153
|
+
):
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
def convert(
|
|
159
|
+
self,
|
|
160
|
+
file_stream: BinaryIO,
|
|
161
|
+
stream_info: StreamInfo,
|
|
162
|
+
**kwargs: Any,
|
|
163
|
+
) -> DocumentConverterResult:
|
|
164
|
+
if _dependency_exc_info is not None:
|
|
165
|
+
raise MissingDependencyException(
|
|
166
|
+
MISSING_DEPENDENCY_MESSAGE.format(
|
|
167
|
+
converter=type(self).__name__,
|
|
168
|
+
extension=".pdf",
|
|
169
|
+
feature="pdf",
|
|
170
|
+
)
|
|
171
|
+
) from _dependency_exc_info[1].with_traceback(
|
|
172
|
+
_dependency_exc_info[2]
|
|
173
|
+
) # type: ignore[union-attr]
|
|
174
|
+
|
|
175
|
+
# Get OCR service if available (from kwargs or instance)
|
|
176
|
+
ocr_service: LLMVisionOCRService | None = (
|
|
177
|
+
kwargs.get("ocr_service") or self.ocr_service
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Read PDF into BytesIO
|
|
181
|
+
file_stream.seek(0)
|
|
182
|
+
pdf_bytes = io.BytesIO(file_stream.read())
|
|
183
|
+
|
|
184
|
+
markdown_content = []
|
|
185
|
+
|
|
186
|
+
try:
|
|
187
|
+
with pdfplumber.open(pdf_bytes) as pdf:
|
|
188
|
+
for page_num, page in enumerate(pdf.pages, 1):
|
|
189
|
+
markdown_content.append(f"\n## Page {page_num}\n")
|
|
190
|
+
|
|
191
|
+
# If OCR is enabled, interleave text and images by position
|
|
192
|
+
if ocr_service:
|
|
193
|
+
images_on_page = self._extract_page_images(pdf_bytes, page_num)
|
|
194
|
+
|
|
195
|
+
if images_on_page:
|
|
196
|
+
# Extract text lines with Y positions
|
|
197
|
+
chars = page.chars
|
|
198
|
+
if chars:
|
|
199
|
+
# Group chars into lines based on Y position
|
|
200
|
+
lines_with_y = []
|
|
201
|
+
current_line = []
|
|
202
|
+
current_y = None
|
|
203
|
+
|
|
204
|
+
for char in sorted(
|
|
205
|
+
chars, key=lambda c: (c["top"], c["x0"])
|
|
206
|
+
):
|
|
207
|
+
y = char["top"]
|
|
208
|
+
if current_y is None:
|
|
209
|
+
current_y = y
|
|
210
|
+
elif abs(y - current_y) > 2: # New line threshold
|
|
211
|
+
if current_line:
|
|
212
|
+
text = "".join(
|
|
213
|
+
[c["text"] for c in current_line]
|
|
214
|
+
)
|
|
215
|
+
lines_with_y.append(
|
|
216
|
+
{"y": current_y, "text": text.strip()}
|
|
217
|
+
)
|
|
218
|
+
current_line = []
|
|
219
|
+
current_y = y
|
|
220
|
+
current_line.append(char)
|
|
221
|
+
|
|
222
|
+
# Add last line
|
|
223
|
+
if current_line:
|
|
224
|
+
text = "".join([c["text"] for c in current_line])
|
|
225
|
+
lines_with_y.append(
|
|
226
|
+
{"y": current_y, "text": text.strip()}
|
|
227
|
+
)
|
|
228
|
+
else:
|
|
229
|
+
# Fallback: use simple text extraction
|
|
230
|
+
text_content = page.extract_text() or ""
|
|
231
|
+
lines_with_y = [
|
|
232
|
+
{"y": i * 10, "text": line}
|
|
233
|
+
for i, line in enumerate(text_content.split("\n"))
|
|
234
|
+
]
|
|
235
|
+
|
|
236
|
+
# OCR all images
|
|
237
|
+
image_data = []
|
|
238
|
+
for img_info in images_on_page:
|
|
239
|
+
ocr_result = ocr_service.extract_text(
|
|
240
|
+
img_info["stream"]
|
|
241
|
+
)
|
|
242
|
+
if ocr_result.text.strip():
|
|
243
|
+
image_data.append(
|
|
244
|
+
{
|
|
245
|
+
"y_pos": img_info["y_pos"],
|
|
246
|
+
"name": img_info["name"],
|
|
247
|
+
"ocr_text": ocr_result.text,
|
|
248
|
+
"backend": ocr_result.backend_used,
|
|
249
|
+
"type": "image",
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Add text items
|
|
254
|
+
content_items = [
|
|
255
|
+
{
|
|
256
|
+
"y_pos": item["y"],
|
|
257
|
+
"text": item["text"],
|
|
258
|
+
"type": "text",
|
|
259
|
+
}
|
|
260
|
+
for item in lines_with_y
|
|
261
|
+
if item["text"]
|
|
262
|
+
]
|
|
263
|
+
content_items.extend(image_data)
|
|
264
|
+
|
|
265
|
+
# Sort all items by Y position (top to bottom)
|
|
266
|
+
content_items.sort(key=lambda x: x["y_pos"])
|
|
267
|
+
|
|
268
|
+
# Build markdown by interleaving text and images
|
|
269
|
+
for item in content_items:
|
|
270
|
+
if item["type"] == "text":
|
|
271
|
+
markdown_content.append(item["text"])
|
|
272
|
+
else: # image
|
|
273
|
+
ocr_text = item["ocr_text"]
|
|
274
|
+
img_marker = (
|
|
275
|
+
f"\n\n*[Image OCR]\n{ocr_text}\n[End OCR]*\n"
|
|
276
|
+
)
|
|
277
|
+
markdown_content.append(img_marker)
|
|
278
|
+
else:
|
|
279
|
+
# No images detected - just extract regular text
|
|
280
|
+
text_content = page.extract_text() or ""
|
|
281
|
+
if text_content.strip():
|
|
282
|
+
markdown_content.append(text_content.strip())
|
|
283
|
+
else:
|
|
284
|
+
# No OCR, just extract text
|
|
285
|
+
text_content = page.extract_text() or ""
|
|
286
|
+
if text_content.strip():
|
|
287
|
+
markdown_content.append(text_content.strip())
|
|
288
|
+
|
|
289
|
+
# Build final markdown
|
|
290
|
+
markdown = "\n\n".join(markdown_content).strip()
|
|
291
|
+
|
|
292
|
+
# Fallback to pdfminer if empty
|
|
293
|
+
if not markdown:
|
|
294
|
+
pdf_bytes.seek(0)
|
|
295
|
+
markdown = pdfminer.high_level.extract_text(pdf_bytes)
|
|
296
|
+
|
|
297
|
+
except Exception:
|
|
298
|
+
# Fallback to pdfminer
|
|
299
|
+
try:
|
|
300
|
+
pdf_bytes.seek(0)
|
|
301
|
+
markdown = pdfminer.high_level.extract_text(pdf_bytes)
|
|
302
|
+
except Exception:
|
|
303
|
+
markdown = ""
|
|
304
|
+
|
|
305
|
+
# Final fallback: If still empty/whitespace and OCR is available,
|
|
306
|
+
# treat as scanned PDF and OCR full pages
|
|
307
|
+
if ocr_service and (not markdown or not markdown.strip()):
|
|
308
|
+
pdf_bytes.seek(0)
|
|
309
|
+
markdown = self._ocr_full_pages(pdf_bytes, ocr_service)
|
|
310
|
+
|
|
311
|
+
return DocumentConverterResult(markdown=markdown)
|
|
312
|
+
|
|
313
|
+
def _extract_page_images(self, pdf_bytes: io.BytesIO, page_num: int) -> list[dict]:
|
|
314
|
+
"""
|
|
315
|
+
Extract images from a PDF page using pdfplumber.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
pdf_bytes: PDF file as BytesIO
|
|
319
|
+
page_num: Page number (1-indexed)
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
List of image info dicts with 'stream', 'bbox', 'name', 'y_pos'
|
|
323
|
+
"""
|
|
324
|
+
images = []
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
pdf_bytes.seek(0)
|
|
328
|
+
with pdfplumber.open(pdf_bytes) as pdf:
|
|
329
|
+
if page_num <= len(pdf.pages):
|
|
330
|
+
page = pdf.pages[page_num - 1] # 0-indexed
|
|
331
|
+
images = _extract_images_from_page(page)
|
|
332
|
+
except Exception:
|
|
333
|
+
pass
|
|
334
|
+
|
|
335
|
+
# Sort by vertical position (top to bottom)
|
|
336
|
+
images.sort(key=lambda x: x["y_pos"])
|
|
337
|
+
|
|
338
|
+
return images
|
|
339
|
+
|
|
340
|
+
def _ocr_full_pages(
|
|
341
|
+
self, pdf_bytes: io.BytesIO, ocr_service: LLMVisionOCRService
|
|
342
|
+
) -> str:
|
|
343
|
+
"""
|
|
344
|
+
Fallback for scanned PDFs: Convert entire pages to images and OCR them.
|
|
345
|
+
Used when text extraction returns empty/whitespace results.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
pdf_bytes: PDF file as BytesIO
|
|
349
|
+
ocr_service: OCR service to use
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Markdown text extracted from OCR of full pages
|
|
353
|
+
"""
|
|
354
|
+
markdown_parts = []
|
|
355
|
+
|
|
356
|
+
try:
|
|
357
|
+
pdf_bytes.seek(0)
|
|
358
|
+
with pdfplumber.open(pdf_bytes) as pdf:
|
|
359
|
+
for page_num, page in enumerate(pdf.pages, 1):
|
|
360
|
+
try:
|
|
361
|
+
markdown_parts.append(f"\n## Page {page_num}\n")
|
|
362
|
+
|
|
363
|
+
# Render page to image
|
|
364
|
+
page_img = page.to_image(resolution=300)
|
|
365
|
+
img_stream = io.BytesIO()
|
|
366
|
+
page_img.original.save(img_stream, format="PNG")
|
|
367
|
+
img_stream.seek(0)
|
|
368
|
+
|
|
369
|
+
# Run OCR
|
|
370
|
+
ocr_result = ocr_service.extract_text(img_stream)
|
|
371
|
+
|
|
372
|
+
if ocr_result.text.strip():
|
|
373
|
+
text = ocr_result.text.strip()
|
|
374
|
+
markdown_parts.append(f"*[Image OCR]\n{text}\n[End OCR]*")
|
|
375
|
+
else:
|
|
376
|
+
markdown_parts.append(
|
|
377
|
+
"*[No text could be extracted from this page]*"
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
except Exception as e:
|
|
381
|
+
markdown_parts.append(
|
|
382
|
+
f"*[Error processing page {page_num}: {str(e)}]*"
|
|
383
|
+
)
|
|
384
|
+
continue
|
|
385
|
+
|
|
386
|
+
except Exception:
|
|
387
|
+
# pdfplumber failed (e.g. malformed EOF) — try PyMuPDF for rendering
|
|
388
|
+
markdown_parts = []
|
|
389
|
+
try:
|
|
390
|
+
import fitz # PyMuPDF
|
|
391
|
+
|
|
392
|
+
pdf_bytes.seek(0)
|
|
393
|
+
doc = fitz.open(stream=pdf_bytes.read(), filetype="pdf")
|
|
394
|
+
for page_num in range(1, doc.page_count + 1):
|
|
395
|
+
try:
|
|
396
|
+
markdown_parts.append(f"\n## Page {page_num}\n")
|
|
397
|
+
page = doc[page_num - 1]
|
|
398
|
+
mat = fitz.Matrix(300 / 72, 300 / 72) # 300 DPI
|
|
399
|
+
pix = page.get_pixmap(matrix=mat)
|
|
400
|
+
img_stream = io.BytesIO(pix.tobytes("png"))
|
|
401
|
+
img_stream.seek(0)
|
|
402
|
+
|
|
403
|
+
ocr_result = ocr_service.extract_text(img_stream)
|
|
404
|
+
|
|
405
|
+
if ocr_result.text.strip():
|
|
406
|
+
text = ocr_result.text.strip()
|
|
407
|
+
markdown_parts.append(f"*[Image OCR]\n{text}\n[End OCR]*")
|
|
408
|
+
else:
|
|
409
|
+
markdown_parts.append(
|
|
410
|
+
"*[No text could be extracted from this page]*"
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
except Exception as e:
|
|
414
|
+
markdown_parts.append(
|
|
415
|
+
f"*[Error processing page {page_num}: {str(e)}]*"
|
|
416
|
+
)
|
|
417
|
+
continue
|
|
418
|
+
doc.close()
|
|
419
|
+
except Exception:
|
|
420
|
+
return "*[Error: Could not process scanned PDF]*"
|
|
421
|
+
|
|
422
|
+
return "\n\n".join(markdown_parts).strip()
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Plugin registration for markitdown-ocr.
|
|
3
|
+
Registers OCR-enhanced converters with priority-based replacement strategy.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
from markitdown import MarkItDown
|
|
8
|
+
|
|
9
|
+
from ._ocr_service import LLMVisionOCRService
|
|
10
|
+
from ._pdf_converter_with_ocr import PdfConverterWithOCR
|
|
11
|
+
from ._docx_converter_with_ocr import DocxConverterWithOCR
|
|
12
|
+
from ._pptx_converter_with_ocr import PptxConverterWithOCR
|
|
13
|
+
from ._xlsx_converter_with_ocr import XlsxConverterWithOCR
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__plugin_interface_version__ = 1
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def register_converters(markitdown: MarkItDown, **kwargs: Any) -> None:
|
|
20
|
+
"""
|
|
21
|
+
Register OCR-enhanced converters with MarkItDown.
|
|
22
|
+
|
|
23
|
+
This plugin provides OCR support for PDF, DOCX, PPTX, and XLSX files.
|
|
24
|
+
The converters are registered with priority -1.0 to run BEFORE built-in
|
|
25
|
+
converters (which have priority 0.0), effectively replacing them when
|
|
26
|
+
the plugin is enabled.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
markitdown: MarkItDown instance to register converters with
|
|
30
|
+
**kwargs: Additional keyword arguments that may include:
|
|
31
|
+
- llm_client: OpenAI-compatible client for LLM-based OCR (required for OCR to work)
|
|
32
|
+
- llm_model: Model name (e.g., 'gpt-4o')
|
|
33
|
+
- llm_prompt: Custom prompt for text extraction
|
|
34
|
+
"""
|
|
35
|
+
# Create OCR service — reads the same llm_client/llm_model kwargs
|
|
36
|
+
# that MarkItDown itself already accepts for image descriptions
|
|
37
|
+
llm_client = kwargs.get("llm_client")
|
|
38
|
+
llm_model = kwargs.get("llm_model")
|
|
39
|
+
llm_prompt = kwargs.get("llm_prompt")
|
|
40
|
+
|
|
41
|
+
ocr_service: LLMVisionOCRService | None = None
|
|
42
|
+
if llm_client and llm_model:
|
|
43
|
+
ocr_service = LLMVisionOCRService(
|
|
44
|
+
client=llm_client,
|
|
45
|
+
model=llm_model,
|
|
46
|
+
default_prompt=llm_prompt,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Register converters with priority -1.0 (before built-ins at 0.0)
|
|
50
|
+
# This effectively "replaces" the built-in converters when plugin is installed
|
|
51
|
+
# Pass the OCR service to each converter's constructor
|
|
52
|
+
PRIORITY_OCR_ENHANCED = -1.0
|
|
53
|
+
|
|
54
|
+
markitdown.register_converter(
|
|
55
|
+
PdfConverterWithOCR(ocr_service=ocr_service), priority=PRIORITY_OCR_ENHANCED
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
markitdown.register_converter(
|
|
59
|
+
DocxConverterWithOCR(ocr_service=ocr_service), priority=PRIORITY_OCR_ENHANCED
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
markitdown.register_converter(
|
|
63
|
+
PptxConverterWithOCR(ocr_service=ocr_service), priority=PRIORITY_OCR_ENHANCED
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
markitdown.register_converter(
|
|
67
|
+
XlsxConverterWithOCR(ocr_service=ocr_service), priority=PRIORITY_OCR_ENHANCED
|
|
68
|
+
)
|