PyPDFForm 3.5.4__py3-none-any.whl → 3.6.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 CHANGED
@@ -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.5.4"
23
+ __version__ = "3.6.0"
24
24
 
25
25
  from .middleware.text import Text # exposing for setting global font attrs
26
26
  from .widgets import Fields
@@ -21,6 +21,12 @@ from .text import TextField
21
21
  FieldTypes = Union[
22
22
  TextField, CheckBoxField, RadioGroup, DropdownField, SignatureField, ImageField
23
23
  ]
24
+ BulkFieldTypes = Union[
25
+ TextField,
26
+ CheckBoxField,
27
+ RadioGroup,
28
+ DropdownField,
29
+ ]
24
30
 
25
31
 
26
32
  @dataclass
PyPDFForm/widgets/base.py CHANGED
@@ -12,6 +12,8 @@ Classes:
12
12
  functionality for rendering and manipulation.
13
13
  """
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  from dataclasses import dataclass
16
18
  from inspect import signature
17
19
  from io import BytesIO
@@ -25,35 +27,6 @@ from ..constants import fieldFlags, required
25
27
  from ..utils import stream_to_io
26
28
 
27
29
 
28
- @dataclass
29
- class Field:
30
- """
31
- Base dataclass for all PDF form fields.
32
-
33
- This class defines the common properties that all types of form fields
34
- (e.g., text fields, checkboxes, radio buttons) share. Specific field types
35
- will extend this class to add their unique attributes.
36
-
37
- Attributes:
38
- name (str): The name of the form field. This is used to identify the
39
- field within the PDF document.
40
- page_number (int): The 1-based page number on which the field is located.
41
- x (float): The x-coordinate of the field's position on the page.
42
- y (float): The y-coordinate of the field's position on the page.
43
- required (Optional[bool]): Indicates whether the field is required to be
44
- filled by the user. Defaults to None, meaning not explicitly set.
45
- tooltip (Optional[str]): A tooltip message that appears when the user
46
- hovers over the field. Defaults to None.
47
- """
48
-
49
- name: str
50
- page_number: int
51
- x: float
52
- y: float
53
- required: Optional[bool] = None
54
- tooltip: Optional[str] = None
55
-
56
-
57
30
  class Widget:
58
31
  """
59
32
  Base class for all widgets in PyPDFForm.
@@ -219,3 +192,94 @@ class Widget:
219
192
  watermark.read() if i == self.page_number - 1 else b""
220
193
  for i in range(page_count)
221
194
  ]
195
+
196
+ @staticmethod
197
+ def bulk_watermarks(widgets: List[Widget], stream: bytes) -> List[bytes]:
198
+ """
199
+ Generates watermarks for multiple widgets in bulk.
200
+
201
+ This static method processes a list of widgets and a PDF stream to create
202
+ a list of watermark streams, one for each page of the PDF. Widgets are
203
+ grouped by their page number, and all widgets for a given page are drawn
204
+ onto a single ReportLab canvas, which is then returned as the watermark
205
+ stream for that page. This is more efficient than generating watermarks
206
+ for each widget individually.
207
+
208
+ Args:
209
+ widgets (List[Widget]): A list of Widget objects to be watermarked.
210
+ stream (bytes): The PDF stream to be watermarked.
211
+
212
+ Returns:
213
+ List[bytes]: A list of watermark streams (bytes), where the index
214
+ corresponds to the 0-based page index of the original PDF.
215
+ Each element is a byte stream representing the combined
216
+ watermark for that page. Pages without any widgets will
217
+ have an empty byte string (b"").
218
+ """
219
+ result = []
220
+
221
+ pdf = PdfReader(stream_to_io(stream))
222
+ watermark = BytesIO()
223
+
224
+ widgets_by_page = {}
225
+ for widget in widgets:
226
+ if widget.page_number not in widgets_by_page:
227
+ widgets_by_page[widget.page_number] = []
228
+ widgets_by_page[widget.page_number].append(widget)
229
+
230
+ for i, page in enumerate(pdf.pages):
231
+ page_num = i + 1
232
+ if page_num not in widgets_by_page:
233
+ result.append(b"")
234
+ continue
235
+
236
+ watermark.seek(0)
237
+ watermark.flush()
238
+
239
+ canvas = Canvas(
240
+ watermark,
241
+ pagesize=(
242
+ float(page.mediabox[2]),
243
+ float(page.mediabox[3]),
244
+ ),
245
+ )
246
+
247
+ for widget in widgets_by_page[page_num]:
248
+ getattr(widget, "_required_handler")(canvas)
249
+ widget.canvas_operations(canvas)
250
+
251
+ canvas.showPage()
252
+ canvas.save()
253
+ watermark.seek(0)
254
+ result.append(watermark.read())
255
+
256
+ return result
257
+
258
+
259
+ @dataclass
260
+ class Field:
261
+ """
262
+ Base dataclass for all PDF form fields.
263
+
264
+ This class defines the common properties that all types of form fields
265
+ (e.g., text fields, checkboxes, radio buttons) share. Specific field types
266
+ will extend this class to add their unique attributes.
267
+
268
+ Attributes:
269
+ name (str): The name of the form field. This is used to identify the
270
+ field within the PDF document.
271
+ page_number (int): The 1-based page number on which the field is located.
272
+ x (float): The x-coordinate of the field's position on the page.
273
+ y (float): The y-coordinate of the field's position on the page.
274
+ required (Optional[bool]): Indicates whether the field is required to be
275
+ filled by the user. Defaults to None, meaning not explicitly set.
276
+ tooltip (Optional[str]): A tooltip message that appears when the user
277
+ hovers over the field. Defaults to None.
278
+ """
279
+
280
+ name: str
281
+ page_number: int
282
+ x: float
283
+ y: float
284
+ required: Optional[bool] = None
285
+ tooltip: Optional[str] = None
@@ -11,40 +11,11 @@ functionality for interacting with checkbox 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
- @dataclass
20
- class CheckBoxField(Field):
21
- """
22
- Represents a checkbox field in a PDF document.
23
-
24
- This dataclass extends the `Field` base class and defines the specific
25
- attributes that can be configured for a checkbox field.
26
-
27
- Attributes:
28
- _field_type (str): The type of the field, fixed as "checkbox".
29
- size (Optional[float]): The size of the checkbox.
30
- button_style (Optional[str]): The visual style of the checkbox button
31
- (e.g., "check", "circle", "cross").
32
- tick_color (Optional[Tuple[float, ...]]): The color of the checkmark or tick.
33
- bg_color (Optional[Tuple[float, ...]]): The background color of the checkbox.
34
- border_color (Optional[Tuple[float, ...]]): The color of the checkbox's border.
35
- border_width (Optional[float]): The width of the checkbox's border.
36
- """
37
-
38
- _field_type: str = "checkbox"
39
-
40
- size: Optional[float] = None
41
- button_style: Optional[str] = None
42
- tick_color: Optional[Tuple[float, ...]] = None
43
- bg_color: Optional[Tuple[float, ...]] = None
44
- border_color: Optional[Tuple[float, ...]] = None
45
- border_width: Optional[float] = None
46
-
47
-
48
19
  class CheckBoxWidget(Widget):
49
20
  """
50
21
  Represents a checkbox widget in a PDF form.
@@ -74,3 +45,34 @@ class CheckBoxWidget(Widget):
74
45
  COLOR_PARAMS = ["tick_color", "bg_color", "border_color"]
75
46
  ALLOWED_HOOK_PARAMS = ["size"]
76
47
  ACRO_FORM_FUNC = "checkbox"
48
+
49
+
50
+ @dataclass
51
+ class CheckBoxField(Field):
52
+ """
53
+ Represents a checkbox field in a PDF document.
54
+
55
+ This dataclass extends the `Field` base class and defines the specific
56
+ attributes that can be configured for a checkbox field.
57
+
58
+ Attributes:
59
+ _field_type (str): The type of the field, fixed as "checkbox".
60
+ _widget_class (Type[Widget]): The widget class associated with this field type.
61
+ size (Optional[float]): The size of the checkbox.
62
+ button_style (Optional[str]): The visual style of the checkbox button
63
+ (e.g., "check", "circle", "cross").
64
+ tick_color (Optional[Tuple[float, ...]]): The color of the checkmark or tick.
65
+ bg_color (Optional[Tuple[float, ...]]): The background color of the checkbox.
66
+ border_color (Optional[Tuple[float, ...]]): The color of the checkbox's border.
67
+ border_width (Optional[float]): The width of the checkbox's border.
68
+ """
69
+
70
+ _field_type: str = "checkbox"
71
+ _widget_class: Type[Widget] = CheckBoxWidget
72
+
73
+ size: Optional[float] = None
74
+ button_style: Optional[str] = None
75
+ tick_color: Optional[Tuple[float, ...]] = None
76
+ bg_color: Optional[Tuple[float, ...]] = None
77
+ border_color: Optional[Tuple[float, ...]] = None
78
+ border_width: Optional[float] = None
@@ -11,49 +11,12 @@ specific functionality for interacting with dropdown form fields in PDFs.
11
11
  """
12
12
 
13
13
  from dataclasses import dataclass
14
- from typing import List, Optional, Tuple, Union
14
+ from typing import List, Optional, Tuple, Type, Union
15
15
 
16
- from .base import Field
16
+ from .base import Field, Widget
17
17
  from .text import TextWidget
18
18
 
19
19
 
20
- @dataclass
21
- class DropdownField(Field):
22
- """
23
- Represents a dropdown field in a PDF document.
24
-
25
- This dataclass extends the `Field` base class and defines the specific
26
- attributes that can be configured for a dropdown selection field.
27
-
28
- Attributes:
29
- _field_type (str): The type of the field, fixed as "dropdown".
30
- options (Optional[List[Union[str, Tuple[str, str]]]]): A list of options
31
- available in the dropdown. Each option can be a string (display value)
32
- or a tuple of strings (display value, export value).
33
- width (Optional[float]): The width of the dropdown field.
34
- height (Optional[float]): The height of the dropdown field.
35
- font (Optional[str]): The font to use for the dropdown text.
36
- font_size (Optional[float]): The font size for the dropdown text.
37
- font_color (Optional[Tuple[float, ...]]): The color of the font as an RGB or RGBA tuple.
38
- bg_color (Optional[Tuple[float, ...]]): The background color of the dropdown field.
39
- border_color (Optional[Tuple[float, ...]]): The color of the dropdown's border.
40
- border_width (Optional[float]): The width of the dropdown's border.
41
- """
42
-
43
- _field_type: str = "dropdown"
44
-
45
- options: Optional[List[Union[str, Tuple[str, str]]]] = None
46
- width: Optional[float] = None
47
- height: Optional[float] = None
48
- # pylint: disable=R0801
49
- font: Optional[str] = None
50
- font_size: Optional[float] = None
51
- font_color: Optional[Tuple[float, ...]] = None
52
- bg_color: Optional[Tuple[float, ...]] = None
53
- border_color: Optional[Tuple[float, ...]] = None
54
- border_width: Optional[float] = None
55
-
56
-
57
20
  class DropdownWidget(TextWidget):
58
21
  """
59
22
  Represents a dropdown widget in a PDF form.
@@ -94,3 +57,42 @@ class DropdownWidget(TextWidget):
94
57
  super().__init__(name, page_number, x, y, **kwargs)
95
58
  self.acro_form_params["wkind"] = "choice"
96
59
  self.acro_form_params["value"] = self.acro_form_params["options"][0]
60
+
61
+
62
+ @dataclass
63
+ class DropdownField(Field):
64
+ """
65
+ Represents a dropdown field in a PDF document.
66
+
67
+ This dataclass extends the `Field` base class and defines the specific
68
+ attributes that can be configured for a dropdown selection field.
69
+
70
+ Attributes:
71
+ _field_type (str): The type of the field, fixed as "dropdown".
72
+ _widget_class (Type[Widget]): The widget class associated with this field type.
73
+ options (Optional[List[Union[str, Tuple[str, str]]]]): A list of options
74
+ available in the dropdown. Each option can be a string (display value)
75
+ or a tuple of strings (display value, export value).
76
+ width (Optional[float]): The width of the dropdown field.
77
+ height (Optional[float]): The height of the dropdown field.
78
+ font (Optional[str]): The font to use for the dropdown text.
79
+ font_size (Optional[float]): The font size for the dropdown text.
80
+ font_color (Optional[Tuple[float, ...]]): The color of the font as an RGB or RGBA tuple.
81
+ bg_color (Optional[Tuple[float, ...]]): The background color of the dropdown field.
82
+ border_color (Optional[Tuple[float, ...]]): The color of the dropdown's border.
83
+ border_width (Optional[float]): The width of the dropdown's border.
84
+ """
85
+
86
+ _field_type: str = "dropdown"
87
+ _widget_class: Type[Widget] = DropdownWidget
88
+
89
+ options: Optional[List[Union[str, Tuple[str, str]]]] = None
90
+ width: Optional[float] = None
91
+ height: Optional[float] = None
92
+ # pylint: disable=R0801
93
+ font: Optional[str] = None
94
+ font_size: Optional[float] = None
95
+ font_color: Optional[Tuple[float, ...]] = None
96
+ bg_color: Optional[Tuple[float, ...]] = None
97
+ border_color: Optional[Tuple[float, ...]] = None
98
+ border_width: Optional[float] = None
@@ -16,22 +16,6 @@ from dataclasses import dataclass
16
16
  from .signature import SignatureField, SignatureWidget
17
17
 
18
18
 
19
- @dataclass
20
- class ImageField(SignatureField):
21
- """
22
- Represents an image field in a PDF document.
23
-
24
- This dataclass extends the `SignatureField` base class and defines the
25
- specific attributes for an image input field. It inherits `width` and
26
- `height` from `SignatureField` as images also have dimensions.
27
-
28
- Attributes:
29
- _field_type (str): The type of the field, fixed as "image".
30
- """
31
-
32
- _field_type: str = "image"
33
-
34
-
35
19
  class ImageWidget(SignatureWidget):
36
20
  """
37
21
  Represents an image widget in a PDF form.
@@ -47,3 +31,19 @@ class ImageWidget(SignatureWidget):
47
31
  """
48
32
 
49
33
  BEDROCK_WIDGET_TO_COPY = "image"
34
+
35
+
36
+ @dataclass
37
+ class ImageField(SignatureField):
38
+ """
39
+ Represents an image field in a PDF document.
40
+
41
+ This dataclass extends the `SignatureField` base class and defines the
42
+ specific attributes for an image input field. It inherits `width` and
43
+ `height` from `SignatureField` as images also have dimensions.
44
+
45
+ Attributes:
46
+ _field_type (str): The type of the field, fixed as "image".
47
+ """
48
+
49
+ _field_type: str = "image"
@@ -11,38 +11,14 @@ specific functionality for interacting with radio button form fields in PDFs.
11
11
  """
12
12
 
13
13
  from dataclasses import dataclass
14
- from typing import List, Optional
14
+ from typing import List, Optional, Type
15
15
 
16
16
  from reportlab.pdfgen.canvas import Canvas
17
17
 
18
+ from .base import Widget
18
19
  from .checkbox import CheckBoxField, CheckBoxWidget
19
20
 
20
21
 
21
- @dataclass
22
- class RadioGroup(CheckBoxField):
23
- """
24
- Represents a group of radio buttons in a PDF document.
25
-
26
- This dataclass extends the `CheckBoxField` base class and defines the specific
27
- attributes that can be configured for a radio button group. Unlike a single
28
- checkbox, a radio group allows for multiple positions (x, y coordinates)
29
- where individual radio buttons can be placed, but only one can be selected.
30
-
31
- Attributes:
32
- _field_type (str): The type of the field, fixed as "radio".
33
- x (List[float]): A list of x-coordinates for each radio button in the group.
34
- y (List[float]): A list of y-coordinates for each radio button in the group.
35
- shape (Optional[str]): The shape of the radio button. Valid values are
36
- "circle" or "square". Defaults to None, which typically means a default circle shape.
37
- """
38
-
39
- _field_type: str = "radio"
40
-
41
- x: List[float]
42
- y: List[float]
43
- shape: Optional[str] = None
44
-
45
-
46
22
  class RadioWidget(CheckBoxWidget):
47
23
  """
48
24
  Represents a radio button widget in a PDF form.
@@ -97,3 +73,30 @@ class RadioWidget(CheckBoxWidget):
97
73
  new_acro_form_params["y"] = y
98
74
  new_acro_form_params["value"] = str(i)
99
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
+ _field_type (str): The type of the field, fixed as "radio".
90
+ _widget_class (Type[Widget]): The widget class associated with this field type.
91
+ x (List[float]): A list of x-coordinates for each radio button in the group.
92
+ y (List[float]): A list of y-coordinates for each radio button in the group.
93
+ shape (Optional[str]): The shape of the radio button. Valid values are
94
+ "circle" or "square". Defaults to None, which typically means a default circle shape.
95
+ """
96
+
97
+ _field_type: str = "radio"
98
+ _widget_class: Type[Widget] = RadioWidget
99
+
100
+ x: List[float]
101
+ y: List[float]
102
+ shape: Optional[str] = None
@@ -26,26 +26,6 @@ from .base import Field
26
26
  from .bedrock import BEDROCK_PDF
27
27
 
28
28
 
29
- @dataclass
30
- class SignatureField(Field):
31
- """
32
- Represents a signature field in a PDF document.
33
-
34
- This dataclass extends the `Field` base class and defines the specific
35
- attributes that can be configured for a signature input field.
36
-
37
- Attributes:
38
- _field_type (str): The type of the field, fixed as "signature".
39
- width (Optional[float]): The width of the signature field.
40
- height (Optional[float]): The height of the signature field.
41
- """
42
-
43
- _field_type: str = "signature"
44
-
45
- width: Optional[float] = None
46
- height: Optional[float] = None
47
-
48
-
49
29
  class SignatureWidget:
50
30
  """
51
31
  Represents a signature widget in a PDF form.
@@ -151,3 +131,23 @@ class SignatureWidget:
151
131
  f.read() if i == self.page_number - 1 else b""
152
132
  for i in range(page_count)
153
133
  ]
134
+
135
+
136
+ @dataclass
137
+ class SignatureField(Field):
138
+ """
139
+ Represents a signature field in a PDF document.
140
+
141
+ This dataclass extends the `Field` base class and defines the specific
142
+ attributes that can be configured for a signature input field.
143
+
144
+ Attributes:
145
+ _field_type (str): The type of the field, fixed as "signature".
146
+ width (Optional[float]): The width of the signature field.
147
+ height (Optional[float]): The height of the signature field.
148
+ """
149
+
150
+ _field_type: str = "signature"
151
+
152
+ width: Optional[float] = None
153
+ 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
  """
@@ -26,6 +63,7 @@ class TextField(Field):
26
63
 
27
64
  Attributes:
28
65
  _field_type (str): The type of the field, fixed as "text".
66
+ _widget_class (Type[Widget]): The widget class associated with this field type.
29
67
  width (Optional[float]): The width of the text field.
30
68
  height (Optional[float]): The height of the text field.
31
69
  max_length (Optional[int]): The maximum number of characters allowed in the text field.
@@ -42,6 +80,7 @@ class TextField(Field):
42
80
  """
43
81
 
44
82
  _field_type: str = "text"
83
+ _widget_class: Type[Widget] = TextWidget
45
84
 
46
85
  width: Optional[float] = None
47
86
  height: Optional[float] = None
@@ -55,40 +94,3 @@ class TextField(Field):
55
94
  border_width: Optional[float] = None
56
95
  alignment: Optional[int] = None
57
96
  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"
PyPDFForm/wrapper.py CHANGED
@@ -18,6 +18,7 @@ underlying PDF manipulation.
18
18
 
19
19
  from __future__ import annotations
20
20
 
21
+ from collections import defaultdict
21
22
  from dataclasses import asdict
22
23
  from functools import cached_property
23
24
  from typing import TYPE_CHECKING, BinaryIO, Dict, List, Sequence, Tuple, Union
@@ -41,6 +42,8 @@ from .utils import (enable_adobe_mode, generate_unique_suffix,
41
42
  get_page_streams, merge_two_pdfs, remove_all_widgets)
42
43
  from .watermark import (copy_watermark_widgets, create_watermarks_and_draw,
43
44
  merge_watermarks_with_pdf)
45
+ from .widgets import CheckBoxField
46
+ from .widgets.base import Widget
44
47
  from .widgets.checkbox import CheckBoxWidget
45
48
  from .widgets.dropdown import DropdownWidget
46
49
  from .widgets.image import ImageWidget
@@ -49,7 +52,7 @@ from .widgets.signature import SignatureWidget
49
52
  from .widgets.text import TextWidget
50
53
 
51
54
  if TYPE_CHECKING:
52
- from .widgets import FieldTypes
55
+ from .widgets import BulkFieldTypes, FieldTypes
53
56
 
54
57
 
55
58
  class PdfWrapper:
@@ -451,6 +454,91 @@ class PdfWrapper:
451
454
 
452
455
  return self
453
456
 
457
+ def bulk_create_fields(self, fields: List[BulkFieldTypes]) -> PdfWrapper:
458
+ """
459
+ Creates multiple new form fields (widgets) on the PDF in a single operation.
460
+
461
+ This method takes a list of field definition objects (`BulkFieldTypes`),
462
+ groups them by type (if necessary for specific widget handling, like CheckBoxField),
463
+ and then delegates the creation to the internal `_bulk_create_fields` method.
464
+ This is the preferred method for creating multiple fields as it minimizes
465
+ PDF manipulation overhead.
466
+
467
+ Args:
468
+ fields (List[BulkFieldTypes]): A list of field definition objects
469
+ (e.g., `TextField`, `CheckBoxField`, etc.) to be created.
470
+
471
+ Returns:
472
+ PdfWrapper: The `PdfWrapper` object, allowing for method chaining.
473
+ """
474
+
475
+ needs_separate_creation = [CheckBoxField]
476
+ needs_separate_creation_dict = defaultdict(list)
477
+ general_creation = []
478
+
479
+ for each in fields:
480
+ if type(each) in needs_separate_creation:
481
+ needs_separate_creation_dict[type(each)].append(each)
482
+ else:
483
+ general_creation.append(each)
484
+
485
+ for each in needs_separate_creation_dict.values():
486
+ self._bulk_create_fields(each)
487
+ self._bulk_create_fields(general_creation)
488
+
489
+ return self
490
+
491
+ def _bulk_create_fields(self, fields: List[BulkFieldTypes]) -> PdfWrapper:
492
+ """
493
+ Internal method to create multiple new form fields (widgets) on the PDF in a single operation.
494
+
495
+ This method takes a list of field definition objects (`BulkFieldTypes`),
496
+ converts them into `Widget` objects, and efficiently draws them onto the
497
+ PDF using bulk watermarking. It is designed to be called by the public
498
+ `bulk_create_fields` method after fields have been grouped for creation.
499
+
500
+ Args:
501
+ fields (List[BulkFieldTypes]): A list of field definition objects
502
+ (e.g., `TextField`, `CheckBoxField`, etc.) to be created.
503
+
504
+ Returns:
505
+ PdfWrapper: The `PdfWrapper` object, allowing for method chaining.
506
+ """
507
+
508
+ widgets = []
509
+ for field in fields:
510
+ field_dict = asdict(field)
511
+ widget_class = getattr(field, "_widget_class")
512
+ name = field_dict.pop("name")
513
+ page_number = field_dict.pop("page_number")
514
+ x = field_dict.pop("x")
515
+ y = field_dict.pop("y")
516
+ widgets.append(
517
+ widget_class(
518
+ name=name,
519
+ page_number=page_number,
520
+ x=x,
521
+ y=y,
522
+ **{k: v for k, v in field_dict.items() if v is not None},
523
+ )
524
+ )
525
+
526
+ watermarks = Widget.bulk_watermarks(widgets, self.read())
527
+ self._stream = copy_watermark_widgets(
528
+ self.read(),
529
+ watermarks,
530
+ [widget.acro_form_params["name"] for widget in widgets],
531
+ None,
532
+ )
533
+
534
+ self._init_helper()
535
+
536
+ for widget in widgets:
537
+ for k, v in widget.hook_params:
538
+ self.widgets[widget.acro_form_params["name"]].__setattr__(k, v)
539
+
540
+ return self
541
+
454
542
  def create_field(
455
543
  self,
456
544
  field: FieldTypes,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPDFForm
3
- Version: 3.5.4
3
+ Version: 3.6.0
4
4
  Summary: The Python library for PDF forms.
5
5
  Author: Jinge Li
6
6
  License-Expression: MIT
@@ -1,4 +1,4 @@
1
- PyPDFForm/__init__.py,sha256=xugCQffMDzGIzmX9CpnPXLKpZGzqUZTtcsdkhhBAUyA,963
1
+ PyPDFForm/__init__.py,sha256=CC5j1ui603ZNCwB7vhfBoSkfUothfptPDO9jrhWLbMs,963
2
2
  PyPDFForm/adapter.py,sha256=0OTZBzGYzOerfXzUPvNq2HNVcMmkRnxNHQ1-Ys2VjYA,2427
3
3
  PyPDFForm/constants.py,sha256=tFtomm4bSsghxRqKaMP6Tln7ug9W_72e-AAU3sEU_no,2917
4
4
  PyPDFForm/coordinate.py,sha256=VMVkqa-VAGJSGVvetZwOxeMzIgQtQdvtn_DI_qSecCE,3876
@@ -10,7 +10,7 @@ PyPDFForm/patterns.py,sha256=Xhdsef7pPEIVEYhQRyOzonld3z1wk6WZ3TTjnj-RvpU,9573
10
10
  PyPDFForm/template.py,sha256=GSNMH5meKk5B62sj_UXxiJgs4SOVDcQCNISvARmXJZ0,10004
11
11
  PyPDFForm/utils.py,sha256=hLSVUG6qnE0iTMB-yPNQQIhmm3R69X7fcnbCTDvSUQs,11001
12
12
  PyPDFForm/watermark.py,sha256=9p1tjaIqicXngTNai_iOEkCoXRYnR66azB4s7wNsZUw,9349
13
- PyPDFForm/wrapper.py,sha256=ayqL-p7Ftwg0txMpK8sNxQ0aIoUrPtyPwJEIgej_Ofo,27598
13
+ PyPDFForm/wrapper.py,sha256=8kYjgd18F_27QJXRJnxcZQ6RLyRmpXJHb0BAdtwUviw,30951
14
14
  PyPDFForm/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  PyPDFForm/middleware/base.py,sha256=YQNVhjoDV2E3WYSQrahHkD6_Zdfxpcnr9ONKeB7hcgM,3223
16
16
  PyPDFForm/middleware/checkbox.py,sha256=OCSZEFD8wQG_Y9qO7Os6VXTaxJCpkRYTxI4wDgG0GZc,1870
@@ -19,17 +19,17 @@ PyPDFForm/middleware/image.py,sha256=eKM7anU56jbaECnK6rq0jGsBRY3HW_fM86fgA3hq7xA
19
19
  PyPDFForm/middleware/radio.py,sha256=PuGDJ8RN1C-MkL9Jf14ABWYV67cN18R66dI4nR-03DU,2211
20
20
  PyPDFForm/middleware/signature.py,sha256=a2IfD36zpEWXWNNWRvtJ6nG6TszkF6Wil82Szsbjfns,2149
21
21
  PyPDFForm/middleware/text.py,sha256=WLK6Ae9zT3uUu1AzcWUhR-hs5rm0z9Ydz-fL8Qu-44o,3997
22
- PyPDFForm/widgets/__init__.py,sha256=TvMMWGtrTC4xRPRokr4Pl8ZtvfYXRPUDgfHd3pnbPE8,1304
23
- PyPDFForm/widgets/base.py,sha256=2DuCZSAcwpb5SrJzmFr7f2v23DmNdR6K1y85SWw1SD8,7876
22
+ PyPDFForm/widgets/__init__.py,sha256=gVJzqdpj6NRltqXrvCeHJO28snlIwHydbftWCnLZnvI,1399
23
+ PyPDFForm/widgets/base.py,sha256=FCFMJPMeqE1z0zNe_IK6kEu8M_sYFy7Tu8SyAMDJ14M,10195
24
24
  PyPDFForm/widgets/bedrock.py,sha256=j6beU04kaQzpAIFZHI5VJLaDT5RVAAa6LzkU1luJpN8,137660
25
- PyPDFForm/widgets/checkbox.py,sha256=IwQvw2W6HuSLyZFv-qGvdvCHp8uAp2KjOAt7-Y73Y74,2870
26
- PyPDFForm/widgets/dropdown.py,sha256=7yPxx-uWyZe8wxxVqYVXvomPHp_5CAygAEMldibe6eg,3528
27
- PyPDFForm/widgets/image.py,sha256=SegpzNjTXPuWX4hk_20yKk_IDaOrc81X9b9B4qK-kME,1628
28
- PyPDFForm/widgets/radio.py,sha256=DtUXjAIwxrRiLWWegrfRaGsSryEuqb9OQtoHbrkLayI,3581
29
- PyPDFForm/widgets/signature.py,sha256=7p53MyLVCfW36VE4IgMyC0jmbctCNSQkAmye89cRDMI,5247
30
- PyPDFForm/widgets/text.py,sha256=EG2mR-POHGWt9I-t3QVsTFIiDB5yqKPDBTKkAytQGp8,3836
31
- pypdfform-3.5.4.dist-info/licenses/LICENSE,sha256=43awmYkI6opyTpg19me731iO1WfXZwViqb67oWtCsFY,1065
32
- pypdfform-3.5.4.dist-info/METADATA,sha256=GVHQ-sigYZbqRWXd2EFLVDJQlss61ARwxAk0uIC6J-A,4269
33
- pypdfform-3.5.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
- pypdfform-3.5.4.dist-info/top_level.txt,sha256=GQQKuWqPUjT9YZqwK95NlAQzxjwoQrsxQ8ureM8lWOY,10
35
- pypdfform-3.5.4.dist-info/RECORD,,
25
+ PyPDFForm/widgets/checkbox.py,sha256=QQH9MURhyASDss83uWRvD8R7uyZ1D9fxtLnpNCVFc10,3013
26
+ PyPDFForm/widgets/dropdown.py,sha256=jIRefrCqUZy9MJxeMlAnreclnSeai6YflsNDuRzim-o,3679
27
+ PyPDFForm/widgets/image.py,sha256=t-nHD38hF_wgHCAeBliqedbHX94k331L4HtKYhxkOaY,1628
28
+ PyPDFForm/widgets/radio.py,sha256=ip1imdGQhbQ6GO57Z8HNrzQkQH7SEq6K-azS949lMkU,3746
29
+ PyPDFForm/widgets/signature.py,sha256=-hc3bvU0zLPnla0CLzrnMAkJkPo_Nce_VO8UdeDUH90,5247
30
+ PyPDFForm/widgets/text.py,sha256=RJXPLCopmuayKJSvVeDVaiMRvqOjFoFwqiEzgyydIhg,3975
31
+ pypdfform-3.6.0.dist-info/licenses/LICENSE,sha256=43awmYkI6opyTpg19me731iO1WfXZwViqb67oWtCsFY,1065
32
+ pypdfform-3.6.0.dist-info/METADATA,sha256=3w5_arGBmdkCiaWFjWYwLlDv-E3D8XFY5N2sh2_KtM4,4269
33
+ pypdfform-3.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
+ pypdfform-3.6.0.dist-info/top_level.txt,sha256=GQQKuWqPUjT9YZqwK95NlAQzxjwoQrsxQ8ureM8lWOY,10
35
+ pypdfform-3.6.0.dist-info/RECORD,,