pdf_writer/color.rs
1use super::*;
2
3/// CIE XYZ coordinates of the D65 noon daylight white.
4const CIE_D65: [f32; 3] = [0.9505, 1.0, 1.0888];
5
6/// CIE XYZ coordinates of the D50 horizon light white.
7const CIE_D50: [f32; 3] = [0.9642, 1.0, 0.82489];
8
9/// CIE XYZ coordinates of the E equal radiator white.
10const CIE_E: [f32; 3] = [1.000, 1.000, 1.000];
11
12/// CIE XYZ coordinates of the C north sky daylight white.
13const CIE_C: [f32; 3] = [0.9807, 1.0000, 1.1822];
14
15/// The type of a color space.
16#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
17#[allow(unused)]
18enum ColorSpaceType {
19 CalGray,
20 CalRgb,
21 Lab,
22 IccBased,
23 DeviceRgb,
24 DeviceCmyk,
25 DeviceGray,
26 Indexed,
27 Pattern,
28 Separation,
29 DeviceN,
30}
31
32impl ColorSpaceType {
33 pub(crate) fn to_name(self) -> Name<'static> {
34 match self {
35 Self::CalRgb => Name(b"CalRGB"),
36 Self::CalGray => Name(b"CalGray"),
37 Self::Lab => Name(b"Lab"),
38 Self::IccBased => Name(b"ICCBased"),
39 Self::DeviceRgb => Name(b"DeviceRGB"),
40 Self::DeviceCmyk => Name(b"DeviceCMYK"),
41 Self::DeviceGray => Name(b"DeviceGray"),
42 Self::Separation => Name(b"Separation"),
43 Self::DeviceN => Name(b"DeviceN"),
44 Self::Indexed => Name(b"Indexed"),
45 Self::Pattern => Name(b"Pattern"),
46 }
47 }
48}
49
50/// The type of a device color space.
51#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
52pub enum DeviceColorSpace {
53 /// Red, green and blue.
54 Rgb,
55 /// Cyan, magenta, yellow and black.
56 Cmyk,
57 /// Gray.
58 Gray,
59}
60
61impl DeviceColorSpace {
62 pub(crate) fn to_name(self) -> Name<'static> {
63 match self {
64 Self::Rgb => Name(b"DeviceRGB"),
65 Self::Cmyk => Name(b"DeviceCMYK"),
66 Self::Gray => Name(b"DeviceGray"),
67 }
68 }
69}
70
71/// Writer for a _color space_.
72///
73/// This struct is created by [`Chunk::color_space`],
74/// [`Chunk::color_space`], [`ImageXObject::color_space`],
75/// [`Separation::alternate_color_space`] and [`Group::color_space`].
76pub struct ColorSpace<'a> {
77 obj: Obj<'a>,
78}
79
80writer!(ColorSpace: |obj| Self { obj });
81
82/// CIE-based color spaces.
83impl ColorSpace<'_> {
84 /// Write a `CalRGB` color space.
85 pub fn cal_rgb(
86 self,
87 white_point: [f32; 3],
88 black_point: Option<[f32; 3]>,
89 gamma: Option<[f32; 3]>,
90 matrix: Option<[f32; 9]>,
91 ) {
92 let mut array = self.obj.array();
93 array.item(ColorSpaceType::CalRgb.to_name());
94
95 let mut dict = array.push().dict();
96 dict.insert(Name(b"WhitePoint")).array().items(white_point);
97
98 if let Some(black_point) = black_point {
99 dict.insert(Name(b"BlackPoint")).array().items(black_point);
100 }
101
102 if let Some(gamma) = gamma {
103 dict.insert(Name(b"Gamma")).array().items(gamma);
104 }
105
106 if let Some(matrix) = matrix {
107 dict.insert(Name(b"Matrix")).array().items(matrix);
108 }
109 }
110
111 /// Write a `CalGray` color space.
112 pub fn cal_gray(
113 self,
114 white_point: [f32; 3],
115 black_point: Option<[f32; 3]>,
116 gamma: Option<f32>,
117 ) {
118 let mut array = self.obj.array();
119 array.item(ColorSpaceType::CalGray.to_name());
120
121 let mut dict = array.push().dict();
122 dict.insert(Name(b"WhitePoint")).array().items(white_point);
123
124 if let Some(black_point) = black_point {
125 dict.insert(Name(b"BlackPoint")).array().items(black_point);
126 }
127
128 if let Some(gamma) = gamma {
129 dict.pair(Name(b"Gamma"), gamma);
130 }
131 }
132
133 /// Write a `Lab` color space.
134 pub fn lab(
135 self,
136 white_point: [f32; 3],
137 black_point: Option<[f32; 3]>,
138 range: Option<[f32; 4]>,
139 ) {
140 let mut array = self.obj.array();
141 array.item(ColorSpaceType::Lab.to_name());
142
143 let mut dict = array.push().dict();
144 dict.insert(Name(b"WhitePoint")).array().items(white_point);
145
146 if let Some(black_point) = black_point {
147 dict.insert(Name(b"BlackPoint")).array().items(black_point);
148 }
149
150 if let Some(range) = range {
151 dict.insert(Name(b"Range")).array().items(range);
152 }
153 }
154
155 /// Write an `ICCBased` color space.
156 ///
157 /// The `stream` argument should be an indirect reference to an [ICC
158 /// profile](IccProfile) stream.
159 pub fn icc_based(self, stream: Ref) {
160 let mut array = self.obj.array();
161 array.item(ColorSpaceType::IccBased.to_name());
162 array.item(stream);
163 }
164}
165
166/// Writer for an _ICC profile stream_.
167///
168/// This struct is created by [`Chunk::icc_profile`].
169pub struct IccProfile<'a> {
170 stream: Stream<'a>,
171}
172
173impl<'a> IccProfile<'a> {
174 /// Create a new ICC profile stream writer
175 pub(crate) fn start(stream: Stream<'a>) -> Self {
176 Self { stream }
177 }
178
179 /// Write the `/N` attribute. Required.
180 ///
181 /// The number of components in the color space.
182 /// Shall be 1, 3, or 4.
183 pub fn n(&mut self, n: i32) -> &mut Self {
184 assert!(n == 1 || n == 3 || n == 4, "n must be 1, 3, or 4, but is {}", n);
185 self.pair(Name(b"N"), n);
186 self
187 }
188
189 /// Write the `/Alternate` attribute with a color space.
190 ///
191 /// The alternate color space to use when the ICC profile is not
192 /// supported. Must be a color space with the same number of
193 /// components as the ICC profile. Pattern color spaces are not
194 /// allowed.
195 pub fn alternate(&mut self) -> ColorSpace<'_> {
196 ColorSpace::start(self.insert(Name(b"Alternate")))
197 }
198
199 /// Write the `/Alternate` attribute with a name.
200 ///
201 /// The alternate color space referenced by name must be registered in the
202 /// current [resource dictionary.](crate::writers::Resources)
203 pub fn alternate_name(&mut self, name: Name<'_>) -> &mut Self {
204 self.pair(Name(b"Alternate"), name);
205 self
206 }
207
208 /// Write the `/Range` attribute.
209 ///
210 /// Specifies the permissible range of values for each component. The array
211 /// shall contain 2 × `N` numbers, where [`N`](Self::n) is the number of
212 /// components in the color space. The array is organized in pairs, where
213 /// the first value shall be the minimum value and the second shall be the
214 /// maximum value.
215 pub fn range(&mut self, range: impl IntoIterator<Item = f32>) -> &mut Self {
216 self.insert(Name(b"Range")).array().typed().items(range);
217 self
218 }
219
220 /// Write the `/Metadata` attribute.
221 ///
222 /// A reference to a [stream containing metadata](crate::writers::Metadata)
223 /// for the ICC profile.
224 pub fn metadata(&mut self, metadata: Ref) -> &mut Self {
225 self.pair(Name(b"Metadata"), metadata);
226 self
227 }
228}
229
230deref!('a, IccProfile<'a> => Stream<'a>, stream);
231
232/// Common calibrated color spaces.
233impl ColorSpace<'_> {
234 /// Write a `CalRGB` color space approximating sRGB.
235 ///
236 /// Use an ICC profile for more accurate results.
237 pub fn srgb(self) {
238 self.cal_rgb(
239 CIE_D65,
240 None,
241 Some([2.2, 2.2, 2.2]),
242 Some([0.4124, 0.2126, 0.0193, 0.3576, 0.715, 0.1192, 0.1805, 0.0722, 0.9505]),
243 )
244 }
245
246 /// Write a `CalRGB` color space approximating Adobe RGB.
247 ///
248 /// Use an ICC profile for more accurate results.
249 pub fn adobe_rgb(self) {
250 self.cal_rgb(
251 CIE_D65,
252 None,
253 Some([2.1992188, 2.1992188, 2.1992188]),
254 Some([
255 0.57667, 0.29734, 0.02703, 0.18556, 0.62736, 0.07069, 0.18823, 0.07529,
256 0.99134,
257 ]),
258 )
259 }
260
261 /// Write a `CalRGB` color space approximating Display P3.
262 ///
263 /// Use an ICC profile for more accurate results.
264 pub fn display_p3(self) {
265 self.cal_rgb(
266 CIE_D65,
267 None,
268 Some([2.6, 2.6, 2.6]),
269 Some([
270 0.48657, 0.2297, 0.0, 0.26567, 0.69174, 0.04511, 0.19822, 0.07929,
271 1.04394,
272 ]),
273 )
274 }
275
276 /// Write a `CalRGB` color space approximating ProPhoto.
277 ///
278 /// Use an ICC profile for more accurate results.
279 pub fn pro_photo(self) {
280 self.cal_rgb(
281 CIE_D50,
282 None,
283 Some([1.8, 1.8, 1.8]),
284 Some([
285 0.7976749, 0.2880402, 0.0, 0.1351917, 0.7118741, 0.0, 0.0313534,
286 0.0000857, 0.82521,
287 ]),
288 )
289 }
290
291 /// Write a `CalRGB` color space for ECI RGB v1.
292 pub fn eci_rgb(self) {
293 self.cal_rgb(
294 CIE_D50,
295 None,
296 Some([1.8, 1.8, 1.8]),
297 Some([
298 0.6502043, 0.3202499, 0.0, 0.1780774, 0.6020711, 0.0678390, 0.1359384,
299 0.0776791, 0.757371,
300 ]),
301 )
302 }
303
304 /// Write a `CalRGB` color space for NTSC RGB.
305 pub fn ntsc(self) {
306 self.cal_rgb(
307 CIE_C,
308 None,
309 Some([2.2, 2.2, 2.2]),
310 Some([
311 0.6068909, 0.2989164, 0.0, 0.1735011, 0.586599, 0.0660957, 0.200348,
312 0.1144845, 1.1162243,
313 ]),
314 )
315 }
316
317 /// Write a `CalRGB` color space for PAL/SECAM RGB.
318 pub fn pal(self) {
319 self.cal_rgb(
320 CIE_D65,
321 None,
322 Some([2.2, 2.2, 2.2]),
323 Some([
324 0.430619, 0.2220379, 0.0201853, 0.3415419, 0.7066384, 0.1295504,
325 0.1783091, 0.0713236, 0.9390944,
326 ]),
327 )
328 }
329
330 /// Write a `CalGray` color space for CIE D65 at a 2.2 gamma, equivalent to
331 /// sRGB, Adobe RGB, Display P3, PAL, ...
332 pub fn d65_gray(self) {
333 self.cal_gray(CIE_D65, None, Some(2.2))
334 }
335
336 /// Write a `CalGray` color space for CIE D50 (horizon light). Set a 1.8
337 /// gamma for ProPhoto or ECI RGB equivalency, 2.2 is another common value.
338 pub fn d50_gray(self, gamma: Option<f32>) {
339 self.cal_gray(CIE_D50, None, gamma)
340 }
341
342 /// Write a `CalGray` color space for CIE C (north sky daylight) at 2.2
343 /// gamma, equivalent to NTSC.
344 pub fn c_gray(self) {
345 self.cal_gray(CIE_C, None, Some(2.2))
346 }
347
348 /// Write a `CalGray` color space for CIE E (equal emission). Common gamma
349 /// values include 1.8 or 2.2.
350 pub fn e_gray(self, gamma: Option<f32>) {
351 self.cal_gray(CIE_E, None, gamma)
352 }
353}
354
355/// Device color spaces.
356///
357/// Please note that the use of the device color spaces is restricted by several
358/// PDF standards such as PDF/A, PDF/X, et cetera. Their appearance will be
359/// governed by any applicable [output intent](crate::writers::OutputIntent) and
360/// default color spaces.
361impl ColorSpace<'_> {
362 /// Write a `DeviceRGB` color space.
363 pub fn device_rgb(self) {
364 self.obj.primitive(ColorSpaceType::DeviceRgb.to_name());
365 }
366
367 /// Write a `DeviceCMYK` color space.
368 pub fn device_cmyk(self) {
369 self.obj.primitive(ColorSpaceType::DeviceCmyk.to_name());
370 }
371
372 /// Write a `DeviceGray` color space.
373 pub fn device_gray(self) {
374 self.obj.primitive(ColorSpaceType::DeviceGray.to_name());
375 }
376}
377
378/// Special color spaces.
379impl<'a> ColorSpace<'a> {
380 /// Start writing a `Separation` color space. PDF 1.2+.
381 ///
382 /// The `color_name` argument is the name of the colorant that will be
383 /// used by the printer.
384 pub fn separation(self, color_name: Name) -> Separation<'a> {
385 let mut array = self.obj.array();
386 array.item(ColorSpaceType::Separation.to_name());
387 array.item(color_name);
388 Separation::start(array)
389 }
390
391 /// Write a `DeviceN` color space. PDF 1.3+.
392 ///
393 /// The `names` argument contains the N names of the colorants for the
394 /// respective components.
395 pub fn device_n<'n>(self, names: impl IntoIterator<Item = Name<'n>>) -> DeviceN<'a> {
396 let mut array = self.obj.array();
397 array.item(ColorSpaceType::DeviceN.to_name());
398 array.push().array().items(names);
399 DeviceN::start(array)
400 }
401
402 /// Write an `Indexed` color space. PDF 1.2+.
403 ///
404 /// The length of the lookup slice must be the product of the dimensions of
405 /// the base color space and (`hival + 1`) and `hival` shall be at most 255.
406 pub fn indexed(self, base: Name, hival: i32, lookup: &[u8]) {
407 let mut array = self.obj.array();
408 array.item(ColorSpaceType::Indexed.to_name());
409 array.item(base);
410 array.item(hival);
411 array.item(Str(lookup));
412 }
413
414 /// Write a `Pattern` color space for uncolored patterns. PDF 1.2+.
415 ///
416 /// The `base` attribute is the color space in which the pattern's
417 /// [tint](Content::set_stroke_pattern) color is specified upon use.
418 pub fn pattern(self, base: Name) {
419 let mut array = self.obj.array();
420 array.item(ColorSpaceType::Pattern.to_name());
421 array.item(base);
422 }
423}
424
425/// Type of pattern.
426#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
427enum PatternType {
428 /// A tiling pattern.
429 Tiling,
430 /// A shading pattern.
431 Shading,
432}
433
434impl PatternType {
435 pub(crate) fn to_int(self) -> i32 {
436 match self {
437 Self::Tiling => 1,
438 Self::Shading => 2,
439 }
440 }
441}
442
443/// Writer for a _separation dictionary_. PDF 1.2+.
444///
445/// First, one of the `alternate_...` methods must be called to specify the
446/// alternate color space. Then, one of the `tint_...` methods must be called
447/// to specify the tint transform method. If the tint transform method is
448/// called before the alternate color space, the function panics. If multiple
449/// alternate color space functions are called, the function panics.
450///
451/// This struct is created by [`ColorSpace::separation`].
452pub struct Separation<'a> {
453 array: Array<'a>,
454 has_alternate: bool,
455}
456
457impl<'a> Separation<'a> {
458 /// Start the wrapper.
459 pub(crate) fn start(array: Array<'a>) -> Self {
460 Self { array, has_alternate: false }
461 }
462
463 /// Write the `alternateSpace` element as a device color space.
464 pub fn alternate_device(&mut self, device_space: DeviceColorSpace) -> &mut Self {
465 if self.has_alternate {
466 panic!("alternate space already specified");
467 }
468 self.array.item(device_space.to_name());
469 self.has_alternate = true;
470 self
471 }
472
473 /// Start writing the `alternateSpace` element as a color space array. The
474 /// color space must not be another `Pattern`, `Separation`, or `DeviceN`
475 /// color space.
476 pub fn alternate_color_space(&mut self) -> ColorSpace<'_> {
477 if self.has_alternate {
478 panic!("alternate space already specified");
479 }
480 self.has_alternate = true;
481 ColorSpace::start(self.array.push())
482 }
483
484 /// Write the `alternateSpace` element as an indirect reference. The color
485 /// space must not be another `Pattern`, `Separation`, or `DeviceN` color
486 /// space.
487 pub fn alternate_color_space_ref(&mut self, id: Ref) -> &mut Self {
488 if self.has_alternate {
489 panic!("alternate space already specified");
490 }
491 self.array.item(id);
492 self.has_alternate = true;
493 self
494 }
495
496 /// Start writing the `tintTransform` element as an exponential
497 /// interpolation function.
498 pub fn tint_exponential(&mut self) -> ExponentialFunction<'_> {
499 if !self.has_alternate {
500 panic!("alternate space must be specified before tint transform");
501 }
502 ExponentialFunction::start(self.array.push())
503 }
504
505 /// Start writing the `tintTransform` element as a stitching function.
506 pub fn tint_stitching(&mut self) -> StitchingFunction<'_> {
507 if !self.has_alternate {
508 panic!("alternate space must be specified before tint transform");
509 }
510 StitchingFunction::start(self.array.push())
511 }
512
513 /// Write the `tintTransform` element as an indirect reference to a
514 /// function. The function must take a single number as input and produce a
515 /// color in the alternate color space as output. This must be used if a
516 /// stream function like [`SampledFunction`] or [`PostScriptFunction`] is
517 /// used.
518 pub fn tint_ref(&mut self, id: Ref) -> &mut Self {
519 if !self.has_alternate {
520 panic!("alternate space must be specified before tint transform");
521 }
522 self.array.item(id);
523 self
524 }
525}
526
527/// Writer for a _DeviceN color space array with attributes_. PDF 1.6+.
528///
529/// First, one of the `alternate_...` methods must be called to specify the
530/// alternate color space. Then, one of the `tint_...` methods must be called to
531/// specify the tint transform method. Finally, the `attrs` function may
532/// optionally be called. If any function is called out of order, the function
533/// panics.
534///
535/// This struct is created by [`ColorSpace::device_n`].
536pub struct DeviceN<'a> {
537 array: Array<'a>,
538 has_alternate: bool,
539 has_tint: bool,
540}
541
542impl<'a> DeviceN<'a> {
543 /// Start the wrapper.
544 pub(crate) fn start(array: Array<'a>) -> Self {
545 Self { array, has_alternate: false, has_tint: false }
546 }
547
548 /// Write the `alternateSpace` element as a device color space.
549 pub fn alternate_device(&mut self, device_space: DeviceColorSpace) -> &mut Self {
550 if self.has_alternate {
551 panic!("alternate space already specified");
552 }
553 self.array.item(device_space.to_name());
554 self.has_alternate = true;
555 self
556 }
557
558 /// Start writing the `alternateSpace` element as a color space array. The
559 /// color space must not be another `Pattern`, `Separation`, or `DeviceN`
560 /// color space.
561 pub fn alternate_color_space(&mut self) -> ColorSpace<'_> {
562 if self.has_alternate {
563 panic!("alternate space already specified");
564 }
565 self.has_alternate = true;
566 ColorSpace::start(self.array.push())
567 }
568
569 /// Write the `alternateSpace` element as an indirect reference. The color
570 /// space must not be another `Pattern`, `Separation`, or `DeviceN` color
571 /// space.
572 pub fn alternate_color_space_ref(&mut self, id: Ref) -> &mut Self {
573 if self.has_alternate {
574 panic!("alternate space already specified");
575 }
576 self.array.item(id);
577 self.has_alternate = true;
578 self
579 }
580
581 /// Start writing the `tintTransform` element as an exponential
582 /// interpolation function.
583 pub fn tint_exponential(&mut self) -> ExponentialFunction<'_> {
584 if !self.has_alternate {
585 panic!("alternate space must be specified before tint transform");
586 } else if self.has_tint {
587 panic!("tint transform already specified");
588 }
589
590 self.has_tint = true;
591 ExponentialFunction::start(self.array.push())
592 }
593
594 /// Start writing the `tintTransform` element as a stitching function.
595 pub fn tint_stitching(&mut self) -> StitchingFunction<'_> {
596 if !self.has_alternate {
597 panic!("alternate space must be specified before tint transform");
598 } else if self.has_tint {
599 panic!("tint transform already specified");
600 }
601
602 self.has_tint = true;
603 StitchingFunction::start(self.array.push())
604 }
605
606 /// Write the `tintTransform` element as an indirect reference to a
607 /// function. The function must take n numbers as input and produce a color
608 /// in the alternate color space as output. This must be used if a stream
609 /// function like [`SampledFunction`] or [`PostScriptFunction`] is used.
610 pub fn tint_ref(&mut self, id: Ref) -> &mut Self {
611 if !self.has_alternate {
612 panic!("alternate space must be specified before tint transform");
613 } else if self.has_tint {
614 panic!("tint transform already specified");
615 }
616
617 self.array.item(id);
618 self.has_tint = true;
619 self
620 }
621
622 /// Start writing the `attrs` dictionary. PDF 1.6+.
623 pub fn attrs(&mut self) -> DeviceNAttrs<'_> {
624 if !self.has_alternate {
625 panic!(
626 "alternate space and tint transform must be specified before attributes"
627 );
628 } else if !self.has_tint {
629 panic!("tint transform must be specified before attributes");
630 }
631
632 DeviceNAttrs::start(self.array.push())
633 }
634}
635
636/// Writer for a _DeviceN attributes dictionary_. PDF 1.6+.
637///
638/// This struct is created by [`DeviceN::attrs`].
639pub struct DeviceNAttrs<'a> {
640 dict: Dict<'a>,
641}
642
643writer!(DeviceNAttrs: |obj| Self { dict: obj.dict() });
644
645impl DeviceNAttrs<'_> {
646 /// Write the `/Subtype` attribute.
647 pub fn subtype(&mut self, subtype: DeviceNSubtype) -> &mut Self {
648 self.dict.pair(Name(b"Subtype"), subtype.to_name());
649 self
650 }
651
652 /// Start writing the `/Colorants` dictionary. Its keys are the colorant
653 /// names and its values are separation color space arrays.
654 ///
655 /// Required if the `/Subtype` attribute is `NChannel`. Required for spot
656 /// colors in PDF/A-2, PDF/A-3, and PDF/A-4.
657 pub fn colorants(&mut self) -> TypedDict<'_, Dict> {
658 self.dict.insert(Name(b"Colorants")).dict().typed()
659 }
660
661 /// Start writing the `/Process` dictionary.
662 ///
663 /// Required if the `/Subtype` attribute is `Separation`.
664 pub fn process(&mut self) -> DeviceNProcess<'_> {
665 DeviceNProcess::start(self.dict.insert(Name(b"Process")))
666 }
667
668 /// Start writing the `/MixingHints` dictionary.
669 pub fn mixing_hints(&mut self) -> DeviceNMixingHints<'_> {
670 DeviceNMixingHints::start(self.dict.insert(Name(b"MixingHints")))
671 }
672}
673
674/// Writer for a _DeviceN process dictionary_. PDF 1.6+.
675///
676/// This struct is created by [`DeviceNAttrs::process`].
677pub struct DeviceNProcess<'a> {
678 dict: Dict<'a>,
679}
680
681writer!(DeviceNProcess: |obj| Self { dict: obj.dict() });
682
683impl DeviceNProcess<'_> {
684 /// Write the `/ColorSpace` attribute with a name. Required.
685 pub fn color_space(&mut self, color_space: Name) -> &mut Self {
686 self.dict.pair(Name(b"ColorSpace"), color_space);
687 self
688 }
689
690 /// Write the `/ColorSpace` attribute with an array. Required.
691 pub fn color_space_array(&mut self) -> ColorSpace<'_> {
692 ColorSpace::start(self.dict.insert(Name(b"ColorSpace")))
693 }
694
695 /// Write the `/Components` attribute. Required.
696 ///
697 /// Contains the names of the colorants in the order in which they appear in
698 /// the color space array.
699 pub fn components<'n>(
700 &mut self,
701 components: impl IntoIterator<Item = Name<'n>>,
702 ) -> &mut Self {
703 self.dict
704 .insert(Name(b"Components"))
705 .array()
706 .typed()
707 .items(components);
708 self
709 }
710}
711
712/// Type of n-dimensional color space.
713pub enum DeviceNSubtype {
714 /// A subtractive color space.
715 DeviceN,
716 /// An additive color space.
717 NChannel,
718}
719
720impl DeviceNSubtype {
721 pub(crate) fn to_name(self) -> Name<'static> {
722 match self {
723 Self::DeviceN => Name(b"DeviceN"),
724 Self::NChannel => Name(b"NChannel"),
725 }
726 }
727}
728
729/// Writer for a _DeviceN mixing hints dictionary_. PDF 1.6+.
730///
731/// This struct is created by [`DeviceNAttrs::mixing_hints`].
732pub struct DeviceNMixingHints<'a> {
733 dict: Dict<'a>,
734}
735
736writer!(DeviceNMixingHints: |obj| Self { dict: obj.dict() });
737
738impl DeviceNMixingHints<'_> {
739 /// Start writing the `/Solidities` dictionary.
740 ///
741 /// Each key in the dictionary is a colorant name and each value is a number
742 /// between 0 and 1 indicating the relative solidity of the colorant.
743 pub fn solidities(&mut self) -> TypedDict<'_, f32> {
744 self.dict.insert(Name(b"Solidities")).dict().typed()
745 }
746
747 /// Write the `/PrintingOrder` attribute.
748 ///
749 /// Required if `/Solidities` is present. An array of colorant names in the
750 /// order in which they should be printed.
751 pub fn printing_order<'n>(
752 &mut self,
753 order: impl IntoIterator<Item = Name<'n>>,
754 ) -> &mut Self {
755 self.dict.insert(Name(b"PrintingOrder")).array().typed().items(order);
756 self
757 }
758
759 /// Start writing the `/DotGain` dictionary.
760 ///
761 /// Each key in the dictionary is a colorant name and each value is a number
762 /// between 0 and 1 indicating the dot gain of the colorant.
763 pub fn dot_gain(&mut self) -> TypedDict<'_, f32> {
764 self.dict.insert(Name(b"DotGain")).dict().typed()
765 }
766}
767
768/// Writer for a _tiling pattern stream_.
769///
770/// This struct is created by [`Chunk::tiling_pattern`].
771pub struct TilingPattern<'a> {
772 stream: Stream<'a>,
773}
774
775impl<'a> TilingPattern<'a> {
776 pub(crate) fn start_with_stream(mut stream: Stream<'a>) -> Self {
777 stream.pair(Name(b"Type"), Name(b"Pattern"));
778 stream.pair(Name(b"PatternType"), PatternType::Tiling.to_int());
779 Self { stream }
780 }
781
782 /// Write the `/PaintType` attribute.
783 ///
784 /// Sets whether to use external or stream color. Required.
785 pub fn paint_type(&mut self, paint_type: PaintType) -> &mut Self {
786 self.stream.pair(Name(b"PaintType"), paint_type.to_int());
787 self
788 }
789
790 /// Write the `/TilingType` attribute.
791 ///
792 /// Sets how to stretch and space the pattern. Required.
793 pub fn tiling_type(&mut self, tiling_type: TilingType) -> &mut Self {
794 self.stream.pair(Name(b"TilingType"), tiling_type.to_int());
795 self
796 }
797
798 /// Write the `/BBox` attribute.
799 ///
800 /// Sets the bounding box of the pattern in the pattern's coordinate system.
801 /// Required.
802 pub fn bbox(&mut self, bbox: Rect) -> &mut Self {
803 self.stream.pair(Name(b"BBox"), bbox);
804 self
805 }
806
807 /// Write the `/XStep` attribute.
808 ///
809 /// Sets the horizontal spacing between pattern cells. Required.
810 ///
811 /// Panics if `x_step` is zero.
812 pub fn x_step(&mut self, x_step: f32) -> &mut Self {
813 assert!(x_step != 0.0, "x step must not be zero");
814 self.stream.pair(Name(b"XStep"), x_step);
815 self
816 }
817
818 /// Write the `/YStep` attribute.
819 ///
820 /// Sets the vertical spacing between pattern cells. Required.
821 ///
822 /// Panics if `y_step` is zero.
823 pub fn y_step(&mut self, y_step: f32) -> &mut Self {
824 assert!(y_step != 0.0, "y step must not be zero");
825 self.stream.pair(Name(b"YStep"), y_step);
826 self
827 }
828
829 /// Start writing the `/Resources` dictionary.
830 ///
831 /// Sets the resources used by the pattern. Required.
832 pub fn resources(&mut self) -> Resources<'_> {
833 self.insert(Name(b"Resources")).start()
834 }
835
836 /// Write the `/Matrix` attribute.
837 ///
838 /// Maps the pattern coordinate system to the parent content stream
839 /// coordinates. The default is the identity matrix.
840 pub fn matrix(&mut self, matrix: [f32; 6]) -> &mut Self {
841 self.stream.insert(Name(b"Matrix")).array().items(matrix);
842 self
843 }
844}
845
846deref!('a, TilingPattern<'a> => Stream<'a>, stream);
847
848/// Type of paint for a tiling pattern.
849#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
850pub enum PaintType {
851 /// Paint the pattern with the colors specified in the stream.
852 Colored,
853 /// Paint the pattern with the colors active when the pattern was painted.
854 Uncolored,
855}
856
857impl PaintType {
858 pub(crate) fn to_int(self) -> i32 {
859 match self {
860 Self::Colored => 1,
861 Self::Uncolored => 2,
862 }
863 }
864}
865
866/// How to adjust tile spacing.
867#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
868pub enum TilingType {
869 /// Constant space between each tile, tiles may be distorted by 1px.
870 ConstantSpacing,
871 /// Tile size is constant, spacing between may vary by 1px.
872 NoDistortion,
873 /// Constant space between each tile and faster drawing, tiles may be distorted.
874 FastConstantSpacing,
875}
876
877impl TilingType {
878 pub(crate) fn to_int(self) -> i32 {
879 match self {
880 Self::ConstantSpacing => 1,
881 Self::NoDistortion => 2,
882 Self::FastConstantSpacing => 3,
883 }
884 }
885}
886
887/// Writer for a _shading pattern dictionary_. PDF 1.3+.
888///
889/// This struct is created by [`Chunk::shading_pattern`].
890pub struct ShadingPattern<'a> {
891 dict: Dict<'a>,
892}
893
894writer!(ShadingPattern: |obj| {
895 let mut dict = obj.dict();
896 dict.pair(Name(b"Type"), Name(b"Pattern"));
897 dict.pair(Name(b"PatternType"), PatternType::Shading.to_int());
898 Self { dict }
899});
900
901impl ShadingPattern<'_> {
902 /// Start writing the `/Shading` dictionary for a type 1, 2, or 3 shading.
903 pub fn function_shading(&mut self) -> FunctionShading<'_> {
904 self.dict.insert(Name(b"Shading")).start()
905 }
906
907 /// Add an indirect reference to a `/Shading` dictionary or a shading stream.
908 pub fn shading_ref(&mut self, id: Ref) -> &mut Self {
909 self.dict.pair(Name(b"Shading"), id);
910 self
911 }
912
913 /// Write the `/Matrix` attribute.
914 ///
915 /// Sets the matrix to use for the pattern. Defaults to the identity matrix.
916 pub fn matrix(&mut self, matrix: [f32; 6]) -> &mut Self {
917 self.dict.insert(Name(b"Matrix")).array().items(matrix);
918 self
919 }
920
921 /// Start writing the `/ExtGState` attribute.
922 pub fn ext_graphics(&mut self) -> ExtGraphicsState<'_> {
923 self.dict.insert(Name(b"ExtGState")).start()
924 }
925}
926
927deref!('a, ShadingPattern<'a> => Dict< 'a>, dict);
928
929/// Writer for a _shading dictionary_. PDF 1.3+.
930///
931/// This struct is created by [`Chunk::function_shading`] and
932/// [`ShadingPattern::function_shading`].
933pub struct FunctionShading<'a> {
934 dict: Dict<'a>,
935}
936
937writer!(FunctionShading: |obj| Self { dict: obj.dict() });
938
939impl FunctionShading<'_> {
940 /// Write the `/ShadingType` attribute.
941 ///
942 /// Sets the type of shading. The available and required attributes change
943 /// depending on this. Required.
944 pub fn shading_type(&mut self, kind: FunctionShadingType) -> &mut Self {
945 self.dict.pair(Name(b"ShadingType"), kind.to_int());
946 self
947 }
948
949 /// Start writing the `/ColorSpace` attribute.
950 ///
951 /// Sets the color space of the shading function. May not be a `Pattern`
952 /// space. Required.
953 pub fn color_space(&mut self) -> ColorSpace<'_> {
954 self.dict.insert(Name(b"ColorSpace")).start()
955 }
956
957 /// Write the `/Background` attribute.
958 ///
959 /// Sets the background color of the area to be shaded. The `background`
960 /// iterator must contain exactly as many elements as the current
961 /// color space has dimensions.
962 pub fn background(&mut self, background: impl IntoIterator<Item = f32>) -> &mut Self {
963 self.dict.insert(Name(b"Background")).array().items(background);
964 self
965 }
966
967 /// Write the `/BBox` attribute.
968 ///
969 /// Sets the bounding box of the shading in the target coordinate system.
970 pub fn bbox(&mut self, bbox: Rect) -> &mut Self {
971 self.dict.pair(Name(b"BBox"), bbox);
972 self
973 }
974
975 /// Write the `/AntiAlias` attribute.
976 ///
977 /// Sets whether to anti-alias the shading.
978 pub fn anti_alias(&mut self, anti_alias: bool) -> &mut Self {
979 self.dict.pair(Name(b"AntiAlias"), anti_alias);
980 self
981 }
982
983 /// Write the `/Domain` attribute.
984 ///
985 /// Sets the domain of the shading function in a rectangle. Can be used for
986 /// function, axial, or radial shadings. Will otherwise default to
987 /// `[x_min = 0, x_max = 1, y_min = 0, y_max = 1]`
988 pub fn domain(&mut self, domain: [f32; 4]) -> &mut Self {
989 self.dict.insert(Name(b"Domain")).array().items(domain);
990 self
991 }
992
993 /// Write the `/Matrix` attribute.
994 ///
995 /// Maps the shading domain rectangle to the target coordinate system. Can
996 /// be used for function shadings. Will otherwise
997 /// default to the identity matrix.
998 pub fn matrix(&mut self, matrix: [f32; 6]) -> &mut Self {
999 self.dict.insert(Name(b"Matrix")).array().items(matrix);
1000 self
1001 }
1002
1003 /// Write the `/Function` attribute.
1004 ///
1005 /// Sets the function to use for shading. Number of in- and outputs depends
1006 /// on shading type. Required for type 1, 2, and 3, optional otherwise.
1007 pub fn function(&mut self, function: Ref) -> &mut Self {
1008 self.dict.pair(Name(b"Function"), function);
1009 self
1010 }
1011
1012 /// Write the `/Coords` attribute.
1013 ///
1014 /// Sets the coordinates of the start and end of the axis in terms of the
1015 /// target coordinate system. Required for axial (4 items) and radial (6
1016 /// items; centers and radii) shadings.
1017 pub fn coords(&mut self, coords: impl IntoIterator<Item = f32>) -> &mut Self {
1018 self.dict.insert(Name(b"Coords")).array().items(coords);
1019 self
1020 }
1021
1022 /// Write the `/Extend` attribute.
1023 ///
1024 /// Set whether the shading should extend beyond either side of the axis /
1025 /// circles. Can be used for axial and radial shadings.
1026 pub fn extend(&mut self, extend: [bool; 2]) -> &mut Self {
1027 self.dict.insert(Name(b"Extend")).array().items(extend);
1028 self
1029 }
1030}
1031
1032deref!('a, FunctionShading<'a> => Dict<'a>, dict);
1033
1034/// Writer for a _embedded file stream_.
1035///
1036/// This struct is created by [`Chunk::stream_shading`].
1037pub struct StreamShading<'a> {
1038 stream: Stream<'a>,
1039}
1040
1041impl<'a> StreamShading<'a> {
1042 /// Create a new character map writer.
1043 pub(crate) fn start(stream: Stream<'a>) -> Self {
1044 Self { stream }
1045 }
1046
1047 /// Write the `/ShadingType` attribute.
1048 ///
1049 /// Sets the type of shading. The available and required attributes change
1050 /// depending on this. Required.
1051 pub fn shading_type(&mut self, kind: StreamShadingType) -> &mut Self {
1052 self.stream.pair(Name(b"ShadingType"), kind.to_int());
1053 self
1054 }
1055
1056 /// Start writing the `/ColorSpace` attribute.
1057 ///
1058 /// Sets the color space of the shading function. May not be a `Pattern`
1059 /// space. Required.
1060 pub fn color_space(&mut self) -> ColorSpace<'_> {
1061 self.stream.insert(Name(b"ColorSpace")).start()
1062 }
1063
1064 /// Write the `/Background` attribute.
1065 ///
1066 /// Sets the background color of the area to be shaded. The `background`
1067 /// iterator must contain exactly as many elements as the current
1068 /// color space has dimensions.
1069 pub fn background(&mut self, background: impl IntoIterator<Item = f32>) -> &mut Self {
1070 self.stream.insert(Name(b"Background")).array().items(background);
1071 self
1072 }
1073
1074 /// Write the `/BBox` attribute.
1075 ///
1076 /// Sets the bounding box of the shading in the target coordinate system.
1077 pub fn bbox(&mut self, bbox: Rect) -> &mut Self {
1078 self.stream.pair(Name(b"BBox"), bbox);
1079 self
1080 }
1081
1082 /// Write the `/AntiAlias` attribute.
1083 ///
1084 /// Sets whether to anti-alias the shading.
1085 pub fn anti_alias(&mut self, anti_alias: bool) -> &mut Self {
1086 self.stream.pair(Name(b"AntiAlias"), anti_alias);
1087 self
1088 }
1089
1090 /// Write the `/Function` attribute.
1091 ///
1092 /// Sets the function to use for shading. Number of in- and outputs depends
1093 /// on shading type. Optional.
1094 pub fn function(&mut self, function: Ref) -> &mut Self {
1095 self.stream.pair(Name(b"Function"), function);
1096 self
1097 }
1098
1099 /// Write the `/BitsPerCoordinate` attribute.
1100 ///
1101 /// Sets how many bits are used to represent each vertex coordinate. Can be
1102 /// any power of 2 between 1 and 32. Required.
1103 pub fn bits_per_coordinate(&mut self, bits: i32) -> &mut Self {
1104 self.stream.pair(Name(b"BitsPerCoordinate"), bits);
1105 self
1106 }
1107
1108 /// Write the `/BitsPerComponent` attribute.
1109 ///
1110 /// Sets how many bits are used to represent each color component. Can be
1111 /// any power of 2 between 1 and 16. Required.
1112 pub fn bits_per_component(&mut self, bits: i32) -> &mut Self {
1113 self.stream.pair(Name(b"BitsPerComponent"), bits);
1114 self
1115 }
1116
1117 /// Write the `/BitsPerFlag` attribute.
1118 ///
1119 /// Sets how many bits are used to represent the vertices' edge flags. Can
1120 /// be 0, 1, or 2. Required for type 4, 6, and 7.
1121 pub fn bits_per_flag(&mut self, bits: i32) -> &mut Self {
1122 self.stream.pair(Name(b"BitsPerFlag"), bits);
1123 self
1124 }
1125
1126 /// Write the `/Decode` attribute.
1127 ///
1128 /// Sets the ranges of the vertices' coordinates. Required.
1129 pub fn decode(&mut self, decode: impl IntoIterator<Item = f32>) -> &mut Self {
1130 self.stream.insert(Name(b"Decode")).array().items(decode);
1131 self
1132 }
1133
1134 /// Write the `/VerticesPerRow` attribute.
1135 ///
1136 /// Sets how many vertices are in each row of the lattice. Must be greater
1137 /// than 2. Required for type 5.
1138 pub fn vertices_per_row(&mut self, vertices: i32) -> &mut Self {
1139 self.stream.pair(Name(b"VerticesPerRow"), vertices);
1140 self
1141 }
1142}
1143
1144deref!('a, StreamShading<'a> => Stream<'a>, stream);
1145
1146/// What kind of shading to use for a function-based shading.
1147#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1148pub enum FunctionShadingType {
1149 /// The function specifies the color for each point in the domain.
1150 Function,
1151 /// The function specifies the color for each point on a line.
1152 Axial,
1153 /// The function specifies the color for each circle between two nested
1154 /// circles.
1155 Radial,
1156}
1157
1158impl FunctionShadingType {
1159 pub(crate) fn to_int(self) -> i32 {
1160 match self {
1161 Self::Function => 1,
1162 Self::Axial => 2,
1163 Self::Radial => 3,
1164 }
1165 }
1166}
1167
1168/// What kind of shading to use.
1169#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1170pub enum StreamShadingType {
1171 /// The function specifies which vertex has which color. The function is
1172 /// optional.
1173 FreeformGouraud,
1174 /// The function specifies which vertex has which color. The function is
1175 /// optional.
1176 LatticeGouraud,
1177 /// The function specifies which corner of the cell has which color. The
1178 /// function is optional.
1179 CoonsPatch,
1180 /// The function specifies which corner of the cell has which color. The
1181 /// function is optional.
1182 TensorProductPatch,
1183}
1184
1185impl StreamShadingType {
1186 pub(crate) fn to_int(self) -> i32 {
1187 match self {
1188 Self::FreeformGouraud => 4,
1189 Self::LatticeGouraud => 5,
1190 Self::CoonsPatch => 6,
1191 Self::TensorProductPatch => 7,
1192 }
1193 }
1194}
1195
1196/// Writer for the _separation information dictionary_. PDF 1.3+.
1197///
1198/// This struct is created by [`Catalog::separation_info`].
1199pub struct SeparationInfo<'a> {
1200 dict: Dict<'a>,
1201}
1202
1203writer!(SeparationInfo: |obj| Self { dict: obj.dict() });
1204
1205impl SeparationInfo<'_> {
1206 /// Write the `/Pages` attribute. Required.
1207 ///
1208 /// This indicates all page dictionaries in the document that represent
1209 /// separations of the same page and shall be rendered together.
1210 pub fn pages(&mut self, pages: impl IntoIterator<Item = Ref>) -> &mut Self {
1211 self.dict.insert(Name(b"Pages")).array().typed().items(pages);
1212 self
1213 }
1214
1215 /// Write the `/DeviceColorant` attribute as a name. Required.
1216 ///
1217 /// The name of the device colorant that corresponds to the separation.
1218 pub fn device_colorant(&mut self, colorant: Name) -> &mut Self {
1219 self.dict.pair(Name(b"DeviceColorant"), colorant);
1220 self
1221 }
1222
1223 /// Write the `/DeviceColorant` attribute as a string. Required.
1224 ///
1225 /// The name of the device colorant that corresponds to the separation.
1226 pub fn device_colorant_str(&mut self, colorant: &str) -> &mut Self {
1227 self.dict.pair(Name(b"DeviceColorant"), TextStr(colorant));
1228 self
1229 }
1230
1231 /// Start writing the `/ColorSpace` array.
1232 ///
1233 /// This shall be an Separation or DeviceN color space that further defines
1234 /// the separation color space.
1235 pub fn color_space(&mut self) -> ColorSpace<'_> {
1236 self.dict.insert(Name(b"ColorSpace")).start()
1237 }
1238}
1239
1240/// Writer for an _output intent dictionary_. PDF 1.4+.
1241///
1242/// This describes the output conditions under which the document may be
1243/// rendered. Encouraged by PDF/A.
1244pub struct OutputIntent<'a> {
1245 dict: Dict<'a>,
1246}
1247
1248writer!(OutputIntent: |obj| {
1249 let mut dict = obj.dict();
1250 dict.pair(Name(b"Type"), Name(b"OutputIntent"));
1251 Self { dict }
1252});
1253
1254impl OutputIntent<'_> {
1255 /// Write the `/S` attribute. Required.
1256 pub fn subtype(&mut self, subtype: OutputIntentSubtype) -> &mut Self {
1257 self.dict.pair(Name(b"S"), subtype.to_name());
1258 self
1259 }
1260
1261 /// Write the `/OutputCondition` attribute.
1262 ///
1263 /// A human-readable description of the output condition.
1264 pub fn output_condition(&mut self, condition: TextStr) -> &mut Self {
1265 self.dict.pair(Name(b"OutputCondition"), condition);
1266 self
1267 }
1268
1269 /// Write the `/OutputConditionIdentifier` attribute.
1270 ///
1271 /// A well-known identifier for the output condition.
1272 pub fn output_condition_identifier(&mut self, identifier: TextStr) -> &mut Self {
1273 self.dict.pair(Name(b"OutputConditionIdentifier"), identifier);
1274 self
1275 }
1276
1277 /// Write the `/RegistryName` attribute.
1278 ///
1279 /// The URI of the registry that contains the output condition.
1280 pub fn registry_name(&mut self, name: TextStr) -> &mut Self {
1281 self.dict.pair(Name(b"RegistryName"), name);
1282 self
1283 }
1284
1285 /// Write the `/Info` attribute.
1286 ///
1287 /// A human-readable string with additional info about the intended output device.
1288 pub fn info(&mut self, info: TextStr) -> &mut Self {
1289 self.dict.pair(Name(b"Info"), info);
1290 self
1291 }
1292
1293 /// Write the `/DestOutputProfile` attribute.
1294 ///
1295 /// Required if `/OutputConditionIdentifier` does not contain a well-known
1296 /// identifier for the output condition.
1297 /// Must reference an [ICC profile](IccProfile) stream.
1298 ///
1299 /// Required for PDF/A. The profile must have the `prtr` or `mntr` tag.
1300 pub fn dest_output_profile(&mut self, profile: Ref) -> &mut Self {
1301 self.dict.pair(Name(b"DestOutputProfile"), profile);
1302 self
1303 }
1304}
1305
1306/// The output intent subtype.
1307#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1308pub enum OutputIntentSubtype<'a> {
1309 /// `GTS_PDFX`
1310 PDFX,
1311 /// `GTS_PDFA1`
1312 ///
1313 /// This is the right value for PDF/A-1 through PDF/A-4.
1314 PDFA,
1315 /// `ISO_PDFE1`
1316 PDFE,
1317 /// Custom name defined in an ISO 32000 extension.
1318 Custom(Name<'a>),
1319}
1320
1321impl<'a> OutputIntentSubtype<'a> {
1322 pub(crate) fn to_name(self) -> Name<'a> {
1323 match self {
1324 Self::PDFX => Name(b"GTS_PDFX"),
1325 Self::PDFA => Name(b"GTS_PDFA1"),
1326 Self::PDFE => Name(b"ISO_PDFE1"),
1327 Self::Custom(name) => name,
1328 }
1329 }
1330}