pdf_writer/
content.rs

1use super::*;
2use crate::object::TextStrLike;
3
4/// A builder for a content stream.
5pub struct Content {
6    buf: Buf,
7    q_depth: usize,
8}
9
10/// Core methods.
11impl Content {
12    /// Create a new content stream with the default buffer capacity
13    /// (currently 1 KB).
14    #[allow(clippy::new_without_default)]
15    pub fn new() -> Self {
16        Self::with_capacity(1024)
17    }
18
19    /// Create a new content stream with the specified initial buffer capacity.
20    pub fn with_capacity(capacity: usize) -> Self {
21        Self { buf: Buf::with_capacity(capacity), q_depth: 0 }
22    }
23
24    /// Start writing an arbitrary operation.
25    #[inline]
26    pub fn op<'a>(&'a mut self, operator: &'a str) -> Operation<'a> {
27        Operation::start(&mut self.buf, operator)
28    }
29
30    /// Return the buffer of the content stream.
31    ///
32    /// The buffer is essentially a thin wrapper around two objects:
33    /// - A [`Limits`] object, which can optionally be used to keep track of
34    ///   data such as the largest used integer or the longest string used in
35    ///   the content streams, which is useful information for some export
36    ///   modes.
37    /// - The actual underlying data of the content stream, which can be written
38    ///   to a chunk (and optionally apply a filter before doing so).
39    pub fn finish(mut self) -> Buf {
40        if self.buf.last() == Some(&b'\n') {
41            self.buf.inner.pop();
42        }
43        self.buf
44    }
45}
46
47/// Writer for an _operation_ in a content stream.
48///
49/// This struct is created by [`Content::op`].
50pub struct Operation<'a> {
51    buf: &'a mut Buf,
52    op: &'a str,
53    first: bool,
54}
55
56impl<'a> Operation<'a> {
57    #[inline]
58    pub(crate) fn start(buf: &'a mut Buf, op: &'a str) -> Self {
59        Self { buf, op, first: true }
60    }
61
62    /// Write a primitive operand.
63    #[inline]
64    pub fn operand<T: Primitive>(&mut self, value: T) -> &mut Self {
65        self.obj().primitive(value);
66        self
67    }
68
69    /// Write a sequence of primitive operands.
70    #[inline]
71    pub fn operands<T, I>(&mut self, values: I) -> &mut Self
72    where
73        T: Primitive,
74        I: IntoIterator<Item = T>,
75    {
76        for value in values {
77            self.operand(value);
78        }
79        self
80    }
81
82    /// Start writing an an arbitrary object operand.
83    #[inline]
84    pub fn obj(&mut self) -> Obj<'_> {
85        if !self.first {
86            self.buf.push(b' ');
87        }
88        self.first = false;
89        Obj::direct(self.buf, 0)
90    }
91}
92
93impl Drop for Operation<'_> {
94    #[inline]
95    fn drop(&mut self) {
96        if !self.first {
97            self.buf.push(b' ');
98        }
99        self.buf.extend(self.op.as_bytes());
100        self.buf.push(b'\n');
101    }
102}
103
104/// General graphics state.
105impl Content {
106    /// `w`: Set the stroke line width.
107    ///
108    /// Panics if `width` is negative.
109    #[inline]
110    pub fn set_line_width(&mut self, width: f32) -> &mut Self {
111        assert!(width >= 0.0, "line width must be positive");
112        self.op("w").operand(width);
113        self
114    }
115
116    /// `J`: Set the line cap style.
117    #[inline]
118    pub fn set_line_cap(&mut self, cap: LineCapStyle) -> &mut Self {
119        self.op("J").operand(cap.to_int());
120        self
121    }
122
123    /// `j`: Set the line join style.
124    #[inline]
125    pub fn set_line_join(&mut self, join: LineJoinStyle) -> &mut Self {
126        self.op("j").operand(join.to_int());
127        self
128    }
129
130    /// `M`: Set the miter limit.
131    #[inline]
132    pub fn set_miter_limit(&mut self, limit: f32) -> &mut Self {
133        self.op("M").operand(limit);
134        self
135    }
136
137    /// `d`: Set the line dash pattern.
138    #[inline]
139    pub fn set_dash_pattern(
140        &mut self,
141        array: impl IntoIterator<Item = f32>,
142        phase: f32,
143    ) -> &mut Self {
144        let mut op = self.op("d");
145        op.obj().array().items(array);
146        op.operand(phase);
147        op.finish();
148        self
149    }
150
151    /// `ri`: Set the color rendering intent to the parameter. PDF 1.1+.
152    #[inline]
153    pub fn set_rendering_intent(&mut self, intent: RenderingIntent) -> &mut Self {
154        self.op("ri").operand(intent.to_name());
155        self
156    }
157
158    /// `i`: Set the flatness tolerance in device pixels.
159    ///
160    /// Panics if `tolerance` is negative or larger than 100.
161    #[inline]
162    pub fn set_flatness(&mut self, tolerance: i32) -> &mut Self {
163        assert!(
164            matches!(tolerance, 0..=100),
165            "flatness tolerance must be between 0 and 100",
166        );
167        self.op("i").operand(tolerance);
168        self
169    }
170
171    /// `gs`: Set the parameters from an `ExtGState` dictionary. PDF 1.2+.
172    #[inline]
173    pub fn set_parameters(&mut self, dict: Name) -> &mut Self {
174        self.op("gs").operand(dict);
175        self
176    }
177}
178
179/// How to terminate lines.
180#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
181pub enum LineCapStyle {
182    /// Square the line of at the endpoints of the path.
183    ButtCap,
184    /// Round the line off at its end with a semicircular arc as wide as the
185    /// stroke.
186    RoundCap,
187    /// End the line with a square cap that protrudes by half the width of the
188    /// stroke.
189    ProjectingSquareCap,
190}
191
192impl LineCapStyle {
193    #[inline]
194    pub(crate) fn to_int(self) -> i32 {
195        match self {
196            Self::ButtCap => 0,
197            Self::RoundCap => 1,
198            Self::ProjectingSquareCap => 2,
199        }
200    }
201}
202
203/// How to join lines at corners.
204#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
205pub enum LineJoinStyle {
206    /// Join the lines with a sharp corner where the outsides of the lines
207    /// intersect.
208    MiterJoin,
209    /// Join the lines with a smooth circular segment.
210    RoundJoin,
211    /// End both lines with butt caps and join them with a triangle.
212    BevelJoin,
213}
214
215impl LineJoinStyle {
216    #[inline]
217    pub(crate) fn to_int(self) -> i32 {
218        match self {
219            Self::MiterJoin => 0,
220            Self::RoundJoin => 1,
221            Self::BevelJoin => 2,
222        }
223    }
224}
225/// How the output device should aim to render colors.
226#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
227pub enum RenderingIntent {
228    /// Only consider the light source, not the output's white point.
229    AbsoluteColorimetric,
230    /// Consider both the light source and the output's white point.
231    #[default]
232    RelativeColorimetric,
233    /// Preserve saturation.
234    Saturation,
235    /// Preserve a pleasing visual appearance.
236    Perceptual,
237}
238
239impl RenderingIntent {
240    #[inline]
241    pub(crate) fn to_name(self) -> Name<'static> {
242        match self {
243            Self::AbsoluteColorimetric => Name(b"AbsoluteColorimetric"),
244            Self::RelativeColorimetric => Name(b"RelativeColorimetric"),
245            Self::Saturation => Name(b"Saturation"),
246            Self::Perceptual => Name(b"Perceptual"),
247        }
248    }
249}
250
251/// Special graphics state.
252impl Content {
253    /// `q`: Save the graphics state on the stack.
254    #[inline]
255    pub fn save_state(&mut self) -> &mut Self {
256        self.op("q");
257        self.q_depth = self.q_depth.saturating_add(1);
258        self
259    }
260
261    /// `Q`: Restore the graphics state from the stack.
262    #[inline]
263    pub fn restore_state(&mut self) -> &mut Self {
264        self.op("Q");
265        self.q_depth = self.q_depth.saturating_sub(1);
266        self
267    }
268
269    /// The current `q` nesting depth.
270    #[inline]
271    pub fn state_nesting_depth(&self) -> usize {
272        self.q_depth
273    }
274
275    /// `cm`: Pre-concatenate the `matrix` with the current transformation
276    /// matrix.
277    #[inline]
278    pub fn transform(&mut self, matrix: [f32; 6]) -> &mut Self {
279        self.op("cm").operands(matrix);
280        self
281    }
282}
283
284/// Path construction.
285impl Content {
286    /// `m`: Begin a new subpath at (x, y).
287    #[inline]
288    pub fn move_to(&mut self, x: f32, y: f32) -> &mut Self {
289        self.op("m").operands([x, y]);
290        self
291    }
292
293    /// `l`: Append a straight line to (x, y).
294    #[inline]
295    pub fn line_to(&mut self, x: f32, y: f32) -> &mut Self {
296        self.op("l").operands([x, y]);
297        self
298    }
299
300    /// `c`: Append a cubic Bézier segment to (x3, y3) with (x1, y1), (x2, y2)
301    /// as control points.
302    #[inline]
303    pub fn cubic_to(
304        &mut self,
305        x1: f32,
306        y1: f32,
307        x2: f32,
308        y2: f32,
309        x3: f32,
310        y3: f32,
311    ) -> &mut Self {
312        self.op("c").operands([x1, y1, x2, y2, x3, y3]);
313        self
314    }
315
316    /// `v`: Append a cubic Bézier segment to (x3, y3) with (x2, y2) as control
317    /// point.
318    #[inline]
319    pub fn cubic_to_initial(&mut self, x2: f32, y2: f32, x3: f32, y3: f32) -> &mut Self {
320        self.op("v").operands([x2, y2, x3, y3]);
321        self
322    }
323
324    /// `y`: Append a cubic Bézier segment to (x3, y3) with (x1, y1) as control
325    /// point.
326    #[inline]
327    pub fn cubic_to_final(&mut self, x1: f32, y1: f32, x3: f32, y3: f32) -> &mut Self {
328        self.op("y").operands([x1, y1, x3, y3]);
329        self
330    }
331
332    /// `h`: Close the current subpath with a straight line.
333    #[inline]
334    pub fn close_path(&mut self) -> &mut Self {
335        self.op("h");
336        self
337    }
338
339    /// `re`: Append a rectangle to the current path.
340    #[inline]
341    pub fn rect(&mut self, x: f32, y: f32, width: f32, height: f32) -> &mut Self {
342        self.op("re").operands([x, y, width, height]);
343        self
344    }
345}
346
347/// Path painting.
348impl Content {
349    /// `S`: Stroke the current path.
350    #[inline]
351    pub fn stroke(&mut self) -> &mut Self {
352        self.op("S");
353        self
354    }
355
356    /// `s`: Close the current path and then stroke it.
357    #[inline]
358    pub fn close_and_stroke(&mut self) -> &mut Self {
359        self.op("s");
360        self
361    }
362
363    /// `f`: Fill the current path using the nonzero winding number rule.
364    #[inline]
365    pub fn fill_nonzero(&mut self) -> &mut Self {
366        self.op("f");
367        self
368    }
369
370    /// `f*`: Fill the current path using the even-odd rule.
371    #[inline]
372    pub fn fill_even_odd(&mut self) -> &mut Self {
373        self.op("f*");
374        self
375    }
376
377    /// `B`: Fill the current path using the nonzero winding number rule and
378    /// then stroke it.
379    #[inline]
380    pub fn fill_nonzero_and_stroke(&mut self) -> &mut Self {
381        self.op("B");
382        self
383    }
384
385    /// `B*`: Fill the current path using the even-odd rule and then stroke it.
386    #[inline]
387    pub fn fill_even_odd_and_stroke(&mut self) -> &mut Self {
388        self.op("B*");
389        self
390    }
391
392    /// `b`: Close the current path, fill it using the nonzero winding number
393    /// rule and then stroke it.
394    #[inline]
395    pub fn close_fill_nonzero_and_stroke(&mut self) -> &mut Self {
396        self.op("b");
397        self
398    }
399
400    /// `b*`: Close the current path, fill it using the even-odd rule and then
401    /// stroke it.
402    #[inline]
403    pub fn close_fill_even_odd_and_stroke(&mut self) -> &mut Self {
404        self.op("b*");
405        self
406    }
407
408    /// `n`: End the current path without filling or stroking it.
409    ///
410    /// This is primarily used for clipping paths.
411    #[inline]
412    pub fn end_path(&mut self) -> &mut Self {
413        self.op("n");
414        self
415    }
416}
417
418/// Clipping paths.
419impl Content {
420    /// `W`: Intersect the current clipping path with the current path using the
421    /// nonzero winding number rule.
422    #[inline]
423    pub fn clip_nonzero(&mut self) -> &mut Self {
424        self.op("W");
425        self
426    }
427
428    /// `W*`: Intersect the current clipping path with the current path using
429    /// the even-odd rule.
430    #[inline]
431    pub fn clip_even_odd(&mut self) -> &mut Self {
432        self.op("W*");
433        self
434    }
435}
436
437/// Text objects.
438impl Content {
439    /// `BT`: Begin a text object.
440    #[inline]
441    pub fn begin_text(&mut self) -> &mut Self {
442        self.op("BT");
443        self
444    }
445
446    /// `ET`: End a text object.
447    #[inline]
448    pub fn end_text(&mut self) -> &mut Self {
449        self.op("ET");
450        self
451    }
452}
453
454/// Text state.
455impl Content {
456    /// `Tc`: Set the character spacing.
457    #[inline]
458    pub fn set_char_spacing(&mut self, spacing: f32) -> &mut Self {
459        self.op("Tc").operand(spacing);
460        self
461    }
462
463    /// `Tw`: Set the word spacing.
464    #[inline]
465    pub fn set_word_spacing(&mut self, spacing: f32) -> &mut Self {
466        self.op("Tw").operand(spacing);
467        self
468    }
469
470    /// `Tz`: Set the horizontal scaling.
471    #[inline]
472    pub fn set_horizontal_scaling(&mut self, scaling: f32) -> &mut Self {
473        self.op("Tz").operand(scaling);
474        self
475    }
476
477    /// `TL`: Set the leading.
478    #[inline]
479    pub fn set_leading(&mut self, leading: f32) -> &mut Self {
480        self.op("TL").operand(leading);
481        self
482    }
483
484    /// `Tf`: Set font and font size.
485    #[inline]
486    pub fn set_font(&mut self, font: Name, size: f32) -> &mut Self {
487        self.op("Tf").operand(font).operand(size);
488        self
489    }
490
491    /// `Tr`: Set the text rendering mode.
492    #[inline]
493    pub fn set_text_rendering_mode(&mut self, mode: TextRenderingMode) -> &mut Self {
494        self.op("Tr").operand(mode.to_int());
495        self
496    }
497
498    /// `Ts`: Set the rise.
499    #[inline]
500    pub fn set_rise(&mut self, rise: f32) -> &mut Self {
501        self.op("Ts").operand(rise);
502        self
503    }
504}
505
506/// How to render text.
507#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
508pub enum TextRenderingMode {
509    /// Just fill the text.
510    #[default]
511    Fill,
512    /// Just stroke the text.
513    Stroke,
514    /// First fill and then stroke the text.
515    FillStroke,
516    /// Don't fill and don't stroke the text.
517    Invisible,
518    /// Fill the text, then apply the text outlines to the current clipping
519    /// path.
520    FillClip,
521    /// Stroke the text, then apply the text outlines to the current clipping
522    /// path.
523    StrokeClip,
524    /// First fill, then stroke the text and finally apply the text outlines to
525    /// the current clipping path.
526    FillStrokeClip,
527    /// Apply the text outlines to the current clipping path.
528    Clip,
529}
530
531impl TextRenderingMode {
532    #[inline]
533    pub(crate) fn to_int(self) -> i32 {
534        match self {
535            Self::Fill => 0,
536            Self::Stroke => 1,
537            Self::FillStroke => 2,
538            Self::Invisible => 3,
539            Self::FillClip => 4,
540            Self::StrokeClip => 5,
541            Self::FillStrokeClip => 6,
542            Self::Clip => 7,
543        }
544    }
545}
546
547/// Text positioning.
548impl Content {
549    /// `Td`: Move to the start of the next line.
550    #[inline]
551    pub fn next_line(&mut self, x: f32, y: f32) -> &mut Self {
552        self.op("Td").operands([x, y]);
553        self
554    }
555
556    /// `TD`: Move to the start of the next line and set the text state's
557    /// leading parameter to `-y`.
558    #[inline]
559    pub fn next_line_and_set_leading(&mut self, x: f32, y: f32) -> &mut Self {
560        self.op("TD").operands([x, y]);
561        self
562    }
563
564    /// `Tm`: Set the text matrix.
565    #[inline]
566    pub fn set_text_matrix(&mut self, matrix: [f32; 6]) -> &mut Self {
567        self.op("Tm").operands(matrix);
568        self
569    }
570
571    /// `T*`: Move to the start of the next line, determining the vertical offset
572    /// through the text state's leading parameter.
573    #[inline]
574    pub fn next_line_using_leading(&mut self) -> &mut Self {
575        self.op("T*");
576        self
577    }
578}
579
580/// Text showing.
581impl Content {
582    /// `Tj`: Show text.
583    ///
584    /// The encoding of the text depends on the font.
585    #[inline]
586    pub fn show(&mut self, text: Str) -> &mut Self {
587        self.op("Tj").operand(text);
588        self
589    }
590
591    /// `'`: Move to the next line and show text.
592    #[inline]
593    pub fn next_line_show(&mut self, text: Str) -> &mut Self {
594        self.op("'").operand(text);
595        self
596    }
597
598    /// `"`: Move to the next line, show text and set the text state's word and
599    /// character spacing.
600    #[inline]
601    pub fn next_line_show_and_set_word_and_char_spacing(
602        &mut self,
603        word_spacing: f32,
604        char_spacing: f32,
605        text: Str,
606    ) -> &mut Self {
607        self.op("\"").operands([word_spacing, char_spacing]).operand(text);
608        self
609    }
610
611    /// `TJ`: Start showing text with individual glyph positioning.
612    #[inline]
613    pub fn show_positioned(&mut self) -> ShowPositioned<'_> {
614        ShowPositioned::start(self.op("TJ"))
615    }
616}
617
618/// Writer for an _individual glyph positioning operation_.
619///
620/// This struct is created by [`Content::show_positioned`].
621pub struct ShowPositioned<'a> {
622    op: Operation<'a>,
623}
624
625impl<'a> ShowPositioned<'a> {
626    #[inline]
627    pub(crate) fn start(op: Operation<'a>) -> Self {
628        Self { op }
629    }
630
631    /// Start writing the array of strings and adjustments. Required.
632    #[inline]
633    pub fn items(&mut self) -> PositionedItems<'_> {
634        PositionedItems::start(self.op.obj())
635    }
636}
637
638deref!('a, ShowPositioned<'a> => Operation<'a>, op);
639
640/// Writer for a _positioned items array_.
641///
642/// This struct is created by [`ShowPositioned::items`].
643pub struct PositionedItems<'a> {
644    array: Array<'a>,
645}
646
647impl<'a> PositionedItems<'a> {
648    #[inline]
649    pub(crate) fn start(obj: Obj<'a>) -> Self {
650        Self { array: obj.array() }
651    }
652
653    /// Show a continuous string without adjustments.
654    ///
655    /// The encoding of the text depends on the font.
656    #[inline]
657    pub fn show(&mut self, text: Str) -> &mut Self {
658        self.array.item(text);
659        self
660    }
661
662    /// Specify an adjustment between two glyphs.
663    ///
664    /// The `amount` is specified in thousands of units of text space and is
665    /// subtracted from the current writing-mode dependent coordinate.
666    #[inline]
667    pub fn adjust(&mut self, amount: f32) -> &mut Self {
668        self.array.item(amount);
669        self
670    }
671}
672
673deref!('a, PositionedItems<'a> => Array<'a>, array);
674
675/// Type 3 fonts.
676///
677/// These operators are only allowed in
678/// [Type 3 CharProcs](crate::font::Type3Font::char_procs).
679impl Content {
680    /// `d0`: Starts a Type 3 glyph that contains color information.
681    /// - `wx` defines the glyph's width
682    /// - `wy` is set to 0.0 automatically
683    #[inline]
684    pub fn start_color_glyph(&mut self, wx: f32) -> &mut Self {
685        self.op("d0").operands([wx, 0.0]);
686        self
687    }
688
689    /// `d1`: Starts a Type 3 glyph that contains only shape information.
690    /// - `wx` defines the glyph's width
691    /// - `wy` is set to 0.0 automatically
692    /// - `ll_x` and `ll_y` define the lower-left corner of the glyph bounding box
693    /// - `ur_x` and `ur_y` define the upper-right corner of the glyph bounding box
694    #[inline]
695    pub fn start_shape_glyph(
696        &mut self,
697        wx: f32,
698        ll_x: f32,
699        ll_y: f32,
700        ur_x: f32,
701        ur_y: f32,
702    ) -> &mut Self {
703        self.op("d1").operands([wx, 0.0, ll_x, ll_y, ur_x, ur_y]);
704        self
705    }
706}
707
708/// Color.
709impl Content {
710    /// `CS`: Set the stroke color space to the parameter. PDF 1.1+.
711    ///
712    /// The parameter must be the name of a parameter-less color space or of a
713    /// color space dictionary within the current resource dictionary.
714    #[inline]
715    pub fn set_stroke_color_space<'a>(
716        &mut self,
717        space: impl Into<ColorSpaceOperand<'a>>,
718    ) -> &mut Self {
719        self.op("CS").operand(space.into().to_name());
720        self
721    }
722
723    /// `cs`: Set the fill color space to the parameter. PDF 1.1+.
724    ///
725    /// The parameter must be the name of a parameter-less color space or of a
726    /// color space dictionary within the current resource dictionary.
727    #[inline]
728    pub fn set_fill_color_space<'a>(
729        &mut self,
730        space: impl Into<ColorSpaceOperand<'a>>,
731    ) -> &mut Self {
732        self.op("cs").operand(space.into().to_name());
733        self
734    }
735
736    /// `SCN`: Set the stroke color to the parameter within the current color
737    /// space. PDF 1.2+.
738    #[inline]
739    pub fn set_stroke_color(
740        &mut self,
741        color: impl IntoIterator<Item = f32>,
742    ) -> &mut Self {
743        self.op("SCN").operands(color);
744        self
745    }
746
747    /// `SCN`: Set the stroke pattern. PDF 1.2+.
748    ///
749    /// The `name` parameter is the name of a pattern. If this is an uncolored
750    /// pattern, a tint color in the current `Pattern` base color space must be
751    /// given, otherwise, the `color` iterator shall remain empty.
752    #[inline]
753    pub fn set_stroke_pattern(
754        &mut self,
755        tint: impl IntoIterator<Item = f32>,
756        name: Name,
757    ) -> &mut Self {
758        self.op("SCN").operands(tint).operand(name);
759        self
760    }
761
762    /// `scn`: Set the fill color to the parameter within the current color
763    /// space. PDF 1.2+.
764    #[inline]
765    pub fn set_fill_color(&mut self, color: impl IntoIterator<Item = f32>) -> &mut Self {
766        self.op("scn").operands(color);
767        self
768    }
769
770    /// `scn`: Set the fill pattern. PDF 1.2+.
771    ///
772    /// The `name` parameter is the name of a pattern. If this is an uncolored
773    /// pattern, a tint color in the current `Pattern` base color space must be
774    /// given, otherwise, the `color` iterator shall remain empty.
775    #[inline]
776    pub fn set_fill_pattern(
777        &mut self,
778        tint: impl IntoIterator<Item = f32>,
779        name: Name,
780    ) -> &mut Self {
781        self.op("scn").operands(tint).operand(name);
782        self
783    }
784
785    /// `G`: Set the stroke color to the parameter and the color space to
786    /// `DeviceGray`.
787    #[inline]
788    pub fn set_stroke_gray(&mut self, gray: f32) -> &mut Self {
789        self.op("G").operand(gray);
790        self
791    }
792
793    /// `g`: Set the fill color to the parameter and the color space to
794    /// `DeviceGray`.
795    #[inline]
796    pub fn set_fill_gray(&mut self, gray: f32) -> &mut Self {
797        self.op("g").operand(gray);
798        self
799    }
800
801    /// `RG`: Set the stroke color to the parameter and the color space to
802    /// `DeviceRGB`.
803    #[inline]
804    pub fn set_stroke_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
805        self.op("RG").operands([r, g, b]);
806        self
807    }
808
809    /// `rg`: Set the fill color to the parameter and the color space to
810    /// `DeviceRGB`.
811    #[inline]
812    pub fn set_fill_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
813        self.op("rg").operands([r, g, b]);
814        self
815    }
816
817    /// `K`: Set the stroke color to the parameter and the color space to
818    /// `DeviceCMYK`.
819    #[inline]
820    pub fn set_stroke_cmyk(&mut self, c: f32, m: f32, y: f32, k: f32) -> &mut Self {
821        self.op("K").operands([c, m, y, k]);
822        self
823    }
824
825    /// `k`: Set the fill color to the parameter and the color space to
826    /// `DeviceCMYK`.
827    #[inline]
828    pub fn set_fill_cmyk(&mut self, c: f32, m: f32, y: f32, k: f32) -> &mut Self {
829        self.op("k").operands([c, m, y, k]);
830        self
831    }
832}
833
834/// A color space operand to the [`CS`](Content::set_stroke_color_space) or
835/// [`cs`](Content::set_fill_color_space) operator.
836#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
837pub enum ColorSpaceOperand<'a> {
838    /// The predefined, parameterless gray device-dependent color space. Needs
839    /// no further entries in the resource dictionary. This is not a normed
840    /// color space, meaning the exact look depends on the device.
841    ///
842    /// Writing `cs` with this is equivalent to writing `g`.
843    DeviceGray,
844    /// The predefined, parameterless RGB device-dependent color space. Needs no
845    /// further entries in the resource dictionary.
846    ///
847    /// Writing `cs` with this is equivalent to writing `rg`.
848    DeviceRgb,
849    /// The predefined, parameterless CMYK device-dependent color space. Needs
850    /// no further entries in the resource dictionary.
851    ///
852    /// Writing `cs` with this is equivalent to writing `k`.
853    DeviceCmyk,
854    /// A pattern with color defined by the pattern itself.
855    ///
856    /// When writing a `cs` operation with this, you must also write an `scn`
857    /// operation with a name pointing to an entry in the current resource
858    /// dictionary's [pattern dictionary](Resources::patterns).
859    Pattern,
860    /// A named color space defined in the current resource dictionary's [color
861    /// space dictionary](Resources::color_spaces).
862    ///
863    /// When this points to a pattern color space, You must also write an `scn`
864    /// operation with a name pointing to an entry in the current resource
865    /// dictionary's [pattern dictionary](Resources::patterns). The color will
866    /// be taken from the `tint` passed to `SCN` and not the pattern itself.
867    Named(Name<'a>),
868}
869
870impl<'a> ColorSpaceOperand<'a> {
871    #[inline]
872    pub(crate) fn to_name(self) -> Name<'a> {
873        match self {
874            Self::DeviceGray => Name(b"DeviceGray"),
875            Self::DeviceRgb => Name(b"DeviceRGB"),
876            Self::DeviceCmyk => Name(b"DeviceCMYK"),
877            Self::Pattern => Name(b"Pattern"),
878            Self::Named(name) => name,
879        }
880    }
881}
882
883impl<'a> From<Name<'a>> for ColorSpaceOperand<'a> {
884    fn from(name: Name<'a>) -> Self {
885        Self::Named(name)
886    }
887}
888
889/// Shading patterns.
890impl Content {
891    /// `sh`: Fill the whole drawing area with the specified shading.
892    #[inline]
893    pub fn shading(&mut self, shading: Name) -> &mut Self {
894        self.op("sh").operand(shading);
895        self
896    }
897}
898
899// TODO: Inline images. Also check clause 6.1.10 of PDF/A-2 spec.
900
901/// XObjects.
902impl Content {
903    /// `Do`: Write an external object.
904    #[inline]
905    pub fn x_object(&mut self, name: Name) -> &mut Self {
906        self.op("Do").operand(name);
907        self
908    }
909}
910
911/// Marked Content.
912impl Content {
913    /// `MP`: Write a marked-content point. PDF 1.2+.
914    #[inline]
915    pub fn marked_content_point(&mut self, tag: Name) -> &mut Self {
916        self.op("MP").operand(tag);
917        self
918    }
919
920    /// `DP`: Start writing a marked-content point operation. PDF 1.2+.
921    #[inline]
922    pub fn marked_content_point_with_properties(&mut self, tag: Name) -> MarkContent<'_> {
923        let mut op = self.op("DP");
924        op.operand(tag);
925        MarkContent::start(op)
926    }
927
928    /// `BMC`: Begin a marked-content sequence. PDF 1.2+.
929    #[inline]
930    pub fn begin_marked_content(&mut self, tag: Name) -> &mut Self {
931        self.op("BMC").operand(tag);
932        self
933    }
934
935    /// `BDC`: Start writing a "begin marked content" operation. PDF 1.2+.
936    #[inline]
937    pub fn begin_marked_content_with_properties(&mut self, tag: Name) -> MarkContent<'_> {
938        let mut op = self.op("BDC");
939        op.operand(tag);
940        MarkContent::start(op)
941    }
942
943    /// `EMC`: End a marked-content sequence. PDF 1.2+.
944    #[inline]
945    pub fn end_marked_content(&mut self) -> &mut Self {
946        self.op("EMC");
947        self
948    }
949}
950
951/// Writer for a _begin marked content operation_. PDF 1.3+.
952pub struct MarkContent<'a> {
953    op: Operation<'a>,
954}
955
956impl<'a> MarkContent<'a> {
957    #[inline]
958    pub(crate) fn start(op: Operation<'a>) -> Self {
959        Self { op }
960    }
961
962    /// Start writing this marked content's property list. Mutually exclusive
963    /// with [`properties_named`](Self::properties_named).
964    #[inline]
965    pub fn properties(&mut self) -> PropertyList<'_> {
966        self.op.obj().start()
967    }
968
969    /// Reference a property list from the Resource dictionary. These property
970    /// lists can be written using the [`Resources::properties`] method.
971    /// Mutually exclusive with [`properties`](Self::properties).
972    #[inline]
973    pub fn properties_named(mut self, name: Name) {
974        self.op.operand(name);
975    }
976}
977
978deref!('a, MarkContent<'a> => Operation<'a>, op);
979
980/// Writer for _property list dictionary_. Can be used as a generic dictionary.
981/// PDF 1.3+.
982pub struct PropertyList<'a> {
983    dict: Dict<'a>,
984}
985
986writer!(PropertyList: |obj| Self { dict: obj.dict() });
987
988impl<'a> PropertyList<'a> {
989    /// Write the `/MCID` marked content identifier.
990    #[inline]
991    pub fn identify(&mut self, identifier: i32) -> &mut Self {
992        self.pair(Name(b"MCID"), identifier);
993        self
994    }
995
996    /// Write the `/ActualText` attribute to indicate the text replacement of
997    /// this marked content sequence. PDF 1.5+.
998    #[inline]
999    pub fn actual_text(&mut self, text: impl TextStrLike) -> &mut Self {
1000        self.pair(Name(b"ActualText"), text);
1001        self
1002    }
1003
1004    /// Start writing artifact property list. The tag of the marked content
1005    /// operation must have been `/Artifact`. PDF 1.4+.
1006    #[inline]
1007    pub fn artifact(self) -> Artifact<'a> {
1008        Artifact::start_with_dict(self.dict)
1009    }
1010}
1011
1012deref!('a, PropertyList<'a> => Dict<'a>, dict);
1013
1014/// Writer for an _actifact property list dictionary_. PDF 1.4+.
1015///
1016/// Required for marking up pagination artifacts in some PDF/A profiles.
1017pub struct Artifact<'a> {
1018    dict: Dict<'a>,
1019}
1020
1021writer!(Artifact: |obj| Self::start_with_dict(obj.dict()));
1022
1023impl<'a> Artifact<'a> {
1024    #[inline]
1025    pub(crate) fn start_with_dict(dict: Dict<'a>) -> Self {
1026        Self { dict }
1027    }
1028
1029    /// Write the `/Type` entry to set the type of artifact. Specific to
1030    /// artifacts. PDF 1.4+.
1031    #[inline]
1032    pub fn kind(&mut self, kind: ArtifactType) -> &mut Self {
1033        self.pair(Name(b"Type"), kind.to_name());
1034        self
1035    }
1036
1037    /// Write the `/Subtype` entry to set the subtype of pagination or inline
1038    /// artifacts. Specific to artifacts. PDF 1.7+.
1039    #[inline]
1040    pub fn subtype(&mut self, subtype: ArtifactSubtype) -> &mut Self {
1041        self.pair(Name(b"Subtype"), subtype.to_name());
1042        self
1043    }
1044
1045    /// Write the `/BBox` entry to set the bounding box of the artifact.
1046    /// Specific to artifacts. Required for background artifacts. PDF 1.4+.
1047    #[inline]
1048    pub fn bounding_box(&mut self, bbox: Rect) -> &mut Self {
1049        self.pair(Name(b"BBox"), bbox);
1050        self
1051    }
1052
1053    /// Write the `/Attached` entry to set where the artifact is attached to the
1054    /// page. Only for pagination and full-page background artifacts. Specific
1055    /// to artifacts. PDF 1.4+.
1056    #[inline]
1057    pub fn attached(
1058        &mut self,
1059        attachment: impl IntoIterator<Item = ArtifactAttachment>,
1060    ) -> &mut Self {
1061        self.insert(Name(b"Attached"))
1062            .array()
1063            .typed()
1064            .items(attachment.into_iter().map(ArtifactAttachment::to_name));
1065        self
1066    }
1067}
1068
1069deref!('a, Artifact<'a> => Dict<'a>, dict);
1070
1071/// The various types of layout artifacts.
1072#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1073pub enum ArtifactType {
1074    /// Artifacts of the pagination process like headers, footers, page numbers.
1075    Pagination,
1076    /// Artifacts of the layout process such as footnote rules.
1077    Layout,
1078    /// Artifacts of the page, like printer's marks.
1079    Page,
1080    /// Background image artifacts. PDF 1.7+.
1081    Background,
1082    /// Artefacts related to inline content, such as line numbers or redactions. PDF 2.0+.
1083    Inline,
1084}
1085
1086impl ArtifactType {
1087    #[inline]
1088    pub(crate) fn to_name(self) -> Name<'static> {
1089        match self {
1090            Self::Pagination => Name(b"Pagination"),
1091            Self::Layout => Name(b"Layout"),
1092            Self::Page => Name(b"Page"),
1093            Self::Background => Name(b"Background"),
1094            Self::Inline => Name(b"Inline"),
1095        }
1096    }
1097}
1098
1099/// The various subtypes of pagination artifacts.
1100#[derive(Debug, Clone, PartialEq)]
1101pub enum ArtifactSubtype<'a> {
1102    /// Headers.
1103    Header,
1104    /// Footers.
1105    Footer,
1106    /// Background watermarks.
1107    Watermark,
1108    /// Page numbers. PDF 2.0+
1109    PageNumber,
1110    /// Bates numbering. PDF 2.0+
1111    Bates,
1112    /// Line numbers. PDF 2.0+
1113    LineNumber,
1114    /// Redactions. PDF 2.0+
1115    Redaction,
1116    /// Custom subtype named according to ISO 32000-1:2008 Annex E.
1117    Custom(Name<'a>),
1118}
1119
1120impl<'a> ArtifactSubtype<'a> {
1121    #[inline]
1122    pub(crate) fn to_name(self) -> Name<'a> {
1123        match self {
1124            Self::Header => Name(b"Header"),
1125            Self::Footer => Name(b"Footer"),
1126            Self::Watermark => Name(b"Watermark"),
1127            Self::PageNumber => Name(b"PageNum"),
1128            Self::Bates => Name(b"Bates"),
1129            Self::LineNumber => Name(b"LineNum"),
1130            Self::Redaction => Name(b"Redaction"),
1131            Self::Custom(name) => name,
1132        }
1133    }
1134}
1135
1136/// Where a layout [`Artifact`] is attached to the page edge.
1137#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1138#[allow(missing_docs)]
1139pub enum ArtifactAttachment {
1140    Left,
1141    Top,
1142    Right,
1143    Bottom,
1144}
1145
1146impl ArtifactAttachment {
1147    #[inline]
1148    pub(crate) fn to_name(self) -> Name<'static> {
1149        match self {
1150            Self::Left => Name(b"Left"),
1151            Self::Top => Name(b"Top"),
1152            Self::Right => Name(b"Right"),
1153            Self::Bottom => Name(b"Bottom"),
1154        }
1155    }
1156}
1157
1158/// Compatibility.
1159impl Content {
1160    /// `BX`: Begin a compatibility section.
1161    #[inline]
1162    pub fn begin_compat(&mut self) -> &mut Self {
1163        self.op("BX");
1164        self
1165    }
1166
1167    /// `EX`: End a compatibility section.
1168    #[inline]
1169    pub fn end_compat(&mut self) -> &mut Self {
1170        self.op("EX");
1171        self
1172    }
1173}
1174
1175/// Writer for a _resource dictionary_.
1176///
1177/// This struct is created by [`Pages::resources`], [`Page::resources`],
1178/// [`FormXObject::resources`], and [`TilingPattern::resources`].
1179pub struct Resources<'a> {
1180    dict: Dict<'a>,
1181}
1182
1183writer!(Resources: |obj| Self { dict: obj.dict() });
1184
1185impl Resources<'_> {
1186    /// Start writing the `/XObject` dictionary.
1187    ///
1188    /// Relevant types:
1189    /// - [`ImageXObject`]
1190    /// - [`FormXObject`]
1191    pub fn x_objects(&mut self) -> Dict<'_> {
1192        self.insert(Name(b"XObject")).dict()
1193    }
1194
1195    /// Start writing the `/Font` dictionary.
1196    ///
1197    /// Relevant types:
1198    /// - [`Type1Font`]
1199    /// - [`Type3Font`]
1200    /// - [`Type0Font`]
1201    pub fn fonts(&mut self) -> Dict<'_> {
1202        self.insert(Name(b"Font")).dict()
1203    }
1204
1205    /// Start writing the `/ColorSpace` dictionary. PDF 1.1+.
1206    ///
1207    /// Relevant types:
1208    /// - [`ColorSpace`]
1209    pub fn color_spaces(&mut self) -> Dict<'_> {
1210        self.insert(Name(b"ColorSpace")).dict()
1211    }
1212
1213    /// Start writing the `/Pattern` dictionary. PDF 1.2+.
1214    ///
1215    /// Relevant types:
1216    /// - [`TilingPattern`]
1217    /// - [`ShadingPattern`]
1218    pub fn patterns(&mut self) -> Dict<'_> {
1219        self.insert(Name(b"Pattern")).dict()
1220    }
1221
1222    /// Start writing the `/Shading` dictionary. PDF 1.3+.
1223    ///
1224    /// Relevant types:
1225    /// - [`FunctionShading`]
1226    pub fn shadings(&mut self) -> Dict<'_> {
1227        self.insert(Name(b"Shading")).dict()
1228    }
1229
1230    /// Start writing the `/ExtGState` dictionary. PDF 1.2+.
1231    ///
1232    /// Relevant types:
1233    /// - [`ExtGraphicsState`]
1234    pub fn ext_g_states(&mut self) -> Dict<'_> {
1235        self.insert(Name(b"ExtGState")).dict()
1236    }
1237
1238    /// Write the `/ProcSet` attribute.
1239    ///
1240    /// This defines what procedure sets are sent to an output device when
1241    /// printing the file as PostScript. The attribute is only used for PDFs
1242    /// with versions below 1.4.
1243    pub fn proc_sets(&mut self, sets: impl IntoIterator<Item = ProcSet>) -> &mut Self {
1244        self.insert(Name(b"ProcSet"))
1245            .array()
1246            .items(sets.into_iter().map(ProcSet::to_name));
1247        self
1248    }
1249
1250    /// Write the `/ProcSet` attribute with all available procedure sets.
1251    ///
1252    /// The PDF 1.7 specification recommends that modern PDFs either omit the
1253    /// attribute or specify all available procedure sets, as this function
1254    /// does.
1255    pub fn proc_sets_all(&mut self) -> &mut Self {
1256        self.proc_sets([
1257            ProcSet::Pdf,
1258            ProcSet::Text,
1259            ProcSet::ImageGrayscale,
1260            ProcSet::ImageColor,
1261            ProcSet::ImageIndexed,
1262        ])
1263    }
1264
1265    /// Start writing the `/Properties` attribute.
1266    ///
1267    /// This allows to write property lists with indirect objects for
1268    /// marked-content sequences. These properties can be used by property lists
1269    /// using the [`MarkContent::properties_named`] method. PDF 1.2+.
1270    pub fn properties(&mut self) -> TypedDict<'_, PropertyList<'_>> {
1271        self.insert(Name(b"Properties")).dict().typed()
1272    }
1273}
1274
1275deref!('a, Resources<'a> => Dict<'a>, dict);
1276
1277/// What procedure sets to send to a PostScript printer or other output device.
1278///
1279/// This enumeration provides compatibility for printing PDFs of versions 1.3 and
1280/// below.
1281#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1282pub enum ProcSet {
1283    /// Painting and graphics state.
1284    Pdf,
1285    /// Text.
1286    Text,
1287    /// Grayscale images and masks.
1288    ImageGrayscale,
1289    /// Color images.
1290    ImageColor,
1291    /// Images with color tables.
1292    ImageIndexed,
1293}
1294
1295impl ProcSet {
1296    pub(crate) fn to_name(self) -> Name<'static> {
1297        match self {
1298            ProcSet::Pdf => Name(b"PDF"),
1299            ProcSet::Text => Name(b"Text"),
1300            ProcSet::ImageGrayscale => Name(b"ImageB"),
1301            ProcSet::ImageColor => Name(b"ImageC"),
1302            ProcSet::ImageIndexed => Name(b"ImageI"),
1303        }
1304    }
1305}
1306
1307/// Writer for a _dictionary with additional parameters for the graphics state._
1308///
1309/// This struct is created by [`Chunk::ext_graphics`] and
1310/// [`ShadingPattern::ext_graphics`].
1311pub struct ExtGraphicsState<'a> {
1312    dict: Dict<'a>,
1313}
1314
1315writer!(ExtGraphicsState: |obj| {
1316    let mut dict = obj.dict();
1317    dict.pair(Name(b"Type"), Name(b"ExtGState"));
1318    Self { dict }
1319});
1320
1321impl ExtGraphicsState<'_> {
1322    /// Write the `LW` attribute to set the line width. PDF 1.3+.
1323    pub fn line_width(&mut self, width: f32) -> &mut Self {
1324        self.pair(Name(b"LW"), width);
1325        self
1326    }
1327
1328    /// Write the `LC` attribute to set the line cap style. PDF 1.3+.
1329    pub fn line_cap(&mut self, cap: LineCapStyle) -> &mut Self {
1330        self.pair(Name(b"LC"), cap.to_int());
1331        self
1332    }
1333
1334    /// Write the `LJ` attribute to set the line join style. PDF 1.3+.
1335    pub fn line_join(&mut self, join: LineJoinStyle) -> &mut Self {
1336        self.pair(Name(b"LJ"), join.to_int());
1337        self
1338    }
1339
1340    /// Write the `ML` attribute to set the miter limit. PDF 1.3+.
1341    pub fn miter_limit(&mut self, limit: f32) -> &mut Self {
1342        self.pair(Name(b"ML"), limit);
1343        self
1344    }
1345
1346    /// Write the `D` attribute to set the dash pattern. PDF 1.3+.
1347    pub fn dash_pattern(
1348        &mut self,
1349        pattern: impl IntoIterator<Item = f32>,
1350        phase: f32,
1351    ) -> &mut Self {
1352        let mut array = self.insert(Name(b"D")).array();
1353        array.push().array().items(pattern);
1354        array.item(phase);
1355        array.finish();
1356        self
1357    }
1358
1359    /// Write the `RI` attribute to set the rendering intent. PDF 1.3+.
1360    pub fn rendering_intent(&mut self, intent: RenderingIntent) -> &mut Self {
1361        self.pair(Name(b"RI"), intent.to_name());
1362        self
1363    }
1364
1365    /// Write the `OP` attribute to set the overprint mode for all operations,
1366    /// except if an `op` entry is present. If so, only influence the stroking
1367    /// operations. PDF 1.2+.
1368    pub fn overprint(&mut self, overprint: bool) -> &mut Self {
1369        self.pair(Name(b"OP"), overprint);
1370        self
1371    }
1372
1373    /// Write the `op` attribute to set the overprint mode for fill operations.
1374    /// PDF 1.3+.
1375    pub fn overprint_fill(&mut self, overprint: bool) -> &mut Self {
1376        self.pair(Name(b"op"), overprint);
1377        self
1378    }
1379
1380    /// Write the `OPM` attribute to set the overprint mode for components that
1381    /// have been zeroed out. PDF 1.3+.
1382    ///
1383    /// Note that this attribute is restricted by PDF/A.
1384    pub fn overprint_mode(&mut self, mode: OverprintMode) -> &mut Self {
1385        self.pair(Name(b"OPM"), mode.to_int());
1386        self
1387    }
1388
1389    /// Write the `Font` attribute to set the font. PDF 1.3+.
1390    pub fn font(&mut self, font: Name, size: f32) -> &mut Self {
1391        let mut array = self.insert(Name(b"Font")).array();
1392        array.item(font);
1393        array.item(size);
1394        array.finish();
1395        self
1396    }
1397
1398    /// Write the `BG` attribute to set the black generation function.
1399    pub fn black_generation(&mut self, func: Ref) -> &mut Self {
1400        self.pair(Name(b"BG"), func);
1401        self
1402    }
1403
1404    /// Write the `BG2` attribute to set the black-generation function back to
1405    /// the function that has been in effect at the beginning of the page. PDF
1406    /// 1.3+.
1407    pub fn black_generation_default(&mut self) -> &mut Self {
1408        self.pair(Name(b"BG2"), Name(b"Default"));
1409        self
1410    }
1411
1412    /// Write the `UCR` attribute to set the undercolor removal function.
1413    pub fn undercolor_removal(&mut self, func: Ref) -> &mut Self {
1414        self.pair(Name(b"UCR"), func);
1415        self
1416    }
1417
1418    /// Write the `UCR2` attribute to set the undercolor removal function back
1419    /// to the function that has been in effect at the beginning of the page.
1420    /// PDF 1.3+.
1421    pub fn undercolor_removal_default(&mut self) -> &mut Self {
1422        self.pair(Name(b"UCR2"), Name(b"Default"));
1423        self
1424    }
1425
1426    /// Write the `TR` attribute to set the transfer function.
1427    ///
1428    /// Note that this key is illegal in PDF/A.
1429    pub fn transfer(&mut self, func: Ref) -> &mut Self {
1430        self.pair(Name(b"TR"), func);
1431        self
1432    }
1433
1434    /// Write the `TR2` attribute to set the transfer function back to the
1435    /// function that has been in effect at the beginning of the page. PDF 1.3+.
1436    pub fn transfer_default(&mut self) -> &mut Self {
1437        self.pair(Name(b"TR2"), Name(b"Default"));
1438        self
1439    }
1440
1441    /// Write the `HT` attribute to set the halftone.
1442    ///
1443    /// Note that this value may be ignored in PDF/A.
1444    pub fn halftone(&mut self, ht: Ref) -> &mut Self {
1445        self.pair(Name(b"HT"), ht);
1446        self
1447    }
1448
1449    /// Write the `HT` attribute to set the halftone back to the one that has
1450    /// been in effect at the beginning of the page.
1451    pub fn halftone_default(&mut self) -> &mut Self {
1452        self.pair(Name(b"HT"), Name(b"Default"));
1453        self
1454    }
1455
1456    /// Write the `FL` attribute to set the flatness tolerance. PDF 1.3+.
1457    ///
1458    /// Note that this key may be ignored in PDF/A.
1459    pub fn flatness(&mut self, tolerance: f32) -> &mut Self {
1460        self.pair(Name(b"FL"), tolerance);
1461        self
1462    }
1463
1464    /// Write the `SM` attribute to set the smoothness tolerance. PDF 1.3+.
1465    pub fn smoothness(&mut self, tolerance: f32) -> &mut Self {
1466        self.pair(Name(b"SM"), tolerance);
1467        self
1468    }
1469
1470    /// Write the `SA` attribute to set automatic stroke adjustment.
1471    pub fn stroke_adjustment(&mut self, adjust: bool) -> &mut Self {
1472        self.pair(Name(b"SA"), adjust);
1473        self
1474    }
1475
1476    /// Write the `BM` attribute to set the blend mode. PDF 1.4+.
1477    ///
1478    /// Note that this key is restricted in PDF/A-1.
1479    pub fn blend_mode(&mut self, mode: BlendMode) -> &mut Self {
1480        self.pair(Name(b"BM"), mode.to_name());
1481        self
1482    }
1483
1484    /// Start writing the `SMask` attribute. PDF 1.4+.
1485    ///
1486    /// Note that this key is forbidden in PDF/A-1.
1487    pub fn soft_mask(&mut self) -> SoftMask<'_> {
1488        self.insert(Name(b"SMask")).start()
1489    }
1490
1491    /// Write the `SMask` attribute using a name. PDF 1.4+.
1492    ///
1493    /// Note that this key is forbidden in PDF/A-1.
1494    pub fn soft_mask_name(&mut self, mask: Name) -> &mut Self {
1495        self.pair(Name(b"SMask"), mask);
1496        self
1497    }
1498
1499    /// Write the `CA` attribute to set the stroking alpha constant. PDF 1.4+.
1500    ///
1501    /// Note that this key is restricted in PDF/A-1.
1502    pub fn stroking_alpha(&mut self, alpha: f32) -> &mut Self {
1503        self.pair(Name(b"CA"), alpha);
1504        self
1505    }
1506
1507    /// Write the `ca` attribute to set the non-stroking alpha constant. PDF
1508    /// 1.4+.
1509    ///
1510    /// Note that this key is restricted in PDF/A-1.
1511    pub fn non_stroking_alpha(&mut self, alpha: f32) -> &mut Self {
1512        self.pair(Name(b"ca"), alpha);
1513        self
1514    }
1515
1516    /// Write the `AIS` attribute to set the alpha source flag. `CA` and `ca`
1517    /// values as well as the `SMask` will be interpreted as shape instead of
1518    /// opacity. PDF 1.4+.
1519    pub fn alpha_source(&mut self, source: bool) -> &mut Self {
1520        self.pair(Name(b"AIS"), source);
1521        self
1522    }
1523
1524    /// Write the `TK` attribute to set the text knockout flag. PDF 1.4+.
1525    pub fn text_knockout(&mut self, knockout: bool) -> &mut Self {
1526        self.pair(Name(b"TK"), knockout);
1527        self
1528    }
1529}
1530
1531deref!('a, ExtGraphicsState<'a> => Dict<'a>, dict);
1532
1533/// How to blend source and backdrop.
1534#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
1535#[allow(missing_docs)]
1536pub enum BlendMode {
1537    #[default]
1538    Normal,
1539    Multiply,
1540    Screen,
1541    Overlay,
1542    Darken,
1543    Lighten,
1544    ColorDodge,
1545    ColorBurn,
1546    HardLight,
1547    SoftLight,
1548    Difference,
1549    Exclusion,
1550    Hue,
1551    Saturation,
1552    Color,
1553    Luminosity,
1554}
1555
1556impl BlendMode {
1557    pub(crate) fn to_name(self) -> Name<'static> {
1558        match self {
1559            BlendMode::Normal => Name(b"Normal"),
1560            BlendMode::Multiply => Name(b"Multiply"),
1561            BlendMode::Screen => Name(b"Screen"),
1562            BlendMode::Overlay => Name(b"Overlay"),
1563            BlendMode::Darken => Name(b"Darken"),
1564            BlendMode::Lighten => Name(b"Lighten"),
1565            BlendMode::ColorDodge => Name(b"ColorDodge"),
1566            BlendMode::ColorBurn => Name(b"ColorBurn"),
1567            BlendMode::HardLight => Name(b"HardLight"),
1568            BlendMode::SoftLight => Name(b"SoftLight"),
1569            BlendMode::Difference => Name(b"Difference"),
1570            BlendMode::Exclusion => Name(b"Exclusion"),
1571            BlendMode::Hue => Name(b"Hue"),
1572            BlendMode::Saturation => Name(b"Saturation"),
1573            BlendMode::Color => Name(b"Color"),
1574            BlendMode::Luminosity => Name(b"Luminosity"),
1575        }
1576    }
1577}
1578
1579/// How to behave when overprinting for colorants with the value zero.
1580#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
1581pub enum OverprintMode {
1582    /// An overprint operation will always discard the underlying color, even if
1583    /// one of the colorants is zero.
1584    #[default]
1585    OverrideAllColorants,
1586    /// An overprint operation will only discard the underlying colorant
1587    /// component (e.g. cyan in CMYK) if the new corresponding colorant is
1588    /// non-zero.
1589    ///
1590    /// Note that this value is forbidden by PDF/A for ICCBased color spaces
1591    /// when overprinting is enabled.
1592    IgnoreZeroChannel,
1593}
1594
1595impl OverprintMode {
1596    pub(crate) fn to_int(self) -> i32 {
1597        match self {
1598            OverprintMode::OverrideAllColorants => 0,
1599            OverprintMode::IgnoreZeroChannel => 1,
1600        }
1601    }
1602}
1603
1604/// Writer for a _soft mask dictionary_.
1605///
1606/// This struct is created by [`ExtGraphicsState::soft_mask`].
1607pub struct SoftMask<'a> {
1608    dict: Dict<'a>,
1609}
1610
1611writer!(SoftMask: |obj| {
1612    let mut dict = obj.dict();
1613    dict.pair(Name(b"Type"), Name(b"Mask"));
1614    Self { dict }
1615});
1616
1617impl SoftMask<'_> {
1618    /// Write the `S` attribute to set the soft mask subtype. Required.
1619    pub fn subtype(&mut self, subtype: MaskType) -> &mut Self {
1620        self.pair(Name(b"S"), subtype.to_name());
1621        self
1622    }
1623
1624    /// Write the `G` attribute to set the transparency group XObject. The group
1625    /// has to have a color space set in the `/CS` attribute if the mask subtype
1626    /// is `Luminosity`. Required.
1627    pub fn group(&mut self, group: Ref) -> &mut Self {
1628        self.pair(Name(b"G"), group);
1629        self
1630    }
1631
1632    /// Write the `BC` attribute to set the background color for the
1633    /// transparency group. Only applicable if the mask subtype is `Luminosity`.
1634    /// Has to be set in the group's color space.
1635    pub fn backdrop(&mut self, color: impl IntoIterator<Item = f32>) -> &mut Self {
1636        self.insert(Name(b"BC")).array().items(color);
1637        self
1638    }
1639
1640    /// Write the `TR` attribute, a function that maps from the group's output
1641    /// values to the mask opacity.
1642    pub fn transfer_function(&mut self, function: Ref) -> &mut Self {
1643        self.pair(Name(b"TR"), function);
1644        self
1645    }
1646}
1647
1648deref!('a, SoftMask<'a> => Dict<'a>, dict);
1649
1650/// What property in the mask influences the target alpha.
1651#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1652pub enum MaskType {
1653    /// The alpha values from the mask are applied to the target.
1654    Alpha,
1655    /// A single-channel luminosity value is calculated for the colors in the
1656    /// mask.
1657    Luminosity,
1658}
1659
1660impl MaskType {
1661    pub(crate) fn to_name(self) -> Name<'static> {
1662        match self {
1663            MaskType::Alpha => Name(b"Alpha"),
1664            MaskType::Luminosity => Name(b"Luminosity"),
1665        }
1666    }
1667}
1668
1669#[cfg(test)]
1670mod tests {
1671    use super::*;
1672
1673    #[test]
1674    fn test_content_encoding() {
1675        let mut content = Content::new();
1676        content
1677            .save_state()
1678            .rect(1.0, 2.0, 3.0, 4.0)
1679            .fill_nonzero()
1680            .set_dash_pattern([7.0, 2.0], 4.0)
1681            .x_object(Name(b"MyImage"))
1682            .set_fill_pattern([2.0, 3.5], Name(b"MyPattern"))
1683            .restore_state();
1684
1685        assert_eq!(
1686            content.finish().into_vec(),
1687            b"q\n1 2 3 4 re\nf\n[7 2] 4 d\n/MyImage Do\n2 3.5 /MyPattern scn\nQ"
1688        );
1689    }
1690
1691    #[test]
1692    fn test_content_text() {
1693        let mut content = Content::new();
1694
1695        content.set_font(Name(b"F1"), 12.0);
1696        content.begin_text();
1697        content.show_positioned().items();
1698        content
1699            .show_positioned()
1700            .items()
1701            .show(Str(b"AB"))
1702            .adjust(2.0)
1703            .show(Str(b"CD"));
1704        content.end_text();
1705
1706        assert_eq!(
1707            content.finish().into_vec(),
1708            b"/F1 12 Tf\nBT\n[] TJ\n[(AB) 2 (CD)] TJ\nET"
1709        );
1710    }
1711}