File: actions.py

package info (click to toggle)
fpdf2 2.8.7-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 114,352 kB
  • sloc: python: 50,410; sh: 133; makefile: 12
file content (143 lines) | stat: -rw-r--r-- 4,695 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import warnings
from abc import ABC
from typing import TYPE_CHECKING, Optional, Union

from .syntax import PDFString, build_obj_dict, create_dictionary_string

if TYPE_CHECKING:
    from .encryption import StandardSecurityHandler
    from .syntax import Destination


class Action(ABC):
    def __init__(self, next_action: Optional["Action"] = None) -> None:
        """
        Args:
            next (PDFObject | str): optional reference to another Action to trigger after this one
        """
        self.next = next_action

    def serialize(
        self,
        _security_handler: Optional["StandardSecurityHandler"] = None,
        _obj_id: Optional[int] = None,
    ) -> str:
        raise NotImplementedError

    def _serialize(
        self,
        key_values: Optional[dict[str, object]] = None,
        _security_handler: Optional["StandardSecurityHandler"] = None,
        _obj_id: Optional[int] = None,
    ) -> str:
        if key_values is None:
            key_values = {}
        if self.next:
            key_values["Next"] = self.next
        obj_dict = build_obj_dict(key_values, _security_handler, _obj_id)
        return create_dictionary_string(obj_dict, field_join=" ")


class URIAction(Action):
    def __init__(self, uri: str, next_action: Optional[Action] = None) -> None:
        super().__init__(next_action)
        self.uri = uri

    def serialize(
        self,
        _security_handler: Optional["StandardSecurityHandler"] = None,
        _obj_id: Optional[int] = None,
    ) -> str:
        return super()._serialize(
            {"s": "/URI", "u_r_i": PDFString(self.uri, encrypt=True)},
            _security_handler=_security_handler,
            _obj_id=_obj_id,
        )


class NamedAction(Action):
    def __init__(self, action_name: str, next_action: Optional[Action] = None) -> None:
        super().__init__(next_action)
        if action_name not in ("NextPage", "PrevPage", "FirstPage", "LastPage"):
            warnings.warn("Non-standard named action added")
        self.action_name = action_name

    def serialize(
        self,
        _security_handler: Optional["StandardSecurityHandler"] = None,
        _obj_id: Optional[int] = None,
    ) -> str:
        return super()._serialize(
            {"s": "/Named", "n": f"/{self.action_name}"},
            _security_handler=_security_handler,
            _obj_id=_obj_id,
        )


class GoToAction(Action):
    def __init__(
        self, dest: Union[str, "Destination"], next_action: Optional[Action] = None
    ) -> None:
        super().__init__(next_action)
        if isinstance(dest, str) and dest.startswith("#"):
            dest = PDFString(dest[1:], encrypt=True)
        self.dest = dest

    def serialize(
        self,
        _security_handler: Optional["StandardSecurityHandler"] = None,
        _obj_id: Optional[int] = None,
    ) -> str:
        return super()._serialize(
            {"s": "/GoTo", "d": self.dest},
            _security_handler=_security_handler,
            _obj_id=_obj_id,
        )


class GoToRemoteAction(Action):
    def __init__(
        self, file: str, dest: "Destination", next_action: Optional[Action] = None
    ) -> None:
        super().__init__(next_action)
        self.file = file
        self.dest = dest

    def serialize(
        self,
        _security_handler: Optional["StandardSecurityHandler"] = None,
        _obj_id: Optional[int] = None,
    ) -> str:
        return super()._serialize(
            {"s": "/GoToR", "f": PDFString(self.file, encrypt=True), "d": self.dest},
            _security_handler=_security_handler,
            _obj_id=_obj_id,
        )


class LaunchAction(Action):
    "As of 2022, this does not seem honored by neither Adobe Acrobat nor Sumatra readers."

    def __init__(self, file: str, next_action: Optional[Action] = None) -> None:
        super().__init__(next_action)
        self.file = file

    def serialize(
        self,
        _security_handler: Optional["StandardSecurityHandler"] = None,
        _obj_id: Optional[int] = None,
    ) -> str:
        return super()._serialize(
            {"s": "/Launch", "f": PDFString(self.file, encrypt=True)},
            _security_handler=_security_handler,
            _obj_id=_obj_id,
        )


# Annotation & actions that we tested implementing,
# but that revealed not be worth the effort:
# * Popup annotation & Hide action: as of june 2021,
#   do not seem support neither by Adobe Acrobat nor by Sumatra.
#   Moreover, they both use to indirect reference annotations,
#   and hence implementing them would need some consequent refactoring,
#   as annotations are currently defined "inline", not as dedicated PDF objects.