Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Add PdfWriter.open_destination property #1431

Merged
merged 11 commits into from Nov 25, 2022
Merged
48 changes: 48 additions & 0 deletions PyPDF2/_writer.py
Expand Up @@ -411,6 +411,54 @@ def insertBlankPage(
deprecate_with_replacement("insertBlankPage", "insert_blank_page")
return self.insert_blank_page(width, height, index)

@property
def opening(self) -> Union[None, Destination, TextStringObject, ByteStringObject]:
"""
property returning the opening Destination
returns None if no destination is set

(value stored in "/OpenAction" entry in the Pdf Catalog)
pubpub-zz marked this conversation as resolved.
Show resolved Hide resolved
"""
if "/OpenAction" not in self._root_object:
return None
oa = self._root_object["/OpenAction"]
if isinstance(oa, (str, bytes)):
return create_string_object(str(oa))
elif isinstance(oa, ArrayObject):
try:
page, typ = oa[0:2] # type: ignore
array = oa[2:]
return Destination("OpenAction", page, typ, *array) # type: ignore
except Exception:
raise Exception(f"Invalid Destination {oa}")
else:
return None

@opening.setter
def opening(self, dest: Union[None, str, Destination, PageObject]) -> None:
"""
property setting the opening Destination or Page or NamedDest (`str`)
None: removes the opening entry
:param destination:.

(value stored in "/OpenAction" entry in the Pdf Catalog)
pubpub-zz marked this conversation as resolved.
Show resolved Hide resolved
"""
if dest is None:
try:
del self._root_object["/OpenAction"]
except Exception:
pubpub-zz marked this conversation as resolved.
Show resolved Hide resolved
pass
elif isinstance(dest, str):
self._root_object[NameObject("/OpenAction")] = TextStringObject(dest)
elif isinstance(dest, Destination):
self._root_object[NameObject("/OpenAction")] = dest.dest_array
elif isinstance(dest, PageObject):
self._root_object[NameObject("/OpenAction")] = Destination(
"Opening",
dest.indirect_ref if dest.indirect_ref is not None else NullObject(),
TextStringObject("/Fit"),
).dest_array

def add_js(self, javascript: str) -> None:
"""
Add Javascript which will launch upon opening this PDF.
Expand Down
39 changes: 39 additions & 0 deletions tests/test_javascript.py
Expand Up @@ -3,6 +3,7 @@
import pytest

from PyPDF2 import PdfReader, PdfWriter
from PyPDF2.generic import NameObject, NumberObject, TextStringObject

# Configure path environment
TESTS_ROOT = Path(__file__).parent.resolve()
Expand Down Expand Up @@ -47,3 +48,41 @@ def get_javascript_name():
assert (
first_js != second_js
), "add_js should add to the previous script in the catalog."


def test_startup_dest(pdf_file_writer):
pubpub-zz marked this conversation as resolved.
Show resolved Hide resolved
assert pdf_file_writer.opening is None
pdf_file_writer.opening = pdf_file_writer.pages[9]
# checked also using Acrobrat to verify the good page is opened
op = pdf_file_writer._root_object["/OpenAction"]
assert op[0] == pdf_file_writer.pages[9].indirect_ref
assert op[1] == "/Fit"
op = pdf_file_writer.opening
assert op.raw_get("/Page") == pdf_file_writer.pages[9].indirect_ref
assert op["/Type"] == "/Fit"
pdf_file_writer.opening = op
assert pdf_file_writer.opening == op

# irrelevant, just for coverage
pdf_file_writer._root_object[NameObject("/OpenAction")][0] = NumberObject(0)
pdf_file_writer.opening
with pytest.raises(Exception) as exc:
del pdf_file_writer._root_object[NameObject("/OpenAction")][0]
pdf_file_writer.opening
assert "Invalid Destination" in str(exc.value)

pdf_file_writer.opening = "Test"
# checked also using Acrobrat to verify opening
op = pdf_file_writer._root_object["/OpenAction"]
assert isinstance(op, TextStringObject)
assert op == "Test"
op = pdf_file_writer.opening
assert isinstance(op, TextStringObject)
assert op == "Test"

# irrelevant, this is just for coverage
pdf_file_writer._root_object[NameObject("/OpenAction")] = NumberObject(0)
assert pdf_file_writer.opening is None
pdf_file_writer.opening = None
assert "/OpenAction" not in pdf_file_writer._root_object
pdf_file_writer.opening = None