PyPDFForm 1.4.22__tar.gz → 1.4.24__tar.gz
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-1.4.22 → pypdfform-1.4.24}/PKG-INFO +1 -1
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/__init__.py +1 -1
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/constants.py +5 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/filler.py +90 -62
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/font.py +24 -16
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/patterns.py +10 -3
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/template.py +69 -24
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/utils.py +4 -1
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/widgets/dropdown.py +6 -16
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm.egg-info/PKG-INFO +1 -1
- {pypdfform-1.4.22 → pypdfform-1.4.24}/LICENSE +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/adapter.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/coordinate.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/image.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/middleware/__init__.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/middleware/base.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/middleware/checkbox.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/middleware/dropdown.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/middleware/image.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/middleware/radio.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/middleware/signature.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/middleware/text.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/watermark.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/widgets/__init__.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/widgets/base.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/widgets/checkbox.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/widgets/text.py +1 -1
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm/wrapper.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm.egg-info/SOURCES.txt +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm.egg-info/dependency_links.txt +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm.egg-info/requires.txt +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/PyPDFForm.egg-info/top_level.txt +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/README.md +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/setup.cfg +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/setup.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/tests/test_create_widget.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/tests/test_dropdown.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/tests/test_dropdown_simple.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/tests/test_fill_max_length_text_field.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/tests/test_fill_max_length_text_field_simple.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/tests/test_functional.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/tests/test_functional_simple.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/tests/test_paragraph.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/tests/test_paragraph_simple.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/tests/test_preview.py +0 -0
- {pypdfform-1.4.22 → pypdfform-1.4.24}/tests/test_signature.py +0 -0
|
@@ -51,6 +51,7 @@ Opt = "/Opt"
|
|
|
51
51
|
MK = "/MK"
|
|
52
52
|
CA = "/CA"
|
|
53
53
|
AS = "/AS"
|
|
54
|
+
Yes = "/Yes"
|
|
54
55
|
Off = "/Off"
|
|
55
56
|
|
|
56
57
|
# Field flag bits
|
|
@@ -78,3 +79,7 @@ BUTTON_STYLES = {
|
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
COORDINATE_GRID_FONT_SIZE_MARGIN_RATIO = DEFAULT_FONT_SIZE / 100
|
|
82
|
+
|
|
83
|
+
# Used for adjusting paragraph font size
|
|
84
|
+
FONT_SIZE_REDUCE_STEP = 0.5
|
|
85
|
+
MARGIN_BETWEEN_LINES = 2
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""Contains helpers for filling a PDF form."""
|
|
3
3
|
|
|
4
4
|
from io import BytesIO
|
|
5
|
-
from typing import Dict, cast
|
|
5
|
+
from typing import Dict, Tuple, Union, cast
|
|
6
6
|
|
|
7
7
|
from pypdf import PdfReader, PdfWriter
|
|
8
8
|
from pypdf.generic import DictionaryObject
|
|
@@ -29,105 +29,133 @@ from .utils import checkbox_radio_to_draw, stream_to_io
|
|
|
29
29
|
from .watermark import create_watermarks_and_draw, merge_watermarks_with_pdf
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
def check_radio_handler(
|
|
33
|
+
widget: dict, middleware: Union[Checkbox, Radio], radio_button_tracker: dict
|
|
34
|
+
) -> Tuple[Text, Union[float, int], Union[float, int], bool]:
|
|
35
|
+
"""Handles draw parameters for checkbox and radio button widgets."""
|
|
36
|
+
|
|
37
|
+
font_size = (
|
|
38
|
+
checkbox_radio_font_size(widget) if middleware.size is None else middleware.size
|
|
39
|
+
)
|
|
40
|
+
to_draw = checkbox_radio_to_draw(middleware, font_size)
|
|
41
|
+
x, y = get_draw_checkbox_radio_coordinates(widget, to_draw)
|
|
42
|
+
text_needs_to_be_drawn = False
|
|
43
|
+
if type(middleware) is Checkbox and middleware.value:
|
|
44
|
+
text_needs_to_be_drawn = True
|
|
45
|
+
elif isinstance(middleware, Radio):
|
|
46
|
+
if middleware.name not in radio_button_tracker:
|
|
47
|
+
radio_button_tracker[middleware.name] = 0
|
|
48
|
+
radio_button_tracker[middleware.name] += 1
|
|
49
|
+
if middleware.value == radio_button_tracker[middleware.name] - 1:
|
|
50
|
+
text_needs_to_be_drawn = True
|
|
51
|
+
|
|
52
|
+
return to_draw, x, y, text_needs_to_be_drawn
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def signature_image_handler(
|
|
56
|
+
widget: dict, middleware: Union[Signature, Image], images_to_draw: list
|
|
57
|
+
) -> bool:
|
|
58
|
+
"""Handles draw parameters for signature and image widgets."""
|
|
59
|
+
|
|
60
|
+
stream = middleware.stream
|
|
61
|
+
any_image_to_draw = False
|
|
62
|
+
if stream is not None:
|
|
63
|
+
any_image_to_draw = True
|
|
64
|
+
stream = any_image_to_jpg(stream)
|
|
65
|
+
x, y, width, height = get_draw_image_coordinates_resolutions(widget)
|
|
66
|
+
images_to_draw.append(
|
|
67
|
+
[
|
|
68
|
+
stream,
|
|
69
|
+
x,
|
|
70
|
+
y,
|
|
71
|
+
width,
|
|
72
|
+
height,
|
|
73
|
+
]
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return any_image_to_draw
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def text_handler(
|
|
80
|
+
widget: dict, middleware: Text
|
|
81
|
+
) -> Tuple[Text, Union[float, int], Union[float, int], bool]:
|
|
82
|
+
"""Handles draw parameters for text field widgets."""
|
|
83
|
+
|
|
84
|
+
middleware.text_line_x_coordinates = get_text_line_x_coordinates(widget, middleware)
|
|
85
|
+
x, y = get_draw_text_coordinates(widget, middleware)
|
|
86
|
+
to_draw = middleware
|
|
87
|
+
text_needs_to_be_drawn = True
|
|
88
|
+
|
|
89
|
+
return to_draw, x, y, text_needs_to_be_drawn
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_drawn_stream(to_draw: dict, stream: bytes, action: str) -> bytes:
|
|
93
|
+
"""Generates a stream of an input PDF stream with stuff drawn on it."""
|
|
94
|
+
|
|
95
|
+
watermark_list = []
|
|
96
|
+
for page, stuffs in to_draw.items():
|
|
97
|
+
watermark_list.append(b"")
|
|
98
|
+
watermarks = create_watermarks_and_draw(stream, page, action, stuffs)
|
|
99
|
+
for i, watermark in enumerate(watermarks):
|
|
100
|
+
if watermark:
|
|
101
|
+
watermark_list[i] = watermark
|
|
102
|
+
|
|
103
|
+
return merge_watermarks_with_pdf(stream, watermark_list)
|
|
104
|
+
|
|
105
|
+
|
|
32
106
|
def fill(
|
|
33
107
|
template_stream: bytes,
|
|
34
108
|
widgets: Dict[str, WIDGET_TYPES],
|
|
35
109
|
) -> bytes:
|
|
36
110
|
"""Fills a PDF using watermarks."""
|
|
37
111
|
|
|
38
|
-
# pylint: disable=too-many-branches
|
|
39
112
|
texts_to_draw = {}
|
|
40
113
|
images_to_draw = {}
|
|
41
114
|
any_image_to_draw = False
|
|
42
|
-
text_watermarks = []
|
|
43
|
-
image_watermarks = []
|
|
44
115
|
|
|
45
116
|
radio_button_tracker = {}
|
|
46
117
|
|
|
47
|
-
for page,
|
|
118
|
+
for page, widget_dicts in get_widgets_by_page(template_stream).items():
|
|
48
119
|
texts_to_draw[page] = []
|
|
49
120
|
images_to_draw[page] = []
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
for _widget in _widgets:
|
|
53
|
-
key = get_widget_key(_widget)
|
|
121
|
+
for widget_dict in widget_dicts:
|
|
122
|
+
key = get_widget_key(widget_dict)
|
|
54
123
|
text_needs_to_be_drawn = False
|
|
55
|
-
|
|
124
|
+
to_draw = x = y = None
|
|
56
125
|
|
|
57
126
|
if isinstance(widgets[key], (Checkbox, Radio)):
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if widgets[key].size is None
|
|
61
|
-
else widgets[key].size
|
|
127
|
+
to_draw, x, y, text_needs_to_be_drawn = check_radio_handler(
|
|
128
|
+
widget_dict, widgets[key], radio_button_tracker
|
|
62
129
|
)
|
|
63
|
-
_to_draw = checkbox_radio_to_draw(widgets[key], font_size)
|
|
64
|
-
x, y = get_draw_checkbox_radio_coordinates(_widget, _to_draw)
|
|
65
|
-
if type(widgets[key]) is Checkbox and widgets[key].value:
|
|
66
|
-
text_needs_to_be_drawn = True
|
|
67
|
-
elif isinstance(widgets[key], Radio):
|
|
68
|
-
if key not in radio_button_tracker:
|
|
69
|
-
radio_button_tracker[key] = 0
|
|
70
|
-
radio_button_tracker[key] += 1
|
|
71
|
-
if widgets[key].value == radio_button_tracker[key] - 1:
|
|
72
|
-
text_needs_to_be_drawn = True
|
|
73
130
|
elif isinstance(widgets[key], (Signature, Image)):
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
stream = any_image_to_jpg(stream)
|
|
78
|
-
x, y, width, height = get_draw_image_coordinates_resolutions(
|
|
79
|
-
_widget
|
|
80
|
-
)
|
|
81
|
-
images_to_draw[page].append(
|
|
82
|
-
[
|
|
83
|
-
stream,
|
|
84
|
-
x,
|
|
85
|
-
y,
|
|
86
|
-
width,
|
|
87
|
-
height,
|
|
88
|
-
]
|
|
89
|
-
)
|
|
131
|
+
any_image_to_draw = signature_image_handler(
|
|
132
|
+
widget_dict, widgets[key], images_to_draw[page]
|
|
133
|
+
)
|
|
90
134
|
else:
|
|
91
|
-
|
|
92
|
-
|
|
135
|
+
to_draw, x, y, text_needs_to_be_drawn = text_handler(
|
|
136
|
+
widget_dict, widgets[key]
|
|
93
137
|
)
|
|
94
|
-
x, y = get_draw_text_coordinates(_widget, widgets[key])
|
|
95
|
-
_to_draw = widgets[key]
|
|
96
|
-
text_needs_to_be_drawn = True
|
|
97
138
|
|
|
98
139
|
if all(
|
|
99
140
|
[
|
|
100
141
|
text_needs_to_be_drawn,
|
|
101
|
-
|
|
142
|
+
to_draw is not None,
|
|
102
143
|
x is not None,
|
|
103
144
|
y is not None,
|
|
104
145
|
]
|
|
105
146
|
):
|
|
106
147
|
texts_to_draw[page].append(
|
|
107
148
|
[
|
|
108
|
-
|
|
149
|
+
to_draw,
|
|
109
150
|
x,
|
|
110
151
|
y,
|
|
111
152
|
]
|
|
112
153
|
)
|
|
113
154
|
|
|
114
|
-
|
|
115
|
-
_watermarks = create_watermarks_and_draw(template_stream, page, "text", texts)
|
|
116
|
-
for i, watermark in enumerate(_watermarks):
|
|
117
|
-
if watermark:
|
|
118
|
-
text_watermarks[i] = watermark
|
|
119
|
-
|
|
120
|
-
result = merge_watermarks_with_pdf(template_stream, text_watermarks)
|
|
155
|
+
result = get_drawn_stream(texts_to_draw, template_stream, "text")
|
|
121
156
|
|
|
122
157
|
if any_image_to_draw:
|
|
123
|
-
|
|
124
|
-
_watermarks = create_watermarks_and_draw(
|
|
125
|
-
template_stream, page, "image", images
|
|
126
|
-
)
|
|
127
|
-
for i, watermark in enumerate(_watermarks):
|
|
128
|
-
if watermark:
|
|
129
|
-
image_watermarks[i] = watermark
|
|
130
|
-
result = merge_watermarks_with_pdf(result, image_watermarks)
|
|
158
|
+
result = get_drawn_stream(images_to_draw, result, "image")
|
|
131
159
|
|
|
132
160
|
return result
|
|
133
161
|
|
|
@@ -33,21 +33,11 @@ def register_font(font_name: str, ttf_stream: bytes) -> bool:
|
|
|
33
33
|
return result
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
def
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
text_appearance = None
|
|
43
|
-
for pattern in TEXT_FIELD_APPEARANCE_PATTERNS:
|
|
44
|
-
text_appearance = traverse_pattern(pattern, widget)
|
|
45
|
-
|
|
46
|
-
if text_appearance:
|
|
47
|
-
break
|
|
48
|
-
|
|
49
|
-
if not text_appearance:
|
|
50
|
-
return result
|
|
36
|
+
def extract_font_from_text_appearance(text_appearance: str) -> Union[str, None]:
|
|
37
|
+
"""
|
|
38
|
+
Uses regex to pattern match out the font from the text
|
|
39
|
+
appearance string of a text field widget.
|
|
40
|
+
"""
|
|
51
41
|
|
|
52
42
|
text_appearance = text_appearance.split(" ")
|
|
53
43
|
|
|
@@ -73,7 +63,25 @@ def auto_detect_font(widget: dict) -> str:
|
|
|
73
63
|
if found:
|
|
74
64
|
return font
|
|
75
65
|
|
|
76
|
-
return
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def auto_detect_font(widget: dict) -> str:
|
|
70
|
+
"""Returns the font of the text field if it is one of the standard fonts."""
|
|
71
|
+
|
|
72
|
+
result = DEFAULT_FONT
|
|
73
|
+
|
|
74
|
+
text_appearance = None
|
|
75
|
+
for pattern in TEXT_FIELD_APPEARANCE_PATTERNS:
|
|
76
|
+
text_appearance = traverse_pattern(pattern, widget)
|
|
77
|
+
|
|
78
|
+
if text_appearance:
|
|
79
|
+
break
|
|
80
|
+
|
|
81
|
+
if not text_appearance:
|
|
82
|
+
return result
|
|
83
|
+
|
|
84
|
+
return extract_font_from_text_appearance(text_appearance) or result
|
|
77
85
|
|
|
78
86
|
|
|
79
87
|
def text_field_font_size(widget: dict) -> Union[float, int]:
|
|
@@ -6,7 +6,7 @@ from pypdf.generic import (DictionaryObject, NameObject, NumberObject,
|
|
|
6
6
|
|
|
7
7
|
from .constants import (AP, AS, CA, DA, FT, IMAGE_FIELD_IDENTIFIER, JS, MK,
|
|
8
8
|
READ_ONLY, A, Btn, Ch, D, Ff, Off, Opt, Parent, Q, Sig,
|
|
9
|
-
Subtype, T, Tx, V, Widget)
|
|
9
|
+
Subtype, T, Tx, V, Widget, Yes)
|
|
10
10
|
from .middleware.checkbox import Checkbox
|
|
11
11
|
from .middleware.dropdown import Dropdown
|
|
12
12
|
from .middleware.image import Image
|
|
@@ -28,7 +28,10 @@ WIDGET_TYPE_PATTERNS = [
|
|
|
28
28
|
Text,
|
|
29
29
|
),
|
|
30
30
|
(
|
|
31
|
-
(
|
|
31
|
+
(
|
|
32
|
+
{FT: Btn},
|
|
33
|
+
{AS: (Yes, Off)},
|
|
34
|
+
),
|
|
32
35
|
Checkbox,
|
|
33
36
|
),
|
|
34
37
|
(
|
|
@@ -47,11 +50,15 @@ WIDGET_TYPE_PATTERNS = [
|
|
|
47
50
|
(
|
|
48
51
|
{Parent: {FT: Btn}},
|
|
49
52
|
{Parent: {Subtype: Widget}},
|
|
53
|
+
{AS: (Yes, Off)},
|
|
50
54
|
),
|
|
51
55
|
Checkbox,
|
|
52
56
|
),
|
|
53
57
|
(
|
|
54
|
-
(
|
|
58
|
+
(
|
|
59
|
+
{Parent: {FT: Btn}},
|
|
60
|
+
{AS: (Yes, Off)},
|
|
61
|
+
),
|
|
55
62
|
Radio,
|
|
56
63
|
),
|
|
57
64
|
]
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""Contains helpers for generic template related processing."""
|
|
3
3
|
|
|
4
|
+
from functools import lru_cache
|
|
4
5
|
from sys import maxsize
|
|
5
6
|
from typing import Dict, List, Tuple, Union
|
|
6
7
|
|
|
7
8
|
from pypdf import PdfReader
|
|
8
9
|
from reportlab.pdfbase.pdfmetrics import stringWidth
|
|
9
10
|
|
|
10
|
-
from .constants import (COMB, DEFAULT_FONT_SIZE,
|
|
11
|
+
from .constants import (COMB, DEFAULT_FONT_SIZE, FONT_SIZE_REDUCE_STEP,
|
|
12
|
+
MARGIN_BETWEEN_LINES, MULTILINE, NEW_LINE_SYMBOL,
|
|
11
13
|
WIDGET_TYPES, MaxLen, Rect)
|
|
12
14
|
from .font import (auto_detect_font, get_text_field_font_color,
|
|
13
15
|
get_text_field_font_size, text_field_font_size)
|
|
@@ -121,23 +123,30 @@ def update_text_field_attributes(
|
|
|
121
123
|
key = get_widget_key(_widget)
|
|
122
124
|
|
|
123
125
|
if isinstance(widgets[key], Text):
|
|
126
|
+
should_adjust_font_size = False
|
|
127
|
+
is_paragraph = is_text_multiline(_widget)
|
|
124
128
|
if widgets[key].font is None:
|
|
125
129
|
widgets[key].font = auto_detect_font(_widget)
|
|
126
130
|
if widgets[key].font_size is None:
|
|
127
|
-
|
|
131
|
+
template_font_size = get_text_field_font_size(_widget)
|
|
132
|
+
widgets[key].font_size = template_font_size or (
|
|
128
133
|
text_field_font_size(_widget)
|
|
129
|
-
if not
|
|
134
|
+
if not is_paragraph
|
|
130
135
|
else DEFAULT_FONT_SIZE
|
|
131
136
|
)
|
|
137
|
+
should_adjust_font_size = is_paragraph and not template_font_size
|
|
132
138
|
if widgets[key].font_color is None:
|
|
133
139
|
widgets[key].font_color = get_text_field_font_color(_widget)
|
|
134
|
-
if
|
|
140
|
+
if is_paragraph and widgets[key].text_wrap_length is None:
|
|
135
141
|
widgets[key].text_lines = get_paragraph_lines(_widget, widgets[key])
|
|
136
142
|
widgets[key].text_wrap_length = get_paragraph_auto_wrap_length(
|
|
137
143
|
widgets[key]
|
|
138
144
|
)
|
|
145
|
+
if widgets[key].value and should_adjust_font_size:
|
|
146
|
+
adjust_paragraph_font_size(_widget, widgets[key])
|
|
139
147
|
|
|
140
148
|
|
|
149
|
+
@lru_cache()
|
|
141
150
|
def get_widgets_by_page(pdf: bytes) -> Dict[int, List[dict]]:
|
|
142
151
|
"""Iterates through a PDF and returns all widgets found grouped by page."""
|
|
143
152
|
|
|
@@ -288,28 +297,23 @@ def get_character_x_paddings(widget: dict, widget_middleware: Text) -> List[floa
|
|
|
288
297
|
return result
|
|
289
298
|
|
|
290
299
|
|
|
291
|
-
def
|
|
292
|
-
|
|
300
|
+
def split_characters_into_lines(
|
|
301
|
+
split_by_new_line_symbol: List[str], middleware: Text, width: float
|
|
302
|
+
) -> List[str]:
|
|
303
|
+
"""
|
|
304
|
+
Given a long string meant to be filled for a paragraph widget
|
|
305
|
+
split by the new line symbol already, splits it further into lines
|
|
306
|
+
where each line would fit into the widget's width.
|
|
307
|
+
"""
|
|
293
308
|
|
|
294
|
-
# pylint: disable=R0912
|
|
295
309
|
lines = []
|
|
296
|
-
result = []
|
|
297
|
-
value = widget_middleware.value or ""
|
|
298
|
-
if widget_middleware.max_length is not None:
|
|
299
|
-
value = value[: widget_middleware.max_length]
|
|
300
|
-
|
|
301
|
-
width = abs(float(widget[Rect][0]) - float(widget[Rect][2]))
|
|
302
|
-
|
|
303
|
-
split_by_new_line_symbol = value.split(NEW_LINE_SYMBOL)
|
|
304
310
|
for line in split_by_new_line_symbol:
|
|
305
311
|
characters = line.split(" ")
|
|
306
312
|
current_line = ""
|
|
307
313
|
for each in characters:
|
|
308
314
|
line_extended = f"{current_line} {each}" if current_line else each
|
|
309
315
|
if (
|
|
310
|
-
stringWidth(
|
|
311
|
-
line_extended, widget_middleware.font, widget_middleware.font_size
|
|
312
|
-
)
|
|
316
|
+
stringWidth(line_extended, middleware.font, middleware.font_size)
|
|
313
317
|
<= width
|
|
314
318
|
):
|
|
315
319
|
current_line = line_extended
|
|
@@ -322,14 +326,23 @@ def get_paragraph_lines(widget: dict, widget_middleware: Text) -> List[str]:
|
|
|
322
326
|
else current_line
|
|
323
327
|
)
|
|
324
328
|
|
|
329
|
+
return lines
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def adjust_each_line(lines: List[str], middleware: Text, width: float) -> List[str]:
|
|
333
|
+
"""
|
|
334
|
+
Given a list of strings which is the return value of
|
|
335
|
+
`split_characters_into_lines`, further adjusts each line
|
|
336
|
+
so that there is neither overflow nor over-splitting into
|
|
337
|
+
unnecessary lines.
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
result = []
|
|
325
341
|
for each in lines:
|
|
326
342
|
tracker = ""
|
|
327
343
|
for char in each:
|
|
328
344
|
check = tracker + char
|
|
329
|
-
if (
|
|
330
|
-
stringWidth(check, widget_middleware.font, widget_middleware.font_size)
|
|
331
|
-
> width
|
|
332
|
-
):
|
|
345
|
+
if stringWidth(check, middleware.font, middleware.font_size) > width:
|
|
333
346
|
result.append(tracker)
|
|
334
347
|
tracker = char
|
|
335
348
|
else:
|
|
@@ -341,8 +354,8 @@ def get_paragraph_lines(widget: dict, widget_middleware: Text) -> List[str]:
|
|
|
341
354
|
result
|
|
342
355
|
and stringWidth(
|
|
343
356
|
f"{each} {result[-1]}",
|
|
344
|
-
|
|
345
|
-
|
|
357
|
+
middleware.font,
|
|
358
|
+
middleware.font_size,
|
|
346
359
|
)
|
|
347
360
|
<= width
|
|
348
361
|
and NEW_LINE_SYMBOL not in result[-1]
|
|
@@ -360,6 +373,23 @@ def get_paragraph_lines(widget: dict, widget_middleware: Text) -> List[str]:
|
|
|
360
373
|
return result
|
|
361
374
|
|
|
362
375
|
|
|
376
|
+
def get_paragraph_lines(widget: dict, widget_middleware: Text) -> List[str]:
|
|
377
|
+
"""Splits the paragraph field's text to a list of lines."""
|
|
378
|
+
|
|
379
|
+
value = widget_middleware.value or ""
|
|
380
|
+
if widget_middleware.max_length is not None:
|
|
381
|
+
value = value[: widget_middleware.max_length]
|
|
382
|
+
|
|
383
|
+
width = abs(float(widget[Rect][0]) - float(widget[Rect][2]))
|
|
384
|
+
|
|
385
|
+
split_by_new_line_symbol = value.split(NEW_LINE_SYMBOL)
|
|
386
|
+
lines = split_characters_into_lines(
|
|
387
|
+
split_by_new_line_symbol, widget_middleware, width
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
return adjust_each_line(lines, widget_middleware, width)
|
|
391
|
+
|
|
392
|
+
|
|
363
393
|
def get_paragraph_auto_wrap_length(widget_middleware: Text) -> int:
|
|
364
394
|
"""Calculates the text wrap length of a paragraph field."""
|
|
365
395
|
|
|
@@ -368,3 +398,18 @@ def get_paragraph_auto_wrap_length(widget_middleware: Text) -> int:
|
|
|
368
398
|
result = min(result, len(line))
|
|
369
399
|
|
|
370
400
|
return result
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def adjust_paragraph_font_size(widget: dict, widget_middleware: Text) -> None:
|
|
404
|
+
"""Reduces the font size of a paragraph field until texts fits."""
|
|
405
|
+
|
|
406
|
+
height = abs(float(widget[Rect][1]) - float(widget[Rect][3]))
|
|
407
|
+
|
|
408
|
+
while (
|
|
409
|
+
widget_middleware.font_size > FONT_SIZE_REDUCE_STEP
|
|
410
|
+
and len(widget_middleware.text_lines)
|
|
411
|
+
* (widget_middleware.font_size + MARGIN_BETWEEN_LINES)
|
|
412
|
+
> height
|
|
413
|
+
):
|
|
414
|
+
widget_middleware.font_size -= FONT_SIZE_REDUCE_STEP
|
|
415
|
+
widget_middleware.text_lines = get_paragraph_lines(widget, widget_middleware)
|
|
@@ -122,7 +122,10 @@ def find_pattern_match(pattern: dict, widget: Union[dict, DictionaryObject]) ->
|
|
|
122
122
|
):
|
|
123
123
|
result = find_pattern_match(pattern[key], value)
|
|
124
124
|
else:
|
|
125
|
-
|
|
125
|
+
if isinstance(pattern[key], tuple):
|
|
126
|
+
result = value in pattern[key]
|
|
127
|
+
else:
|
|
128
|
+
result = pattern[key] == value
|
|
126
129
|
if result:
|
|
127
130
|
return result
|
|
128
131
|
return False
|
|
@@ -1,26 +1,13 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""Contains dropdown widget to create."""
|
|
3
3
|
|
|
4
|
-
from .
|
|
4
|
+
from .text import TextWidget
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
class DropdownWidget(
|
|
7
|
+
class DropdownWidget(TextWidget):
|
|
8
8
|
"""Dropdown widget to create."""
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
USER_PARAMS = [
|
|
13
|
-
("width", "width"),
|
|
14
|
-
("height", "height"),
|
|
15
|
-
("options", "options"),
|
|
16
|
-
("font", "fontName"),
|
|
17
|
-
("font_size", "fontSize"),
|
|
18
|
-
("font_color", "textColor"),
|
|
19
|
-
("bg_color", "fillColor"),
|
|
20
|
-
("border_color", "borderColor"),
|
|
21
|
-
("border_width", "borderWidth"),
|
|
22
|
-
]
|
|
23
|
-
COLOR_PARAMS = ["font_color", "bg_color", "border_color"]
|
|
10
|
+
NONE_DEFAULTS = []
|
|
24
11
|
ACRO_FORM_FUNC = "_textfield"
|
|
25
12
|
|
|
26
13
|
def __init__(
|
|
@@ -33,6 +20,9 @@ class DropdownWidget(Widget):
|
|
|
33
20
|
) -> None:
|
|
34
21
|
"""Sets acro form parameters."""
|
|
35
22
|
|
|
23
|
+
self.USER_PARAMS = super().USER_PARAMS[:-1] + [
|
|
24
|
+
("options", "options"),
|
|
25
|
+
]
|
|
36
26
|
super().__init__(name, page_number, x, y, **kwargs)
|
|
37
27
|
self.acro_form_params["wkind"] = "choice"
|
|
38
28
|
self.acro_form_params["value"] = self.acro_form_params["options"][0]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -10,13 +10,13 @@ class TextWidget(Widget):
|
|
|
10
10
|
USER_PARAMS = [
|
|
11
11
|
("width", "width"),
|
|
12
12
|
("height", "height"),
|
|
13
|
-
("max_length", "maxlen"),
|
|
14
13
|
("font", "fontName"),
|
|
15
14
|
("font_size", "fontSize"),
|
|
16
15
|
("font_color", "textColor"),
|
|
17
16
|
("bg_color", "fillColor"),
|
|
18
17
|
("border_color", "borderColor"),
|
|
19
18
|
("border_width", "borderWidth"),
|
|
19
|
+
("max_length", "maxlen"),
|
|
20
20
|
]
|
|
21
21
|
COLOR_PARAMS = ["font_color", "bg_color", "border_color"]
|
|
22
22
|
NONE_DEFAULTS = ["max_length"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|