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
PyPDFForm/wrapper.py
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
"""
|
|
2
|
+
"""Provides high-level wrapper classes for working with PDF forms.
|
|
3
|
+
|
|
4
|
+
This module contains the FormWrapper and PdfWrapper classes which provide
|
|
5
|
+
a user-friendly interface for:
|
|
6
|
+
- Filling PDF form fields
|
|
7
|
+
- Creating and modifying PDF form widgets
|
|
8
|
+
- Drawing text and images on PDFs
|
|
9
|
+
- Merging PDF documents
|
|
10
|
+
- Generating coordinate grids
|
|
11
|
+
- Other PDF manipulation tasks
|
|
12
|
+
|
|
13
|
+
The wrappers handle low-level PDF operations while exposing simple methods
|
|
14
|
+
for common use cases.
|
|
15
|
+
"""
|
|
3
16
|
|
|
4
17
|
from __future__ import annotations
|
|
5
18
|
|
|
@@ -21,27 +34,61 @@ from .template import (build_widgets, dropdown_to_text,
|
|
|
21
34
|
update_widget_keys)
|
|
22
35
|
from .utils import (generate_unique_suffix, get_page_streams, merge_two_pdfs,
|
|
23
36
|
preview_widget_to_draw, remove_all_widgets)
|
|
24
|
-
from .watermark import create_watermarks_and_draw,
|
|
37
|
+
from .watermark import (copy_watermark_widgets, create_watermarks_and_draw,
|
|
38
|
+
merge_watermarks_with_pdf)
|
|
25
39
|
from .widgets.base import handle_non_acro_form_params
|
|
26
40
|
from .widgets.checkbox import CheckBoxWidget
|
|
27
41
|
from .widgets.dropdown import DropdownWidget
|
|
42
|
+
from .widgets.radio import RadioWidget
|
|
28
43
|
from .widgets.text import TextWidget
|
|
29
44
|
|
|
30
45
|
|
|
31
46
|
class FormWrapper:
|
|
32
|
-
"""
|
|
47
|
+
"""Base class providing core PDF form filling functionality.
|
|
48
|
+
|
|
49
|
+
This wrapper handles basic PDF form operations:
|
|
50
|
+
- Accessing raw PDF data through the read() method
|
|
51
|
+
- Filling existing form fields with provided values
|
|
52
|
+
|
|
53
|
+
Note: This class does not parse or analyze form fields - it only fills values
|
|
54
|
+
into fields that already exist in the template PDF.
|
|
55
|
+
|
|
56
|
+
The FormWrapper is designed to be extended by PdfWrapper which adds
|
|
57
|
+
more advanced features like form analysis and widget creation.
|
|
58
|
+
"""
|
|
33
59
|
|
|
34
60
|
def __init__(
|
|
35
61
|
self,
|
|
36
62
|
template: Union[bytes, str, BinaryIO] = b"",
|
|
37
63
|
) -> None:
|
|
38
|
-
"""
|
|
64
|
+
"""Initializes the base form wrapper with a PDF template.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
template: PDF form as bytes, file path, or file object. Defaults to
|
|
68
|
+
empty bytes if not provided.
|
|
69
|
+
|
|
70
|
+
Initializes:
|
|
71
|
+
- Internal PDF stream from the template
|
|
72
|
+
- Basic form filling capabilities
|
|
73
|
+
|
|
74
|
+
Note:
|
|
75
|
+
This base class is designed to be extended by PdfWrapper which adds
|
|
76
|
+
more advanced features. For most use cases, you'll want to use PdfWrapper.
|
|
77
|
+
"""
|
|
39
78
|
|
|
40
79
|
super().__init__()
|
|
41
80
|
self.stream = fp_or_f_obj_or_stream_to_stream(template)
|
|
42
81
|
|
|
43
82
|
def read(self) -> bytes:
|
|
44
|
-
"""
|
|
83
|
+
"""Returns the raw bytes of the PDF form data.
|
|
84
|
+
|
|
85
|
+
This method provides access to the underlying PDF bytes after operations
|
|
86
|
+
like fill() have been performed. No parsing or analysis of the PDF
|
|
87
|
+
content is done - the bytes are returned as-is.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
bytes: The complete PDF document as a byte string
|
|
91
|
+
"""
|
|
45
92
|
|
|
46
93
|
return self.stream
|
|
47
94
|
|
|
@@ -50,7 +97,21 @@ class FormWrapper:
|
|
|
50
97
|
data: Dict[str, Union[str, bool, int]],
|
|
51
98
|
**kwargs,
|
|
52
99
|
) -> FormWrapper:
|
|
53
|
-
"""Fills
|
|
100
|
+
"""Fills form fields in the PDF with provided values.
|
|
101
|
+
|
|
102
|
+
Takes a dictionary of field names to values and updates the corresponding
|
|
103
|
+
form fields in the PDF. Only fields that exist in the template PDF will
|
|
104
|
+
be filled - unknown field names are silently ignored.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
data: Dictionary mapping field names to values (str, bool or int)
|
|
108
|
+
**kwargs: Additional options:
|
|
109
|
+
flatten: If True, makes form fields read-only after filling
|
|
110
|
+
adobe_mode: If True, uses Adobe-compatible filling logic
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
FormWrapper: Returns self to allow method chaining
|
|
114
|
+
"""
|
|
54
115
|
|
|
55
116
|
widgets = build_widgets(self.stream, False, False) if self.stream else {}
|
|
56
117
|
|
|
@@ -69,7 +130,22 @@ class FormWrapper:
|
|
|
69
130
|
|
|
70
131
|
|
|
71
132
|
class PdfWrapper(FormWrapper):
|
|
72
|
-
"""
|
|
133
|
+
"""Extended PDF form wrapper with advanced features.
|
|
134
|
+
|
|
135
|
+
Inherits from FormWrapper and adds capabilities for:
|
|
136
|
+
- Creating and modifying form widgets
|
|
137
|
+
- Drawing text and images
|
|
138
|
+
- Merging PDF documents
|
|
139
|
+
- Generating coordinate grids
|
|
140
|
+
- Form schema generation
|
|
141
|
+
- Font registration
|
|
142
|
+
|
|
143
|
+
Key Features:
|
|
144
|
+
- Maintains widget state and properties
|
|
145
|
+
- Supports per-page operations
|
|
146
|
+
- Handles PDF version management
|
|
147
|
+
- Provides preview functionality
|
|
148
|
+
"""
|
|
73
149
|
|
|
74
150
|
USER_PARAMS = [
|
|
75
151
|
("global_font", None),
|
|
@@ -84,7 +160,23 @@ class PdfWrapper(FormWrapper):
|
|
|
84
160
|
template: Union[bytes, str, BinaryIO] = b"",
|
|
85
161
|
**kwargs,
|
|
86
162
|
) -> None:
|
|
87
|
-
"""
|
|
163
|
+
"""Initializes the PDF wrapper with template and configuration.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
template: PDF form as bytes, file path, or file object. Defaults to
|
|
167
|
+
empty bytes if not provided.
|
|
168
|
+
**kwargs: Optional configuration parameters including:
|
|
169
|
+
global_font: Default font name for text fields
|
|
170
|
+
global_font_size: Default font size
|
|
171
|
+
global_font_color: Default font color as RGB tuple
|
|
172
|
+
use_full_widget_name: Whether to use full widget names
|
|
173
|
+
render_widgets: Whether to render widgets in the PDF
|
|
174
|
+
|
|
175
|
+
Initializes:
|
|
176
|
+
- Widgets dictionary to track form fields
|
|
177
|
+
- Keys update queue for deferred operations
|
|
178
|
+
- Any specified global settings from kwargs
|
|
179
|
+
"""
|
|
88
180
|
|
|
89
181
|
super().__init__(template)
|
|
90
182
|
self.widgets = {}
|
|
@@ -96,7 +188,23 @@ class PdfWrapper(FormWrapper):
|
|
|
96
188
|
self._init_helper()
|
|
97
189
|
|
|
98
190
|
def _init_helper(self, key_to_refresh: str = None) -> None:
|
|
99
|
-
"""
|
|
191
|
+
"""Internal method to refresh widget state after PDF stream changes.
|
|
192
|
+
|
|
193
|
+
Called whenever the underlying PDF stream is modified to:
|
|
194
|
+
- Rebuild the widgets dictionary
|
|
195
|
+
- Preserve existing widget properties
|
|
196
|
+
- Apply global font settings to text widgets
|
|
197
|
+
- Handle special refresh cases for specific widgets
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
key_to_refresh: Optional specific widget key that needs refreshing.
|
|
201
|
+
If provided, only that widget's font properties will be updated.
|
|
202
|
+
If None, all text widgets will have their fonts updated.
|
|
203
|
+
|
|
204
|
+
Note:
|
|
205
|
+
This is an internal method and typically shouldn't be called directly.
|
|
206
|
+
It's automatically invoked after operations that modify the PDF stream.
|
|
207
|
+
"""
|
|
100
208
|
|
|
101
209
|
refresh_not_needed = {}
|
|
102
210
|
new_widgets = (
|
|
@@ -126,13 +234,34 @@ class PdfWrapper(FormWrapper):
|
|
|
126
234
|
|
|
127
235
|
@property
|
|
128
236
|
def sample_data(self) -> dict:
|
|
129
|
-
"""
|
|
237
|
+
"""Generates a dictionary of sample values for all form fields.
|
|
238
|
+
|
|
239
|
+
Returns a dictionary mapping each widget/field name to an appropriate
|
|
240
|
+
sample value based on its type:
|
|
241
|
+
- Text fields: Field name (truncated if max_length specified)
|
|
242
|
+
- Checkboxes: True
|
|
243
|
+
- Dropdowns: Index of last available choice
|
|
244
|
+
- Other fields: Type-specific sample values
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
dict: Field names mapped to their sample values
|
|
248
|
+
"""
|
|
130
249
|
|
|
131
250
|
return {key: value.sample_value for key, value in self.widgets.items()}
|
|
132
251
|
|
|
133
252
|
@property
|
|
134
253
|
def version(self) -> Union[str, None]:
|
|
135
|
-
"""Gets the version
|
|
254
|
+
"""Gets the PDF version number from the document header.
|
|
255
|
+
|
|
256
|
+
The version is extracted from the PDF header which contains a version
|
|
257
|
+
identifier like '%PDF-1.4'. This method returns just the version number
|
|
258
|
+
portion (e.g. '1.4') if found, or None if no valid version identifier
|
|
259
|
+
is present.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
str: The PDF version number (e.g. '1.4') if found
|
|
263
|
+
None: If no valid version identifier exists in the PDF
|
|
264
|
+
"""
|
|
136
265
|
|
|
137
266
|
for each in VERSION_IDENTIFIERS:
|
|
138
267
|
if self.stream.startswith(each):
|
|
@@ -142,7 +271,18 @@ class PdfWrapper(FormWrapper):
|
|
|
142
271
|
|
|
143
272
|
@cached_property
|
|
144
273
|
def pages(self) -> List[PdfWrapper]:
|
|
145
|
-
"""Returns
|
|
274
|
+
"""Returns individual page wrappers for each page in the PDF.
|
|
275
|
+
|
|
276
|
+
Creates a separate PdfWrapper instance for each page, maintaining all
|
|
277
|
+
the original wrapper's settings (fonts, rendering options etc.). This
|
|
278
|
+
allows per-page operations while preserving the parent's configuration.
|
|
279
|
+
|
|
280
|
+
The result is cached after first access for better performance with
|
|
281
|
+
repeated calls.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
List[PdfWrapper]: List of wrapper objects, one per page
|
|
285
|
+
"""
|
|
146
286
|
|
|
147
287
|
return [
|
|
148
288
|
self.__class__(
|
|
@@ -152,7 +292,17 @@ class PdfWrapper(FormWrapper):
|
|
|
152
292
|
]
|
|
153
293
|
|
|
154
294
|
def change_version(self, version: str) -> PdfWrapper:
|
|
155
|
-
"""Changes the version
|
|
295
|
+
"""Changes the PDF version identifier in the document header.
|
|
296
|
+
|
|
297
|
+
Modifies the version header (e.g. '%PDF-1.4') to match the specified version.
|
|
298
|
+
Note this only changes the version identifier, not the actual PDF features used.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
version: Target version string (e.g. '1.4', '1.7')
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
PdfWrapper: Returns self to allow method chaining
|
|
305
|
+
"""
|
|
156
306
|
|
|
157
307
|
self.stream = self.stream.replace(
|
|
158
308
|
VERSION_IDENTIFIER_PREFIX + bytes(self.version, "utf-8"),
|
|
@@ -163,7 +313,19 @@ class PdfWrapper(FormWrapper):
|
|
|
163
313
|
return self
|
|
164
314
|
|
|
165
315
|
def __add__(self, other: PdfWrapper) -> PdfWrapper:
|
|
166
|
-
"""
|
|
316
|
+
"""Merges two PDF forms together using the + operator.
|
|
317
|
+
|
|
318
|
+
Combines the content of both PDF forms while:
|
|
319
|
+
- Preserving each form's widgets and data
|
|
320
|
+
- Adding unique suffixes to duplicate field names
|
|
321
|
+
- Maintaining all page content and ordering
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
other: Another PdfWrapper instance to merge with
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
PdfWrapper: New wrapper containing merged PDF
|
|
328
|
+
"""
|
|
167
329
|
|
|
168
330
|
if not self.stream:
|
|
169
331
|
return other
|
|
@@ -182,7 +344,16 @@ class PdfWrapper(FormWrapper):
|
|
|
182
344
|
|
|
183
345
|
@property
|
|
184
346
|
def preview(self) -> bytes:
|
|
185
|
-
"""
|
|
347
|
+
"""Generates a preview PDF showing widget names above their locations.
|
|
348
|
+
|
|
349
|
+
Creates a modified version of the PDF where:
|
|
350
|
+
- All form widgets are removed
|
|
351
|
+
- Widget names are drawn slightly above their original positions
|
|
352
|
+
- Helps visualize form field locations without interactive widgets
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
bytes: PDF bytes containing the preview annotations
|
|
356
|
+
"""
|
|
186
357
|
|
|
187
358
|
return remove_all_widgets(
|
|
188
359
|
fill(
|
|
@@ -197,7 +368,20 @@ class PdfWrapper(FormWrapper):
|
|
|
197
368
|
def generate_coordinate_grid(
|
|
198
369
|
self, color: Tuple[float, float, float] = (1, 0, 0), margin: float = 100
|
|
199
370
|
) -> PdfWrapper:
|
|
200
|
-
"""
|
|
371
|
+
"""Generates a coordinate grid overlay for the PDF.
|
|
372
|
+
|
|
373
|
+
Creates a visual grid showing x,y coordinates to help with:
|
|
374
|
+
- Precise widget placement
|
|
375
|
+
- Measuring distances between elements
|
|
376
|
+
- Debugging layout issues
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
color: RGB tuple (0-1 range) for grid line color (default: red)
|
|
380
|
+
margin: Spacing between grid lines in PDF units (default: 100)
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
PdfWrapper: Returns self to allow method chaining
|
|
384
|
+
"""
|
|
201
385
|
|
|
202
386
|
self.stream = generate_coordinate_grid(
|
|
203
387
|
remove_all_widgets(
|
|
@@ -220,7 +404,20 @@ class PdfWrapper(FormWrapper):
|
|
|
220
404
|
data: Dict[str, Union[str, bool, int]],
|
|
221
405
|
**kwargs,
|
|
222
406
|
) -> PdfWrapper:
|
|
223
|
-
"""Fills
|
|
407
|
+
"""Fills form fields while preserving widget properties and positions.
|
|
408
|
+
|
|
409
|
+
Extends FormWrapper.fill() with additional features:
|
|
410
|
+
- Maintains widget properties like fonts and styles
|
|
411
|
+
- Converts dropdowns to text fields while preserving choices
|
|
412
|
+
- Updates text field attributes and character spacing
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
data: Dictionary mapping field names to values (str, bool or int)
|
|
416
|
+
**kwargs: Currently unused, maintained for future compatibility
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
PdfWrapper: Returns self to allow method chaining
|
|
420
|
+
"""
|
|
224
421
|
|
|
225
422
|
for key, value in data.items():
|
|
226
423
|
if key in self.widgets:
|
|
@@ -243,11 +440,34 @@ class PdfWrapper(FormWrapper):
|
|
|
243
440
|
widget_type: str,
|
|
244
441
|
name: str,
|
|
245
442
|
page_number: int,
|
|
246
|
-
x: float,
|
|
247
|
-
y: float,
|
|
443
|
+
x: Union[float, List[float]],
|
|
444
|
+
y: Union[float, List[float]],
|
|
248
445
|
**kwargs,
|
|
249
446
|
) -> PdfWrapper:
|
|
250
|
-
"""
|
|
447
|
+
"""
|
|
448
|
+
Creates a new interactive widget (form field) on the PDF.
|
|
449
|
+
|
|
450
|
+
Supported widget types:
|
|
451
|
+
- "text": Text input field
|
|
452
|
+
- "checkbox": Checkbox field
|
|
453
|
+
- "dropdown": Dropdown/combobox field
|
|
454
|
+
- "radio": Radio button field
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
widget_type (str): Type of widget to create. Must be one of:
|
|
458
|
+
"text", "checkbox", "dropdown", or "radio".
|
|
459
|
+
name (str): Unique name/identifier for the widget.
|
|
460
|
+
page_number (int): 1-based page number to add the widget to.
|
|
461
|
+
x (float or List[float]): X coordinate(s) for widget position.
|
|
462
|
+
y (float or List[float]): Y coordinate(s) for widget position.
|
|
463
|
+
**kwargs: Additional widget-specific parameters:
|
|
464
|
+
For text fields: width, height, font, font_size, etc.
|
|
465
|
+
For checkboxes: size, checked, etc.
|
|
466
|
+
For dropdowns: choices, default_index, etc.
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
PdfWrapper: Returns self to allow method chaining.
|
|
470
|
+
"""
|
|
251
471
|
|
|
252
472
|
_class = None
|
|
253
473
|
if widget_type == "text":
|
|
@@ -256,13 +476,18 @@ class PdfWrapper(FormWrapper):
|
|
|
256
476
|
_class = CheckBoxWidget
|
|
257
477
|
if widget_type == "dropdown":
|
|
258
478
|
_class = DropdownWidget
|
|
479
|
+
if widget_type == "radio":
|
|
480
|
+
_class = RadioWidget
|
|
259
481
|
if _class is None:
|
|
260
482
|
return self
|
|
261
483
|
|
|
262
484
|
obj = _class(name=name, page_number=page_number, x=x, y=y, **kwargs)
|
|
263
485
|
watermarks = obj.watermarks(self.read())
|
|
264
486
|
|
|
265
|
-
|
|
487
|
+
if widget_type == "radio":
|
|
488
|
+
self.stream = copy_watermark_widgets(self.read(), watermarks)
|
|
489
|
+
else:
|
|
490
|
+
self.stream = merge_watermarks_with_pdf(self.read(), watermarks)
|
|
266
491
|
if obj.non_acro_form_params:
|
|
267
492
|
self.stream = handle_non_acro_form_params(
|
|
268
493
|
self.stream, name, obj.non_acro_form_params
|
|
@@ -279,7 +504,23 @@ class PdfWrapper(FormWrapper):
|
|
|
279
504
|
def update_widget_key(
|
|
280
505
|
self, old_key: str, new_key: str, index: int = 0, defer: bool = False
|
|
281
506
|
) -> PdfWrapper:
|
|
282
|
-
"""Updates the key of an
|
|
507
|
+
"""Updates the field name/key of an existing widget in the PDF form.
|
|
508
|
+
|
|
509
|
+
Allows renaming form fields while preserving all other properties.
|
|
510
|
+
Supports both immediate and deferred (batched) updates.
|
|
511
|
+
|
|
512
|
+
Args:
|
|
513
|
+
old_key: Current field name/key to be updated
|
|
514
|
+
new_key: New field name/key to use
|
|
515
|
+
index: Index for widgets with duplicate names (default: 0)
|
|
516
|
+
defer: If True, queues the update for later batch processing
|
|
517
|
+
|
|
518
|
+
Returns:
|
|
519
|
+
PdfWrapper: Returns self to allow method chaining
|
|
520
|
+
|
|
521
|
+
Raises:
|
|
522
|
+
NotImplementedError: When use_full_widget_name is enabled
|
|
523
|
+
"""
|
|
283
524
|
|
|
284
525
|
if getattr(self, "use_full_widget_name"):
|
|
285
526
|
raise NotImplementedError
|
|
@@ -296,7 +537,18 @@ class PdfWrapper(FormWrapper):
|
|
|
296
537
|
return self
|
|
297
538
|
|
|
298
539
|
def commit_widget_key_updates(self) -> PdfWrapper:
|
|
299
|
-
"""
|
|
540
|
+
"""Processes all deferred widget key updates in a single batch operation.
|
|
541
|
+
|
|
542
|
+
Applies all key updates that were queued using update_widget_key() with
|
|
543
|
+
defer=True. This is more efficient than individual updates when renaming
|
|
544
|
+
multiple fields.
|
|
545
|
+
|
|
546
|
+
Returns:
|
|
547
|
+
PdfWrapper: Returns self to allow method chaining
|
|
548
|
+
|
|
549
|
+
Raises:
|
|
550
|
+
NotImplementedError: When use_full_widget_name is enabled
|
|
551
|
+
"""
|
|
300
552
|
|
|
301
553
|
if getattr(self, "use_full_widget_name"):
|
|
302
554
|
raise NotImplementedError
|
|
@@ -321,7 +573,24 @@ class PdfWrapper(FormWrapper):
|
|
|
321
573
|
y: Union[float, int],
|
|
322
574
|
**kwargs,
|
|
323
575
|
) -> PdfWrapper:
|
|
324
|
-
"""Draws
|
|
576
|
+
"""Draws static text onto the PDF document at specified coordinates.
|
|
577
|
+
|
|
578
|
+
Adds non-interactive text that becomes part of the PDF content rather
|
|
579
|
+
than a form field. Useful for annotations, labels, signatures, etc.
|
|
580
|
+
|
|
581
|
+
Args:
|
|
582
|
+
text: The text content to draw (supports newlines with NEW_LINE_SYMBOL)
|
|
583
|
+
page_number: Page number (1-based) to draw text on
|
|
584
|
+
x: X coordinate for text position
|
|
585
|
+
y: Y coordinate for text position
|
|
586
|
+
**kwargs: Text formatting options:
|
|
587
|
+
font: Font name (default: "Helvetica")
|
|
588
|
+
font_size: Font size in points (default: 12)
|
|
589
|
+
font_color: Font color as RGB tuple (default: (0, 0, 0))
|
|
590
|
+
|
|
591
|
+
Returns:
|
|
592
|
+
PdfWrapper: Returns self to allow method chaining
|
|
593
|
+
"""
|
|
325
594
|
|
|
326
595
|
new_widget = Text("new")
|
|
327
596
|
new_widget.value = text
|
|
@@ -359,7 +628,25 @@ class PdfWrapper(FormWrapper):
|
|
|
359
628
|
height: Union[float, int],
|
|
360
629
|
rotation: Union[float, int] = 0,
|
|
361
630
|
) -> PdfWrapper:
|
|
362
|
-
"""Draws an image
|
|
631
|
+
"""Draws an image onto the PDF document at specified coordinates.
|
|
632
|
+
|
|
633
|
+
Supports common image formats (JPEG, PNG) from various sources:
|
|
634
|
+
- Raw image bytes
|
|
635
|
+
- File path
|
|
636
|
+
- File-like object
|
|
637
|
+
|
|
638
|
+
Args:
|
|
639
|
+
image: Image data as bytes, file path, or file object
|
|
640
|
+
page_number: Page number (1-based) to draw image on
|
|
641
|
+
x: X coordinate for image position (lower-left corner)
|
|
642
|
+
y: Y coordinate for image position (lower-left corner)
|
|
643
|
+
width: Width of the drawn image in PDF units
|
|
644
|
+
height: Height of the drawn image in PDF units
|
|
645
|
+
rotation: Rotation angle in degrees (default: 0)
|
|
646
|
+
|
|
647
|
+
Returns:
|
|
648
|
+
PdfWrapper: Returns self to allow method chaining
|
|
649
|
+
"""
|
|
363
650
|
|
|
364
651
|
image = fp_or_f_obj_or_stream_to_stream(image)
|
|
365
652
|
image = rotate_image(image, rotation)
|
|
@@ -373,7 +660,20 @@ class PdfWrapper(FormWrapper):
|
|
|
373
660
|
|
|
374
661
|
@property
|
|
375
662
|
def schema(self) -> dict:
|
|
376
|
-
"""Generates a
|
|
663
|
+
"""Generates a JSON schema describing the PDF form's fields and types.
|
|
664
|
+
|
|
665
|
+
The schema includes:
|
|
666
|
+
- Field names as property names
|
|
667
|
+
- Type information (string, boolean, integer)
|
|
668
|
+
- Field-specific constraints like max lengths for text fields
|
|
669
|
+
- Choice indices for dropdown fields
|
|
670
|
+
|
|
671
|
+
Note: Does not include required field indicators since the PDF form's
|
|
672
|
+
validation rules are not extracted.
|
|
673
|
+
|
|
674
|
+
Returns:
|
|
675
|
+
dict: A JSON Schema dictionary following Draft 7 format
|
|
676
|
+
"""
|
|
377
677
|
|
|
378
678
|
return {
|
|
379
679
|
"type": "object",
|
|
@@ -386,7 +686,18 @@ class PdfWrapper(FormWrapper):
|
|
|
386
686
|
def register_font(
|
|
387
687
|
cls, font_name: str, ttf_file: Union[bytes, str, BinaryIO]
|
|
388
688
|
) -> bool:
|
|
389
|
-
"""
|
|
689
|
+
"""Class method to register a TrueType font for use in PDF form text fields.
|
|
690
|
+
|
|
691
|
+
Registers the font globally so it can be used by all PdfWrapper instances.
|
|
692
|
+
The font will be available when specified by name in text operations.
|
|
693
|
+
|
|
694
|
+
Args:
|
|
695
|
+
font_name: Name to register the font under (used when setting font)
|
|
696
|
+
ttf_file: The TTF font data as bytes, file path, or file object
|
|
697
|
+
|
|
698
|
+
Returns:
|
|
699
|
+
bool: True if registration succeeded, False if failed
|
|
700
|
+
"""
|
|
390
701
|
|
|
391
702
|
ttf_file = fp_or_f_obj_or_stream_to_stream(ttf_file)
|
|
392
703
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
PyPDFForm/__init__.py,sha256=EyOnLO6Ibtooi4rjstapYKKP-L3qfTbNPy5CLJU_58c,328
|
|
2
|
+
PyPDFForm/adapter.py,sha256=_5fP5UR-NzjDDayJmBRO36DgbnbUzNbjZtHZtPvSM14,1909
|
|
3
|
+
PyPDFForm/constants.py,sha256=r4lR-lQ8KjKRLMowjxIv6k3wBPOxvt9-JAMugZwQmZM,2285
|
|
4
|
+
PyPDFForm/coordinate.py,sha256=AgVrBoUo6fLVTVdZrlVRf6tGwD-AX54ICMLfbvhYfRs,14879
|
|
5
|
+
PyPDFForm/filler.py,sha256=n21ryOZylrRaG21BKKzqIYh8BpJU6wf6Vk5Sq6xusyc,12984
|
|
6
|
+
PyPDFForm/font.py,sha256=eRbDyQFhXUkHzyZvCtru9Ypg_ukfbBAnSM5xNzPb5ss,7280
|
|
7
|
+
PyPDFForm/image.py,sha256=aYk7BC-AHiqt73durGIQ3e6gE5Ggbdr8jmkCUaQdsk8,1627
|
|
8
|
+
PyPDFForm/patterns.py,sha256=iChwqR-aZUKhEdnbQ8OEwnES2-NaMhBUy4dUrnuDPpA,9243
|
|
9
|
+
PyPDFForm/template.py,sha256=v1ZM52xHCzO8Xm7EXinbTepm2G7MU7StUgCFtuUdcbI,18899
|
|
10
|
+
PyPDFForm/utils.py,sha256=ubqTaItrs6pEYcza-12bxLUiFYe_sl-0SdeowD_BSG0,8444
|
|
11
|
+
PyPDFForm/watermark.py,sha256=yvlLuC_J2Py_-d-f00PpHpXoPb5sPrRuM2fn0l40icc,10473
|
|
12
|
+
PyPDFForm/wrapper.py,sha256=EPI0bjoRQwm5LBWx8FFolq4XG6ZaFibCRr9-ooawlCQ,24175
|
|
13
|
+
PyPDFForm/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
PyPDFForm/middleware/base.py,sha256=d4z7M7pm80176cC4H85m3ZRWzAu_Fm1HkwQkmSSi-WE,2832
|
|
15
|
+
PyPDFForm/middleware/checkbox.py,sha256=gRGhFySPoIpKBseoiOTS3WggoBBik12dXbZ-LJKzIwM,2611
|
|
16
|
+
PyPDFForm/middleware/dropdown.py,sha256=McuZl8Pc2IYPkEHRmM1OcEsh9njekySqjqVxRmQKmWU,1773
|
|
17
|
+
PyPDFForm/middleware/image.py,sha256=HlPUsIktj-NryIkwwdZlilvrd6sZYifs9IuDgTHp7uQ,950
|
|
18
|
+
PyPDFForm/middleware/radio.py,sha256=M4yqHYzHj0jvOGbjYdqeYnNAlYhTF-h47qxqrjXDOoU,1921
|
|
19
|
+
PyPDFForm/middleware/signature.py,sha256=0gexCQwHCEOrjrgvUXeJJCGo2plfSEbXlykPJJCqpfA,2380
|
|
20
|
+
PyPDFForm/middleware/text.py,sha256=eAxeVwboPPlnDT4aaOi7UpQ_xPmnlzkIbUOtRm1VMd0,2944
|
|
21
|
+
PyPDFForm/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
PyPDFForm/widgets/base.py,sha256=VrNN5vDmNW2t0c5Sr8y7ldS9t72_G97g8zywsY-bMaw,5410
|
|
23
|
+
PyPDFForm/widgets/checkbox.py,sha256=_1I5yh1211RgRUyWzd3NNYpI9JchqJNSJWaZAhl2uOo,1248
|
|
24
|
+
PyPDFForm/widgets/dropdown.py,sha256=zszIT5MI6ggBRUEn7oGBKK0pKmDC9LQw3RnqaKG8ocQ,1764
|
|
25
|
+
PyPDFForm/widgets/radio.py,sha256=ipadJyHbgftDUvjGk15kapzgHPN3HjdF_iB_7amXR6o,2737
|
|
26
|
+
PyPDFForm/widgets/text.py,sha256=HP2cPEUAzK5QL3kDfMz7gQcC3svCpmYuyFItBjlrBpI,1233
|
|
27
|
+
pypdfform-2.1.0.dist-info/licenses/LICENSE,sha256=43awmYkI6opyTpg19me731iO1WfXZwViqb67oWtCsFY,1065
|
|
28
|
+
pypdfform-2.1.0.dist-info/METADATA,sha256=_evTIudX2wlvxH1IAyTQmwpInVcQ3Hryc88d0fTWhGQ,5265
|
|
29
|
+
pypdfform-2.1.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
30
|
+
pypdfform-2.1.0.dist-info/top_level.txt,sha256=GQQKuWqPUjT9YZqwK95NlAQzxjwoQrsxQ8ureM8lWOY,10
|
|
31
|
+
pypdfform-2.1.0.dist-info/RECORD,,
|
pypdfform-2.0.0.dist-info/RECORD
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
PyPDFForm/__init__.py,sha256=CtFNtHaoeqe0pQK6LTIHuWlPop37g6DGqsfPmxnqyUA,178
|
|
2
|
-
PyPDFForm/adapter.py,sha256=qt0lqd7zRzaMHD2As47oiIhungO3AyWbAkxx1ZAZi9A,862
|
|
3
|
-
PyPDFForm/constants.py,sha256=2ti8lDF2K965jfom3l9jN_5cKxjEYyQNjr1VQlYvpNU,1869
|
|
4
|
-
PyPDFForm/coordinate.py,sha256=FtVkpmBYtBfwDLkr5o2VxifaWvEV8MKbRoTdLrcPHR0,9082
|
|
5
|
-
PyPDFForm/filler.py,sha256=oxSDZWsaXLI085W1ALEqBRuKS0tBbunoX1WzEztMYBk,9765
|
|
6
|
-
PyPDFForm/font.py,sha256=RHTZAYHfmIZedeNZwNlSg8aNUCyBsJA0t_hhHiV9GoQ,5566
|
|
7
|
-
PyPDFForm/image.py,sha256=IsmT2_LAvoGTxbsrelXnP8B4vUKetOVwp6oiE9MGnU8,835
|
|
8
|
-
PyPDFForm/patterns.py,sha256=X8lTlpn2FxHDKLgNy3O2PzKIcx9wF2FziI1JGC2Z-cg,5708
|
|
9
|
-
PyPDFForm/template.py,sha256=IQDLhFt7sVWNaY0SRRosWIk_nvMVFDex7N7RGZsQcvE,15197
|
|
10
|
-
PyPDFForm/utils.py,sha256=hdj-0nP3t2UBZnEUB74W_WMPxHNN2SpZ1MyMHar90bQ,6013
|
|
11
|
-
PyPDFForm/watermark.py,sha256=m3PXsNGJdW_DWKegHtQmWjCcDEbFiDPw_wyL_GNyKLI,5926
|
|
12
|
-
PyPDFForm/wrapper.py,sha256=zzf2VkfkviA51dKiVSkCHgCUwBC4yxADOpvb_VRLbtI,11909
|
|
13
|
-
PyPDFForm/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
PyPDFForm/middleware/base.py,sha256=cfCzpbFkvCxQlFwmZ8BV-HiJ_8YhXvbBp45kUWEudQs,1351
|
|
15
|
-
PyPDFForm/middleware/checkbox.py,sha256=GQPaKlPo6IX14N4tLLNf2hyJZBZmERs_9cWiwhvLg0Q,1376
|
|
16
|
-
PyPDFForm/middleware/dropdown.py,sha256=_RXs9o3vWH_a402zhTOYE_mcWbtFfVU7bReYcEFAIyk,793
|
|
17
|
-
PyPDFForm/middleware/image.py,sha256=tQrLKZUKOz90jKxuUQpTP4Awuvtf0I92LeANH0k6mzQ,212
|
|
18
|
-
PyPDFForm/middleware/radio.py,sha256=4Twv2uOW_GhVV7PHR3RFm4SDyGBEKqvL1cqj7i_Zi7Q,891
|
|
19
|
-
PyPDFForm/middleware/signature.py,sha256=1ZOkBKectZ7kptJMQ-EuOA_xI7j8dlwseo2p2Z0FS2o,1138
|
|
20
|
-
PyPDFForm/middleware/text.py,sha256=7HFHy_lfBFfYbSUzgVKovSoShbNP6L7tiILIw-9_kpo,1549
|
|
21
|
-
PyPDFForm/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
-
PyPDFForm/widgets/base.py,sha256=2v0y3vb5Dqy8G4oLm-xHkqi6GNWV1nnL8GEIe8DEU0c,3427
|
|
23
|
-
PyPDFForm/widgets/checkbox.py,sha256=MGB6NGI-XMBz4j2erykgYOCd1pswvthuQ6UFowQOd4o,503
|
|
24
|
-
PyPDFForm/widgets/dropdown.py,sha256=2_R5xcLUWAL0G4raVgWWtE7TJdiWJITay-Gtl8YI2QI,705
|
|
25
|
-
PyPDFForm/widgets/text.py,sha256=sCB8CQAjh30QOGr8FTIDSk3X6dXSHJztOz6YkKEBDss,691
|
|
26
|
-
pypdfform-2.0.0.dist-info/licenses/LICENSE,sha256=43awmYkI6opyTpg19me731iO1WfXZwViqb67oWtCsFY,1065
|
|
27
|
-
pypdfform-2.0.0.dist-info/METADATA,sha256=rUiD5ajgngBZ8crGRl488hnleb0ah4HOgytdKk6vMs4,5265
|
|
28
|
-
pypdfform-2.0.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
29
|
-
pypdfform-2.0.0.dist-info/top_level.txt,sha256=GQQKuWqPUjT9YZqwK95NlAQzxjwoQrsxQ8ureM8lWOY,10
|
|
30
|
-
pypdfform-2.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|