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}