PyPDFForm 3.5.2__tar.gz → 3.5.4__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.
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PKG-INFO +3 -3
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/__init__.py +1 -1
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/adapter.py +0 -1
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/coordinate.py +0 -2
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/filler.py +0 -5
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/font.py +0 -5
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/hooks.py +19 -23
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/image.py +0 -3
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/middleware/base.py +3 -4
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/middleware/signature.py +0 -1
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/patterns.py +0 -4
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/template.py +1 -6
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/utils.py +0 -8
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/watermark.py +0 -7
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/widgets/base.py +0 -3
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/widgets/radio.py +0 -2
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/widgets/signature.py +0 -4
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/wrapper.py +0 -11
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm.egg-info/PKG-INFO +3 -3
- {pypdfform-3.5.2 → pypdfform-3.5.4}/pyproject.toml +7 -2
- {pypdfform-3.5.2 → pypdfform-3.5.4}/tests/test_adobe_mode.py +3 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/tests/test_create_widget.py +39 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/tests/test_dropdown.py +11 -9
- {pypdfform-3.5.2 → pypdfform-3.5.4}/tests/test_font_widths.py +3 -1
- {pypdfform-3.5.2 → pypdfform-3.5.4}/tests/test_functional.py +20 -12
- {pypdfform-3.5.2 → pypdfform-3.5.4}/tests/test_paragraph.py +26 -24
- {pypdfform-3.5.2 → pypdfform-3.5.4}/tests/test_signature.py +9 -3
- {pypdfform-3.5.2 → pypdfform-3.5.4}/tests/test_widget_attr_trigger.py +26 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/LICENSE +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/constants.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/middleware/__init__.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/middleware/checkbox.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/middleware/dropdown.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/middleware/image.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/middleware/radio.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/middleware/text.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/widgets/__init__.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/widgets/bedrock.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/widgets/checkbox.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/widgets/dropdown.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/widgets/image.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm/widgets/text.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm.egg-info/SOURCES.txt +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm.egg-info/dependency_links.txt +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm.egg-info/requires.txt +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/PyPDFForm.egg-info/top_level.txt +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/README.md +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/setup.cfg +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/tests/test_extract_values.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/tests/test_fill_max_length_text_field.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/tests/test_fill_method.py +0 -0
- {pypdfform-3.5.2 → pypdfform-3.5.4}/tests/test_use_full_widget_name.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyPDFForm
|
|
3
|
-
Version: 3.5.
|
|
3
|
+
Version: 3.5.4
|
|
4
4
|
Summary: The Python library for PDF forms.
|
|
5
5
|
Author: Jinge Li
|
|
6
6
|
License-Expression: MIT
|
|
@@ -10,14 +10,14 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
18
|
Classifier: Operating System :: OS Independent
|
|
19
19
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
23
|
Requires-Dist: cryptography
|
|
@@ -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__ = "3.5.
|
|
23
|
+
__version__ = "3.5.4"
|
|
24
24
|
|
|
25
25
|
from .middleware.text import Text # exposing for setting global font attrs
|
|
26
26
|
from .widgets import Fields
|
|
@@ -9,7 +9,6 @@ filling operations, where the input PDF template can be provided in different
|
|
|
9
9
|
forms. The module ensures that the input is properly converted into a byte
|
|
10
10
|
stream before further processing.
|
|
11
11
|
"""
|
|
12
|
-
# TODO: For large PDF files, reading the entire file into memory using `_file.read()` in `fp_or_f_obj_or_stream_to_stream` can be inefficient. Consider streaming or chunking if downstream processing allows.
|
|
13
12
|
|
|
14
13
|
from os.path import isfile
|
|
15
14
|
from typing import Any, BinaryIO, Union
|
|
@@ -6,8 +6,6 @@ This module provides functionality to generate coordinate grids on existing PDF
|
|
|
6
6
|
It allows developers to visualize the coordinate system of each page in a PDF, which can be helpful
|
|
7
7
|
for debugging and precisely positioning elements when filling or drawing on PDF forms.
|
|
8
8
|
"""
|
|
9
|
-
# TODO: The `PdfReader` object is initialized twice (lines 42 and implicitly within `create_watermarks_and_draw` if it re-reads the PDF). Consider initializing it once and passing the object or its relevant parts to avoid redundant parsing, especially for large PDFs.
|
|
10
|
-
# TODO: Drawing operations for lines and texts are performed and merged separately. It might be more efficient to combine all drawing operations for a page into a single `create_watermarks_and_draw` call or to merge all watermarks in one final step to reduce PDF processing overhead.
|
|
11
9
|
|
|
12
10
|
from typing import Tuple
|
|
13
11
|
|
|
@@ -7,11 +7,6 @@ It includes functions for handling various form field types, such as text fields
|
|
|
7
7
|
checkboxes, radio buttons, dropdowns, images, and signatures. The module also
|
|
8
8
|
supports flattening the filled form to prevent further modifications.
|
|
9
9
|
"""
|
|
10
|
-
# TODO: In `fill` function, `PdfReader(stream_to_io(template))` and `out.append(pdf)` might involve re-parsing or copying the entire PDF. For very large PDFs, consider if `pypdf` offers more efficient ways to modify in-place or stream processing.
|
|
11
|
-
# TODO: The `get_widget_key` function is called repeatedly in a loop. If its internal logic is complex, consider caching its results or optimizing its implementation to avoid redundant computations.
|
|
12
|
-
# TODO: The `signature_image_handler` function involves `get_image_dimensions` and `get_draw_image_resolutions`. If image processing is a bottleneck, consider optimizing these image-related operations, perhaps by using faster image libraries or pre-calculating dimensions if images are reused.
|
|
13
|
-
# TODO: Similar to `coordinate.py`, `get_drawn_stream` involves multiple `create_watermarks_and_draw` and `merge_watermarks_with_pdf` calls. Combining drawing operations or merging watermarks in a single pass could reduce overhead.
|
|
14
|
-
# TODO: The `radio_button_tracker` logic involves iterating through all radio buttons. For forms with many radio buttons, consider optimizing the lookup or update mechanism if performance becomes an issue.
|
|
15
10
|
|
|
16
11
|
from io import BytesIO
|
|
17
12
|
from typing import Dict, Union, cast
|
|
@@ -6,11 +6,6 @@ It includes functions for registering fonts with ReportLab and within the PDF's
|
|
|
6
6
|
allowing these fonts to be used when filling form fields. The module also provides utilities
|
|
7
7
|
for extracting font information from TTF streams and managing font names within a PDF.
|
|
8
8
|
"""
|
|
9
|
-
# TODO: In `get_additional_font_params`, iterating through `reader.pages[0][Resources][Font].values()` can be inefficient for PDFs with many fonts. Consider building a font lookup dictionary once per PDF or caching results if this function is called frequently with the same PDF.
|
|
10
|
-
# TODO: In `register_font_acroform`, `PdfReader(stream_to_io(pdf))` and `writer.append(reader)` involve re-parsing and appending the PDF. For large PDFs, passing `PdfReader` and `PdfWriter` objects directly could reduce overhead.
|
|
11
|
-
# TODO: In `register_font_acroform`, `compress(ttf_stream)` can be CPU-intensive. If the same font stream is registered multiple times within a single PDF processing session, consider caching the compressed stream to avoid redundant compression.
|
|
12
|
-
# TODO: In `get_new_font_name`, while `existing` is a set, if `n` needs to increment many times due to a dense range of existing font names, the `while` loop could be slow. However, this is likely a minor bottleneck in typical scenarios.
|
|
13
|
-
# TODO: In `get_all_available_fonts`, the `replace("/", "")` operation on `BaseFont` could be avoided if font names are consistently handled with or without the leading slash to prevent string manipulation overhead in a loop.
|
|
14
9
|
|
|
15
10
|
from functools import lru_cache
|
|
16
11
|
from io import BytesIO
|
|
@@ -8,10 +8,6 @@ of checkbox and radio button widgets. It also provides functions for flattening
|
|
|
8
8
|
generic and radio button widgets. These hooks are triggered during the PDF form
|
|
9
9
|
filling process, allowing for customization of the form's appearance and behavior.
|
|
10
10
|
"""
|
|
11
|
-
# TODO: In `trigger_widget_hooks`, the PDF is read and written in each call. If this function is part of a larger workflow, consider passing `PdfReader` and `PdfWriter` objects to avoid redundant parsing and writing, allowing modifications to be accumulated and written once.
|
|
12
|
-
# TODO: String manipulations (split/join) in `update_text_field_font`, `update_text_field_font_size`, and `update_text_field_font_color` could be optimized for very long `DA` strings, potentially using more efficient string manipulation techniques or regex if the structure is consistent.
|
|
13
|
-
# TODO: The `get_widget_key` function is called in a loop within `trigger_widget_hooks`. If its internal logic is complex, consider caching its results or optimizing its implementation to avoid redundant computations.
|
|
14
|
-
# TODO: In `flatten_radio` and `flatten_generic`, `annot.get(NameObject(Ff), 0)` is called twice within the conditional. Store this value in a local variable to avoid redundant dictionary lookups.
|
|
15
11
|
|
|
16
12
|
import sys
|
|
17
13
|
from io import BytesIO
|
|
@@ -216,7 +212,8 @@ def update_text_field_multiline(annot: DictionaryObject, val: bool) -> None:
|
|
|
216
212
|
val (bool): True to enable multiline, False to disable.
|
|
217
213
|
"""
|
|
218
214
|
if val:
|
|
219
|
-
|
|
215
|
+
# Ff in annot[Parent] only in hooks.py, or when editing instead of retrieving
|
|
216
|
+
if Parent in annot and Ff in annot[Parent]:
|
|
220
217
|
annot[NameObject(Parent)][NameObject(Ff)] = NumberObject(
|
|
221
218
|
int(
|
|
222
219
|
annot[NameObject(Parent)][NameObject(Ff)]
|
|
@@ -244,7 +241,7 @@ def update_text_field_comb(annot: DictionaryObject, val: bool) -> None:
|
|
|
244
241
|
val (bool): True to enable comb, False to disable.
|
|
245
242
|
"""
|
|
246
243
|
if val:
|
|
247
|
-
if Parent in annot and Ff
|
|
244
|
+
if Parent in annot and Ff in annot[Parent]:
|
|
248
245
|
annot[NameObject(Parent)][NameObject(Ff)] = NumberObject(
|
|
249
246
|
int(
|
|
250
247
|
annot[NameObject(Parent)][NameObject(Ff)]
|
|
@@ -364,7 +361,7 @@ def flatten_generic(annot: DictionaryObject, val: bool) -> None:
|
|
|
364
361
|
annot (DictionaryObject): The annotation dictionary.
|
|
365
362
|
val (bool): True to flatten (make read-only), False to unflatten (make editable).
|
|
366
363
|
"""
|
|
367
|
-
if Parent in annot and Ff not in annot:
|
|
364
|
+
if Parent in annot and (Ff in annot[Parent] or Ff not in annot):
|
|
368
365
|
annot[NameObject(Parent)][NameObject(Ff)] = NumberObject(
|
|
369
366
|
(
|
|
370
367
|
int(annot.get(NameObject(Ff), 0)) | READ_ONLY
|
|
@@ -409,20 +406,19 @@ def update_field_required(annot: DictionaryObject, val: bool) -> None:
|
|
|
409
406
|
annot (DictionaryObject): The annotation dictionary for the form field.
|
|
410
407
|
val (bool): True to set the field as required, False to make it optional.
|
|
411
408
|
"""
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
409
|
+
if Parent in annot and Ff in annot[Parent]:
|
|
410
|
+
annot[NameObject(Parent)][NameObject(Ff)] = NumberObject(
|
|
411
|
+
(
|
|
412
|
+
int(annot.get(NameObject(Ff), 0)) | REQUIRED
|
|
413
|
+
if val
|
|
414
|
+
else int(annot.get(NameObject(Ff), 0)) & ~REQUIRED
|
|
415
|
+
)
|
|
416
|
+
)
|
|
417
|
+
else:
|
|
418
|
+
annot[NameObject(Ff)] = NumberObject(
|
|
419
|
+
(
|
|
420
|
+
int(annot.get(NameObject(Ff), 0)) | REQUIRED
|
|
421
|
+
if val
|
|
422
|
+
else int(annot.get(NameObject(Ff), 0)) & ~REQUIRED
|
|
423
|
+
)
|
|
427
424
|
)
|
|
428
|
-
)
|
|
@@ -6,9 +6,6 @@ It includes functions for rotating images, retrieving image dimensions, and
|
|
|
6
6
|
calculating the resolutions for drawing an image on a PDF page, taking into
|
|
7
7
|
account whether to preserve the aspect ratio.
|
|
8
8
|
"""
|
|
9
|
-
# TODO: In `rotate_image` and `get_image_dimensions`, `BytesIO` is used to wrap the image stream. While necessary for PIL, consider if the `image_stream` is already a file-like object in some calling contexts, which could avoid redundant copying to `BytesIO`.
|
|
10
|
-
# TODO: The `rotate_image` function creates a new `BytesIO` object and saves the image to it. For multiple rotations or image manipulations, consider keeping the `PIL.Image.Image` object in memory and performing operations on it directly before a final save to bytes, to avoid repeated I/O operations.
|
|
11
|
-
# TODO: The `get_image_dimensions` function opens the image to get its size. If image dimensions are frequently needed for the same image, consider caching the dimensions to avoid re-opening and re-parsing the image data.
|
|
12
9
|
|
|
13
10
|
from io import BytesIO
|
|
14
11
|
from typing import Tuple, Union
|
|
@@ -42,8 +42,7 @@ class Widget:
|
|
|
42
42
|
super().__init__()
|
|
43
43
|
self._name = name
|
|
44
44
|
self._value = value
|
|
45
|
-
self.
|
|
46
|
-
self.tooltip: str = None # TODO: sync tooltip and desc
|
|
45
|
+
self.tooltip: str = None
|
|
47
46
|
self.readonly: bool = None
|
|
48
47
|
self.required: bool = None
|
|
49
48
|
self.hooks_to_trigger: list = []
|
|
@@ -107,8 +106,8 @@ class Widget:
|
|
|
107
106
|
"""
|
|
108
107
|
result = {}
|
|
109
108
|
|
|
110
|
-
if self.
|
|
111
|
-
result["description"] = self.
|
|
109
|
+
if self.tooltip is not None:
|
|
110
|
+
result["description"] = self.tooltip
|
|
112
111
|
|
|
113
112
|
return result
|
|
114
113
|
|
|
@@ -6,7 +6,6 @@ This module defines the Signature class, which is a subclass of the
|
|
|
6
6
|
Widget class. It represents a signature form field in a PDF document,
|
|
7
7
|
allowing users to add their signature as an image.
|
|
8
8
|
"""
|
|
9
|
-
# TODO: In the `stream` property, `fp_or_f_obj_or_stream_to_stream` is called every time the property is accessed. If the signature image is large or the property is accessed frequently, consider caching the result of `fp_or_f_obj_or_stream_to_stream` to avoid redundant file reads.
|
|
10
9
|
|
|
11
10
|
from os.path import expanduser
|
|
12
11
|
from typing import Union
|
|
@@ -7,10 +7,6 @@ checkboxes, radio buttons, dropdowns, images, and signatures) based on their
|
|
|
7
7
|
properties in the PDF's annotation dictionary. It also provides utility functions
|
|
8
8
|
for updating these widgets.
|
|
9
9
|
"""
|
|
10
|
-
# TODO: The `WIDGET_TYPE_PATTERNS` list is iterated through to determine widget types. For very large numbers of annotations or complex pattern matching, consider optimizing this lookup, perhaps by pre-compiling regexes or using a more efficient data structure if the patterns allow.
|
|
11
|
-
# TODO: In `update_checkbox_value` and `update_radio_value`, iterating through `annot[AP][N]` to find the correct appearance state might be slow if `N` contains many entries. If possible, a direct lookup or a more optimized search could improve performance.
|
|
12
|
-
# TODO: In `update_dropdown_value`, the list comprehension for `ArrayObject` can be computationally intensive for dropdowns with many choices, as it creates new `TextStringObject` and `ArrayObject` instances for each choice. Consider optimizing this if dropdowns have a very large number of options.
|
|
13
|
-
# TODO: The `get_checkbox_value` and `get_radio_value` functions involve dictionary lookups and comparisons. While generally fast, repeated calls in a tight loop for many widgets could accumulate overhead.
|
|
14
10
|
|
|
15
11
|
from typing import Union
|
|
16
12
|
|
|
@@ -7,11 +7,6 @@ in PDF form templates. It leverages the pypdf library for PDF manipulation
|
|
|
7
7
|
and defines specific patterns for identifying and constructing different
|
|
8
8
|
types of widgets.
|
|
9
9
|
"""
|
|
10
|
-
# TODO: In `build_widgets`, the `get_widgets_by_page` function is called, which then iterates through pages and annotations. For very large PDFs, this initial parsing and iteration can be a bottleneck. Consider optimizing the widget extraction process if possible, perhaps by using a more direct method to access annotations if `pypdf` allows.
|
|
11
|
-
# TODO: The `construct_widget` function iterates through `WIDGET_TYPE_PATTERNS` for each widget. If there are many patterns or many widgets, this repeated iteration could be optimized by pre-compiling patterns or using a more efficient lookup mechanism.
|
|
12
|
-
# TODO: In `get_widget_key`, the recursive call for `Parent` can lead to deep recursion for deeply nested widgets, potentially impacting performance or hitting recursion limits for extremely complex forms. Consider an iterative approach if deep nesting is common.
|
|
13
|
-
# TODO: In `update_widget_keys`, the nested loops iterating through `old_keys`, `out.pages`, and `page.get(Annots, [])` can be very inefficient for large numbers of keys, pages, or annotations. Consider creating a lookup structure for annotations by key to avoid repeated linear scans.
|
|
14
|
-
# TODO: In `update_widget_keys`, `PdfReader(stream_to_io(template))` and `out.append(pdf)` involve re-parsing and appending the PDF. For large PDFs, passing `PdfReader` and `PdfWriter` objects directly could reduce overhead.
|
|
15
10
|
|
|
16
11
|
from functools import lru_cache
|
|
17
12
|
from io import BytesIO
|
|
@@ -62,7 +57,7 @@ def build_widgets(
|
|
|
62
57
|
key = get_widget_key(widget, use_full_widget_name)
|
|
63
58
|
_widget = construct_widget(widget, key)
|
|
64
59
|
if _widget is not None:
|
|
65
|
-
_widget.
|
|
60
|
+
_widget.__dict__["tooltip"] = extract_widget_property(
|
|
66
61
|
widget, WIDGET_DESCRIPTION_PATTERNS, None, str
|
|
67
62
|
)
|
|
68
63
|
|
|
@@ -12,14 +12,6 @@ It includes functions for:
|
|
|
12
12
|
- Generating unique suffixes for internal use.
|
|
13
13
|
- Enabling Adobe-specific settings in the PDF to ensure proper rendering of form fields.
|
|
14
14
|
"""
|
|
15
|
-
# TODO: In `enable_adobe_mode`, `PdfReader(stream_to_io(pdf))` and `writer.append(reader)` involve re-parsing and appending the PDF. For large PDFs, passing `PdfReader` and `PdfWriter` objects directly could reduce overhead.
|
|
16
|
-
# TODO: In `remove_all_widgets`, `PdfReader(stream_to_io(pdf))` and iterating through pages to add them to a new writer can be inefficient for large PDFs. Consider if `pypdf` offers a more direct way to remove annotations without reconstructing the entire PDF.
|
|
17
|
-
# TODO: In `get_page_streams`, `PdfReader(stream_to_io(pdf))` and then creating a new `PdfWriter` for each page can be very inefficient. It would be more performant to iterate through the pages of a single `PdfReader` and extract their content streams directly if possible, or to use a single `PdfWriter` to extract multiple pages.
|
|
18
|
-
# TODO: In `merge_two_pdfs`, the function reads and writes PDFs multiple times (`PdfReader`, `PdfWriter`, `remove_all_widgets`, then another `PdfReader` and `PdfWriter`). This is highly inefficient. The PDF objects should be passed around and modified in-place as much as possible, with a single final write operation.
|
|
19
|
-
# TODO: The `merge_two_pdfs` function has a `TODO: refactor duplicate logic with copy_watermark_widgets` comment. This indicates a potential for code duplication and inefficiency. Refactoring this to a shared helper function would improve maintainability and potentially performance.
|
|
20
|
-
# TODO: In `find_pattern_match` and `traverse_pattern`, the recursive nature and repeated dictionary lookups (`widget.items()`, `value.get_object()`) can be slow for deeply nested or complex widget structures. Consider optimizing these traversals, perhaps by pre-flattening the widget dictionary or using a more direct access method if `pypdf` allows.
|
|
21
|
-
# TODO: In `extract_widget_property`, the loop iterates through `patterns` and calls `traverse_pattern` for each. If `patterns` is long or `traverse_pattern` is expensive, this could be a bottleneck. Consider optimizing the pattern matching or lookup.
|
|
22
|
-
# TODO: `generate_unique_suffix` uses `choice` in a loop. While generally fast, for extremely high call volumes, pre-generating a pool of characters or using a faster random string generation method might offer minor improvements.
|
|
23
15
|
|
|
24
16
|
from collections.abc import Callable
|
|
25
17
|
from functools import lru_cache
|
|
@@ -7,13 +7,6 @@ It supports drawing text, lines, and images as watermarks.
|
|
|
7
7
|
The module also includes functions to merge these watermarks with the original PDF content
|
|
8
8
|
and to copy specific widgets from the watermarks to the original PDF.
|
|
9
9
|
"""
|
|
10
|
-
# TODO: In `draw_image`, `ImageReader(image_buff)` is created for each image. If the same image is drawn multiple times, consider caching `ImageReader` objects or passing pre-processed image data to avoid redundant processing.
|
|
11
|
-
# TODO: In `create_watermarks_and_draw`, `PdfReader(stream_to_io(pdf))` is called, which re-parses the PDF. If this function is called repeatedly for the same PDF, consider passing the `PdfReader` object directly to avoid redundant parsing.
|
|
12
|
-
# TODO: In `create_watermarks_and_draw`, the function returns a list of watermarks where only one element is populated. This can be inefficient for memory if there are many pages but only one watermark is created. Consider returning only the created watermark and its page number, and let the caller handle placement.
|
|
13
|
-
# TODO: In `merge_watermarks_with_pdf`, `PdfReader(stream_to_io(pdf))` and `PdfReader(stream_to_io(watermarks[i]))` are called in a loop. This leads to repeated parsing of the base PDF and each watermark. It would be more efficient to parse the base PDF once and then merge watermark pages directly into the existing `PdfWriter` object.
|
|
14
|
-
# TODO: In `copy_watermark_widgets`, the function reads the PDF and watermarks multiple times. Similar to `merge_watermarks_with_pdf`, optimize by parsing the base PDF and watermarks once and then manipulating the `PdfWriter` object.
|
|
15
|
-
# TODO: The `copy_watermark_widgets` function has a `TODO: refactor duplicate logic with merge_two_pdfs` comment. This indicates a potential for code duplication and inefficiency. Refactoring this to a shared helper function would improve maintainability and potentially performance.
|
|
16
|
-
# TODO: In `copy_watermark_widgets`, the nested loops iterating through `watermarks`, `watermark_file.pages`, and `page.get(Annots, [])` can be very inefficient for large numbers of watermarks, pages, or annotations. Consider creating a lookup structure for annotations by key to avoid repeated linear scans.
|
|
17
10
|
|
|
18
11
|
from io import BytesIO
|
|
19
12
|
from typing import List, Union
|
|
@@ -12,9 +12,6 @@ Classes:
|
|
|
12
12
|
functionality for rendering and manipulation.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
# TODO: In `watermarks`, `PdfReader(stream_to_io(stream))` is called, which re-parses the PDF for each widget. If multiple widgets are being processed, consider passing the `PdfReader` object directly to avoid redundant parsing.
|
|
16
|
-
# TODO: In `watermarks`, the list comprehension `[watermark.read() if i == self.page_number - 1 else b"" for i in range(page_count)]` creates a new `BytesIO` object and reads from it for each widget. If many widgets are created, this could be optimized by creating the `BytesIO` object once and passing it around, or by directly returning the watermark bytes and its page number.
|
|
17
|
-
|
|
18
15
|
from dataclasses import dataclass
|
|
19
16
|
from inspect import signature
|
|
20
17
|
from io import BytesIO
|
|
@@ -10,8 +10,6 @@ The `RadioWidget` class extends the base `CheckBoxWidget` class to provide
|
|
|
10
10
|
specific functionality for interacting with radio button form fields in PDFs.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
# TODO: In `canvas_operations`, `self.acro_form_params.copy()` creates a shallow copy of the dictionary in each iteration of the loop. For a large number of radio buttons, this repeated copying can be inefficient. Consider modifying the dictionary in place and then reverting changes if necessary, or restructuring the data to avoid repeated copying.
|
|
14
|
-
|
|
15
13
|
from dataclasses import dataclass
|
|
16
14
|
from typing import List, Optional
|
|
17
15
|
|
|
@@ -11,10 +11,6 @@ signature form fields in PDFs, including handling their creation, rendering, and
|
|
|
11
11
|
integration into the document.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
# TODO: In `watermarks`, `PdfReader(stream_to_io(BEDROCK_PDF))` is called every time the method is invoked. If `BEDROCK_PDF` is static, consider parsing it once and caching the `PdfReader` object to avoid redundant I/O and parsing.
|
|
15
|
-
# TODO: In `watermarks`, the list comprehension `[f.read() if i == self.page_number - 1 else b"" for i in range(page_count)]` reads the entire `BytesIO` object `f` multiple times if `page_count` is large. Read `f` once into a variable and then use that variable in the list comprehension.
|
|
16
|
-
# TODO: The `input_pdf` is created in `watermarks` but only its page count is used. If the `PdfReader` object is not needed for other operations, consider a lighter way to get the page count or pass the `PdfReader` object from the caller if it's already available.
|
|
17
|
-
|
|
18
14
|
from dataclasses import dataclass
|
|
19
15
|
from io import BytesIO
|
|
20
16
|
from typing import List, Optional
|
|
@@ -15,17 +15,6 @@ methods for interacting with its form fields and content. It leverages
|
|
|
15
15
|
lower-level modules within the `PyPDFForm` library to handle the
|
|
16
16
|
underlying PDF manipulation.
|
|
17
17
|
"""
|
|
18
|
-
# TODO: The `__add__` method (merging PDFs) involves multiple `self.read()` and `other.read()` calls, leading to redundant PDF parsing. Consider optimizing by passing `PdfReader` objects directly or by performing a single read and then merging.
|
|
19
|
-
# TODO: In `_init_helper`, `build_widgets` and `get_all_available_fonts` both call `self.read()`, causing the PDF to be parsed multiple times. Optimize by parsing the PDF once and passing the `PdfReader` object to these functions.
|
|
20
|
-
# TODO: The `pages` property's implementation involves `get_page_streams(remove_all_widgets(self.read()))` and `copy_watermark_widgets(each, self.read(), None, i)`. This leads to excessive PDF parsing, widget removal, and copying for each page. Refactor to minimize PDF I/O operations, possibly by working with `pypdf` page objects directly.
|
|
21
|
-
# TODO: The `read` method triggers `trigger_widget_hooks` and `enable_adobe_mode`, both of which can involve PDF parsing and writing. Since `read` is called frequently, this can be a performance bottleneck. Consider a more granular dirty-flag system to only apply changes when necessary, or accumulate changes and apply them in a single PDF write operation.
|
|
22
|
-
# TODO: The `write` method calls `self.read()`, which in turn triggers all pending operations. This can lead to redundant processing if `read()` has already been called or if multiple `write()` calls are made.
|
|
23
|
-
# TODO: In `change_version`, replacing a byte string in the entire PDF stream can be inefficient for very large PDFs. Consider if `pypdf` offers a more direct way to update the PDF version without full stream manipulation.
|
|
24
|
-
# TODO: In `generate_coordinate_grid`, `self.read()` is called multiple times, and then `remove_all_widgets`, `generate_coordinate_grid`, and `copy_watermark_widgets` are called, all of which involve PDF parsing and manipulation. Optimize by minimizing PDF I/O and object re-creation.
|
|
25
|
-
# TODO: In `fill`, `self.read()` is called, and then `fill` (from `filler.py`), `remove_all_widgets`, and `copy_watermark_widgets` are called. This is a major operation and likely a performance hotspot due to repeated PDF processing. Streamline the PDF modification workflow to reduce redundant parsing and writing.
|
|
26
|
-
# TODO: In `create_widget`, `obj.watermarks(self.read())` and `copy_watermark_widgets(self.read(), watermarks, [name], None)` involve reading the PDF multiple times. Optimize by passing the PDF stream or `PdfReader` object more efficiently.
|
|
27
|
-
# TODO: The `commit_widget_key_updates` method calls `update_widget_keys`, which involves re-parsing and writing the PDF. For bulk updates, consider a mechanism to apply all key changes in a single PDF modification operation.
|
|
28
|
-
# TODO: General: Many methods repeatedly call `self.read()`, which re-parses the PDF. Consider maintaining a persistent `pypdf.PdfReader` and `pypdf.PdfWriter` object internally and only writing to a byte stream when explicitly requested (e.g., by the `read()` or `write()` methods) to avoid redundant I/O and parsing overhead.
|
|
29
18
|
|
|
30
19
|
from __future__ import annotations
|
|
31
20
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyPDFForm
|
|
3
|
-
Version: 3.5.
|
|
3
|
+
Version: 3.5.4
|
|
4
4
|
Summary: The Python library for PDF forms.
|
|
5
5
|
Author: Jinge Li
|
|
6
6
|
License-Expression: MIT
|
|
@@ -10,14 +10,14 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
18
|
Classifier: Operating System :: OS Independent
|
|
19
19
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
23
|
Requires-Dist: cryptography
|
|
@@ -17,15 +17,15 @@ classifiers = [
|
|
|
17
17
|
"Intended Audience :: Developers",
|
|
18
18
|
"Programming Language :: Python :: 3",
|
|
19
19
|
"Programming Language :: Python :: 3 :: Only",
|
|
20
|
-
"Programming Language :: Python :: 3.9",
|
|
21
20
|
"Programming Language :: Python :: 3.10",
|
|
22
21
|
"Programming Language :: Python :: 3.11",
|
|
23
22
|
"Programming Language :: Python :: 3.12",
|
|
24
23
|
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
25
|
"Operating System :: OS Independent",
|
|
26
26
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
27
|
]
|
|
28
|
-
requires-python = ">=3.
|
|
28
|
+
requires-python = ">=3.10"
|
|
29
29
|
dependencies = [
|
|
30
30
|
"cryptography",
|
|
31
31
|
"fonttools",
|
|
@@ -132,3 +132,8 @@ version = {attr = "PyPDFForm.__version__"}
|
|
|
132
132
|
|
|
133
133
|
[tool.setuptools.packages.find]
|
|
134
134
|
include = ["PyPDFForm*"]
|
|
135
|
+
|
|
136
|
+
[tool.pytest.ini_options]
|
|
137
|
+
markers = [
|
|
138
|
+
"posix_only",
|
|
139
|
+
]
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
5
7
|
from PyPDFForm import Fields, PdfWrapper
|
|
6
8
|
|
|
7
9
|
|
|
@@ -110,6 +112,7 @@ def test_issue_613(pdf_samples, request):
|
|
|
110
112
|
assert obj.read() == expected
|
|
111
113
|
|
|
112
114
|
|
|
115
|
+
@pytest.mark.posix_only
|
|
113
116
|
def test_sample_template_library(
|
|
114
117
|
pdf_samples, image_samples, sample_font_stream, request
|
|
115
118
|
):
|
|
@@ -25,6 +25,7 @@ def test_create_not_supported_type_not_working(template_stream):
|
|
|
25
25
|
assert r
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
@pytest.mark.posix_only
|
|
28
29
|
def test_create_checkbox_default(template_stream, pdf_samples, request):
|
|
29
30
|
expected_path = os.path.join(
|
|
30
31
|
pdf_samples, "widget", "test_create_checkbox_default.pdf"
|
|
@@ -49,6 +50,7 @@ def test_create_checkbox_default(template_stream, pdf_samples, request):
|
|
|
49
50
|
assert obj.read() == expected
|
|
50
51
|
|
|
51
52
|
|
|
53
|
+
@pytest.mark.posix_only
|
|
52
54
|
def test_create_checkbox_default_filled(template_stream, pdf_samples, request):
|
|
53
55
|
expected_path = os.path.join(
|
|
54
56
|
pdf_samples, "widget", "test_create_checkbox_default_filled.pdf"
|
|
@@ -73,6 +75,7 @@ def test_create_checkbox_default_filled(template_stream, pdf_samples, request):
|
|
|
73
75
|
assert obj.read() == expected
|
|
74
76
|
|
|
75
77
|
|
|
78
|
+
@pytest.mark.posix_only
|
|
76
79
|
def test_create_checkbox_default_filled_flatten(template_stream, pdf_samples, request):
|
|
77
80
|
expected_path = os.path.join(
|
|
78
81
|
pdf_samples, "widget", "test_create_checkbox_default_filled_flatten.pdf"
|
|
@@ -97,6 +100,7 @@ def test_create_checkbox_default_filled_flatten(template_stream, pdf_samples, re
|
|
|
97
100
|
assert obj.read() == expected
|
|
98
101
|
|
|
99
102
|
|
|
103
|
+
@pytest.mark.posix_only
|
|
100
104
|
def test_create_checkbox_complex(template_stream, pdf_samples, request):
|
|
101
105
|
expected_path = os.path.join(
|
|
102
106
|
pdf_samples, "widget", "test_create_checkbox_complex.pdf"
|
|
@@ -128,6 +132,7 @@ def test_create_checkbox_complex(template_stream, pdf_samples, request):
|
|
|
128
132
|
assert obj.read() == expected
|
|
129
133
|
|
|
130
134
|
|
|
135
|
+
@pytest.mark.posix_only
|
|
131
136
|
def test_create_checkbox_complex_fill(template_stream, pdf_samples, request):
|
|
132
137
|
expected_path = os.path.join(
|
|
133
138
|
pdf_samples, "widget", "test_create_checkbox_complex_fill.pdf"
|
|
@@ -160,6 +165,7 @@ def test_create_checkbox_complex_fill(template_stream, pdf_samples, request):
|
|
|
160
165
|
assert obj.read() == expected
|
|
161
166
|
|
|
162
167
|
|
|
168
|
+
@pytest.mark.posix_only
|
|
163
169
|
def test_create_checkbox_complex_fill_flatten(template_stream, pdf_samples, request):
|
|
164
170
|
expected_path = os.path.join(
|
|
165
171
|
pdf_samples, "widget", "test_create_checkbox_complex_fill_flatten.pdf"
|
|
@@ -192,6 +198,7 @@ def test_create_checkbox_complex_fill_flatten(template_stream, pdf_samples, requ
|
|
|
192
198
|
assert obj.read() == expected
|
|
193
199
|
|
|
194
200
|
|
|
201
|
+
@pytest.mark.posix_only
|
|
195
202
|
def test_create_checkbox_check_fill(template_stream, pdf_samples, request):
|
|
196
203
|
expected_path = os.path.join(
|
|
197
204
|
pdf_samples, "widget", "test_create_checkbox_check_fill.pdf"
|
|
@@ -217,6 +224,7 @@ def test_create_checkbox_check_fill(template_stream, pdf_samples, request):
|
|
|
217
224
|
assert obj.read() == expected
|
|
218
225
|
|
|
219
226
|
|
|
227
|
+
@pytest.mark.posix_only
|
|
220
228
|
def test_create_checkbox_check_fill_flatten(template_stream, pdf_samples, request):
|
|
221
229
|
expected_path = os.path.join(
|
|
222
230
|
pdf_samples, "widget", "test_create_checkbox_check_fill_flatten.pdf"
|
|
@@ -242,6 +250,7 @@ def test_create_checkbox_check_fill_flatten(template_stream, pdf_samples, reques
|
|
|
242
250
|
assert obj.read() == expected
|
|
243
251
|
|
|
244
252
|
|
|
253
|
+
@pytest.mark.posix_only
|
|
245
254
|
def test_create_checkbox_circle_fill(template_stream, pdf_samples, request):
|
|
246
255
|
expected_path = os.path.join(
|
|
247
256
|
pdf_samples, "widget", "test_create_checkbox_circle_fill.pdf"
|
|
@@ -267,6 +276,7 @@ def test_create_checkbox_circle_fill(template_stream, pdf_samples, request):
|
|
|
267
276
|
assert obj.read() == expected
|
|
268
277
|
|
|
269
278
|
|
|
279
|
+
@pytest.mark.posix_only
|
|
270
280
|
def test_create_checkbox_circle_fill_flatten(template_stream, pdf_samples, request):
|
|
271
281
|
expected_path = os.path.join(
|
|
272
282
|
pdf_samples, "widget", "test_create_checkbox_circle_fill_flatten.pdf"
|
|
@@ -292,6 +302,7 @@ def test_create_checkbox_circle_fill_flatten(template_stream, pdf_samples, reque
|
|
|
292
302
|
assert obj.read() == expected
|
|
293
303
|
|
|
294
304
|
|
|
305
|
+
@pytest.mark.posix_only
|
|
295
306
|
def test_create_checkbox_cross_fill(template_stream, pdf_samples, request):
|
|
296
307
|
expected_path = os.path.join(
|
|
297
308
|
pdf_samples, "widget", "test_create_checkbox_cross_fill.pdf"
|
|
@@ -317,6 +328,7 @@ def test_create_checkbox_cross_fill(template_stream, pdf_samples, request):
|
|
|
317
328
|
assert obj.read() == expected
|
|
318
329
|
|
|
319
330
|
|
|
331
|
+
@pytest.mark.posix_only
|
|
320
332
|
def test_create_checkbox_cross_fill_flatten(template_stream, pdf_samples, request):
|
|
321
333
|
expected_path = os.path.join(
|
|
322
334
|
pdf_samples, "widget", "test_create_checkbox_cross_fill_flatten.pdf"
|
|
@@ -342,6 +354,7 @@ def test_create_checkbox_cross_fill_flatten(template_stream, pdf_samples, reques
|
|
|
342
354
|
assert obj.read() == expected
|
|
343
355
|
|
|
344
356
|
|
|
357
|
+
@pytest.mark.posix_only
|
|
345
358
|
def test_create_text_default(template_stream, pdf_samples, request):
|
|
346
359
|
expected_path = os.path.join(pdf_samples, "widget", "test_create_text_default.pdf")
|
|
347
360
|
with open(expected_path, "rb+") as f:
|
|
@@ -364,6 +377,7 @@ def test_create_text_default(template_stream, pdf_samples, request):
|
|
|
364
377
|
assert obj.read() == expected
|
|
365
378
|
|
|
366
379
|
|
|
380
|
+
@pytest.mark.posix_only
|
|
367
381
|
def test_create_text_alpha_bg_color(template_stream, pdf_samples, request):
|
|
368
382
|
expected_path = os.path.join(
|
|
369
383
|
pdf_samples, "widget", "test_create_text_alpha_bg_color.pdf"
|
|
@@ -389,6 +403,7 @@ def test_create_text_alpha_bg_color(template_stream, pdf_samples, request):
|
|
|
389
403
|
assert obj.read() == expected
|
|
390
404
|
|
|
391
405
|
|
|
406
|
+
@pytest.mark.posix_only
|
|
392
407
|
def test_create_text_align_center(template_stream, pdf_samples, request):
|
|
393
408
|
expected_path = os.path.join(
|
|
394
409
|
pdf_samples, "widget", "test_create_text_align_center.pdf"
|
|
@@ -414,6 +429,7 @@ def test_create_text_align_center(template_stream, pdf_samples, request):
|
|
|
414
429
|
assert obj.read() == expected
|
|
415
430
|
|
|
416
431
|
|
|
432
|
+
@pytest.mark.posix_only
|
|
417
433
|
def test_create_text_align_right(template_stream, pdf_samples, request):
|
|
418
434
|
expected_path = os.path.join(
|
|
419
435
|
pdf_samples, "widget", "test_create_text_align_right.pdf"
|
|
@@ -439,6 +455,7 @@ def test_create_text_align_right(template_stream, pdf_samples, request):
|
|
|
439
455
|
assert obj.read() == expected
|
|
440
456
|
|
|
441
457
|
|
|
458
|
+
@pytest.mark.posix_only
|
|
442
459
|
def test_create_text_multiline(template_stream, pdf_samples, request):
|
|
443
460
|
expected_path = os.path.join(
|
|
444
461
|
pdf_samples, "widget", "test_create_text_align_multiline.pdf"
|
|
@@ -464,6 +481,7 @@ def test_create_text_multiline(template_stream, pdf_samples, request):
|
|
|
464
481
|
assert obj.read() == expected
|
|
465
482
|
|
|
466
483
|
|
|
484
|
+
@pytest.mark.posix_only
|
|
467
485
|
def test_create_text_default_filled(template_stream, pdf_samples, request):
|
|
468
486
|
expected_path = os.path.join(
|
|
469
487
|
pdf_samples, "widget", "test_create_text_default_filled.pdf"
|
|
@@ -488,6 +506,7 @@ def test_create_text_default_filled(template_stream, pdf_samples, request):
|
|
|
488
506
|
assert obj.read() == expected
|
|
489
507
|
|
|
490
508
|
|
|
509
|
+
@pytest.mark.posix_only
|
|
491
510
|
def test_create_text_default_filled_flatten(template_stream, pdf_samples, request):
|
|
492
511
|
expected_path = os.path.join(
|
|
493
512
|
pdf_samples, "widget", "test_create_text_default_filled_flatten.pdf"
|
|
@@ -512,6 +531,7 @@ def test_create_text_default_filled_flatten(template_stream, pdf_samples, reques
|
|
|
512
531
|
assert obj.read() == expected
|
|
513
532
|
|
|
514
533
|
|
|
534
|
+
@pytest.mark.posix_only
|
|
515
535
|
def test_create_text_complex(template_stream, pdf_samples, sample_font_stream, request):
|
|
516
536
|
expected_path = os.path.join(pdf_samples, "widget", "test_create_text_complex.pdf")
|
|
517
537
|
with open(expected_path, "rb+") as f:
|
|
@@ -548,6 +568,7 @@ def test_create_text_complex(template_stream, pdf_samples, sample_font_stream, r
|
|
|
548
568
|
assert obj.read() == expected
|
|
549
569
|
|
|
550
570
|
|
|
571
|
+
@pytest.mark.posix_only
|
|
551
572
|
def test_create_text_complex_filled(
|
|
552
573
|
template_stream, pdf_samples, sample_font_stream, request
|
|
553
574
|
):
|
|
@@ -587,6 +608,7 @@ def test_create_text_complex_filled(
|
|
|
587
608
|
assert obj.read() == expected
|
|
588
609
|
|
|
589
610
|
|
|
611
|
+
@pytest.mark.posix_only
|
|
590
612
|
def test_create_text_complex_filled_flatten(
|
|
591
613
|
template_stream, pdf_samples, sample_font_stream, request
|
|
592
614
|
):
|
|
@@ -626,6 +648,7 @@ def test_create_text_complex_filled_flatten(
|
|
|
626
648
|
assert obj.read() == expected
|
|
627
649
|
|
|
628
650
|
|
|
651
|
+
@pytest.mark.posix_only
|
|
629
652
|
def test_create_text_comb(template_stream, pdf_samples, request):
|
|
630
653
|
expected_path = os.path.join(pdf_samples, "widget", "test_create_text_comb.pdf")
|
|
631
654
|
with open(expected_path, "rb+") as f:
|
|
@@ -649,6 +672,7 @@ def test_create_text_comb(template_stream, pdf_samples, request):
|
|
|
649
672
|
assert obj.read() == expected
|
|
650
673
|
|
|
651
674
|
|
|
675
|
+
@pytest.mark.posix_only
|
|
652
676
|
def test_create_checkbox_persist_old_widgets_fill(
|
|
653
677
|
template_stream, pdf_samples, request
|
|
654
678
|
):
|
|
@@ -678,6 +702,7 @@ def test_create_checkbox_persist_old_widgets_fill(
|
|
|
678
702
|
assert obj.read() == expected
|
|
679
703
|
|
|
680
704
|
|
|
705
|
+
@pytest.mark.posix_only
|
|
681
706
|
def test_create_checkbox_persist_old_widgets_fill_flatten(
|
|
682
707
|
template_stream, pdf_samples, request
|
|
683
708
|
):
|
|
@@ -709,6 +734,7 @@ def test_create_checkbox_persist_old_widgets_fill_flatten(
|
|
|
709
734
|
assert obj.read() == expected
|
|
710
735
|
|
|
711
736
|
|
|
737
|
+
@pytest.mark.posix_only
|
|
712
738
|
def test_create_widget_sejda_fill(sejda_template, pdf_samples, request):
|
|
713
739
|
expected_path = os.path.join(
|
|
714
740
|
pdf_samples, "widget", "test_create_widget_sejda_fill.pdf"
|
|
@@ -740,6 +766,7 @@ def test_create_widget_sejda_fill(sejda_template, pdf_samples, request):
|
|
|
740
766
|
assert obj.read() == expected
|
|
741
767
|
|
|
742
768
|
|
|
769
|
+
@pytest.mark.posix_only
|
|
743
770
|
def test_create_widget_sejda_fill_flatten_before(sejda_template, pdf_samples, request):
|
|
744
771
|
expected_path = os.path.join(
|
|
745
772
|
pdf_samples, "widget", "test_create_widget_sejda_fill_flatten_before.pdf"
|
|
@@ -771,6 +798,7 @@ def test_create_widget_sejda_fill_flatten_before(sejda_template, pdf_samples, re
|
|
|
771
798
|
assert obj.read() == expected
|
|
772
799
|
|
|
773
800
|
|
|
801
|
+
@pytest.mark.posix_only
|
|
774
802
|
def test_create_widget_sejda_fill_flatten_after(sejda_template, pdf_samples, request):
|
|
775
803
|
expected_path = os.path.join(
|
|
776
804
|
pdf_samples, "widget", "test_create_widget_sejda_fill_flatten_after.pdf"
|
|
@@ -828,6 +856,7 @@ def test_create_widget_sejda_schema(sejda_template):
|
|
|
828
856
|
assert len(schema["properties"]) == len(old_schema["properties"]) + 1
|
|
829
857
|
|
|
830
858
|
|
|
859
|
+
@pytest.mark.posix_only
|
|
831
860
|
def test_create_dropdown(template_stream, pdf_samples, sample_font_stream, request):
|
|
832
861
|
expected_path = os.path.join(pdf_samples, "widget", "test_create_dropdown.pdf")
|
|
833
862
|
with open(expected_path, "rb+") as f:
|
|
@@ -867,6 +896,7 @@ def test_create_dropdown(template_stream, pdf_samples, sample_font_stream, reque
|
|
|
867
896
|
assert obj.read() == expected
|
|
868
897
|
|
|
869
898
|
|
|
899
|
+
@pytest.mark.posix_only
|
|
870
900
|
def test_create_dropdown_with_export_values(template_stream, pdf_samples, request):
|
|
871
901
|
expected_path = os.path.join(
|
|
872
902
|
pdf_samples, "widget", "test_create_dropdown_with_export_values.pdf"
|
|
@@ -930,6 +960,7 @@ def test_fill_cmyk_color_flatten(pdf_samples, request):
|
|
|
930
960
|
assert obj.read() == expected
|
|
931
961
|
|
|
932
962
|
|
|
963
|
+
@pytest.mark.posix_only
|
|
933
964
|
def test_create_radio_default(template_stream, pdf_samples, request):
|
|
934
965
|
expected_path = os.path.join(pdf_samples, "widget", "test_create_radio_default.pdf")
|
|
935
966
|
with open(expected_path, "rb+") as f:
|
|
@@ -952,6 +983,7 @@ def test_create_radio_default(template_stream, pdf_samples, request):
|
|
|
952
983
|
assert obj.read() == expected
|
|
953
984
|
|
|
954
985
|
|
|
986
|
+
@pytest.mark.posix_only
|
|
955
987
|
def test_create_radio_default_filled(template_stream, pdf_samples, request):
|
|
956
988
|
expected_path = os.path.join(
|
|
957
989
|
pdf_samples, "widget", "test_create_radio_default_filled.pdf"
|
|
@@ -976,6 +1008,7 @@ def test_create_radio_default_filled(template_stream, pdf_samples, request):
|
|
|
976
1008
|
assert obj.read() == expected
|
|
977
1009
|
|
|
978
1010
|
|
|
1011
|
+
@pytest.mark.posix_only
|
|
979
1012
|
def test_create_radio_default_filled_flatten(template_stream, pdf_samples, request):
|
|
980
1013
|
expected_path = os.path.join(
|
|
981
1014
|
pdf_samples, "widget", "test_create_radio_default_filled_flatten.pdf"
|
|
@@ -1000,6 +1033,7 @@ def test_create_radio_default_filled_flatten(template_stream, pdf_samples, reque
|
|
|
1000
1033
|
assert obj.read() == expected
|
|
1001
1034
|
|
|
1002
1035
|
|
|
1036
|
+
@pytest.mark.posix_only
|
|
1003
1037
|
def test_create_radio_complex(template_stream, pdf_samples, request):
|
|
1004
1038
|
expected_path = os.path.join(pdf_samples, "widget", "test_create_radio_complex.pdf")
|
|
1005
1039
|
with open(expected_path, "rb+") as f:
|
|
@@ -1055,6 +1089,7 @@ def test_create_signature_default(template_stream, pdf_samples, request):
|
|
|
1055
1089
|
assert obj.read() == expected
|
|
1056
1090
|
|
|
1057
1091
|
|
|
1092
|
+
@pytest.mark.posix_only
|
|
1058
1093
|
def test_create_signature_default_filled(
|
|
1059
1094
|
template_stream, pdf_samples, image_samples, request
|
|
1060
1095
|
):
|
|
@@ -1086,6 +1121,7 @@ def test_create_signature_default_filled(
|
|
|
1086
1121
|
assert obj.read() == expected
|
|
1087
1122
|
|
|
1088
1123
|
|
|
1124
|
+
@pytest.mark.posix_only
|
|
1089
1125
|
def test_create_signature_default_filled_flatten(
|
|
1090
1126
|
template_stream, pdf_samples, image_samples, request
|
|
1091
1127
|
):
|
|
@@ -1209,6 +1245,7 @@ def test_create_image_default_filled_flatten(
|
|
|
1209
1245
|
assert obj.read() == expected
|
|
1210
1246
|
|
|
1211
1247
|
|
|
1248
|
+
@pytest.mark.posix_only
|
|
1212
1249
|
def test_create_required_fields(pdf_samples, request):
|
|
1213
1250
|
expected_path = os.path.join(
|
|
1214
1251
|
pdf_samples, "widget", "test_create_required_fields.pdf"
|
|
@@ -1270,6 +1307,7 @@ def test_create_required_fields(pdf_samples, request):
|
|
|
1270
1307
|
assert obj.read() == expected
|
|
1271
1308
|
|
|
1272
1309
|
|
|
1310
|
+
@pytest.mark.posix_only
|
|
1273
1311
|
def test_create_not_required_fields(pdf_samples, request):
|
|
1274
1312
|
expected_path = os.path.join(
|
|
1275
1313
|
pdf_samples, "widget", "test_create_not_required_fields.pdf"
|
|
@@ -1331,6 +1369,7 @@ def test_create_not_required_fields(pdf_samples, request):
|
|
|
1331
1369
|
assert obj.read() == expected
|
|
1332
1370
|
|
|
1333
1371
|
|
|
1372
|
+
@pytest.mark.posix_only
|
|
1334
1373
|
def test_create_fields_with_tooltips(pdf_samples, request):
|
|
1335
1374
|
expected_path = os.path.join(
|
|
1336
1375
|
pdf_samples, "widget", "test_create_fields_with_tooltips.pdf"
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
5
7
|
from PyPDFForm import PdfWrapper
|
|
6
8
|
|
|
7
9
|
|
|
@@ -270,6 +272,7 @@ def test_dropdown_four_flatten(sample_template_with_dropdown, pdf_samples, reque
|
|
|
270
272
|
assert obj.read() == expected
|
|
271
273
|
|
|
272
274
|
|
|
275
|
+
@pytest.mark.posix_only
|
|
273
276
|
def test_dropdown_alignment(dropdown_alignment, pdf_samples, request):
|
|
274
277
|
expected_path = os.path.join(pdf_samples, "dropdown", "test_dropdown_alignment.pdf")
|
|
275
278
|
with open(expected_path, "rb+") as f:
|
|
@@ -286,11 +289,11 @@ def test_dropdown_alignment(dropdown_alignment, pdf_samples, request):
|
|
|
286
289
|
|
|
287
290
|
expected = f.read()
|
|
288
291
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
assert obj.read() == expected
|
|
292
|
+
assert len(obj.read()) == len(expected)
|
|
293
|
+
assert obj.read() == expected
|
|
292
294
|
|
|
293
295
|
|
|
296
|
+
@pytest.mark.posix_only
|
|
294
297
|
def test_dropdown_alignment_flatten(dropdown_alignment, pdf_samples, request):
|
|
295
298
|
expected_path = os.path.join(
|
|
296
299
|
pdf_samples, "dropdown", "test_dropdown_alignment_flatten.pdf"
|
|
@@ -310,11 +313,11 @@ def test_dropdown_alignment_flatten(dropdown_alignment, pdf_samples, request):
|
|
|
310
313
|
|
|
311
314
|
expected = f.read()
|
|
312
315
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
assert obj.read() == expected
|
|
316
|
+
assert len(obj.read()) == len(expected)
|
|
317
|
+
assert obj.read() == expected
|
|
316
318
|
|
|
317
319
|
|
|
320
|
+
@pytest.mark.posix_only
|
|
318
321
|
def test_dropdown_alignment_flatten_then_unflatten(
|
|
319
322
|
dropdown_alignment, pdf_samples, request
|
|
320
323
|
):
|
|
@@ -337,9 +340,8 @@ def test_dropdown_alignment_flatten_then_unflatten(
|
|
|
337
340
|
|
|
338
341
|
expected = f.read()
|
|
339
342
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
assert obj.read() == expected
|
|
343
|
+
assert len(obj.read()) == len(expected)
|
|
344
|
+
assert obj.read() == expected
|
|
343
345
|
|
|
344
346
|
|
|
345
347
|
def test_dropdown_alignment_sejda(dropdown_alignment_sejda, pdf_samples, request):
|
|
@@ -99,7 +99,9 @@ def test_pdf_widths_match_computed_font_widths(
|
|
|
99
99
|
# Assume that rounding floats to 3 decimal is accurate for most cases
|
|
100
100
|
assert all(
|
|
101
101
|
round(pdf_width, 3) == round(computed_width, 3)
|
|
102
|
-
for pdf_width, computed_width in zip(
|
|
102
|
+
for pdf_width, computed_width in zip(
|
|
103
|
+
pdf_widths_array, computed_widths_array, strict=True
|
|
104
|
+
)
|
|
103
105
|
)
|
|
104
106
|
|
|
105
107
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
+
import pytest
|
|
5
6
|
from jsonschema import ValidationError, validate
|
|
6
7
|
|
|
7
8
|
from PyPDFForm import PdfWrapper
|
|
@@ -81,6 +82,7 @@ def test_register_bad_fonts():
|
|
|
81
82
|
assert "foo" not in obj.fonts
|
|
82
83
|
|
|
83
84
|
|
|
85
|
+
@pytest.mark.posix_only
|
|
84
86
|
def test_register_global_font_fill(
|
|
85
87
|
template_stream, pdf_samples, sample_font_stream, data_dict, request
|
|
86
88
|
):
|
|
@@ -110,6 +112,7 @@ def test_register_global_font_fill(
|
|
|
110
112
|
assert obj.read() == expected
|
|
111
113
|
|
|
112
114
|
|
|
115
|
+
@pytest.mark.posix_only
|
|
113
116
|
def test_register_global_font_fill_flatten(
|
|
114
117
|
template_stream, pdf_samples, sample_font_stream, data_dict, request
|
|
115
118
|
):
|
|
@@ -232,6 +235,7 @@ def test_fill_font_color_red_flatten(template_stream, pdf_samples, data_dict, re
|
|
|
232
235
|
assert obj.read() == expected
|
|
233
236
|
|
|
234
237
|
|
|
238
|
+
@pytest.mark.posix_only
|
|
235
239
|
def test_fill_with_customized_widgets(
|
|
236
240
|
template_stream, pdf_samples, sample_font_stream, data_dict, request
|
|
237
241
|
):
|
|
@@ -261,6 +265,7 @@ def test_fill_with_customized_widgets(
|
|
|
261
265
|
assert obj.read() == expected
|
|
262
266
|
|
|
263
267
|
|
|
268
|
+
@pytest.mark.posix_only
|
|
264
269
|
def test_fill_with_customized_widgets_flatten(
|
|
265
270
|
template_stream, pdf_samples, sample_font_stream, data_dict, request
|
|
266
271
|
):
|
|
@@ -592,6 +597,7 @@ def test_draw_image_on_sejda_template(
|
|
|
592
597
|
assert obj.read() == expected
|
|
593
598
|
|
|
594
599
|
|
|
600
|
+
@pytest.mark.posix_only
|
|
595
601
|
def test_draw_png_image_on_one_page(
|
|
596
602
|
template_stream, image_samples, pdf_samples, request
|
|
597
603
|
):
|
|
@@ -614,6 +620,7 @@ def test_draw_png_image_on_one_page(
|
|
|
614
620
|
assert obj.read() == expected
|
|
615
621
|
|
|
616
622
|
|
|
623
|
+
@pytest.mark.posix_only
|
|
617
624
|
def test_draw_transparent_png_image_on_one_page(
|
|
618
625
|
template_stream, image_samples, pdf_samples, request
|
|
619
626
|
):
|
|
@@ -816,6 +823,7 @@ def test_version(pdf_samples):
|
|
|
816
823
|
assert obj.version is None
|
|
817
824
|
|
|
818
825
|
|
|
826
|
+
@pytest.mark.posix_only
|
|
819
827
|
def test_fill_font_color(sample_template_with_font_colors, pdf_samples, request):
|
|
820
828
|
expected_path = os.path.join(pdf_samples, "test_fill_font_color.pdf")
|
|
821
829
|
with open(expected_path, "rb+") as f:
|
|
@@ -833,11 +841,11 @@ def test_fill_font_color(sample_template_with_font_colors, pdf_samples, request)
|
|
|
833
841
|
|
|
834
842
|
expected = f.read()
|
|
835
843
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
assert obj.read() == expected
|
|
844
|
+
assert len(obj.read()) == len(expected)
|
|
845
|
+
assert obj.read() == expected
|
|
839
846
|
|
|
840
847
|
|
|
848
|
+
@pytest.mark.posix_only
|
|
841
849
|
def test_fill_font_color_flatten(
|
|
842
850
|
sample_template_with_font_colors, pdf_samples, request
|
|
843
851
|
):
|
|
@@ -858,11 +866,11 @@ def test_fill_font_color_flatten(
|
|
|
858
866
|
|
|
859
867
|
expected = f.read()
|
|
860
868
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
assert obj.read() == expected
|
|
869
|
+
assert len(obj.read()) == len(expected)
|
|
870
|
+
assert obj.read() == expected
|
|
864
871
|
|
|
865
872
|
|
|
873
|
+
@pytest.mark.posix_only
|
|
866
874
|
def test_fill_complex_fonts(sample_template_with_complex_fonts, pdf_samples, request):
|
|
867
875
|
expected_path = os.path.join(pdf_samples, "test_fill_complex_fonts.pdf")
|
|
868
876
|
with open(expected_path, "rb+") as f:
|
|
@@ -888,11 +896,11 @@ def test_fill_complex_fonts(sample_template_with_complex_fonts, pdf_samples, req
|
|
|
888
896
|
|
|
889
897
|
expected = f.read()
|
|
890
898
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
assert obj.read() == expected
|
|
899
|
+
assert len(obj.read()) == len(expected)
|
|
900
|
+
assert obj.read() == expected
|
|
894
901
|
|
|
895
902
|
|
|
903
|
+
@pytest.mark.posix_only
|
|
896
904
|
def test_fill_complex_fonts_flatten(
|
|
897
905
|
sample_template_with_complex_fonts, pdf_samples, request
|
|
898
906
|
):
|
|
@@ -921,9 +929,8 @@ def test_fill_complex_fonts_flatten(
|
|
|
921
929
|
|
|
922
930
|
expected = f.read()
|
|
923
931
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
assert obj.read() == expected
|
|
932
|
+
assert len(obj.read()) == len(expected)
|
|
933
|
+
assert obj.read() == expected
|
|
927
934
|
|
|
928
935
|
|
|
929
936
|
def test_pages(template_stream, pdf_samples, request):
|
|
@@ -936,6 +943,7 @@ def test_pages(template_stream, pdf_samples, request):
|
|
|
936
943
|
assert obj.pages[0].read() == f.read()
|
|
937
944
|
|
|
938
945
|
|
|
946
|
+
@pytest.mark.posix_only
|
|
939
947
|
def test_pages_preserve_font(template_stream, pdf_samples, sample_font_stream, request):
|
|
940
948
|
expected_path = os.path.join(pdf_samples, "pages", "test_pages_preserve_font.pdf")
|
|
941
949
|
obj = PdfWrapper(template_stream)
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
5
7
|
from PyPDFForm import PdfWrapper
|
|
6
8
|
|
|
7
9
|
|
|
@@ -87,6 +89,7 @@ def test_paragraph_auto_wrap_flatten(
|
|
|
87
89
|
assert obj.read() == expected
|
|
88
90
|
|
|
89
91
|
|
|
92
|
+
@pytest.mark.posix_only
|
|
90
93
|
def test_paragraph_auto_font(
|
|
91
94
|
sample_template_with_paragraph_auto_font, pdf_samples, request
|
|
92
95
|
):
|
|
@@ -103,11 +106,11 @@ def test_paragraph_auto_font(
|
|
|
103
106
|
|
|
104
107
|
expected = f.read()
|
|
105
108
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
assert obj.read() == expected
|
|
109
|
+
assert len(obj.read()) == len(expected)
|
|
110
|
+
assert obj.read() == expected
|
|
109
111
|
|
|
110
112
|
|
|
113
|
+
@pytest.mark.posix_only
|
|
111
114
|
def test_paragraph_auto_font_flatten(
|
|
112
115
|
sample_template_with_paragraph_auto_font, pdf_samples, request
|
|
113
116
|
):
|
|
@@ -124,11 +127,11 @@ def test_paragraph_auto_font_flatten(
|
|
|
124
127
|
|
|
125
128
|
expected = f.read()
|
|
126
129
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
assert obj.read() == expected
|
|
130
|
+
assert len(obj.read()) == len(expected)
|
|
131
|
+
assert obj.read() == expected
|
|
130
132
|
|
|
131
133
|
|
|
134
|
+
@pytest.mark.posix_only
|
|
132
135
|
def test_paragraph_auto_font_auto_wrap(
|
|
133
136
|
sample_template_with_paragraph_auto_font, pdf_samples, request
|
|
134
137
|
):
|
|
@@ -147,11 +150,11 @@ def test_paragraph_auto_font_auto_wrap(
|
|
|
147
150
|
|
|
148
151
|
expected = f.read()
|
|
149
152
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
assert obj.read() == expected
|
|
153
|
+
assert len(obj.read()) == len(expected)
|
|
154
|
+
assert obj.read() == expected
|
|
153
155
|
|
|
154
156
|
|
|
157
|
+
@pytest.mark.posix_only
|
|
155
158
|
def test_paragraph_auto_font_auto_wrap_flatten(
|
|
156
159
|
sample_template_with_paragraph_auto_font, pdf_samples, request
|
|
157
160
|
):
|
|
@@ -171,9 +174,8 @@ def test_paragraph_auto_font_auto_wrap_flatten(
|
|
|
171
174
|
|
|
172
175
|
expected = f.read()
|
|
173
176
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
assert obj.read() == expected
|
|
177
|
+
assert len(obj.read()) == len(expected)
|
|
178
|
+
assert obj.read() == expected
|
|
177
179
|
|
|
178
180
|
|
|
179
181
|
def test_fill_sejda_complex(sejda_template_complex, pdf_samples, request):
|
|
@@ -314,6 +316,7 @@ def test_sejda_complex_paragraph_multiple_line_alignment_flatten(
|
|
|
314
316
|
assert obj.read() == expected
|
|
315
317
|
|
|
316
318
|
|
|
319
|
+
@pytest.mark.posix_only
|
|
317
320
|
def test_paragraph_complex(sample_template_paragraph_complex, pdf_samples, request):
|
|
318
321
|
expected_path = os.path.join(pdf_samples, "paragraph", "test_paragraph_complex.pdf")
|
|
319
322
|
with open(expected_path, "rb+") as f:
|
|
@@ -333,11 +336,11 @@ def test_paragraph_complex(sample_template_paragraph_complex, pdf_samples, reque
|
|
|
333
336
|
|
|
334
337
|
expected = f.read()
|
|
335
338
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
assert obj.read() == expected
|
|
339
|
+
assert len(obj.read()) == len(expected)
|
|
340
|
+
assert obj.read() == expected
|
|
339
341
|
|
|
340
342
|
|
|
343
|
+
@pytest.mark.posix_only
|
|
341
344
|
def test_paragraph_complex_flatten(
|
|
342
345
|
sample_template_paragraph_complex, pdf_samples, request
|
|
343
346
|
):
|
|
@@ -362,11 +365,11 @@ def test_paragraph_complex_flatten(
|
|
|
362
365
|
|
|
363
366
|
expected = f.read()
|
|
364
367
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
assert obj.read() == expected
|
|
368
|
+
assert len(obj.read()) == len(expected)
|
|
369
|
+
assert obj.read() == expected
|
|
368
370
|
|
|
369
371
|
|
|
372
|
+
@pytest.mark.posix_only
|
|
370
373
|
def test_paragraph_max_length(
|
|
371
374
|
sample_template_with_paragraph_max_length, pdf_samples, request
|
|
372
375
|
):
|
|
@@ -385,11 +388,11 @@ def test_paragraph_max_length(
|
|
|
385
388
|
|
|
386
389
|
expected = f.read()
|
|
387
390
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
assert obj.read() == expected
|
|
391
|
+
assert len(obj.read()) == len(expected)
|
|
392
|
+
assert obj.read() == expected
|
|
391
393
|
|
|
392
394
|
|
|
395
|
+
@pytest.mark.posix_only
|
|
393
396
|
def test_paragraph_max_length_flatten(
|
|
394
397
|
sample_template_with_paragraph_max_length, pdf_samples, request
|
|
395
398
|
):
|
|
@@ -409,6 +412,5 @@ def test_paragraph_max_length_flatten(
|
|
|
409
412
|
|
|
410
413
|
expected = f.read()
|
|
411
414
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
assert obj.read() == expected
|
|
415
|
+
assert len(obj.read()) == len(expected)
|
|
416
|
+
assert obj.read() == expected
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
5
7
|
from PyPDFForm import PdfWrapper
|
|
6
8
|
|
|
7
9
|
|
|
10
|
+
@pytest.mark.posix_only
|
|
8
11
|
def test_fill_signature(pdf_samples, image_samples, request):
|
|
9
12
|
expected_path = os.path.join(pdf_samples, "signature", "test_fill_signature.pdf")
|
|
10
13
|
with open(expected_path, "rb+") as f:
|
|
@@ -17,9 +20,8 @@ def test_fill_signature(pdf_samples, image_samples, request):
|
|
|
17
20
|
|
|
18
21
|
expected = f.read()
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
assert obj.read() == expected
|
|
23
|
+
assert len(obj.read()) == len(expected)
|
|
24
|
+
assert obj.read() == expected
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
def test_signature_schema(pdf_samples):
|
|
@@ -40,6 +42,7 @@ def test_signature_sample_value(pdf_samples):
|
|
|
40
42
|
)
|
|
41
43
|
|
|
42
44
|
|
|
45
|
+
@pytest.mark.posix_only
|
|
43
46
|
def test_fill_signature_overlap(pdf_samples, image_samples, request):
|
|
44
47
|
expected_path = os.path.join(
|
|
45
48
|
pdf_samples, "signature", "test_fill_signature_overlap.pdf"
|
|
@@ -60,6 +63,7 @@ def test_fill_signature_overlap(pdf_samples, image_samples, request):
|
|
|
60
63
|
assert obj.read() == expected
|
|
61
64
|
|
|
62
65
|
|
|
66
|
+
@pytest.mark.posix_only
|
|
63
67
|
def test_fill_signature_overlap_not_preserve_aspect_ratio(
|
|
64
68
|
pdf_samples, image_samples, request
|
|
65
69
|
):
|
|
@@ -86,6 +90,7 @@ def test_fill_signature_overlap_not_preserve_aspect_ratio(
|
|
|
86
90
|
assert obj.read() == expected
|
|
87
91
|
|
|
88
92
|
|
|
93
|
+
@pytest.mark.posix_only
|
|
89
94
|
def test_fill_small_icon(pdf_samples, image_samples, request):
|
|
90
95
|
expected_path = os.path.join(pdf_samples, "signature", "test_fill_small_icon.pdf")
|
|
91
96
|
with open(expected_path, "rb+") as f:
|
|
@@ -105,6 +110,7 @@ def test_fill_small_icon(pdf_samples, image_samples, request):
|
|
|
105
110
|
assert obj.read() == expected
|
|
106
111
|
|
|
107
112
|
|
|
113
|
+
@pytest.mark.posix_only
|
|
108
114
|
def test_fill_small_icon_not_preserve_aspect_ratio(pdf_samples, image_samples, request):
|
|
109
115
|
expected_path = os.path.join(
|
|
110
116
|
pdf_samples, "signature", "test_fill_small_icon_not_preserve_aspect_ratio.pdf"
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
5
7
|
from PyPDFForm import Fields, PdfWrapper
|
|
6
8
|
|
|
7
9
|
|
|
10
|
+
@pytest.mark.posix_only
|
|
8
11
|
def test_register_font_no_form_fields(pdf_samples, sample_font_stream, request):
|
|
9
12
|
expected_path = os.path.join(
|
|
10
13
|
pdf_samples, "test_widget_attr_trigger", "test_register_font_no_form_fields.pdf"
|
|
@@ -25,6 +28,7 @@ def test_register_font_no_form_fields(pdf_samples, sample_font_stream, request):
|
|
|
25
28
|
assert obj.read() == expected
|
|
26
29
|
|
|
27
30
|
|
|
31
|
+
@pytest.mark.posix_only
|
|
28
32
|
def test_set_text_field_font(pdf_samples, font_samples, template_stream, request):
|
|
29
33
|
expected_path = os.path.join(
|
|
30
34
|
pdf_samples, "test_widget_attr_trigger", "test_set_text_field_font.pdf"
|
|
@@ -44,6 +48,7 @@ def test_set_text_field_font(pdf_samples, font_samples, template_stream, request
|
|
|
44
48
|
assert obj.read() == expected
|
|
45
49
|
|
|
46
50
|
|
|
51
|
+
@pytest.mark.posix_only
|
|
47
52
|
def test_set_text_field_font_sejda(pdf_samples, font_samples, sejda_template, request):
|
|
48
53
|
expected_path = os.path.join(
|
|
49
54
|
pdf_samples,
|
|
@@ -354,6 +359,7 @@ def test_set_radio_size_sejda(pdf_samples, sejda_template, request):
|
|
|
354
359
|
assert obj.read() == expected
|
|
355
360
|
|
|
356
361
|
|
|
362
|
+
@pytest.mark.posix_only
|
|
357
363
|
def test_set_dropdown_font(
|
|
358
364
|
pdf_samples, sample_template_with_dropdown, sample_font_stream, request
|
|
359
365
|
):
|
|
@@ -374,6 +380,7 @@ def test_set_dropdown_font(
|
|
|
374
380
|
assert obj.read() == expected
|
|
375
381
|
|
|
376
382
|
|
|
383
|
+
@pytest.mark.posix_only
|
|
377
384
|
def test_set_dropdown_font_sejda(
|
|
378
385
|
pdf_samples, dropdown_alignment_sejda, sample_font_stream, request
|
|
379
386
|
):
|
|
@@ -462,3 +469,22 @@ def test_set_dropdown_font_color_sejda(pdf_samples, dropdown_alignment_sejda, re
|
|
|
462
469
|
|
|
463
470
|
assert len(obj.read()) == len(expected)
|
|
464
471
|
assert obj.read() == expected
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def test_set_text_field_required_sejda(pdf_samples, sejda_template, request):
|
|
475
|
+
expected_path = os.path.join(
|
|
476
|
+
pdf_samples,
|
|
477
|
+
"test_widget_attr_trigger",
|
|
478
|
+
"test_set_text_field_required_sejda.pdf",
|
|
479
|
+
)
|
|
480
|
+
with open(expected_path, "rb+") as f:
|
|
481
|
+
obj = PdfWrapper(sejda_template)
|
|
482
|
+
obj.widgets["buyer_address"].required = True
|
|
483
|
+
|
|
484
|
+
request.config.results["expected_path"] = expected_path
|
|
485
|
+
request.config.results["stream"] = obj.read()
|
|
486
|
+
|
|
487
|
+
expected = f.read()
|
|
488
|
+
|
|
489
|
+
assert len(obj.read()) == len(expected)
|
|
490
|
+
assert obj.read() == expected
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|