PyPDFForm 1.4.34__tar.gz → 1.4.36__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 (47) hide show
  1. {pypdfform-1.4.34 → pypdfform-1.4.36}/PKG-INFO +1 -1
  2. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/__init__.py +1 -1
  3. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/filler.py +1 -1
  4. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/patterns.py +9 -0
  5. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/template.py +47 -4
  6. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/widgets/base.py +1 -0
  7. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/wrapper.py +17 -1
  8. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm.egg-info/PKG-INFO +1 -1
  9. {pypdfform-1.4.34 → pypdfform-1.4.36}/tests/test_create_widget.py +24 -0
  10. {pypdfform-1.4.34 → pypdfform-1.4.36}/tests/test_functional.py +41 -17
  11. {pypdfform-1.4.34 → pypdfform-1.4.36}/LICENSE +0 -0
  12. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/adapter.py +0 -0
  13. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/constants.py +0 -0
  14. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/coordinate.py +0 -0
  15. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/font.py +0 -0
  16. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/image.py +0 -0
  17. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/middleware/__init__.py +0 -0
  18. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/middleware/base.py +0 -0
  19. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/middleware/checkbox.py +0 -0
  20. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/middleware/dropdown.py +0 -0
  21. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/middleware/image.py +0 -0
  22. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/middleware/radio.py +0 -0
  23. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/middleware/signature.py +0 -0
  24. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/middleware/text.py +0 -0
  25. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/utils.py +0 -0
  26. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/watermark.py +0 -0
  27. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/widgets/__init__.py +0 -0
  28. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/widgets/checkbox.py +0 -0
  29. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/widgets/dropdown.py +0 -0
  30. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm/widgets/text.py +0 -0
  31. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm.egg-info/SOURCES.txt +0 -0
  32. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm.egg-info/dependency_links.txt +0 -0
  33. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm.egg-info/requires.txt +0 -0
  34. {pypdfform-1.4.34 → pypdfform-1.4.36}/PyPDFForm.egg-info/top_level.txt +0 -0
  35. {pypdfform-1.4.34 → pypdfform-1.4.36}/README.md +0 -0
  36. {pypdfform-1.4.34 → pypdfform-1.4.36}/setup.cfg +0 -0
  37. {pypdfform-1.4.34 → pypdfform-1.4.36}/setup.py +0 -0
  38. {pypdfform-1.4.34 → pypdfform-1.4.36}/tests/test_adobe_mode.py +0 -0
  39. {pypdfform-1.4.34 → pypdfform-1.4.36}/tests/test_dropdown.py +0 -0
  40. {pypdfform-1.4.34 → pypdfform-1.4.36}/tests/test_dropdown_simple.py +0 -0
  41. {pypdfform-1.4.34 → pypdfform-1.4.36}/tests/test_fill_max_length_text_field.py +0 -0
  42. {pypdfform-1.4.34 → pypdfform-1.4.36}/tests/test_fill_max_length_text_field_simple.py +0 -0
  43. {pypdfform-1.4.34 → pypdfform-1.4.36}/tests/test_functional_simple.py +0 -0
  44. {pypdfform-1.4.34 → pypdfform-1.4.36}/tests/test_paragraph.py +0 -0
  45. {pypdfform-1.4.34 → pypdfform-1.4.36}/tests/test_paragraph_simple.py +0 -0
  46. {pypdfform-1.4.34 → pypdfform-1.4.36}/tests/test_preview.py +0 -0
  47. {pypdfform-1.4.34 → pypdfform-1.4.36}/tests/test_signature.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyPDFForm
3
- Version: 1.4.34
3
+ Version: 1.4.36
4
4
  Summary: The Python library for PDF forms.
5
5
  Home-page: https://github.com/chinapandaman/PyPDFForm
6
6
  Author: Jinge Li
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Contains any object users might need."""
3
3
 
4
- __version__ = "1.4.34"
4
+ __version__ = "1.4.36"
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:
@@ -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()
@@ -52,6 +52,7 @@ class Widget:
52
52
  value[0],
53
53
  value[1],
54
54
  value[2],
55
+ value[3] if len(value) == 4 else 1,
55
56
  )
56
57
  self.acro_form_params[param] = value
57
58
  elif user_input in self.NONE_DEFAULTS:
@@ -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.34
3
+ Version: 1.4.36
4
4
  Summary: The Python library for PDF forms.
5
5
  Home-page: https://github.com/chinapandaman/PyPDFForm
6
6
  Author: Jinge Li
@@ -198,6 +198,30 @@ def test_create_text_default(template_stream, pdf_samples, request):
198
198
  assert obj.stream == expected
199
199
 
200
200
 
201
+ def test_create_text_alpha_bg_color(template_stream, pdf_samples, request):
202
+ expected_path = os.path.join(
203
+ pdf_samples, "widget", "create_text_alpha_bg_color.pdf"
204
+ )
205
+ with open(expected_path, "rb+") as f:
206
+ obj = PdfWrapper(template_stream).create_widget(
207
+ "text",
208
+ "foo",
209
+ 1,
210
+ 100,
211
+ 100,
212
+ bg_color=(0, 0, 1, 0),
213
+ )
214
+ assert obj.schema["properties"]["foo"]["type"] == "string"
215
+
216
+ request.config.results["expected_path"] = expected_path
217
+ request.config.results["stream"] = obj.read()
218
+
219
+ expected = f.read()
220
+
221
+ assert len(obj.stream) == len(expected)
222
+ assert obj.stream == expected
223
+
224
+
201
225
  def test_create_text_align_center(template_stream, pdf_samples, request):
202
226
  expected_path = os.path.join(pdf_samples, "widget", "create_text_align_center.pdf")
203
227
  with open(expected_path, "rb+") as f:
@@ -258,7 +258,7 @@ def test_draw_image_on_one_page(template_stream, image_samples, pdf_samples, req
258
258
 
259
259
  expected = f.read()
260
260
 
261
- if os.name == "nt":
261
+ if os.name != "nt":
262
262
  request.config.results["expected_path"] = expected_path
263
263
  request.config.results["stream"] = obj.read()
264
264
  assert len(obj.stream) == len(expected)
@@ -281,7 +281,7 @@ def test_draw_png_image_on_one_page(
281
281
 
282
282
  expected = f.read()
283
283
 
284
- if os.name == "nt":
284
+ if os.name != "nt":
285
285
  request.config.results["expected_path"] = expected_path
286
286
  request.config.results["stream"] = obj.read()
287
287
  assert len(obj.stream) == len(expected)
@@ -304,7 +304,7 @@ def test_draw_transparent_png_image_on_one_page(
304
304
 
305
305
  expected = f.read()
306
306
 
307
- if os.name == "nt":
307
+ if os.name != "nt":
308
308
  request.config.results["expected_path"] = expected_path
309
309
  request.config.results["stream"] = obj.read()
310
310
  assert len(obj.stream) == len(expected)
@@ -510,24 +510,15 @@ def test_fill_complex_fonts(sample_template_with_complex_fonts, pdf_samples, req
510
510
  assert obj.stream == expected
511
511
 
512
512
 
513
- def test_pages(template_stream, pdf_samples):
513
+ def test_pages(template_stream, pdf_samples, request):
514
+ expected_path = os.path.join(pdf_samples, "pages", "sample_template_page_1.pdf")
514
515
  obj = PdfWrapper(template_stream)
515
516
 
516
- with open(
517
- os.path.join(pdf_samples, "pages", "sample_template_page_1.pdf"), "rb+"
518
- ) as f:
517
+ with open(expected_path, "rb+") as f:
518
+ request.config.results["expected_path"] = expected_path
519
+ request.config.results["stream"] = obj.pages[0].read()
519
520
  assert obj.pages[0].read() == f.read()
520
521
 
521
- with open(
522
- os.path.join(pdf_samples, "pages", "sample_template_page_2.pdf"), "rb+"
523
- ) as f:
524
- assert obj.pages[1].read() == f.read()
525
-
526
- with open(
527
- os.path.join(pdf_samples, "pages", "sample_template_page_3.pdf"), "rb+"
528
- ) as f:
529
- assert obj.pages[2].read() == f.read()
530
-
531
522
 
532
523
  def test_generate_coordinate_grid(template_stream, pdf_samples, request):
533
524
  expected_path = os.path.join(pdf_samples, "test_generate_coordinate_grid.pdf")
@@ -635,3 +626,36 @@ def test_fill_image(
635
626
 
636
627
  assert len(obj.read()) == len(expected)
637
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
File without changes