PyPDFForm 2.0.0__py3-none-any.whl → 2.1.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 +6 -2
- PyPDFForm/adapter.py +37 -3
- PyPDFForm/constants.py +12 -1
- PyPDFForm/coordinate.py +218 -59
- PyPDFForm/filler.py +104 -9
- PyPDFForm/font.py +80 -16
- PyPDFForm/image.py +32 -3
- PyPDFForm/middleware/base.py +57 -8
- PyPDFForm/middleware/checkbox.py +49 -7
- PyPDFForm/middleware/dropdown.py +41 -5
- PyPDFForm/middleware/image.py +26 -2
- PyPDFForm/middleware/radio.py +41 -5
- PyPDFForm/middleware/signature.py +49 -6
- PyPDFForm/middleware/text.py +55 -7
- PyPDFForm/patterns.py +112 -10
- PyPDFForm/template.py +181 -29
- PyPDFForm/utils.py +112 -15
- PyPDFForm/watermark.py +151 -9
- PyPDFForm/widgets/base.py +65 -9
- PyPDFForm/widgets/checkbox.py +22 -2
- PyPDFForm/widgets/dropdown.py +31 -3
- PyPDFForm/widgets/radio.py +78 -0
- PyPDFForm/widgets/text.py +19 -2
- PyPDFForm/wrapper.py +338 -27
- {pypdfform-2.0.0.dist-info → pypdfform-2.1.0.dist-info}/METADATA +1 -1
- pypdfform-2.1.0.dist-info/RECORD +31 -0
- pypdfform-2.0.0.dist-info/RECORD +0 -30
- {pypdfform-2.0.0.dist-info → pypdfform-2.1.0.dist-info}/WHEEL +0 -0
- {pypdfform-2.0.0.dist-info → pypdfform-2.1.0.dist-info}/licenses/LICENSE +0 -0
- {pypdfform-2.0.0.dist-info → pypdfform-2.1.0.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
"""
|
|
2
|
+
"""Provides middleware for PDF signature field widgets.
|
|
3
|
+
|
|
4
|
+
This module contains the Signature class which handles:
|
|
5
|
+
- Signature image processing
|
|
6
|
+
- Aspect ratio preservation
|
|
7
|
+
- File path resolution
|
|
8
|
+
- PDF form field integration
|
|
9
|
+
"""
|
|
3
10
|
|
|
4
11
|
from os.path import expanduser
|
|
5
12
|
from typing import BinaryIO, Union
|
|
@@ -9,34 +16,70 @@ from .base import Widget
|
|
|
9
16
|
|
|
10
17
|
|
|
11
18
|
class Signature(Widget):
|
|
12
|
-
"""
|
|
19
|
+
"""Middleware for PDF signature field widgets.
|
|
20
|
+
|
|
21
|
+
Handles all aspects of signature field processing including:
|
|
22
|
+
- Signature image handling
|
|
23
|
+
- Aspect ratio control
|
|
24
|
+
- File path resolution
|
|
25
|
+
- PDF form field integration
|
|
26
|
+
|
|
27
|
+
Inherits from Widget base class and extends it with signature-specific features.
|
|
28
|
+
"""
|
|
13
29
|
|
|
14
30
|
preserve_aspect_ratio = True
|
|
31
|
+
"""Whether to preserve the original image's aspect ratio when scaling."""
|
|
15
32
|
|
|
16
33
|
def __init__(
|
|
17
34
|
self,
|
|
18
35
|
name: str,
|
|
19
36
|
value: Union[bytes, str, BinaryIO] = None,
|
|
20
37
|
) -> None:
|
|
21
|
-
"""
|
|
38
|
+
"""Initializes a new signature field widget.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
name: Field name/key for the signature
|
|
42
|
+
value: Signature image as bytes, file path, or file object (default: None)
|
|
43
|
+
"""
|
|
22
44
|
|
|
23
45
|
super().__init__(name, value)
|
|
24
46
|
|
|
25
47
|
@property
|
|
26
48
|
def schema_definition(self) -> dict:
|
|
27
|
-
"""
|
|
49
|
+
"""Generates a JSON schema definition for the signature field.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
dict: Schema properties including:
|
|
53
|
+
- type: string (file path)
|
|
54
|
+
- description (if available from base class)
|
|
55
|
+
"""
|
|
28
56
|
|
|
29
57
|
return {"type": "string"}
|
|
30
58
|
|
|
31
59
|
@property
|
|
32
60
|
def sample_value(self) -> str:
|
|
33
|
-
"""
|
|
61
|
+
"""Generates a sample value for the signature field.
|
|
62
|
+
|
|
63
|
+
Returns a default sample image path from the user's Downloads folder.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
str: Path to sample image file
|
|
67
|
+
"""
|
|
34
68
|
|
|
35
69
|
return expanduser("~/Downloads/sample_image.jpg")
|
|
36
70
|
|
|
37
71
|
@property
|
|
38
72
|
def stream(self) -> Union[bytes, None]:
|
|
39
|
-
"""Converts the
|
|
73
|
+
"""Converts the signature image to a byte stream.
|
|
74
|
+
|
|
75
|
+
Handles conversion of:
|
|
76
|
+
- Raw image bytes
|
|
77
|
+
- File paths
|
|
78
|
+
- File-like objects
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Union[bytes, None]: Image data as bytes, or None if no value set
|
|
82
|
+
"""
|
|
40
83
|
|
|
41
84
|
return (
|
|
42
85
|
fp_or_f_obj_or_stream_to_stream(self.value)
|
PyPDFForm/middleware/text.py
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
"""
|
|
2
|
+
"""Provides middleware for PDF text field widgets.
|
|
3
|
+
|
|
4
|
+
This module contains the Text class which handles:
|
|
5
|
+
- Text field value management
|
|
6
|
+
- Font properties (family, size, color)
|
|
7
|
+
- Text wrapping and formatting
|
|
8
|
+
- Comb field (fixed character spacing) support
|
|
9
|
+
- Preview mode for form field visualization
|
|
10
|
+
"""
|
|
3
11
|
|
|
4
12
|
from typing import Any
|
|
5
13
|
|
|
@@ -7,14 +15,29 @@ from .base import Widget
|
|
|
7
15
|
|
|
8
16
|
|
|
9
17
|
class Text(Widget):
|
|
10
|
-
"""
|
|
18
|
+
"""Middleware for PDF text field widgets.
|
|
19
|
+
|
|
20
|
+
Handles all aspects of text field processing including:
|
|
21
|
+
- Value conversion and validation
|
|
22
|
+
- Font styling and formatting
|
|
23
|
+
- Multiline text wrapping
|
|
24
|
+
- Comb field character spacing
|
|
25
|
+
- Preview mode rendering
|
|
26
|
+
|
|
27
|
+
Inherits from Widget base class and extends it with text-specific features.
|
|
28
|
+
"""
|
|
11
29
|
|
|
12
30
|
def __init__(
|
|
13
31
|
self,
|
|
14
32
|
name: str,
|
|
15
33
|
value: str = None,
|
|
16
34
|
) -> None:
|
|
17
|
-
"""
|
|
35
|
+
"""Initializes a new text field widget.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
name: Field name/key for the text field
|
|
39
|
+
value: Initial text value (default: None)
|
|
40
|
+
"""
|
|
18
41
|
|
|
19
42
|
super().__init__(name, value)
|
|
20
43
|
|
|
@@ -31,7 +54,13 @@ class Text(Widget):
|
|
|
31
54
|
|
|
32
55
|
@property
|
|
33
56
|
def value(self) -> Any:
|
|
34
|
-
"""
|
|
57
|
+
"""Gets the text field's current value with type conversion.
|
|
58
|
+
|
|
59
|
+
Converts numeric values to strings automatically.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Any: The text value as a string (converted if numeric)
|
|
63
|
+
"""
|
|
35
64
|
|
|
36
65
|
if isinstance(self._value, (int, float)):
|
|
37
66
|
return str(self._value)
|
|
@@ -40,13 +69,26 @@ class Text(Widget):
|
|
|
40
69
|
|
|
41
70
|
@value.setter
|
|
42
71
|
def value(self, value: str) -> None:
|
|
43
|
-
"""Sets
|
|
72
|
+
"""Sets the text field's value.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
value: New text value (will be converted to string if numeric)
|
|
76
|
+
"""
|
|
44
77
|
|
|
45
78
|
self._value = value
|
|
46
79
|
|
|
47
80
|
@property
|
|
48
81
|
def schema_definition(self) -> dict:
|
|
49
|
-
"""
|
|
82
|
+
"""Generates a JSON schema definition for the text field.
|
|
83
|
+
|
|
84
|
+
Includes:
|
|
85
|
+
- Type constraint (string)
|
|
86
|
+
- Max length if specified
|
|
87
|
+
- Any inherited schema properties
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
dict: Complete JSON schema definition
|
|
91
|
+
"""
|
|
50
92
|
|
|
51
93
|
result = {"type": "string"}
|
|
52
94
|
|
|
@@ -57,7 +99,13 @@ class Text(Widget):
|
|
|
57
99
|
|
|
58
100
|
@property
|
|
59
101
|
def sample_value(self) -> str:
|
|
60
|
-
"""
|
|
102
|
+
"""Generates a sample value demonstrating the text field's capacity.
|
|
103
|
+
|
|
104
|
+
Uses the field name as the sample value, truncated to max_length if specified.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
str: Sample text value for demonstration purposes
|
|
108
|
+
"""
|
|
61
109
|
|
|
62
110
|
return (
|
|
63
111
|
self.name[: self.max_length] if self.max_length is not None else self.name
|
PyPDFForm/patterns.py
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
"""
|
|
2
|
+
"""Pattern matching utilities for PDF form widgets.
|
|
3
|
+
|
|
4
|
+
This module provides:
|
|
5
|
+
- Pattern definitions for identifying widget types and properties
|
|
6
|
+
- Functions for updating widget states and appearances
|
|
7
|
+
- Support for common PDF form operations like flattening fields
|
|
8
|
+
|
|
9
|
+
Patterns are used throughout PyPDFForm to:
|
|
10
|
+
- Classify widget types (text, checkbox, radio, etc.)
|
|
11
|
+
- Extract widget properties (alignment, colors, flags)
|
|
12
|
+
- Modify widget states (values, appearances, flags)
|
|
13
|
+
|
|
14
|
+
The module also contains utility functions for common PDF form operations
|
|
15
|
+
like updating field values and flattening form fields.
|
|
16
|
+
"""
|
|
3
17
|
|
|
4
18
|
from pypdf.generic import (DictionaryObject, NameObject, NumberObject,
|
|
5
19
|
TextStringObject)
|
|
@@ -24,10 +38,23 @@ WIDGET_TYPE_PATTERNS = [
|
|
|
24
38
|
({FT: Sig},),
|
|
25
39
|
Signature,
|
|
26
40
|
),
|
|
41
|
+
(
|
|
42
|
+
({Parent: {FT: Sig}},),
|
|
43
|
+
Signature,
|
|
44
|
+
),
|
|
27
45
|
(
|
|
28
46
|
({FT: Tx},),
|
|
29
47
|
Text,
|
|
30
48
|
),
|
|
49
|
+
(
|
|
50
|
+
# reportlab creation pattern
|
|
51
|
+
(
|
|
52
|
+
{FT: Btn},
|
|
53
|
+
{Parent: {FT: Btn}},
|
|
54
|
+
{AS: (Yes, Off)},
|
|
55
|
+
),
|
|
56
|
+
Radio,
|
|
57
|
+
),
|
|
31
58
|
(
|
|
32
59
|
(
|
|
33
60
|
{FT: Btn},
|
|
@@ -111,7 +138,16 @@ BORDER_DASH_ARRAY_PATTERNS = [{BS: {D: True}}]
|
|
|
111
138
|
|
|
112
139
|
|
|
113
140
|
def simple_update_checkbox_value(annot: DictionaryObject, check: bool = False) -> None:
|
|
114
|
-
"""
|
|
141
|
+
"""Update checkbox annotation values based on check state.
|
|
142
|
+
|
|
143
|
+
Modifies the appearance state (AS) and value (V) of a checkbox annotation
|
|
144
|
+
to reflect the desired checked/unchecked state. Uses the annotation's
|
|
145
|
+
appearance dictionary (AP/N) to determine valid states.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
annot: PDF annotation dictionary to modify
|
|
149
|
+
check: Whether the checkbox should be checked (True) or unchecked (False)
|
|
150
|
+
"""
|
|
115
151
|
|
|
116
152
|
for each in annot[AP][N]: # noqa
|
|
117
153
|
if (check and str(each) != Off) or (not check and str(each) == Off):
|
|
@@ -121,7 +157,15 @@ def simple_update_checkbox_value(annot: DictionaryObject, check: bool = False) -
|
|
|
121
157
|
|
|
122
158
|
|
|
123
159
|
def simple_update_radio_value(annot: DictionaryObject) -> None:
|
|
124
|
-
"""
|
|
160
|
+
"""Update radio button annotation values to selected state.
|
|
161
|
+
|
|
162
|
+
Modifies the appearance state (AS) of a radio button annotation and updates
|
|
163
|
+
the parent's value (V) to reflect the selected state. Uses the annotation's
|
|
164
|
+
appearance dictionary (AP/N) to determine valid states.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
annot: PDF radio button annotation dictionary to modify
|
|
168
|
+
"""
|
|
125
169
|
|
|
126
170
|
for each in annot[AP][N]: # noqa
|
|
127
171
|
if str(each) != Off:
|
|
@@ -131,7 +175,16 @@ def simple_update_radio_value(annot: DictionaryObject) -> None:
|
|
|
131
175
|
|
|
132
176
|
|
|
133
177
|
def simple_update_dropdown_value(annot: DictionaryObject, widget: Dropdown) -> None:
|
|
134
|
-
"""
|
|
178
|
+
"""Update dropdown annotation values based on widget selection.
|
|
179
|
+
|
|
180
|
+
Modifies the value (V) and appearance (AP) of a dropdown annotation to
|
|
181
|
+
reflect the currently selected choice from the widget. Handles both
|
|
182
|
+
standalone dropdowns and those with parent annotations.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
annot: PDF dropdown annotation dictionary to modify
|
|
186
|
+
widget: Dropdown widget containing the selected value
|
|
187
|
+
"""
|
|
135
188
|
|
|
136
189
|
if Parent in annot and T not in annot:
|
|
137
190
|
annot[NameObject(Parent)][NameObject(V)] = TextStringObject( # noqa
|
|
@@ -144,7 +197,16 @@ def simple_update_dropdown_value(annot: DictionaryObject, widget: Dropdown) -> N
|
|
|
144
197
|
|
|
145
198
|
|
|
146
199
|
def simple_update_text_value(annot: DictionaryObject, widget: Text) -> None:
|
|
147
|
-
"""
|
|
200
|
+
"""Update text field annotation values based on widget content.
|
|
201
|
+
|
|
202
|
+
Modifies the value (V) and appearance (AP) of a text field annotation to
|
|
203
|
+
reflect the current value from the text widget. Handles both standalone
|
|
204
|
+
text fields and those with parent annotations.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
annot: PDF text field annotation dictionary to modify
|
|
208
|
+
widget: Text widget containing the value to set
|
|
209
|
+
"""
|
|
148
210
|
|
|
149
211
|
if Parent in annot and T not in annot:
|
|
150
212
|
annot[NameObject(Parent)][NameObject(V)] = TextStringObject( # noqa
|
|
@@ -157,7 +219,15 @@ def simple_update_text_value(annot: DictionaryObject, widget: Text) -> None:
|
|
|
157
219
|
|
|
158
220
|
|
|
159
221
|
def simple_flatten_radio(annot: DictionaryObject) -> None:
|
|
160
|
-
"""
|
|
222
|
+
"""Flatten radio button annotation by making it read-only.
|
|
223
|
+
|
|
224
|
+
Modifies the field flags (Ff) of a radio button's parent annotation
|
|
225
|
+
to set the read-only flag, effectively flattening the field and
|
|
226
|
+
preventing further user interaction.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
annot: PDF radio button annotation dictionary to flatten
|
|
230
|
+
"""
|
|
161
231
|
|
|
162
232
|
annot[NameObject(Parent)][NameObject(Ff)] = NumberObject( # noqa
|
|
163
233
|
int(annot[NameObject(Parent)].get(NameObject(Ff), 0)) | READ_ONLY # noqa
|
|
@@ -165,7 +235,15 @@ def simple_flatten_radio(annot: DictionaryObject) -> None:
|
|
|
165
235
|
|
|
166
236
|
|
|
167
237
|
def simple_flatten_generic(annot: DictionaryObject) -> None:
|
|
168
|
-
"""
|
|
238
|
+
"""Flatten generic annotation by making it read-only.
|
|
239
|
+
|
|
240
|
+
Modifies the field flags (Ff) of an annotation to set the read-only flag,
|
|
241
|
+
effectively flattening the field and preventing further user interaction.
|
|
242
|
+
Handles both standalone annotations and those with parent annotations.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
annot: PDF annotation dictionary to flatten
|
|
246
|
+
"""
|
|
169
247
|
|
|
170
248
|
if Parent in annot and Ff not in annot:
|
|
171
249
|
annot[NameObject(Parent)][NameObject(Ff)] = NumberObject( # noqa
|
|
@@ -178,7 +256,15 @@ def simple_flatten_generic(annot: DictionaryObject) -> None:
|
|
|
178
256
|
|
|
179
257
|
|
|
180
258
|
def update_annotation_name(annot: DictionaryObject, val: str) -> None:
|
|
181
|
-
"""
|
|
259
|
+
"""Update the name/title of a PDF annotation.
|
|
260
|
+
|
|
261
|
+
Modifies the title (T) field of an annotation to set a new name.
|
|
262
|
+
Handles both standalone annotations and those with parent annotations.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
annot: PDF annotation dictionary to modify
|
|
266
|
+
val: New name/title to set for the annotation
|
|
267
|
+
"""
|
|
182
268
|
|
|
183
269
|
if Parent in annot and T not in annot:
|
|
184
270
|
annot[NameObject(Parent)][NameObject(T)] = TextStringObject(val) # noqa
|
|
@@ -187,13 +273,29 @@ def update_annotation_name(annot: DictionaryObject, val: str) -> None:
|
|
|
187
273
|
|
|
188
274
|
|
|
189
275
|
def update_created_text_field_alignment(annot: DictionaryObject, val: int) -> None:
|
|
190
|
-
"""
|
|
276
|
+
"""Update text alignment for created text field annotations.
|
|
277
|
+
|
|
278
|
+
Modifies the alignment (Q) field of a text field annotation created
|
|
279
|
+
by the library to set the specified text alignment.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
annot: PDF text field annotation dictionary to modify
|
|
283
|
+
val: Alignment value to set (typically 0=left, 1=center, 2=right)
|
|
284
|
+
"""
|
|
191
285
|
|
|
192
286
|
annot[NameObject(Q)] = NumberObject(val)
|
|
193
287
|
|
|
194
288
|
|
|
195
289
|
def update_created_text_field_multiline(annot: DictionaryObject, val: bool) -> None:
|
|
196
|
-
"""
|
|
290
|
+
"""Update multiline flag for created text field annotations.
|
|
291
|
+
|
|
292
|
+
Modifies the field flags (Ff) of a text field annotation created by
|
|
293
|
+
the library to set or clear the multiline flag based on the input value.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
annot: PDF text field annotation dictionary to modify
|
|
297
|
+
val: Whether to enable multiline (True) or disable (False)
|
|
298
|
+
"""
|
|
197
299
|
|
|
198
300
|
if val:
|
|
199
301
|
annot[NameObject(Ff)] = NumberObject(
|