PyPDFForm 3.0.1__tar.gz → 3.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.
Potentially problematic release.
This version of PyPDFForm might be problematic. Click here for more details.
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PKG-INFO +1 -1
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/__init__.py +1 -1
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/filler.py +6 -4
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/hooks.py +75 -4
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/middleware/base.py +4 -1
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/middleware/checkbox.py +5 -4
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/middleware/dropdown.py +6 -5
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/middleware/radio.py +5 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/middleware/text.py +10 -9
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/patterns.py +3 -40
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm.egg-info/PKG-INFO +1 -1
- {pypdfform-3.0.1 → pypdfform-3.1.0}/tests/test_create_widget.py +28 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/tests/test_dropdown.py +80 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/tests/test_functional.py +74 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/LICENSE +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/adapter.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/constants.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/coordinate.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/font.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/image.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/middleware/__init__.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/middleware/image.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/middleware/signature.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/template.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/utils.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/watermark.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/widgets/__init__.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/widgets/base.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/widgets/bedrock.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/widgets/checkbox.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/widgets/dropdown.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/widgets/image.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/widgets/radio.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/widgets/signature.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/widgets/text.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm/wrapper.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm.egg-info/SOURCES.txt +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm.egg-info/dependency_links.txt +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm.egg-info/requires.txt +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/PyPDFForm.egg-info/top_level.txt +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/README.md +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/pyproject.toml +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/setup.cfg +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/tests/test_adobe_mode.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/tests/test_fill_max_length_text_field.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/tests/test_fill_method.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/tests/test_paragraph.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/tests/test_signature.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/tests/test_use_full_widget_name.py +0 -0
- {pypdfform-3.0.1 → pypdfform-3.1.0}/tests/test_widget_attr_trigger.py +0 -0
|
@@ -20,7 +20,7 @@ The library supports various PDF form features, including:
|
|
|
20
20
|
PyPDFForm aims to simplify PDF form manipulation, making it accessible to developers of all skill levels.
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
__version__ = "3.0
|
|
23
|
+
__version__ = "3.1.0"
|
|
24
24
|
|
|
25
25
|
from .middleware.text import Text # exposing for setting global font attrs
|
|
26
26
|
from .wrapper import PdfWrapper
|
|
@@ -15,6 +15,7 @@ from pypdf import PdfReader, PdfWriter
|
|
|
15
15
|
from pypdf.generic import DictionaryObject
|
|
16
16
|
|
|
17
17
|
from .constants import WIDGET_TYPES, Annots
|
|
18
|
+
from .hooks import flatten_generic, flatten_radio
|
|
18
19
|
from .image import get_draw_image_resolutions, get_image_dimensions
|
|
19
20
|
from .middleware.checkbox import Checkbox
|
|
20
21
|
from .middleware.dropdown import Dropdown
|
|
@@ -22,9 +23,8 @@ from .middleware.image import Image
|
|
|
22
23
|
from .middleware.radio import Radio
|
|
23
24
|
from .middleware.signature import Signature
|
|
24
25
|
from .middleware.text import Text
|
|
25
|
-
from .patterns import (
|
|
26
|
-
|
|
27
|
-
update_text_value)
|
|
26
|
+
from .patterns import (update_checkbox_value, update_dropdown_value,
|
|
27
|
+
update_radio_value, update_text_value)
|
|
28
28
|
from .template import get_widget_key
|
|
29
29
|
from .utils import stream_to_io
|
|
30
30
|
from .watermark import create_watermarks_and_draw, merge_watermarks_with_pdf
|
|
@@ -145,7 +145,9 @@ def fill(
|
|
|
145
145
|
|
|
146
146
|
# flatten all
|
|
147
147
|
if flatten:
|
|
148
|
-
(flatten_radio if isinstance(widget, Radio) else flatten_generic)(
|
|
148
|
+
(flatten_radio if isinstance(widget, Radio) else flatten_generic)(
|
|
149
|
+
annot, True
|
|
150
|
+
)
|
|
149
151
|
if widget.value is None:
|
|
150
152
|
continue
|
|
151
153
|
|
|
@@ -4,7 +4,8 @@ This module defines widget hooks that allow for dynamic modification of PDF form
|
|
|
4
4
|
|
|
5
5
|
It provides functions to trigger these hooks, enabling changes to text field properties
|
|
6
6
|
like font, font size, color, alignment, and multiline settings, as well as the size
|
|
7
|
-
of checkbox and radio button widgets.
|
|
7
|
+
of checkbox and radio button widgets. It also provides functions for flattening
|
|
8
|
+
generic and radio button widgets. These hooks are triggered during the PDF form
|
|
8
9
|
filling process, allowing for customization of the form's appearance and behavior.
|
|
9
10
|
"""
|
|
10
11
|
|
|
@@ -17,7 +18,7 @@ from pypdf.generic import (ArrayObject, DictionaryObject, FloatObject,
|
|
|
17
18
|
NameObject, NumberObject, TextStringObject)
|
|
18
19
|
|
|
19
20
|
from .constants import (COMB, DA, FONT_COLOR_IDENTIFIER, FONT_SIZE_IDENTIFIER,
|
|
20
|
-
MULTILINE, Annots, Ff, Opt, Parent, Q, Rect)
|
|
21
|
+
MULTILINE, READ_ONLY, Annots, Ff, Opt, Parent, Q, Rect)
|
|
21
22
|
from .template import get_widget_key
|
|
22
23
|
from .utils import stream_to_io
|
|
23
24
|
|
|
@@ -249,8 +250,78 @@ def update_dropdown_choices(annot: DictionaryObject, val: list) -> None:
|
|
|
249
250
|
|
|
250
251
|
Args:
|
|
251
252
|
annot (DictionaryObject): The annotation dictionary for the dropdown field.
|
|
252
|
-
val (list): A list of strings representing the new choices for the dropdown.
|
|
253
|
+
val (list): A list of strings or tuples representing the new choices for the dropdown.
|
|
253
254
|
"""
|
|
254
255
|
annot[NameObject(Opt)] = ArrayObject(
|
|
255
|
-
[
|
|
256
|
+
[
|
|
257
|
+
(
|
|
258
|
+
ArrayObject([TextStringObject(each[1]), TextStringObject(each[0])])
|
|
259
|
+
if isinstance(each, tuple)
|
|
260
|
+
else ArrayObject([TextStringObject(each), TextStringObject(each)])
|
|
261
|
+
)
|
|
262
|
+
for each in val
|
|
263
|
+
]
|
|
256
264
|
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def flatten_radio(annot: DictionaryObject, val: bool) -> None:
|
|
268
|
+
"""
|
|
269
|
+
Flattens a radio button annotation by setting or unsetting the ReadOnly flag,
|
|
270
|
+
making it non-editable or editable based on the `val` parameter.
|
|
271
|
+
|
|
272
|
+
This function modifies the Ff (flags) entry in the radio button's annotation
|
|
273
|
+
dictionary or its parent dictionary if `Parent` exists in `annot`, to set or
|
|
274
|
+
unset the ReadOnly flag, preventing or allowing the user from changing the
|
|
275
|
+
selected option.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
annot (DictionaryObject): The radio button annotation dictionary.
|
|
279
|
+
val (bool): True to flatten (make read-only), False to unflatten (make editable).
|
|
280
|
+
"""
|
|
281
|
+
if Parent in annot:
|
|
282
|
+
annot[NameObject(Parent)][NameObject(Ff)] = NumberObject(
|
|
283
|
+
(
|
|
284
|
+
int(annot[NameObject(Parent)].get(NameObject(Ff), 0)) | READ_ONLY
|
|
285
|
+
if val
|
|
286
|
+
else int(annot[NameObject(Parent)].get(NameObject(Ff), 0)) & ~READ_ONLY
|
|
287
|
+
)
|
|
288
|
+
)
|
|
289
|
+
else:
|
|
290
|
+
annot[NameObject(Ff)] = NumberObject(
|
|
291
|
+
(
|
|
292
|
+
int(annot.get(NameObject(Ff), 0)) | READ_ONLY
|
|
293
|
+
if val
|
|
294
|
+
else int(annot.get(NameObject(Ff), 0)) & ~READ_ONLY
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def flatten_generic(annot: DictionaryObject, val: bool) -> None:
|
|
300
|
+
"""
|
|
301
|
+
Flattens a generic annotation by setting or unsetting the ReadOnly flag,
|
|
302
|
+
making it non-editable or editable based on the `val` parameter.
|
|
303
|
+
|
|
304
|
+
This function modifies the Ff (flags) entry in the annotation dictionary to
|
|
305
|
+
set or unset the ReadOnly flag, preventing or allowing the user from
|
|
306
|
+
interacting with the form field.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
annot (DictionaryObject): The annotation dictionary.
|
|
310
|
+
val (bool): True to flatten (make read-only), False to unflatten (make editable).
|
|
311
|
+
"""
|
|
312
|
+
if Parent in annot and Ff not in annot:
|
|
313
|
+
annot[NameObject(Parent)][NameObject(Ff)] = NumberObject(
|
|
314
|
+
(
|
|
315
|
+
int(annot.get(NameObject(Ff), 0)) | READ_ONLY
|
|
316
|
+
if val
|
|
317
|
+
else int(annot.get(NameObject(Ff), 0)) & ~READ_ONLY
|
|
318
|
+
)
|
|
319
|
+
)
|
|
320
|
+
else:
|
|
321
|
+
annot[NameObject(Ff)] = NumberObject(
|
|
322
|
+
(
|
|
323
|
+
int(annot.get(NameObject(Ff), 0)) | READ_ONLY
|
|
324
|
+
if val
|
|
325
|
+
else int(annot.get(NameObject(Ff), 0)) & ~READ_ONLY
|
|
326
|
+
)
|
|
327
|
+
)
|
|
@@ -21,7 +21,9 @@ class Widget:
|
|
|
21
21
|
as name, value, and schema definition.
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
SET_ATTR_TRIGGER_HOOK_MAP = {
|
|
24
|
+
SET_ATTR_TRIGGER_HOOK_MAP = {
|
|
25
|
+
"readonly": "flatten_generic",
|
|
26
|
+
}
|
|
25
27
|
|
|
26
28
|
def __init__(
|
|
27
29
|
self,
|
|
@@ -39,6 +41,7 @@ class Widget:
|
|
|
39
41
|
self._name = name
|
|
40
42
|
self._value = value
|
|
41
43
|
self.desc = None
|
|
44
|
+
self.readonly = None
|
|
42
45
|
self.hooks_to_trigger = []
|
|
43
46
|
|
|
44
47
|
def __setattr__(self, name: str, value: Any) -> None:
|
|
@@ -20,10 +20,6 @@ class Checkbox(Widget):
|
|
|
20
20
|
implements the schema_definition and sample_value properties.
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
SET_ATTR_TRIGGER_HOOK_MAP = {
|
|
24
|
-
"size": "update_check_radio_size",
|
|
25
|
-
}
|
|
26
|
-
|
|
27
23
|
def __init__(
|
|
28
24
|
self,
|
|
29
25
|
name: str,
|
|
@@ -39,6 +35,11 @@ class Checkbox(Widget):
|
|
|
39
35
|
Attributes:
|
|
40
36
|
size (int): The size of the checkbox. Defaults to None.
|
|
41
37
|
"""
|
|
38
|
+
self.SET_ATTR_TRIGGER_HOOK_MAP.update(
|
|
39
|
+
{
|
|
40
|
+
"size": "update_check_radio_size",
|
|
41
|
+
}
|
|
42
|
+
)
|
|
42
43
|
super().__init__(name, value)
|
|
43
44
|
|
|
44
45
|
self.size = None
|
|
@@ -26,11 +26,6 @@ class Dropdown(Widget):
|
|
|
26
26
|
sample_value: Returns a sample value for the dropdown.
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
|
-
SET_ATTR_TRIGGER_HOOK_MAP = {
|
|
30
|
-
"font": "update_text_field_font",
|
|
31
|
-
"choices": "update_dropdown_choices",
|
|
32
|
-
}
|
|
33
|
-
|
|
34
29
|
def __init__(
|
|
35
30
|
self,
|
|
36
31
|
name: str,
|
|
@@ -47,6 +42,12 @@ class Dropdown(Widget):
|
|
|
47
42
|
font (str): The font of the dropdown field.
|
|
48
43
|
choices (List[str]): The list of choices for the dropdown.
|
|
49
44
|
"""
|
|
45
|
+
self.SET_ATTR_TRIGGER_HOOK_MAP.update(
|
|
46
|
+
{
|
|
47
|
+
"font": "update_text_field_font",
|
|
48
|
+
"choices": "update_dropdown_choices",
|
|
49
|
+
}
|
|
50
|
+
)
|
|
50
51
|
super().__init__(name, value)
|
|
51
52
|
|
|
52
53
|
self.font = None
|
|
@@ -34,6 +34,11 @@ class Radio(Checkbox):
|
|
|
34
34
|
Attributes:
|
|
35
35
|
number_of_options (int): The number of options for the radio button.
|
|
36
36
|
"""
|
|
37
|
+
self.SET_ATTR_TRIGGER_HOOK_MAP.update(
|
|
38
|
+
{
|
|
39
|
+
"readonly": "flatten_radio",
|
|
40
|
+
}
|
|
41
|
+
)
|
|
37
42
|
super().__init__(name, value)
|
|
38
43
|
|
|
39
44
|
self.number_of_options = 0
|
|
@@ -22,15 +22,6 @@ class Text(Widget):
|
|
|
22
22
|
font_size, font_color, comb, alignment, and multiline.
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
|
-
SET_ATTR_TRIGGER_HOOK_MAP = {
|
|
26
|
-
"font": "update_text_field_font",
|
|
27
|
-
"font_size": "update_text_field_font_size",
|
|
28
|
-
"font_color": "update_text_field_font_color",
|
|
29
|
-
"comb": "update_text_field_comb",
|
|
30
|
-
"alignment": "update_text_field_alignment",
|
|
31
|
-
"multiline": "update_text_field_multiline",
|
|
32
|
-
}
|
|
33
|
-
|
|
34
25
|
def __init__(
|
|
35
26
|
self,
|
|
36
27
|
name: str,
|
|
@@ -52,6 +43,16 @@ class Text(Widget):
|
|
|
52
43
|
multiline (bool): Whether the text field is multiline. Defaults to None.
|
|
53
44
|
max_length (int): The maximum length of the text field. Defaults to None.
|
|
54
45
|
"""
|
|
46
|
+
self.SET_ATTR_TRIGGER_HOOK_MAP.update(
|
|
47
|
+
{
|
|
48
|
+
"font": "update_text_field_font",
|
|
49
|
+
"font_size": "update_text_field_font_size",
|
|
50
|
+
"font_color": "update_text_field_font_color",
|
|
51
|
+
"comb": "update_text_field_comb",
|
|
52
|
+
"alignment": "update_text_field_alignment",
|
|
53
|
+
"multiline": "update_text_field_multiline",
|
|
54
|
+
}
|
|
55
|
+
)
|
|
55
56
|
super().__init__(name, value)
|
|
56
57
|
|
|
57
58
|
self.font = None
|
|
@@ -5,15 +5,14 @@ This module defines patterns and utility functions for interacting with PDF form
|
|
|
5
5
|
It includes patterns for identifying different types of widgets (e.g., text fields,
|
|
6
6
|
checkboxes, radio buttons, dropdowns, images, and signatures) based on their
|
|
7
7
|
properties in the PDF's annotation dictionary. It also provides utility functions
|
|
8
|
-
for updating
|
|
8
|
+
for updating these widgets.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
from pypdf.generic import (ArrayObject, DictionaryObject, NameObject,
|
|
12
12
|
NumberObject, TextStringObject)
|
|
13
13
|
|
|
14
|
-
from .constants import (AP, AS, DV, FT, IMAGE_FIELD_IDENTIFIER, JS,
|
|
15
|
-
|
|
16
|
-
V, Yes)
|
|
14
|
+
from .constants import (AP, AS, DV, FT, IMAGE_FIELD_IDENTIFIER, JS, TU, A, Btn,
|
|
15
|
+
Ch, I, N, Off, Opt, Parent, Sig, T, Tx, V, Yes)
|
|
17
16
|
from .middleware.checkbox import Checkbox
|
|
18
17
|
from .middleware.dropdown import Dropdown
|
|
19
18
|
from .middleware.image import Image
|
|
@@ -177,42 +176,6 @@ def update_text_value(annot: DictionaryObject, widget: Text) -> None:
|
|
|
177
176
|
annot[NameObject(AP)] = TextStringObject(widget.value)
|
|
178
177
|
|
|
179
178
|
|
|
180
|
-
def flatten_radio(annot: DictionaryObject) -> None:
|
|
181
|
-
"""
|
|
182
|
-
Flattens a radio button annotation by setting the ReadOnly flag, making it non-editable.
|
|
183
|
-
|
|
184
|
-
This function modifies the Ff (flags) entry in the radio button's parent
|
|
185
|
-
dictionary to set the ReadOnly flag, preventing the user from changing the
|
|
186
|
-
selected option.
|
|
187
|
-
|
|
188
|
-
Args:
|
|
189
|
-
annot (DictionaryObject): The radio button annotation dictionary.
|
|
190
|
-
"""
|
|
191
|
-
annot[NameObject(Parent)][NameObject(Ff)] = NumberObject(
|
|
192
|
-
int(annot[NameObject(Parent)].get(NameObject(Ff), 0)) | READ_ONLY
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
def flatten_generic(annot: DictionaryObject) -> None:
|
|
197
|
-
"""
|
|
198
|
-
Flattens a generic annotation by setting the ReadOnly flag, making it non-editable.
|
|
199
|
-
|
|
200
|
-
This function modifies the Ff (flags) entry in the annotation dictionary to
|
|
201
|
-
set the ReadOnly flag, preventing the user from interacting with the form field.
|
|
202
|
-
|
|
203
|
-
Args:
|
|
204
|
-
annot (DictionaryObject): The annotation dictionary.
|
|
205
|
-
"""
|
|
206
|
-
if Parent in annot and Ff not in annot:
|
|
207
|
-
annot[NameObject(Parent)][NameObject(Ff)] = NumberObject(
|
|
208
|
-
int(annot.get(NameObject(Ff), 0)) | READ_ONLY
|
|
209
|
-
)
|
|
210
|
-
else:
|
|
211
|
-
annot[NameObject(Ff)] = NumberObject(
|
|
212
|
-
int(annot.get(NameObject(Ff), 0)) | READ_ONLY
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
|
|
216
179
|
def update_annotation_name(annot: DictionaryObject, val: str) -> None:
|
|
217
180
|
"""
|
|
218
181
|
Updates the name of an annotation, setting the T (title) entry.
|
|
@@ -832,6 +832,34 @@ def test_create_dropdown(template_stream, pdf_samples, sample_font_stream, reque
|
|
|
832
832
|
assert obj.read() == expected
|
|
833
833
|
|
|
834
834
|
|
|
835
|
+
def test_create_dropdown_with_export_values(template_stream, pdf_samples, request):
|
|
836
|
+
expected_path = os.path.join(
|
|
837
|
+
pdf_samples, "widget", "test_create_dropdown_with_export_values.pdf"
|
|
838
|
+
)
|
|
839
|
+
with open(expected_path, "rb+") as f:
|
|
840
|
+
obj = PdfWrapper(template_stream).create_widget(
|
|
841
|
+
widget_type="dropdown",
|
|
842
|
+
name="new_dropdown_widget",
|
|
843
|
+
page_number=1,
|
|
844
|
+
x=57,
|
|
845
|
+
y=700,
|
|
846
|
+
options=[
|
|
847
|
+
("foo", "foo_export"),
|
|
848
|
+
("bar", "bar_export"),
|
|
849
|
+
("foobar", "foobar_export"),
|
|
850
|
+
],
|
|
851
|
+
)
|
|
852
|
+
assert obj.schema["properties"]["new_dropdown_widget"]["type"] == "integer"
|
|
853
|
+
|
|
854
|
+
request.config.results["expected_path"] = expected_path
|
|
855
|
+
request.config.results["stream"] = obj.read()
|
|
856
|
+
|
|
857
|
+
expected = f.read()
|
|
858
|
+
|
|
859
|
+
assert len(obj.read()) == len(expected)
|
|
860
|
+
assert obj.read() == expected
|
|
861
|
+
|
|
862
|
+
|
|
835
863
|
def test_fill_cmyk_color(pdf_samples, request):
|
|
836
864
|
expected_path = os.path.join(pdf_samples, "widget", "test_fill_cmyk_color.pdf")
|
|
837
865
|
with open(expected_path, "rb+") as f:
|
|
@@ -290,6 +290,33 @@ def test_dropdown_alignment_flatten(dropdown_alignment, pdf_samples, request):
|
|
|
290
290
|
assert obj.read() == expected
|
|
291
291
|
|
|
292
292
|
|
|
293
|
+
def test_dropdown_alignment_flatten_then_unflatten(
|
|
294
|
+
dropdown_alignment, pdf_samples, request
|
|
295
|
+
):
|
|
296
|
+
expected_path = os.path.join(
|
|
297
|
+
pdf_samples, "dropdown", "test_dropdown_alignment_flatten_then_unflatten.pdf"
|
|
298
|
+
)
|
|
299
|
+
with open(expected_path, "rb+") as f:
|
|
300
|
+
obj = PdfWrapper(dropdown_alignment).fill(
|
|
301
|
+
{
|
|
302
|
+
"dropdown_left": 0,
|
|
303
|
+
"dropdown_center": 1,
|
|
304
|
+
"dropdown_right": 2,
|
|
305
|
+
},
|
|
306
|
+
flatten=True,
|
|
307
|
+
)
|
|
308
|
+
obj.widgets["dropdown_center"].readonly = False
|
|
309
|
+
|
|
310
|
+
request.config.results["expected_path"] = expected_path
|
|
311
|
+
request.config.results["stream"] = obj.read()
|
|
312
|
+
|
|
313
|
+
expected = f.read()
|
|
314
|
+
|
|
315
|
+
if os.name != "nt":
|
|
316
|
+
assert len(obj.read()) == len(expected)
|
|
317
|
+
assert obj.read() == expected
|
|
318
|
+
|
|
319
|
+
|
|
293
320
|
def test_dropdown_alignment_sejda(dropdown_alignment_sejda, pdf_samples, request):
|
|
294
321
|
expected_path = os.path.join(
|
|
295
322
|
pdf_samples, "dropdown", "test_dropdown_alignment_sejda.pdf"
|
|
@@ -343,6 +370,37 @@ def test_dropdown_alignment_sejda_flatten(
|
|
|
343
370
|
assert obj.read() == expected
|
|
344
371
|
|
|
345
372
|
|
|
373
|
+
def test_dropdown_alignment_sejda_flatten_then_unflatten(
|
|
374
|
+
dropdown_alignment_sejda, pdf_samples, request
|
|
375
|
+
):
|
|
376
|
+
expected_path = os.path.join(
|
|
377
|
+
pdf_samples,
|
|
378
|
+
"dropdown",
|
|
379
|
+
"test_dropdown_alignment_sejda_flatten_then_unflatten.pdf",
|
|
380
|
+
)
|
|
381
|
+
with open(
|
|
382
|
+
expected_path,
|
|
383
|
+
"rb+",
|
|
384
|
+
) as f:
|
|
385
|
+
obj = PdfWrapper(dropdown_alignment_sejda).fill(
|
|
386
|
+
{
|
|
387
|
+
"dropdown_left": 0,
|
|
388
|
+
"dropdown_center": 1,
|
|
389
|
+
"dropdown_right": 2,
|
|
390
|
+
},
|
|
391
|
+
flatten=True,
|
|
392
|
+
)
|
|
393
|
+
obj.widgets["dropdown_center"].readonly = False
|
|
394
|
+
|
|
395
|
+
request.config.results["expected_path"] = expected_path
|
|
396
|
+
request.config.results["stream"] = obj.read()
|
|
397
|
+
|
|
398
|
+
expected = f.read()
|
|
399
|
+
|
|
400
|
+
assert len(obj.read()) == len(expected)
|
|
401
|
+
assert obj.read() == expected
|
|
402
|
+
|
|
403
|
+
|
|
346
404
|
def test_change_dropdown_choices(sample_template_with_dropdown, pdf_samples, request):
|
|
347
405
|
expected_path = os.path.join(
|
|
348
406
|
pdf_samples, "dropdown", "test_change_dropdown_choices.pdf"
|
|
@@ -358,3 +416,25 @@ def test_change_dropdown_choices(sample_template_with_dropdown, pdf_samples, req
|
|
|
358
416
|
|
|
359
417
|
assert len(obj.read()) == len(expected)
|
|
360
418
|
assert obj.read() == expected
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def test_change_dropdown_choices_with_export_values(
|
|
422
|
+
sample_template_with_dropdown, pdf_samples, request
|
|
423
|
+
):
|
|
424
|
+
expected_path = os.path.join(
|
|
425
|
+
pdf_samples, "dropdown", "test_change_dropdown_choices_with_export_values.pdf"
|
|
426
|
+
)
|
|
427
|
+
with open(expected_path, "rb+") as f:
|
|
428
|
+
obj = PdfWrapper(sample_template_with_dropdown, adobe_mode=True)
|
|
429
|
+
obj.widgets["dropdown_1"].choices = [
|
|
430
|
+
("apple", "apple_export"),
|
|
431
|
+
("banana", "banana_export"),
|
|
432
|
+
]
|
|
433
|
+
|
|
434
|
+
request.config.results["expected_path"] = expected_path
|
|
435
|
+
request.config.results["stream"] = obj.read()
|
|
436
|
+
|
|
437
|
+
expected = f.read()
|
|
438
|
+
|
|
439
|
+
assert len(obj.read()) == len(expected)
|
|
440
|
+
assert obj.read() == expected
|
|
@@ -55,6 +55,24 @@ def test_fill_flatten(template_stream, pdf_samples, data_dict, request):
|
|
|
55
55
|
assert obj.read() == expected
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
def test_fill_flatten_then_unflatten(template_stream, pdf_samples, data_dict, request):
|
|
59
|
+
expected_path = os.path.join(pdf_samples, "test_fill_flatten_then_unflatten.pdf")
|
|
60
|
+
with open(expected_path, "rb+") as f:
|
|
61
|
+
obj = PdfWrapper(template_stream).fill(data_dict, flatten=True)
|
|
62
|
+
obj.widgets["test_2"].readonly = False
|
|
63
|
+
obj.widgets["check_3"].readonly = False
|
|
64
|
+
|
|
65
|
+
request.config.results["expected_path"] = expected_path
|
|
66
|
+
request.config.results["stream"] = obj.read()
|
|
67
|
+
assert len(obj.read()) == len(obj.read())
|
|
68
|
+
assert obj.read() == obj.read()
|
|
69
|
+
|
|
70
|
+
expected = f.read()
|
|
71
|
+
|
|
72
|
+
assert len(obj.read()) == len(expected)
|
|
73
|
+
assert obj.read() == expected
|
|
74
|
+
|
|
75
|
+
|
|
58
76
|
def test_register_bad_fonts():
|
|
59
77
|
assert not PdfWrapper().register_font("foo", b"foo").read()
|
|
60
78
|
assert not PdfWrapper().register_font("foo", "foo").read()
|
|
@@ -324,6 +342,35 @@ def test_fill_radiobutton_flatten(
|
|
|
324
342
|
assert obj.read() == expected
|
|
325
343
|
|
|
326
344
|
|
|
345
|
+
def test_fill_radiobutton_flatten_then_unflatten(
|
|
346
|
+
template_with_radiobutton_stream, pdf_samples, request
|
|
347
|
+
):
|
|
348
|
+
expected_path = os.path.join(
|
|
349
|
+
pdf_samples, "test_fill_radiobutton_flatten_then_unflatten.pdf"
|
|
350
|
+
)
|
|
351
|
+
with open(
|
|
352
|
+
expected_path,
|
|
353
|
+
"rb+",
|
|
354
|
+
) as f:
|
|
355
|
+
obj = PdfWrapper(template_with_radiobutton_stream).fill(
|
|
356
|
+
{
|
|
357
|
+
"radio_1": 0,
|
|
358
|
+
"radio_2": 1,
|
|
359
|
+
"radio_3": 2,
|
|
360
|
+
},
|
|
361
|
+
flatten=True,
|
|
362
|
+
)
|
|
363
|
+
obj.widgets["radio_2"].readonly = False
|
|
364
|
+
|
|
365
|
+
request.config.results["expected_path"] = expected_path
|
|
366
|
+
request.config.results["stream"] = obj.read()
|
|
367
|
+
|
|
368
|
+
expected = f.read()
|
|
369
|
+
|
|
370
|
+
assert len(obj.read()) == len(expected)
|
|
371
|
+
assert obj.read() == expected
|
|
372
|
+
|
|
373
|
+
|
|
327
374
|
def test_fill_sejda(sejda_template, pdf_samples, sejda_data, request):
|
|
328
375
|
expected_path = os.path.join(pdf_samples, "test_fill_sejda.pdf")
|
|
329
376
|
with open(
|
|
@@ -361,6 +408,33 @@ def test_fill_sejda_flatten(sejda_template, pdf_samples, sejda_data, request):
|
|
|
361
408
|
assert obj.read() == expected
|
|
362
409
|
|
|
363
410
|
|
|
411
|
+
def test_fill_sejda_flatten_then_unflatten(
|
|
412
|
+
sejda_template, pdf_samples, sejda_data, request
|
|
413
|
+
):
|
|
414
|
+
expected_path = os.path.join(
|
|
415
|
+
pdf_samples, "test_fill_sejda_flatten_then_unflatten.pdf"
|
|
416
|
+
)
|
|
417
|
+
with open(
|
|
418
|
+
expected_path,
|
|
419
|
+
"rb+",
|
|
420
|
+
) as f:
|
|
421
|
+
obj = PdfWrapper(sejda_template).fill(
|
|
422
|
+
sejda_data,
|
|
423
|
+
flatten=True,
|
|
424
|
+
)
|
|
425
|
+
obj.widgets["buyer_name"].readonly = False
|
|
426
|
+
obj.widgets["at_future_date"].readonly = False
|
|
427
|
+
obj.widgets["purchase_option"].readonly = False
|
|
428
|
+
|
|
429
|
+
request.config.results["expected_path"] = expected_path
|
|
430
|
+
request.config.results["stream"] = obj.read()
|
|
431
|
+
|
|
432
|
+
expected = f.read()
|
|
433
|
+
|
|
434
|
+
assert len(obj.read()) == len(expected)
|
|
435
|
+
assert obj.read() == expected
|
|
436
|
+
|
|
437
|
+
|
|
364
438
|
def test_draw_text_on_one_page(template_stream, pdf_samples, request):
|
|
365
439
|
expected_path = os.path.join(pdf_samples, "test_draw_text_on_one_page.pdf")
|
|
366
440
|
with open(expected_path, "rb+") as f:
|
|
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
|