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);