PyPDFForm 4.5.0__tar.gz → 4.5.2__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.
Files changed (71) hide show
  1. {pypdfform-4.5.0 → pypdfform-4.5.2}/PKG-INFO +1 -1
  2. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/__init__.py +1 -1
  3. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/adapter.py +8 -11
  4. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/annotations/__init__.py +1 -2
  5. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/constants.py +1 -3
  6. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/filler.py +88 -35
  7. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/font.py +4 -2
  8. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/hooks.py +15 -15
  9. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/image.py +3 -3
  10. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/middleware/base.py +15 -15
  11. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/middleware/checkbox.py +4 -4
  12. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/middleware/dropdown.py +9 -9
  13. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/middleware/signature.py +2 -3
  14. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/middleware/text.py +9 -7
  15. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/patterns.py +9 -9
  16. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/raw/__init__.py +1 -2
  17. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/raw/image.py +2 -2
  18. pypdfform-4.5.2/PyPDFForm/template.py +480 -0
  19. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/types.py +4 -4
  20. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/utils.py +38 -27
  21. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/watermark.py +192 -43
  22. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/widgets/__init__.py +3 -4
  23. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/widgets/base.py +5 -5
  24. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/widgets/dropdown.py +3 -3
  25. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/widgets/signature.py +5 -3
  26. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/wrapper.py +28 -28
  27. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm.egg-info/PKG-INFO +1 -1
  28. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_create_widget.py +0 -32
  29. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_font_widths.py +4 -1
  30. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_functional.py +1 -1
  31. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_generate_appearance_streams.py +4 -23
  32. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_need_appearances.py +4 -23
  33. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_paragraph.py +8 -48
  34. pypdfform-4.5.0/PyPDFForm/template.py +0 -342
  35. {pypdfform-4.5.0 → pypdfform-4.5.2}/LICENSE +0 -0
  36. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/annotations/base.py +0 -0
  37. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/annotations/text.py +0 -0
  38. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/ap.py +0 -0
  39. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/assets/__init__.py +0 -0
  40. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/assets/bedrock.py +0 -0
  41. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/assets/blank.py +0 -0
  42. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/coordinate.py +0 -0
  43. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/deprecation.py +0 -0
  44. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/middleware/__init__.py +0 -0
  45. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/middleware/image.py +0 -0
  46. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/middleware/radio.py +0 -0
  47. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/raw/circle.py +0 -0
  48. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/raw/ellipse.py +0 -0
  49. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/raw/line.py +0 -0
  50. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/raw/rect.py +0 -0
  51. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/raw/text.py +0 -0
  52. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/widgets/checkbox.py +0 -0
  53. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/widgets/image.py +0 -0
  54. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/widgets/radio.py +0 -0
  55. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm/widgets/text.py +0 -0
  56. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm.egg-info/SOURCES.txt +0 -0
  57. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm.egg-info/dependency_links.txt +0 -0
  58. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm.egg-info/requires.txt +0 -0
  59. {pypdfform-4.5.0 → pypdfform-4.5.2}/PyPDFForm.egg-info/top_level.txt +0 -0
  60. {pypdfform-4.5.0 → pypdfform-4.5.2}/README.md +0 -0
  61. {pypdfform-4.5.0 → pypdfform-4.5.2}/pyproject.toml +0 -0
  62. {pypdfform-4.5.0 → pypdfform-4.5.2}/setup.cfg +0 -0
  63. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_bulk_create_fields.py +0 -0
  64. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_draw_elements.py +0 -0
  65. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_dropdown.py +0 -0
  66. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_extract_middleware_attributes.py +0 -0
  67. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_fill_max_length_text_field.py +0 -0
  68. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_js.py +0 -0
  69. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_signature.py +0 -0
  70. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_use_full_widget_name.py +0 -0
  71. {pypdfform-4.5.0 → pypdfform-4.5.2}/tests/test_widget_attr_trigger.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPDFForm
3
- Version: 4.5.0
3
+ Version: 4.5.2
4
4
  Summary: The Python library for PDF forms.
5
5
  Author: Jinge Li
6
6
  License-Expression: MIT
@@ -20,7 +20,7 @@ The library supports various PDF form features, including:
20
20
  PyPDFForm aims to simplify PDF form manipulation, making it accessible to developers of all skill levels.
21
21
  """
22
22
 
23
- __version__ = "4.5.0"
23
+ __version__ = "4.5.2"
24
24
 
25
25
  from .annotations import Annotations
26
26
  from .assets.blank import BlankPage
@@ -11,7 +11,7 @@ stream before further processing.
11
11
  """
12
12
 
13
13
  from os.path import isfile
14
- from typing import Any, BinaryIO, Union
14
+ from typing import Any, BinaryIO
15
15
 
16
16
 
17
17
  def readable(obj: Any) -> bool:
@@ -32,7 +32,7 @@ def readable(obj: Any) -> bool:
32
32
 
33
33
 
34
34
  def fp_or_f_obj_or_stream_to_stream(
35
- fp_or_f_obj_or_stream: Union[bytes, str, BinaryIO],
35
+ fp_or_f_obj_or_stream: bytes | str | BinaryIO,
36
36
  ) -> bytes:
37
37
  """
38
38
  Adapt a file path, file object, or stream to a byte stream.
@@ -44,7 +44,7 @@ def fp_or_f_obj_or_stream_to_stream(
44
44
  - file-like objects with a read() method (BinaryIO)
45
45
 
46
46
  Args:
47
- fp_or_f_obj_or_stream (Union[bytes, str, BinaryIO]): The input to adapt.
47
+ fp_or_f_obj_or_stream (bytes | str | BinaryIO): The input to adapt.
48
48
  It can be a byte stream, a file path (string), or a file object.
49
49
 
50
50
  Returns:
@@ -59,17 +59,14 @@ def fp_or_f_obj_or_stream_to_stream(
59
59
  elif readable(fp_or_f_obj_or_stream):
60
60
  result = fp_or_f_obj_or_stream.read()
61
61
 
62
- elif isinstance(fp_or_f_obj_or_stream, str):
63
- if not isfile(fp_or_f_obj_or_stream):
64
- pass
65
- else:
66
- with open(fp_or_f_obj_or_stream, "rb") as _file:
67
- result = _file.read()
62
+ elif isinstance(fp_or_f_obj_or_stream, str) and isfile(fp_or_f_obj_or_stream):
63
+ with open(fp_or_f_obj_or_stream, "rb") as _file:
64
+ result = _file.read()
68
65
  return result
69
66
 
70
67
 
71
68
  def fp_or_f_obj_or_f_content_to_content(
72
- fp_or_f_obj_or_f_content: Union[str, BinaryIO],
69
+ fp_or_f_obj_or_f_content: str | BinaryIO,
73
70
  ) -> str:
74
71
  """
75
72
  Adapt a file path, file object, or file content to file content.
@@ -81,7 +78,7 @@ def fp_or_f_obj_or_f_content_to_content(
81
78
  - file-like objects with a read() method (BinaryIO)
82
79
 
83
80
  Args:
84
- fp_or_f_obj_or_f_content (Union[str, BinaryIO]): The input to adapt.
81
+ fp_or_f_obj_or_f_content (str | BinaryIO): The input to adapt.
85
82
  It can be file content, a file path (string), or a file object.
86
83
 
87
84
  Returns:
@@ -10,11 +10,10 @@ annotations, facilitating their creation and manipulation within PDF documents.
10
10
  """
11
11
 
12
12
  from dataclasses import dataclass
13
- from typing import Union
14
13
 
15
14
  from .text import TextAnnotation
16
15
 
17
- AnnotationTypes = Union[TextAnnotation]
16
+ AnnotationTypes = TextAnnotation
18
17
 
19
18
 
20
19
  @dataclass
@@ -15,8 +15,6 @@ Using constants improves code readability and maintainability by providing
15
15
  meaningful names for frequently used values and reducing the risk of typos.
16
16
  """
17
17
 
18
- from typing import Union
19
-
20
18
  from .middleware.checkbox import Checkbox
21
19
  from .middleware.dropdown import Dropdown
22
20
  from .middleware.image import Image
@@ -37,7 +35,7 @@ VERSION_IDENTIFIERS = [
37
35
  ]
38
36
  VERSION_IDENTIFIER_PREFIX = "%PDF-".encode("utf-8")
39
37
 
40
- WIDGET_TYPES = Union[Text, Checkbox, Radio, Dropdown, Signature, Image]
38
+ WIDGET_TYPES = Text | Checkbox | Radio | Dropdown | Signature | Image
41
39
 
42
40
  DEPRECATION_NOTICE = "{} will be deprecated soon. Use {} instead."
43
41
 
@@ -9,7 +9,7 @@ supports flattening the filled form to prevent further modifications.
9
9
  """
10
10
 
11
11
  from io import BytesIO
12
- from typing import Dict, Union, cast
12
+ from typing import Dict, cast
13
13
 
14
14
  from pypdf import PdfReader, PdfWriter
15
15
  from pypdf.generic import DictionaryObject
@@ -31,7 +31,7 @@ from .watermark import create_watermarks_and_draw, merge_watermarks_with_pdf
31
31
 
32
32
 
33
33
  def signature_image_handler(
34
- widget: dict, middleware: Union[Signature, Image], images_to_draw: list
34
+ widget: dict, middleware: Signature | Image, images_to_draw: list
35
35
  ) -> bool:
36
36
  """Handles signature and image widgets by extracting image data and preparing it for drawing.
37
37
 
@@ -43,7 +43,7 @@ def signature_image_handler(
43
43
 
44
44
  Args:
45
45
  widget (dict): The widget dictionary representing the signature or image field.
46
- middleware (Union[Signature, Image]): The middleware object containing the image data and properties.
46
+ middleware (Signature | Image): The middleware object containing the image data and properties.
47
47
  images_to_draw (list): A list to store image data for drawing.
48
48
 
49
49
  Returns:
@@ -70,6 +70,81 @@ def signature_image_handler(
70
70
  return any_image_to_draw
71
71
 
72
72
 
73
+ def update_widget(
74
+ annot: DictionaryObject,
75
+ widget: WIDGET_TYPES,
76
+ key: str,
77
+ radio_button_tracker: Dict[str, int],
78
+ images_to_draw_page: list,
79
+ need_appearances: bool,
80
+ flatten: bool,
81
+ ) -> bool:
82
+ """Updates a single widget's value and handles its properties.
83
+
84
+ This function updates the value of a single PDF form widget based on its type. It also
85
+ handles widget flattening and prepares images or signatures for drawing if applicable.
86
+
87
+ Args:
88
+ annot (DictionaryObject): The annotation object representing the widget in the PDF.
89
+ widget (WIDGET_TYPES): The widget middleware object containing the new value.
90
+ key (str): The unique key identifying the widget.
91
+ radio_button_tracker (Dict[str, int]): A tracker for radio button groups to manage their indices.
92
+ images_to_draw_page (list): A list to store image data for the current page.
93
+ need_appearances (bool): If True, skips updating appearance streams for certain fields.
94
+ flatten (bool): Whether to flatten the widget to prevent further editing.
95
+
96
+ Returns:
97
+ bool: True if an image or signature was prepared for drawing, False otherwise.
98
+ """
99
+ if flatten:
100
+ flatten_field(annot, True)
101
+ if widget.value is None:
102
+ return False
103
+
104
+ any_image_to_draw = False
105
+ if isinstance(widget, (Signature, Image)):
106
+ any_image_to_draw = signature_image_handler(annot, widget, images_to_draw_page)
107
+ elif type(widget) is Checkbox:
108
+ update_checkbox_value(annot, widget.value)
109
+ elif isinstance(widget, Radio):
110
+ if key not in radio_button_tracker:
111
+ radio_button_tracker[key] = 0
112
+ radio_button_tracker[key] += 1
113
+ if widget.value == radio_button_tracker[key] - 1:
114
+ update_radio_value(annot)
115
+ elif isinstance(widget, Dropdown):
116
+ update_dropdown_value(annot, widget, need_appearances)
117
+ elif isinstance(widget, Text):
118
+ update_text_value(annot, widget, need_appearances)
119
+
120
+ return any_image_to_draw
121
+
122
+
123
+ def handle_image_drawing(
124
+ result: bytes,
125
+ images_to_draw: Dict[int, list],
126
+ ) -> bytes:
127
+ """Merges prepared images and signatures with the filled PDF.
128
+
129
+ This function takes the filled PDF and a dictionary of images to draw (from signatures
130
+ or image fields) and merges them into the PDF as watermarks.
131
+
132
+ Args:
133
+ result (bytes): The filled PDF as bytes.
134
+ images_to_draw (Dict[int, list]): A dictionary mapping page numbers to lists of image data.
135
+
136
+ Returns:
137
+ bytes: The PDF with images and signatures merged.
138
+ """
139
+ images = []
140
+ for page, elements in images_to_draw.items():
141
+ images.extend(
142
+ [{"page_number": page, "type": "image", **element} for element in elements]
143
+ )
144
+
145
+ return merge_watermarks_with_pdf(result, create_watermarks_and_draw(result, images))
146
+
147
+
73
148
  def fill(
74
149
  template: bytes,
75
150
  widgets: Dict[str, WIDGET_TYPES],
@@ -101,7 +176,6 @@ def fill(
101
176
  The image drawn stream is only returned if there are any image or signature widgets
102
177
  in the form.
103
178
  """
104
- # pylint: disable=R0912
105
179
  pdf = PdfReader(stream_to_io(template))
106
180
  out = PdfWriter()
107
181
  out.append(pdf)
@@ -120,28 +194,15 @@ def fill(
120
194
  if widget is None:
121
195
  continue
122
196
 
123
- # flatten all
124
- if flatten:
125
- flatten_field(annot, True)
126
- if widget.value is None:
127
- continue
128
-
129
- if isinstance(widgets[key], (Signature, Image)):
130
- any_image_to_draw |= signature_image_handler(
131
- annot, widgets[key], images_to_draw[page_num + 1]
132
- )
133
- elif type(widget) is Checkbox:
134
- update_checkbox_value(annot, widget.value)
135
- elif isinstance(widget, Radio):
136
- if key not in radio_button_tracker:
137
- radio_button_tracker[key] = 0
138
- radio_button_tracker[key] += 1
139
- if widget.value == radio_button_tracker[key] - 1:
140
- update_radio_value(annot)
141
- elif isinstance(widget, Dropdown):
142
- update_dropdown_value(annot, widget, need_appearances)
143
- elif isinstance(widget, Text):
144
- update_text_value(annot, widget, need_appearances)
197
+ any_image_to_draw |= update_widget(
198
+ annot,
199
+ widget,
200
+ key,
201
+ radio_button_tracker,
202
+ images_to_draw[page_num + 1],
203
+ need_appearances,
204
+ flatten,
205
+ )
145
206
 
146
207
  with BytesIO() as f:
147
208
  out.write(f)
@@ -151,12 +212,4 @@ def fill(
151
212
  if not any_image_to_draw:
152
213
  return result, None
153
214
 
154
- images = []
155
- for page, elements in images_to_draw.items():
156
- images.extend(
157
- [{"page_number": page, "type": "image", **element} for element in elements]
158
- )
159
-
160
- return result, (
161
- merge_watermarks_with_pdf(result, create_watermarks_and_draw(result, images))
162
- )
215
+ return result, handle_image_drawing(result, images_to_draw)
@@ -27,6 +27,7 @@ from .constants import (DEFAULT_ASSUMED_GLYPH_WIDTH, DR, EM_TO_PDF_FACTOR,
27
27
  MissingWidth, Resources, Subtype, TrueType, Type,
28
28
  Widths, WinAnsiEncoding)
29
29
  from .utils import stream_to_io
30
+ from .watermark import get_watermark_with_font
30
31
 
31
32
 
32
33
  @lru_cache
@@ -144,7 +145,7 @@ def compute_font_glyph_widths(ttf_file: BytesIO, missing_width: float) -> list[f
144
145
 
145
146
 
146
147
  def register_font_acroform(
147
- pdf: bytes, ttf_stream: bytes, need_appearances: bool
148
+ pdf: bytes, font_name: str, ttf_stream: bytes, need_appearances: bool
148
149
  ) -> tuple:
149
150
  """
150
151
  Registers a TrueType font within the PDF's AcroForm dictionary.
@@ -156,6 +157,7 @@ def register_font_acroform(
156
157
  Args:
157
158
  pdf (bytes): The PDF file data as bytes. This is the PDF document that
158
159
  will be modified to include the new font.
160
+ font_name (str): The name of the font being registered.
159
161
  ttf_stream (bytes): The font file data in TTF format as bytes. This is the
160
162
  raw data of the TrueType font file.
161
163
  need_appearances (bool): If True, attempts to retrieve existing font parameters
@@ -175,7 +177,7 @@ def register_font_acroform(
175
177
  font_dict_params = {}
176
178
  if need_appearances:
177
179
  font_descriptor_params, font_dict_params = get_additional_font_params(
178
- pdf, base_font_name
180
+ get_watermark_with_font(font_name), base_font_name
179
181
  )
180
182
 
181
183
  font_file_stream = StreamObject()
@@ -11,7 +11,7 @@ filling process, allowing for customization of the form's appearance and behavio
11
11
 
12
12
  import sys
13
13
  from io import BytesIO
14
- from typing import TextIO, Union, cast
14
+ from typing import TextIO, cast
15
15
 
16
16
  from pypdf import PdfReader, PdfWriter
17
17
  from pypdf.generic import (ArrayObject, DictionaryObject, FloatObject,
@@ -393,7 +393,7 @@ def update_field_hidden(annot: DictionaryObject, val: bool) -> None:
393
393
 
394
394
 
395
395
  def _update_field_javascript(
396
- annot: DictionaryObject, trigger_event: str, val: Union[str, TextIO]
396
+ annot: DictionaryObject, trigger_event: str, val: str | TextIO
397
397
  ) -> None:
398
398
  """
399
399
  Updates a specific JavaScript action for a form field annotation.
@@ -405,7 +405,7 @@ def _update_field_javascript(
405
405
  annot (DictionaryObject): The annotation dictionary for the form field.
406
406
  trigger_event (str): The event that triggers the JavaScript action
407
407
  (e.g., E for enter, X for exit, D for down, U for up, Fo for focus, Bl for blur).
408
- val (Union[str, TextIO]): The JavaScript code to execute. Can be a string
408
+ val (str | TextIO): The JavaScript code to execute. Can be a string
409
409
  containing the code or a file-like object/path to a file containing the code.
410
410
  """
411
411
  if AA not in annot:
@@ -424,7 +424,7 @@ def _update_field_javascript(
424
424
 
425
425
 
426
426
  def update_field_on_hovered_over_javascript(
427
- annot: DictionaryObject, val: Union[str, TextIO]
427
+ annot: DictionaryObject, val: str | TextIO
428
428
  ) -> None:
429
429
  """
430
430
  Updates the JavaScript action triggered when the mouse enters the field area.
@@ -434,13 +434,13 @@ def update_field_on_hovered_over_javascript(
434
434
 
435
435
  Args:
436
436
  annot (DictionaryObject): The annotation dictionary for the form field.
437
- val (Union[str, TextIO]): The JavaScript code to execute.
437
+ val (str | TextIO): The JavaScript code to execute.
438
438
  """
439
439
  _update_field_javascript(annot, E, val)
440
440
 
441
441
 
442
442
  def update_field_on_hovered_off_javascript(
443
- annot: DictionaryObject, val: Union[str, TextIO]
443
+ annot: DictionaryObject, val: str | TextIO
444
444
  ) -> None:
445
445
  """
446
446
  Updates the JavaScript action triggered when the mouse exits the field area.
@@ -450,13 +450,13 @@ def update_field_on_hovered_off_javascript(
450
450
 
451
451
  Args:
452
452
  annot (DictionaryObject): The annotation dictionary for the form field.
453
- val (Union[str, TextIO]): The JavaScript code to execute.
453
+ val (str | TextIO): The JavaScript code to execute.
454
454
  """
455
455
  _update_field_javascript(annot, X, val)
456
456
 
457
457
 
458
458
  def update_field_on_mouse_pressed_javascript(
459
- annot: DictionaryObject, val: Union[str, TextIO]
459
+ annot: DictionaryObject, val: str | TextIO
460
460
  ) -> None:
461
461
  """
462
462
  Updates the JavaScript action triggered when the mouse button is pressed down inside the field.
@@ -466,13 +466,13 @@ def update_field_on_mouse_pressed_javascript(
466
466
 
467
467
  Args:
468
468
  annot (DictionaryObject): The annotation dictionary for the form field.
469
- val (Union[str, TextIO]): The JavaScript code to execute.
469
+ val (str | TextIO): The JavaScript code to execute.
470
470
  """
471
471
  _update_field_javascript(annot, D, val)
472
472
 
473
473
 
474
474
  def update_field_on_mouse_released_javascript(
475
- annot: DictionaryObject, val: Union[str, TextIO]
475
+ annot: DictionaryObject, val: str | TextIO
476
476
  ) -> None:
477
477
  """
478
478
  Updates the JavaScript action triggered when the mouse button is released inside the field.
@@ -482,13 +482,13 @@ def update_field_on_mouse_released_javascript(
482
482
 
483
483
  Args:
484
484
  annot (DictionaryObject): The annotation dictionary for the form field.
485
- val (Union[str, TextIO]): The JavaScript code to execute.
485
+ val (str | TextIO): The JavaScript code to execute.
486
486
  """
487
487
  _update_field_javascript(annot, U, val)
488
488
 
489
489
 
490
490
  def update_field_on_focused_javascript(
491
- annot: DictionaryObject, val: Union[str, TextIO]
491
+ annot: DictionaryObject, val: str | TextIO
492
492
  ) -> None:
493
493
  """
494
494
  Updates the JavaScript action triggered when the field receives input focus.
@@ -498,13 +498,13 @@ def update_field_on_focused_javascript(
498
498
 
499
499
  Args:
500
500
  annot (DictionaryObject): The annotation dictionary for the form field.
501
- val (Union[str, TextIO]): The JavaScript code to execute.
501
+ val (str | TextIO): The JavaScript code to execute.
502
502
  """
503
503
  _update_field_javascript(annot, Fo, val)
504
504
 
505
505
 
506
506
  def update_field_on_blurred_javascript(
507
- annot: DictionaryObject, val: Union[str, TextIO]
507
+ annot: DictionaryObject, val: str | TextIO
508
508
  ) -> None:
509
509
  """
510
510
  Updates the JavaScript action triggered when the field loses input focus.
@@ -514,6 +514,6 @@ def update_field_on_blurred_javascript(
514
514
 
515
515
  Args:
516
516
  annot (DictionaryObject): The annotation dictionary for the form field.
517
- val (Union[str, TextIO]): The JavaScript code to execute.
517
+ val (str | TextIO): The JavaScript code to execute.
518
518
  """
519
519
  _update_field_javascript(annot, Bl, val)
@@ -8,14 +8,14 @@ account whether to preserve the aspect ratio.
8
8
  """
9
9
 
10
10
  from io import BytesIO
11
- from typing import Tuple, Union
11
+ from typing import Tuple
12
12
 
13
13
  from PIL import Image
14
14
 
15
15
  from .constants import Rect
16
16
 
17
17
 
18
- def rotate_image(image_stream: bytes, rotation: Union[float, int]) -> bytes:
18
+ def rotate_image(image_stream: bytes, rotation: float | int) -> bytes:
19
19
  """
20
20
  Rotates an image by a specified angle in degrees.
21
21
 
@@ -26,7 +26,7 @@ def rotate_image(image_stream: bytes, rotation: Union[float, int]) -> bytes:
26
26
 
27
27
  Args:
28
28
  image_stream (bytes): The image data as bytes.
29
- rotation (Union[float, int]): The rotation angle in degrees. Positive values
29
+ rotation (float | int): The rotation angle in degrees. Positive values
30
30
  rotate the image counterclockwise, while negative values rotate it clockwise.
31
31
 
32
32
  Returns:
@@ -8,7 +8,7 @@ common attributes and methods for all form widgets, such as name, value,
8
8
  and schema definition.
9
9
  """
10
10
 
11
- from typing import Any, List, TextIO, Union
11
+ from typing import Any, List, Optional, TextIO
12
12
 
13
13
 
14
14
  class Widget:
@@ -49,25 +49,25 @@ class Widget:
49
49
  super().__init__()
50
50
  self._name = name
51
51
  self._value = value
52
- self.tooltip: str = None
53
- self.readonly: bool = None
54
- self.required: bool = None
55
- self.hidden: bool = None
52
+ self.tooltip: Optional[str] = None
53
+ self.readonly: Optional[bool] = None
54
+ self.required: Optional[bool] = None
55
+ self.hidden: Optional[bool] = None
56
56
  self.hooks_to_trigger: list = []
57
57
 
58
58
  # coordinate & dimension
59
- self.x: Union[float, List[float]] = None
60
- self.y: Union[float, List[float]] = None
61
- self.width: Union[float, List[float]] = None
62
- self.height: Union[float, List[float]] = None
59
+ self.x: Optional[float | List[float]] = None
60
+ self.y: Optional[float | List[float]] = None
61
+ self.width: Optional[float | List[float]] = None
62
+ self.height: Optional[float | List[float]] = None
63
63
 
64
64
  # javascript
65
- self.on_hovered_over_javascript: Union[str, TextIO] = None
66
- self.on_hovered_off_javascript: Union[str, TextIO] = None
67
- self.on_mouse_pressed_javascript: Union[str, TextIO] = None
68
- self.on_mouse_released_javascript: Union[str, TextIO] = None
69
- self.on_focused_javascript: Union[str, TextIO] = None
70
- self.on_blurred_javascript: Union[str, TextIO] = None
65
+ self.on_hovered_over_javascript: Optional[str | TextIO] = None
66
+ self.on_hovered_off_javascript: Optional[str | TextIO] = None
67
+ self.on_mouse_pressed_javascript: Optional[str | TextIO] = None
68
+ self.on_mouse_released_javascript: Optional[str | TextIO] = None
69
+ self.on_focused_javascript: Optional[str | TextIO] = None
70
+ self.on_blurred_javascript: Optional[str | TextIO] = None
71
71
 
72
72
  def __setattr__(self, name: str, value: Any) -> None:
73
73
  """
@@ -6,7 +6,7 @@ This module defines the Checkbox class, which is a subclass of the
6
6
  Widget class. It represents a checkbox form field in a PDF document.
7
7
  """
8
8
 
9
- from typing import Any, Union
9
+ from typing import Any, Optional
10
10
 
11
11
  from .base import Widget
12
12
 
@@ -42,7 +42,7 @@ class Checkbox(Widget):
42
42
  )
43
43
  super().__init__(name, value)
44
44
 
45
- self.size: float = None
45
+ self.size: Optional[float] = None
46
46
 
47
47
  def __setattr__(self, name: str, value: Any) -> None:
48
48
  """
@@ -75,7 +75,7 @@ class Checkbox(Widget):
75
75
  return {"type": "boolean", **super().schema_definition}
76
76
 
77
77
  @property
78
- def sample_value(self) -> Union[bool, int]:
78
+ def sample_value(self) -> bool | int:
79
79
  """
80
80
  Returns a sample value for the checkbox.
81
81
 
@@ -83,6 +83,6 @@ class Checkbox(Widget):
83
83
  checkbox field.
84
84
 
85
85
  Returns:
86
- Union[bool, int]: A sample value for the checkbox.
86
+ bool | int: A sample value for the checkbox.
87
87
  """
88
88
  return True
@@ -6,7 +6,7 @@ This module defines the Dropdown class, which is a subclass of the
6
6
  Widget class. It represents a dropdown form field in a PDF document.
7
7
  """
8
8
 
9
- from typing import Union
9
+ from typing import Optional
10
10
 
11
11
  from .base import Widget
12
12
 
@@ -54,10 +54,10 @@ class Dropdown(Widget):
54
54
  )
55
55
  super().__init__(name, value)
56
56
 
57
- self.font: str = None
58
- self.font_size: float = None
59
- self.font_color: tuple = None
60
- self.choices: Union[tuple, list] = None
57
+ self.font: Optional[str] = None
58
+ self.font_size: Optional[float] = None
59
+ self.font_color: Optional[tuple] = None
60
+ self.choices: Optional[tuple | list] = None
61
61
 
62
62
  @property
63
63
  def value(self) -> int:
@@ -70,7 +70,7 @@ class Dropdown(Widget):
70
70
  return super().value
71
71
 
72
72
  @value.setter
73
- def value(self, value: Union[str, int]) -> None:
73
+ def value(self, value: str | int) -> None:
74
74
  """
75
75
  Sets the value of the dropdown.
76
76
 
@@ -79,7 +79,7 @@ class Dropdown(Widget):
79
79
  added to the choices, and its new index is used.
80
80
 
81
81
  Args:
82
- value (Union[str, int]): The value to set. Can be a string
82
+ value (str | int): The value to set. Can be a string
83
83
  (option text) or an integer (index).
84
84
  """
85
85
  if isinstance(value, str):
@@ -91,7 +91,7 @@ class Dropdown(Widget):
91
91
 
92
92
  self._value = value
93
93
 
94
- def _get_option_index(self, value: str) -> Union[int, None]:
94
+ def _get_option_index(self, value: str) -> int | None:
95
95
  """
96
96
  Gets the index of a given option value in the dropdown's choices.
97
97
 
@@ -99,7 +99,7 @@ class Dropdown(Widget):
99
99
  value (str): The option value to search for.
100
100
 
101
101
  Returns:
102
- Union[int, None]: The index of the option if found, otherwise None.
102
+ int | None: The index of the option if found, otherwise None.
103
103
  """
104
104
  for i, each in enumerate(self.choices):
105
105
  if value == each:
@@ -8,7 +8,6 @@ allowing users to add their signature as an image.
8
8
  """
9
9
 
10
10
  from os.path import expanduser
11
- from typing import Union
12
11
 
13
12
  from ..adapter import fp_or_f_obj_or_stream_to_stream
14
13
  from .base import Widget
@@ -55,7 +54,7 @@ class Signature(Widget):
55
54
  return expanduser("~/Downloads/sample_image.jpg")
56
55
 
57
56
  @property
58
- def stream(self) -> Union[bytes, None]:
57
+ def stream(self) -> bytes | None:
59
58
  """
60
59
  Returns the stream of the signature image.
61
60
 
@@ -64,7 +63,7 @@ class Signature(Widget):
64
63
  as a stream of bytes.
65
64
 
66
65
  Returns:
67
- Union[bytes, None]: The stream of the signature image.
66
+ bytes | None: The stream of the signature image.
68
67
  """
69
68
  return (
70
69
  fp_or_f_obj_or_stream_to_stream(self.value)
@@ -7,6 +7,8 @@ Widget class. It represents a text field form field in a PDF document,
7
7
  allowing users to enter text.
8
8
  """
9
9
 
10
+ from typing import Optional
11
+
10
12
  from .base import Widget
11
13
 
12
14
 
@@ -56,13 +58,13 @@ class Text(Widget):
56
58
  )
57
59
  super().__init__(name, value)
58
60
 
59
- self.font: str = None
60
- self.font_size: float = None
61
- self.font_color: tuple = None
62
- self.comb: bool = None
63
- self.alignment: int = None
64
- self.multiline: bool = None
65
- self.max_length: int = None
61
+ self.font: Optional[str] = None
62
+ self.font_size: Optional[float] = None
63
+ self.font_color: Optional[tuple] = None
64
+ self.comb: Optional[bool] = None
65
+ self.alignment: Optional[int] = None
66
+ self.multiline: Optional[bool] = None
67
+ self.max_length: Optional[int] = None
66
68
 
67
69
  @property
68
70
  def value(self) -> str: