forked from pymupdf/pymupdf4llm
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path__init__.py
More file actions
295 lines (261 loc) · 8.04 KB
/
Copy path__init__.py
File metadata and controls
295 lines (261 loc) · 8.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
import pathlib
import pymupdf
from .versions_file import VERSION, VERSION_TUPLE
import pymupdf4llm.helpers.pymupdf_rag
import pymupdf4llm.helpers.document_layout
_pvt = tuple(map(int, pymupdf.__version__.split(".")))
if _pvt != VERSION_TUPLE:
raise ImportError(
f"Requires PyMuPDF {VERSION=} {VERSION_TUPLE=}, but you have {pymupdf.__version__=} {_pvt=}"
)
__version__ = VERSION
version = VERSION
version_tuple = tuple(map(int, version.split(".")))
def use_layout(yes):
global _use_layout
global IdentifyHeaders
global TocHeaders
_use_layout = yes
if _use_layout:
# IdentifyHeaders and TocHeaders are not available.
try:
del IdentifyHeaders
except Exception:
pass
try:
del TocHeaders
except Exception:
pass
import pymupdf.layout
pymupdf.layout.activate()
else:
IdentifyHeaders = pymupdf4llm.helpers.pymupdf_rag.IdentifyHeaders
TocHeaders = pymupdf4llm.helpers.pymupdf_rag.TocHeaders
import pymupdf
pymupdf._get_layout = None
# Always attempt to use Layout by default.
try:
import pymupdf.layout
except ImportError as e:
use_layout(False)
else:
use_layout(True)
def _layout_to_markdown(
doc,
*,
dpi=150,
embed_images=False,
filename="",
footer=True,
force_ocr=False,
force_text=True,
header=True,
ignore_code=False,
image_format="png",
image_path="",
ocr_dpi=300,
ocr_function=None,
ocr_language="eng",
page_chunks=False,
page_height=None,
page_separators=False,
pages=None,
page_width=612,
show_progress=False,
use_ocr=True,
write_images=False,
render_html_tables=None,
edge_threshold=None,
# unsupported options for pymupdf layout:
**kwargs,
):
if write_images and embed_images:
raise ValueError("Cannot both write_images and embed_images")
parsed_doc = pymupdf4llm.helpers.document_layout.parse_document(
doc,
filename=filename,
image_dpi=dpi,
image_format=image_format,
image_path=image_path,
pages=pages,
ocr_dpi=ocr_dpi,
write_images=write_images,
embed_images=embed_images,
show_progress=show_progress,
force_text=force_text,
use_ocr=use_ocr,
force_ocr=force_ocr,
ocr_language=ocr_language,
ocr_function=ocr_function,
render_html_tables=render_html_tables,
edge_threshold=edge_threshold,
)
return parsed_doc.to_markdown(
header=header,
footer=footer,
write_images=write_images,
embed_images=embed_images,
ignore_code=ignore_code,
show_progress=show_progress,
page_separators=page_separators,
page_chunks=page_chunks,
)
def _layout_to_json(
doc,
image_dpi=150,
image_format="png",
image_path="",
pages=None,
ocr_dpi=300,
write_images=False,
embed_images=False,
show_progress=False,
force_text=True,
use_ocr=True,
force_ocr=False,
ocr_language="eng",
ocr_function=None,
render_html_tables=None,
edge_threshold=None,
# unsupported options for pymupdf layout:
**kwargs,
):
parsed_doc = pymupdf4llm.helpers.document_layout.parse_document(
doc,
image_dpi=image_dpi,
image_format=image_format,
image_path=image_path,
pages=pages,
embed_images=embed_images,
write_images=write_images,
show_progress=show_progress,
force_text=force_text,
use_ocr=use_ocr,
force_ocr=force_ocr,
ocr_language=ocr_language,
ocr_function=ocr_function,
render_html_tables=render_html_tables,
edge_threshold=edge_threshold,
)
return parsed_doc.to_json()
def _layout_to_text(
doc,
filename="",
header=True,
footer=True,
pages=None,
ignore_code=False,
show_progress=False,
force_text=True,
ocr_dpi=300,
use_ocr=True,
force_ocr=False,
ocr_language="eng",
ocr_function=None,
table_format="grid",
table_max_width=100,
table_min_col_width=10,
page_chunks=False,
edge_threshold=None,
# unsupported options for pymupdf layout:
**kwargs,
):
parsed_doc = pymupdf4llm.helpers.document_layout.parse_document(
doc,
filename=filename,
pages=pages,
embed_images=False,
write_images=False,
show_progress=show_progress,
force_text=force_text,
use_ocr=use_ocr,
force_ocr=force_ocr,
ocr_language=ocr_language,
ocr_function=ocr_function,
edge_threshold=edge_threshold,
)
return parsed_doc.to_text(
header=header,
footer=footer,
ignore_code=ignore_code,
show_progress=show_progress,
table_format=table_format,
table_max_width=table_max_width,
table_min_col_width=table_min_col_width,
page_chunks=page_chunks,
)
def to_markdown(*args, **kwargs):
# `render_html_tables` is an internal flag this wrapper injects for
# table_output="html"; it is not a public kwarg. Drop any user-supplied value
# so it cannot silently enable/disable HTML tables via **kwargs.
kwargs.pop("render_html_tables", None)
if kwargs.get("table_output") == "html":
# Render tables as HTML via table_html.
kwargs = dict(kwargs)
kwargs.pop("table_output", None)
if _use_layout:
# Preferred path: render HTML tables on the layout path, reusing the
# GNN layout. Keeps the layout path's text, reading order, and OCR --
# only the table rendering is swapped.
return _layout_to_markdown(*args, render_html_tables=True, **kwargs)
# No layout engine available: fall back to the rag path, which has its own
# table_output="html" wiring. OCR is not available on this path.
legacy_kwargs = dict(kwargs)
for name in (
"footer",
"header",
"ocr_dpi",
"ocr_function",
"ocr_language",
"use_ocr",
"force_ocr",
):
legacy_kwargs.pop(name, None)
return pymupdf4llm.helpers.pymupdf_rag.to_markdown(
*args, table_output="html", **legacy_kwargs
)
if _use_layout:
return _layout_to_markdown(*args, **kwargs)
else:
return pymupdf4llm.helpers.pymupdf_rag.to_markdown(*args, **kwargs)
def to_json(*args, **kwargs):
# See to_markdown: `render_html_tables` is internal, not a public kwarg.
kwargs.pop("render_html_tables", None)
if kwargs.get("table_output") == "html":
kwargs = dict(kwargs)
kwargs.pop("table_output", None)
kwargs["render_html_tables"] = True
if _use_layout:
return _layout_to_json(*args, **kwargs)
else:
return pymupdf4llm.helpers.pymupdf_rag.to_json(*args, **kwargs)
def to_text(*args, **kwargs):
if _use_layout:
return _layout_to_text(*args, **kwargs)
else:
return pymupdf4llm.helpers.pymupdf_rag.to_text(*args, **kwargs)
def get_key_values(doc, xrefs=False, **kwargs):
"""Extract form fields and their values from a PDF document.
Args:
doc: A file path to a PDF document or a pymupdf.Document object.
xrefs: If True, include the xref numbers of the form fields in the output.
The xrefs can be useful to directly load a widget via Page.load_widget(xref).
**kwargs: Additional keyword arguments (currently ignored).
"""
from .helpers import utils
if kwargs:
print(f"Warning: keyword arguments ignored: {set(kwargs.keys())}")
if isinstance(doc, pymupdf.Document):
mydoc = doc
else:
mydoc = pymupdf.open(doc)
if mydoc.is_form_pdf:
rc = utils.extract_form_fields_with_pages(mydoc, xrefs=xrefs)
else:
rc = {}
if mydoc != doc:
mydoc.close()
return rc
def LlamaMarkdownReader(*args, **kwargs):
from .llama import pdf_markdown_reader
return pdf_markdown_reader.PDFMarkdownReader(*args, **kwargs)