pdf_writer/actions.rs
1use super::*;
2
3/// Writer for an _action dictionary_.
4///
5/// This struct is created by [`Annotation::action`] and many keys of
6/// [`AdditionalActions`].
7pub struct Action<'a> {
8 dict: Dict<'a>,
9}
10
11writer!(Action: |obj| {
12 let mut dict = obj.dict();
13 dict.pair(Name(b"Type"), Name(b"Action"));
14 Self { dict }
15});
16
17impl Action<'_> {
18 /// Write the `/S` attribute to set the action type.
19 pub fn action_type(&mut self, kind: ActionType) -> &mut Self {
20 self.pair(Name(b"S"), kind.to_name());
21 self
22 }
23
24 /// Start writing the `/D` attribute to set the destination of this
25 /// GoTo-type action.
26 pub fn destination(&mut self) -> Destination<'_> {
27 self.insert(Name(b"D")).start()
28 }
29
30 /// Write the `/D` attribute to set the destination of this GoTo-type action
31 /// to a named destination.
32 pub fn destination_named(&mut self, name: Name) -> &mut Self {
33 self.pair(Name(b"D"), name);
34 self
35 }
36
37 /// Start writing the `/F` attribute, depending on the [`ActionType`], setting:
38 /// - `RemoteGoTo`: which file to go to
39 /// - `Launch`: which application to launch
40 /// - `SubmitForm`: script location of the webserver that processes the
41 /// submission
42 /// - `ImportData`: the FDF file from which to import data.
43 pub fn file_spec(&mut self) -> FileSpec<'_> {
44 self.insert(Name(b"F")).start()
45 }
46
47 /// Write the `/NewWindow` attribute to set whether this remote GoTo action
48 /// should open the referenced destination in another window.
49 pub fn new_window(&mut self, new: bool) -> &mut Self {
50 self.pair(Name(b"NewWindow"), new);
51 self
52 }
53
54 /// Write the `/URI` attribute to set where this link action goes.
55 pub fn uri(&mut self, uri: Str) -> &mut Self {
56 self.pair(Name(b"URI"), uri);
57 self
58 }
59
60 /// Write the `/IsMap` attribute to set if the click position of the user's
61 /// cursor inside the link rectangle should be appended to the referenced
62 /// URI as a query parameter.
63 pub fn is_map(&mut self, map: bool) -> &mut Self {
64 self.pair(Name(b"IsMap"), map);
65 self
66 }
67
68 /// Write the `/JS` attribute to set the script of this action as a text
69 /// string. Only permissible for JavaScript and Rendition actions.
70 pub fn js_string(&mut self, script: TextStr) -> &mut Self {
71 self.pair(Name(b"JS"), script);
72 self
73 }
74
75 /// Write the `/JS` attribute to set the script of this action as a text
76 /// stream. The indirect reference shall point to a stream containing valid
77 /// ECMAScript. The stream must have `PdfDocEncoding` or be in Unicode,
78 /// starting with `U+FEFF`. Only permissible for JavaScript and Rendition
79 /// actions.
80 pub fn js_stream(&mut self, script: Ref) -> &mut Self {
81 self.pair(Name(b"JS"), script);
82 self
83 }
84
85 /// Start writing the `/Fields` array to set the fields which are
86 /// [include/exclude](FormActionFlags::INCLUDE_EXCLUDE) when submitting a
87 /// form, resetting a form, or loading an FDF file.
88 pub fn fields(&mut self) -> Fields<'_> {
89 self.insert(Name(b"Fields")).start()
90 }
91
92 /// Write the `/Flags` attribute to set the various characteristics of form
93 /// action.
94 pub fn form_flags(&mut self, flags: FormActionFlags) -> &mut Self {
95 self.pair(Name(b"Flags"), flags.bits() as i32);
96 self
97 }
98
99 /// Write the `/OP` attribute to set the operation to perform when the
100 /// action is triggered.
101 pub fn operation(&mut self, op: RenditionOperation) -> &mut Self {
102 self.pair(Name(b"OP"), op as i32);
103 self
104 }
105
106 /// Write the `/AN` attribute to provide a reference to the screen
107 /// annotation for the operation. Required if OP is present.
108 pub fn annotation(&mut self, id: Ref) -> &mut Self {
109 self.pair(Name(b"AN"), id);
110 self
111 }
112
113 /// Start writing the `/R` dictionary. Only permissible for the subtype
114 /// `Rendition`.
115 pub fn rendition(&mut self) -> Rendition<'_> {
116 self.insert(Name(b"R")).start()
117 }
118}
119
120deref!('a, Action<'a> => Dict<'a>, dict);
121
122/// The operation to perform when a rendition action is triggered.
123#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
124pub enum RenditionOperation {
125 /// Play the rendition specified by /R, and associating it with the
126 /// annotation. If a rendition is already associated with the annotation, it
127 /// shall be stopped, and the new rendition shall be associated with the
128 /// annotation.
129 Play = 0,
130 /// Stop any rendition being played in association with the annotation.
131 Stop = 1,
132 /// Pause any rendition being played in association with the annotation.
133 Pause = 2,
134 /// Resume any rendition being played in association with the annotation.
135 Resume = 3,
136 /// Play the rendition specified by /R, and associating it with the
137 /// annotation, or resume if a rendition is already associated.
138 PlayOrResume = 4,
139}
140
141/// Writer for a _fields array_.
142///
143/// This struct is created by [`Action::fields`].
144pub struct Fields<'a> {
145 array: Array<'a>,
146}
147
148writer!(Fields: |obj| Self { array: obj.array() });
149
150impl Fields<'_> {
151 /// The indirect reference to the field.
152 pub fn id(&mut self, id: Ref) -> &mut Self {
153 self.array.item(id);
154 self
155 }
156
157 /// The indirect references to the fields.
158 pub fn ids(&mut self, ids: impl IntoIterator<Item = Ref>) -> &mut Self {
159 self.array.items(ids);
160 self
161 }
162
163 /// The fully qualified name of the field. PDF 1.3+.
164 pub fn name(&mut self, name: TextStr) -> &mut Self {
165 self.array.item(name);
166 self
167 }
168
169 /// The fully qualified names of the fields. PDF 1.3+.
170 pub fn names<'b>(
171 &mut self,
172 names: impl IntoIterator<Item = TextStr<'b>>,
173 ) -> &mut Self {
174 self.array.items(names);
175 self
176 }
177}
178
179deref!('a, Fields<'a> => Array<'a>, array);
180
181/// What kind of action to perform when clicking a link annotation.
182#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
183pub enum ActionType {
184 /// Go to a destination in the document.
185 GoTo,
186 /// Go to a destination in another document.
187 RemoteGoTo,
188 /// Launch an application.
189 ///
190 /// This action type is forbidden in PDF/A.
191 Launch,
192 /// Open a URI.
193 Uri,
194 /// Set an annotation's hidden flag. PDF 1.2+.
195 SubmitForm,
196 /// Set form fields to their default values. PDF 1.2+.
197 ///
198 /// This action type is forbidden in PDF/A.
199 ResetForm,
200 /// Import form field values from a file. PDF 1.2+.
201 ///
202 /// This action type is forbidden in PDF/A.
203 ImportData,
204 /// Execute a JavaScript action. PDF 1.2+.
205 ///
206 /// See Adobe's
207 /// [JavaScript for Acrobat API Reference](https://opensource.adobe.com/dc-acrobat-sdk-docs/acrobatsdk/pdfs/acrobatsdk_jsapiref.pdf)
208 /// and ISO 21757.
209 ///
210 /// This action type is forbidden in PDF/A.
211 JavaScript,
212 /// A rendition action to control the playing of multimedia content. PDF 1.5+.
213 ///
214 /// This action type is forbidden in PDF/A.
215 Rendition,
216}
217
218impl ActionType {
219 pub(crate) fn to_name(self) -> Name<'static> {
220 match self {
221 Self::GoTo => Name(b"GoTo"),
222 Self::RemoteGoTo => Name(b"GoToR"),
223 Self::Launch => Name(b"Launch"),
224 Self::Uri => Name(b"URI"),
225 Self::SubmitForm => Name(b"SubmitForm"),
226 Self::ResetForm => Name(b"ResetForm"),
227 Self::ImportData => Name(b"ImportData"),
228 Self::JavaScript => Name(b"JavaScript"),
229 Self::Rendition => Name(b"Rendition"),
230 }
231 }
232}
233
234bitflags::bitflags! {
235 /// A set of flags specifying various characteristics of an [`Action`].
236 pub struct FormActionFlags: u32 {
237 /// Whether to include (unset) or exclude (set) the values in the
238 /// `/Fields` attribute on form submission or reset. This Flag has very
239 /// specific interacitons with other flags and fields, read the PDF 1.7
240 /// spec for more info.
241 const INCLUDE_EXCLUDE = 1;
242 /// Fields shall be submitted regardless of if they have a value or
243 /// not, otherwise they are excluded.
244 const INCLUDE_NO_VALUE_FIELDS = 2;
245 /// Export the fields as HTML instead of submitting as FDF. Ignored if
246 /// `SUBMIT_PDF` or `XFDF` are set.
247 const EXPORT_FORMAT = 1 << 2;
248 /// Field name should be submitted using an HTTP GET request, otherwise
249 /// POST. Should only be if `EXPORT_FORMAT` is also set.
250 const GET_METHOD = 1 << 3;
251 /// Include the coordinates of the mouse when submit was pressed. Should
252 /// only be if `EXPORT_FORMAT` is also set.
253 const SUBMIT_COORDINATES = 1 << 4;
254 /// Submit field names and values as XFDF instead of submitting an FDF.
255 /// Should not be set if `SUBMIT_PDF` is set. PDF1.4+.
256 const XFDF = 1 << 5;
257 /// Include all updates done to the PDF document in the submission FDF
258 /// file. Should only be used when `XFDF` and `EXPORT_FORMAT` are not
259 /// set. PDF 1.4+.
260 const INCLUDE_APPEND_SAVES = 1 << 6;
261 /// Include all markup annotations of the PDF dcoument in the submission
262 /// FDF file. Should only be used when `XFDF` and `EXPORT_FORMAT` are
263 /// not set. PDF 1.4+.
264 const INCLUDE_ANNOTATIONS = 1 << 7;
265 /// Submit the PDF file instead of an FDF file. All other flags other
266 /// than `GET_METHOD` are ignored if this is set. PDF 1.4+.
267 const SUBMIT_PDF = 1 << 8;
268 /// Convert fields which represent dates into the
269 /// [canonical date format](crate::types::Date). The interpretation of
270 /// a form field as a date is is not specified in the field but the
271 /// JavaScript code that processes it. PDF 1.4+.
272 const CANONICAL_FORMAT = 1 << 9;
273 /// Include only the markup annotations made by the current user (the
274 /// `/T` entry of the annotation) as determined by the remote server
275 /// the form will be submitted to. Should only be used when `XFDF` and
276 /// `EXPORT_FORMAT` are not set and `INCLUDE_ANNOTATIONS` is set. PDF
277 /// 1.4+.
278 const EXCLUDE_NON_USER_ANNOTS = 1 << 10;
279 /// Include the F entry in the FDF file.
280 /// Should only be used when `XFDF` and `EXPORT_FORMAT` are not set.
281 /// PDF 1.4+
282 const EXCLUDE_F_KEY = 1 << 11;
283 /// Include the PDF file as a stream in the FDF file that will be submitted.
284 /// Should only be used when `XFDF` and `EXPORT_FORMAT` are not set.
285 /// PDF 1.5+.
286 const EMBED_FORM = 1 << 13;
287 }
288}
289
290/// Writer for an _additional actions dictionary_.
291///
292/// This struct is created by [`Annotation::additional_actions`],
293/// [`Field::additional_actions`], [`Page::additional_actions`] and
294/// [`Catalog::additional_actions`].
295pub struct AdditionalActions<'a> {
296 dict: Dict<'a>,
297}
298
299writer!(AdditionalActions: |obj| Self { dict: obj.dict() });
300
301/// Only permissible for [annotations](Annotation).
302impl AdditionalActions<'_> {
303 /// Start writing the `/E` dictionary. An action that shall be performed
304 /// when the cursor enters the annotation's active area. Only permissible
305 /// for annotations. PDF 1.2+.
306 pub fn annot_curser_enter(&mut self) -> Action<'_> {
307 self.insert(Name(b"E")).start()
308 }
309
310 /// Start writing the `/X` dictionary. An action that shall be performed
311 /// when the cursor exits the annotation's active area. Only permissible for
312 /// annotations. PDF 1.2+.
313 pub fn annot_cursor_exit(&mut self) -> Action<'_> {
314 self.insert(Name(b"X")).start()
315 }
316
317 /// Start writing the `/D` dictionary. This sets the action action
318 /// that shall be performed when the mouse button is pressed inside the
319 /// annotation's active area. Only permissible for annotations. PDF 1.2+.
320 pub fn annot_mouse_press(&mut self) -> Action<'_> {
321 self.insert(Name(b"D")).start()
322 }
323
324 /// Start writing the `/U` dictionary. This sets the action action that
325 /// shall be performed when the mouse button is released inside the
326 /// annotation's active area. Only permissible for annotations. PDF 1.2+.
327 pub fn annot_mouse_release(&mut self) -> Action<'_> {
328 self.insert(Name(b"U")).start()
329 }
330
331 /// Start writing the `/PO` dictionary. This sets the action action that
332 /// shall be performed when the page containing the annotation is opened.
333 /// Only permissible for annotations. PDF 1.5+.
334 pub fn annot_page_open(&mut self) -> Action<'_> {
335 self.insert(Name(b"PO")).start()
336 }
337
338 /// Start writing the `/PC` dictionary. This sets the action action that
339 /// shall be performed when the page containing the annotation is closed.
340 /// Only permissible for annotations. PDF 1.5+.
341 pub fn annot_page_close(&mut self) -> Action<'_> {
342 self.insert(Name(b"PV")).start()
343 }
344
345 /// Start writing the `/PV` dictionary. This sets the action action that
346 /// shall be performed when the page containing the annotation becomes
347 /// visible. Only permissible for annotations. PDF 1.5+.
348 pub fn annot_page_visible(&mut self) -> Action<'_> {
349 self.insert(Name(b"PV")).start()
350 }
351
352 /// Start writing the `/PI` dictionary. This sets the action action that
353 /// shall be performed when the page containing the annotation is no longer
354 /// visible in the conforming reader's user interface. Only permissible for
355 /// annotations. PDF 1.5+.
356 pub fn annot_page_invisible(&mut self) -> Action<'_> {
357 self.insert(Name(b"PI")).start()
358 }
359}
360
361/// Only permissible for [widget](crate::types::AnnotationType::Widget)
362/// [annotations](Annotation).
363impl AdditionalActions<'_> {
364 /// Start writing the `/Fo` dictionary. This sets the action that shall be
365 /// performed when the annotation receives the input focus. Only permissible
366 /// for widget annotations. PDF 1.2+.
367 pub fn widget_focus(&mut self) -> Action<'_> {
368 self.insert(Name(b"Fo")).start()
369 }
370
371 /// Start writing the `/Bl` dictionary. This sets the action that shall be
372 /// performed when the annotation loses the input focus. Only permissible
373 /// for widget annotations. PDF 1.2+.
374 pub fn widget_focus_loss(&mut self) -> Action<'_> {
375 self.insert(Name(b"Bl")).start()
376 }
377}
378
379/// Only permissible for [page objects](Page).
380impl AdditionalActions<'_> {
381 /// Start writing the `/O` dictionary. This sets the action that shall be
382 /// performed when the page is opened. This action is independent of any
383 /// that may be defined by the open action entry in the
384 /// [document catalog](Catalog) and shall be executed after such an action.
385 /// Only permissible for [page objects](Page). PDF 1.2+.
386 pub fn page_open(&mut self) -> Action<'_> {
387 self.insert(Name(b"O")).start()
388 }
389
390 /// Start writing the `/C` dictionary. This sets the action that shall
391 /// be performed when the page is closed. This action applies to the page
392 /// being closed and shall be executed before any other page is opened. Only
393 /// permissible for [page objects](Page). PDF 1.2+.
394 pub fn page_close(&mut self) -> Action<'_> {
395 self.insert(Name(b"C")).start()
396 }
397}
398
399/// Only permisible for form fields.
400impl AdditionalActions<'_> {
401 /// Start writing the `/K` dictionary. This sets the JavaScript action that
402 /// shall be performed when the user modifies a character in a text field
403 /// or combo box or modifies the selection in a scrollable list box. This
404 /// action may check the added text for validity and reject or modify it.
405 /// Only permissible for form fields. PDF 1.3+.
406 pub fn form_calculate_partial(&mut self) -> Action<'_> {
407 self.insert(Name(b"K")).start()
408 }
409
410 /// Start writing the `/F` dictionary. This sets the JavaScript action
411 /// that shall be performed before the field is formatted to display its
412 /// value. This action may modify the field's value before formatting. Only
413 /// permissible for form fields. PDF 1.3+.
414 pub fn form_format(&mut self) -> Action<'_> {
415 self.insert(Name(b"F")).start()
416 }
417
418 /// Start writing the `/V` dictionary. This sets the JavaScript action that
419 /// shall be performed when the field's value is changed. This action may
420 /// check the new value for validity. Only permissible for form fields.
421 /// PDF 1.3+.
422 pub fn form_validate(&mut self) -> Action<'_> {
423 self.insert(Name(b"V")).start()
424 }
425
426 /// Start writing the `/C` dictionary. This sets the JavaScript action that
427 /// shall be performed to recalculate the value of this field when that
428 /// of another field changes. The order in which the document's fields are
429 /// recalculated shall be defined by the `/CO` entry in the interactive form
430 /// dictionary. Only permissible for form fields. PDF 1.3+.
431 pub fn form_calculate(&mut self) -> Action<'_> {
432 self.insert(Name(b"C")).start()
433 }
434}
435
436/// Only permisible for [document catalog](Catalog).
437impl AdditionalActions<'_> {
438 /// Start writing the `/WC` dictionary. This sets the JavaScript action
439 /// that shall be performed before closing a document. Only permissible for
440 /// the [document catalog](Catalog) PDF 1.4+.
441 pub fn cat_before_close(&mut self) -> Action<'_> {
442 self.insert(Name(b"WC")).start()
443 }
444
445 /// Start writing the `/WS` dictionary. This sets the JavaScript action
446 /// that shall be performed before saving a document. Only permissible for
447 /// the [document catalog](Catalog) PDF 1.4+.
448 pub fn cat_before_save(&mut self) -> Action<'_> {
449 self.insert(Name(b"WS")).start()
450 }
451
452 /// Start writing the `/DS` dictionary. This sets the JavaScript action
453 /// that shall be performed after saving a document. Only permissible for
454 /// the [document catalog](Catalog) PDF 1.4+.
455 pub fn cat_after_save(&mut self) -> Action<'_> {
456 self.insert(Name(b"DS")).start()
457 }
458
459 /// Start writing the `/WP` dictionary. This sets the JavaScript action
460 /// that shall be performed before printing a document. Only permissible for
461 /// the [document catalog](Catalog) PDF 1.4+.
462 pub fn cat_before_print(&mut self) -> Action<'_> {
463 self.insert(Name(b"WP")).start()
464 }
465
466 /// Start writing the `/DP` dictionary. This sets the JavaScript action
467 /// that shall be performed after printing a document. Only permissible for
468 /// the [document catalog](Catalog) PDF 1.4+.
469 pub fn cat_after_print(&mut self) -> Action<'_> {
470 self.insert(Name(b"DP")).start()
471 }
472}
473
474deref!('a, AdditionalActions<'a> => Dict<'a>, dict);