PyPDFForm 2.2.3__py3-none-any.whl → 2.2.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of PyPDFForm might be problematic. Click here for more details.
- PyPDFForm/__init__.py +1 -1
- PyPDFForm/constants.py +1 -1
- PyPDFForm/coordinate.py +76 -32
- PyPDFForm/filler.py +26 -21
- PyPDFForm/utils.py +2 -0
- PyPDFForm/watermark.py +111 -105
- PyPDFForm/widgets/base.py +1 -1
- PyPDFForm/widgets/signature.py +1 -1
- PyPDFForm/wrapper.py +15 -11
- {pypdfform-2.2.3.dist-info → pypdfform-2.2.5.dist-info}/METADATA +1 -1
- {pypdfform-2.2.3.dist-info → pypdfform-2.2.5.dist-info}/RECORD +14 -14
- {pypdfform-2.2.3.dist-info → pypdfform-2.2.5.dist-info}/WHEEL +1 -1
- {pypdfform-2.2.3.dist-info → pypdfform-2.2.5.dist-info}/licenses/LICENSE +0 -0
- {pypdfform-2.2.3.dist-info → pypdfform-2.2.5.dist-info}/top_level.txt +0 -0
PyPDFForm/__init__.py
CHANGED
PyPDFForm/constants.py
CHANGED
PyPDFForm/coordinate.py
CHANGED
|
@@ -28,25 +28,51 @@ from .utils import extract_widget_property, handle_color, stream_to_io
|
|
|
28
28
|
from .watermark import create_watermarks_and_draw, merge_watermarks_with_pdf
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def get_draw_border_coordinates(widget: dict, shape: str) ->
|
|
32
|
-
"""Calculates coordinates for drawing widget borders.
|
|
31
|
+
def get_draw_border_coordinates(widget: dict, shape: str) -> dict:
|
|
32
|
+
"""Calculates coordinates for drawing widget borders in PDF coordinate space.
|
|
33
33
|
|
|
34
34
|
Args:
|
|
35
|
-
widget: PDF form widget dictionary containing Rect coordinates
|
|
36
|
-
shape: Type of border to draw
|
|
35
|
+
widget: PDF form widget dictionary containing Rect coordinates (in PDF points)
|
|
36
|
+
shape: Type of border to draw:
|
|
37
|
+
- "rect": Standard rectangular border
|
|
38
|
+
- "ellipse": Circular/oval border
|
|
39
|
+
- "line": Straight line border
|
|
37
40
|
|
|
38
41
|
Returns:
|
|
39
|
-
|
|
40
|
-
For
|
|
41
|
-
|
|
42
|
+
dict: Coordinate dictionary with different keys depending on shape:
|
|
43
|
+
- For "rect":
|
|
44
|
+
{
|
|
45
|
+
"x": bottom-left x,
|
|
46
|
+
"y": bottom-left y,
|
|
47
|
+
"width": total width,
|
|
48
|
+
"height": total height
|
|
49
|
+
}
|
|
50
|
+
- For "ellipse":
|
|
51
|
+
{
|
|
52
|
+
"x1": left bound,
|
|
53
|
+
"y1": bottom bound,
|
|
54
|
+
"x2": right bound,
|
|
55
|
+
"y2": top bound
|
|
56
|
+
}
|
|
57
|
+
- For "line":
|
|
58
|
+
{
|
|
59
|
+
"src_x": start x,
|
|
60
|
+
"src_y": start y,
|
|
61
|
+
"dest_x": end x,
|
|
62
|
+
"dest_y": end y
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Note:
|
|
66
|
+
All coordinates are in PDF points (1/72 inch) with origin (0,0) at bottom-left.
|
|
67
|
+
For ellipses, the bounds form a square that would contain the ellipse.
|
|
42
68
|
"""
|
|
43
69
|
|
|
44
|
-
result =
|
|
45
|
-
float(widget[Rect][0]),
|
|
46
|
-
float(widget[Rect][1]),
|
|
47
|
-
abs(float(widget[Rect][0]) - float(widget[Rect][2])),
|
|
48
|
-
abs(float(widget[Rect][1]) - float(widget[Rect][3])),
|
|
49
|
-
|
|
70
|
+
result = {
|
|
71
|
+
"x": float(widget[Rect][0]),
|
|
72
|
+
"y": float(widget[Rect][1]),
|
|
73
|
+
"width": abs(float(widget[Rect][0]) - float(widget[Rect][2])),
|
|
74
|
+
"height": abs(float(widget[Rect][1]) - float(widget[Rect][3])),
|
|
75
|
+
}
|
|
50
76
|
|
|
51
77
|
if shape == "ellipse":
|
|
52
78
|
width = abs(float(widget[Rect][0]) - float(widget[Rect][2]))
|
|
@@ -57,19 +83,19 @@ def get_draw_border_coordinates(widget: dict, shape: str) -> List[float]:
|
|
|
57
83
|
|
|
58
84
|
less = min(width, height)
|
|
59
85
|
|
|
60
|
-
result =
|
|
61
|
-
width_mid - less / 2,
|
|
62
|
-
height_mid - less / 2,
|
|
63
|
-
width_mid + less / 2,
|
|
64
|
-
height_mid + less / 2,
|
|
65
|
-
|
|
86
|
+
result = {
|
|
87
|
+
"x1": width_mid - less / 2,
|
|
88
|
+
"y1": height_mid - less / 2,
|
|
89
|
+
"x2": width_mid + less / 2,
|
|
90
|
+
"y2": height_mid + less / 2,
|
|
91
|
+
}
|
|
66
92
|
elif shape == "line":
|
|
67
|
-
result =
|
|
68
|
-
float(widget[Rect][0]),
|
|
69
|
-
float(widget[Rect][1]),
|
|
70
|
-
float(widget[Rect][2]),
|
|
71
|
-
float(widget[Rect][1]),
|
|
72
|
-
|
|
93
|
+
result = {
|
|
94
|
+
"src_x": float(widget[Rect][0]),
|
|
95
|
+
"src_y": float(widget[Rect][1]),
|
|
96
|
+
"dest_x": float(widget[Rect][2]),
|
|
97
|
+
"dest_y": float(widget[Rect][1]),
|
|
98
|
+
}
|
|
73
99
|
|
|
74
100
|
return result
|
|
75
101
|
|
|
@@ -397,14 +423,32 @@ def generate_coordinate_grid(
|
|
|
397
423
|
current = margin
|
|
398
424
|
while current < width:
|
|
399
425
|
lines_by_page[i + 1].append(
|
|
400
|
-
|
|
426
|
+
{
|
|
427
|
+
"src_x": current,
|
|
428
|
+
"src_y": 0,
|
|
429
|
+
"dest_x": current,
|
|
430
|
+
"dest_y": height,
|
|
431
|
+
"border_color": handle_color([r, g, b]),
|
|
432
|
+
"background_color": None,
|
|
433
|
+
"border_width": 1,
|
|
434
|
+
"dash_array": None,
|
|
435
|
+
}
|
|
401
436
|
)
|
|
402
437
|
current += margin
|
|
403
438
|
|
|
404
439
|
current = margin
|
|
405
440
|
while current < height:
|
|
406
441
|
lines_by_page[i + 1].append(
|
|
407
|
-
|
|
442
|
+
{
|
|
443
|
+
"src_x": 0,
|
|
444
|
+
"src_y": current,
|
|
445
|
+
"dest_x": width,
|
|
446
|
+
"dest_y": current,
|
|
447
|
+
"border_color": handle_color([r, g, b]),
|
|
448
|
+
"background_color": None,
|
|
449
|
+
"border_width": 1,
|
|
450
|
+
"dash_array": None,
|
|
451
|
+
}
|
|
408
452
|
)
|
|
409
453
|
current += margin
|
|
410
454
|
|
|
@@ -419,11 +463,11 @@ def generate_coordinate_grid(
|
|
|
419
463
|
text.font_size = font_size
|
|
420
464
|
text.font_color = color
|
|
421
465
|
texts_by_page[i + 1].append(
|
|
422
|
-
|
|
423
|
-
text,
|
|
424
|
-
x - stringWidth(value, DEFAULT_FONT, font_size),
|
|
425
|
-
y - font_size,
|
|
426
|
-
|
|
466
|
+
{
|
|
467
|
+
"widget": text,
|
|
468
|
+
"x": x - stringWidth(value, DEFAULT_FONT, font_size),
|
|
469
|
+
"y": y - font_size,
|
|
470
|
+
}
|
|
427
471
|
)
|
|
428
472
|
y += margin
|
|
429
473
|
x += margin
|
PyPDFForm/filler.py
CHANGED
|
@@ -104,13 +104,13 @@ def signature_image_handler(
|
|
|
104
104
|
widget, middleware.preserve_aspect_ratio, image_width, image_height
|
|
105
105
|
)
|
|
106
106
|
images_to_draw.append(
|
|
107
|
-
|
|
108
|
-
stream,
|
|
109
|
-
x,
|
|
110
|
-
y,
|
|
111
|
-
width,
|
|
112
|
-
height,
|
|
113
|
-
|
|
107
|
+
{
|
|
108
|
+
"stream": stream,
|
|
109
|
+
"x": x,
|
|
110
|
+
"y": y,
|
|
111
|
+
"width": width,
|
|
112
|
+
"height": height,
|
|
113
|
+
}
|
|
114
114
|
)
|
|
115
115
|
|
|
116
116
|
return any_image_to_draw
|
|
@@ -172,19 +172,24 @@ def border_handler(
|
|
|
172
172
|
shape = "rect"
|
|
173
173
|
|
|
174
174
|
list_to_append.append(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
middleware.border_color,
|
|
178
|
-
middleware.background_color,
|
|
179
|
-
middleware.border_width,
|
|
180
|
-
middleware.dash_array,
|
|
181
|
-
|
|
175
|
+
{
|
|
176
|
+
**get_draw_border_coordinates(widget, shape),
|
|
177
|
+
"border_color": middleware.border_color,
|
|
178
|
+
"background_color": middleware.background_color,
|
|
179
|
+
"border_width": middleware.border_width,
|
|
180
|
+
"dash_array": middleware.dash_array,
|
|
181
|
+
}
|
|
182
182
|
)
|
|
183
183
|
|
|
184
184
|
if shape == "line":
|
|
185
185
|
rect_borders_to_draw.append(
|
|
186
|
-
|
|
187
|
-
|
|
186
|
+
{
|
|
187
|
+
**get_draw_border_coordinates(widget, "rect"),
|
|
188
|
+
"border_color": None,
|
|
189
|
+
"background_color": middleware.background_color,
|
|
190
|
+
"border_width": 0,
|
|
191
|
+
"dash_array": None,
|
|
192
|
+
}
|
|
188
193
|
)
|
|
189
194
|
|
|
190
195
|
|
|
@@ -281,11 +286,11 @@ def fill(
|
|
|
281
286
|
]
|
|
282
287
|
):
|
|
283
288
|
texts_to_draw[page].append(
|
|
284
|
-
|
|
285
|
-
to_draw,
|
|
286
|
-
x,
|
|
287
|
-
y,
|
|
288
|
-
|
|
289
|
+
{
|
|
290
|
+
"widget": to_draw,
|
|
291
|
+
"x": x,
|
|
292
|
+
"y": y,
|
|
293
|
+
}
|
|
289
294
|
)
|
|
290
295
|
|
|
291
296
|
result = template_stream
|
PyPDFForm/utils.py
CHANGED
|
@@ -11,6 +11,7 @@ This module contains general-purpose utilities used throughout PyPDFForm:
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
from collections.abc import Callable
|
|
14
|
+
from functools import lru_cache
|
|
14
15
|
from io import BytesIO
|
|
15
16
|
from secrets import choice
|
|
16
17
|
from string import ascii_letters, digits, punctuation
|
|
@@ -29,6 +30,7 @@ from .middleware.radio import Radio
|
|
|
29
30
|
from .middleware.text import Text
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
@lru_cache
|
|
32
34
|
def stream_to_io(stream: bytes) -> BinaryIO:
|
|
33
35
|
"""Converts a byte stream to a seekable binary IO object.
|
|
34
36
|
|
PyPDFForm/watermark.py
CHANGED
|
@@ -22,7 +22,7 @@ from .patterns import WIDGET_KEY_PATTERNS
|
|
|
22
22
|
from .utils import extract_widget_property, stream_to_io
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def draw_text(
|
|
25
|
+
def draw_text(canvas: Canvas, **kwargs) -> None:
|
|
26
26
|
"""Draws text onto a watermark canvas with proper formatting.
|
|
27
27
|
|
|
28
28
|
Handles:
|
|
@@ -32,16 +32,16 @@ def draw_text(*args) -> None:
|
|
|
32
32
|
- Text alignment
|
|
33
33
|
|
|
34
34
|
Args:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
canvas: Canvas object to draw on
|
|
36
|
+
**kwargs: Additional arguments including:
|
|
37
|
+
widget: Text widget with content and properties
|
|
38
|
+
x: X coordinate for drawing
|
|
39
|
+
y: Y coordinate for drawing
|
|
39
40
|
"""
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
coordinate_y = args[3]
|
|
42
|
+
widget = kwargs["widget"]
|
|
43
|
+
coordinate_x = kwargs["x"]
|
|
44
|
+
coordinate_y = kwargs["y"]
|
|
45
45
|
|
|
46
46
|
text_to_draw = widget.value
|
|
47
47
|
|
|
@@ -96,106 +96,105 @@ def draw_text(*args) -> None:
|
|
|
96
96
|
canvas.restoreState()
|
|
97
97
|
|
|
98
98
|
|
|
99
|
-
def draw_rect(
|
|
99
|
+
def draw_rect(canvas: Canvas, **kwargs) -> None:
|
|
100
100
|
"""Draws a rectangle onto a watermark canvas.
|
|
101
101
|
|
|
102
102
|
Args:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
103
|
+
canvas: Canvas object to draw on
|
|
104
|
+
**kwargs: Additional arguments including:
|
|
105
|
+
x: X coordinate of bottom-left corner
|
|
106
|
+
y: Y coordinate of bottom-left corner
|
|
107
|
+
width: Width of rectangle
|
|
108
|
+
height: Height of rectangle
|
|
109
|
+
border_color: Border color
|
|
110
|
+
background_color: Background color
|
|
111
|
+
border_width: Border width
|
|
112
|
+
dash_array: Dash pattern for border
|
|
112
113
|
"""
|
|
113
114
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
height = args[4]
|
|
115
|
+
x = kwargs["x"]
|
|
116
|
+
y = kwargs["y"]
|
|
117
|
+
width = kwargs["width"]
|
|
118
|
+
height = kwargs["height"]
|
|
119
119
|
|
|
120
120
|
canvas.saveState()
|
|
121
|
-
stroke, fill = set_border_and_background_styles(
|
|
121
|
+
stroke, fill = set_border_and_background_styles(canvas, **kwargs)
|
|
122
122
|
canvas.rect(x, y, width, height, stroke=stroke, fill=fill)
|
|
123
123
|
canvas.restoreState()
|
|
124
124
|
|
|
125
125
|
|
|
126
|
-
def draw_ellipse(
|
|
126
|
+
def draw_ellipse(canvas: Canvas, **kwargs) -> None:
|
|
127
127
|
"""Draws an ellipse onto a watermark canvas.
|
|
128
128
|
|
|
129
129
|
Args:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
130
|
+
canvas: Canvas object to draw on
|
|
131
|
+
**kwargs: Additional arguments including:
|
|
132
|
+
x1: X coordinate of first bounding point
|
|
133
|
+
y1: Y coordinate of first bounding point
|
|
134
|
+
x2: X coordinate of second bounding point
|
|
135
|
+
y2: Y coordinate of second bounding point
|
|
136
|
+
border_color: Border color
|
|
137
|
+
background_color: Background color
|
|
138
|
+
border_width: Border width
|
|
139
|
+
dash_array: Dash pattern for border
|
|
139
140
|
"""
|
|
140
141
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
y2 = args[4]
|
|
142
|
+
x1 = kwargs["x1"]
|
|
143
|
+
y1 = kwargs["y1"]
|
|
144
|
+
x2 = kwargs["x2"]
|
|
145
|
+
y2 = kwargs["y2"]
|
|
146
146
|
|
|
147
147
|
canvas.saveState()
|
|
148
|
-
stroke, fill = set_border_and_background_styles(
|
|
148
|
+
stroke, fill = set_border_and_background_styles(canvas, **kwargs)
|
|
149
149
|
canvas.ellipse(x1, y1, x2, y2, stroke=stroke, fill=fill)
|
|
150
150
|
canvas.restoreState()
|
|
151
151
|
|
|
152
152
|
|
|
153
|
-
def draw_line(
|
|
153
|
+
def draw_line(canvas: Canvas, **kwargs) -> None:
|
|
154
154
|
"""Draws a line onto a watermark canvas.
|
|
155
155
|
|
|
156
156
|
Args:
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
157
|
+
canvas: Canvas object to draw on
|
|
158
|
+
**kwargs: Additional arguments including:
|
|
159
|
+
src_x: X coordinate of start point
|
|
160
|
+
src_y: Y coordinate of start point
|
|
161
|
+
dest_x: X coordinate of end point
|
|
162
|
+
dest_y: Y coordinate of end point
|
|
163
|
+
border_color: Line color
|
|
164
|
+
border_width: Line width
|
|
165
|
+
dash_array: Dash pattern for line
|
|
166
166
|
"""
|
|
167
167
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
dest_y = args[4]
|
|
168
|
+
src_x = kwargs["src_x"]
|
|
169
|
+
src_y = kwargs["src_y"]
|
|
170
|
+
dest_x = kwargs["dest_x"]
|
|
171
|
+
dest_y = kwargs["dest_y"]
|
|
173
172
|
|
|
174
173
|
canvas.saveState()
|
|
175
|
-
set_border_and_background_styles(
|
|
174
|
+
set_border_and_background_styles(canvas, **kwargs)
|
|
176
175
|
canvas.line(src_x, src_y, dest_x, dest_y)
|
|
177
176
|
canvas.restoreState()
|
|
178
177
|
|
|
179
178
|
|
|
180
|
-
def set_border_and_background_styles(
|
|
179
|
+
def set_border_and_background_styles(canvas: Canvas, **kwargs) -> tuple:
|
|
181
180
|
"""Configures stroke and fill styles for drawing operations.
|
|
182
181
|
|
|
183
182
|
Args:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
183
|
+
canvas: Canvas object to configure
|
|
184
|
+
**kwargs: Additional arguments including:
|
|
185
|
+
border_color: Border color
|
|
186
|
+
background_color: Background color
|
|
187
|
+
border_width: Border width
|
|
188
|
+
dash_array: Dash pattern for border
|
|
189
189
|
|
|
190
190
|
Returns:
|
|
191
191
|
tuple: (stroke_flag, fill_flag) indicating which styles were set
|
|
192
192
|
"""
|
|
193
193
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
dash_array = args[8]
|
|
194
|
+
border_color = kwargs["border_color"]
|
|
195
|
+
background_color = kwargs["background_color"]
|
|
196
|
+
border_width = kwargs["border_width"]
|
|
197
|
+
dash_array = kwargs["dash_array"]
|
|
199
198
|
|
|
200
199
|
stroke = 0
|
|
201
200
|
fill = 0
|
|
@@ -213,24 +212,24 @@ def set_border_and_background_styles(*args) -> tuple:
|
|
|
213
212
|
return stroke, fill
|
|
214
213
|
|
|
215
214
|
|
|
216
|
-
def draw_image(
|
|
215
|
+
def draw_image(canvas: Canvas, **kwargs) -> None:
|
|
217
216
|
"""Draws an image onto a watermark canvas.
|
|
218
217
|
|
|
219
218
|
Args:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
219
|
+
canvas: Canvas object to draw on
|
|
220
|
+
**kwargs: Additional arguments including:
|
|
221
|
+
stream: Image data as bytes
|
|
222
|
+
x: X coordinate for drawing
|
|
223
|
+
y: Y coordinate for drawing
|
|
224
|
+
width: Width of drawn image
|
|
225
|
+
height: Height of drawn image
|
|
226
226
|
"""
|
|
227
227
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
height = args[5]
|
|
228
|
+
image_stream = kwargs["stream"]
|
|
229
|
+
coordinate_x = kwargs["x"]
|
|
230
|
+
coordinate_y = kwargs["y"]
|
|
231
|
+
width = kwargs["width"]
|
|
232
|
+
height = kwargs["height"]
|
|
234
233
|
|
|
235
234
|
image_buff = BytesIO()
|
|
236
235
|
image_buff.write(image_stream)
|
|
@@ -252,7 +251,7 @@ def create_watermarks_and_draw(
|
|
|
252
251
|
pdf: bytes,
|
|
253
252
|
page_number: int,
|
|
254
253
|
action_type: str,
|
|
255
|
-
actions: List[
|
|
254
|
+
actions: List[dict],
|
|
256
255
|
) -> List[bytes]:
|
|
257
256
|
"""Creates watermarks for each page with specified drawing operations.
|
|
258
257
|
|
|
@@ -277,21 +276,17 @@ def create_watermarks_and_draw(
|
|
|
277
276
|
),
|
|
278
277
|
)
|
|
279
278
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
elif action_type == "rect":
|
|
290
|
-
for each in actions:
|
|
291
|
-
draw_rect(*([canvas, *each]))
|
|
292
|
-
elif action_type == "ellipse":
|
|
279
|
+
action_type_to_func = {
|
|
280
|
+
"image": draw_image,
|
|
281
|
+
"text": draw_text,
|
|
282
|
+
"line": draw_line,
|
|
283
|
+
"rect": draw_rect,
|
|
284
|
+
"ellipse": draw_ellipse,
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if action_type_to_func.get(action_type):
|
|
293
288
|
for each in actions:
|
|
294
|
-
|
|
289
|
+
action_type_to_func[action_type](canvas, **each)
|
|
295
290
|
|
|
296
291
|
canvas.save()
|
|
297
292
|
buff.seek(0)
|
|
@@ -335,22 +330,28 @@ def merge_watermarks_with_pdf(
|
|
|
335
330
|
|
|
336
331
|
|
|
337
332
|
def copy_watermark_widgets(
|
|
338
|
-
pdf: bytes,
|
|
333
|
+
pdf: bytes,
|
|
334
|
+
watermarks: Union[List[bytes], bytes],
|
|
335
|
+
keys: Union[List[str], None],
|
|
336
|
+
page_num: Union[int, None],
|
|
339
337
|
) -> bytes:
|
|
340
|
-
"""
|
|
341
|
-
Copies annotation widgets (form fields) from watermark PDFs onto the corresponding pages of a base PDF,
|
|
342
|
-
including only those widgets whose key matches an entry in the provided keys list.
|
|
338
|
+
"""Copies annotation widgets (form fields) from watermark PDFs onto the corresponding pages of a base PDF.
|
|
343
339
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
340
|
+
This function selectively copies annotation widgets (form fields) from watermark PDFs to a base PDF.
|
|
341
|
+
It allows specifying which widgets to copy based on their keys, and optionally restricts the operation
|
|
342
|
+
to a specific page number.
|
|
343
|
+
|
|
344
|
+
The function can handle either a list of watermarks (one per page) or a single watermark PDF applied to all pages.
|
|
345
|
+
Widgets are only copied if their key is present in the provided keys list.
|
|
347
346
|
|
|
348
347
|
Args:
|
|
349
348
|
pdf: The original PDF document as bytes.
|
|
350
|
-
watermarks: Either a list of watermark PDF data (as bytes, one per page) or a single watermark PDF.
|
|
349
|
+
watermarks: Either a list of watermark PDF data (as bytes, one per page) or a single watermark PDF as bytes.
|
|
351
350
|
Empty or None entries are skipped.
|
|
352
351
|
keys: List of widget keys (str). Only widgets whose key is in this list will be copied.
|
|
353
352
|
If None, all widgets will be copied.
|
|
353
|
+
page_num: Optional page number (0-based) to restrict widget copying to. If None, widgets are copied
|
|
354
|
+
across all pages.
|
|
354
355
|
|
|
355
356
|
Returns:
|
|
356
357
|
bytes: The resulting PDF document with selected annotation widgets from watermarks copied onto their respective pages.
|
|
@@ -368,6 +369,9 @@ def copy_watermark_widgets(
|
|
|
368
369
|
watermarks = [watermarks]
|
|
369
370
|
widgets_to_copy = widgets_to_copy_pdf
|
|
370
371
|
|
|
372
|
+
if page_num is not None:
|
|
373
|
+
widgets_to_copy = widgets_to_copy_watermarks
|
|
374
|
+
|
|
371
375
|
for i, watermark in enumerate(watermarks):
|
|
372
376
|
if not watermark:
|
|
373
377
|
continue
|
|
@@ -380,7 +384,9 @@ def copy_watermark_widgets(
|
|
|
380
384
|
key = extract_widget_property(
|
|
381
385
|
annot.get_object(), WIDGET_KEY_PATTERNS, None, str
|
|
382
386
|
)
|
|
383
|
-
if keys is None or key in keys
|
|
387
|
+
if (keys is None or key in keys) and (
|
|
388
|
+
page_num is None or page_num == j
|
|
389
|
+
):
|
|
384
390
|
widgets_to_copy_watermarks[i].append(annot.clone(out))
|
|
385
391
|
widgets_to_copy_pdf[j].append(annot.clone(out))
|
|
386
392
|
|
PyPDFForm/widgets/base.py
CHANGED
|
@@ -83,7 +83,7 @@ class Widget:
|
|
|
83
83
|
)
|
|
84
84
|
self.acro_form_params[param] = value
|
|
85
85
|
elif user_input in self.NONE_DEFAULTS:
|
|
86
|
-
self.acro_form_params[param] = None
|
|
86
|
+
self.acro_form_params[param] = None # noqa
|
|
87
87
|
|
|
88
88
|
for each in self.ALLOWED_NON_ACRO_FORM_PARAMS:
|
|
89
89
|
if each in kwargs:
|
PyPDFForm/widgets/signature.py
CHANGED
|
@@ -101,7 +101,7 @@ class SignatureWidget:
|
|
|
101
101
|
|
|
102
102
|
input_pdf = PdfReader(stream_to_io(stream))
|
|
103
103
|
page_count = len(input_pdf.pages)
|
|
104
|
-
pdf = PdfReader(stream_to_io(BEDROCK_PDF))
|
|
104
|
+
pdf = PdfReader(stream_to_io(BEDROCK_PDF)) # noqa
|
|
105
105
|
out = PdfWriter()
|
|
106
106
|
out.append(pdf)
|
|
107
107
|
|
PyPDFForm/wrapper.py
CHANGED
|
@@ -288,9 +288,10 @@ class PdfWrapper(FormWrapper):
|
|
|
288
288
|
|
|
289
289
|
return [
|
|
290
290
|
self.__class__(
|
|
291
|
-
each,
|
|
291
|
+
copy_watermark_widgets(each, self.stream, None, i),
|
|
292
|
+
**{param: getattr(self, param) for param, _ in self.USER_PARAMS},
|
|
292
293
|
)
|
|
293
|
-
for each in get_page_streams(self.
|
|
294
|
+
for i, each in enumerate(get_page_streams(remove_all_widgets(self.read())))
|
|
294
295
|
]
|
|
295
296
|
|
|
296
297
|
def change_version(self, version: str) -> PdfWrapper:
|
|
@@ -497,7 +498,7 @@ class PdfWrapper(FormWrapper):
|
|
|
497
498
|
obj = _class(name=name, page_number=page_number, x=x, y=y, **kwargs)
|
|
498
499
|
watermarks = obj.watermarks(self.read())
|
|
499
500
|
|
|
500
|
-
self.stream = copy_watermark_widgets(self.read(), watermarks, [name])
|
|
501
|
+
self.stream = copy_watermark_widgets(self.read(), watermarks, [name], None)
|
|
501
502
|
if obj.non_acro_form_params:
|
|
502
503
|
self.stream = handle_non_acro_form_params(
|
|
503
504
|
self.stream, name, obj.non_acro_form_params
|
|
@@ -619,18 +620,18 @@ class PdfWrapper(FormWrapper):
|
|
|
619
620
|
page_number,
|
|
620
621
|
"text",
|
|
621
622
|
[
|
|
622
|
-
|
|
623
|
-
new_widget,
|
|
624
|
-
x,
|
|
625
|
-
y,
|
|
626
|
-
|
|
623
|
+
{
|
|
624
|
+
"widget": new_widget,
|
|
625
|
+
"x": x,
|
|
626
|
+
"y": y,
|
|
627
|
+
}
|
|
627
628
|
],
|
|
628
629
|
)
|
|
629
630
|
|
|
630
631
|
stream_with_widgets = self.read()
|
|
631
632
|
self.stream = merge_watermarks_with_pdf(self.stream, watermarks)
|
|
632
633
|
self.stream = copy_watermark_widgets(
|
|
633
|
-
remove_all_widgets(self.stream), stream_with_widgets, None
|
|
634
|
+
remove_all_widgets(self.stream), stream_with_widgets, None, None
|
|
634
635
|
)
|
|
635
636
|
|
|
636
637
|
return self
|
|
@@ -666,13 +667,16 @@ class PdfWrapper(FormWrapper):
|
|
|
666
667
|
image = fp_or_f_obj_or_stream_to_stream(image)
|
|
667
668
|
image = rotate_image(image, rotation)
|
|
668
669
|
watermarks = create_watermarks_and_draw(
|
|
669
|
-
self.stream,
|
|
670
|
+
self.stream,
|
|
671
|
+
page_number,
|
|
672
|
+
"image",
|
|
673
|
+
[{"stream": image, "x": x, "y": y, "width": width, "height": height}],
|
|
670
674
|
)
|
|
671
675
|
|
|
672
676
|
stream_with_widgets = self.read()
|
|
673
677
|
self.stream = merge_watermarks_with_pdf(self.stream, watermarks)
|
|
674
678
|
self.stream = copy_watermark_widgets(
|
|
675
|
-
remove_all_widgets(self.stream), stream_with_widgets, None
|
|
679
|
+
remove_all_widgets(self.stream), stream_with_widgets, None, None
|
|
676
680
|
)
|
|
677
681
|
|
|
678
682
|
return self
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
PyPDFForm/__init__.py,sha256=
|
|
1
|
+
PyPDFForm/__init__.py,sha256=Ulyaw2X0oRHS1yjzGumWDaCeTzvMeqy0nBfrWQkXDF0,328
|
|
2
2
|
PyPDFForm/adapter.py,sha256=_5fP5UR-NzjDDayJmBRO36DgbnbUzNbjZtHZtPvSM14,1909
|
|
3
|
-
PyPDFForm/constants.py,sha256=
|
|
4
|
-
PyPDFForm/coordinate.py,sha256=
|
|
5
|
-
PyPDFForm/filler.py,sha256=
|
|
3
|
+
PyPDFForm/constants.py,sha256=C83k7Hz0LFzYGy--qGG7rfEQrtXfZCgBBnWMhe7L73A,2300
|
|
4
|
+
PyPDFForm/coordinate.py,sha256=gQI7z-GsdCO33Qny5kXLBs6Y2TW5KO_mJ2in64fvXcg,16412
|
|
5
|
+
PyPDFForm/filler.py,sha256=K-8JFhS4I7aiK8ZRKtovJ9QsUFgM2DjQ1n6B0P6qyaU,13264
|
|
6
6
|
PyPDFForm/font.py,sha256=eRbDyQFhXUkHzyZvCtru9Ypg_ukfbBAnSM5xNzPb5ss,7280
|
|
7
7
|
PyPDFForm/image.py,sha256=aYk7BC-AHiqt73durGIQ3e6gE5Ggbdr8jmkCUaQdsk8,1627
|
|
8
8
|
PyPDFForm/patterns.py,sha256=iChwqR-aZUKhEdnbQ8OEwnES2-NaMhBUy4dUrnuDPpA,9243
|
|
9
9
|
PyPDFForm/template.py,sha256=v1ZM52xHCzO8Xm7EXinbTepm2G7MU7StUgCFtuUdcbI,18899
|
|
10
|
-
PyPDFForm/utils.py,sha256=
|
|
11
|
-
PyPDFForm/watermark.py,sha256=
|
|
12
|
-
PyPDFForm/wrapper.py,sha256=
|
|
10
|
+
PyPDFForm/utils.py,sha256=M07Q_2defFGmhBqvgEoQ9pt_IYZwXCsqrMljlKYYwIM,8487
|
|
11
|
+
PyPDFForm/watermark.py,sha256=Ni6Y2-DSaAaxqXtCSTrmDOMO0XW7-u3Hucr6J4njtG8,12475
|
|
12
|
+
PyPDFForm/wrapper.py,sha256=mCoJtYHM-OSogXKiQTA8XHlD0dnWwonPYcou4zMdtzg,25359
|
|
13
13
|
PyPDFForm/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
PyPDFForm/middleware/base.py,sha256=d4z7M7pm80176cC4H85m3ZRWzAu_Fm1HkwQkmSSi-WE,2832
|
|
15
15
|
PyPDFForm/middleware/checkbox.py,sha256=gRGhFySPoIpKBseoiOTS3WggoBBik12dXbZ-LJKzIwM,2611
|
|
@@ -19,16 +19,16 @@ PyPDFForm/middleware/radio.py,sha256=M4yqHYzHj0jvOGbjYdqeYnNAlYhTF-h47qxqrjXDOoU
|
|
|
19
19
|
PyPDFForm/middleware/signature.py,sha256=0gexCQwHCEOrjrgvUXeJJCGo2plfSEbXlykPJJCqpfA,2380
|
|
20
20
|
PyPDFForm/middleware/text.py,sha256=eAxeVwboPPlnDT4aaOi7UpQ_xPmnlzkIbUOtRm1VMd0,2944
|
|
21
21
|
PyPDFForm/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
-
PyPDFForm/widgets/base.py,sha256=
|
|
22
|
+
PyPDFForm/widgets/base.py,sha256=Rv4_fxpq-qDFFGLVGUlbxNmhllNEIhU_P5m0kJFFYiM,5418
|
|
23
23
|
PyPDFForm/widgets/bedrock.py,sha256=j6beU04kaQzpAIFZHI5VJLaDT5RVAAa6LzkU1luJpN8,137660
|
|
24
24
|
PyPDFForm/widgets/checkbox.py,sha256=_1I5yh1211RgRUyWzd3NNYpI9JchqJNSJWaZAhl2uOo,1248
|
|
25
25
|
PyPDFForm/widgets/dropdown.py,sha256=zszIT5MI6ggBRUEn7oGBKK0pKmDC9LQw3RnqaKG8ocQ,1764
|
|
26
26
|
PyPDFForm/widgets/image.py,sha256=6y8Ysmk49USr_qWOXD6KGL6cch516cUIlrxoj0pJy9Q,797
|
|
27
27
|
PyPDFForm/widgets/radio.py,sha256=ipadJyHbgftDUvjGk15kapzgHPN3HjdF_iB_7amXR6o,2737
|
|
28
|
-
PyPDFForm/widgets/signature.py,sha256=
|
|
28
|
+
PyPDFForm/widgets/signature.py,sha256=EndajR-SQWDZyVfbvDoz5Bjuer5KbURjFlRGqxeXYeY,5115
|
|
29
29
|
PyPDFForm/widgets/text.py,sha256=HP2cPEUAzK5QL3kDfMz7gQcC3svCpmYuyFItBjlrBpI,1233
|
|
30
|
-
pypdfform-2.2.
|
|
31
|
-
pypdfform-2.2.
|
|
32
|
-
pypdfform-2.2.
|
|
33
|
-
pypdfform-2.2.
|
|
34
|
-
pypdfform-2.2.
|
|
30
|
+
pypdfform-2.2.5.dist-info/licenses/LICENSE,sha256=43awmYkI6opyTpg19me731iO1WfXZwViqb67oWtCsFY,1065
|
|
31
|
+
pypdfform-2.2.5.dist-info/METADATA,sha256=5iVR9CNMRq8Srj3SjGGxppWg7Cso03Jl9ea4hNuTbzA,5605
|
|
32
|
+
pypdfform-2.2.5.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
|
|
33
|
+
pypdfform-2.2.5.dist-info/top_level.txt,sha256=GQQKuWqPUjT9YZqwK95NlAQzxjwoQrsxQ8ureM8lWOY,10
|
|
34
|
+
pypdfform-2.2.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|