pdf_writer/renditions.rs
1use super::*;
2use crate::object::TextStrLike;
3
4/// Writer for an _rendition dictionary_.
5///
6/// This struct is created by [`Action::rendition`].
7pub struct Rendition<'a> {
8 dict: Dict<'a>,
9}
10
11writer!(Rendition: |obj| {
12 let mut dict = obj.dict();
13 dict.pair(Name(b"Type"), Name(b"Rendition"));
14 Self { dict }
15});
16
17impl Rendition<'_> {
18 /// Write the `/S` attribute to set the rendition type.
19 pub fn subtype(&mut self, kind: RenditionType) -> &mut Self {
20 self.pair(Name(b"S"), kind.to_name());
21 self
22 }
23
24 /// Write the `/N` attribute. Specify the name of the rendition for use in a
25 /// user interface and for name tree lookup by JavaScript actions.
26 pub fn name(&mut self, text: impl TextStrLike) -> &mut Self {
27 self.pair(Name(b"N"), text);
28 self
29 }
30
31 /// Start writing the `/C`, i.e. media clip, dictionary which specifies what
32 /// media should be played. Only permissible for Media Renditions.
33 pub fn media_clip(&mut self) -> MediaClip<'_> {
34 self.insert(Name(b"C")).start()
35 }
36
37 /// Start writing the `/P`, i.e. media play parameters, dictionary which
38 /// specifies how the media should be played. Only permissible for Media
39 /// Renditions.
40 pub fn media_play_params(&mut self) -> MediaPlayParams<'_> {
41 self.insert(Name(b"P")).start()
42 }
43}
44
45deref!('a, Rendition<'a> => Dict<'a>, dict);
46
47/// Writer for an _media clip dictionary_.
48///
49/// This struct is created by [`Rendition::media_clip`].
50///
51/// ## Note on reader compatibility
52///
53/// Different PDF readers may have support for different media codecs and
54/// container formats.
55///
56/// For example, [Adobe's documentation][1] states that Adobe Acrobat can play
57/// videos in MP4, MOV, M4V, 3GP, and 3G2 containers using the H.264 codec.
58///
59/// Other readers may depend on the media libraries installed on the system. KDE
60/// Okular, for example, uses the Phonon library to support a range of media
61/// formats.
62///
63/// Yet other viewers do not support media clips at all. At the time of writing,
64/// this includes the popular Pdfium library used by Google Chrome and Microsoft
65/// Edge, `pdf.js` used by Firefox, mupdf, and Quartz, the PDF viewer on Apple
66/// platforms.
67///
68/// [1]: https://helpx.adobe.com/acrobat/using/playing-video-audio-multimedia-formats.html#supported_video_audio_and_interactive_formats
69pub struct MediaClip<'a> {
70 dict: Dict<'a>,
71}
72
73writer!(MediaClip: |obj| {
74 let mut dict = obj.dict();
75 dict.pair(Name(b"Type"), Name(b"MediaClip"));
76 Self { dict }
77});
78
79impl MediaClip<'_> {
80 /// Write the `/S` attribute to set the media clip type.
81 pub fn subtype(&mut self, kind: MediaClipType) -> &mut Self {
82 self.pair(Name(b"S"), kind.to_name());
83 self
84 }
85
86 /// Write the `/N` attribute. Specifies the name of the media clip, for use
87 /// in the user interface.
88 pub fn name(&mut self, text: impl TextStrLike) -> &mut Self {
89 self.pair(Name(b"N"), text);
90 self
91 }
92
93 /// Start writing the `/D` dictionary specifying the media data.
94 pub fn data(&mut self) -> FileSpec<'_> {
95 self.insert(Name(b"D")).start()
96 }
97
98 /// Write the `/CT` attribute identifying the type of data in `/D`, i.e. the
99 /// MIME type.
100 pub fn data_type(&mut self, tf: Str) -> &mut Self {
101 self.pair(Name(b"CT"), tf);
102 self
103 }
104
105 /// Start writing the `/P`, i.e. media permissions, dictionary.
106 pub fn permissions(&mut self) -> MediaPermissions<'_> {
107 self.insert(Name(b"P")).start()
108 }
109
110 /// Write the `/Alt` attribute, listing alternate text descriptions which
111 /// are specified as a multi-language text array. A multi-language text
112 /// array shall contain pairs of strings.
113 pub fn alt_texts<'b>(
114 &mut self,
115 texts: impl IntoIterator<Item = TextStr<'b>>,
116 ) -> &mut Self {
117 self.insert(Name(b"Alt")).array().items(texts);
118 self
119 }
120}
121
122deref!('a, MediaClip<'a> => Dict<'a>, dict);
123
124/// Writer for an _media play parameters dictionary_.
125///
126/// This struct is created by [`Rendition::media_play_params`].
127pub struct MediaPlayParams<'a> {
128 dict: Dict<'a>,
129}
130
131writer!(MediaPlayParams: |obj| {
132 let mut dict = obj.dict();
133 dict.pair(Name(b"Type"), Name(b"MediaPlayParams"));
134 Self { dict }
135});
136
137impl MediaPlayParams<'_> {
138 /// Write the `/C` attribute inside a `/BE` dictionary specifying whether to
139 /// display a player-specific controls.
140 ///
141 /// This avoids implementing the "must honour" (MH) or "best effort" (BE)
142 /// dictionaries for MediaPlayParams, as the required boiler-plate code
143 /// would be high, and its usefulness low.
144 pub fn controls(&mut self, c: bool) -> &mut Self {
145 self.insert(Name(b"BE")).dict().pair(Name(b"C"), c);
146 self
147 }
148}
149
150deref!('a, MediaPlayParams<'a> => Dict<'a>, dict);
151
152/// Writer for an _media permissions dictionary_.
153///
154/// This struct is created by [`MediaClip::permissions`].
155pub struct MediaPermissions<'a> {
156 dict: Dict<'a>,
157}
158
159writer!(MediaPermissions: |obj| {
160 let mut dict = obj.dict();
161 dict.pair(Name(b"Type"), Name(b"MediaPermissions"));
162 Self { dict }
163});
164
165impl MediaPermissions<'_> {
166 /// Write the `/TF` attribute to control permissions to write a temporary file.
167 pub fn temp_file(&mut self, tf: TempFileType) -> &mut Self {
168 self.pair(Name(b"TF"), tf.to_str());
169 self
170 }
171}
172
173deref!('a, MediaPermissions<'a> => Dict<'a>, dict);
174
175/// The circumstances under which it is acceptable to write a temporary file in
176/// order to play a media clip.
177#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
178pub enum TempFileType {
179 /// Never allowed.
180 #[default]
181 Never,
182 /// Allowed only if the document permissions allow content extraction.
183 Extract,
184 /// Allowed only if the document permissions allow content extraction,
185 /// including for accessibility purposes.
186 Access,
187 /// Always allowed.
188 Always,
189}
190
191impl TempFileType {
192 pub(crate) fn to_str(self) -> Str<'static> {
193 match self {
194 Self::Never => Str(b"TEMPNEVER"),
195 Self::Extract => Str(b"TEMPEXTRACT"),
196 Self::Access => Str(b"TEMPACCESS"),
197 Self::Always => Str(b"TEMPALWAYS"),
198 }
199 }
200}
201
202/// Type of rendition objects.
203#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
204pub enum RenditionType {
205 /// Media Rendition.
206 Media,
207 /// Selector Rendition.
208 Selector,
209}
210
211impl RenditionType {
212 pub(crate) fn to_name(self) -> Name<'static> {
213 match self {
214 Self::Media => Name(b"MR"),
215 Self::Selector => Name(b"SR"),
216 }
217 }
218}
219
220/// Type of media clip objects.
221#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
222pub enum MediaClipType {
223 /// Media Clip Data.
224 Data,
225 /// Media Clip Section.
226 Section,
227}
228
229impl MediaClipType {
230 pub(crate) fn to_name(self) -> Name<'static> {
231 match self {
232 Self::Data => Name(b"MCD"),
233 Self::Section => Name(b"MCS"),
234 }
235 }
236}