PyPDFForm 1.4.23__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.23 → pypdfform-1.4.24}/PKG-INFO +1 -1
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/__init__.py +1 -1
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/constants.py +5 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/filler.py +90 -61
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/font.py +24 -15
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/patterns.py +10 -3
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/template.py +67 -23
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/utils.py +4 -1
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm.egg-info/PKG-INFO +1 -1
- {pypdfform-1.4.23 → pypdfform-1.4.24}/LICENSE +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/adapter.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/coordinate.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/image.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/middleware/__init__.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/middleware/base.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/middleware/checkbox.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/middleware/dropdown.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/middleware/image.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/middleware/radio.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/middleware/signature.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/middleware/text.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/watermark.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/widgets/__init__.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/widgets/base.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/widgets/checkbox.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/widgets/dropdown.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/widgets/text.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm/wrapper.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm.egg-info/SOURCES.txt +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm.egg-info/dependency_links.txt +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm.egg-info/requires.txt +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/PyPDFForm.egg-info/top_level.txt +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/README.md +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/setup.cfg +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/setup.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/tests/test_create_widget.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/tests/test_dropdown.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/tests/test_dropdown_simple.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/tests/test_fill_max_length_text_field.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/tests/test_fill_max_length_text_field_simple.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/tests/test_functional.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/tests/test_functional_simple.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/tests/test_paragraph.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/tests/test_paragraph_simple.py +0 -0
- {pypdfform-1.4.23 → pypdfform-1.4.24}/tests/test_preview.py +0 -0
- {pypdfform-1.4.23 → 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,6 +29,80 @@ 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],
|
|
@@ -38,95 +112,50 @@ def fill(
|
|
|
38
112
|
texts_to_draw = {}
|
|
39
113
|
images_to_draw = {}
|
|
40
114
|
any_image_to_draw = False
|
|
41
|
-
text_watermarks = []
|
|
42
|
-
image_watermarks = []
|
|
43
115
|
|
|
44
116
|
radio_button_tracker = {}
|
|
45
117
|
|
|
46
|
-
for page,
|
|
118
|
+
for page, widget_dicts in get_widgets_by_page(template_stream).items():
|
|
47
119
|
texts_to_draw[page] = []
|
|
48
120
|
images_to_draw[page] = []
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
for _widget in _widgets:
|
|
52
|
-
key = get_widget_key(_widget)
|
|
121
|
+
for widget_dict in widget_dicts:
|
|
122
|
+
key = get_widget_key(widget_dict)
|
|
53
123
|
text_needs_to_be_drawn = False
|
|
54
|
-
|
|
124
|
+
to_draw = x = y = None
|
|
55
125
|
|
|
56
126
|
if isinstance(widgets[key], (Checkbox, Radio)):
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if widgets[key].size is None
|
|
60
|
-
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
|
|
61
129
|
)
|
|
62
|
-
_to_draw = checkbox_radio_to_draw(widgets[key], font_size)
|
|
63
|
-
x, y = get_draw_checkbox_radio_coordinates(_widget, _to_draw)
|
|
64
|
-
if type(widgets[key]) is Checkbox and widgets[key].value:
|
|
65
|
-
text_needs_to_be_drawn = True
|
|
66
|
-
elif isinstance(widgets[key], Radio):
|
|
67
|
-
if key not in radio_button_tracker:
|
|
68
|
-
radio_button_tracker[key] = 0
|
|
69
|
-
radio_button_tracker[key] += 1
|
|
70
|
-
if widgets[key].value == radio_button_tracker[key] - 1:
|
|
71
|
-
text_needs_to_be_drawn = True
|
|
72
130
|
elif isinstance(widgets[key], (Signature, Image)):
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
stream = any_image_to_jpg(stream)
|
|
77
|
-
x, y, width, height = get_draw_image_coordinates_resolutions(
|
|
78
|
-
_widget
|
|
79
|
-
)
|
|
80
|
-
images_to_draw[page].append(
|
|
81
|
-
[
|
|
82
|
-
stream,
|
|
83
|
-
x,
|
|
84
|
-
y,
|
|
85
|
-
width,
|
|
86
|
-
height,
|
|
87
|
-
]
|
|
88
|
-
)
|
|
131
|
+
any_image_to_draw = signature_image_handler(
|
|
132
|
+
widget_dict, widgets[key], images_to_draw[page]
|
|
133
|
+
)
|
|
89
134
|
else:
|
|
90
|
-
|
|
91
|
-
|
|
135
|
+
to_draw, x, y, text_needs_to_be_drawn = text_handler(
|
|
136
|
+
widget_dict, widgets[key]
|
|
92
137
|
)
|
|
93
|
-
x, y = get_draw_text_coordinates(_widget, widgets[key])
|
|
94
|
-
_to_draw = widgets[key]
|
|
95
|
-
text_needs_to_be_drawn = True
|
|
96
138
|
|
|
97
139
|
if all(
|
|
98
140
|
[
|
|
99
141
|
text_needs_to_be_drawn,
|
|
100
|
-
|
|
142
|
+
to_draw is not None,
|
|
101
143
|
x is not None,
|
|
102
144
|
y is not None,
|
|
103
145
|
]
|
|
104
146
|
):
|
|
105
147
|
texts_to_draw[page].append(
|
|
106
148
|
[
|
|
107
|
-
|
|
149
|
+
to_draw,
|
|
108
150
|
x,
|
|
109
151
|
y,
|
|
110
152
|
]
|
|
111
153
|
)
|
|
112
154
|
|
|
113
|
-
|
|
114
|
-
_watermarks = create_watermarks_and_draw(template_stream, page, "text", texts)
|
|
115
|
-
for i, watermark in enumerate(_watermarks):
|
|
116
|
-
if watermark:
|
|
117
|
-
text_watermarks[i] = watermark
|
|
118
|
-
|
|
119
|
-
result = merge_watermarks_with_pdf(template_stream, text_watermarks)
|
|
155
|
+
result = get_drawn_stream(texts_to_draw, template_stream, "text")
|
|
120
156
|
|
|
121
157
|
if any_image_to_draw:
|
|
122
|
-
|
|
123
|
-
_watermarks = create_watermarks_and_draw(
|
|
124
|
-
template_stream, page, "image", images
|
|
125
|
-
)
|
|
126
|
-
for i, watermark in enumerate(_watermarks):
|
|
127
|
-
if watermark:
|
|
128
|
-
image_watermarks[i] = watermark
|
|
129
|
-
result = merge_watermarks_with_pdf(result, image_watermarks)
|
|
158
|
+
result = get_drawn_stream(images_to_draw, result, "image")
|
|
130
159
|
|
|
131
160
|
return result
|
|
132
161
|
|
|
@@ -33,20 +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
|
-
text_appearance = None
|
|
42
|
-
for pattern in TEXT_FIELD_APPEARANCE_PATTERNS:
|
|
43
|
-
text_appearance = traverse_pattern(pattern, widget)
|
|
44
|
-
|
|
45
|
-
if text_appearance:
|
|
46
|
-
break
|
|
47
|
-
|
|
48
|
-
if not text_appearance:
|
|
49
|
-
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
|
+
"""
|
|
50
41
|
|
|
51
42
|
text_appearance = text_appearance.split(" ")
|
|
52
43
|
|
|
@@ -72,7 +63,25 @@ def auto_detect_font(widget: dict) -> str:
|
|
|
72
63
|
if found:
|
|
73
64
|
return font
|
|
74
65
|
|
|
75
|
-
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
|
|
76
85
|
|
|
77
86
|
|
|
78
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
|
]
|
|
@@ -8,7 +8,8 @@ from typing import Dict, List, Tuple, Union
|
|
|
8
8
|
from pypdf import PdfReader
|
|
9
9
|
from reportlab.pdfbase.pdfmetrics import stringWidth
|
|
10
10
|
|
|
11
|
-
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,
|
|
12
13
|
WIDGET_TYPES, MaxLen, Rect)
|
|
13
14
|
from .font import (auto_detect_font, get_text_field_font_color,
|
|
14
15
|
get_text_field_font_size, text_field_font_size)
|
|
@@ -122,21 +123,27 @@ def update_text_field_attributes(
|
|
|
122
123
|
key = get_widget_key(_widget)
|
|
123
124
|
|
|
124
125
|
if isinstance(widgets[key], Text):
|
|
126
|
+
should_adjust_font_size = False
|
|
127
|
+
is_paragraph = is_text_multiline(_widget)
|
|
125
128
|
if widgets[key].font is None:
|
|
126
129
|
widgets[key].font = auto_detect_font(_widget)
|
|
127
130
|
if widgets[key].font_size is None:
|
|
128
|
-
|
|
131
|
+
template_font_size = get_text_field_font_size(_widget)
|
|
132
|
+
widgets[key].font_size = template_font_size or (
|
|
129
133
|
text_field_font_size(_widget)
|
|
130
|
-
if not
|
|
134
|
+
if not is_paragraph
|
|
131
135
|
else DEFAULT_FONT_SIZE
|
|
132
136
|
)
|
|
137
|
+
should_adjust_font_size = is_paragraph and not template_font_size
|
|
133
138
|
if widgets[key].font_color is None:
|
|
134
139
|
widgets[key].font_color = get_text_field_font_color(_widget)
|
|
135
|
-
if
|
|
140
|
+
if is_paragraph and widgets[key].text_wrap_length is None:
|
|
136
141
|
widgets[key].text_lines = get_paragraph_lines(_widget, widgets[key])
|
|
137
142
|
widgets[key].text_wrap_length = get_paragraph_auto_wrap_length(
|
|
138
143
|
widgets[key]
|
|
139
144
|
)
|
|
145
|
+
if widgets[key].value and should_adjust_font_size:
|
|
146
|
+
adjust_paragraph_font_size(_widget, widgets[key])
|
|
140
147
|
|
|
141
148
|
|
|
142
149
|
@lru_cache()
|
|
@@ -290,27 +297,23 @@ def get_character_x_paddings(widget: dict, widget_middleware: Text) -> List[floa
|
|
|
290
297
|
return result
|
|
291
298
|
|
|
292
299
|
|
|
293
|
-
def
|
|
294
|
-
|
|
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
|
+
"""
|
|
295
308
|
|
|
296
309
|
lines = []
|
|
297
|
-
result = []
|
|
298
|
-
value = widget_middleware.value or ""
|
|
299
|
-
if widget_middleware.max_length is not None:
|
|
300
|
-
value = value[: widget_middleware.max_length]
|
|
301
|
-
|
|
302
|
-
width = abs(float(widget[Rect][0]) - float(widget[Rect][2]))
|
|
303
|
-
|
|
304
|
-
split_by_new_line_symbol = value.split(NEW_LINE_SYMBOL)
|
|
305
310
|
for line in split_by_new_line_symbol:
|
|
306
311
|
characters = line.split(" ")
|
|
307
312
|
current_line = ""
|
|
308
313
|
for each in characters:
|
|
309
314
|
line_extended = f"{current_line} {each}" if current_line else each
|
|
310
315
|
if (
|
|
311
|
-
stringWidth(
|
|
312
|
-
line_extended, widget_middleware.font, widget_middleware.font_size
|
|
313
|
-
)
|
|
316
|
+
stringWidth(line_extended, middleware.font, middleware.font_size)
|
|
314
317
|
<= width
|
|
315
318
|
):
|
|
316
319
|
current_line = line_extended
|
|
@@ -323,14 +326,23 @@ def get_paragraph_lines(widget: dict, widget_middleware: Text) -> List[str]:
|
|
|
323
326
|
else current_line
|
|
324
327
|
)
|
|
325
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 = []
|
|
326
341
|
for each in lines:
|
|
327
342
|
tracker = ""
|
|
328
343
|
for char in each:
|
|
329
344
|
check = tracker + char
|
|
330
|
-
if (
|
|
331
|
-
stringWidth(check, widget_middleware.font, widget_middleware.font_size)
|
|
332
|
-
> width
|
|
333
|
-
):
|
|
345
|
+
if stringWidth(check, middleware.font, middleware.font_size) > width:
|
|
334
346
|
result.append(tracker)
|
|
335
347
|
tracker = char
|
|
336
348
|
else:
|
|
@@ -342,8 +354,8 @@ def get_paragraph_lines(widget: dict, widget_middleware: Text) -> List[str]:
|
|
|
342
354
|
result
|
|
343
355
|
and stringWidth(
|
|
344
356
|
f"{each} {result[-1]}",
|
|
345
|
-
|
|
346
|
-
|
|
357
|
+
middleware.font,
|
|
358
|
+
middleware.font_size,
|
|
347
359
|
)
|
|
348
360
|
<= width
|
|
349
361
|
and NEW_LINE_SYMBOL not in result[-1]
|
|
@@ -361,6 +373,23 @@ def get_paragraph_lines(widget: dict, widget_middleware: Text) -> List[str]:
|
|
|
361
373
|
return result
|
|
362
374
|
|
|
363
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
|
+
|
|
364
393
|
def get_paragraph_auto_wrap_length(widget_middleware: Text) -> int:
|
|
365
394
|
"""Calculates the text wrap length of a paragraph field."""
|
|
366
395
|
|
|
@@ -369,3 +398,18 @@ def get_paragraph_auto_wrap_length(widget_middleware: Text) -> int:
|
|
|
369
398
|
result = min(result, len(line))
|
|
370
399
|
|
|
371
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
|
|
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
|
|
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
|