-
Notifications
You must be signed in to change notification settings - Fork 735
Expand file tree
/
Copy pathtest_insertpdf.py
More file actions
316 lines (268 loc) · 10.7 KB
/
test_insertpdf.py
File metadata and controls
316 lines (268 loc) · 10.7 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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
"""
* Join multiple PDFs into a new one.
* Compare with stored earlier result:
- must have identical object definitions
- must have different trailers
* Try inserting files in a loop.
"""
import io
import os
import re
import pymupdf
from pymupdf import mupdf
scriptdir = os.path.abspath(os.path.dirname(__file__))
resources = os.path.join(scriptdir, "resources")
def approx_parse( text):
'''
Splits <text> into sequence of (text, number) pairs. Where sequence of
[0-9.] is not convertible to a number (e.g. '4.5.6'), <number> will be
None.
'''
ret = []
for m in re.finditer('([^0-9]+)([0-9.]*)', text):
text = m.group(1)
try:
number = float( m.group(2))
except Exception:
text += m.group(2)
number = None
ret.append( (text, number))
return ret
def approx_compare( a, b, max_delta):
'''
Compares <a> and <b>, allowing numbers to differ by up to <delta>.
'''
aa = approx_parse( a)
bb = approx_parse( b)
if len(aa) != len(bb):
return 1
ret = 1
for (at, an), (bt, bn) in zip( aa, bb):
if at != bt:
break
if an is not None and bn is not None:
if abs( an - bn) >= max_delta:
print( f'diff={an-bn}: an={an} bn={bn}')
break
elif (an is None) != (bn is None):
break
else:
ret = 0
if ret:
print( f'Differ:\n a={a!r}\n b={b!r}')
return ret
def test_insert():
all_text_original = [] # text on input pages
all_text_combined = [] # text on resulting output pages
# prepare input PDFs
doc1 = pymupdf.open()
for i in range(5): # just arbitrary number of pages
text = f"doc 1, page {i}" # the 'globally' unique text
page = doc1.new_page()
page.insert_text((100, 72), text)
all_text_original.append(text)
doc2 = pymupdf.open()
for i in range(4):
text = f"doc 2, page {i}"
page = doc2.new_page()
page.insert_text((100, 72), text)
all_text_original.append(text)
doc3 = pymupdf.open()
for i in range(3):
text = f"doc 3, page {i}"
page = doc3.new_page()
page.insert_text((100, 72), text)
all_text_original.append(text)
doc4 = pymupdf.open()
for i in range(6):
text = f"doc 4, page {i}"
page = doc4.new_page()
page.insert_text((100, 72), text)
all_text_original.append(text)
new_doc = pymupdf.open() # make combined PDF of input files
new_doc.insert_pdf(doc1)
new_doc.insert_pdf(doc2)
new_doc.insert_pdf(doc3)
new_doc.insert_pdf(doc4)
# read text from all pages and store in list
for page in new_doc:
all_text_combined.append(page.get_text().replace("\n", ""))
# the lists must be equal
assert all_text_combined == all_text_original
def test_issue1417_insertpdf_in_loop():
"""Using a context manager instead of explicitly closing files"""
f = os.path.join(resources, "1.pdf")
big_doc = pymupdf.open()
fd1 = os.open( f, os.O_RDONLY)
os.close( fd1)
for n in range(0, 1025):
with pymupdf.open(f) as pdf:
big_doc.insert_pdf(pdf)
# Create a raw file descriptor. If the above pymupdf.open() context leaks
# a file descriptor, fd will be seen to increment.
fd2 = os.open( f, os.O_RDONLY)
assert fd2 == fd1
os.close( fd2)
big_doc.close()
def _test_insert_adobe():
path = os.path.abspath( f'{__file__}/../../../PyMuPDF-performance/adobe.pdf')
if not os.path.exists(path):
print(f'Not running test_insert_adobe() because does not exist: {os.path.relpath(path)}')
return
a = pymupdf.Document()
b = pymupdf.Document(path)
a.insert_pdf(b)
def _2861_2871_merge_pdf(content: bytes, coverpage: bytes):
with pymupdf.Document(stream=coverpage, filetype="pdf") as coverpage_pdf:
with pymupdf.Document(stream=content, filetype="pdf") as content_pdf:
coverpage_pdf.insert_pdf(content_pdf)
doc = coverpage_pdf.write()
return doc
def test_2861():
path = os.path.abspath(f'{__file__}/../../tests/resources/test_2861.pdf')
with open(path, "rb") as content_pdf:
with open(path, "rb") as coverpage_pdf:
content = content_pdf.read()
coverpage = coverpage_pdf.read()
_2861_2871_merge_pdf(content, coverpage)
def test_2871():
path = os.path.abspath(f'{__file__}/../../tests/resources/test_2871.pdf')
with open(path, "rb") as content_pdf:
with open(path, "rb") as coverpage_pdf:
content = content_pdf.read()
coverpage = coverpage_pdf.read()
_2861_2871_merge_pdf(content, coverpage)
def test_3789():
file_path = os.path.abspath(f'{__file__}/../../tests/resources/test_3789.pdf')
result_path = os.path.abspath(f'{__file__}/../../tests/test_3789_out')
pages_per_split = 5
# Clean pdf
doc = pymupdf.open(file_path)
tmp = io.BytesIO()
tmp.write(doc.write(garbage=4, deflate=True))
source_doc = pymupdf.Document('pdf', tmp.getvalue())
tmp.close()
# Calculate the number of pages per split file and the number of split files
page_range = pages_per_split - 1
split_range = range(0, source_doc.page_count, pages_per_split)
num_splits = len(split_range)
# Loop through each split range and create a new PDF file
for i, start in enumerate(split_range):
output_doc = pymupdf.open()
# Determine the ending page for this split file
to_page = start + page_range if i < num_splits - 1 else -1
output_doc.insert_pdf(source_doc, from_page=start, to_page=to_page)
# Save the output document to a file and add the path to the list of split files
path = f'{result_path}_{i}.pdf'
output_doc.save(path, garbage=2)
print(f'Have saved to {path=}.')
# If this is the last split file, exit the loop
if to_page == -1:
break
def test_widget_insert():
"""Confirm copy of form fields / widgets."""
tar = pymupdf.open(os.path.join(resources, "merge-form1.pdf"))
pc0 = tar.page_count # for later assertion
src = pymupdf.open(os.path.join(resources, "interfield-calculation.pdf"))
pc1 = src.page_count # for later assertion
tarpdf = pymupdf._as_pdf_document(tar)
tar_field_count = mupdf.pdf_array_len(
mupdf.pdf_dict_getp(mupdf.pdf_trailer(tarpdf), "Root/AcroForm/Fields")
)
tar_co_count = mupdf.pdf_array_len(
mupdf.pdf_dict_getp(mupdf.pdf_trailer(tarpdf), "Root/AcroForm/CO")
)
srcpdf = pymupdf._as_pdf_document(src)
src_field_count = mupdf.pdf_array_len(
mupdf.pdf_dict_getp(mupdf.pdf_trailer(srcpdf), "Root/AcroForm/Fields")
)
src_co_count = mupdf.pdf_array_len(
mupdf.pdf_dict_getp(mupdf.pdf_trailer(srcpdf), "Root/AcroForm/CO")
)
tar.insert_pdf(src)
new_field_count = mupdf.pdf_array_len(
mupdf.pdf_dict_getp(mupdf.pdf_trailer(tarpdf), "Root/AcroForm/Fields")
)
new_co_count = mupdf.pdf_array_len(
mupdf.pdf_dict_getp(mupdf.pdf_trailer(tarpdf), "Root/AcroForm/CO")
)
assert tar.page_count == pc0 + pc1
assert new_field_count == tar_field_count + src_field_count
assert new_co_count == tar_co_count + src_co_count
def names_and_kids(doc):
"""Return a list of dictionaries with keys "name" and "kids".
"name" is the name of a root field in "Root/AcroForm/Fields", and
"kids" is the count of its immediate children.
"""
rc = []
pdf = pymupdf._as_pdf_document(doc)
fields = mupdf.pdf_dict_getl(
mupdf.pdf_trailer(pdf),
pymupdf.PDF_NAME("Root"),
pymupdf.PDF_NAME("AcroForm"),
pymupdf.PDF_NAME("Fields"),
)
if not fields.pdf_is_array():
return rc
root_count = fields.pdf_array_len()
if not root_count:
return rc
for i in range(root_count):
field = fields.pdf_array_get(i)
kids = field.pdf_dict_get(pymupdf.PDF_NAME("Kids"))
kid_count = kids.pdf_array_len()
T = field.pdf_dict_get_text_string(pymupdf.PDF_NAME("T"))
field_dict = {"name": T, "kids": kid_count}
rc.append(field_dict)
return rc
def test_merge_checks1():
"""Merge Form PDFs making any duplicate names unique."""
merge_file1 = os.path.join(resources, "merge-form1.pdf")
merge_file2 = os.path.join(resources, "merge-form2.pdf")
tar = pymupdf.open(merge_file1)
rc0 = names_and_kids(tar)
src = pymupdf.open(merge_file2)
rc1 = names_and_kids(src)
tar.insert_pdf(src, join_duplicates=False)
rc2 = names_and_kids(tar)
assert len(rc2) == len(rc0) + len(rc1)
def test_merge_checks2():
# Join / merge Form PDFs joining any duplicate names in the src PDF.
merge_file1 = os.path.join(resources, "merge-form1.pdf")
merge_file2 = os.path.join(resources, "merge-form2.pdf")
tar = pymupdf.open(merge_file1)
rc0 = names_and_kids(tar) # list of root names and kid counts
names0 = [itm["name"] for itm in rc0] # root names in target
kids0 = sum([itm["kids"] for itm in rc0]) # number of kids in target
src = pymupdf.open(merge_file2)
rc1 = names_and_kids(src) # list of root namesand kids in source PDF
dup_count = 0 # counts duplicate names in source PDF
dup_kids = 0 # counts the expected kids after merge
for itm in rc1: # walk root fields of source pdf
if itm["name"] not in names0: # not a duplicate name
continue
# if target field has kids, add their count, else add 1
dup_kids0 = sum([i["kids"] for i in rc0 if i["name"] == itm["name"]])
dup_kids += dup_kids0 if dup_kids0 else 1
# if source field has kids add their count, else add 1
dup_kids += itm["kids"] if itm["kids"] else 1
names1 = [itm["name"] for itm in rc1] # names in source
tar.insert_pdf(src, join_duplicates=True) # join merging any duplicate names
rc2 = names_and_kids(tar) # get names and kid counts in resulting PDF
names2 = [itm["name"] for itm in rc2] # resulting names in target
kids2 = sum([itm["kids"] for itm in rc2]) # total resulting kid count
assert len(set(names0 + names1)) == len(names2)
assert kids2 == dup_kids
test_4412_path = os.path.normpath(f'{__file__}/../../tests/resources/test_4412.pdf')
def test_4412():
# This tests whether a page from a PDF containing widgets found in the wild
# can be inserted into a new document with default options (widget=True)
# and widget=False.
print()
for widget in True, False:
print(f'{widget=}', flush=1)
with pymupdf.open(test_4412_path) as doc, pymupdf.open() as new_doc:
buf = io.BytesIO()
new_doc.insert_pdf(doc, from_page=1, to_page=1)
new_doc.save(buf)
assert len(new_doc)==1