pdf_writer/
xobject.rs

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