pdf_writer/xobject.rs
1use super::*;
2use crate::object::TextStrLike;
3use crate::types::RenderingIntent;
4
5/// Writer for an _image XObject stream_.
6///
7/// This struct is created by [`Chunk::image_xobject`].
8pub struct ImageXObject<'a> {
9 stream: Stream<'a>,
10}
11
12impl<'a> ImageXObject<'a> {
13 /// Create a new image stream writer.
14 pub(crate) fn start(mut stream: Stream<'a>) -> Self {
15 stream.pair(Name(b"Type"), Name(b"XObject"));
16 stream.pair(Name(b"Subtype"), Name(b"Image"));
17 Self { stream }
18 }
19
20 /// Write the `/Width` attribute.
21 pub fn width(&mut self, width: i32) -> &mut Self {
22 self.pair(Name(b"Width"), width);
23 self
24 }
25
26 /// Write the `/Height` attribute.
27 pub fn height(&mut self, height: i32) -> &mut Self {
28 self.pair(Name(b"Height"), height);
29 self
30 }
31
32 /// Start writing the `/ColorSpace` attribute.
33 ///
34 /// Required for all images except if using the `JPXDecode` filter.
35 /// If this is an image soft mask, the color space must be `DeviceGray`.
36 /// Must not be `Pattern`.
37 pub fn color_space(&mut self) -> ColorSpace<'_> {
38 self.insert(Name(b"ColorSpace")).start()
39 }
40
41 /// Write the `/ColorSpace` attribute as a name from the resource dictionary.
42 ///
43 /// Required for all images except if using the `JPXDecode` filter.
44 /// If this is an image soft mask, the color space must be `DeviceGray`.
45 /// Must not be `Pattern`.
46 pub fn color_space_name(&mut self, name: Name) -> &mut Self {
47 self.pair(Name(b"ColorSpace"), name);
48 self
49 }
50
51 /// Write the `/BitsPerComponent` attribute. Required.
52 ///
53 /// Required for all images except if using the `JPXDecode` filter.
54 pub fn bits_per_component(&mut self, bits: i32) -> &mut Self {
55 self.pair(Name(b"BitsPerComponent"), bits);
56 self
57 }
58
59 /// Write the `/Intent` attribute. PDF 1.1+.
60 pub fn intent(&mut self, intent: RenderingIntent) -> &mut Self {
61 self.pair(Name(b"Intent"), intent.to_name());
62 self
63 }
64
65 /// Write the `/ImageMask` attribute to set whether this image is a clipping
66 /// mask. If so, the `/BitsPerComponent` must be `1` and `/Mask` and
67 /// `/ColorSpace` attributes shall be left undefined.
68 pub fn image_mask(&mut self, mask: bool) -> &mut Self {
69 self.pair(Name(b"ImageMask"), mask);
70 self
71 }
72
73 /// Write the `/Mask` attribute to set a color key mask. The iterable color
74 /// argument must contain a range of colors (minimum and maximum) for each
75 /// channel that shall be masked out. PDF 1.3+.
76 pub fn color_mask(&mut self, colors: impl IntoIterator<Item = i32>) -> &mut Self {
77 self.insert(Name(b"Mask")).array().typed().items(colors);
78 self
79 }
80
81 /// Write the `/Mask` attribute to set another image as the stencil mask of
82 /// this image.
83 pub fn stencil_mask(&mut self, mask: Ref) -> &mut Self {
84 self.pair(Name(b"Mask"), mask);
85 self
86 }
87
88 /// Write the `/Decode` attribute to set the decoding of the image sample
89 /// colors to the specified color space. Must have twice the amount of
90 /// elements as the color space.
91 pub fn decode(&mut self, decode: impl IntoIterator<Item = f32>) -> &mut Self {
92 self.insert(Name(b"Decode")).array().typed().items(decode);
93 self
94 }
95
96 /// Write the `/Interpolate` attribute.
97 ///
98 /// Must be false or unset for PDF/A files.
99 pub fn interpolate(&mut self, interpolate: bool) -> &mut Self {
100 self.pair(Name(b"Interpolate"), interpolate);
101 self
102 }
103
104 /// Write the `/Alternates` attribute. PDF 1.3+.
105 ///
106 /// Images that may replace this image. The order is not relevant.
107 ///
108 /// Note that this key is forbidden in PDF/A.
109 pub fn alternates(&mut self, alternates: impl IntoIterator<Item = Ref>) -> &mut Self {
110 self.insert(Name(b"Alternates")).array().items(alternates);
111 self
112 }
113
114 /// Start writing the `/SMask` attribute. PDF 1.4+.
115 ///
116 /// Must not be used if this image already is an image soft mask.
117 ///
118 /// Note that this key is forbidden in PDF/A-1.
119 pub fn s_mask(&mut self, x_object: Ref) -> &mut Self {
120 self.pair(Name(b"SMask"), x_object);
121 self
122 }
123
124 /// Write the `/SMaskInData` attribute. PDF 1.5+.
125 ///
126 /// May only be used for images that use the `JPXDecode` filter. If set to
127 /// something other than `Ignore`, the `SMask` attribute must not be used.
128 pub fn s_mask_in_data(&mut self, mode: SMaskInData) -> &mut Self {
129 self.pair(Name(b"SMaskInData"), mode.to_int());
130 self
131 }
132
133 /// Write the `/StructParent` attribute to indicate the [structure tree
134 /// element][StructElement] this image belongs to. PDF 1.3+.
135 pub fn struct_parent(&mut self, key: i32) -> &mut Self {
136 self.pair(Name(b"StructParent"), key);
137 self
138 }
139
140 /// Write the `/Matte` attribute for image soft masks. PDF 1.4+.
141 ///
142 /// This shall be the matte color of the parent image encoded in its color
143 /// space.
144 pub fn matte(&mut self, color: impl IntoIterator<Item = f32>) -> &mut Self {
145 self.insert(Name(b"Matte")).array().items(color);
146 self
147 }
148
149 /// Write the `/Metadata` attribute to specify the image's metadata. PDF
150 /// 1.4+.
151 ///
152 /// The reference shall point to a [metadata stream](Metadata).
153 pub fn metadata(&mut self, id: Ref) -> &mut Self {
154 self.pair(Name(b"Metadata"), id);
155 self
156 }
157
158 /// Start writing the `/AF` array to specify the associated files of the
159 /// image. PDF 2.0+ or PDF/A-3.
160 pub fn associated_files(&mut self) -> TypedArray<'_, FileSpec<'_>> {
161 self.insert(Name(b"AF")).array().typed()
162 }
163}
164
165deref!('a, ImageXObject<'a> => Stream<'a>, stream);
166
167/// What to do with in-data mask information in `JPXDecode` images.
168#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
169pub enum SMaskInData {
170 /// Discard the mask data.
171 #[default]
172 Ignore,
173 /// Use the mask data.
174 Use,
175 /// Use the mask data on the image whose backdrop has been pre-blended with
176 /// a matte color.
177 Preblended,
178}
179
180impl SMaskInData {
181 pub(crate) fn to_int(&self) -> i32 {
182 match self {
183 Self::Ignore => 0,
184 Self::Use => 1,
185 Self::Preblended => 2,
186 }
187 }
188}
189
190/// Writer for an _form XObject stream_. PDF 1.1+.
191///
192/// This struct is created by [`Chunk::form_xobject`].
193///
194/// Note that these have nothing to do with forms that have fields to fill out.
195/// Rather, they are a way to encapsulate and reuse content across the file.
196pub struct FormXObject<'a> {
197 stream: Stream<'a>,
198}
199
200impl<'a> FormXObject<'a> {
201 /// Create a new form stream writer.
202 pub(crate) fn start(mut stream: Stream<'a>) -> Self {
203 stream.pair(Name(b"Type"), Name(b"XObject"));
204 stream.pair(Name(b"Subtype"), Name(b"Form"));
205 Self { stream }
206 }
207
208 /// Write the `/BBox` attribute. Required.
209 ///
210 /// This clips the form xobject to coordinates in its coordinate system.
211 pub fn bbox(&mut self, bbox: Rect) -> &mut Self {
212 self.pair(Name(b"BBox"), bbox);
213 self
214 }
215
216 /// Write the `/Matrix` attribute to map form space to user space.
217 pub fn matrix(&mut self, matrix: [f32; 6]) -> &mut Self {
218 self.insert(Name(b"Matrix")).array().items(matrix);
219 self
220 }
221
222 /// Start writing the `/Resources` dictionary to specify the resources used
223 /// by the XObject. This makes it independent of the parent content stream
224 /// it is eventually invoked in. PDF 1.2+.
225 pub fn resources(&mut self) -> Resources<'_> {
226 self.insert(Name(b"Resources")).start()
227 }
228
229 /// Start writing the `/Group` dictionary to set up transparency model
230 /// parameters and let this XObject be known as a group. PDF 1.4+.
231 pub fn group(&mut self) -> Group<'_> {
232 self.insert(Name(b"Group")).start()
233 }
234
235 /// Write the `/StructParent` attribute to indicate the [structure tree
236 /// element][StructElement] this XObject belongs to. Mutually exclusive with
237 /// [`Self::struct_parents`]. PDF 1.3+.
238 pub fn struct_parent(&mut self, key: i32) -> &mut Self {
239 self.pair(Name(b"StructParent"), key);
240 self
241 }
242
243 /// Write the `/StructParents` attribute to indicate the [structure tree
244 /// elements][StructElement] the contents of this XObject may belong to.
245 /// Mutually exclusive with [`Self::struct_parent`]. PDF 1.3+.
246 pub fn struct_parents(&mut self, key: i32) -> &mut Self {
247 self.pair(Name(b"StructParents"), key);
248 self
249 }
250
251 /// Start writing the `/Ref` dictionary to identify the page from an
252 /// external document that the XObject is a reference to. PDF 1.4+.
253 ///
254 /// Note that this key is forbidden in PDF/A.
255 pub fn reference(&mut self) -> Reference<'_> {
256 self.insert(Name(b"Ref")).start()
257 }
258
259 /// Write the `/Metadata` attribute to specify the XObject's metadata. PDF
260 /// 1.4+.
261 ///
262 /// The reference shall point to a [metadata stream](Metadata).
263 pub fn metadata(&mut self, id: Ref) -> &mut Self {
264 self.pair(Name(b"Metadata"), id);
265 self
266 }
267
268 /// Write the `/LastModified` attribute. PDF 1.3+.
269 pub fn last_modified(&mut self, last_modified: Date) -> &mut Self {
270 self.pair(Name(b"LastModified"), last_modified);
271 self
272 }
273
274 /// Start writing the `/AF` array to specify the associated files of the
275 /// Form XObject. PDF 2.0+ or PDF/A-3.
276 pub fn associated_files(&mut self) -> TypedArray<'_, FileSpec<'_>> {
277 self.insert(Name(b"AF")).array().typed()
278 }
279}
280
281deref!('a, FormXObject<'a> => Stream<'a>, stream);
282
283/// Writer for a _group XObject dictionary_. PDF 1.4+.
284///
285/// This struct is created by [`FormXObject::group`] and [`Page::group`].
286pub struct Group<'a> {
287 dict: Dict<'a>,
288}
289
290writer!(Group: |obj| {
291 let mut dict = obj.dict();
292 dict.pair(Name(b"Type"), Name(b"Group"));
293 Self { dict }
294});
295
296impl Group<'_> {
297 /// Set the `/S` attribute to `/Transparency`. Required to set the remaining
298 /// transparency parameters.
299 pub fn transparency(&mut self) -> &mut Self {
300 self.pair(Name(b"S"), Name(b"Transparency"));
301 self
302 }
303
304 /// Start writing the `/CS` attribute to set the color space.
305 ///
306 /// This is optional for isolated groups and required for groups where the
307 /// color space cannot be derived from the parent.
308 ///
309 /// Required in PDF/A-2 through PDF/A-4 if there is no OutputIntent.
310 pub fn color_space(&mut self) -> ColorSpace<'_> {
311 self.insert(Name(b"CS")).start()
312 }
313
314 /// Set the `/I` attribute to indicate whether the group is isolated.
315 ///
316 /// If it is true, the group will initially be composited against a clear
317 /// backdrop. If it is false, the group will be composited against the
318 /// backdrop of the parent group.
319 pub fn isolated(&mut self, isolated: bool) -> &mut Self {
320 self.pair(Name(b"I"), isolated);
321 self
322 }
323
324 /// Set the `/K` attribute to indicate whether the group is a knockout
325 /// group.
326 ///
327 /// Within a knockout group, the group children are all composited
328 /// separately against the backdrop instead of on top of each other.
329 pub fn knockout(&mut self, knockout: bool) -> &mut Self {
330 self.pair(Name(b"K"), knockout);
331 self
332 }
333}
334
335deref!('a, Group<'a> => Dict<'a>, dict);
336
337/// Writer for an _external XObject reference dictionary_. PDF 1.4+.
338///
339/// This struct is created by [`FormXObject::reference`].
340///
341/// Reference XObjects are forbidden in PDF/A.
342pub struct Reference<'a> {
343 dict: Dict<'a>,
344}
345
346writer!(Reference: |obj| Self { dict: obj.dict() });
347
348impl Reference<'_> {
349 /// Start writing the `/F` attribute to set a file specification dictionary.
350 /// Required.
351 pub fn file_spec(&mut self) -> FileSpec<'_> {
352 self.insert(Name(b"F")).start()
353 }
354
355 /// Write the `/Page` attribute to set the page number. Setting the
356 /// attribute through either this function or [`Self::page_label`] is
357 /// required. Page indices start at 0.
358 pub fn page_number(&mut self, page: i32) -> &mut Self {
359 self.pair(Name(b"Page"), page);
360 self
361 }
362
363 /// Write the `/Page` attribute to set the page label. Setting the attribute
364 /// through either this function or [`Self::page_number`] is required.
365 pub fn page_label(&mut self, label: impl TextStrLike) -> &mut Self {
366 self.pair(Name(b"Page"), label);
367 self
368 }
369
370 /// Write the `/ID` attribute to set the file identifier.
371 pub fn id(&mut self, id: [Str; 2]) -> &mut Self {
372 self.insert(Name(b"ID")).array().items(id);
373 self
374 }
375}
376
377deref!('a, Reference<'a> => Dict<'a>, dict);