PyPDFForm 1.4.35__tar.gz → 1.4.37__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 (48) hide show
  1. {pypdfform-1.4.35 → pypdfform-1.4.37}/PKG-INFO +2 -1
  2. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/__init__.py +1 -1
  3. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/filler.py +1 -1
  4. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/middleware/base.py +13 -1
  5. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/middleware/text.py +17 -0
  6. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/patterns.py +9 -0
  7. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/template.py +47 -4
  8. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/wrapper.py +17 -1
  9. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm.egg-info/PKG-INFO +2 -1
  10. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm.egg-info/SOURCES.txt +1 -0
  11. {pypdfform-1.4.35 → pypdfform-1.4.37}/setup.py +1 -0
  12. pypdfform-1.4.37/tests/test_fill_method.py +141 -0
  13. {pypdfform-1.4.35 → pypdfform-1.4.37}/tests/test_functional.py +33 -0
  14. {pypdfform-1.4.35 → pypdfform-1.4.37}/LICENSE +0 -0
  15. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/adapter.py +0 -0
  16. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/constants.py +0 -0
  17. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/coordinate.py +0 -0
  18. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/font.py +0 -0
  19. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/image.py +0 -0
  20. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/middleware/__init__.py +0 -0
  21. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/middleware/checkbox.py +0 -0
  22. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/middleware/dropdown.py +0 -0
  23. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/middleware/image.py +0 -0
  24. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/middleware/radio.py +0 -0
  25. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/middleware/signature.py +0 -0
  26. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/utils.py +0 -0
  27. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/watermark.py +0 -0
  28. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/widgets/__init__.py +0 -0
  29. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/widgets/base.py +0 -0
  30. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/widgets/checkbox.py +0 -0
  31. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/widgets/dropdown.py +0 -0
  32. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm/widgets/text.py +0 -0
  33. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm.egg-info/dependency_links.txt +0 -0
  34. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm.egg-info/requires.txt +0 -0
  35. {pypdfform-1.4.35 → pypdfform-1.4.37}/PyPDFForm.egg-info/top_level.txt +0 -0
  36. {pypdfform-1.4.35 → pypdfform-1.4.37}/README.md +0 -0
  37. {pypdfform-1.4.35 → pypdfform-1.4.37}/setup.cfg +0 -0
  38. {pypdfform-1.4.35 → pypdfform-1.4.37}/tests/test_adobe_mode.py +0 -0
  39. {pypdfform-1.4.35 → pypdfform-1.4.37}/tests/test_create_widget.py +0 -0
  40. {pypdfform-1.4.35 → pypdfform-1.4.37}/tests/test_dropdown.py +0 -0
  41. {pypdfform-1.4.35 → pypdfform-1.4.37}/tests/test_dropdown_simple.py +0 -0
  42. {pypdfform-1.4.35 → pypdfform-1.4.37}/tests/test_fill_max_length_text_field.py +0 -0
  43. {pypdfform-1.4.35 → pypdfform-1.4.37}/tests/test_fill_max_length_text_field_simple.py +0 -0
  44. {pypdfform-1.4.35 → pypdfform-1.4.37}/tests/test_functional_simple.py +0 -0
  45. {pypdfform-1.4.35 → pypdfform-1.4.37}/tests/test_paragraph.py +0 -0
  46. {pypdfform-1.4.35 → pypdfform-1.4.37}/tests/test_paragraph_simple.py +0 -0
  47. {pypdfform-1.4.35 → pypdfform-1.4.37}/tests/test_preview.py +0 -0
  48. {pypdfform-1.4.35 → pypdfform-1.4.37}/tests/test_signature.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyPDFForm
3
- Version: 1.4.35
3
+ Version: 1.4.37
4
4
  Summary: The Python library for PDF forms.
5
5
  Home-page: https://github.com/chinapandaman/PyPDFForm
6
6
  Author: Jinge Li
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
16
17
  Classifier: License :: OSI Approved :: MIT License
17
18
  Classifier: Operating System :: OS Independent
18
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Contains any object users might need."""
3
3
 
4
- __version__ = "1.4.35"
4
+ __version__ = "1.4.37"
5
5
 
6
6
  from .wrapper import FormWrapper, PdfWrapper
7
7
 
@@ -128,7 +128,7 @@ def fill(
128
128
  widget_dict, widgets[key], radio_button_tracker
129
129
  )
130
130
  elif isinstance(widgets[key], (Signature, Image)):
131
- any_image_to_draw = signature_image_handler(
131
+ any_image_to_draw |= signature_image_handler(
132
132
  widget_dict, widgets[key], images_to_draw[page]
133
133
  )
134
134
  else:
@@ -16,7 +16,7 @@ class Widget:
16
16
 
17
17
  super().__init__()
18
18
  self._name = name
19
- self.value = value
19
+ self._value = value
20
20
 
21
21
  @property
22
22
  def name(self) -> str:
@@ -24,6 +24,18 @@ class Widget:
24
24
 
25
25
  return self._name
26
26
 
27
+ @property
28
+ def value(self) -> Any:
29
+ """Value to fill for the widget."""
30
+
31
+ return self._value
32
+
33
+ @value.setter
34
+ def value(self, value: Any) -> None:
35
+ """Sets value to fill for the widget."""
36
+
37
+ self._value = value
38
+
27
39
  @property
28
40
  def schema_definition(self) -> dict:
29
41
  """Json schema definition of the widget."""
@@ -1,6 +1,8 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Contains text middleware."""
3
3
 
4
+ from typing import Any
5
+
4
6
  from .base import Widget
5
7
 
6
8
 
@@ -27,6 +29,21 @@ class Text(Widget):
27
29
  self.text_line_x_coordinates = None
28
30
  self.preview = False
29
31
 
32
+ @property
33
+ def value(self) -> Any:
34
+ """Value to fill for the text field."""
35
+
36
+ if isinstance(self._value, (int, float)):
37
+ return str(self._value)
38
+
39
+ return self._value
40
+
41
+ @value.setter
42
+ def value(self, value: str) -> None:
43
+ """Sets value to fill for the text field."""
44
+
45
+ self._value = value
46
+
30
47
  @property
31
48
  def schema_definition(self) -> dict:
32
49
  """Json schema definition of the text field."""
@@ -160,6 +160,15 @@ def simple_flatten_generic(annot: DictionaryObject) -> None:
160
160
  )
161
161
 
162
162
 
163
+ def update_annotation_name(annot: DictionaryObject, val: str) -> None:
164
+ """Patterns to update the name of an annotation."""
165
+
166
+ if Parent in annot and T not in annot:
167
+ annot[NameObject(Parent)][NameObject(T)] = TextStringObject(val) # noqa
168
+ else:
169
+ annot[NameObject(T)] = TextStringObject(val) # noqa
170
+
171
+
163
172
  def update_created_text_field_alignment(annot: DictionaryObject, val: int) -> None:
164
173
  """Patterns to update text alignment for text annotations created by the library."""
165
174
 
@@ -2,14 +2,16 @@
2
2
  """Contains helpers for generic template related processing."""
3
3
 
4
4
  from functools import lru_cache
5
+ from io import BytesIO
5
6
  from sys import maxsize
6
- from typing import Dict, List, Tuple, Union
7
+ from typing import Dict, List, Tuple, Union, cast
7
8
 
8
- from pypdf import PdfReader
9
+ from pypdf import PdfReader, PdfWriter
10
+ from pypdf.generic import DictionaryObject
9
11
  from reportlab.pdfbase.pdfmetrics import stringWidth
10
12
 
11
13
  from .constants import (COMB, DEFAULT_FONT_SIZE, MULTILINE, NEW_LINE_SYMBOL,
12
- WIDGET_TYPES, MaxLen, Rect)
14
+ WIDGET_TYPES, Annots, MaxLen, Rect)
13
15
  from .font import (adjust_paragraph_font_size, adjust_text_field_font_size,
14
16
  auto_detect_font, get_text_field_font_color,
15
17
  get_text_field_font_size, text_field_font_size)
@@ -19,7 +21,8 @@ from .middleware.radio import Radio
19
21
  from .middleware.text import Text
20
22
  from .patterns import (BUTTON_STYLE_PATTERNS, DROPDOWN_CHOICE_PATTERNS,
21
23
  TEXT_FIELD_FLAG_PATTERNS, WIDGET_ALIGNMENT_PATTERNS,
22
- WIDGET_KEY_PATTERNS, WIDGET_TYPE_PATTERNS)
24
+ WIDGET_KEY_PATTERNS, WIDGET_TYPE_PATTERNS,
25
+ update_annotation_name)
23
26
  from .utils import find_pattern_match, stream_to_io, traverse_pattern
24
27
  from .watermark import create_watermarks_and_draw
25
28
 
@@ -403,3 +406,43 @@ def get_paragraph_auto_wrap_length(widget_middleware: Text) -> int:
403
406
  result = min(result, len(line))
404
407
 
405
408
  return result
409
+
410
+
411
+ def update_widget_key(
412
+ template: bytes,
413
+ widgets: Dict[str, WIDGET_TYPES],
414
+ old_key: str,
415
+ new_key: str,
416
+ index: int,
417
+ ) -> bytes:
418
+ """Updates the key of a widget."""
419
+ # pylint: disable=R0801
420
+
421
+ pdf = PdfReader(stream_to_io(template))
422
+ out = PdfWriter()
423
+ out.append(pdf)
424
+
425
+ tracker = -1
426
+
427
+ for page in out.pages:
428
+ for annot in page.get(Annots, []): # noqa
429
+ annot = cast(DictionaryObject, annot.get_object())
430
+ key = get_widget_key(annot.get_object())
431
+
432
+ widget = widgets.get(key)
433
+ if widget is None:
434
+ continue
435
+
436
+ if old_key != key:
437
+ continue
438
+
439
+ tracker += 1
440
+ if not isinstance(widget, Radio) and tracker != index:
441
+ continue
442
+
443
+ update_annotation_name(annot, new_key)
444
+
445
+ with BytesIO() as f:
446
+ out.write(f)
447
+ f.seek(0)
448
+ return f.read()
@@ -18,7 +18,7 @@ from .middleware.dropdown import Dropdown
18
18
  from .middleware.text import Text
19
19
  from .template import (build_widgets, dropdown_to_text,
20
20
  set_character_x_paddings, update_text_field_attributes,
21
- widget_rect_watermarks)
21
+ update_widget_key, widget_rect_watermarks)
22
22
  from .utils import (get_page_streams, merge_two_pdfs, preview_widget_to_draw,
23
23
  remove_all_widgets)
24
24
  from .watermark import create_watermarks_and_draw, merge_watermarks_with_pdf
@@ -235,6 +235,22 @@ class PdfWrapper(FormWrapper):
235
235
 
236
236
  return self
237
237
 
238
+ def update_widget_key(
239
+ self, old_key: str, new_key: str, index: int = 0
240
+ ) -> PdfWrapper:
241
+ """Updates the key of an existed widget on a PDF form."""
242
+
243
+ self.__init__(
244
+ template=update_widget_key(
245
+ self.read(), self.widgets, old_key, new_key, index
246
+ ),
247
+ global_font=self.global_font,
248
+ global_font_size=self.global_font_size,
249
+ global_font_color=self.global_font_color,
250
+ )
251
+
252
+ return self
253
+
238
254
  def draw_text(
239
255
  self,
240
256
  text: str,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyPDFForm
3
- Version: 1.4.35
3
+ Version: 1.4.37
4
4
  Summary: The Python library for PDF forms.
5
5
  Home-page: https://github.com/chinapandaman/PyPDFForm
6
6
  Author: Jinge Li
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
16
17
  Classifier: License :: OSI Approved :: MIT License
17
18
  Classifier: Operating System :: OS Independent
18
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -37,6 +37,7 @@ tests/test_dropdown.py
37
37
  tests/test_dropdown_simple.py
38
38
  tests/test_fill_max_length_text_field.py
39
39
  tests/test_fill_max_length_text_field_simple.py
40
+ tests/test_fill_method.py
40
41
  tests/test_functional.py
41
42
  tests/test_functional_simple.py
42
43
  tests/test_paragraph.py
@@ -35,6 +35,7 @@ setuptools.setup(
35
35
  "Programming Language :: Python :: 3.10",
36
36
  "Programming Language :: Python :: 3.11",
37
37
  "Programming Language :: Python :: 3.12",
38
+ "Programming Language :: Python :: 3.13",
38
39
  "License :: OSI Approved :: MIT License",
39
40
  "Operating System :: OS Independent",
40
41
  "Topic :: Software Development :: Libraries :: Python Modules",
@@ -0,0 +1,141 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import os
4
+
5
+ from PyPDFForm import PdfWrapper
6
+
7
+
8
+ def test_fill_with_varied_int_values(template_stream, pdf_samples, request):
9
+ expected_path = os.path.join(
10
+ pdf_samples, "fill_method", "sample_filled_varied_ints.pdf"
11
+ )
12
+ with open(expected_path, "rb+") as f:
13
+ obj = PdfWrapper(template_stream).fill(
14
+ {
15
+ "test": 100,
16
+ "test_2": -250,
17
+ "test_3": 0,
18
+ }
19
+ )
20
+
21
+ request.config.results["expected_path"] = expected_path
22
+ request.config.results["stream"] = obj.read()
23
+
24
+ expected = f.read()
25
+
26
+ assert len(obj.read()) == len(expected)
27
+ assert obj.read() == expected
28
+
29
+
30
+ def test_fill_with_boolean_and_int_values(template_stream, pdf_samples, request):
31
+ expected_path = os.path.join(
32
+ pdf_samples, "fill_method", "sample_filled_boolean_and_ints.pdf"
33
+ )
34
+ with open(expected_path, "rb+") as f:
35
+ obj = PdfWrapper(template_stream).fill(
36
+ {
37
+ "test": 42,
38
+ "test_2": True,
39
+ "test_3": False,
40
+ }
41
+ )
42
+
43
+ request.config.results["expected_path"] = expected_path
44
+ request.config.results["stream"] = obj.read()
45
+
46
+ expected = f.read()
47
+
48
+ assert len(obj.read()) == len(expected)
49
+ assert obj.read() == expected
50
+
51
+
52
+ def test_fill_with_empty_string_and_int(template_stream, pdf_samples, request):
53
+ expected_path = os.path.join(
54
+ pdf_samples, "fill_method", "sample_filled_with_empty_string_and_int.pdf"
55
+ )
56
+ with open(expected_path, "rb+") as f:
57
+ obj = PdfWrapper(template_stream).fill(
58
+ {
59
+ "test": 42,
60
+ "test_2": "",
61
+ "test_3": 33,
62
+ }
63
+ )
64
+
65
+ request.config.results["expected_path"] = expected_path
66
+ request.config.results["stream"] = obj.read()
67
+
68
+ expected = f.read()
69
+
70
+ assert len(obj.read()) == len(expected)
71
+ assert obj.read() == expected
72
+
73
+
74
+ def test_fill_with_large_and_small_ints(template_stream, pdf_samples, request):
75
+ expected_path = os.path.join(
76
+ pdf_samples, "fill_method", "sample_filled_large_and_small_ints.pdf"
77
+ )
78
+ with open(expected_path, "rb+") as f:
79
+ obj = PdfWrapper(template_stream).fill(
80
+ {
81
+ "test": 999999999999,
82
+ "test_2": -999999999999,
83
+ "test_3": 1,
84
+ }
85
+ )
86
+
87
+ request.config.results["expected_path"] = expected_path
88
+ request.config.results["stream"] = obj.read()
89
+
90
+ expected = f.read()
91
+
92
+ assert len(obj.read()) == len(expected)
93
+ assert obj.read() == expected
94
+
95
+
96
+ def test_fill_with_varied_float_values(template_stream, pdf_samples, request):
97
+ expected_path = os.path.join(
98
+ pdf_samples, "fill_method", "sample_filled_varied_float_values.pdf"
99
+ )
100
+ with open(expected_path, "rb+") as f:
101
+ obj = PdfWrapper(template_stream).fill(
102
+ {
103
+ "test": 1.5,
104
+ "test_2": 13.8,
105
+ "test_3": 543,
106
+ }
107
+ )
108
+
109
+ request.config.results["expected_path"] = expected_path
110
+ request.config.results["stream"] = obj.read()
111
+
112
+ expected = f.read()
113
+
114
+ assert len(obj.read()) == len(expected)
115
+ assert obj.read() == expected
116
+
117
+
118
+ def test_fill_with_negative_and_positive_floats_and_int(
119
+ template_stream, pdf_samples, request
120
+ ):
121
+ expected_path = os.path.join(
122
+ pdf_samples,
123
+ "fill_method",
124
+ "sample_filled_negative_and_positive_floats_and_int.pdf",
125
+ )
126
+ with open(expected_path, "rb+") as f:
127
+ obj = PdfWrapper(template_stream).fill(
128
+ {
129
+ "test": -1.5,
130
+ "test_2": -6.9,
131
+ "test_3": 22,
132
+ }
133
+ )
134
+
135
+ request.config.results["expected_path"] = expected_path
136
+ request.config.results["stream"] = obj.read()
137
+
138
+ expected = f.read()
139
+
140
+ assert len(obj.read()) == len(expected)
141
+ assert obj.read() == expected
@@ -626,3 +626,36 @@ def test_fill_image(
626
626
 
627
627
  assert len(obj.read()) == len(expected)
628
628
  assert obj.stream == expected
629
+
630
+
631
+ def test_update_radio_key(template_with_radiobutton_stream, pdf_samples, request):
632
+ expected_path = os.path.join(pdf_samples, "test_update_radio_key.pdf")
633
+ with open(expected_path, "rb+") as f:
634
+ obj = PdfWrapper(template_with_radiobutton_stream)
635
+ obj.update_widget_key("radio_3", "RADIO")
636
+
637
+ request.config.results["expected_path"] = expected_path
638
+ request.config.results["stream"] = obj.read()
639
+
640
+ expected = f.read()
641
+
642
+ assert len(obj.preview) == len(expected)
643
+ assert obj.preview == expected
644
+
645
+
646
+ def test_update_sejda_key(sejda_template, pdf_samples, request):
647
+ expected_path = os.path.join(pdf_samples, "test_update_sejda_key.pdf")
648
+ with open(expected_path, "rb+") as f:
649
+ obj = PdfWrapper(sejda_template)
650
+ obj.update_widget_key("year", "YEAR")
651
+ obj.update_widget_key("at_future_date", "FUTURE_DATE")
652
+ obj.update_widget_key("purchase_option", "PURCHASE_OPTION")
653
+ obj.update_widget_key("buyer_signed_date", "BUYER_SIGNED_DATE")
654
+
655
+ request.config.results["expected_path"] = expected_path
656
+ request.config.results["stream"] = obj.read()
657
+
658
+ expected = f.read()
659
+
660
+ assert len(obj.preview) == len(expected)
661
+ assert obj.preview == expected
File without changes
File without changes
File without changes
File without changes