PyPDFForm 3.5.1__py3-none-any.whl → 4.2.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.
- PyPDFForm/__init__.py +5 -3
- PyPDFForm/adapter.py +33 -1
- PyPDFForm/ap.py +99 -0
- PyPDFForm/assets/__init__.py +0 -0
- PyPDFForm/assets/blank.py +100 -0
- PyPDFForm/constants.py +20 -2
- PyPDFForm/coordinate.py +7 -11
- PyPDFForm/deprecation.py +30 -0
- PyPDFForm/filler.py +17 -36
- PyPDFForm/font.py +16 -16
- PyPDFForm/hooks.py +169 -31
- PyPDFForm/image.py +0 -3
- PyPDFForm/middleware/__init__.py +35 -0
- PyPDFForm/middleware/base.py +24 -5
- PyPDFForm/middleware/checkbox.py +18 -1
- PyPDFForm/middleware/signature.py +0 -1
- PyPDFForm/patterns.py +71 -13
- PyPDFForm/raw/__init__.py +37 -0
- PyPDFForm/raw/circle.py +65 -0
- PyPDFForm/raw/ellipse.py +69 -0
- PyPDFForm/raw/image.py +79 -0
- PyPDFForm/raw/line.py +65 -0
- PyPDFForm/raw/rect.py +70 -0
- PyPDFForm/raw/text.py +73 -0
- PyPDFForm/template.py +114 -10
- PyPDFForm/types.py +49 -0
- PyPDFForm/utils.py +31 -41
- PyPDFForm/watermark.py +153 -44
- PyPDFForm/widgets/__init__.py +1 -0
- PyPDFForm/widgets/base.py +79 -59
- PyPDFForm/widgets/checkbox.py +30 -30
- PyPDFForm/widgets/dropdown.py +42 -40
- PyPDFForm/widgets/image.py +17 -16
- PyPDFForm/widgets/radio.py +27 -28
- PyPDFForm/widgets/signature.py +96 -60
- PyPDFForm/widgets/text.py +40 -40
- PyPDFForm/wrapper.py +256 -240
- {pypdfform-3.5.1.dist-info → pypdfform-4.2.0.dist-info}/METADATA +33 -26
- pypdfform-4.2.0.dist-info/RECORD +47 -0
- {pypdfform-3.5.1.dist-info → pypdfform-4.2.0.dist-info}/licenses/LICENSE +1 -1
- pypdfform-3.5.1.dist-info/RECORD +0 -35
- /PyPDFForm/{widgets → assets}/bedrock.py +0 -0
- {pypdfform-3.5.1.dist-info → pypdfform-4.2.0.dist-info}/WHEEL +0 -0
- {pypdfform-3.5.1.dist-info → pypdfform-4.2.0.dist-info}/top_level.txt +0 -0
PyPDFForm/widgets/radio.py
CHANGED
|
@@ -10,41 +10,15 @@ The `RadioWidget` class extends the base `CheckBoxWidget` class to provide
|
|
|
10
10
|
specific functionality for interacting with radio button form fields in PDFs.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
# TODO: In `canvas_operations`, `self.acro_form_params.copy()` creates a shallow copy of the dictionary in each iteration of the loop. For a large number of radio buttons, this repeated copying can be inefficient. Consider modifying the dictionary in place and then reverting changes if necessary, or restructuring the data to avoid repeated copying.
|
|
14
|
-
|
|
15
13
|
from dataclasses import dataclass
|
|
16
|
-
from typing import List, Optional
|
|
14
|
+
from typing import List, Optional, Type
|
|
17
15
|
|
|
18
16
|
from reportlab.pdfgen.canvas import Canvas
|
|
19
17
|
|
|
18
|
+
from .base import Widget
|
|
20
19
|
from .checkbox import CheckBoxField, CheckBoxWidget
|
|
21
20
|
|
|
22
21
|
|
|
23
|
-
@dataclass
|
|
24
|
-
class RadioGroup(CheckBoxField):
|
|
25
|
-
"""
|
|
26
|
-
Represents a group of radio buttons in a PDF document.
|
|
27
|
-
|
|
28
|
-
This dataclass extends the `CheckBoxField` base class and defines the specific
|
|
29
|
-
attributes that can be configured for a radio button group. Unlike a single
|
|
30
|
-
checkbox, a radio group allows for multiple positions (x, y coordinates)
|
|
31
|
-
where individual radio buttons can be placed, but only one can be selected.
|
|
32
|
-
|
|
33
|
-
Attributes:
|
|
34
|
-
_field_type (str): The type of the field, fixed as "radio".
|
|
35
|
-
x (List[float]): A list of x-coordinates for each radio button in the group.
|
|
36
|
-
y (List[float]): A list of y-coordinates for each radio button in the group.
|
|
37
|
-
shape (Optional[str]): The shape of the radio button. Valid values are
|
|
38
|
-
"circle" or "square". Defaults to None, which typically means a default circle shape.
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
_field_type: str = "radio"
|
|
42
|
-
|
|
43
|
-
x: List[float]
|
|
44
|
-
y: List[float]
|
|
45
|
-
shape: Optional[str] = None
|
|
46
|
-
|
|
47
|
-
|
|
48
22
|
class RadioWidget(CheckBoxWidget):
|
|
49
23
|
"""
|
|
50
24
|
Represents a radio button widget in a PDF form.
|
|
@@ -99,3 +73,28 @@ class RadioWidget(CheckBoxWidget):
|
|
|
99
73
|
new_acro_form_params["y"] = y
|
|
100
74
|
new_acro_form_params["value"] = str(i)
|
|
101
75
|
getattr(canvas.acroForm, self.ACRO_FORM_FUNC)(**new_acro_form_params)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class RadioGroup(CheckBoxField):
|
|
80
|
+
"""
|
|
81
|
+
Represents a group of radio buttons in a PDF document.
|
|
82
|
+
|
|
83
|
+
This dataclass extends the `CheckBoxField` base class and defines the specific
|
|
84
|
+
attributes that can be configured for a radio button group. Unlike a single
|
|
85
|
+
checkbox, a radio group allows for multiple positions (x, y coordinates)
|
|
86
|
+
where individual radio buttons can be placed, but only one can be selected.
|
|
87
|
+
|
|
88
|
+
Attributes:
|
|
89
|
+
_widget_class (Type[Widget]): The widget class associated with this field type.
|
|
90
|
+
x (List[float]): A list of x-coordinates for each radio button in the group.
|
|
91
|
+
y (List[float]): A list of y-coordinates for each radio button in the group.
|
|
92
|
+
shape (Optional[str]): The shape of the radio button. Valid values are
|
|
93
|
+
"circle" or "square". Defaults to None, which typically means a default circle shape.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
_widget_class: Type[Widget] = RadioWidget
|
|
97
|
+
|
|
98
|
+
x: List[float]
|
|
99
|
+
y: List[float]
|
|
100
|
+
shape: Optional[str] = None
|
PyPDFForm/widgets/signature.py
CHANGED
|
@@ -11,43 +11,23 @@ signature form fields in PDFs, including handling their creation, rendering, and
|
|
|
11
11
|
integration into the document.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
# TODO: In `watermarks`, the list comprehension `[f.read() if i == self.page_number - 1 else b"" for i in range(page_count)]` reads the entire `BytesIO` object `f` multiple times if `page_count` is large. Read `f` once into a variable and then use that variable in the list comprehension.
|
|
16
|
-
# TODO: The `input_pdf` is created in `watermarks` but only its page count is used. If the `PdfReader` object is not needed for other operations, consider a lighter way to get the page count or pass the `PdfReader` object from the caller if it's already available.
|
|
14
|
+
from __future__ import annotations
|
|
17
15
|
|
|
16
|
+
from collections import defaultdict
|
|
18
17
|
from dataclasses import dataclass
|
|
19
18
|
from io import BytesIO
|
|
20
|
-
from typing import List, Optional
|
|
19
|
+
from typing import List, Optional, Type
|
|
21
20
|
|
|
22
21
|
from pypdf import PdfReader, PdfWriter
|
|
23
22
|
from pypdf.generic import (ArrayObject, FloatObject, NameObject,
|
|
24
23
|
TextStringObject)
|
|
24
|
+
from reportlab.pdfgen.canvas import Canvas
|
|
25
25
|
|
|
26
|
+
from ..assets.bedrock import BEDROCK_PDF
|
|
26
27
|
from ..constants import Annots, Rect, T
|
|
27
28
|
from ..template import get_widget_key
|
|
28
29
|
from ..utils import stream_to_io
|
|
29
30
|
from .base import Field
|
|
30
|
-
from .bedrock import BEDROCK_PDF
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@dataclass
|
|
34
|
-
class SignatureField(Field):
|
|
35
|
-
"""
|
|
36
|
-
Represents a signature field in a PDF document.
|
|
37
|
-
|
|
38
|
-
This dataclass extends the `Field` base class and defines the specific
|
|
39
|
-
attributes that can be configured for a signature input field.
|
|
40
|
-
|
|
41
|
-
Attributes:
|
|
42
|
-
_field_type (str): The type of the field, fixed as "signature".
|
|
43
|
-
width (Optional[float]): The width of the signature field.
|
|
44
|
-
height (Optional[float]): The height of the signature field.
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
_field_type: str = "signature"
|
|
48
|
-
|
|
49
|
-
width: Optional[float] = None
|
|
50
|
-
height: Optional[float] = None
|
|
51
31
|
|
|
52
32
|
|
|
53
33
|
class SignatureWidget:
|
|
@@ -106,52 +86,108 @@ class SignatureWidget:
|
|
|
106
86
|
if each in kwargs:
|
|
107
87
|
self.hook_params.append((each, kwargs.get(each)))
|
|
108
88
|
|
|
109
|
-
|
|
89
|
+
@staticmethod
|
|
90
|
+
def bulk_watermarks(widgets: List[SignatureWidget], stream: bytes) -> List[bytes]:
|
|
110
91
|
"""
|
|
111
|
-
Generates watermarks for
|
|
92
|
+
Generates watermarks for multiple signature widgets in bulk.
|
|
112
93
|
|
|
113
|
-
This method
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
94
|
+
This static method processes a list of SignatureWidget objects and a PDF stream
|
|
95
|
+
to create a list of watermark PDF streams, one for each page of the input PDF.
|
|
96
|
+
Each watermark PDF contains all the signature widgets that belong to that page.
|
|
97
|
+
This is more efficient than generating watermarks one by one.
|
|
117
98
|
|
|
118
99
|
Args:
|
|
119
|
-
|
|
100
|
+
widgets (List[SignatureWidget]): A list of SignatureWidget objects to be
|
|
101
|
+
added as watermarks.
|
|
102
|
+
stream (bytes): The PDF stream of the document to be watermarked.
|
|
120
103
|
|
|
121
104
|
Returns:
|
|
122
|
-
List[bytes]: A list of
|
|
123
|
-
|
|
124
|
-
page matches the signature's page number, the corresponding element
|
|
125
|
-
will contain the watermark data. Otherwise, the element will be an
|
|
126
|
-
empty byte string.
|
|
105
|
+
List[bytes]: A list of watermark PDF streams. Each element corresponds to
|
|
106
|
+
a page in the input PDF.
|
|
127
107
|
"""
|
|
128
|
-
|
|
129
|
-
page_count = len(input_pdf.pages)
|
|
130
|
-
pdf = PdfReader(stream_to_io(BEDROCK_PDF))
|
|
131
|
-
out = PdfWriter()
|
|
132
|
-
out.append(pdf)
|
|
108
|
+
result = []
|
|
133
109
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
110
|
+
page_to_widgets = defaultdict(list)
|
|
111
|
+
for widget in widgets:
|
|
112
|
+
page_to_widgets[widget.page_number].append(widget)
|
|
137
113
|
|
|
138
|
-
|
|
139
|
-
continue
|
|
114
|
+
input_pdf = PdfReader(stream_to_io(stream))
|
|
140
115
|
|
|
141
|
-
|
|
142
|
-
|
|
116
|
+
bedrock = PdfReader(stream_to_io(BEDROCK_PDF))
|
|
117
|
+
page = bedrock.pages[0]
|
|
118
|
+
annot_type_to_annot = {}
|
|
119
|
+
for annot in page.get(Annots, []): # pylint: disable=E1101
|
|
120
|
+
key = get_widget_key(annot.get_object(), False)
|
|
121
|
+
annot_type_to_annot[key] = annot.get_object()
|
|
122
|
+
|
|
123
|
+
watermark = BytesIO()
|
|
124
|
+
|
|
125
|
+
for i, p in enumerate(input_pdf.pages):
|
|
126
|
+
# pylint: disable=R0801
|
|
127
|
+
watermark.seek(0)
|
|
128
|
+
watermark.flush()
|
|
129
|
+
canvas = Canvas(
|
|
130
|
+
watermark,
|
|
131
|
+
pagesize=(
|
|
132
|
+
float(p.mediabox[2]),
|
|
133
|
+
float(p.mediabox[3]),
|
|
134
|
+
),
|
|
135
|
+
)
|
|
136
|
+
canvas.showPage()
|
|
137
|
+
canvas.save()
|
|
138
|
+
watermark.seek(0)
|
|
139
|
+
|
|
140
|
+
out = PdfWriter(watermark)
|
|
141
|
+
|
|
142
|
+
page_widgets = page_to_widgets.get(i + 1, [])
|
|
143
|
+
|
|
144
|
+
widgets_to_copy = []
|
|
145
|
+
for widget in page_widgets:
|
|
146
|
+
widget_to_copy = annot_type_to_annot[
|
|
147
|
+
widget.BEDROCK_WIDGET_TO_COPY
|
|
148
|
+
].clone(out, force_duplicate=True)
|
|
149
|
+
|
|
150
|
+
widget_to_copy.get_object()[NameObject(T)] = TextStringObject(
|
|
151
|
+
widget.name
|
|
152
|
+
)
|
|
153
|
+
widget_to_copy.get_object()[NameObject(Rect)] = ArrayObject(
|
|
143
154
|
[
|
|
144
|
-
FloatObject(
|
|
145
|
-
FloatObject(
|
|
146
|
-
FloatObject(
|
|
147
|
-
FloatObject(
|
|
155
|
+
FloatObject(widget.x),
|
|
156
|
+
FloatObject(widget.y),
|
|
157
|
+
FloatObject(widget.x + widget.optional_params.get("width")),
|
|
158
|
+
FloatObject(widget.y + widget.optional_params.get("height")),
|
|
148
159
|
]
|
|
149
160
|
)
|
|
150
161
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
162
|
+
widgets_to_copy.append(widget_to_copy)
|
|
163
|
+
|
|
164
|
+
out.pages[0][NameObject(Annots)] = ArrayObject( # pylint: disable=E1137
|
|
165
|
+
widgets_to_copy
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
with BytesIO() as f:
|
|
169
|
+
out.write(f)
|
|
170
|
+
f.seek(0)
|
|
171
|
+
result.append(f.read())
|
|
172
|
+
|
|
173
|
+
return result
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass
|
|
177
|
+
class SignatureField(Field):
|
|
178
|
+
"""
|
|
179
|
+
Represents a signature field in a PDF document.
|
|
180
|
+
|
|
181
|
+
This dataclass extends the `Field` base class and defines the specific
|
|
182
|
+
attributes that can be configured for a signature input field.
|
|
183
|
+
|
|
184
|
+
Attributes:
|
|
185
|
+
_widget_class (Type[SignatureWidget]): The widget class associated with this field type.
|
|
186
|
+
width (Optional[float]): The width of the signature field.
|
|
187
|
+
height (Optional[float]): The height of the signature field.
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
_widget_class: Type[SignatureWidget] = SignatureWidget
|
|
191
|
+
|
|
192
|
+
width: Optional[float] = None
|
|
193
|
+
height: Optional[float] = None
|
PyPDFForm/widgets/text.py
CHANGED
|
@@ -11,11 +11,48 @@ functionality for interacting with text form fields in PDFs.
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
from dataclasses import dataclass
|
|
14
|
-
from typing import Optional, Tuple
|
|
14
|
+
from typing import Optional, Tuple, Type
|
|
15
15
|
|
|
16
16
|
from .base import Field, Widget
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
class TextWidget(Widget):
|
|
20
|
+
"""
|
|
21
|
+
Represents a text widget in a PDF form.
|
|
22
|
+
|
|
23
|
+
This class inherits from the base Widget class and provides specific
|
|
24
|
+
parameters for text field styling, such as width, height, font size,
|
|
25
|
+
font color, background color, border color, border width, and maximum
|
|
26
|
+
length.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
USER_PARAMS (list): A list of tuples, where each tuple contains the
|
|
30
|
+
user-facing parameter name and the corresponding AcroForm parameter name.
|
|
31
|
+
COLOR_PARAMS (list): A list of user-facing parameter names that represent colors.
|
|
32
|
+
ALLOWED_HOOK_PARAMS (list): A list of allowed hook parameters.
|
|
33
|
+
NONE_DEFAULTS (list): A list of parameters that default to None.
|
|
34
|
+
ACRO_FORM_FUNC (str): The name of the AcroForm function to use for
|
|
35
|
+
creating the text field.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
USER_PARAMS = [
|
|
39
|
+
("required", "required"),
|
|
40
|
+
("tooltip", "tooltip"),
|
|
41
|
+
("width", "width"),
|
|
42
|
+
("height", "height"),
|
|
43
|
+
("font_size", "fontSize"),
|
|
44
|
+
("font_color", "textColor"),
|
|
45
|
+
("bg_color", "fillColor"),
|
|
46
|
+
("border_color", "borderColor"),
|
|
47
|
+
("border_width", "borderWidth"),
|
|
48
|
+
("max_length", "maxlen"),
|
|
49
|
+
]
|
|
50
|
+
COLOR_PARAMS = ["font_color", "bg_color", "border_color"]
|
|
51
|
+
ALLOWED_HOOK_PARAMS = ["alignment", "multiline", "comb", "font"]
|
|
52
|
+
NONE_DEFAULTS = ["max_length"]
|
|
53
|
+
ACRO_FORM_FUNC = "textfield"
|
|
54
|
+
|
|
55
|
+
|
|
19
56
|
@dataclass
|
|
20
57
|
class TextField(Field):
|
|
21
58
|
"""
|
|
@@ -25,7 +62,7 @@ class TextField(Field):
|
|
|
25
62
|
attributes that can be configured for a text input field.
|
|
26
63
|
|
|
27
64
|
Attributes:
|
|
28
|
-
|
|
65
|
+
_widget_class (Type[Widget]): The widget class associated with this field type.
|
|
29
66
|
width (Optional[float]): The width of the text field.
|
|
30
67
|
height (Optional[float]): The height of the text field.
|
|
31
68
|
max_length (Optional[int]): The maximum number of characters allowed in the text field.
|
|
@@ -41,7 +78,7 @@ class TextField(Field):
|
|
|
41
78
|
multiline (Optional[bool]): If True, the text field can display multiple lines of text.
|
|
42
79
|
"""
|
|
43
80
|
|
|
44
|
-
|
|
81
|
+
_widget_class: Type[Widget] = TextWidget
|
|
45
82
|
|
|
46
83
|
width: Optional[float] = None
|
|
47
84
|
height: Optional[float] = None
|
|
@@ -55,40 +92,3 @@ class TextField(Field):
|
|
|
55
92
|
border_width: Optional[float] = None
|
|
56
93
|
alignment: Optional[int] = None
|
|
57
94
|
multiline: Optional[bool] = None
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class TextWidget(Widget):
|
|
61
|
-
"""
|
|
62
|
-
Represents a text widget in a PDF form.
|
|
63
|
-
|
|
64
|
-
This class inherits from the base Widget class and provides specific
|
|
65
|
-
parameters for text field styling, such as width, height, font size,
|
|
66
|
-
font color, background color, border color, border width, and maximum
|
|
67
|
-
length.
|
|
68
|
-
|
|
69
|
-
Attributes:
|
|
70
|
-
USER_PARAMS (list): A list of tuples, where each tuple contains the
|
|
71
|
-
user-facing parameter name and the corresponding AcroForm parameter name.
|
|
72
|
-
COLOR_PARAMS (list): A list of user-facing parameter names that represent colors.
|
|
73
|
-
ALLOWED_HOOK_PARAMS (list): A list of allowed hook parameters.
|
|
74
|
-
NONE_DEFAULTS (list): A list of parameters that default to None.
|
|
75
|
-
ACRO_FORM_FUNC (str): The name of the AcroForm function to use for
|
|
76
|
-
creating the text field.
|
|
77
|
-
"""
|
|
78
|
-
|
|
79
|
-
USER_PARAMS = [
|
|
80
|
-
("required", "required"),
|
|
81
|
-
("tooltip", "tooltip"),
|
|
82
|
-
("width", "width"),
|
|
83
|
-
("height", "height"),
|
|
84
|
-
("font_size", "fontSize"),
|
|
85
|
-
("font_color", "textColor"),
|
|
86
|
-
("bg_color", "fillColor"),
|
|
87
|
-
("border_color", "borderColor"),
|
|
88
|
-
("border_width", "borderWidth"),
|
|
89
|
-
("max_length", "maxlen"),
|
|
90
|
-
]
|
|
91
|
-
COLOR_PARAMS = ["font_color", "bg_color", "border_color"]
|
|
92
|
-
ALLOWED_HOOK_PARAMS = ["alignment", "multiline", "comb", "font"]
|
|
93
|
-
NONE_DEFAULTS = ["max_length"]
|
|
94
|
-
ACRO_FORM_FUNC = "textfield"
|