PyPDFForm 1.4.23__tar.gz → 1.4.25__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.

Files changed (46) hide show
  1. {pypdfform-1.4.23 → pypdfform-1.4.25}/PKG-INFO +1 -1
  2. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/__init__.py +1 -1
  3. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/constants.py +6 -1
  4. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/filler.py +90 -61
  5. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/font.py +24 -15
  6. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/patterns.py +13 -6
  7. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/template.py +67 -23
  8. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/utils.py +4 -1
  9. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm.egg-info/PKG-INFO +1 -1
  10. {pypdfform-1.4.23 → pypdfform-1.4.25}/LICENSE +0 -0
  11. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/adapter.py +0 -0
  12. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/coordinate.py +0 -0
  13. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/image.py +0 -0
  14. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/middleware/__init__.py +0 -0
  15. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/middleware/base.py +0 -0
  16. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/middleware/checkbox.py +0 -0
  17. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/middleware/dropdown.py +0 -0
  18. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/middleware/image.py +0 -0
  19. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/middleware/radio.py +0 -0
  20. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/middleware/signature.py +0 -0
  21. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/middleware/text.py +0 -0
  22. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/watermark.py +0 -0
  23. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/widgets/__init__.py +0 -0
  24. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/widgets/base.py +0 -0
  25. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/widgets/checkbox.py +0 -0
  26. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/widgets/dropdown.py +0 -0
  27. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/widgets/text.py +0 -0
  28. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm/wrapper.py +0 -0
  29. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm.egg-info/SOURCES.txt +0 -0
  30. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm.egg-info/dependency_links.txt +0 -0
  31. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm.egg-info/requires.txt +0 -0
  32. {pypdfform-1.4.23 → pypdfform-1.4.25}/PyPDFForm.egg-info/top_level.txt +0 -0
  33. {pypdfform-1.4.23 → pypdfform-1.4.25}/README.md +0 -0
  34. {pypdfform-1.4.23 → pypdfform-1.4.25}/setup.cfg +0 -0
  35. {pypdfform-1.4.23 → pypdfform-1.4.25}/setup.py +0 -0
  36. {pypdfform-1.4.23 → pypdfform-1.4.25}/tests/test_create_widget.py +0 -0
  37. {pypdfform-1.4.23 → pypdfform-1.4.25}/tests/test_dropdown.py +0 -0
  38. {pypdfform-1.4.23 → pypdfform-1.4.25}/tests/test_dropdown_simple.py +0 -0
  39. {pypdfform-1.4.23 → pypdfform-1.4.25}/tests/test_fill_max_length_text_field.py +0 -0
  40. {pypdfform-1.4.23 → pypdfform-1.4.25}/tests/test_fill_max_length_text_field_simple.py +0 -0
  41. {pypdfform-1.4.23 → pypdfform-1.4.25}/tests/test_functional.py +0 -0
  42. {pypdfform-1.4.23 → pypdfform-1.4.25}/tests/test_functional_simple.py +0 -0
  43. {pypdfform-1.4.23 → pypdfform-1.4.25}/tests/test_paragraph.py +0 -0
  44. {pypdfform-1.4.23 → pypdfform-1.4.25}/tests/test_paragraph_simple.py +0 -0
  45. {pypdfform-1.4.23 → pypdfform-1.4.25}/tests/test_preview.py +0 -0
  46. {pypdfform-1.4.23 → pypdfform-1.4.25}/tests/test_signature.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyPDFForm
3
- Version: 1.4.23
3
+ Version: 1.4.25
4
4
  Summary: The Python library for PDF forms.
5
5
  Home-page: https://github.com/chinapandaman/PyPDFForm
6
6
  Author: Jinge Li
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Contains any object users might need."""
3
3
 
4
- __version__ = "1.4.23"
4
+ __version__ = "1.4.25"
5
5
 
6
6
  from .wrapper import FormWrapper, PdfWrapper, PyPDFForm
@@ -40,7 +40,7 @@ Ff = "/Ff"
40
40
  Tx = "/Tx"
41
41
  V = "/V"
42
42
  AP = "/AP"
43
- D = "/D"
43
+ N = "/N"
44
44
  Sig = "/Sig"
45
45
  DA = "/DA"
46
46
  Btn = "/Btn"
@@ -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, _widgets in get_widgets_by_page(template_stream).items():
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
- text_watermarks.append(b"")
50
- image_watermarks.append(b"")
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
- _to_draw = x = y = None
124
+ to_draw = x = y = None
55
125
 
56
126
  if isinstance(widgets[key], (Checkbox, Radio)):
57
- font_size = (
58
- checkbox_radio_font_size(_widget)
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
- stream = widgets[key].stream
74
- if stream is not None:
75
- any_image_to_draw = True
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
- widgets[key].text_line_x_coordinates = get_text_line_x_coordinates(
91
- _widget, widgets[key]
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
- _to_draw is not None,
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
- _to_draw,
149
+ to_draw,
108
150
  x,
109
151
  y,
110
152
  ]
111
153
  )
112
154
 
113
- for page, texts in texts_to_draw.items():
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
- for page, images in images_to_draw.items():
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 auto_detect_font(widget: dict) -> str:
37
- """Returns the font of the text field if it is one of the standard fonts."""
38
-
39
- result = DEFAULT_FONT
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 result
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]:
@@ -5,8 +5,8 @@ from pypdf.generic import (DictionaryObject, NameObject, NumberObject,
5
5
  TextStringObject)
6
6
 
7
7
  from .constants import (AP, AS, CA, DA, FT, IMAGE_FIELD_IDENTIFIER, JS, MK,
8
- READ_ONLY, A, Btn, Ch, D, Ff, Off, Opt, Parent, Q, Sig,
9
- Subtype, T, Tx, V, Widget)
8
+ READ_ONLY, A, Btn, Ch, Ff, N, Off, Opt, Parent, Q, Sig,
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
- ({FT: Btn},),
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
- ({Parent: {FT: Btn}},),
58
+ (
59
+ {Parent: {FT: Btn}},
60
+ {AS: (Yes, Off)},
61
+ ),
55
62
  Radio,
56
63
  ),
57
64
  ]
@@ -89,7 +96,7 @@ BUTTON_STYLE_PATTERNS = [
89
96
  def simple_update_checkbox_value(annot: DictionaryObject, check: bool = False) -> None:
90
97
  """Patterns to update values for checkbox annotations."""
91
98
 
92
- for each in annot[AP][D]: # noqa
99
+ for each in annot[AP][N]: # noqa
93
100
  if (check and str(each) != Off) or (not check and str(each) == Off):
94
101
  annot[NameObject(AS)] = NameObject(each)
95
102
  annot[NameObject(V)] = NameObject(each)
@@ -99,7 +106,7 @@ def simple_update_checkbox_value(annot: DictionaryObject, check: bool = False) -
99
106
  def simple_update_radio_value(annot: DictionaryObject) -> None:
100
107
  """Patterns to update values for radio annotations."""
101
108
 
102
- for each in annot[AP][D]: # noqa
109
+ for each in annot[AP][N]: # noqa
103
110
  if str(each) != Off:
104
111
  annot[NameObject(AS)] = NameObject(each)
105
112
  annot[NameObject(Parent)][NameObject(V)] = NameObject(each) # noqa
@@ -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, MULTILINE, NEW_LINE_SYMBOL,
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
- widgets[key].font_size = get_text_field_font_size(_widget) or (
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 is_text_multiline(_widget)
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 is_text_multiline(_widget) and widgets[key].text_wrap_length is None:
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 get_paragraph_lines(widget: dict, widget_middleware: Text) -> List[str]:
294
- """Splits the paragraph field's text to a list of lines."""
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
- widget_middleware.font,
346
- widget_middleware.font_size,
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
- result = pattern[key] == value
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyPDFForm
3
- Version: 1.4.23
3
+ Version: 1.4.25
4
4
  Summary: The Python library for PDF forms.
5
5
  Home-page: https://github.com/chinapandaman/PyPDFForm
6
6
  Author: Jinge Li
File without changes
File without changes
File without changes
File without changes