PyPDFForm 2.2.2__py3-none-any.whl → 2.2.4__py3-none-any.whl

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/__init__.py CHANGED
@@ -5,7 +5,7 @@ This package provides tools for filling PDF forms, drawing text and images,
5
5
  and manipulating PDF form elements programmatically.
6
6
  """
7
7
 
8
- __version__ = "2.2.2"
8
+ __version__ = "2.2.4"
9
9
 
10
10
  from .wrapper import FormWrapper, PdfWrapper
11
11
 
PyPDFForm/watermark.py CHANGED
@@ -10,7 +10,7 @@ This module handles:
10
10
  """
11
11
 
12
12
  from io import BytesIO
13
- from typing import List
13
+ from typing import List, Union
14
14
 
15
15
  from pypdf import PdfReader, PdfWriter
16
16
  from pypdf.generic import ArrayObject, NameObject
@@ -335,20 +335,28 @@ def merge_watermarks_with_pdf(
335
335
 
336
336
 
337
337
  def copy_watermark_widgets(
338
- pdf: bytes, watermarks: List[bytes], keys: List[str]
338
+ pdf: bytes,
339
+ watermarks: Union[List[bytes], bytes],
340
+ keys: Union[List[str], None],
341
+ page_num: Union[int, None],
339
342
  ) -> bytes:
340
- """
341
- Copies annotation widgets (form fields) from watermark PDFs onto the corresponding pages of a base PDF,
342
- including only those widgets whose key matches an entry in the provided keys list.
343
+ """Copies annotation widgets (form fields) from watermark PDFs onto the corresponding pages of a base PDF.
344
+
345
+ This function selectively copies annotation widgets (form fields) from watermark PDFs to a base PDF.
346
+ It allows specifying which widgets to copy based on their keys, and optionally restricts the operation
347
+ to a specific page number.
343
348
 
344
- For each watermark in the provided list, any annotation widgets (such as form fields) are cloned
345
- and appended to the annotations of the corresponding page in the base PDF, but only if their key
346
- matches one of the specified keys.
349
+ The function can handle either a list of watermarks (one per page) or a single watermark PDF applied to all pages.
350
+ Widgets are only copied if their key is present in the provided keys list.
347
351
 
348
352
  Args:
349
353
  pdf: The original PDF document as bytes.
350
- watermarks: List of watermark PDF data (as bytes), one per page. Empty or None entries are skipped.
354
+ watermarks: Either a list of watermark PDF data (as bytes, one per page) or a single watermark PDF as bytes.
355
+ Empty or None entries are skipped.
351
356
  keys: List of widget keys (str). Only widgets whose key is in this list will be copied.
357
+ If None, all widgets will be copied.
358
+ page_num: Optional page number (0-based) to restrict widget copying to. If None, widgets are copied
359
+ across all pages.
352
360
 
353
361
  Returns:
354
362
  bytes: The resulting PDF document with selected annotation widgets from watermarks copied onto their respective pages.
@@ -358,21 +366,34 @@ def copy_watermark_widgets(
358
366
  out = PdfWriter()
359
367
  out.append(pdf_file)
360
368
 
361
- widgets_to_copy = {}
369
+ widgets_to_copy_watermarks = {}
370
+ widgets_to_copy_pdf = {}
371
+
372
+ widgets_to_copy = widgets_to_copy_watermarks
373
+ if isinstance(watermarks, bytes):
374
+ watermarks = [watermarks]
375
+ widgets_to_copy = widgets_to_copy_pdf
376
+
377
+ if page_num is not None:
378
+ widgets_to_copy = widgets_to_copy_watermarks
362
379
 
363
380
  for i, watermark in enumerate(watermarks):
364
381
  if not watermark:
365
382
  continue
366
383
 
367
- widgets_to_copy[i] = []
384
+ widgets_to_copy_watermarks[i] = []
368
385
  watermark_file = PdfReader(stream_to_io(watermark))
369
- for page in watermark_file.pages:
386
+ for j, page in enumerate(watermark_file.pages):
387
+ widgets_to_copy_pdf[j] = []
370
388
  for annot in page.get(Annots, []): # noqa
371
389
  key = extract_widget_property(
372
390
  annot.get_object(), WIDGET_KEY_PATTERNS, None, str
373
391
  )
374
- if key in keys:
375
- widgets_to_copy[i].append(annot.clone(out))
392
+ if (keys is None or key in keys) and (
393
+ page_num is None or page_num == j
394
+ ):
395
+ widgets_to_copy_watermarks[i].append(annot.clone(out))
396
+ widgets_to_copy_pdf[j].append(annot.clone(out))
376
397
 
377
398
  for i, page in enumerate(out.pages):
378
399
  if i in widgets_to_copy:
PyPDFForm/wrapper.py CHANGED
@@ -288,9 +288,10 @@ class PdfWrapper(FormWrapper):
288
288
 
289
289
  return [
290
290
  self.__class__(
291
- each, **{param: getattr(self, param) for param, _ in self.USER_PARAMS}
291
+ copy_watermark_widgets(each, self.stream, None, i),
292
+ **{param: getattr(self, param) for param, _ in self.USER_PARAMS},
292
293
  )
293
- for each in get_page_streams(self.stream)
294
+ for i, each in enumerate(get_page_streams(remove_all_widgets(self.read())))
294
295
  ]
295
296
 
296
297
  def change_version(self, version: str) -> PdfWrapper:
@@ -497,7 +498,7 @@ class PdfWrapper(FormWrapper):
497
498
  obj = _class(name=name, page_number=page_number, x=x, y=y, **kwargs)
498
499
  watermarks = obj.watermarks(self.read())
499
500
 
500
- self.stream = copy_watermark_widgets(self.read(), watermarks, [name])
501
+ self.stream = copy_watermark_widgets(self.read(), watermarks, [name], None)
501
502
  if obj.non_acro_form_params:
502
503
  self.stream = handle_non_acro_form_params(
503
504
  self.stream, name, obj.non_acro_form_params
@@ -586,7 +587,10 @@ class PdfWrapper(FormWrapper):
586
587
  """Draws static text onto the PDF document at specified coordinates.
587
588
 
588
589
  Adds non-interactive text that becomes part of the PDF content rather
589
- than a form field. Useful for annotations, labels, signatures, etc.
590
+ than a form field. The text is drawn using a temporary Text widget and
591
+ merged via watermark operations, preserving existing form fields.
592
+
593
+ Supports multi-line text (using NEW_LINE_SYMBOL) and custom formatting.
590
594
 
591
595
  Args:
592
596
  text: The text content to draw (supports newlines with NEW_LINE_SYMBOL)
@@ -624,7 +628,11 @@ class PdfWrapper(FormWrapper):
624
628
  ],
625
629
  )
626
630
 
631
+ stream_with_widgets = self.read()
627
632
  self.stream = merge_watermarks_with_pdf(self.stream, watermarks)
633
+ self.stream = copy_watermark_widgets(
634
+ remove_all_widgets(self.stream), stream_with_widgets, None, None
635
+ )
628
636
 
629
637
  return self
630
638
 
@@ -640,10 +648,8 @@ class PdfWrapper(FormWrapper):
640
648
  ) -> PdfWrapper:
641
649
  """Draws an image onto the PDF document at specified coordinates.
642
650
 
643
- Supports common image formats (JPEG, PNG) from various sources:
644
- - Raw image bytes
645
- - File path
646
- - File-like object
651
+ The image is merged via watermark operations, preserving existing form fields.
652
+ Supports common formats (JPEG, PNG) from bytes, file paths, or file objects.
647
653
 
648
654
  Args:
649
655
  image: Image data as bytes, file path, or file object
@@ -664,7 +670,11 @@ class PdfWrapper(FormWrapper):
664
670
  self.stream, page_number, "image", [[image, x, y, width, height]]
665
671
  )
666
672
 
673
+ stream_with_widgets = self.read()
667
674
  self.stream = merge_watermarks_with_pdf(self.stream, watermarks)
675
+ self.stream = copy_watermark_widgets(
676
+ remove_all_widgets(self.stream), stream_with_widgets, None, None
677
+ )
668
678
 
669
679
  return self
670
680
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPDFForm
3
- Version: 2.2.2
3
+ Version: 2.2.4
4
4
  Summary: The Python library for PDF forms.
5
5
  Author: Jinge Li
6
6
  License-Expression: MIT
@@ -1,4 +1,4 @@
1
- PyPDFForm/__init__.py,sha256=HNulIew3N71YCiwiDKAcAlO6PzDIaF1VrqRgHbnFC6A,328
1
+ PyPDFForm/__init__.py,sha256=O-6vjBgvz5Cs4XbRwRyzJxW8Qk81vE3itghTLrSUgkI,328
2
2
  PyPDFForm/adapter.py,sha256=_5fP5UR-NzjDDayJmBRO36DgbnbUzNbjZtHZtPvSM14,1909
3
3
  PyPDFForm/constants.py,sha256=r4lR-lQ8KjKRLMowjxIv6k3wBPOxvt9-JAMugZwQmZM,2285
4
4
  PyPDFForm/coordinate.py,sha256=AgVrBoUo6fLVTVdZrlVRf6tGwD-AX54ICMLfbvhYfRs,14879
@@ -8,8 +8,8 @@ PyPDFForm/image.py,sha256=aYk7BC-AHiqt73durGIQ3e6gE5Ggbdr8jmkCUaQdsk8,1627
8
8
  PyPDFForm/patterns.py,sha256=iChwqR-aZUKhEdnbQ8OEwnES2-NaMhBUy4dUrnuDPpA,9243
9
9
  PyPDFForm/template.py,sha256=v1ZM52xHCzO8Xm7EXinbTepm2G7MU7StUgCFtuUdcbI,18899
10
10
  PyPDFForm/utils.py,sha256=ubqTaItrs6pEYcza-12bxLUiFYe_sl-0SdeowD_BSG0,8444
11
- PyPDFForm/watermark.py,sha256=9ydcqy8wNrea_1BvywyUsbcD4TzxQuEAdo0izOZKYyU,11101
12
- PyPDFForm/wrapper.py,sha256=4owXTiSyvx2DSA2XGnssIjH-ygL2pznmRjpPOuWwaWw,24625
11
+ PyPDFForm/watermark.py,sha256=w4m5wMOnVvDD693gbKrpZ1rvmjsgxrRaiSvDQfFCky0,12074
12
+ PyPDFForm/wrapper.py,sha256=spClcCLAfjTrwMwn6lP4zEtEKQ_Pb-SBy_US9SmS_Xc,25263
13
13
  PyPDFForm/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  PyPDFForm/middleware/base.py,sha256=d4z7M7pm80176cC4H85m3ZRWzAu_Fm1HkwQkmSSi-WE,2832
15
15
  PyPDFForm/middleware/checkbox.py,sha256=gRGhFySPoIpKBseoiOTS3WggoBBik12dXbZ-LJKzIwM,2611
@@ -27,8 +27,8 @@ PyPDFForm/widgets/image.py,sha256=6y8Ysmk49USr_qWOXD6KGL6cch516cUIlrxoj0pJy9Q,79
27
27
  PyPDFForm/widgets/radio.py,sha256=ipadJyHbgftDUvjGk15kapzgHPN3HjdF_iB_7amXR6o,2737
28
28
  PyPDFForm/widgets/signature.py,sha256=RII_fgjPRbM5_72ih4L6ohaWCBOyjgIoY6odkd15VY8,5107
29
29
  PyPDFForm/widgets/text.py,sha256=HP2cPEUAzK5QL3kDfMz7gQcC3svCpmYuyFItBjlrBpI,1233
30
- pypdfform-2.2.2.dist-info/licenses/LICENSE,sha256=43awmYkI6opyTpg19me731iO1WfXZwViqb67oWtCsFY,1065
31
- pypdfform-2.2.2.dist-info/METADATA,sha256=uConsIdDy3O6gtKYxjRAROunBPR5ghd4_gNAoedhQ3A,5605
32
- pypdfform-2.2.2.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
33
- pypdfform-2.2.2.dist-info/top_level.txt,sha256=GQQKuWqPUjT9YZqwK95NlAQzxjwoQrsxQ8ureM8lWOY,10
34
- pypdfform-2.2.2.dist-info/RECORD,,
30
+ pypdfform-2.2.4.dist-info/licenses/LICENSE,sha256=43awmYkI6opyTpg19me731iO1WfXZwViqb67oWtCsFY,1065
31
+ pypdfform-2.2.4.dist-info/METADATA,sha256=40T0_d8c8w_xm84oLVUOTdl60vutnZgAMu_Y6KQrtug,5605
32
+ pypdfform-2.2.4.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
33
+ pypdfform-2.2.4.dist-info/top_level.txt,sha256=GQQKuWqPUjT9YZqwK95NlAQzxjwoQrsxQ8ureM8lWOY,10
34
+ pypdfform-2.2.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.0)
2
+ Generator: setuptools (79.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5