umya_spreadsheet/structs/
color.rs

1// color
2use super::DoubleValue;
3use super::StringValue;
4use super::UInt32Value;
5use crate::helper::color::*;
6use crate::reader::driver::*;
7use crate::structs::drawing::Theme;
8use crate::writer::driver::*;
9use md5::Digest;
10use quick_xml::events::{BytesStart, Event};
11use quick_xml::Reader;
12use quick_xml::Writer;
13use std::borrow::Cow;
14use std::io::Cursor;
15
16const INDEXED_COLORS: &[&str] = &[
17    "FF000000", //  System Colour #1 - Black
18    "FFFFFFFF", //  System Colour #2 - White
19    "FFFF0000", //  System Colour #3 - Red
20    "FF00FF00", //  System Colour #4 - Green
21    "FF0000FF", //  System Colour #5 - Blue
22    "FFFFFF00", //  System Colour #6 - Yellow
23    "FFFF00FF", //  System Colour #7- Magenta
24    "FF00FFFF", //  System Colour #8- Cyan
25    "FF000000", //  System Colour #1 - Black
26    "FFFFFFFF", //  System Colour #2 - White
27    "FFFF0000", //  System Colour #3 - Red
28    "FF00FF00", //  System Colour #4 - Green
29    "FF0000FF", //  System Colour #5 - Blue
30    "FFFFFF00", //  System Colour #6 - Yellow
31    "FFFF00FF", //  System Colour #7- Magenta
32    "FF00FFFF", //  System Colour #8- Cyan
33    "FF800000", //  Standard Colour #9
34    "FF008000", //  Standard Colour #10
35    "FF000080", //  Standard Colour #11
36    "FF808000", //  Standard Colour #12
37    "FF800080", //  Standard Colour #13
38    "FF008080", //  Standard Colour #14
39    "FFC0C0C0", //  Standard Colour #15
40    "FF808080", //  Standard Colour #16
41    "FF9999FF", //  Chart Fill Colour #17
42    "FF993366", //  Chart Fill Colour #18
43    "FFFFFFCC", //  Chart Fill Colour #19
44    "FFCCFFFF", //  Chart Fill Colour #20
45    "FF660066", //  Chart Fill Colour #21
46    "FFFF8080", //  Chart Fill Colour #22
47    "FF0066CC", //  Chart Fill Colour #23
48    "FFCCCCFF", //  Chart Fill Colour #24
49    "FF000080", //  Chart Line Colour #25
50    "FFFF00FF", //  Chart Line Colour #26
51    "FFFFFF00", //  Chart Line Colour #27
52    "FF00FFFF", //  Chart Line Colour #28
53    "FF800080", //  Chart Line Colour #29
54    "FF800000", //  Chart Line Colour #30
55    "FF008080", //  Chart Line Colour #31
56    "FF0000FF", //  Chart Line Colour #32
57    "FF00CCFF", //  Standard Colour #33
58    "FFCCFFFF", //  Standard Colour #34
59    "FFCCFFCC", //  Standard Colour #35
60    "FFFFFF99", //  Standard Colour #36
61    "FF99CCFF", //  Standard Colour #37
62    "FFFF99CC", //  Standard Colour #38
63    "FFCC99FF", //  Standard Colour #39
64    "FFFFCC99", //  Standard Colour #40
65    "FF3366FF", //  Standard Colour #41
66    "FF33CCCC", //  Standard Colour #42
67    "FF99CC00", //  Standard Colour #43
68    "FFFFCC00", //  Standard Colour #44
69    "FFFF9900", //  Standard Colour #45
70    "FFFF6600", //  Standard Colour #46
71    "FF666699", //  Standard Colour #47
72    "FF969696", //  Standard Colour #48
73    "FF003366", //  Standard Colour #49
74    "FF339966", //  Standard Colour #50
75    "FF003300", //  Standard Colour #51
76    "FF333300", //  Standard Colour #52
77    "FF993300", //  Standard Colour #53
78    "FF993366", //  Standard Colour #54
79    "FF333399", //  Standard Colour #55
80    "FF333333", //  Standard Colour #56
81];
82
83#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
84pub struct Color {
85    indexed: UInt32Value,
86    theme_index: UInt32Value,
87    argb: StringValue,
88    tint: DoubleValue,
89}
90impl Color {
91    pub const NAMED_COLORS: &'static [&'static str] = &[
92        "Black", "White", "Red", "Green", "Blue", "Yellow", "Magenta", "Cyan",
93    ];
94
95    // Colors
96    pub const COLOR_BLACK: &'static str = "FF000000";
97    pub const COLOR_WHITE: &'static str = "FFFFFFFF";
98    pub const COLOR_RED: &'static str = "FFFF0000";
99    pub const COLOR_DARKRED: &'static str = "FF800000";
100    pub const COLOR_BLUE: &'static str = "FF0000FF";
101    pub const COLOR_DARKBLUE: &'static str = "FF000080";
102    pub const COLOR_GREEN: &'static str = "FF00FF00";
103    pub const COLOR_DARKGREEN: &'static str = "FF008000";
104    pub const COLOR_YELLOW: &'static str = "FFFFFF00";
105    pub const COLOR_DARKYELLOW: &'static str = "FF808000";
106
107    /// Get Argb.
108    /// If the color is based on the theme, it cannot be obtained with this function.
109    /// In that case, use get_argb_with_theme(&self, theme: &Theme).
110    pub fn get_argb(&self) -> &str {
111        if self.indexed.has_value() {
112            match INDEXED_COLORS.get(self.indexed.get_value().clone() as usize) {
113                Some(v) => return v,
114                None => {}
115            }
116        }
117        self.argb.get_value_str()
118    }
119
120    /// Get Argb.
121    /// Color information based on the theme can also be obtained.
122    /// # Examples
123    /// ```
124    /// let mut book = umya_spreadsheet::new_file();
125    /// let theme = book.get_theme();
126    /// ```
127    pub fn get_argb_with_theme(&self, theme: &Theme) -> Cow<'static, str> {
128        if self.indexed.has_value() {
129            return self.get_argb().to_owned().into();
130        }
131        if self.theme_index.has_value() {
132            let key = *self.theme_index.get_value();
133            match theme
134                .get_theme_elements()
135                .get_color_scheme()
136                .get_color_map()
137                .get(key as usize)
138            {
139                Some(v) => {
140                    if self.tint.has_value() {
141                        return calc_tint(v, self.tint.get_value()).into();
142                    }
143                    return v.to_string().into();
144                }
145                None => {}
146            }
147        }
148        self.argb.get_value_str().to_string().into()
149    }
150
151    pub fn set_argb<S: Into<String>>(&mut self, value: S) -> &mut Self {
152        let argb = value.into();
153        let indexed = INDEXED_COLORS.iter().position(|&r| r == argb);
154        match indexed {
155            Some(v) => {
156                self.indexed.set_value(v as u32);
157                self.argb.remove_value();
158            }
159            None => {
160                self.indexed.remove_value();
161                self.argb.set_value(argb);
162            }
163        }
164        self.theme_index.remove_value();
165        self
166    }
167
168    #[inline]
169    pub fn get_indexed(&self) -> &u32 {
170        self.indexed.get_value()
171    }
172
173    #[inline]
174    pub fn set_indexed(&mut self, index: u32) -> &mut Self {
175        self.indexed.set_value(index);
176        self.theme_index.remove_value();
177        self.argb.remove_value();
178        self
179    }
180
181    #[inline]
182    pub fn get_theme_index(&self) -> &u32 {
183        self.theme_index.get_value()
184    }
185
186    #[inline]
187    pub fn set_theme_index(&mut self, index: u32) -> &mut Self {
188        self.indexed.remove_value();
189        self.theme_index.set_value(index);
190        self.argb.remove_value();
191        self
192    }
193
194    #[inline]
195    pub fn get_tint(&self) -> &f64 {
196        self.tint.get_value()
197    }
198
199    #[inline]
200    pub fn set_tint(&mut self, value: f64) -> &mut Color {
201        self.tint.set_value(value);
202        self
203    }
204
205    #[inline]
206    pub(crate) fn has_value(&self) -> bool {
207        self.theme_index.has_value()
208            || self.indexed.has_value()
209            || self.argb.has_value()
210            || self.tint.has_value()
211    }
212
213    #[inline]
214    pub(crate) fn get_hash_code(&self) -> String {
215        format!(
216            "{:x}",
217            md5::Md5::digest(format!(
218                "{}{}{}{}",
219                &self.indexed.get_hash_string(),
220                &self.theme_index.get_hash_string(),
221                &self.argb.get_hash_string(),
222                &self.tint.get_hash_string()
223            ))
224        )
225    }
226
227    // When opened in software such as Excel, it is visually blank.
228    #[inline]
229    pub(crate) fn is_visually_empty(&self) -> bool {
230        !self.has_value()
231    }
232
233    pub(crate) fn set_attributes<R: std::io::BufRead>(
234        &mut self,
235        reader: &mut Reader<R>,
236        e: &BytesStart,
237        empty_flg: bool,
238    ) {
239        for a in e.attributes().with_checks(false) {
240            match a {
241                Ok(ref attr) => match attr.key.0 {
242                    b"indexed" => {
243                        self.indexed
244                            .set_value_string(get_attribute_value(attr).unwrap());
245                    }
246                    b"theme" => {
247                        self.theme_index
248                            .set_value_string(get_attribute_value(attr).unwrap());
249                    }
250                    b"rgb" => {
251                        self.argb
252                            .set_value_string(get_attribute_value(attr).unwrap());
253                    }
254                    b"tint" => {
255                        self.tint
256                            .set_value_string(get_attribute_value(attr).unwrap());
257                    }
258                    _ => {}
259                },
260                Err(_) => {}
261            }
262        }
263
264        if empty_flg {
265            return;
266        }
267
268        let mut buf = Vec::new();
269        loop {
270            match reader.read_event_into(&mut buf) {
271                Ok(Event::End(ref e)) => match e.name().into_inner() {
272                    b"color" => return,
273                    b"fgColor" => return,
274                    b"bgColor" => return,
275                    b"tabColor" => return,
276                    _ => (),
277                },
278                Ok(Event::Eof) => panic!(
279                    "Error: Could not find {} end element",
280                    "color,fgColor,bgColor,tabColor"
281                ),
282                Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
283                _ => (),
284            }
285            buf.clear();
286        }
287    }
288
289    #[inline]
290    pub(crate) fn write_to_color(&self, writer: &mut Writer<Cursor<Vec<u8>>>) {
291        // color
292        self.write_to(writer, "color");
293    }
294
295    #[inline]
296    pub(crate) fn write_to_fg_color(&self, writer: &mut Writer<Cursor<Vec<u8>>>) {
297        // fgColor
298        self.write_to(writer, "fgColor");
299    }
300
301    #[inline]
302    pub(crate) fn write_to_bg_color(&self, writer: &mut Writer<Cursor<Vec<u8>>>) {
303        // bgColor
304        self.write_to(writer, "bgColor");
305    }
306
307    #[inline]
308    pub(crate) fn write_to_tab_color(&self, writer: &mut Writer<Cursor<Vec<u8>>>) {
309        // tabColor
310        self.write_to(writer, "tabColor");
311    }
312
313    fn write_to(&self, writer: &mut Writer<Cursor<Vec<u8>>>, tag_name: &str) {
314        let mut attributes: Vec<(&str, &str)> = Vec::new();
315        let theme_index = self.theme_index.get_value_string();
316        let indexed = self.indexed.get_value_string();
317        if self.theme_index.has_value() {
318            attributes.push(("theme", &theme_index));
319        } else if self.indexed.has_value() {
320            attributes.push(("indexed", &indexed));
321        } else if self.argb.has_value() {
322            attributes.push(("rgb", self.argb.get_value_str()));
323        }
324        let tint = self.tint.get_value_string();
325        if self.tint.has_value() {
326            attributes.push(("tint", &tint));
327        }
328
329        if !attributes.is_empty() {
330            write_start_tag(writer, tag_name, attributes, true);
331        }
332    }
333}
334
335#[cfg(test)]
336mod tests {
337    use super::*;
338
339    #[test]
340    fn set_value() {
341        let mut obj = Color::default();
342        obj.set_argb("F34F8080");
343        assert_eq!(obj.get_argb(), "F34F8080");
344
345        let mut obj = Color::default();
346        obj.set_argb("FFFF8080");
347        assert_eq!(obj.get_indexed(), &29);
348        assert_eq!(obj.get_argb(), "FFFF8080");
349
350        let mut obj = Color::default();
351        let theme = Theme::get_default_value();
352        obj.set_theme_index(1);
353        assert_eq!(obj.get_argb_with_theme(&theme), "000000");
354    }
355}