PyPDFForm 2.4.0__py3-none-any.whl → 2.5.0__py3-none-any.whl
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/__init__.py +1 -1
- PyPDFForm/hooks.py +216 -0
- PyPDFForm/middleware/base.py +24 -0
- PyPDFForm/middleware/checkbox.py +4 -0
- PyPDFForm/middleware/text.py +5 -0
- PyPDFForm/patterns.py +3 -38
- PyPDFForm/widgets/base.py +1 -1
- PyPDFForm/widgets/text.py +1 -1
- PyPDFForm/wrapper.py +59 -31
- {pypdfform-2.4.0.dist-info → pypdfform-2.5.0.dist-info}/METADATA +1 -1
- {pypdfform-2.4.0.dist-info → pypdfform-2.5.0.dist-info}/RECORD +14 -13
- {pypdfform-2.4.0.dist-info → pypdfform-2.5.0.dist-info}/WHEEL +1 -1
- {pypdfform-2.4.0.dist-info → pypdfform-2.5.0.dist-info}/licenses/LICENSE +0 -0
- {pypdfform-2.4.0.dist-info → pypdfform-2.5.0.dist-info}/top_level.txt +0 -0
PyPDFForm/__init__.py
CHANGED
PyPDFForm/hooks.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Module containing hook functions for PDF form widget manipulation.
|
|
3
|
+
|
|
4
|
+
This module provides functions to apply various transformations and modifications
|
|
5
|
+
to PDF form widgets through a hook system. It allows dynamic modification of
|
|
6
|
+
widget properties like font sizes and other attributes.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
from io import BytesIO
|
|
11
|
+
from typing import cast
|
|
12
|
+
|
|
13
|
+
from pypdf import PdfReader, PdfWriter
|
|
14
|
+
from pypdf.generic import (ArrayObject, DictionaryObject, FloatObject,
|
|
15
|
+
NameObject, NumberObject, TextStringObject)
|
|
16
|
+
|
|
17
|
+
from .constants import (COMB, DA, FONT_COLOR_IDENTIFIER, FONT_SIZE_IDENTIFIER,
|
|
18
|
+
MULTILINE, Annots, Ff, Parent, Q, Rect)
|
|
19
|
+
from .template import get_widget_key
|
|
20
|
+
from .utils import stream_to_io
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def trigger_widget_hooks(
|
|
24
|
+
pdf: bytes,
|
|
25
|
+
widgets: dict,
|
|
26
|
+
use_full_widget_name: bool,
|
|
27
|
+
) -> bytes:
|
|
28
|
+
"""Apply all registered widget hooks to a PDF document.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
pdf: The input PDF document as bytes
|
|
32
|
+
widgets: Dictionary mapping widget names to widget objects
|
|
33
|
+
use_full_widget_name: Whether to use full widget names including parent hierarchy
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
The modified PDF document as bytes
|
|
37
|
+
|
|
38
|
+
Note:
|
|
39
|
+
This function processes all pages and annotations in the PDF, applying
|
|
40
|
+
any hooks registered in the widget objects.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
pdf_file = PdfReader(stream_to_io(pdf))
|
|
44
|
+
output = PdfWriter()
|
|
45
|
+
output.append(pdf_file)
|
|
46
|
+
|
|
47
|
+
for page in output.pages:
|
|
48
|
+
for annot in page.get(Annots, []):
|
|
49
|
+
annot = cast(DictionaryObject, annot.get_object())
|
|
50
|
+
key = get_widget_key(annot.get_object(), use_full_widget_name)
|
|
51
|
+
|
|
52
|
+
widget = widgets.get(key)
|
|
53
|
+
if widget is None or not widget.hooks_to_trigger:
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
for hook in widget.hooks_to_trigger:
|
|
57
|
+
getattr(sys.modules[__name__], hook[0])(annot, hook[1])
|
|
58
|
+
|
|
59
|
+
for widget in widgets.values():
|
|
60
|
+
widget.hooks_to_trigger = []
|
|
61
|
+
|
|
62
|
+
with BytesIO() as f:
|
|
63
|
+
output.write(f)
|
|
64
|
+
f.seek(0)
|
|
65
|
+
return f.read()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def update_text_field_font_size(annot: DictionaryObject, val: float) -> None:
|
|
69
|
+
"""Update the font size of a text field widget.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
annot: The PDF annotation (widget) dictionary object
|
|
73
|
+
val: The new font size value to apply
|
|
74
|
+
|
|
75
|
+
Note:
|
|
76
|
+
Handles both direct font size specification and inherited font sizes
|
|
77
|
+
from parent objects. Modifies the DA (default appearance) string.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
if Parent in annot and DA not in annot:
|
|
81
|
+
text_appearance = annot[Parent][DA]
|
|
82
|
+
else:
|
|
83
|
+
text_appearance = annot[DA]
|
|
84
|
+
|
|
85
|
+
text_appearance = text_appearance.split(" ")
|
|
86
|
+
font_size_index = 0
|
|
87
|
+
for i, value in enumerate(text_appearance):
|
|
88
|
+
if value.startswith(FONT_SIZE_IDENTIFIER):
|
|
89
|
+
font_size_index = i - 1
|
|
90
|
+
break
|
|
91
|
+
|
|
92
|
+
text_appearance[font_size_index] = str(val)
|
|
93
|
+
new_text_appearance = " ".join(text_appearance)
|
|
94
|
+
|
|
95
|
+
if Parent in annot and DA not in annot:
|
|
96
|
+
annot[NameObject(Parent)][NameObject(DA)] = TextStringObject(
|
|
97
|
+
new_text_appearance
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
annot[NameObject(DA)] = TextStringObject(new_text_appearance)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def update_text_field_font_color(annot: DictionaryObject, val: tuple) -> None:
|
|
104
|
+
"""Update the font color of a text field widget.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
annot: The PDF annotation (widget) dictionary object
|
|
108
|
+
val: Tuple containing RGB color values (e.g. (1, 0, 0) for red)
|
|
109
|
+
|
|
110
|
+
Note:
|
|
111
|
+
Handles both direct font color specification and inherited font colors
|
|
112
|
+
from parent objects. Modifies the DA (default appearance) string to
|
|
113
|
+
include the new color values after the font size identifier.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
if Parent in annot and DA not in annot:
|
|
117
|
+
text_appearance = annot[Parent][DA]
|
|
118
|
+
else:
|
|
119
|
+
text_appearance = annot[DA]
|
|
120
|
+
|
|
121
|
+
text_appearance = text_appearance.split(" ")
|
|
122
|
+
font_size_identifier_index = 0
|
|
123
|
+
for i, value in enumerate(text_appearance):
|
|
124
|
+
if value == FONT_SIZE_IDENTIFIER:
|
|
125
|
+
font_size_identifier_index = i
|
|
126
|
+
break
|
|
127
|
+
|
|
128
|
+
new_text_appearance = (
|
|
129
|
+
text_appearance[:font_size_identifier_index]
|
|
130
|
+
+ [FONT_SIZE_IDENTIFIER]
|
|
131
|
+
+ [str(each) for each in val]
|
|
132
|
+
)
|
|
133
|
+
new_text_appearance = " ".join(new_text_appearance) + FONT_COLOR_IDENTIFIER
|
|
134
|
+
|
|
135
|
+
if Parent in annot and DA not in annot:
|
|
136
|
+
annot[NameObject(Parent)][NameObject(DA)] = TextStringObject(
|
|
137
|
+
new_text_appearance
|
|
138
|
+
)
|
|
139
|
+
else:
|
|
140
|
+
annot[NameObject(DA)] = TextStringObject(new_text_appearance)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def update_text_field_alignment(annot: DictionaryObject, val: int) -> None:
|
|
144
|
+
"""Update text alignment for text field annotations.
|
|
145
|
+
|
|
146
|
+
Modifies the alignment (Q) field of a text field annotation to set the
|
|
147
|
+
specified text alignment.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
annot: PDF text field annotation dictionary to modify
|
|
151
|
+
val: Alignment value to set (typically 0=left, 1=center, 2=right)
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
annot[NameObject(Q)] = NumberObject(val)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def update_text_field_multiline(annot: DictionaryObject, val: bool) -> None:
|
|
158
|
+
"""Update multiline flag for text field annotations.
|
|
159
|
+
|
|
160
|
+
Modifies the field flags (Ff) of a text field annotation to set or
|
|
161
|
+
clear the multiline flag based on the input value.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
annot: PDF text field annotation dictionary to modify
|
|
165
|
+
val: Whether to enable multiline (True) or disable (False)
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
if val:
|
|
169
|
+
annot[NameObject(Ff)] = NumberObject(int(annot[NameObject(Ff)]) | MULTILINE)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def update_text_field_comb(annot: DictionaryObject, val: bool) -> None:
|
|
173
|
+
"""Update comb formatting flag for text field annotations.
|
|
174
|
+
|
|
175
|
+
Modifies the field flags (Ff) of a text field annotation to set or
|
|
176
|
+
clear the comb flag which enables/disables comb formatting.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
annot: PDF text field annotation dictionary to modify
|
|
180
|
+
val: Whether to enable comb formatting (True) or disable (False)
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
if val:
|
|
184
|
+
annot[NameObject(Ff)] = NumberObject(int(annot[NameObject(Ff)]) | COMB)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def update_check_radio_size(annot: DictionaryObject, val: float) -> None:
|
|
188
|
+
"""Update the size of a checkbox or radio button widget while maintaining center position.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
annot: PDF annotation dictionary containing the widget to modify
|
|
192
|
+
val: New size value (width and height) for the widget
|
|
193
|
+
|
|
194
|
+
Note:
|
|
195
|
+
The widget will be resized symmetrically around its center point,
|
|
196
|
+
maintaining the same center position while changing its dimensions.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
rect = annot[Rect]
|
|
200
|
+
center_x = (rect[0] + rect[2]) / 2
|
|
201
|
+
center_y = (rect[1] + rect[3]) / 2
|
|
202
|
+
new_rect = [
|
|
203
|
+
FloatObject(center_x - val / 2),
|
|
204
|
+
FloatObject(center_y - val / 2),
|
|
205
|
+
FloatObject(center_x + val / 2),
|
|
206
|
+
FloatObject(center_y + val / 2),
|
|
207
|
+
]
|
|
208
|
+
annot[NameObject(Rect)] = ArrayObject(new_rect)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# TODO: remove this and switch to hooks
|
|
212
|
+
NON_ACRO_FORM_PARAM_TO_FUNC = {
|
|
213
|
+
("TextWidget", "alignment"): update_text_field_alignment,
|
|
214
|
+
("TextWidget", "multiline"): update_text_field_multiline,
|
|
215
|
+
("TextWidget", "comb"): update_text_field_comb,
|
|
216
|
+
}
|
PyPDFForm/middleware/base.py
CHANGED
|
@@ -26,6 +26,8 @@ class Widget:
|
|
|
26
26
|
- Any widget-specific functionality
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
|
+
SET_ATTR_TRIGGER_HOOK_MAP = {}
|
|
30
|
+
|
|
29
31
|
def __init__(
|
|
30
32
|
self,
|
|
31
33
|
name: str,
|
|
@@ -48,6 +50,28 @@ class Widget:
|
|
|
48
50
|
self.border_style = None
|
|
49
51
|
self.dash_array = None
|
|
50
52
|
self.render_widget = None
|
|
53
|
+
self.hooks_to_trigger = []
|
|
54
|
+
|
|
55
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
56
|
+
"""Sets an attribute on the widget with special handling for hook-triggering attributes.
|
|
57
|
+
|
|
58
|
+
For attributes listed in SET_ATTR_TRIGGER_HOOK_MAP, when set to non-None values,
|
|
59
|
+
adds a hook to hooks_to_trigger list with the mapped hook name and value.
|
|
60
|
+
All other attributes are set normally via the standard object.__setattr__.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
name: Name of the attribute to set
|
|
64
|
+
value: Value to set the attribute to
|
|
65
|
+
|
|
66
|
+
Note:
|
|
67
|
+
The hook triggering only occurs when:
|
|
68
|
+
1. The attribute is in SET_ATTR_TRIGGER_HOOK_MAP
|
|
69
|
+
2. The value being set is not None
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
if name in self.SET_ATTR_TRIGGER_HOOK_MAP and value is not None:
|
|
73
|
+
self.hooks_to_trigger.append((self.SET_ATTR_TRIGGER_HOOK_MAP[name], value))
|
|
74
|
+
super().__setattr__(name, value)
|
|
51
75
|
|
|
52
76
|
@property
|
|
53
77
|
def name(self) -> str:
|
PyPDFForm/middleware/checkbox.py
CHANGED
|
@@ -25,6 +25,10 @@ class Checkbox(Widget):
|
|
|
25
25
|
Inherits from Widget base class and extends it with checkbox-specific features.
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
+
SET_ATTR_TRIGGER_HOOK_MAP = {
|
|
29
|
+
"size": "update_check_radio_size",
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
BUTTON_STYLE_MAPPING = {
|
|
29
33
|
"check": "4",
|
|
30
34
|
"cross": "5",
|
PyPDFForm/middleware/text.py
CHANGED
|
@@ -27,6 +27,11 @@ class Text(Widget):
|
|
|
27
27
|
Inherits from Widget base class and extends it with text-specific features.
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
|
+
SET_ATTR_TRIGGER_HOOK_MAP = {
|
|
31
|
+
"font_size": "update_text_field_font_size",
|
|
32
|
+
"font_color": "update_text_field_font_color",
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
def __init__(
|
|
31
36
|
self,
|
|
32
37
|
name: str,
|
PyPDFForm/patterns.py
CHANGED
|
@@ -19,9 +19,9 @@ from pypdf.generic import (ArrayObject, DictionaryObject, NameObject,
|
|
|
19
19
|
NumberObject, TextStringObject)
|
|
20
20
|
|
|
21
21
|
from .constants import (AP, AS, BC, BG, BS, CA, DA, DV, FT,
|
|
22
|
-
IMAGE_FIELD_IDENTIFIER, JS, MK,
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
IMAGE_FIELD_IDENTIFIER, JS, MK, READ_ONLY, TU, A, Btn,
|
|
23
|
+
Ch, D, Ff, I, N, Off, Opt, Parent, Q, S, Sig, T, Tx, V,
|
|
24
|
+
W, Yes)
|
|
25
25
|
from .middleware.checkbox import Checkbox
|
|
26
26
|
from .middleware.dropdown import Dropdown
|
|
27
27
|
from .middleware.image import Image
|
|
@@ -273,38 +273,3 @@ def update_annotation_name(annot: DictionaryObject, val: str) -> None:
|
|
|
273
273
|
annot[NameObject(Parent)][NameObject(T)] = TextStringObject(val)
|
|
274
274
|
else:
|
|
275
275
|
annot[NameObject(T)] = TextStringObject(val)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
def update_created_text_field_alignment(annot: DictionaryObject, val: int) -> None:
|
|
279
|
-
"""Update text alignment for created text field annotations.
|
|
280
|
-
|
|
281
|
-
Modifies the alignment (Q) field of a text field annotation created
|
|
282
|
-
by the library to set the specified text alignment.
|
|
283
|
-
|
|
284
|
-
Args:
|
|
285
|
-
annot: PDF text field annotation dictionary to modify
|
|
286
|
-
val: Alignment value to set (typically 0=left, 1=center, 2=right)
|
|
287
|
-
"""
|
|
288
|
-
|
|
289
|
-
annot[NameObject(Q)] = NumberObject(val)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
def update_created_text_field_multiline(annot: DictionaryObject, val: bool) -> None:
|
|
293
|
-
"""Update multiline flag for created text field annotations.
|
|
294
|
-
|
|
295
|
-
Modifies the field flags (Ff) of a text field annotation created by
|
|
296
|
-
the library to set or clear the multiline flag based on the input value.
|
|
297
|
-
|
|
298
|
-
Args:
|
|
299
|
-
annot: PDF text field annotation dictionary to modify
|
|
300
|
-
val: Whether to enable multiline (True) or disable (False)
|
|
301
|
-
"""
|
|
302
|
-
|
|
303
|
-
if val:
|
|
304
|
-
annot[NameObject(Ff)] = NumberObject(int(annot[NameObject(Ff)]) | MULTILINE)
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
NON_ACRO_FORM_PARAM_TO_FUNC = {
|
|
308
|
-
("TextWidget", "alignment"): update_created_text_field_alignment,
|
|
309
|
-
("TextWidget", "multiline"): update_created_text_field_multiline,
|
|
310
|
-
}
|
PyPDFForm/widgets/base.py
CHANGED
|
@@ -16,7 +16,7 @@ from reportlab.lib.colors import Color
|
|
|
16
16
|
from reportlab.pdfgen.canvas import Canvas
|
|
17
17
|
|
|
18
18
|
from ..constants import Annots
|
|
19
|
-
from ..
|
|
19
|
+
from ..hooks import NON_ACRO_FORM_PARAM_TO_FUNC
|
|
20
20
|
from ..template import get_widget_key
|
|
21
21
|
from ..utils import stream_to_io
|
|
22
22
|
|
PyPDFForm/widgets/text.py
CHANGED
|
@@ -36,6 +36,6 @@ class TextWidget(Widget):
|
|
|
36
36
|
("max_length", "maxlen"),
|
|
37
37
|
]
|
|
38
38
|
COLOR_PARAMS = ["font_color", "bg_color", "border_color"]
|
|
39
|
-
ALLOWED_NON_ACRO_FORM_PARAMS = ["alignment", "multiline"]
|
|
39
|
+
ALLOWED_NON_ACRO_FORM_PARAMS = ["alignment", "multiline", "comb"]
|
|
40
40
|
NONE_DEFAULTS = ["max_length"]
|
|
41
41
|
ACRO_FORM_FUNC = "textfield"
|
PyPDFForm/wrapper.py
CHANGED
|
@@ -26,6 +26,7 @@ from .constants import (DEFAULT_FONT, DEFAULT_FONT_COLOR, DEFAULT_FONT_SIZE,
|
|
|
26
26
|
from .coordinate import generate_coordinate_grid
|
|
27
27
|
from .filler import fill, simple_fill
|
|
28
28
|
from .font import register_font
|
|
29
|
+
from .hooks import trigger_widget_hooks
|
|
29
30
|
from .image import rotate_image
|
|
30
31
|
from .middleware.dropdown import Dropdown
|
|
31
32
|
from .middleware.text import Text
|
|
@@ -84,7 +85,7 @@ class FormWrapper:
|
|
|
84
85
|
"""
|
|
85
86
|
|
|
86
87
|
super().__init__()
|
|
87
|
-
self.
|
|
88
|
+
self._stream = fp_or_f_obj_or_stream_to_stream(template)
|
|
88
89
|
self.use_full_widget_name = kwargs.get("use_full_widget_name", False)
|
|
89
90
|
|
|
90
91
|
def read(self) -> bytes:
|
|
@@ -98,7 +99,7 @@ class FormWrapper:
|
|
|
98
99
|
bytes: The complete PDF document as a byte string
|
|
99
100
|
"""
|
|
100
101
|
|
|
101
|
-
return self.
|
|
102
|
+
return self._stream
|
|
102
103
|
|
|
103
104
|
def fill(
|
|
104
105
|
self,
|
|
@@ -132,8 +133,8 @@ class FormWrapper:
|
|
|
132
133
|
"""
|
|
133
134
|
|
|
134
135
|
widgets = (
|
|
135
|
-
build_widgets(self.
|
|
136
|
-
if self.
|
|
136
|
+
build_widgets(self.read(), self.use_full_widget_name, False)
|
|
137
|
+
if self.read()
|
|
137
138
|
else {}
|
|
138
139
|
)
|
|
139
140
|
|
|
@@ -141,7 +142,7 @@ class FormWrapper:
|
|
|
141
142
|
if key in widgets:
|
|
142
143
|
widgets[key].value = value
|
|
143
144
|
|
|
144
|
-
self.
|
|
145
|
+
self._stream = simple_fill(
|
|
145
146
|
self.read(),
|
|
146
147
|
widgets,
|
|
147
148
|
use_full_widget_name=self.use_full_widget_name,
|
|
@@ -177,6 +178,8 @@ class PdfWrapper(FormWrapper):
|
|
|
177
178
|
("use_full_widget_name", False),
|
|
178
179
|
("render_widgets", True),
|
|
179
180
|
]
|
|
181
|
+
# TODO: remove, always default to True
|
|
182
|
+
TRIGGER_WIDGET_HOOKS = False
|
|
180
183
|
|
|
181
184
|
def __init__(
|
|
182
185
|
self,
|
|
@@ -255,6 +258,31 @@ class PdfWrapper(FormWrapper):
|
|
|
255
258
|
value.font_size = getattr(self, "global_font_size")
|
|
256
259
|
value.font_color = getattr(self, "global_font_color")
|
|
257
260
|
|
|
261
|
+
def read(self) -> bytes:
|
|
262
|
+
"""Returns the raw bytes of the PDF form data with optional widget hook processing.
|
|
263
|
+
|
|
264
|
+
Extends FormWrapper.read() with additional functionality:
|
|
265
|
+
- Triggers any registered widget hooks if TRIGGER_WIDGET_HOOKS is True
|
|
266
|
+
- Maintains all parent class behavior of returning raw PDF bytes
|
|
267
|
+
|
|
268
|
+
The method first processes any widget hooks that need triggering, then delegates
|
|
269
|
+
to the parent class's read() implementation to return the PDF bytes.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
bytes: The complete PDF document as a byte string, after any hook processing
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
if self.TRIGGER_WIDGET_HOOKS and any(
|
|
276
|
+
widget.hooks_to_trigger for widget in self.widgets.values()
|
|
277
|
+
):
|
|
278
|
+
self._stream = trigger_widget_hooks(
|
|
279
|
+
self._stream,
|
|
280
|
+
self.widgets,
|
|
281
|
+
getattr(self, "use_full_widget_name"),
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
return super().read()
|
|
285
|
+
|
|
258
286
|
@property
|
|
259
287
|
def sample_data(self) -> dict:
|
|
260
288
|
"""Generates a dictionary of sample values for all form fields.
|
|
@@ -287,7 +315,7 @@ class PdfWrapper(FormWrapper):
|
|
|
287
315
|
"""
|
|
288
316
|
|
|
289
317
|
for each in VERSION_IDENTIFIERS:
|
|
290
|
-
if self.
|
|
318
|
+
if self.read().startswith(each):
|
|
291
319
|
return each.replace(VERSION_IDENTIFIER_PREFIX, b"").decode()
|
|
292
320
|
|
|
293
321
|
return None
|
|
@@ -309,7 +337,7 @@ class PdfWrapper(FormWrapper):
|
|
|
309
337
|
|
|
310
338
|
return [
|
|
311
339
|
self.__class__(
|
|
312
|
-
copy_watermark_widgets(each, self.
|
|
340
|
+
copy_watermark_widgets(each, self.read(), None, i),
|
|
313
341
|
**{param: getattr(self, param) for param, _ in self.USER_PARAMS},
|
|
314
342
|
)
|
|
315
343
|
for i, each in enumerate(get_page_streams(remove_all_widgets(self.read())))
|
|
@@ -328,7 +356,7 @@ class PdfWrapper(FormWrapper):
|
|
|
328
356
|
PdfWrapper: Returns self to allow method chaining
|
|
329
357
|
"""
|
|
330
358
|
|
|
331
|
-
self.
|
|
359
|
+
self._stream = self.read().replace(
|
|
332
360
|
VERSION_IDENTIFIER_PREFIX + bytes(self.version, "utf-8"),
|
|
333
361
|
VERSION_IDENTIFIER_PREFIX + bytes(version, "utf-8"),
|
|
334
362
|
1,
|
|
@@ -351,10 +379,10 @@ class PdfWrapper(FormWrapper):
|
|
|
351
379
|
PdfWrapper: New wrapper containing merged PDF
|
|
352
380
|
"""
|
|
353
381
|
|
|
354
|
-
if not self.
|
|
382
|
+
if not self.read():
|
|
355
383
|
return other
|
|
356
384
|
|
|
357
|
-
if not other.
|
|
385
|
+
if not other.read():
|
|
358
386
|
return self
|
|
359
387
|
|
|
360
388
|
unique_suffix = generate_unique_suffix()
|
|
@@ -364,7 +392,7 @@ class PdfWrapper(FormWrapper):
|
|
|
364
392
|
|
|
365
393
|
other.commit_widget_key_updates()
|
|
366
394
|
|
|
367
|
-
return self.__class__(merge_two_pdfs(self.
|
|
395
|
+
return self.__class__(merge_two_pdfs(self.read(), other.read()))
|
|
368
396
|
|
|
369
397
|
@property
|
|
370
398
|
def preview(self) -> bytes:
|
|
@@ -381,7 +409,7 @@ class PdfWrapper(FormWrapper):
|
|
|
381
409
|
|
|
382
410
|
return remove_all_widgets(
|
|
383
411
|
fill(
|
|
384
|
-
self.
|
|
412
|
+
self.read(),
|
|
385
413
|
{
|
|
386
414
|
key: preview_widget_to_draw(key, value, True)
|
|
387
415
|
for key, value in self.widgets.items()
|
|
@@ -408,10 +436,10 @@ class PdfWrapper(FormWrapper):
|
|
|
408
436
|
PdfWrapper: Returns self to allow method chaining
|
|
409
437
|
"""
|
|
410
438
|
|
|
411
|
-
self.
|
|
439
|
+
self._stream = generate_coordinate_grid(
|
|
412
440
|
remove_all_widgets(
|
|
413
441
|
fill(
|
|
414
|
-
self.
|
|
442
|
+
self.read(),
|
|
415
443
|
{
|
|
416
444
|
key: preview_widget_to_draw(key, value, False)
|
|
417
445
|
for key, value in self.widgets.items()
|
|
@@ -454,15 +482,15 @@ class PdfWrapper(FormWrapper):
|
|
|
454
482
|
self.widgets[key] = dropdown_to_text(value)
|
|
455
483
|
|
|
456
484
|
update_text_field_attributes(
|
|
457
|
-
self.
|
|
485
|
+
self.read(), self.widgets, getattr(self, "use_full_widget_name")
|
|
458
486
|
)
|
|
459
487
|
if self.read():
|
|
460
488
|
self.widgets = set_character_x_paddings(
|
|
461
|
-
self.
|
|
489
|
+
self.read(), self.widgets, getattr(self, "use_full_widget_name")
|
|
462
490
|
)
|
|
463
491
|
|
|
464
|
-
self.
|
|
465
|
-
fill(self.
|
|
492
|
+
self._stream = remove_all_widgets(
|
|
493
|
+
fill(self.read(), self.widgets, getattr(self, "use_full_widget_name"))
|
|
466
494
|
)
|
|
467
495
|
|
|
468
496
|
return self
|
|
@@ -527,10 +555,10 @@ class PdfWrapper(FormWrapper):
|
|
|
527
555
|
obj = _class(name=name, page_number=page_number, x=x, y=y, **kwargs)
|
|
528
556
|
watermarks = obj.watermarks(self.read())
|
|
529
557
|
|
|
530
|
-
self.
|
|
558
|
+
self._stream = copy_watermark_widgets(self.read(), watermarks, [name], None)
|
|
531
559
|
if obj.non_acro_form_params:
|
|
532
|
-
self.
|
|
533
|
-
self.
|
|
560
|
+
self._stream = handle_non_acro_form_params(
|
|
561
|
+
self.read(), name, obj.non_acro_form_params
|
|
534
562
|
)
|
|
535
563
|
|
|
536
564
|
key_to_refresh = ""
|
|
@@ -569,7 +597,7 @@ class PdfWrapper(FormWrapper):
|
|
|
569
597
|
self._keys_to_update.append((old_key, new_key, index))
|
|
570
598
|
return self
|
|
571
599
|
|
|
572
|
-
self.
|
|
600
|
+
self._stream = update_widget_keys(
|
|
573
601
|
self.read(), self.widgets, [old_key], [new_key], [index]
|
|
574
602
|
)
|
|
575
603
|
self._init_helper()
|
|
@@ -597,7 +625,7 @@ class PdfWrapper(FormWrapper):
|
|
|
597
625
|
new_keys = [each[1] for each in self._keys_to_update]
|
|
598
626
|
indices = [each[2] for each in self._keys_to_update]
|
|
599
627
|
|
|
600
|
-
self.
|
|
628
|
+
self._stream = update_widget_keys(
|
|
601
629
|
self.read(), self.widgets, old_keys, new_keys, indices
|
|
602
630
|
)
|
|
603
631
|
self._init_helper()
|
|
@@ -645,7 +673,7 @@ class PdfWrapper(FormWrapper):
|
|
|
645
673
|
new_widget.text_lines = text.split(NEW_LINE_SYMBOL)
|
|
646
674
|
|
|
647
675
|
watermarks = create_watermarks_and_draw(
|
|
648
|
-
self.
|
|
676
|
+
self.read(),
|
|
649
677
|
page_number,
|
|
650
678
|
"text",
|
|
651
679
|
[
|
|
@@ -658,9 +686,9 @@ class PdfWrapper(FormWrapper):
|
|
|
658
686
|
)
|
|
659
687
|
|
|
660
688
|
stream_with_widgets = self.read()
|
|
661
|
-
self.
|
|
662
|
-
self.
|
|
663
|
-
remove_all_widgets(self.
|
|
689
|
+
self._stream = merge_watermarks_with_pdf(self.read(), watermarks)
|
|
690
|
+
self._stream = copy_watermark_widgets(
|
|
691
|
+
remove_all_widgets(self.read()), stream_with_widgets, None, None
|
|
664
692
|
)
|
|
665
693
|
|
|
666
694
|
return self
|
|
@@ -696,16 +724,16 @@ class PdfWrapper(FormWrapper):
|
|
|
696
724
|
image = fp_or_f_obj_or_stream_to_stream(image)
|
|
697
725
|
image = rotate_image(image, rotation)
|
|
698
726
|
watermarks = create_watermarks_and_draw(
|
|
699
|
-
self.
|
|
727
|
+
self.read(),
|
|
700
728
|
page_number,
|
|
701
729
|
"image",
|
|
702
730
|
[{"stream": image, "x": x, "y": y, "width": width, "height": height}],
|
|
703
731
|
)
|
|
704
732
|
|
|
705
733
|
stream_with_widgets = self.read()
|
|
706
|
-
self.
|
|
707
|
-
self.
|
|
708
|
-
remove_all_widgets(self.
|
|
734
|
+
self._stream = merge_watermarks_with_pdf(self.read(), watermarks)
|
|
735
|
+
self._stream = copy_watermark_widgets(
|
|
736
|
+
remove_all_widgets(self.read()), stream_with_widgets, None, None
|
|
709
737
|
)
|
|
710
738
|
|
|
711
739
|
return self
|
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
PyPDFForm/__init__.py,sha256=
|
|
1
|
+
PyPDFForm/__init__.py,sha256=wAEMuODSkOqin_uUjzUY5oitVvnunyq9iTT7k_VrRbM,328
|
|
2
2
|
PyPDFForm/adapter.py,sha256=_5fP5UR-NzjDDayJmBRO36DgbnbUzNbjZtHZtPvSM14,1909
|
|
3
3
|
PyPDFForm/constants.py,sha256=3ed0j11cWd9Uo4s-XvZwwJojPSz8aqdmZgaEishWjqE,2342
|
|
4
4
|
PyPDFForm/coordinate.py,sha256=gQI7z-GsdCO33Qny5kXLBs6Y2TW5KO_mJ2in64fvXcg,16412
|
|
5
5
|
PyPDFForm/filler.py,sha256=oCXDzz8paw8-MIQLiTTv5goYlfCfz_EcczCj8K4JB3g,14116
|
|
6
6
|
PyPDFForm/font.py,sha256=eRbDyQFhXUkHzyZvCtru9Ypg_ukfbBAnSM5xNzPb5ss,7280
|
|
7
|
+
PyPDFForm/hooks.py,sha256=e9mjZgGl7t9Spok3Tgwl9Osmlzx4vod-5z5jVF84x-Q,7117
|
|
7
8
|
PyPDFForm/image.py,sha256=aYk7BC-AHiqt73durGIQ3e6gE5Ggbdr8jmkCUaQdsk8,1627
|
|
8
|
-
PyPDFForm/patterns.py,sha256=
|
|
9
|
+
PyPDFForm/patterns.py,sha256=nkvtwsm1dbHjAH7FBNwMR5YI6_Jj16KYBm1HxB12vOk,8100
|
|
9
10
|
PyPDFForm/template.py,sha256=N_Pj3SaSKs3sqIuIdtgHPXvxIHLnHeR2M9IwRCWWKoQ,20043
|
|
10
11
|
PyPDFForm/utils.py,sha256=QxJSG96FHGch863UdBPT8DdTKN9gVzQzA2ezifTm7z4,8575
|
|
11
12
|
PyPDFForm/watermark.py,sha256=yvtZKcdPAPprBVEWcFZEnZMJcTwVpQ6u72m0jqYHxx8,12357
|
|
12
|
-
PyPDFForm/wrapper.py,sha256=
|
|
13
|
+
PyPDFForm/wrapper.py,sha256=ndk6isS8scu9nxGsgcA9MU1-NNzYsqMLtxm3EY4aqSQ,27678
|
|
13
14
|
PyPDFForm/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
PyPDFForm/middleware/base.py,sha256=
|
|
15
|
-
PyPDFForm/middleware/checkbox.py,sha256=
|
|
15
|
+
PyPDFForm/middleware/base.py,sha256=lHIWBs2axKDYZj4rYRHrBHzSqF7yk0o9S8uAFTWspSA,3783
|
|
16
|
+
PyPDFForm/middleware/checkbox.py,sha256=sUk5hclIKYE8N85ZYvYOI_SYK2WVqlqIP7oBYWlv_yY,2695
|
|
16
17
|
PyPDFForm/middleware/dropdown.py,sha256=McuZl8Pc2IYPkEHRmM1OcEsh9njekySqjqVxRmQKmWU,1773
|
|
17
18
|
PyPDFForm/middleware/image.py,sha256=HlPUsIktj-NryIkwwdZlilvrd6sZYifs9IuDgTHp7uQ,950
|
|
18
19
|
PyPDFForm/middleware/radio.py,sha256=M4yqHYzHj0jvOGbjYdqeYnNAlYhTF-h47qxqrjXDOoU,1921
|
|
19
20
|
PyPDFForm/middleware/signature.py,sha256=0gexCQwHCEOrjrgvUXeJJCGo2plfSEbXlykPJJCqpfA,2380
|
|
20
|
-
PyPDFForm/middleware/text.py,sha256=
|
|
21
|
+
PyPDFForm/middleware/text.py,sha256=VWL2kLXY_UTz1DkUJOgai9VczU3E4yn_Sa5LwUVTgt4,3091
|
|
21
22
|
PyPDFForm/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
-
PyPDFForm/widgets/base.py,sha256=
|
|
23
|
+
PyPDFForm/widgets/base.py,sha256=vrWsnrFumgZScSMzRtqxapIkFm3yvasV_xIs2lmI2a0,5327
|
|
23
24
|
PyPDFForm/widgets/bedrock.py,sha256=j6beU04kaQzpAIFZHI5VJLaDT5RVAAa6LzkU1luJpN8,137660
|
|
24
25
|
PyPDFForm/widgets/checkbox.py,sha256=_1I5yh1211RgRUyWzd3NNYpI9JchqJNSJWaZAhl2uOo,1248
|
|
25
26
|
PyPDFForm/widgets/dropdown.py,sha256=zszIT5MI6ggBRUEn7oGBKK0pKmDC9LQw3RnqaKG8ocQ,1764
|
|
26
27
|
PyPDFForm/widgets/image.py,sha256=6y8Ysmk49USr_qWOXD6KGL6cch516cUIlrxoj0pJy9Q,797
|
|
27
28
|
PyPDFForm/widgets/radio.py,sha256=ipadJyHbgftDUvjGk15kapzgHPN3HjdF_iB_7amXR6o,2737
|
|
28
29
|
PyPDFForm/widgets/signature.py,sha256=FdXzja3RTuyU9iyA5Y1yPb4Gsfe4rYlM4gcwekIuwog,4997
|
|
29
|
-
PyPDFForm/widgets/text.py,sha256=
|
|
30
|
-
pypdfform-2.
|
|
31
|
-
pypdfform-2.
|
|
32
|
-
pypdfform-2.
|
|
33
|
-
pypdfform-2.
|
|
34
|
-
pypdfform-2.
|
|
30
|
+
PyPDFForm/widgets/text.py,sha256=O9aTZLnpB_k-ZWx6jNbfKbVLq-O1zjwN3reBMwTNG2M,1241
|
|
31
|
+
pypdfform-2.5.0.dist-info/licenses/LICENSE,sha256=43awmYkI6opyTpg19me731iO1WfXZwViqb67oWtCsFY,1065
|
|
32
|
+
pypdfform-2.5.0.dist-info/METADATA,sha256=r860fmyW62b7Vl9lIJiIVllhtQOKrunEnL9DQb0Bu-0,4688
|
|
33
|
+
pypdfform-2.5.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
|
34
|
+
pypdfform-2.5.0.dist-info/top_level.txt,sha256=GQQKuWqPUjT9YZqwK95NlAQzxjwoQrsxQ8ureM8lWOY,10
|
|
35
|
+
pypdfform-2.5.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|