PyPDFForm 1.4.37__tar.gz → 1.5.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-1.4.37 → pypdfform-1.5.0}/PKG-INFO +1 -1
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/__init__.py +1 -1
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/constants.py +1 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/middleware/base.py +7 -1
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/middleware/checkbox.py +1 -1
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/middleware/dropdown.py +6 -1
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/middleware/radio.py +5 -1
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/middleware/text.py +1 -1
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/patterns.py +3 -1
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/template.py +38 -26
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/wrapper.py +52 -23
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm.egg-info/PKG-INFO +1 -1
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_functional.py +3 -7
- {pypdfform-1.4.37 → pypdfform-1.5.0}/LICENSE +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/adapter.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/coordinate.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/filler.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/font.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/image.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/middleware/__init__.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/middleware/image.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/middleware/signature.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/utils.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/watermark.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/widgets/__init__.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/widgets/base.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/widgets/checkbox.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/widgets/dropdown.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm/widgets/text.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm.egg-info/SOURCES.txt +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm.egg-info/dependency_links.txt +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm.egg-info/requires.txt +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/PyPDFForm.egg-info/top_level.txt +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/README.md +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/setup.cfg +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/setup.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_adobe_mode.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_create_widget.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_dropdown.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_dropdown_simple.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_fill_max_length_text_field.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_fill_max_length_text_field_simple.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_fill_method.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_functional_simple.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_paragraph.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_paragraph_simple.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_preview.py +0 -0
- {pypdfform-1.4.37 → pypdfform-1.5.0}/tests/test_signature.py +0 -0
|
@@ -17,6 +17,7 @@ class Widget:
|
|
|
17
17
|
super().__init__()
|
|
18
18
|
self._name = name
|
|
19
19
|
self._value = value
|
|
20
|
+
self.desc = None
|
|
20
21
|
|
|
21
22
|
@property
|
|
22
23
|
def name(self) -> str:
|
|
@@ -40,7 +41,12 @@ class Widget:
|
|
|
40
41
|
def schema_definition(self) -> dict:
|
|
41
42
|
"""Json schema definition of the widget."""
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
result = {}
|
|
45
|
+
|
|
46
|
+
if self.desc is not None:
|
|
47
|
+
result["description"] = self.desc
|
|
48
|
+
|
|
49
|
+
return result
|
|
44
50
|
|
|
45
51
|
@property
|
|
46
52
|
def sample_value(self) -> Any:
|
|
@@ -31,7 +31,7 @@ class Checkbox(Widget):
|
|
|
31
31
|
def schema_definition(self) -> dict:
|
|
32
32
|
"""Json schema definition of the checkbox."""
|
|
33
33
|
|
|
34
|
-
return {"type": "boolean"}
|
|
34
|
+
return {"type": "boolean", **super().schema_definition}
|
|
35
35
|
|
|
36
36
|
@property
|
|
37
37
|
def sample_value(self) -> Union[bool, int]:
|
|
@@ -17,12 +17,17 @@ class Dropdown(Widget):
|
|
|
17
17
|
super().__init__(name, value)
|
|
18
18
|
|
|
19
19
|
self.choices = []
|
|
20
|
+
self.desc = None
|
|
20
21
|
|
|
21
22
|
@property
|
|
22
23
|
def schema_definition(self) -> dict:
|
|
23
24
|
"""Json schema definition of the dropdown."""
|
|
24
25
|
|
|
25
|
-
return {
|
|
26
|
+
return {
|
|
27
|
+
"type": "integer",
|
|
28
|
+
"maximum": len(self.choices) - 1,
|
|
29
|
+
**super().schema_definition,
|
|
30
|
+
}
|
|
26
31
|
|
|
27
32
|
@property
|
|
28
33
|
def sample_value(self) -> int:
|
|
@@ -22,7 +22,11 @@ class Radio(Checkbox):
|
|
|
22
22
|
def schema_definition(self) -> dict:
|
|
23
23
|
"""Json schema definition of the radiobutton."""
|
|
24
24
|
|
|
25
|
-
return {
|
|
25
|
+
return {
|
|
26
|
+
"maximum": self.number_of_options - 1,
|
|
27
|
+
**super().schema_definition,
|
|
28
|
+
"type": "integer",
|
|
29
|
+
}
|
|
26
30
|
|
|
27
31
|
@property
|
|
28
32
|
def sample_value(self) -> int:
|
|
@@ -5,7 +5,7 @@ from pypdf.generic import (DictionaryObject, NameObject, NumberObject,
|
|
|
5
5
|
TextStringObject)
|
|
6
6
|
|
|
7
7
|
from .constants import (AP, AS, CA, DA, DV, FT, IMAGE_FIELD_IDENTIFIER, JS, MK,
|
|
8
|
-
MULTILINE, READ_ONLY, A, Btn, Ch, Ff, N, Off, Opt,
|
|
8
|
+
MULTILINE, READ_ONLY, TU, A, Btn, Ch, Ff, N, Off, Opt,
|
|
9
9
|
Parent, Q, Sig, T, Tx, V, Yes)
|
|
10
10
|
from .middleware.checkbox import Checkbox
|
|
11
11
|
from .middleware.dropdown import Dropdown
|
|
@@ -68,6 +68,8 @@ WIDGET_KEY_PATTERNS = [
|
|
|
68
68
|
{Parent: {T: True}},
|
|
69
69
|
]
|
|
70
70
|
|
|
71
|
+
WIDGET_DESCRIPTION_PATTERNS = [{TU: True}, {Parent: {TU: True}}]
|
|
72
|
+
|
|
71
73
|
DROPDOWN_CHOICE_PATTERNS = [
|
|
72
74
|
{Opt: True},
|
|
73
75
|
{Parent: {Opt: True}},
|
|
@@ -21,8 +21,8 @@ from .middleware.radio import Radio
|
|
|
21
21
|
from .middleware.text import Text
|
|
22
22
|
from .patterns import (BUTTON_STYLE_PATTERNS, DROPDOWN_CHOICE_PATTERNS,
|
|
23
23
|
TEXT_FIELD_FLAG_PATTERNS, WIDGET_ALIGNMENT_PATTERNS,
|
|
24
|
-
|
|
25
|
-
update_annotation_name)
|
|
24
|
+
WIDGET_DESCRIPTION_PATTERNS, WIDGET_KEY_PATTERNS,
|
|
25
|
+
WIDGET_TYPE_PATTERNS, update_annotation_name)
|
|
26
26
|
from .utils import find_pattern_match, stream_to_io, traverse_pattern
|
|
27
27
|
from .watermark import create_watermarks_and_draw
|
|
28
28
|
|
|
@@ -51,10 +51,9 @@ def build_widgets(pdf_stream: bytes) -> Dict[str, WIDGET_TYPES]:
|
|
|
51
51
|
for widgets in get_widgets_by_page(pdf_stream).values():
|
|
52
52
|
for widget in widgets:
|
|
53
53
|
key = get_widget_key(widget)
|
|
54
|
-
|
|
55
54
|
_widget = construct_widget(widget, key)
|
|
56
|
-
|
|
57
55
|
if _widget is not None:
|
|
56
|
+
_widget.desc = get_widget_description(widget)
|
|
58
57
|
if isinstance(_widget, Text):
|
|
59
58
|
_widget.max_length = get_text_field_max_length(widget)
|
|
60
59
|
if _widget.max_length is not None and is_text_field_comb(widget):
|
|
@@ -74,7 +73,6 @@ def build_widgets(pdf_stream: bytes) -> Dict[str, WIDGET_TYPES]:
|
|
|
74
73
|
continue
|
|
75
74
|
|
|
76
75
|
results[key] = _widget
|
|
77
|
-
|
|
78
76
|
return results
|
|
79
77
|
|
|
80
78
|
|
|
@@ -225,6 +223,18 @@ def get_text_field_max_length(widget: dict) -> Union[int, None]:
|
|
|
225
223
|
return int(widget[MaxLen]) or None if MaxLen in widget else None
|
|
226
224
|
|
|
227
225
|
|
|
226
|
+
def get_widget_description(widget: dict) -> Union[str, None]:
|
|
227
|
+
"""Returns the description of the widget if presented or None."""
|
|
228
|
+
|
|
229
|
+
result = None
|
|
230
|
+
for pattern in WIDGET_DESCRIPTION_PATTERNS:
|
|
231
|
+
value = traverse_pattern(pattern, widget)
|
|
232
|
+
if value:
|
|
233
|
+
result = str(value)
|
|
234
|
+
break
|
|
235
|
+
return result
|
|
236
|
+
|
|
237
|
+
|
|
228
238
|
def check_field_flag_bit(widget: dict, bit: int) -> bool:
|
|
229
239
|
"""Checks if a bit is set in a widget's field flag."""
|
|
230
240
|
|
|
@@ -408,39 +418,41 @@ def get_paragraph_auto_wrap_length(widget_middleware: Text) -> int:
|
|
|
408
418
|
return result
|
|
409
419
|
|
|
410
420
|
|
|
411
|
-
def
|
|
421
|
+
def update_widget_keys(
|
|
412
422
|
template: bytes,
|
|
413
423
|
widgets: Dict[str, WIDGET_TYPES],
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
424
|
+
old_keys: List[str],
|
|
425
|
+
new_keys: List[str],
|
|
426
|
+
indices: List[int],
|
|
417
427
|
) -> bytes:
|
|
418
|
-
"""Updates
|
|
428
|
+
"""Updates a list of keys of widgets."""
|
|
419
429
|
# pylint: disable=R0801
|
|
420
430
|
|
|
421
431
|
pdf = PdfReader(stream_to_io(template))
|
|
422
432
|
out = PdfWriter()
|
|
423
433
|
out.append(pdf)
|
|
424
434
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
+
for i, old_key in enumerate(old_keys):
|
|
436
|
+
index = indices[i]
|
|
437
|
+
new_key = new_keys[i]
|
|
438
|
+
tracker = -1
|
|
439
|
+
for page in out.pages:
|
|
440
|
+
for annot in page.get(Annots, []): # noqa
|
|
441
|
+
annot = cast(DictionaryObject, annot.get_object())
|
|
442
|
+
key = get_widget_key(annot.get_object())
|
|
443
|
+
|
|
444
|
+
widget = widgets.get(key)
|
|
445
|
+
if widget is None:
|
|
446
|
+
continue
|
|
435
447
|
|
|
436
|
-
|
|
437
|
-
|
|
448
|
+
if old_key != key:
|
|
449
|
+
continue
|
|
438
450
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
451
|
+
tracker += 1
|
|
452
|
+
if not isinstance(widget, Radio) and tracker != index:
|
|
453
|
+
continue
|
|
442
454
|
|
|
443
|
-
|
|
455
|
+
update_annotation_name(annot, new_key)
|
|
444
456
|
|
|
445
457
|
with BytesIO() as f:
|
|
446
458
|
out.write(f)
|
|
@@ -18,7 +18,7 @@ from .middleware.dropdown import Dropdown
|
|
|
18
18
|
from .middleware.text import Text
|
|
19
19
|
from .template import (build_widgets, dropdown_to_text,
|
|
20
20
|
set_character_x_paddings, update_text_field_attributes,
|
|
21
|
-
|
|
21
|
+
update_widget_keys, widget_rect_watermarks)
|
|
22
22
|
from .utils import (get_page_streams, merge_two_pdfs, preview_widget_to_draw,
|
|
23
23
|
remove_all_widgets)
|
|
24
24
|
from .watermark import create_watermarks_and_draw, merge_watermarks_with_pdf
|
|
@@ -79,17 +79,35 @@ class PdfWrapper(FormWrapper):
|
|
|
79
79
|
"""Constructs all attributes for the object."""
|
|
80
80
|
|
|
81
81
|
super().__init__(template)
|
|
82
|
-
self.widgets =
|
|
82
|
+
self.widgets = {}
|
|
83
|
+
self._keys_to_update = []
|
|
83
84
|
|
|
84
85
|
self.global_font = kwargs.get("global_font")
|
|
85
86
|
self.global_font_size = kwargs.get("global_font_size")
|
|
86
87
|
self.global_font_color = kwargs.get("global_font_color")
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
self._init_helper()
|
|
90
|
+
|
|
91
|
+
def _init_helper(self, key_to_refresh: str = None) -> None:
|
|
92
|
+
"""Updates all attributes when the state of the PDF stream changes."""
|
|
93
|
+
|
|
94
|
+
refresh_not_needed = {}
|
|
95
|
+
new_widgets = build_widgets(self.read()) if self.read() else {}
|
|
96
|
+
for k, v in self.widgets.items():
|
|
97
|
+
if k in new_widgets:
|
|
98
|
+
new_widgets[k] = v
|
|
99
|
+
refresh_not_needed[k] = True
|
|
100
|
+
self.widgets = new_widgets
|
|
101
|
+
|
|
102
|
+
for key, value in self.widgets.items():
|
|
103
|
+
if (key_to_refresh and key == key_to_refresh) or (
|
|
104
|
+
key_to_refresh is None
|
|
105
|
+
and isinstance(value, Text)
|
|
106
|
+
and not refresh_not_needed.get(key)
|
|
107
|
+
):
|
|
108
|
+
value.font = self.global_font
|
|
109
|
+
value.font_size = self.global_font_size
|
|
110
|
+
value.font_color = self.global_font_color
|
|
93
111
|
|
|
94
112
|
@property
|
|
95
113
|
def sample_data(self) -> dict:
|
|
@@ -223,31 +241,42 @@ class PdfWrapper(FormWrapper):
|
|
|
223
241
|
self.stream, name, obj.non_acro_form_params
|
|
224
242
|
)
|
|
225
243
|
|
|
226
|
-
|
|
227
|
-
for k, v in self.widgets.items():
|
|
228
|
-
if k in new_widgets:
|
|
229
|
-
new_widgets[k] = v
|
|
230
|
-
self.widgets = new_widgets
|
|
244
|
+
key_to_refresh = ""
|
|
231
245
|
if widget_type in ("text", "dropdown"):
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
246
|
+
key_to_refresh = name
|
|
247
|
+
|
|
248
|
+
self._init_helper(key_to_refresh)
|
|
235
249
|
|
|
236
250
|
return self
|
|
237
251
|
|
|
238
252
|
def update_widget_key(
|
|
239
|
-
self, old_key: str, new_key: str, index: int = 0
|
|
253
|
+
self, old_key: str, new_key: str, index: int = 0, defer: bool = False
|
|
240
254
|
) -> PdfWrapper:
|
|
241
255
|
"""Updates the key of an existed widget on a PDF form."""
|
|
242
256
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
257
|
+
if defer:
|
|
258
|
+
self._keys_to_update.append((old_key, new_key, index))
|
|
259
|
+
return self
|
|
260
|
+
|
|
261
|
+
self.stream = update_widget_keys(
|
|
262
|
+
self.read(), self.widgets, [old_key], [new_key], [index]
|
|
263
|
+
)
|
|
264
|
+
self._init_helper()
|
|
265
|
+
|
|
266
|
+
return self
|
|
267
|
+
|
|
268
|
+
def commit_widget_key_updates(self) -> PdfWrapper:
|
|
269
|
+
"""Commits all deferred widget key updates on a PDF form."""
|
|
270
|
+
|
|
271
|
+
old_keys = [each[0] for each in self._keys_to_update]
|
|
272
|
+
new_keys = [each[1] for each in self._keys_to_update]
|
|
273
|
+
indices = [each[2] for each in self._keys_to_update]
|
|
274
|
+
|
|
275
|
+
self.stream = update_widget_keys(
|
|
276
|
+
self.read(), self.widgets, old_keys, new_keys, indices
|
|
250
277
|
)
|
|
278
|
+
self._init_helper()
|
|
279
|
+
self._keys_to_update = []
|
|
251
280
|
|
|
252
281
|
return self
|
|
253
282
|
|
|
@@ -10,11 +10,7 @@ from PyPDFForm.middleware.text import Text
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def test_base_schema_definition():
|
|
13
|
-
|
|
14
|
-
assert Widget("foo").schema_definition
|
|
15
|
-
raise AssertionError
|
|
16
|
-
except NotImplementedError:
|
|
17
|
-
pass
|
|
13
|
+
assert Widget("foo").schema_definition == {}
|
|
18
14
|
|
|
19
15
|
|
|
20
16
|
def test_fill(template_stream, pdf_samples, data_dict, request):
|
|
@@ -635,7 +631,7 @@ def test_update_radio_key(template_with_radiobutton_stream, pdf_samples, request
|
|
|
635
631
|
obj.update_widget_key("radio_3", "RADIO")
|
|
636
632
|
|
|
637
633
|
request.config.results["expected_path"] = expected_path
|
|
638
|
-
request.config.results["stream"] = obj.
|
|
634
|
+
request.config.results["stream"] = obj.preview
|
|
639
635
|
|
|
640
636
|
expected = f.read()
|
|
641
637
|
|
|
@@ -653,7 +649,7 @@ def test_update_sejda_key(sejda_template, pdf_samples, request):
|
|
|
653
649
|
obj.update_widget_key("buyer_signed_date", "BUYER_SIGNED_DATE")
|
|
654
650
|
|
|
655
651
|
request.config.results["expected_path"] = expected_path
|
|
656
|
-
request.config.results["stream"] = obj.
|
|
652
|
+
request.config.results["stream"] = obj.preview
|
|
657
653
|
|
|
658
654
|
expected = f.read()
|
|
659
655
|
|
|
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
|