Skip to main content

pdfium_render/pdf/
color.rs

1//! Defines the [PdfColor] struct, a 32-bit RGB color value with an optional alpha channel.
2
3use crate::bindgen::FPDF_DWORD;
4use crate::error::PdfiumError;
5
6/// A 32-bit RGB color value with an optional alpha channel.
7///
8/// A variety of non-transparent colors are available as const values on this struct.
9///
10/// Note that when used as a form field highlight color, a solid color with no opacity
11/// will overprint any user data in the field. Use the [PdfColor::with_alpha()] function
12/// to apply an alpha channel value to an existing [PdfColor].
13#[derive(Debug, Copy, Clone)]
14pub struct PdfColor {
15    r: u8,
16    g: u8,
17    b: u8,
18    a: u8,
19}
20
21impl PdfColor {
22    pub const WHITE: PdfColor = PdfColor::new(255, 255, 255, 255);
23    pub const BLACK: PdfColor = PdfColor::new(0, 0, 0, 255);
24
25    #[deprecated(
26        since = "0.8.6",
27        note = "The SOLID_ prefix is superfluous. Use PdfColor::WHITE instead."
28    )]
29    pub const SOLID_WHITE: PdfColor = PdfColor::new(255, 255, 255, 255);
30    #[deprecated(
31        since = "0.8.6",
32        note = "The SOLID_ prefix is superfluous. Use PdfColor::RED instead."
33    )]
34    pub const SOLID_RED: PdfColor = PdfColor::new(255, 0, 0, 255);
35    #[deprecated(
36        since = "0.8.6",
37        note = "The SOLID_ prefix is superfluous. Use PdfColor::GREEN instead."
38    )]
39    pub const SOLID_GREEN: PdfColor = PdfColor::new(0, 255, 0, 255);
40    #[deprecated(
41        since = "0.8.6",
42        note = "The SOLID_ prefix is superfluous. Use PdfColor::BLUE instead."
43    )]
44    pub const SOLID_BLUE: PdfColor = PdfColor::new(0, 0, 255, 255);
45    #[deprecated(
46        since = "0.8.6",
47        note = "The SOLID_ prefix is superfluous. Use PdfColor::MAGENTA instead."
48    )]
49    pub const SOLID_MAGENTA: PdfColor = PdfColor::new(255, 0, 255, 255);
50    #[deprecated(
51        since = "0.8.6",
52        note = "The SOLID_ prefix is superfluous. Use PdfColor::CYAN instead."
53    )]
54    pub const SOLID_CYAN: PdfColor = PdfColor::new(0, 255, 255, 255);
55    #[deprecated(
56        since = "0.8.6",
57        note = "The SOLID_ prefix is superfluous. Use PdfColor::YELLOW instead."
58    )]
59    pub const SOLID_YELLOW: PdfColor = PdfColor::new(255, 255, 0, 255);
60    #[deprecated(
61        since = "0.8.6",
62        note = "The SOLID_ prefix is superfluous. Use PdfColor::BLACK instead."
63    )]
64    pub const SOLID_BLACK: PdfColor = PdfColor::new(0, 0, 0, 255);
65
66    pub const GREY_90: PdfColor = PdfColor::new(230, 230, 230, 255);
67    pub const GREY_80: PdfColor = PdfColor::new(204, 204, 204, 255);
68    pub const GREY_70: PdfColor = PdfColor::new(179, 179, 179, 255);
69    pub const GREY_60: PdfColor = PdfColor::new(153, 153, 153, 255);
70    pub const GREY_50: PdfColor = PdfColor::new(128, 128, 128, 255);
71    pub const GREY_40: PdfColor = PdfColor::new(102, 102, 102, 255);
72    pub const GREY_30: PdfColor = PdfColor::new(77, 77, 77, 255);
73    pub const GREY_20: PdfColor = PdfColor::new(51, 51, 51, 255);
74    pub const GREY_10: PdfColor = PdfColor::new(26, 26, 26, 255);
75
76    pub const LIME: PdfColor = PdfColor::new(0, 255, 0, 255);
77    pub const BLUE: PdfColor = PdfColor::new(0, 0, 255, 255);
78    pub const YELLOW: PdfColor = PdfColor::new(255, 255, 0, 255);
79    pub const CYAN: PdfColor = PdfColor::new(0, 255, 255, 255);
80    pub const MAGENTA: PdfColor = PdfColor::new(255, 0, 255, 255);
81    pub const SILVER: PdfColor = PdfColor::new(192, 192, 192, 255);
82    pub const OLIVE: PdfColor = PdfColor::new(128, 128, 0, 255);
83    pub const PURPLE: PdfColor = PdfColor::new(128, 0, 128, 255);
84    pub const TEAL: PdfColor = PdfColor::new(0, 128, 128, 255);
85    pub const NAVY: PdfColor = PdfColor::new(0, 0, 128, 255);
86    pub const MAROON: PdfColor = PdfColor::new(128, 0, 0, 255);
87    pub const DARK_RED: PdfColor = PdfColor::new(139, 0, 0, 255);
88    pub const BROWN: PdfColor = PdfColor::new(165, 42, 42, 255);
89    pub const FIREBRICK: PdfColor = PdfColor::new(178, 34, 34, 255);
90    pub const CRIMSON: PdfColor = PdfColor::new(220, 20, 60, 255);
91    pub const RED: PdfColor = PdfColor::new(255, 0, 0, 255);
92    pub const TOMATO: PdfColor = PdfColor::new(255, 99, 71, 255);
93    pub const CORAL: PdfColor = PdfColor::new(255, 127, 80, 255);
94    pub const INDIAN_RED: PdfColor = PdfColor::new(205, 92, 92, 255);
95    pub const LIGHT_CORAL: PdfColor = PdfColor::new(240, 128, 128, 255);
96    pub const DARK_SALMON: PdfColor = PdfColor::new(233, 150, 122, 255);
97    pub const SALMON: PdfColor = PdfColor::new(250, 128, 114, 255);
98    pub const LIGHT_SALMON: PdfColor = PdfColor::new(255, 160, 122, 255);
99    pub const ORANGE_RED: PdfColor = PdfColor::new(255, 69, 0, 255);
100    pub const DARK_ORANGE: PdfColor = PdfColor::new(255, 140, 0, 255);
101    pub const ORANGE: PdfColor = PdfColor::new(255, 165, 0, 255);
102    pub const GOLD: PdfColor = PdfColor::new(255, 215, 0, 255);
103    pub const DARK_GOLDEN_ROD: PdfColor = PdfColor::new(184, 134, 11, 255);
104    pub const GOLDEN_ROD: PdfColor = PdfColor::new(218, 165, 32, 255);
105    pub const PALE_GOLDEN_ROD: PdfColor = PdfColor::new(238, 232, 170, 255);
106    pub const DARK_KHAKI: PdfColor = PdfColor::new(189, 183, 107, 255);
107    pub const KHAKI: PdfColor = PdfColor::new(240, 230, 140, 255);
108    pub const YELLOW_GREEN: PdfColor = PdfColor::new(154, 205, 50, 255);
109    pub const DARK_OLIVE_GREEN: PdfColor = PdfColor::new(85, 107, 47, 255);
110    pub const OLIVE_DRAB: PdfColor = PdfColor::new(107, 142, 35, 255);
111    pub const LAWN_GREEN: PdfColor = PdfColor::new(124, 252, 0, 255);
112    pub const CHARTREUSE: PdfColor = PdfColor::new(127, 255, 0, 255);
113    pub const GREEN_YELLOW: PdfColor = PdfColor::new(173, 255, 47, 255);
114    pub const DARK_GREEN: PdfColor = PdfColor::new(0, 100, 0, 255);
115    pub const GREEN: PdfColor = PdfColor::new(0, 128, 0, 255);
116    pub const FOREST_GREEN: PdfColor = PdfColor::new(34, 139, 34, 255);
117    pub const LIME_GREEN: PdfColor = PdfColor::new(50, 205, 50, 255);
118    pub const LIGHT_GREEN: PdfColor = PdfColor::new(144, 238, 144, 255);
119    pub const PALE_GREEN: PdfColor = PdfColor::new(152, 251, 152, 255);
120    pub const DARK_SEA_GREEN: PdfColor = PdfColor::new(143, 188, 143, 255);
121    pub const MEDIUM_SPRING_GREEN: PdfColor = PdfColor::new(0, 250, 154, 255);
122    pub const SPRING_GREEN: PdfColor = PdfColor::new(0, 255, 127, 255);
123    pub const SEA_GREEN: PdfColor = PdfColor::new(46, 139, 87, 255);
124    pub const MEDIUM_AQUA_MARINE: PdfColor = PdfColor::new(102, 205, 170, 255);
125    pub const MEDIUM_SEA_GREEN: PdfColor = PdfColor::new(60, 179, 113, 255);
126    pub const LIGHT_SEA_GREEN: PdfColor = PdfColor::new(32, 178, 170, 255);
127    pub const DARK_SLATE_GRAY: PdfColor = PdfColor::new(47, 79, 79, 255);
128    pub const DARK_CYAN: PdfColor = PdfColor::new(0, 139, 139, 255);
129    pub const AQUA: PdfColor = PdfColor::new(0, 255, 255, 255);
130    pub const LIGHT_CYAN: PdfColor = PdfColor::new(224, 255, 255, 255);
131    pub const DARK_TURQUOISE: PdfColor = PdfColor::new(0, 206, 209, 255);
132    pub const TURQUOISE: PdfColor = PdfColor::new(64, 224, 208, 255);
133    pub const MEDIUM_TURQUOISE: PdfColor = PdfColor::new(72, 209, 204, 255);
134    pub const PALE_TURQUOISE: PdfColor = PdfColor::new(175, 238, 238, 255);
135    pub const AQUA_MARINE: PdfColor = PdfColor::new(127, 255, 212, 255);
136    pub const POWDER_BLUE: PdfColor = PdfColor::new(176, 224, 230, 255);
137    pub const CADET_BLUE: PdfColor = PdfColor::new(95, 158, 160, 255);
138    pub const STEEL_BLUE: PdfColor = PdfColor::new(70, 130, 180, 255);
139    pub const CORNFLOWER_BLUE: PdfColor = PdfColor::new(100, 149, 237, 255);
140    pub const DEEP_SKY_BLUE: PdfColor = PdfColor::new(0, 191, 255, 255);
141    pub const DODGER_BLUE: PdfColor = PdfColor::new(30, 144, 255, 255);
142    pub const LIGHT_BLUE: PdfColor = PdfColor::new(173, 216, 230, 255);
143    pub const SKY_BLUE: PdfColor = PdfColor::new(135, 206, 235, 255);
144    pub const LIGHT_SKY_BLUE: PdfColor = PdfColor::new(135, 206, 250, 255);
145    pub const MIDNIGHT_BLUE: PdfColor = PdfColor::new(25, 25, 112, 255);
146    pub const DARK_BLUE: PdfColor = PdfColor::new(0, 0, 139, 255);
147    pub const MEDIUM_BLUE: PdfColor = PdfColor::new(0, 0, 205, 255);
148    pub const ROYAL_BLUE: PdfColor = PdfColor::new(65, 105, 225, 255);
149    pub const BLUE_VIOLET: PdfColor = PdfColor::new(138, 43, 226, 255);
150    pub const INDIGO: PdfColor = PdfColor::new(75, 0, 130, 255);
151    pub const DARK_SLATE_BLUE: PdfColor = PdfColor::new(72, 61, 139, 255);
152    pub const SLATE_BLUE: PdfColor = PdfColor::new(106, 90, 205, 255);
153    pub const MEDIUM_SLATE_BLUE: PdfColor = PdfColor::new(123, 104, 238, 255);
154    pub const MEDIUM_PURPLE: PdfColor = PdfColor::new(147, 112, 219, 255);
155    pub const DARK_MAGENTA: PdfColor = PdfColor::new(139, 0, 139, 255);
156    pub const DARK_VIOLET: PdfColor = PdfColor::new(148, 0, 211, 255);
157    pub const DARK_ORCHID: PdfColor = PdfColor::new(153, 50, 204, 255);
158    pub const MEDIUM_ORCHID: PdfColor = PdfColor::new(186, 85, 211, 255);
159    pub const THISTLE: PdfColor = PdfColor::new(216, 191, 216, 255);
160    pub const PLUM: PdfColor = PdfColor::new(221, 160, 221, 255);
161    pub const VIOLET: PdfColor = PdfColor::new(238, 130, 238, 255);
162    pub const ORCHID: PdfColor = PdfColor::new(218, 112, 214, 255);
163    pub const MEDIUM_VIOLET_RED: PdfColor = PdfColor::new(199, 21, 133, 255);
164    pub const PALE_VIOLET_RED: PdfColor = PdfColor::new(219, 112, 147, 255);
165    pub const DEEP_PINK: PdfColor = PdfColor::new(255, 20, 147, 255);
166    pub const HOT_PINK: PdfColor = PdfColor::new(255, 105, 180, 255);
167    pub const LIGHT_PINK: PdfColor = PdfColor::new(255, 182, 193, 255);
168    pub const PINK: PdfColor = PdfColor::new(255, 192, 203, 255);
169    pub const ANTIQUE_WHITE: PdfColor = PdfColor::new(250, 235, 215, 255);
170    pub const BEIGE: PdfColor = PdfColor::new(245, 245, 220, 255);
171    pub const BISQUE: PdfColor = PdfColor::new(255, 228, 196, 255);
172    pub const BLANCHED_ALMOND: PdfColor = PdfColor::new(255, 235, 205, 255);
173    pub const WHEAT: PdfColor = PdfColor::new(245, 222, 179, 255);
174    pub const CORN_SILK: PdfColor = PdfColor::new(255, 248, 220, 255);
175    pub const LEMON_CHIFFON: PdfColor = PdfColor::new(255, 250, 205, 255);
176    pub const LIGHT_GOLDEN_ROD_YELLOW: PdfColor = PdfColor::new(250, 250, 210, 255);
177    pub const LIGHT_YELLOW: PdfColor = PdfColor::new(255, 255, 224, 255);
178    pub const SADDLE_BROWN: PdfColor = PdfColor::new(139, 69, 19, 255);
179    pub const SIENNA: PdfColor = PdfColor::new(160, 82, 45, 255);
180    pub const CHOCOLATE: PdfColor = PdfColor::new(210, 105, 30, 255);
181    pub const PERU: PdfColor = PdfColor::new(205, 133, 63, 255);
182    pub const SANDY_BROWN: PdfColor = PdfColor::new(244, 164, 96, 255);
183    pub const BURLY_WOOD: PdfColor = PdfColor::new(222, 184, 135, 255);
184    pub const TAN: PdfColor = PdfColor::new(210, 180, 140, 255);
185    pub const ROSY_BROWN: PdfColor = PdfColor::new(188, 143, 143, 255);
186    pub const MOCCASIN: PdfColor = PdfColor::new(255, 228, 181, 255);
187    pub const NAVAJO_WHITE: PdfColor = PdfColor::new(255, 222, 173, 255);
188    pub const PEACH_PUFF: PdfColor = PdfColor::new(255, 218, 185, 255);
189    pub const MISTY_ROSE: PdfColor = PdfColor::new(255, 228, 225, 255);
190    pub const LAVENDER_BLUSH: PdfColor = PdfColor::new(255, 240, 245, 255);
191    pub const LINEN: PdfColor = PdfColor::new(250, 240, 230, 255);
192    pub const OLD_LACE: PdfColor = PdfColor::new(253, 245, 230, 255);
193    pub const PAPAYA_WHIP: PdfColor = PdfColor::new(255, 239, 213, 255);
194    pub const SEA_SHELL: PdfColor = PdfColor::new(255, 245, 238, 255);
195    pub const MINT_CREAM: PdfColor = PdfColor::new(245, 255, 250, 255);
196    pub const SLATE_GRAY: PdfColor = PdfColor::new(112, 128, 144, 255);
197    pub const LIGHT_SLATE_GRAY: PdfColor = PdfColor::new(119, 136, 153, 255);
198    pub const LIGHT_STEEL_BLUE: PdfColor = PdfColor::new(176, 196, 222, 255);
199    pub const LAVENDER: PdfColor = PdfColor::new(230, 230, 250, 255);
200    pub const FLORAL_WHITE: PdfColor = PdfColor::new(255, 250, 240, 255);
201    pub const ALICE_BLUE: PdfColor = PdfColor::new(240, 248, 255, 255);
202    pub const GHOST_WHITE: PdfColor = PdfColor::new(248, 248, 255, 255);
203    pub const HONEYDEW: PdfColor = PdfColor::new(240, 255, 240, 255);
204    pub const IVORY: PdfColor = PdfColor::new(255, 255, 240, 255);
205    pub const AZURE: PdfColor = PdfColor::new(240, 255, 255, 255);
206    pub const SNOW: PdfColor = PdfColor::new(255, 250, 250, 255);
207    pub const DIM_GREY: PdfColor = PdfColor::new(105, 105, 105, 255);
208    pub const GREY: PdfColor = PdfColor::new(128, 128, 128, 255);
209    pub const DARK_GREY: PdfColor = PdfColor::new(169, 169, 169, 255);
210    pub const LIGHT_GREY: PdfColor = PdfColor::new(211, 211, 211, 255);
211    pub const GAINSBORO: PdfColor = PdfColor::new(220, 220, 220, 255);
212    pub const WHITE_SMOKE: PdfColor = PdfColor::new(245, 245, 245, 255);
213
214    #[inline]
215    #[allow(dead_code)]
216    pub(crate) const fn from_pdfium(argb: FPDF_DWORD) -> Self {
217        Self::new(
218            ((argb & 0xFF0000) >> 16) as u8,
219            ((argb & 0xFF00) >> 8) as u8,
220            (argb & 0xFF) as u8,
221            ((argb & 0xFF000000) >> 24) as u8,
222        )
223    }
224
225    /// Constructs a new [PdfColor] object from the given arguments.
226    #[inline]
227    pub const fn new(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
228        Self {
229            r: red,
230            g: green,
231            b: blue,
232            a: alpha,
233        }
234    }
235
236    /// Returns the result of importing the given hexadecimal color specification,
237    /// as in HTML. For example, `#800080` represents a shade of purple with 100% opacity,
238    /// and `#40800080` is the same shade of purple with 25% opacity. The leading hash
239    /// symbol is required.
240    pub fn from_hex(hex: &str) -> Result<Self, PdfiumError> {
241        if hex.starts_with('#') {
242            match hex.len() {
243                7 => FPDF_DWORD::from_str_radix(&hex[1..hex.len()], 16)
244                    .map(PdfColor::from_pdfium)
245                    .map(|color| color.with_alpha(255))
246                    .map_err(PdfiumError::ParseHexadecimalColorError),
247                9 => FPDF_DWORD::from_str_radix(&hex[1..hex.len()], 16)
248                    .map(PdfColor::from_pdfium)
249                    .map_err(PdfiumError::ParseHexadecimalColorError),
250                _ => Err(PdfiumError::ParseHexadecimalColorUnexpectedLength),
251            }
252        } else {
253            Err(PdfiumError::ParseHexadecimalColorMissingLeadingHash)
254        }
255    }
256
257    /// Returns the result of averaging the RGB and alpha values of the two given [PdfColor] objects.
258    #[inline]
259    pub const fn mix(a: &PdfColor, b: &PdfColor) -> Self {
260        a.mix_with(b)
261    }
262
263    /// Returns the result of averaging the RGB and alpha values of this [PdfColor] with the given [PdfColor].
264    #[inline]
265    pub const fn mix_with(&self, other: &PdfColor) -> Self {
266        Self {
267            r: (self.r + other.r) / 2,
268            g: (self.g + other.g) / 2,
269            b: (self.b + other.b) / 2,
270            a: (self.a + other.a) / 2,
271        }
272    }
273
274    /// Constructs a new [PdfColor] by copying the red, green, and blue color components
275    /// of this color and applying the given alpha value.
276    #[inline]
277    pub const fn with_alpha(&self, alpha: u8) -> Self {
278        Self {
279            r: self.r,
280            g: self.g,
281            b: self.b,
282            a: alpha,
283        }
284    }
285
286    /// Returns the alpha (opacity) component of this color, with 0 = completely transparent
287    /// and 255 = completely opaque (solid).
288    #[inline]
289    pub fn alpha(&self) -> u8 {
290        self.a
291    }
292
293    /// Returns the red component of this color.
294    #[inline]
295    pub fn red(&self) -> u8 {
296        self.r
297    }
298
299    /// Returns the green component of this color.
300    #[inline]
301    pub fn green(&self) -> u8 {
302        self.g
303    }
304
305    /// Returns the blue component of this color.
306    #[inline]
307    pub fn blue(&self) -> u8 {
308        self.b
309    }
310
311    /// Returns the hexadecimal representation of this color, as in HTML, without
312    /// a leading hash symbol. Excludes the alpha channel value. For example,
313    /// `PdfColor::PURPLE.to_hex()` will return "800080".
314    #[inline]
315    pub fn to_hex(&self) -> String {
316        format!("{:02X?}{:02X?}{:02X?}", self.r, self.g, self.b)
317    }
318
319    /// Returns the hexadecimal representation of this color, as in HTML, without
320    /// a leading hash symbol. Includes the alpha channel value. For example,
321    /// `PdfColor::PURPLE.to_hex_with_alpha()` will return "FF800080".
322    #[inline]
323    pub fn to_hex_with_alpha(&self) -> String {
324        format!("{:02X?}{:02X?}{:02X?}{:02X?}", self.a, self.r, self.g, self.b)
325    }
326
327    /// Returns this color encoded as a 32-bit hexadecimal 0xAARRGGBB value,
328    /// suitable for passing to Pdfium.
329    #[inline]
330    pub(crate) fn as_pdfium_color(&self) -> FPDF_DWORD {
331        let (alpha, r, g, b) = self.color_components();
332
333        ((alpha << 24) | (b << 16) | (g << 8) | r) as FPDF_DWORD
334    }
335
336    /// Returns a tuple comprising this color encoded as a 32-bit hexadecimal 0xFFRRGGBB value
337    /// and this alpha encoded as an 8-bit value, suitable for passing to Pdfium.
338    #[inline]
339    pub(crate) fn as_pdfium_color_with_alpha(&self) -> (FPDF_DWORD, u8) {
340        let (alpha, r, g, b) = self.color_components();
341
342        (((0xFF << 24) | (b << 16) | (g << 8) | r) as FPDF_DWORD, alpha as u8)
343    }
344
345    /// Returns the raw color components of this [PdfColor] in the order (alpha, R, G, B).
346    #[inline]
347    fn color_components(&self) -> (FPDF_DWORD, FPDF_DWORD, FPDF_DWORD, FPDF_DWORD) {
348        (
349            self.a as FPDF_DWORD,
350            self.r as FPDF_DWORD,
351            self.g as FPDF_DWORD,
352            self.b as FPDF_DWORD,
353        )
354    }
355}
356
357#[cfg(test)]
358mod tests {
359    use crate::prelude::*;
360
361    #[test]
362    fn test_from_hex() {
363        assert_eq!(
364            PdfColor::from_hex("#800080").unwrap().color_components(),
365            PdfColor::PURPLE.color_components()
366        );
367        assert_eq!(
368            PdfColor::from_hex("#FF800080").unwrap().color_components(),
369            PdfColor::PURPLE.color_components()
370        );
371        assert_eq!(
372            PdfColor::from_hex("#40800080").unwrap().color_components(),
373            PdfColor::PURPLE.with_alpha(64).color_components()
374        );
375    }
376
377    #[test]
378    fn test_to_hex() {
379        assert_eq!(PdfColor::PURPLE.to_hex(), "800080");
380        assert_eq!(PdfColor::PURPLE.with_alpha(64).to_hex(), "800080");
381        assert_eq!(PdfColor::PURPLE.to_hex_with_alpha(), "FF800080");
382        assert_eq!(PdfColor::PURPLE.with_alpha(64).to_hex_with_alpha(), "40800080");
383    }
384}