PyPDFForm 5.2.3__tar.gz → 5.2.4__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.
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PKG-INFO +1 -1
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/__init__.py +1 -1
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/egress.py +72 -9
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/utils.py +58 -1
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/wrapper.py +44 -18
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm.egg-info/PKG-INFO +1 -1
- {pypdfform-5.2.3 → pypdfform-5.2.4}/pyproject.toml +1 -1
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_bulk_create_fields.py +2 -2
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_create_widget.py +16 -16
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_draw_elements.py +2 -2
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_dropdown.py +2 -2
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_functional.py +37 -13
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_generate_appearance_streams.py +27 -28
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_need_appearances.py +5 -6
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_paragraph.py +2 -2
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_signature.py +3 -3
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_widget_attr_trigger.py +3 -3
- {pypdfform-5.2.3 → pypdfform-5.2.4}/LICENSE +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/cli/__init__.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/cli/common.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/cli/create.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/cli/entry.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/cli/inspect.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/cli/remove.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/cli/root.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/cli/schemas/__init__.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/cli/schemas/create.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/cli/schemas/update.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/cli/update.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/__init__.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/adapter.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/annotations/__init__.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/annotations/base.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/annotations/link.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/annotations/stamp.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/annotations/text.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/annotations/text_markup.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/assets/__init__.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/assets/bedrock.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/assets/blank.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/constants.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/coordinate.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/deprecation.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/filler.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/font.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/hooks.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/image.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/middleware/__init__.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/middleware/base.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/middleware/checkbox.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/middleware/dropdown.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/middleware/image.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/middleware/radio.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/middleware/signature.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/middleware/text.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/patterns.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/raw/__init__.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/raw/circle.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/raw/ellipse.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/raw/image.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/raw/line.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/raw/rect.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/raw/text.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/template.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/types.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/watermark.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/widgets/__init__.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/widgets/base.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/widgets/checkbox.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/widgets/dropdown.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/widgets/image.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/widgets/radio.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/widgets/signature.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm/lib/widgets/text.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm.egg-info/SOURCES.txt +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm.egg-info/dependency_links.txt +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm.egg-info/entry_points.txt +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm.egg-info/requires.txt +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/PyPDFForm.egg-info/top_level.txt +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/README.md +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/setup.cfg +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_extract_middleware_attributes.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_fill_max_length_text_field.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_font_widths.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_js.py +0 -0
- {pypdfform-5.2.3 → pypdfform-5.2.4}/tests/test_use_full_widget_name.py +0 -0
|
@@ -6,19 +6,33 @@ This module provides functionalities that prepare the final PDF for output (egre
|
|
|
6
6
|
ensuring that it is properly formatted and ready for the end-user. This includes
|
|
7
7
|
managing appearance streams (so form fields display correctly after being filled),
|
|
8
8
|
handling the /NeedAppearances flag, and preserving or updating document-level
|
|
9
|
-
properties like metadata, title, and OpenAction scripts.
|
|
10
|
-
|
|
9
|
+
properties like metadata, title, and OpenAction scripts. It can also rebuild the
|
|
10
|
+
AcroForm `/Fields` array from the widget annotations present on each page. These
|
|
11
|
+
functions are typically called right before the final PDF byte stream is returned by
|
|
12
|
+
the wrapper module.
|
|
11
13
|
"""
|
|
12
14
|
|
|
13
15
|
from functools import lru_cache
|
|
14
16
|
from io import BytesIO
|
|
15
|
-
from warnings import catch_warnings,
|
|
17
|
+
from warnings import catch_warnings, filterwarnings
|
|
16
18
|
|
|
17
19
|
from pikepdf import Pdf
|
|
18
20
|
from pypdf import PdfReader, PdfWriter
|
|
19
|
-
from pypdf.generic import DictionaryObject, NameObject, TextStringObject
|
|
20
|
-
|
|
21
|
-
from .constants import
|
|
21
|
+
from pypdf.generic import ArrayObject, DictionaryObject, NameObject, TextStringObject
|
|
22
|
+
|
|
23
|
+
from .constants import (
|
|
24
|
+
JS,
|
|
25
|
+
XFA,
|
|
26
|
+
AcroForm,
|
|
27
|
+
Annots,
|
|
28
|
+
Fields,
|
|
29
|
+
JavaScript,
|
|
30
|
+
OpenAction,
|
|
31
|
+
Root,
|
|
32
|
+
S,
|
|
33
|
+
Title,
|
|
34
|
+
)
|
|
35
|
+
from .template import get_widget_key
|
|
22
36
|
|
|
23
37
|
|
|
24
38
|
@lru_cache(maxsize=128)
|
|
@@ -58,12 +72,14 @@ def appearance_streams_handler(pdf: bytes, generate_appearance_streams: bool) ->
|
|
|
58
72
|
result = f.read()
|
|
59
73
|
|
|
60
74
|
if generate_appearance_streams:
|
|
61
|
-
# TODO: remove after fixing /Annots /Fields mismatch
|
|
62
75
|
with Pdf.open(BytesIO(result)) as f, catch_warnings():
|
|
63
|
-
|
|
76
|
+
filterwarnings(
|
|
77
|
+
"ignore", message=".*/AcroForm.*"
|
|
78
|
+
) # handled by rebuild_acroform_fields
|
|
79
|
+
|
|
64
80
|
f.generate_appearance_streams()
|
|
65
81
|
with BytesIO() as r:
|
|
66
|
-
f.save(r)
|
|
82
|
+
f.save(r, deterministic_id=True)
|
|
67
83
|
r.seek(0)
|
|
68
84
|
result = r.read()
|
|
69
85
|
|
|
@@ -114,3 +130,50 @@ def preserve_pdf_properties(
|
|
|
114
130
|
writer.write(f)
|
|
115
131
|
f.seek(0)
|
|
116
132
|
return f.read()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def rebuild_acroform_fields(
|
|
136
|
+
pdf: bytes, widget_keys: set, use_full_widget_name: bool
|
|
137
|
+
) -> bytes:
|
|
138
|
+
"""
|
|
139
|
+
Rebuilds the AcroForm `/Fields` array from matching page annotations.
|
|
140
|
+
|
|
141
|
+
The existing `/Fields` array is replaced, creating an AcroForm dictionary
|
|
142
|
+
when necessary. Each page annotation is resolved to a widget key, and only
|
|
143
|
+
annotations whose keys are present in `widget_keys` are added to the new
|
|
144
|
+
array. Page annotation arrays are left unchanged. When no matching page
|
|
145
|
+
annotations are found, the original PDF stream is returned unchanged to
|
|
146
|
+
avoid an unnecessary rewrite.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
pdf (bytes): The PDF stream whose AcroForm fields should be rebuilt.
|
|
150
|
+
widget_keys (set): Widget keys to include in the rebuilt `/Fields` array.
|
|
151
|
+
use_full_widget_name (bool): Whether to resolve annotations using their
|
|
152
|
+
full widget names, including parent names.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
bytes: The PDF stream with a rebuilt AcroForm `/Fields` array, or the
|
|
156
|
+
original stream when there are no matching widgets to rebuild.
|
|
157
|
+
"""
|
|
158
|
+
writer = PdfWriter(BytesIO(pdf))
|
|
159
|
+
root = writer._root_object # type: ignore # noqa: SLF001 # # pylint: disable=W0212
|
|
160
|
+
|
|
161
|
+
if AcroForm not in root:
|
|
162
|
+
root[NameObject(AcroForm)] = DictionaryObject({})
|
|
163
|
+
root[AcroForm][NameObject(Fields)] = ArrayObject([])
|
|
164
|
+
|
|
165
|
+
needs_update = False
|
|
166
|
+
for page in writer.pages:
|
|
167
|
+
for annot in page.get(Annots, []):
|
|
168
|
+
key = get_widget_key(annot.get_object(), use_full_widget_name)
|
|
169
|
+
if key in widget_keys:
|
|
170
|
+
root[AcroForm][Fields].append(annot)
|
|
171
|
+
needs_update = True
|
|
172
|
+
|
|
173
|
+
if not needs_update:
|
|
174
|
+
return pdf
|
|
175
|
+
|
|
176
|
+
with BytesIO() as f:
|
|
177
|
+
writer.write(f)
|
|
178
|
+
f.seek(0)
|
|
179
|
+
return f.read()
|
|
@@ -21,7 +21,13 @@ from typing import Any, List
|
|
|
21
21
|
from pypdf import PdfReader, PdfWriter
|
|
22
22
|
from pypdf.generic import ArrayObject, DictionaryObject, NameObject
|
|
23
23
|
|
|
24
|
-
from .constants import
|
|
24
|
+
from .constants import (
|
|
25
|
+
SLASH,
|
|
26
|
+
UNIQUE_SUFFIX_LENGTH,
|
|
27
|
+
VERSION_IDENTIFIER_PREFIX,
|
|
28
|
+
VERSION_IDENTIFIERS,
|
|
29
|
+
Annots,
|
|
30
|
+
)
|
|
25
31
|
|
|
26
32
|
|
|
27
33
|
@lru_cache(maxsize=128)
|
|
@@ -329,3 +335,54 @@ def generate_unique_suffix() -> str:
|
|
|
329
335
|
for _ in range(UNIQUE_SUFFIX_LENGTH)
|
|
330
336
|
]
|
|
331
337
|
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def get_version(pdf: bytes) -> str | None:
|
|
341
|
+
"""
|
|
342
|
+
Extracts the PDF header version from a byte stream.
|
|
343
|
+
|
|
344
|
+
The stream is checked against the supported PDF header identifiers defined
|
|
345
|
+
in constants. The function inspects the original bytes directly so callers
|
|
346
|
+
can capture a document's version before handing it to tools that may rewrite
|
|
347
|
+
the header during output processing.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
pdf (bytes): The PDF stream to inspect.
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
str | None: The PDF version string, or None when the stream does not
|
|
354
|
+
start with a known PDF version identifier.
|
|
355
|
+
"""
|
|
356
|
+
|
|
357
|
+
result = None
|
|
358
|
+
for each in VERSION_IDENTIFIERS:
|
|
359
|
+
if pdf.startswith(each):
|
|
360
|
+
result = each.replace(VERSION_IDENTIFIER_PREFIX, b"").decode()
|
|
361
|
+
break
|
|
362
|
+
|
|
363
|
+
return result
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def set_version(pdf: bytes, old: str, new: str) -> bytes:
|
|
367
|
+
"""
|
|
368
|
+
Replaces the first PDF header version marker in a byte stream.
|
|
369
|
+
|
|
370
|
+
This helper only changes the literal header marker, such as `%PDF-1.7`; it
|
|
371
|
+
does not validate or rewrite the document for version-specific feature
|
|
372
|
+
compatibility. It is used after egress rewrites so output keeps the wrapper's
|
|
373
|
+
cached version instead of inheriting a writer-selected version.
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
pdf (bytes): The PDF stream to update.
|
|
377
|
+
old (str): The currently present PDF version string.
|
|
378
|
+
new (str): The PDF version string to write into the header.
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
bytes: The PDF stream with the first matching version marker replaced.
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
return pdf.replace(
|
|
385
|
+
VERSION_IDENTIFIER_PREFIX + bytes(old, "utf-8"),
|
|
386
|
+
VERSION_IDENTIFIER_PREFIX + bytes(new, "utf-8"),
|
|
387
|
+
1,
|
|
388
|
+
)
|
|
@@ -37,9 +37,12 @@ from .adapter import (
|
|
|
37
37
|
fp_or_f_obj_or_f_content_to_content,
|
|
38
38
|
fp_or_f_obj_or_stream_to_stream,
|
|
39
39
|
)
|
|
40
|
-
from .constants import VERSION_IDENTIFIER_PREFIX, VERSION_IDENTIFIERS
|
|
41
40
|
from .coordinate import generate_coordinate_grid
|
|
42
|
-
from .egress import
|
|
41
|
+
from .egress import (
|
|
42
|
+
appearance_streams_handler,
|
|
43
|
+
preserve_pdf_properties,
|
|
44
|
+
rebuild_acroform_fields,
|
|
45
|
+
)
|
|
43
46
|
from .filler import fill
|
|
44
47
|
from .font import (
|
|
45
48
|
get_all_available_fonts,
|
|
@@ -62,8 +65,10 @@ from .types import PdfArray
|
|
|
62
65
|
from .utils import (
|
|
63
66
|
generate_unique_suffix,
|
|
64
67
|
get_page_streams,
|
|
68
|
+
get_version,
|
|
65
69
|
merge_pdfs,
|
|
66
70
|
remove_all_widgets,
|
|
71
|
+
set_version,
|
|
67
72
|
)
|
|
68
73
|
from .watermark import (
|
|
69
74
|
copy_watermark_widgets,
|
|
@@ -144,12 +149,14 @@ class PdfWrapper:
|
|
|
144
149
|
self._stream = fp_or_f_obj_or_stream_to_stream(template)
|
|
145
150
|
self.widgets = {}
|
|
146
151
|
self.title: Optional[str] = None
|
|
152
|
+
|
|
153
|
+
self._version = None
|
|
147
154
|
self._metadata = (
|
|
148
155
|
get_metadata(self._read()) if kwargs.get("preserve_metadata") else {}
|
|
149
156
|
)
|
|
150
157
|
self._on_open_javascript = None
|
|
151
158
|
self._available_fonts = {} # for setting /F1
|
|
152
|
-
self._available_fonts_loaded =
|
|
159
|
+
self._available_fonts_loaded = None # for lazy loading fonts
|
|
153
160
|
self._font_register_events = [] # for reregister
|
|
154
161
|
self._key_update_tracker = {} # for update key preserve old key attrs
|
|
155
162
|
self._keys_to_update = [] # for bulk update keys
|
|
@@ -221,8 +228,8 @@ class PdfWrapper:
|
|
|
221
228
|
It rebuilds the widget dictionary and invalidates the lazily loaded font cache.
|
|
222
229
|
"""
|
|
223
230
|
|
|
224
|
-
stream = self._read()
|
|
225
231
|
self._available_fonts_loaded = False
|
|
232
|
+
stream = self._read()
|
|
226
233
|
new_widgets = (
|
|
227
234
|
build_widgets(
|
|
228
235
|
stream,
|
|
@@ -363,15 +370,18 @@ class PdfWrapper:
|
|
|
363
370
|
"""
|
|
364
371
|
Returns the PDF version of the underlying PDF document.
|
|
365
372
|
|
|
373
|
+
The version is read from the PDF header lazily and cached so egress-only
|
|
374
|
+
rewrites can restore the wrapper's original version even if an underlying
|
|
375
|
+
PDF writer emits a different default header.
|
|
376
|
+
|
|
366
377
|
Returns:
|
|
367
378
|
str | None: The PDF version as a string, or None if the version cannot be determined.
|
|
368
379
|
"""
|
|
369
380
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
return each.replace(VERSION_IDENTIFIER_PREFIX, b"").decode()
|
|
381
|
+
if self._version is None:
|
|
382
|
+
self._version = get_version(self._read())
|
|
373
383
|
|
|
374
|
-
return
|
|
384
|
+
return self._version
|
|
375
385
|
|
|
376
386
|
@property
|
|
377
387
|
def fonts(self) -> list:
|
|
@@ -452,6 +462,11 @@ class PdfWrapper:
|
|
|
452
462
|
generating appearance streams.
|
|
453
463
|
3. If `preserve_metadata`, title, or on-open JavaScript are set, it preserves
|
|
454
464
|
or updates the corresponding PDF properties accordingly.
|
|
465
|
+
4. Rebuilds the AcroForm `/Fields` array from page annotations for
|
|
466
|
+
widgets known to this wrapper, leaving the stream unchanged when no
|
|
467
|
+
matching widget annotations are found.
|
|
468
|
+
5. Restores the wrapper's cached PDF header version after egress
|
|
469
|
+
processing, since PDF writers may emit their own default version.
|
|
455
470
|
The wrapper's stored stream is not replaced by these final egress-only changes.
|
|
456
471
|
|
|
457
472
|
Returns:
|
|
@@ -464,8 +479,15 @@ class PdfWrapper:
|
|
|
464
479
|
result, getattr(self, "generate_appearance_streams")
|
|
465
480
|
) # cached
|
|
466
481
|
|
|
467
|
-
if
|
|
468
|
-
|
|
482
|
+
if (
|
|
483
|
+
any(
|
|
484
|
+
[
|
|
485
|
+
getattr(self, "preserve_metadata"),
|
|
486
|
+
self.title,
|
|
487
|
+
self.on_open_javascript,
|
|
488
|
+
]
|
|
489
|
+
)
|
|
490
|
+
and result
|
|
469
491
|
):
|
|
470
492
|
result = preserve_pdf_properties(
|
|
471
493
|
result,
|
|
@@ -474,6 +496,12 @@ class PdfWrapper:
|
|
|
474
496
|
self._metadata if getattr(self, "preserve_metadata") else None,
|
|
475
497
|
)
|
|
476
498
|
|
|
499
|
+
if result:
|
|
500
|
+
result = rebuild_acroform_fields(
|
|
501
|
+
result, set(self.widgets.keys()), getattr(self, "use_full_widget_name")
|
|
502
|
+
)
|
|
503
|
+
if self.version:
|
|
504
|
+
result = set_version(result, get_version(result), self.version)
|
|
477
505
|
return result
|
|
478
506
|
|
|
479
507
|
def _read(self) -> bytes:
|
|
@@ -546,9 +574,9 @@ class PdfWrapper:
|
|
|
546
574
|
"""
|
|
547
575
|
Changes the PDF version of the underlying document.
|
|
548
576
|
|
|
549
|
-
The method replaces the first PDF header version marker in the current stream
|
|
550
|
-
|
|
551
|
-
compatibility.
|
|
577
|
+
The method replaces the first PDF header version marker in the current stream
|
|
578
|
+
and updates the cached version used by later egress processing. It does not
|
|
579
|
+
otherwise validate or rewrite the document for version-specific compatibility.
|
|
552
580
|
|
|
553
581
|
Args:
|
|
554
582
|
version (str): The new PDF version string (e.g., "1.7").
|
|
@@ -557,11 +585,9 @@ class PdfWrapper:
|
|
|
557
585
|
PdfWrapper: The `PdfWrapper` object, allowing for method chaining.
|
|
558
586
|
"""
|
|
559
587
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
1,
|
|
564
|
-
)
|
|
588
|
+
if self.version:
|
|
589
|
+
self._stream = set_version(self._read(), self.version, version)
|
|
590
|
+
self._version = version
|
|
565
591
|
|
|
566
592
|
return self
|
|
567
593
|
|
|
@@ -10,7 +10,7 @@ from PyPDFForm import BlankPage, Fields, PdfWrapper
|
|
|
10
10
|
from PyPDFForm.lib.constants import Annots, Subtype, Widget
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
@pytest.mark.
|
|
13
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
14
14
|
def test_bulk_create_fields_stress_max(pdf_samples, request):
|
|
15
15
|
expected_path = os.path.join(
|
|
16
16
|
pdf_samples, "bulk_create_fields", "test_bulk_create_fields_stress_max.pdf"
|
|
@@ -86,7 +86,7 @@ def test_bulk_create_fields_stress_max(pdf_samples, request):
|
|
|
86
86
|
assert obj.read() == expected
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
@pytest.mark.
|
|
89
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
90
90
|
def test_bulk_create_fields_stress_max_mixed(pdf_samples, request):
|
|
91
91
|
expected_path = os.path.join(
|
|
92
92
|
pdf_samples,
|
|
@@ -7,7 +7,7 @@ import pytest
|
|
|
7
7
|
from PyPDFForm import Fields, PdfWrapper
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
@pytest.mark.
|
|
10
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
11
11
|
def test_create_checkbox_complex_fill(template_stream, pdf_samples, request):
|
|
12
12
|
expected_path = os.path.join(
|
|
13
13
|
pdf_samples, "widget", "test_create_checkbox_complex_fill.pdf"
|
|
@@ -42,7 +42,7 @@ def test_create_checkbox_complex_fill(template_stream, pdf_samples, request):
|
|
|
42
42
|
assert obj.read() == expected
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
@pytest.mark.
|
|
45
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
46
46
|
def test_create_checkbox_check_fill(template_stream, pdf_samples, request):
|
|
47
47
|
expected_path = os.path.join(
|
|
48
48
|
pdf_samples, "widget", "test_create_checkbox_check_fill.pdf"
|
|
@@ -70,7 +70,7 @@ def test_create_checkbox_check_fill(template_stream, pdf_samples, request):
|
|
|
70
70
|
assert obj.read() == expected
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
@pytest.mark.
|
|
73
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
74
74
|
def test_create_checkbox_circle_fill(template_stream, pdf_samples, request):
|
|
75
75
|
expected_path = os.path.join(
|
|
76
76
|
pdf_samples, "widget", "test_create_checkbox_circle_fill.pdf"
|
|
@@ -98,7 +98,7 @@ def test_create_checkbox_circle_fill(template_stream, pdf_samples, request):
|
|
|
98
98
|
assert obj.read() == expected
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
@pytest.mark.
|
|
101
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
102
102
|
def test_create_checkbox_cross_fill(template_stream, pdf_samples, request):
|
|
103
103
|
expected_path = os.path.join(
|
|
104
104
|
pdf_samples, "widget", "test_create_checkbox_cross_fill.pdf"
|
|
@@ -126,7 +126,7 @@ def test_create_checkbox_cross_fill(template_stream, pdf_samples, request):
|
|
|
126
126
|
assert obj.read() == expected
|
|
127
127
|
|
|
128
128
|
|
|
129
|
-
@pytest.mark.
|
|
129
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
130
130
|
def test_create_text_alpha_bg_color(template_stream, pdf_samples, request):
|
|
131
131
|
expected_path = os.path.join(
|
|
132
132
|
pdf_samples, "widget", "test_create_text_alpha_bg_color.pdf"
|
|
@@ -154,7 +154,7 @@ def test_create_text_alpha_bg_color(template_stream, pdf_samples, request):
|
|
|
154
154
|
assert obj.read() == expected
|
|
155
155
|
|
|
156
156
|
|
|
157
|
-
@pytest.mark.
|
|
157
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
158
158
|
def test_create_text_align_center(template_stream, pdf_samples, request):
|
|
159
159
|
expected_path = os.path.join(
|
|
160
160
|
pdf_samples, "widget", "test_create_text_align_center.pdf"
|
|
@@ -182,7 +182,7 @@ def test_create_text_align_center(template_stream, pdf_samples, request):
|
|
|
182
182
|
assert obj.read() == expected
|
|
183
183
|
|
|
184
184
|
|
|
185
|
-
@pytest.mark.
|
|
185
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
186
186
|
def test_create_text_multiline(template_stream, pdf_samples, request):
|
|
187
187
|
expected_path = os.path.join(
|
|
188
188
|
pdf_samples, "widget", "test_create_text_align_multiline.pdf"
|
|
@@ -210,7 +210,7 @@ def test_create_text_multiline(template_stream, pdf_samples, request):
|
|
|
210
210
|
assert obj.read() == expected
|
|
211
211
|
|
|
212
212
|
|
|
213
|
-
@pytest.mark.
|
|
213
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
214
214
|
def test_create_text_complex_filled(
|
|
215
215
|
template_stream, pdf_samples, sample_font_stream, request
|
|
216
216
|
):
|
|
@@ -252,7 +252,7 @@ def test_create_text_complex_filled(
|
|
|
252
252
|
assert obj.read() == expected
|
|
253
253
|
|
|
254
254
|
|
|
255
|
-
@pytest.mark.
|
|
255
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
256
256
|
def test_create_text_comb(template_stream, pdf_samples, request):
|
|
257
257
|
expected_path = os.path.join(pdf_samples, "widget", "test_create_text_comb.pdf")
|
|
258
258
|
with open(expected_path, "rb+") as f:
|
|
@@ -278,7 +278,7 @@ def test_create_text_comb(template_stream, pdf_samples, request):
|
|
|
278
278
|
assert obj.read() == expected
|
|
279
279
|
|
|
280
280
|
|
|
281
|
-
@pytest.mark.
|
|
281
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
282
282
|
def test_create_checkbox_persist_old_widgets_fill(
|
|
283
283
|
template_stream, pdf_samples, request
|
|
284
284
|
):
|
|
@@ -310,7 +310,7 @@ def test_create_checkbox_persist_old_widgets_fill(
|
|
|
310
310
|
assert obj.read() == expected
|
|
311
311
|
|
|
312
312
|
|
|
313
|
-
@pytest.mark.
|
|
313
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
314
314
|
def test_create_widget_sejda_fill_flatten_before(sejda_template, pdf_samples, request):
|
|
315
315
|
expected_path = os.path.join(
|
|
316
316
|
pdf_samples, "widget", "test_create_widget_sejda_fill_flatten_before.pdf"
|
|
@@ -344,7 +344,7 @@ def test_create_widget_sejda_fill_flatten_before(sejda_template, pdf_samples, re
|
|
|
344
344
|
assert obj.read() == expected
|
|
345
345
|
|
|
346
346
|
|
|
347
|
-
@pytest.mark.
|
|
347
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
348
348
|
def test_create_widget_sejda_fill_flatten_after(sejda_template, pdf_samples, request):
|
|
349
349
|
expected_path = os.path.join(
|
|
350
350
|
pdf_samples, "widget", "test_create_widget_sejda_fill_flatten_after.pdf"
|
|
@@ -422,7 +422,7 @@ def test_fill_cmyk_color(pdf_samples, request):
|
|
|
422
422
|
assert obj.read() == expected
|
|
423
423
|
|
|
424
424
|
|
|
425
|
-
@pytest.mark.
|
|
425
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
426
426
|
def test_create_radio_complex(template_stream, pdf_samples, request):
|
|
427
427
|
expected_path = os.path.join(pdf_samples, "widget", "test_create_radio_complex.pdf")
|
|
428
428
|
with open(expected_path, "rb+") as f:
|
|
@@ -454,7 +454,7 @@ def test_create_radio_complex(template_stream, pdf_samples, request):
|
|
|
454
454
|
assert obj.read() == expected
|
|
455
455
|
|
|
456
456
|
|
|
457
|
-
@pytest.mark.
|
|
457
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
458
458
|
def test_create_required_fields(pdf_samples, request):
|
|
459
459
|
expected_path = os.path.join(
|
|
460
460
|
pdf_samples, "widget", "test_create_required_fields.pdf"
|
|
@@ -505,7 +505,7 @@ def test_create_required_fields(pdf_samples, request):
|
|
|
505
505
|
assert obj.read() == expected
|
|
506
506
|
|
|
507
507
|
|
|
508
|
-
@pytest.mark.
|
|
508
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
509
509
|
def test_create_not_required_fields(pdf_samples, request):
|
|
510
510
|
expected_path = os.path.join(
|
|
511
511
|
pdf_samples, "widget", "test_create_not_required_fields.pdf"
|
|
@@ -556,7 +556,7 @@ def test_create_not_required_fields(pdf_samples, request):
|
|
|
556
556
|
assert obj.read() == expected
|
|
557
557
|
|
|
558
558
|
|
|
559
|
-
@pytest.mark.
|
|
559
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
560
560
|
def test_create_fields_with_tooltips(pdf_samples, request):
|
|
561
561
|
expected_path = os.path.join(
|
|
562
562
|
pdf_samples, "widget", "test_create_fields_with_tooltips.pdf"
|
|
@@ -142,7 +142,7 @@ def test_draw_image_on_sejda_template(
|
|
|
142
142
|
assert obj.read() == expected
|
|
143
143
|
|
|
144
144
|
|
|
145
|
-
@pytest.mark.
|
|
145
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
146
146
|
def test_draw_png_image_on_one_page(
|
|
147
147
|
template_stream, image_samples, pdf_samples, request
|
|
148
148
|
):
|
|
@@ -169,7 +169,7 @@ def test_draw_png_image_on_one_page(
|
|
|
169
169
|
assert obj.read() == expected
|
|
170
170
|
|
|
171
171
|
|
|
172
|
-
@pytest.mark.
|
|
172
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
173
173
|
def test_draw_transparent_png_image_on_one_page(
|
|
174
174
|
template_stream, image_samples, pdf_samples, request
|
|
175
175
|
):
|
|
@@ -65,7 +65,7 @@ def test_dropdown_one_flatten(sample_template_with_dropdown, pdf_samples, reques
|
|
|
65
65
|
assert obj.read() == expected
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
@pytest.mark.
|
|
68
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
69
69
|
def test_dropdown_alignment(dropdown_alignment, pdf_samples, request):
|
|
70
70
|
expected_path = os.path.join(pdf_samples, "dropdown", "test_dropdown_alignment.pdf")
|
|
71
71
|
with open(expected_path, "rb+") as f:
|
|
@@ -86,7 +86,7 @@ def test_dropdown_alignment(dropdown_alignment, pdf_samples, request):
|
|
|
86
86
|
assert obj.read() == expected
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
@pytest.mark.
|
|
89
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
90
90
|
def test_dropdown_alignment_flatten_then_unflatten(
|
|
91
91
|
dropdown_alignment, pdf_samples, request
|
|
92
92
|
):
|
|
@@ -5,12 +5,22 @@ from io import BytesIO
|
|
|
5
5
|
|
|
6
6
|
import pytest
|
|
7
7
|
from jsonschema import ValidationError, validate
|
|
8
|
+
from pypdf import PdfReader
|
|
8
9
|
|
|
9
10
|
from PyPDFForm import Annotations, BlankPage, Fields, PdfArray, PdfWrapper
|
|
10
|
-
from PyPDFForm.lib.constants import
|
|
11
|
+
from PyPDFForm.lib.constants import (
|
|
12
|
+
DA,
|
|
13
|
+
UNIQUE_SUFFIX_LENGTH,
|
|
14
|
+
AcroForm,
|
|
15
|
+
T,
|
|
16
|
+
V,
|
|
17
|
+
)
|
|
18
|
+
from PyPDFForm.lib.constants import (
|
|
19
|
+
Fields as FieldsConst,
|
|
20
|
+
)
|
|
11
21
|
from PyPDFForm.lib.deprecation import deprecation_notice
|
|
12
22
|
from PyPDFForm.lib.middleware.base import Widget
|
|
13
|
-
from PyPDFForm.lib.template import get_widgets_by_page
|
|
23
|
+
from PyPDFForm.lib.template import get_widget_key, get_widgets_by_page
|
|
14
24
|
|
|
15
25
|
|
|
16
26
|
def test_deprecation_warning():
|
|
@@ -95,9 +105,9 @@ def test_base_schema_definition():
|
|
|
95
105
|
assert Widget("foo").schema_definition == {}
|
|
96
106
|
|
|
97
107
|
|
|
98
|
-
def test_write(template_stream,
|
|
108
|
+
def test_write(template_stream, tmp_path):
|
|
99
109
|
assert PdfWrapper(template_stream).write(
|
|
100
|
-
os.path.join(
|
|
110
|
+
os.path.join(tmp_path, "sample_template.pdf")
|
|
101
111
|
)
|
|
102
112
|
|
|
103
113
|
|
|
@@ -106,7 +116,7 @@ def test_write_io(template_stream):
|
|
|
106
116
|
PdfWrapper(template_stream).write(buff)
|
|
107
117
|
buff.seek(0)
|
|
108
118
|
|
|
109
|
-
assert buff.read()
|
|
119
|
+
assert buff.read()
|
|
110
120
|
|
|
111
121
|
|
|
112
122
|
def test_fill_flatten_then_unflatten(template_stream, pdf_samples, data_dict, request):
|
|
@@ -135,7 +145,7 @@ def test_register_bad_fonts():
|
|
|
135
145
|
assert "foo" not in obj.fonts
|
|
136
146
|
|
|
137
147
|
|
|
138
|
-
@pytest.mark.
|
|
148
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
139
149
|
def test_fill_with_customized_widgets(
|
|
140
150
|
template_stream, pdf_samples, sample_font_stream, data_dict, request
|
|
141
151
|
):
|
|
@@ -165,7 +175,7 @@ def test_fill_with_customized_widgets(
|
|
|
165
175
|
assert obj.read() == expected
|
|
166
176
|
|
|
167
177
|
|
|
168
|
-
@pytest.mark.
|
|
178
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
169
179
|
def test_fill_with_customized_widgets_flatten(
|
|
170
180
|
template_stream, pdf_samples, sample_font_stream, data_dict, request
|
|
171
181
|
):
|
|
@@ -497,9 +507,10 @@ def test_version(pdf_samples):
|
|
|
497
507
|
|
|
498
508
|
obj = PdfWrapper(os.path.join(pdf_samples, "versions", "unknown.pdf"))
|
|
499
509
|
assert obj.version is None
|
|
510
|
+
assert obj.read()
|
|
500
511
|
|
|
501
512
|
|
|
502
|
-
@pytest.mark.
|
|
513
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
503
514
|
def test_fill_font_color(sample_template_with_font_colors, pdf_samples, request):
|
|
504
515
|
expected_path = os.path.join(pdf_samples, "test_fill_font_color.pdf")
|
|
505
516
|
with open(expected_path, "rb+") as f:
|
|
@@ -521,7 +532,7 @@ def test_fill_font_color(sample_template_with_font_colors, pdf_samples, request)
|
|
|
521
532
|
assert obj.read() == expected
|
|
522
533
|
|
|
523
534
|
|
|
524
|
-
@pytest.mark.
|
|
535
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
525
536
|
def test_fill_complex_fonts(sample_template_with_complex_fonts, pdf_samples, request):
|
|
526
537
|
expected_path = os.path.join(pdf_samples, "test_fill_complex_fonts.pdf")
|
|
527
538
|
with open(expected_path, "rb+") as f:
|
|
@@ -551,7 +562,7 @@ def test_fill_complex_fonts(sample_template_with_complex_fonts, pdf_samples, req
|
|
|
551
562
|
assert obj.read() == expected
|
|
552
563
|
|
|
553
564
|
|
|
554
|
-
@pytest.mark.
|
|
565
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
555
566
|
def test_pages_preserve_font(template_stream, pdf_samples, sample_font_stream, request):
|
|
556
567
|
expected_path = os.path.join(pdf_samples, "pages", "test_pages_preserve_font.pdf")
|
|
557
568
|
obj = PdfWrapper(template_stream)
|
|
@@ -703,7 +714,7 @@ def test_uncheck_checkbox(pdf_samples, request):
|
|
|
703
714
|
assert obj.read() == expected
|
|
704
715
|
|
|
705
716
|
|
|
706
|
-
@pytest.mark.
|
|
717
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
707
718
|
def test_blank_page(pdf_samples, request):
|
|
708
719
|
expected_path = os.path.join(pdf_samples, "test_blank_page.pdf")
|
|
709
720
|
with open(expected_path, "rb+") as f:
|
|
@@ -718,7 +729,7 @@ def test_blank_page(pdf_samples, request):
|
|
|
718
729
|
request.config.results["skip_regenerate"] = len(obj.read()) == len(expected)
|
|
719
730
|
|
|
720
731
|
|
|
721
|
-
@pytest.mark.
|
|
732
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
722
733
|
def test_blank_page_custom_size_multiply(pdf_samples, request):
|
|
723
734
|
expected_path = os.path.join(
|
|
724
735
|
pdf_samples, "test_blank_page_custom_size_multiply.pdf"
|
|
@@ -844,7 +855,7 @@ def test_remove_fields_update_widgets(template_stream):
|
|
|
844
855
|
|
|
845
856
|
|
|
846
857
|
def test_remove_fields_no_keys_specified(template_stream):
|
|
847
|
-
assert PdfWrapper(template_stream).remove_fields([]).read()
|
|
858
|
+
assert PdfWrapper(template_stream).remove_fields([]).read()
|
|
848
859
|
|
|
849
860
|
|
|
850
861
|
def test_merge(template_stream):
|
|
@@ -1102,3 +1113,16 @@ def test_rubber_stamp_annotation(template_stream, pdf_samples, request):
|
|
|
1102
1113
|
|
|
1103
1114
|
assert len(obj.read()) == len(expected)
|
|
1104
1115
|
assert obj.read() == expected
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
def test_rebuild_acroform_fields():
|
|
1119
|
+
pdf = PdfWrapper(BlankPage() * 2)
|
|
1120
|
+
|
|
1121
|
+
pdf.bulk_create_fields(
|
|
1122
|
+
[Fields.TextField("foo", 1, 100, 100), Fields.TextField("bar", 2, 100, 200)]
|
|
1123
|
+
)
|
|
1124
|
+
|
|
1125
|
+
reader = PdfReader(BytesIO(pdf.read()))
|
|
1126
|
+
|
|
1127
|
+
assert get_widget_key(reader.root_object[AcroForm][FieldsConst][0], False) == "foo"
|
|
1128
|
+
assert get_widget_key(reader.root_object[AcroForm][FieldsConst][1], False) == "bar"
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
# TODO: why does pikepdf randomize final streams?
|
|
3
2
|
|
|
4
3
|
import os
|
|
5
4
|
|
|
@@ -24,7 +23,30 @@ def test_fill(template_stream, pdf_samples, data_dict, request):
|
|
|
24
23
|
expected = f.read()
|
|
25
24
|
|
|
26
25
|
assert len(obj.read()) == len(expected)
|
|
27
|
-
|
|
26
|
+
assert obj.read() == expected
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_fill_sejda_complex(
|
|
30
|
+
sejda_template_complex, sejda_complex_data, pdf_samples, request
|
|
31
|
+
):
|
|
32
|
+
expected_path = os.path.join(
|
|
33
|
+
pdf_samples,
|
|
34
|
+
"generate_appearance_streams",
|
|
35
|
+
"paragraph",
|
|
36
|
+
"sample_filled_sejda_complex.pdf",
|
|
37
|
+
)
|
|
38
|
+
with open(expected_path, "rb+") as f:
|
|
39
|
+
obj = PdfWrapper(sejda_template_complex, generate_appearance_streams=True).fill(
|
|
40
|
+
sejda_complex_data,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
request.config.results["expected_path"] = expected_path
|
|
44
|
+
request.config.results["stream"] = obj.read()
|
|
45
|
+
|
|
46
|
+
expected = f.read()
|
|
47
|
+
|
|
48
|
+
assert len(obj.read()) == len(expected)
|
|
49
|
+
assert obj.read() == expected
|
|
28
50
|
|
|
29
51
|
|
|
30
52
|
def test_dropdown_two(sample_template_with_dropdown, pdf_samples, request):
|
|
@@ -53,30 +75,7 @@ def test_dropdown_two(sample_template_with_dropdown, pdf_samples, request):
|
|
|
53
75
|
expected = f.read()
|
|
54
76
|
|
|
55
77
|
assert len(obj.read()) == len(expected)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def test_fill_sejda_complex(
|
|
60
|
-
sejda_template_complex, sejda_complex_data, pdf_samples, request
|
|
61
|
-
):
|
|
62
|
-
expected_path = os.path.join(
|
|
63
|
-
pdf_samples,
|
|
64
|
-
"generate_appearance_streams",
|
|
65
|
-
"paragraph",
|
|
66
|
-
"sample_filled_sejda_complex.pdf",
|
|
67
|
-
)
|
|
68
|
-
with open(expected_path, "rb+") as f:
|
|
69
|
-
obj = PdfWrapper(sejda_template_complex, generate_appearance_streams=True).fill(
|
|
70
|
-
sejda_complex_data,
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
request.config.results["expected_path"] = expected_path
|
|
74
|
-
request.config.results["stream"] = obj.read()
|
|
75
|
-
|
|
76
|
-
expected = f.read()
|
|
77
|
-
|
|
78
|
-
assert len(obj.read()) == len(expected)
|
|
79
|
-
request.config.results["skip_regenerate"] = len(obj.read()) == len(expected)
|
|
78
|
+
assert obj.read() == expected
|
|
80
79
|
|
|
81
80
|
|
|
82
81
|
def test_issue_613(pdf_samples, request):
|
|
@@ -100,10 +99,10 @@ def test_issue_613(pdf_samples, request):
|
|
|
100
99
|
expected = f.read()
|
|
101
100
|
|
|
102
101
|
assert len(obj.read()) == len(expected)
|
|
103
|
-
|
|
102
|
+
assert obj.read() == expected
|
|
104
103
|
|
|
105
104
|
|
|
106
|
-
@pytest.mark.
|
|
105
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
107
106
|
def test_sample_template_library(
|
|
108
107
|
pdf_samples, image_samples, sample_font_stream, request
|
|
109
108
|
):
|
|
@@ -94,7 +94,9 @@ def run_sample_template_library_test(
|
|
|
94
94
|
|
|
95
95
|
obj.widgets["new_text_field_widget"].font = "new_font"
|
|
96
96
|
obj.widgets["new_text_field_widget"].font_color = (1, 0, 0)
|
|
97
|
-
|
|
97
|
+
|
|
98
|
+
# Currently alignment is not respected due to qpdf limitations
|
|
99
|
+
# https://github.com/qpdf/qpdf/blob/503d401615a842f7c5220a3ee425f5db2f25f537/TODO.md#text-appearance-streams
|
|
98
100
|
obj.widgets["new_text_field_widget"].alignment = 2
|
|
99
101
|
|
|
100
102
|
obj.widgets["new_checkbox_widget"].size = 40
|
|
@@ -106,10 +108,7 @@ def run_sample_template_library_test(
|
|
|
106
108
|
expected = f.read()
|
|
107
109
|
|
|
108
110
|
assert len(obj.read()) == len(expected)
|
|
109
|
-
|
|
110
|
-
request.config.results["skip_regenerate"] = len(obj.read()) == len(expected)
|
|
111
|
-
else:
|
|
112
|
-
assert obj.read() == expected
|
|
111
|
+
assert obj.read() == expected
|
|
113
112
|
|
|
114
113
|
|
|
115
114
|
def test_fill(template_stream, pdf_samples, data_dict, request):
|
|
@@ -199,7 +198,7 @@ def test_issue_613(pdf_samples, request):
|
|
|
199
198
|
assert obj.read() == expected
|
|
200
199
|
|
|
201
200
|
|
|
202
|
-
@pytest.mark.
|
|
201
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
203
202
|
def test_sample_template_library(
|
|
204
203
|
pdf_samples, image_samples, sample_font_stream, request
|
|
205
204
|
):
|
|
@@ -46,7 +46,7 @@ def test_fill_sejda_complex_flatten(
|
|
|
46
46
|
assert obj.read() == expected
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
@pytest.mark.
|
|
49
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
50
50
|
def test_paragraph_complex(sample_template_paragraph_complex, pdf_samples, request):
|
|
51
51
|
expected_path = os.path.join(pdf_samples, "paragraph", "test_paragraph_complex.pdf")
|
|
52
52
|
with open(expected_path, "rb+") as f:
|
|
@@ -70,7 +70,7 @@ def test_paragraph_complex(sample_template_paragraph_complex, pdf_samples, reque
|
|
|
70
70
|
assert obj.read() == expected
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
@pytest.mark.
|
|
73
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
74
74
|
def test_paragraph_max_length(
|
|
75
75
|
sample_template_with_paragraph_max_length, pdf_samples, request
|
|
76
76
|
):
|
|
@@ -25,7 +25,7 @@ def test_signature_sample_value(pdf_samples):
|
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
@pytest.mark.
|
|
28
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
29
29
|
def test_fill_signature_overlap(pdf_samples, image_samples, request):
|
|
30
30
|
expected_path = os.path.join(
|
|
31
31
|
pdf_samples, "signature", "test_fill_signature_overlap.pdf"
|
|
@@ -46,7 +46,7 @@ def test_fill_signature_overlap(pdf_samples, image_samples, request):
|
|
|
46
46
|
assert obj.read() == expected
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
@pytest.mark.
|
|
49
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
50
50
|
def test_fill_small_icon(pdf_samples, image_samples, request):
|
|
51
51
|
expected_path = os.path.join(pdf_samples, "signature", "test_fill_small_icon.pdf")
|
|
52
52
|
with open(expected_path, "rb+") as f:
|
|
@@ -66,7 +66,7 @@ def test_fill_small_icon(pdf_samples, image_samples, request):
|
|
|
66
66
|
assert obj.read() == expected
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
@pytest.mark.
|
|
69
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
70
70
|
def test_fill_small_icon_not_preserve_aspect_ratio(pdf_samples, image_samples, request):
|
|
71
71
|
expected_path = os.path.join(
|
|
72
72
|
pdf_samples, "signature", "test_fill_small_icon_not_preserve_aspect_ratio.pdf"
|
|
@@ -7,7 +7,7 @@ import pytest
|
|
|
7
7
|
from PyPDFForm import Fields, PdfWrapper, RawElements
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
@pytest.mark.
|
|
10
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
11
11
|
def test_register_font_no_form_fields(pdf_samples, sample_font_stream, request):
|
|
12
12
|
expected_path = os.path.join(
|
|
13
13
|
pdf_samples, "test_widget_attr_trigger", "test_register_font_no_form_fields.pdf"
|
|
@@ -28,7 +28,7 @@ def test_register_font_no_form_fields(pdf_samples, sample_font_stream, request):
|
|
|
28
28
|
assert obj.read() == expected
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
@pytest.mark.
|
|
31
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
32
32
|
def test_set_text_field_font_sejda(pdf_samples, font_samples, sejda_template, request):
|
|
33
33
|
expected_path = os.path.join(
|
|
34
34
|
pdf_samples,
|
|
@@ -219,7 +219,7 @@ def test_set_radio_size_sejda(pdf_samples, sejda_template, request):
|
|
|
219
219
|
assert obj.read() == expected
|
|
220
220
|
|
|
221
221
|
|
|
222
|
-
@pytest.mark.
|
|
222
|
+
@pytest.mark.requires_zlib_over_zlib_ng
|
|
223
223
|
def test_set_dropdown_font_sejda(
|
|
224
224
|
pdf_samples, dropdown_alignment_sejda, sample_font_stream, request
|
|
225
225
|
):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|