Skip to main content

pdfrum_edit/canvas/
blend.rs

1//! Blend modes (ISO 32000-1 ยง11.3.5): how what is drawn next combines with
2//! what is already on the page.
3
4use pdfrum_object::{Dict, Name, Object, names as pdf_names};
5
6use super::Canvas;
7
8/// A separable or non-separable blend mode, ISO 32000-1 tables 136-137.
9///
10/// An enum rather than the `/BM` name so a misspelt mode cannot compile; the
11/// set is PDF's own and closed.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
13pub enum BlendMode {
14    /// The source replaces the backdrop. PDF's default.
15    #[default]
16    Normal,
17    /// `Multiply`.
18    Multiply,
19    /// `Screen`.
20    Screen,
21    /// `Overlay`.
22    Overlay,
23    /// `Darken`.
24    Darken,
25    /// `Lighten`.
26    Lighten,
27    /// `ColorDodge`.
28    ColorDodge,
29    /// `ColorBurn`.
30    ColorBurn,
31    /// `HardLight`.
32    HardLight,
33    /// `SoftLight`.
34    SoftLight,
35    /// `Difference`.
36    Difference,
37    /// `Exclusion`.
38    Exclusion,
39    /// `Hue`.
40    Hue,
41    /// `Saturation`.
42    Saturation,
43    /// `Color`.
44    Color,
45    /// `Luminosity`.
46    Luminosity,
47}
48
49impl BlendMode {
50    /// The `/BM` name.
51    fn name(self) -> &'static str {
52        match self {
53            Self::Normal => "Normal",
54            Self::Multiply => "Multiply",
55            Self::Screen => "Screen",
56            Self::Overlay => "Overlay",
57            Self::Darken => "Darken",
58            Self::Lighten => "Lighten",
59            Self::ColorDodge => "ColorDodge",
60            Self::ColorBurn => "ColorBurn",
61            Self::HardLight => "HardLight",
62            Self::SoftLight => "SoftLight",
63            Self::Difference => "Difference",
64            Self::Exclusion => "Exclusion",
65            Self::Hue => "Hue",
66            Self::Saturation => "Saturation",
67            Self::Color => "Color",
68            Self::Luminosity => "Luminosity",
69        }
70    }
71}
72
73impl Canvas<'_, '_> {
74    /// Blend everything drawn after it by `mode`, as an `/ExtGState` naming
75    /// `/BM`.
76    ///
77    /// Scoped by [`Canvas::saved`], like [`Canvas::opacity`].
78    ///
79    /// ```
80    /// use pdfrum_edit::{BlendMode, EditDoc, Size, blank_document};
81    /// use pdfrum_common::Limits;
82    /// use kurbo::Rect;
83    /// use peniko::Color;
84    ///
85    /// let base = blank_document(&[Size::new(100.0, 100.0)])?;
86    /// let mut edit = EditDoc::new(&base);
87    /// edit.draw_page(0, &Limits::default(), |c| {
88    ///     c.saved(|c| {
89    ///         c.blend(BlendMode::Multiply);
90    ///         c.fill_rect(Rect::new(0.0, 0.0, 50.0, 50.0), Color::from_rgb8(255, 200, 0));
91    ///     });
92    /// })?;
93    /// # Ok::<(), pdfrum_edit::Error>(())
94    /// ```
95    pub fn blend(&mut self, mode: BlendMode) {
96        let state = Dict::from_pairs([(Name::from("BM"), Object::Name(Name::from(mode.name())))]);
97        let name = self.realize(pdf_names::EXT_G_STATE, Object::Dict(state));
98        self.out.push('/');
99        self.push_name(&name);
100        self.out.push_str(" gs\n");
101    }
102}