PyPDFForm 1.3.5__py3-none-any.whl → 1.4.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 +3 -5
- PyPDFForm/core/constants.py +1 -1
- PyPDFForm/core/coordinate.py +65 -65
- PyPDFForm/core/filler.py +17 -17
- PyPDFForm/core/font.py +38 -44
- PyPDFForm/core/patterns.py +15 -14
- PyPDFForm/core/template.py +64 -65
- PyPDFForm/core/utils.py +35 -35
- PyPDFForm/core/watermark.py +30 -32
- PyPDFForm/middleware/checkbox.py +6 -6
- PyPDFForm/middleware/constants.py +3 -1
- PyPDFForm/middleware/dropdown.py +6 -6
- PyPDFForm/middleware/radio.py +6 -6
- PyPDFForm/middleware/template.py +30 -32
- PyPDFForm/middleware/text.py +6 -6
- PyPDFForm/middleware/{element.py → widget.py} +10 -10
- PyPDFForm/wrapper.py +82 -34
- {PyPDFForm-1.3.5.dist-info → PyPDFForm-1.4.0.dist-info}/METADATA +20 -4
- PyPDFForm-1.4.0.dist-info/RECORD +26 -0
- PyPDFForm-1.3.5.dist-info/RECORD +0 -26
- {PyPDFForm-1.3.5.dist-info → PyPDFForm-1.4.0.dist-info}/LICENSE +0 -0
- {PyPDFForm-1.3.5.dist-info → PyPDFForm-1.4.0.dist-info}/WHEEL +0 -0
- {PyPDFForm-1.3.5.dist-info → PyPDFForm-1.4.0.dist-info}/top_level.txt +0 -0
PyPDFForm/core/template.py
CHANGED
|
@@ -6,19 +6,19 @@ from typing import Dict, List, Tuple, Union
|
|
|
6
6
|
from pdfrw import PdfDict, PdfReader
|
|
7
7
|
from reportlab.pdfbase.pdfmetrics import stringWidth
|
|
8
8
|
|
|
9
|
-
from ..middleware.constants import
|
|
9
|
+
from ..middleware.constants import WIDGET_TYPES
|
|
10
10
|
from ..middleware.text import Text
|
|
11
11
|
from .constants import (ANNOTATION_KEY, ANNOTATION_RECTANGLE_KEY,
|
|
12
12
|
FIELD_FLAG_KEY, NEW_LINE_SYMBOL,
|
|
13
13
|
TEXT_FIELD_MAX_LENGTH_KEY)
|
|
14
|
-
from .patterns import (DROPDOWN_CHOICE_PATTERNS,
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
from .patterns import (DROPDOWN_CHOICE_PATTERNS, TEXT_FIELD_FLAG_PATTERNS,
|
|
15
|
+
WIDGET_ALIGNMENT_PATTERNS, WIDGET_KEY_PATTERNS,
|
|
16
|
+
WIDGET_TYPE_PATTERNS)
|
|
17
17
|
from .utils import find_pattern_match, traverse_pattern
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
def
|
|
21
|
-
"""Iterates through a PDF and returns all
|
|
20
|
+
def get_widgets_by_page(pdf: Union[bytes, PdfReader]) -> Dict[int, List[PdfDict]]:
|
|
21
|
+
"""Iterates through a PDF and returns all widgets found grouped by page."""
|
|
22
22
|
|
|
23
23
|
if isinstance(pdf, bytes):
|
|
24
24
|
pdf = PdfReader(fdata=pdf)
|
|
@@ -26,86 +26,86 @@ def get_elements_by_page(pdf: Union[bytes, PdfReader]) -> Dict[int, List[PdfDict
|
|
|
26
26
|
result = {}
|
|
27
27
|
|
|
28
28
|
for i, page in enumerate(pdf.pages):
|
|
29
|
-
|
|
29
|
+
widgets = page[ANNOTATION_KEY]
|
|
30
30
|
result[i + 1] = []
|
|
31
|
-
if
|
|
32
|
-
for
|
|
33
|
-
for each in
|
|
31
|
+
if widgets:
|
|
32
|
+
for widget in widgets:
|
|
33
|
+
for each in WIDGET_TYPE_PATTERNS:
|
|
34
34
|
patterns = each[0]
|
|
35
35
|
check = True
|
|
36
36
|
for pattern in patterns:
|
|
37
|
-
check = check and find_pattern_match(pattern,
|
|
37
|
+
check = check and find_pattern_match(pattern, widget)
|
|
38
38
|
if check:
|
|
39
|
-
result[i + 1].append(
|
|
39
|
+
result[i + 1].append(widget)
|
|
40
40
|
break
|
|
41
41
|
|
|
42
42
|
return result
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
def
|
|
46
|
-
"""Finds a PDF
|
|
45
|
+
def get_widget_key(widget: PdfDict) -> Union[str, None]:
|
|
46
|
+
"""Finds a PDF widget's annotated key by pattern matching."""
|
|
47
47
|
|
|
48
48
|
result = None
|
|
49
|
-
for pattern in
|
|
50
|
-
value = traverse_pattern(pattern,
|
|
49
|
+
for pattern in WIDGET_KEY_PATTERNS:
|
|
50
|
+
value = traverse_pattern(pattern, widget)
|
|
51
51
|
if value:
|
|
52
52
|
result = value[1:-1]
|
|
53
53
|
break
|
|
54
54
|
return result
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def
|
|
58
|
-
"""Finds a PDF
|
|
57
|
+
def get_widget_alignment(widget: PdfDict) -> Union[str, None]:
|
|
58
|
+
"""Finds a PDF widget's alignment by pattern matching."""
|
|
59
59
|
|
|
60
60
|
result = None
|
|
61
|
-
for pattern in
|
|
62
|
-
value = traverse_pattern(pattern,
|
|
61
|
+
for pattern in WIDGET_ALIGNMENT_PATTERNS:
|
|
62
|
+
value = traverse_pattern(pattern, widget)
|
|
63
63
|
if value:
|
|
64
64
|
result = value
|
|
65
65
|
break
|
|
66
66
|
return result
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
def
|
|
70
|
-
"""Finds a PDF
|
|
69
|
+
def construct_widget(widget: PdfDict, key: str) -> Union[WIDGET_TYPES, None]:
|
|
70
|
+
"""Finds a PDF widget's annotated type by pattern matching."""
|
|
71
71
|
|
|
72
72
|
result = None
|
|
73
|
-
for each in
|
|
73
|
+
for each in WIDGET_TYPE_PATTERNS:
|
|
74
74
|
patterns, _type = each
|
|
75
75
|
check = True
|
|
76
76
|
for pattern in patterns:
|
|
77
|
-
check = check and find_pattern_match(pattern,
|
|
77
|
+
check = check and find_pattern_match(pattern, widget)
|
|
78
78
|
if check:
|
|
79
79
|
result = _type(key)
|
|
80
80
|
break
|
|
81
81
|
return result
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
def get_text_field_max_length(
|
|
84
|
+
def get_text_field_max_length(widget: PdfDict) -> Union[int, None]:
|
|
85
85
|
"""Returns the max length of the text field if presented or None."""
|
|
86
86
|
|
|
87
87
|
return (
|
|
88
|
-
int(
|
|
89
|
-
if TEXT_FIELD_MAX_LENGTH_KEY in
|
|
88
|
+
int(widget[TEXT_FIELD_MAX_LENGTH_KEY])
|
|
89
|
+
if TEXT_FIELD_MAX_LENGTH_KEY in widget
|
|
90
90
|
else None
|
|
91
91
|
)
|
|
92
92
|
|
|
93
93
|
|
|
94
|
-
def is_text_field_comb(
|
|
94
|
+
def is_text_field_comb(widget: PdfDict) -> bool:
|
|
95
95
|
"""Returns true if characters in a text field needs to be formatted into combs."""
|
|
96
96
|
|
|
97
97
|
try:
|
|
98
|
-
return "{0:b}".format(int(
|
|
98
|
+
return "{0:b}".format(int(widget[FIELD_FLAG_KEY]))[::-1][24] == "1"
|
|
99
99
|
except (IndexError, TypeError):
|
|
100
100
|
return False
|
|
101
101
|
|
|
102
102
|
|
|
103
|
-
def is_text_multiline(
|
|
103
|
+
def is_text_multiline(widget: PdfDict) -> bool:
|
|
104
104
|
"""Returns true if a text field is a paragraph field."""
|
|
105
105
|
|
|
106
106
|
field_flag = None
|
|
107
107
|
for pattern in TEXT_FIELD_FLAG_PATTERNS:
|
|
108
|
-
field_flag = traverse_pattern(pattern,
|
|
108
|
+
field_flag = traverse_pattern(pattern, widget)
|
|
109
109
|
if field_flag is not None:
|
|
110
110
|
break
|
|
111
111
|
|
|
@@ -118,12 +118,12 @@ def is_text_multiline(element: PdfDict) -> bool:
|
|
|
118
118
|
return False
|
|
119
119
|
|
|
120
120
|
|
|
121
|
-
def get_dropdown_choices(
|
|
121
|
+
def get_dropdown_choices(widget: PdfDict) -> Union[Tuple[str], None]:
|
|
122
122
|
"""Returns string options of a dropdown field."""
|
|
123
123
|
|
|
124
124
|
result = None
|
|
125
125
|
for pattern in DROPDOWN_CHOICE_PATTERNS:
|
|
126
|
-
choices = traverse_pattern(pattern,
|
|
126
|
+
choices = traverse_pattern(pattern, widget)
|
|
127
127
|
if choices:
|
|
128
128
|
result = tuple(
|
|
129
129
|
(each if isinstance(each, str) else str(each[1]))
|
|
@@ -136,45 +136,44 @@ def get_dropdown_choices(element: PdfDict) -> Union[Tuple[str], None]:
|
|
|
136
136
|
return result
|
|
137
137
|
|
|
138
138
|
|
|
139
|
-
def get_char_rect_width(
|
|
139
|
+
def get_char_rect_width(widget: PdfDict, widget_middleware: Text) -> float:
|
|
140
140
|
"""Returns rectangular width of each character for combed text fields."""
|
|
141
141
|
|
|
142
142
|
rect_width = abs(
|
|
143
|
-
float(
|
|
144
|
-
- float(
|
|
143
|
+
float(widget[ANNOTATION_RECTANGLE_KEY][0])
|
|
144
|
+
- float(widget[ANNOTATION_RECTANGLE_KEY][2])
|
|
145
145
|
)
|
|
146
|
-
return rect_width /
|
|
146
|
+
return rect_width / widget_middleware.max_length
|
|
147
147
|
|
|
148
148
|
|
|
149
|
-
def get_character_x_paddings(
|
|
149
|
+
def get_character_x_paddings(widget: PdfDict, widget_middleware: Text) -> List[float]:
|
|
150
150
|
"""Returns paddings between characters for combed text fields."""
|
|
151
151
|
|
|
152
|
-
length = min(len(
|
|
153
|
-
char_rect_width = get_char_rect_width(
|
|
152
|
+
length = min(len(widget_middleware.value or ""), widget_middleware.max_length)
|
|
153
|
+
char_rect_width = get_char_rect_width(widget, widget_middleware)
|
|
154
154
|
|
|
155
155
|
result = []
|
|
156
156
|
|
|
157
157
|
current_x = 0
|
|
158
|
-
for char in (
|
|
158
|
+
for char in (widget_middleware.value or "")[:length]:
|
|
159
159
|
current_mid_point = current_x + char_rect_width / 2
|
|
160
160
|
result.append(
|
|
161
161
|
current_mid_point
|
|
162
|
-
- stringWidth(char,
|
|
163
|
-
/ 2
|
|
162
|
+
- stringWidth(char, widget_middleware.font, widget_middleware.font_size) / 2
|
|
164
163
|
)
|
|
165
164
|
current_x += char_rect_width
|
|
166
165
|
|
|
167
166
|
return result
|
|
168
167
|
|
|
169
168
|
|
|
170
|
-
def calculate_wrap_length(
|
|
169
|
+
def calculate_wrap_length(widget: PdfDict, widget_middleware: Text, v: str) -> int:
|
|
171
170
|
"""Increments the substring until reaching maximum horizontal width."""
|
|
172
171
|
|
|
173
172
|
width = abs(
|
|
174
|
-
float(
|
|
175
|
-
- float(
|
|
173
|
+
float(widget[ANNOTATION_RECTANGLE_KEY][0])
|
|
174
|
+
- float(widget[ANNOTATION_RECTANGLE_KEY][2])
|
|
176
175
|
)
|
|
177
|
-
value =
|
|
176
|
+
value = widget_middleware.value or ""
|
|
178
177
|
value = value.replace(NEW_LINE_SYMBOL, " ")
|
|
179
178
|
|
|
180
179
|
counter = 0
|
|
@@ -183,26 +182,26 @@ def calculate_wrap_length(element: PdfDict, element_middleware: Text, v: str) ->
|
|
|
183
182
|
counter += 1
|
|
184
183
|
_width = stringWidth(
|
|
185
184
|
v[:counter],
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
widget_middleware.font,
|
|
186
|
+
widget_middleware.font_size,
|
|
188
187
|
)
|
|
189
188
|
return counter - 1
|
|
190
189
|
|
|
191
190
|
|
|
192
|
-
def get_paragraph_lines(
|
|
191
|
+
def get_paragraph_lines(widget: PdfDict, widget_middleware: Text) -> List[str]:
|
|
193
192
|
"""Splits the paragraph field's text to a list of lines."""
|
|
194
193
|
|
|
195
194
|
# pylint: disable=R0912
|
|
196
195
|
lines = []
|
|
197
196
|
result = []
|
|
198
|
-
text_wrap_length =
|
|
199
|
-
value =
|
|
200
|
-
if
|
|
201
|
-
value = value[:
|
|
197
|
+
text_wrap_length = widget_middleware.text_wrap_length
|
|
198
|
+
value = widget_middleware.value or ""
|
|
199
|
+
if widget_middleware.max_length is not None:
|
|
200
|
+
value = value[: widget_middleware.max_length]
|
|
202
201
|
|
|
203
202
|
width = abs(
|
|
204
|
-
float(
|
|
205
|
-
- float(
|
|
203
|
+
float(widget[ANNOTATION_RECTANGLE_KEY][0])
|
|
204
|
+
- float(widget[ANNOTATION_RECTANGLE_KEY][2])
|
|
206
205
|
)
|
|
207
206
|
|
|
208
207
|
split_by_new_line_symbol = value.split(NEW_LINE_SYMBOL)
|
|
@@ -226,8 +225,8 @@ def get_paragraph_lines(element: PdfDict, element_middleware: Text) -> List[str]
|
|
|
226
225
|
while (
|
|
227
226
|
stringWidth(
|
|
228
227
|
line[:text_wrap_length],
|
|
229
|
-
|
|
230
|
-
|
|
228
|
+
widget_middleware.font,
|
|
229
|
+
widget_middleware.font_size,
|
|
231
230
|
)
|
|
232
231
|
> width
|
|
233
232
|
):
|
|
@@ -256,26 +255,26 @@ def get_paragraph_lines(element: PdfDict, element_middleware: Text) -> List[str]
|
|
|
256
255
|
return result
|
|
257
256
|
|
|
258
257
|
|
|
259
|
-
def get_paragraph_auto_wrap_length(
|
|
258
|
+
def get_paragraph_auto_wrap_length(widget: PdfDict, widget_middleware: Text) -> int:
|
|
260
259
|
"""Calculates the text wrap length of a paragraph field."""
|
|
261
260
|
|
|
262
|
-
value =
|
|
261
|
+
value = widget_middleware.value or ""
|
|
263
262
|
value = value.replace(NEW_LINE_SYMBOL, " ")
|
|
264
263
|
width = abs(
|
|
265
|
-
float(
|
|
266
|
-
- float(
|
|
264
|
+
float(widget[ANNOTATION_RECTANGLE_KEY][0])
|
|
265
|
+
- float(widget[ANNOTATION_RECTANGLE_KEY][2])
|
|
267
266
|
)
|
|
268
267
|
text_width = stringWidth(
|
|
269
268
|
value,
|
|
270
|
-
|
|
271
|
-
|
|
269
|
+
widget_middleware.font,
|
|
270
|
+
widget_middleware.font_size,
|
|
272
271
|
)
|
|
273
272
|
|
|
274
273
|
lines = text_width / width
|
|
275
274
|
if lines > 1:
|
|
276
275
|
current_min = 0
|
|
277
276
|
while len(value) and current_min < len(value):
|
|
278
|
-
result = calculate_wrap_length(
|
|
277
|
+
result = calculate_wrap_length(widget, widget_middleware, value)
|
|
279
278
|
value = value[result:]
|
|
280
279
|
if current_min == 0:
|
|
281
280
|
current_min = result
|
PyPDFForm/core/utils.py
CHANGED
|
@@ -7,7 +7,7 @@ from typing import List, Union
|
|
|
7
7
|
from pdfrw import PdfDict, PdfReader, PdfWriter
|
|
8
8
|
|
|
9
9
|
from ..middleware.checkbox import Checkbox
|
|
10
|
-
from ..middleware.constants import
|
|
10
|
+
from ..middleware.constants import WIDGET_TYPES
|
|
11
11
|
from ..middleware.radio import Radio
|
|
12
12
|
from ..middleware.text import Text
|
|
13
13
|
from .constants import (ANNOTATION_KEY, CHECKBOX_TO_DRAW, DEFAULT_FONT,
|
|
@@ -30,51 +30,51 @@ def generate_stream(pdf: PdfReader) -> bytes:
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def checkbox_radio_to_draw(
|
|
33
|
-
|
|
33
|
+
widget: Union[Checkbox, Radio], font_size: Union[float, int]
|
|
34
34
|
) -> Text:
|
|
35
|
-
"""Converts a checkbox/radio
|
|
35
|
+
"""Converts a checkbox/radio widget to a drawable text widget."""
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
new_widget = Text(
|
|
38
|
+
name=widget.name,
|
|
39
|
+
value="",
|
|
40
40
|
)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
new_widget.font = DEFAULT_FONT
|
|
42
|
+
new_widget.font_size = font_size
|
|
43
|
+
new_widget.font_color = DEFAULT_FONT_COLOR
|
|
44
44
|
|
|
45
|
-
if isinstance(
|
|
46
|
-
|
|
47
|
-
elif isinstance(
|
|
48
|
-
|
|
45
|
+
if isinstance(widget, Checkbox):
|
|
46
|
+
new_widget.value = CHECKBOX_TO_DRAW
|
|
47
|
+
elif isinstance(widget, Radio):
|
|
48
|
+
new_widget.value = RADIO_TO_DRAW
|
|
49
49
|
|
|
50
|
-
return
|
|
50
|
+
return new_widget
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def
|
|
54
|
-
"""Converts
|
|
53
|
+
def preview_widget_to_draw(widget: WIDGET_TYPES) -> Text:
|
|
54
|
+
"""Converts a widget to a preview text widget."""
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
new_widget = Text(
|
|
57
|
+
name=widget.name,
|
|
58
|
+
value="{" + f" {widget.name} " + "}",
|
|
59
59
|
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
new_widget.font = DEFAULT_FONT
|
|
61
|
+
new_widget.font_size = DEFAULT_FONT_SIZE
|
|
62
|
+
new_widget.font_color = PREVIEW_FONT_COLOR
|
|
63
|
+
new_widget.preview = True
|
|
64
64
|
|
|
65
|
-
return
|
|
65
|
+
return new_widget
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def
|
|
69
|
-
"""Removes all
|
|
68
|
+
def remove_all_widgets(pdf: bytes) -> bytes:
|
|
69
|
+
"""Removes all widgets from a pdfrw parsed PDF form."""
|
|
70
70
|
|
|
71
71
|
pdf = PdfReader(fdata=pdf)
|
|
72
72
|
|
|
73
73
|
for page in pdf.pages:
|
|
74
|
-
|
|
75
|
-
if
|
|
76
|
-
for j in reversed(range(len(
|
|
77
|
-
|
|
74
|
+
widgets = page[ANNOTATION_KEY]
|
|
75
|
+
if widgets:
|
|
76
|
+
for j in reversed(range(len(widgets))):
|
|
77
|
+
widgets.pop(j)
|
|
78
78
|
|
|
79
79
|
return generate_stream(pdf)
|
|
80
80
|
|
|
@@ -114,10 +114,10 @@ def merge_two_pdfs(pdf: bytes, other: bytes) -> bytes:
|
|
|
114
114
|
return result
|
|
115
115
|
|
|
116
116
|
|
|
117
|
-
def find_pattern_match(pattern: dict,
|
|
118
|
-
"""Checks if a PDF dict pattern exists in a PDF
|
|
117
|
+
def find_pattern_match(pattern: dict, widget: PdfDict) -> bool:
|
|
118
|
+
"""Checks if a PDF dict pattern exists in a PDF widget."""
|
|
119
119
|
|
|
120
|
-
for key, value in
|
|
120
|
+
for key, value in widget.items():
|
|
121
121
|
result = False
|
|
122
122
|
if key in pattern:
|
|
123
123
|
if isinstance(pattern[key], dict) and isinstance(value, PdfDict):
|
|
@@ -129,10 +129,10 @@ def find_pattern_match(pattern: dict, element: PdfDict) -> bool:
|
|
|
129
129
|
return False
|
|
130
130
|
|
|
131
131
|
|
|
132
|
-
def traverse_pattern(pattern: dict,
|
|
132
|
+
def traverse_pattern(pattern: dict, widget: PdfDict) -> Union[str, list, None]:
|
|
133
133
|
"""Traverses down a PDF dict pattern and find the value."""
|
|
134
134
|
|
|
135
|
-
for key, value in
|
|
135
|
+
for key, value in widget.items():
|
|
136
136
|
result = None
|
|
137
137
|
if key in pattern:
|
|
138
138
|
if isinstance(pattern[key], dict) and isinstance(value, PdfDict):
|
PyPDFForm/core/watermark.py
CHANGED
|
@@ -23,70 +23,68 @@ def draw_text(
|
|
|
23
23
|
) -> None:
|
|
24
24
|
"""Draws a text on the watermark."""
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
canvas = args[0]
|
|
27
|
+
widget = args[1]
|
|
28
28
|
coordinate_x = args[2]
|
|
29
29
|
coordinate_y = args[3]
|
|
30
30
|
|
|
31
|
-
text_to_draw =
|
|
31
|
+
text_to_draw = widget.value
|
|
32
32
|
|
|
33
33
|
if not text_to_draw:
|
|
34
34
|
text_to_draw = ""
|
|
35
35
|
|
|
36
|
-
if
|
|
37
|
-
text_to_draw = text_to_draw[:
|
|
36
|
+
if widget.max_length is not None:
|
|
37
|
+
text_to_draw = text_to_draw[: widget.max_length]
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
canvas.setFont(widget.font, widget.font_size)
|
|
40
|
+
canvas.setFillColorRGB(
|
|
41
|
+
widget.font_color[0], widget.font_color[1], widget.font_color[2]
|
|
42
42
|
)
|
|
43
43
|
|
|
44
|
-
if
|
|
44
|
+
if widget.comb is True:
|
|
45
45
|
for i, char in enumerate(text_to_draw):
|
|
46
|
-
|
|
47
|
-
coordinate_x +
|
|
46
|
+
canvas.drawString(
|
|
47
|
+
coordinate_x + widget.character_paddings[i],
|
|
48
48
|
coordinate_y,
|
|
49
49
|
char,
|
|
50
50
|
)
|
|
51
51
|
elif (
|
|
52
|
-
|
|
53
|
-
) and
|
|
54
|
-
|
|
52
|
+
widget.text_wrap_length is None or len(text_to_draw) < widget.text_wrap_length
|
|
53
|
+
) and widget.text_lines is None:
|
|
54
|
+
canvas.drawString(
|
|
55
55
|
coordinate_x,
|
|
56
56
|
coordinate_y,
|
|
57
57
|
text_to_draw,
|
|
58
58
|
)
|
|
59
59
|
else:
|
|
60
|
-
text_obj =
|
|
61
|
-
for i, line in enumerate(
|
|
60
|
+
text_obj = canvas.beginText(0, 0)
|
|
61
|
+
for i, line in enumerate(widget.text_lines):
|
|
62
62
|
cursor_moved = False
|
|
63
63
|
if (
|
|
64
|
-
|
|
65
|
-
and
|
|
64
|
+
widget.text_line_x_coordinates is not None
|
|
65
|
+
and widget.text_line_x_coordinates[i] - coordinate_x != 0
|
|
66
66
|
):
|
|
67
|
-
text_obj.moveCursor(
|
|
68
|
-
element.text_line_x_coordinates[i] - coordinate_x, 0
|
|
69
|
-
)
|
|
67
|
+
text_obj.moveCursor(widget.text_line_x_coordinates[i] - coordinate_x, 0)
|
|
70
68
|
cursor_moved = True
|
|
71
69
|
text_obj.textLine(line)
|
|
72
70
|
if cursor_moved:
|
|
73
71
|
text_obj.moveCursor(
|
|
74
|
-
-1 * (
|
|
72
|
+
-1 * (widget.text_line_x_coordinates[i] - coordinate_x), 0
|
|
75
73
|
)
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
canvas.saveState()
|
|
76
|
+
canvas.translate(
|
|
79
77
|
coordinate_x,
|
|
80
78
|
coordinate_y,
|
|
81
79
|
)
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
canvas.drawText(text_obj)
|
|
81
|
+
canvas.restoreState()
|
|
84
82
|
|
|
85
83
|
|
|
86
84
|
def draw_image(*args: Union[Canvas, bytes, float, int]) -> None:
|
|
87
85
|
"""Draws an image on the watermark."""
|
|
88
86
|
|
|
89
|
-
|
|
87
|
+
canvas = args[0]
|
|
90
88
|
image_stream = args[1]
|
|
91
89
|
coordinate_x = args[2]
|
|
92
90
|
coordinate_y = args[3]
|
|
@@ -97,7 +95,7 @@ def draw_image(*args: Union[Canvas, bytes, float, int]) -> None:
|
|
|
97
95
|
image_buff.write(image_stream)
|
|
98
96
|
image_buff.seek(0)
|
|
99
97
|
|
|
100
|
-
|
|
98
|
+
canvas.drawImage(
|
|
101
99
|
ImageReader(image_buff),
|
|
102
100
|
coordinate_x,
|
|
103
101
|
coordinate_y,
|
|
@@ -129,7 +127,7 @@ def create_watermarks_and_draw(
|
|
|
129
127
|
pdf_file = PdfReader(fdata=pdf)
|
|
130
128
|
buff = BytesIO()
|
|
131
129
|
|
|
132
|
-
|
|
130
|
+
canvas = Canvas(
|
|
133
131
|
buff,
|
|
134
132
|
pagesize=(
|
|
135
133
|
float(pdf_file.pages[page_number - 1].MediaBox[2]),
|
|
@@ -139,12 +137,12 @@ def create_watermarks_and_draw(
|
|
|
139
137
|
|
|
140
138
|
if action_type == "image":
|
|
141
139
|
for each in actions:
|
|
142
|
-
draw_image(*([
|
|
140
|
+
draw_image(*([canvas, *each]))
|
|
143
141
|
elif action_type == "text":
|
|
144
142
|
for each in actions:
|
|
145
|
-
draw_text(*([
|
|
143
|
+
draw_text(*([canvas, *each]))
|
|
146
144
|
|
|
147
|
-
|
|
145
|
+
canvas.save()
|
|
148
146
|
buff.seek(0)
|
|
149
147
|
|
|
150
148
|
watermark = buff.read()
|
PyPDFForm/middleware/checkbox.py
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""Contains checkbox middleware."""
|
|
3
3
|
|
|
4
|
-
from .
|
|
4
|
+
from .widget import Widget
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
class Checkbox(
|
|
8
|
-
"""A class to represent a checkbox
|
|
7
|
+
class Checkbox(Widget):
|
|
8
|
+
"""A class to represent a checkbox widget."""
|
|
9
9
|
|
|
10
10
|
def __init__(
|
|
11
11
|
self,
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
name: str,
|
|
13
|
+
value: bool = None,
|
|
14
14
|
) -> None:
|
|
15
15
|
"""Constructs all attributes for the checkbox."""
|
|
16
16
|
|
|
17
|
-
super().__init__(
|
|
17
|
+
super().__init__(name, value)
|
|
18
18
|
|
|
19
19
|
@property
|
|
20
20
|
def schema_definition(self) -> dict:
|
PyPDFForm/middleware/dropdown.py
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""Contains dropdown middleware."""
|
|
3
3
|
|
|
4
|
-
from .
|
|
4
|
+
from .widget import Widget
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
class Dropdown(
|
|
8
|
-
"""A class to represent a dropdown
|
|
7
|
+
class Dropdown(Widget):
|
|
8
|
+
"""A class to represent a dropdown widget."""
|
|
9
9
|
|
|
10
10
|
def __init__(
|
|
11
11
|
self,
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
name: str,
|
|
13
|
+
value: int = None,
|
|
14
14
|
) -> None:
|
|
15
15
|
"""Constructs all attributes for the dropdown."""
|
|
16
16
|
|
|
17
|
-
super().__init__(
|
|
17
|
+
super().__init__(name, value)
|
|
18
18
|
|
|
19
19
|
self.choices = None
|
|
20
20
|
|
PyPDFForm/middleware/radio.py
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""Contains radio middleware."""
|
|
3
3
|
|
|
4
|
-
from .
|
|
4
|
+
from .widget import Widget
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
class Radio(
|
|
8
|
-
"""A class to represent a radiobutton
|
|
7
|
+
class Radio(Widget):
|
|
8
|
+
"""A class to represent a radiobutton widget."""
|
|
9
9
|
|
|
10
10
|
def __init__(
|
|
11
11
|
self,
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
name: str,
|
|
13
|
+
value: int = None,
|
|
14
14
|
) -> None:
|
|
15
15
|
"""Constructs all attributes for the radiobutton."""
|
|
16
16
|
|
|
17
|
-
super().__init__(
|
|
17
|
+
super().__init__(name, value)
|
|
18
18
|
|
|
19
19
|
self.number_of_options = 0
|
|
20
20
|
|