PyPDFForm 4.2.0__tar.gz → 4.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PKG-INFO +1 -1
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/__init__.py +1 -1
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/ap.py +31 -25
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/template.py +2 -93
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/watermark.py +2 -2
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/wrapper.py +27 -38
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm.egg-info/PKG-INFO +1 -1
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_js.py +27 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/LICENSE +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/adapter.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/assets/__init__.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/assets/bedrock.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/assets/blank.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/constants.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/coordinate.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/deprecation.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/filler.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/font.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/hooks.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/image.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/middleware/__init__.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/middleware/base.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/middleware/checkbox.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/middleware/dropdown.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/middleware/image.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/middleware/radio.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/middleware/signature.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/middleware/text.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/patterns.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/raw/__init__.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/raw/circle.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/raw/ellipse.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/raw/image.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/raw/line.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/raw/rect.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/raw/text.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/types.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/utils.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/widgets/__init__.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/widgets/base.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/widgets/checkbox.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/widgets/dropdown.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/widgets/image.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/widgets/radio.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/widgets/signature.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm/widgets/text.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm.egg-info/SOURCES.txt +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm.egg-info/dependency_links.txt +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm.egg-info/requires.txt +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/PyPDFForm.egg-info/top_level.txt +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/README.md +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/pyproject.toml +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/setup.cfg +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_bulk_create_fields.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_create_widget.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_draw_elements.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_dropdown.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_extract_values.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_fill_max_length_text_field.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_fill_method.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_font_widths.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_functional.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_generate_appearance_streams.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_need_appearances.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_paragraph.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_signature.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_use_full_widget_name.py +0 -0
- {pypdfform-4.2.0 → pypdfform-4.2.2}/tests/test_widget_attr_trigger.py +0 -0
|
@@ -20,7 +20,7 @@ The library supports various PDF form features, including:
|
|
|
20
20
|
PyPDFForm aims to simplify PDF form manipulation, making it accessible to developers of all skill levels.
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
__version__ = "4.2.
|
|
23
|
+
__version__ = "4.2.2"
|
|
24
24
|
|
|
25
25
|
from .assets.blank import BlankPage
|
|
26
26
|
from .middleware import Widgets
|
|
@@ -12,10 +12,10 @@ from io import BytesIO
|
|
|
12
12
|
|
|
13
13
|
from pikepdf import Pdf
|
|
14
14
|
from pypdf import PdfReader, PdfWriter
|
|
15
|
+
from pypdf.generic import DictionaryObject, NameObject, TextStringObject
|
|
15
16
|
|
|
16
|
-
from .constants import XFA, AcroForm, Root
|
|
17
|
-
|
|
18
|
-
set_on_open_javascript, set_pdf_title)
|
|
17
|
+
from .constants import (JS, XFA, AcroForm, JavaScript, OpenAction, Root, S,
|
|
18
|
+
Title)
|
|
19
19
|
from .utils import stream_to_io
|
|
20
20
|
|
|
21
21
|
|
|
@@ -31,8 +31,6 @@ def appearance_streams_handler(pdf: bytes, generate_appearance_streams: bool) ->
|
|
|
31
31
|
PDF viewers to generate appearance streams for form fields.
|
|
32
32
|
3. Optionally generating appearance streams explicitly using pikepdf if
|
|
33
33
|
`generate_appearance_streams` is True.
|
|
34
|
-
4. Preserving the title from the original PDF.
|
|
35
|
-
5. Preserving the on-open JavaScript from the original PDF.
|
|
36
34
|
|
|
37
35
|
The result is cached using lru_cache for performance.
|
|
38
36
|
|
|
@@ -65,35 +63,43 @@ def appearance_streams_handler(pdf: bytes, generate_appearance_streams: bool) ->
|
|
|
65
63
|
r.seek(0)
|
|
66
64
|
result = r.read()
|
|
67
65
|
|
|
68
|
-
|
|
69
|
-
return preserve_on_open_javascript(pdf, result)
|
|
66
|
+
return result
|
|
70
67
|
|
|
71
68
|
|
|
72
|
-
|
|
69
|
+
@lru_cache
|
|
70
|
+
def preserve_pdf_properties(pdf: bytes, title: str, script: str) -> bytes:
|
|
73
71
|
"""
|
|
74
|
-
Preserves
|
|
72
|
+
Preserves and updates PDF properties such as title and OpenAction scripts.
|
|
73
|
+
|
|
74
|
+
This function allows setting or updating the PDF's title in its metadata and
|
|
75
|
+
attaching a JavaScript script that executes when the PDF is opened.
|
|
75
76
|
|
|
76
77
|
Args:
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
pdf (bytes): The PDF file content as a bytes stream.
|
|
79
|
+
title (str): The title to be set in the PDF metadata.
|
|
80
|
+
script (str): JavaScript code to be executed when the PDF is opened.
|
|
79
81
|
|
|
80
82
|
Returns:
|
|
81
|
-
bytes: The modified
|
|
83
|
+
bytes: The modified PDF content as a bytes stream.
|
|
82
84
|
"""
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
reader = PdfReader(stream_to_io(pdf))
|
|
86
|
+
writer = PdfWriter()
|
|
87
|
+
writer.append(reader)
|
|
85
88
|
|
|
89
|
+
if title:
|
|
90
|
+
metadata = reader.metadata or {}
|
|
91
|
+
metadata[NameObject(Title)] = TextStringObject(title)
|
|
86
92
|
|
|
87
|
-
|
|
88
|
-
"""
|
|
89
|
-
Preserves the on-open JavaScript from the source PDF to the destination PDF.
|
|
93
|
+
writer.add_metadata(metadata)
|
|
90
94
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
if script:
|
|
96
|
+
open_action = DictionaryObject()
|
|
97
|
+
open_action[NameObject(S)] = NameObject(JavaScript)
|
|
98
|
+
open_action[NameObject(JS)] = TextStringObject(script)
|
|
94
99
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
writer._root_object.update({NameObject(OpenAction): open_action}) # type: ignore # noqa: SLF001 # # pylint: disable=W0212
|
|
101
|
+
|
|
102
|
+
with BytesIO() as f:
|
|
103
|
+
writer.write(f)
|
|
104
|
+
f.seek(0)
|
|
105
|
+
return f.read()
|
|
@@ -13,10 +13,9 @@ from io import BytesIO
|
|
|
13
13
|
from typing import Dict, List, Tuple, Union, cast
|
|
14
14
|
|
|
15
15
|
from pypdf import PdfReader, PdfWriter
|
|
16
|
-
from pypdf.generic import DictionaryObject
|
|
16
|
+
from pypdf.generic import DictionaryObject
|
|
17
17
|
|
|
18
|
-
from .constants import
|
|
19
|
-
OpenAction, Parent, S, T, Title)
|
|
18
|
+
from .constants import WIDGET_TYPES, Annots, MaxLen, Parent, T
|
|
20
19
|
from .middleware.checkbox import Checkbox
|
|
21
20
|
from .middleware.dropdown import Dropdown
|
|
22
21
|
from .middleware.radio import Radio
|
|
@@ -244,96 +243,6 @@ def get_dropdown_choices(widget: dict) -> Union[Tuple[str, ...], None]:
|
|
|
244
243
|
)
|
|
245
244
|
|
|
246
245
|
|
|
247
|
-
def get_on_open_javascript(pdf: bytes) -> Union[str, None]:
|
|
248
|
-
"""
|
|
249
|
-
Retrieves the JavaScript that runs when the PDF is opened.
|
|
250
|
-
|
|
251
|
-
Args:
|
|
252
|
-
pdf (bytes): The PDF file content as a bytes stream.
|
|
253
|
-
|
|
254
|
-
Returns:
|
|
255
|
-
Union[str, None]: The JavaScript that runs when the PDF is opened, or None if it's not present.
|
|
256
|
-
"""
|
|
257
|
-
reader = PdfReader(stream_to_io(pdf))
|
|
258
|
-
try:
|
|
259
|
-
return reader.root_object[OpenAction][JS]
|
|
260
|
-
except KeyError:
|
|
261
|
-
return None
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
def set_on_open_javascript(pdf: bytes, script: str) -> bytes:
|
|
265
|
-
"""
|
|
266
|
-
Sets the JavaScript that runs when the PDF is opened.
|
|
267
|
-
|
|
268
|
-
Args:
|
|
269
|
-
pdf (bytes): The PDF file content as a bytes stream.
|
|
270
|
-
script (str): The JavaScript to run when the PDF is opened.
|
|
271
|
-
|
|
272
|
-
Returns:
|
|
273
|
-
bytes: The modified PDF content as a bytes stream.
|
|
274
|
-
"""
|
|
275
|
-
if not script:
|
|
276
|
-
return pdf
|
|
277
|
-
|
|
278
|
-
reader = PdfReader(stream_to_io(pdf))
|
|
279
|
-
writer = PdfWriter()
|
|
280
|
-
writer.append(reader)
|
|
281
|
-
|
|
282
|
-
open_action = DictionaryObject()
|
|
283
|
-
open_action[NameObject(S)] = NameObject(JavaScript)
|
|
284
|
-
open_action[NameObject(JS)] = TextStringObject(script)
|
|
285
|
-
|
|
286
|
-
writer._root_object.update({NameObject(OpenAction): open_action}) # type: ignore # noqa: SLF001 # # pylint: disable=W0212
|
|
287
|
-
|
|
288
|
-
with BytesIO() as f:
|
|
289
|
-
writer.write(f)
|
|
290
|
-
f.seek(0)
|
|
291
|
-
return f.read()
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
def get_pdf_title(pdf: bytes) -> Union[str, None]:
|
|
295
|
-
"""
|
|
296
|
-
Retrieves the title of a PDF from its metadata.
|
|
297
|
-
|
|
298
|
-
Args:
|
|
299
|
-
pdf (bytes): The PDF file content as a bytes stream.
|
|
300
|
-
|
|
301
|
-
Returns:
|
|
302
|
-
Union[str, None]: The title of the PDF, or None if it's not present.
|
|
303
|
-
"""
|
|
304
|
-
reader = PdfReader(stream_to_io(pdf))
|
|
305
|
-
return (reader.metadata or {}).get(Title)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
def set_pdf_title(pdf: bytes, title: str) -> bytes:
|
|
309
|
-
"""
|
|
310
|
-
Sets the title of a PDF in its metadata.
|
|
311
|
-
|
|
312
|
-
Args:
|
|
313
|
-
pdf (bytes): The PDF file content as a bytes stream.
|
|
314
|
-
title (str): The new title for the PDF.
|
|
315
|
-
|
|
316
|
-
Returns:
|
|
317
|
-
bytes: The modified PDF content as a bytes stream.
|
|
318
|
-
"""
|
|
319
|
-
if not title:
|
|
320
|
-
return pdf
|
|
321
|
-
|
|
322
|
-
reader = PdfReader(stream_to_io(pdf))
|
|
323
|
-
writer = PdfWriter()
|
|
324
|
-
writer.append(reader)
|
|
325
|
-
|
|
326
|
-
metadata = reader.metadata or {}
|
|
327
|
-
metadata[NameObject(Title)] = TextStringObject(title)
|
|
328
|
-
|
|
329
|
-
writer.add_metadata(metadata)
|
|
330
|
-
|
|
331
|
-
with BytesIO() as f:
|
|
332
|
-
writer.write(f)
|
|
333
|
-
f.seek(0)
|
|
334
|
-
return f.read()
|
|
335
|
-
|
|
336
|
-
|
|
337
246
|
def update_widget_keys(
|
|
338
247
|
template: bytes,
|
|
339
248
|
widgets: Dict[str, WIDGET_TYPES],
|
|
@@ -305,13 +305,13 @@ def merge_watermarks_with_pdf(
|
|
|
305
305
|
result = BytesIO()
|
|
306
306
|
pdf_file = PdfReader(stream_to_io(pdf))
|
|
307
307
|
output = PdfWriter()
|
|
308
|
+
output.append(pdf_file)
|
|
308
309
|
|
|
309
|
-
for i, page in enumerate(
|
|
310
|
+
for i, page in enumerate(output.pages):
|
|
310
311
|
if watermarks[i]:
|
|
311
312
|
watermark = PdfReader(stream_to_io(watermarks[i]))
|
|
312
313
|
if watermark.pages:
|
|
313
314
|
page.merge_page(watermark.pages[0])
|
|
314
|
-
output.add_page(page)
|
|
315
315
|
|
|
316
316
|
output.write(result)
|
|
317
317
|
result.seek(0)
|
|
@@ -27,7 +27,7 @@ from typing import (TYPE_CHECKING, BinaryIO, Dict, Sequence, TextIO, Tuple,
|
|
|
27
27
|
|
|
28
28
|
from .adapter import (fp_or_f_obj_or_f_content_to_content,
|
|
29
29
|
fp_or_f_obj_or_stream_to_stream)
|
|
30
|
-
from .ap import appearance_streams_handler
|
|
30
|
+
from .ap import appearance_streams_handler, preserve_pdf_properties
|
|
31
31
|
from .constants import VERSION_IDENTIFIER_PREFIX, VERSION_IDENTIFIERS
|
|
32
32
|
from .coordinate import generate_coordinate_grid
|
|
33
33
|
from .filler import fill
|
|
@@ -38,9 +38,7 @@ from .middleware.dropdown import Dropdown
|
|
|
38
38
|
from .middleware.signature import Signature
|
|
39
39
|
from .middleware.text import Text
|
|
40
40
|
from .raw import RawText, RawTypes
|
|
41
|
-
from .template import
|
|
42
|
-
set_on_open_javascript, set_pdf_title,
|
|
43
|
-
update_widget_keys)
|
|
41
|
+
from .template import build_widgets, update_widget_keys
|
|
44
42
|
from .types import PdfWrapperList
|
|
45
43
|
from .utils import (generate_unique_suffix, get_page_streams, merge_pdfs,
|
|
46
44
|
remove_all_widgets)
|
|
@@ -106,6 +104,8 @@ class PdfWrapper:
|
|
|
106
104
|
super().__init__()
|
|
107
105
|
self._stream = fp_or_f_obj_or_stream_to_stream(template)
|
|
108
106
|
self.widgets = {}
|
|
107
|
+
self.title: str = None
|
|
108
|
+
self._on_open_javascript = None
|
|
109
109
|
self._available_fonts = {} # for setting /F1
|
|
110
110
|
self._font_register_events = [] # for reregister
|
|
111
111
|
self._key_update_tracker = {} # for update key preserve old key attrs
|
|
@@ -223,28 +223,6 @@ class PdfWrapper:
|
|
|
223
223
|
|
|
224
224
|
return self
|
|
225
225
|
|
|
226
|
-
@property
|
|
227
|
-
def title(self) -> Union[str, None]:
|
|
228
|
-
"""
|
|
229
|
-
Returns the title of the PDF document.
|
|
230
|
-
|
|
231
|
-
Returns:
|
|
232
|
-
Union[str, None]: The title of the PDF, or None if it's not set.
|
|
233
|
-
"""
|
|
234
|
-
|
|
235
|
-
return get_pdf_title(self._read())
|
|
236
|
-
|
|
237
|
-
@title.setter
|
|
238
|
-
def title(self, value: str) -> None:
|
|
239
|
-
"""
|
|
240
|
-
Sets the title of the PDF document.
|
|
241
|
-
|
|
242
|
-
Args:
|
|
243
|
-
value (str): The new title for the PDF document.
|
|
244
|
-
"""
|
|
245
|
-
|
|
246
|
-
self._stream = set_pdf_title(self._read(), value)
|
|
247
|
-
|
|
248
226
|
@property
|
|
249
227
|
def schema(self) -> dict:
|
|
250
228
|
"""
|
|
@@ -348,37 +326,41 @@ class PdfWrapper:
|
|
|
348
326
|
@property
|
|
349
327
|
def on_open_javascript(self) -> Union[str, None]:
|
|
350
328
|
"""
|
|
351
|
-
Returns the JavaScript that
|
|
329
|
+
Returns the JavaScript script that executes when the PDF is opened.
|
|
352
330
|
|
|
353
331
|
Returns:
|
|
354
|
-
Union[str, None]: The JavaScript
|
|
332
|
+
Union[str, None]: The JavaScript script, or None if no script is set.
|
|
355
333
|
"""
|
|
356
334
|
|
|
357
|
-
return
|
|
335
|
+
return self._on_open_javascript
|
|
358
336
|
|
|
359
337
|
@on_open_javascript.setter
|
|
360
338
|
def on_open_javascript(self, value: Union[str, TextIO]) -> None:
|
|
361
339
|
"""
|
|
362
|
-
Sets the JavaScript that
|
|
340
|
+
Sets the JavaScript script that executes when the PDF is opened.
|
|
363
341
|
|
|
364
342
|
Args:
|
|
365
|
-
value (Union[str, TextIO]): The JavaScript
|
|
366
|
-
|
|
343
|
+
value (Union[str, TextIO]): The JavaScript script, provided as either:
|
|
344
|
+
- str: The JavaScript code as a string, or a file path to a .js file.
|
|
345
|
+
- TextIO: An open file-like object containing the JavaScript code.
|
|
367
346
|
"""
|
|
368
347
|
|
|
369
|
-
self.
|
|
370
|
-
self._read(), fp_or_f_obj_or_f_content_to_content(value)
|
|
371
|
-
)
|
|
348
|
+
self._on_open_javascript = fp_or_f_obj_or_f_content_to_content(value)
|
|
372
349
|
|
|
373
350
|
def read(self) -> bytes:
|
|
374
351
|
"""
|
|
375
352
|
Reads the PDF document and returns its content as bytes.
|
|
376
353
|
|
|
377
|
-
This method retrieves the PDF stream and
|
|
378
|
-
|
|
354
|
+
This method retrieves the PDF stream and performs several processing steps:
|
|
355
|
+
1. Triggers widget hooks and updates font mappings (via `_read`).
|
|
356
|
+
2. If `need_appearances` is enabled, it handles appearance streams and the
|
|
357
|
+
`/NeedAppearances` flag, which may include removing XFA and explicitly
|
|
358
|
+
generating appearance streams.
|
|
359
|
+
3. If a title or on-open JavaScript is set, it updates the PDF properties
|
|
360
|
+
accordingly.
|
|
379
361
|
|
|
380
362
|
Returns:
|
|
381
|
-
bytes: The PDF document content as a byte string.
|
|
363
|
+
bytes: The processed PDF document content as a byte string.
|
|
382
364
|
"""
|
|
383
365
|
|
|
384
366
|
result = self._read()
|
|
@@ -387,6 +369,13 @@ class PdfWrapper:
|
|
|
387
369
|
result, getattr(self, "generate_appearance_streams")
|
|
388
370
|
) # cached
|
|
389
371
|
|
|
372
|
+
if any([self.title, self.on_open_javascript]):
|
|
373
|
+
result = preserve_pdf_properties(
|
|
374
|
+
result,
|
|
375
|
+
self.title,
|
|
376
|
+
self.on_open_javascript,
|
|
377
|
+
)
|
|
378
|
+
|
|
390
379
|
return result
|
|
391
380
|
|
|
392
381
|
def _read(self) -> bytes:
|
|
@@ -220,3 +220,30 @@ def test_on_open_script(template_stream, pdf_samples, request):
|
|
|
220
220
|
|
|
221
221
|
assert len(pdf.read()) == len(expected)
|
|
222
222
|
assert pdf.read() == expected
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def test_on_open_script_title_coexist(template_stream, pdf_samples, request):
|
|
226
|
+
expected_path = os.path.join(
|
|
227
|
+
pdf_samples, "js", "test_on_open_script_title_coexist.pdf"
|
|
228
|
+
)
|
|
229
|
+
with open(expected_path, "rb+") as f:
|
|
230
|
+
pdf = PdfWrapper(template_stream, title="My PDF")
|
|
231
|
+
pdf.on_open_javascript = 'this.getField("test").value = "opened";'
|
|
232
|
+
|
|
233
|
+
assert pdf.title == "My PDF"
|
|
234
|
+
assert pdf.on_open_javascript == 'this.getField("test").value = "opened";'
|
|
235
|
+
|
|
236
|
+
request.config.results["expected_path"] = expected_path
|
|
237
|
+
request.config.results["stream"] = pdf.read()
|
|
238
|
+
|
|
239
|
+
expected = f.read()
|
|
240
|
+
|
|
241
|
+
assert len(pdf.read()) == len(expected)
|
|
242
|
+
assert pdf.read() == expected
|
|
243
|
+
|
|
244
|
+
pdf2 = PdfWrapper(template_stream)
|
|
245
|
+
pdf2.on_open_javascript = 'this.getField("test").value = "opened";'
|
|
246
|
+
pdf2.title = "My PDF"
|
|
247
|
+
|
|
248
|
+
assert len(pdf2.read()) == len(expected)
|
|
249
|
+
assert pdf2.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
|
|
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
|
|
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
|