Skip to main content

pdf_writer/
content.rs

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