Skip to main content

umya_spreadsheet/structs/
color.rs

1// color
2use std::{
3    borrow::Cow,
4    io::Cursor,
5};
6
7use md5::Digest;
8use phf::phf_map;
9use quick_xml::{
10    Reader,
11    Writer,
12    events::{
13        BytesStart,
14        Event,
15    },
16};
17use rgb::Argb;
18
19use crate::{
20    helper::color::calc_tint,
21    reader::driver::get_attribute_value,
22    structs::drawing::Theme,
23    writer::driver::write_start_tag,
24};
25
26pub type ARGB8 = Argb<u8>;
27
28macro_rules! argb {
29    ($a:expr, $r:expr, $g:expr, $b:expr) => {
30        ARGB8 {
31            a: $a,
32            r: $r,
33            g: $g,
34            b: $b,
35        }
36    };
37}
38
39static INDEX_TO_COLOR: phf::Map<u32, ARGB8> = phf_map! {
40    0u32 => argb!(0xFF, 0x00, 0x00, 0x00), // System Colour #1 - Black
41    1u32 => argb!(0xFF, 0xFF, 0xFF, 0xFF), // System Colour #2 - White
42    2u32 => argb!(0xFF, 0xFF, 0x00, 0x00), // System Colour #3 - Red
43    3u32 => argb!(0xFF, 0x00, 0xFF, 0x00), // System Colour #4 - Green
44    4u32 => argb!(0xFF, 0x00, 0x00, 0xFF), // System Colour #5 - Blue
45    5u32 => argb!(0xFF, 0xFF, 0xFF, 0x00), // System Colour #6 - Yellow
46    6u32 => argb!(0xFF, 0xFF, 0x00, 0xFF), // System Colour #7- Magenta
47    7u32 => argb!(0xFF, 0x00, 0xFF, 0xFF), // System Colour #8- Cyan
48    8u32 => argb!(0xFF, 0x00, 0x00, 0x00), // System Colour #1 - Black
49    9u32 => argb!(0xFF, 0xFF, 0xFF, 0xFF), // System Colour #2 - White
50    10u32 => argb!(0xFF, 0xFF, 0x00, 0x00), // System Colour #3 - Red
51    11u32 => argb!(0xFF, 0x00, 0xFF, 0x00), // System Colour #4 - Green
52    12u32 => argb!(0xFF, 0x00, 0x00, 0xFF), // System Colour #5 - Blue
53    13u32 => argb!(0xFF, 0xFF, 0xFF, 0x00), // System Colour #6 - Yellow
54    14u32 => argb!(0xFF, 0xFF, 0x00, 0xFF), // System Colour #7- Magenta
55    15u32 => argb!(0xFF, 0x00, 0xFF, 0xFF), // System Colour #8- Cyan
56    16u32 => argb!(0xFF, 0x80, 0x00, 0x00), // Standard Colour #9
57    17u32 => argb!(0xFF, 0x00, 0x80, 0x00), // Standard Colour #10
58    18u32 => argb!(0xFF, 0x00, 0x00, 0x80), // Standard Colour #11
59    19u32 => argb!(0xFF, 0x80, 0x80, 0x00), // Standard Colour #12
60    20u32 => argb!(0xFF, 0x80, 0x00, 0x80), // Standard Colour #13
61    21u32 => argb!(0xFF, 0x00, 0x80, 0x80), // Standard Colour #14
62    22u32 => argb!(0xFF, 0xC0, 0xC0, 0xC0), // Standard Colour #15
63    23u32 => argb!(0xFF, 0x80, 0x80, 0x80), // Standard Colour #16
64    24u32 => argb!(0xFF, 0x99, 0x99, 0xFF), // Chart Fill Colour #17
65    25u32 => argb!(0xFF, 0x99, 0x33, 0x66), // Chart Fill Colour #18
66    26u32 => argb!(0xFF, 0xFF, 0xFF, 0xCC), // Chart Fill Colour #19
67    27u32 => argb!(0xFF, 0xCC, 0xFF, 0xFF), // Chart Fill Colour #20
68    28u32 => argb!(0xFF, 0x66, 0x00, 0x66), // Chart Fill Colour #21
69    29u32 => argb!(0xFF, 0xFF, 0x80, 0x80), // Chart Fill Colour #22
70    30u32 => argb!(0xFF, 0x00, 0x66, 0xCC), // Chart Fill Colour #23
71    31u32 => argb!(0xFF, 0xCC, 0xCC, 0xFF), // Chart Fill Colour #24
72    32u32 => argb!(0xFF, 0x00, 0x00, 0x80), // Chart Line Colour #25
73    33u32 => argb!(0xFF, 0xFF, 0x00, 0xFF), // Chart Line Colour #26
74    34u32 => argb!(0xFF, 0xFF, 0xFF, 0x00), // Chart Line Colour #27
75    35u32 => argb!(0xFF, 0x00, 0xFF, 0xFF), // Chart Line Colour #28
76    36u32 => argb!(0xFF, 0x80, 0x00, 0x80), // Chart Line Colour #29
77    37u32 => argb!(0xFF, 0x80, 0x00, 0x00), // Chart Line Colour #30
78    38u32 => argb!(0xFF, 0x00, 0x80, 0x80), // Chart Line Colour #31
79    39u32 => argb!(0xFF, 0x00, 0x00, 0xFF), // Chart Line Colour #32
80    40u32 => argb!(0xFF, 0x00, 0xCC, 0xFF), // Standard Colour #33
81    41u32 => argb!(0xFF, 0xCC, 0xFF, 0xFF), // Standard Colour #34
82    42u32 => argb!(0xFF, 0xCC, 0xFF, 0xCC), // Standard Colour #35
83    43u32 => argb!(0xFF, 0xFF, 0xFF, 0x99), // Standard Colour #36
84    44u32 => argb!(0xFF, 0x99, 0xCC, 0xFF), // Standard Colour #37
85    45u32 => argb!(0xFF, 0xFF, 0x99, 0xCC), // Standard Colour #38
86    46u32 => argb!(0xFF, 0xCC, 0x99, 0xFF), // Standard Colour #39
87    47u32 => argb!(0xFF, 0xFF, 0xCC, 0x99), // Standard Colour #40
88    48u32 => argb!(0xFF, 0x33, 0x66, 0xFF), // Standard Colour #41
89    49u32 => argb!(0xFF, 0x33, 0xCC, 0xCC), // Standard Colour #42
90    50u32 => argb!(0xFF, 0x99, 0xCC, 0x00), // Standard Colour #43
91    51u32 => argb!(0xFF, 0xFF, 0xCC, 0x00), // Standard Colour #44
92    52u32 => argb!(0xFF, 0xFF, 0x99, 0x00), // Standard Colour #45
93    53u32 => argb!(0xFF, 0xFF, 0x66, 0x00), // Standard Colour #46
94    54u32 => argb!(0xFF, 0x66, 0x66, 0x99), // Standard Colour #47
95    55u32 => argb!(0xFF, 0x96, 0x96, 0x96), // Standard Colour #48
96    56u32 => argb!(0xFF, 0x00, 0x33, 0x66), // Standard Colour #49
97    57u32 => argb!(0xFF, 0x33, 0x99, 0x66), // Standard Colour #50
98    58u32 => argb!(0xFF, 0x00, 0x33, 0x00), // Standard Colour #51
99    59u32 => argb!(0xFF, 0x33, 0x33, 0x00), // Standard Colour #52
100    60u32 => argb!(0xFF, 0x99, 0x33, 0x00), // Standard Colour #53
101    61u32 => argb!(0xFF, 0x99, 0x33, 0x66), // Standard Colour #54
102    62u32 => argb!(0xFF, 0x33, 0x33, 0x99), // Standard Colour #55
103    63u32 => argb!(0xFF, 0x33, 0x33, 0x33), // Standard Colour #56
104};
105
106static COLOR_STR_TO_INDEX: phf::Map<&'static str, u32> = phf_map! {
107    "FF000000" => 0u32, // System Colour #1 - Black
108    "FFFFFFFF" => 1u32, // System Colour #2 - White
109    "FFFF0000" => 2u32, // System Colour #3 - Red
110    "FF00FF00" => 3u32, // System Colour #4 - Green
111    "FF0000FF" => 4u32, // System Colour #5 - Blue
112    "FFFFFF00" => 5u32, // System Colour #6 - Yellow
113    "FFFF00FF" => 6u32, // System Colour #7- Magenta
114    "FF00FFFF" => 7u32, // System Colour #8- Cyan
115//    "FF000000" => 8u32, // System Colour #1 - Black - Duplicate Key !
116//    "FFFFFFFF" => 9u32, // System Colour #2 - White - Duplicate Key !
117//    "FFFF0000" => 10u32, // System Colour #3 - Red - Duplicate Key !
118//    "FF00FF00" => 11u32, // System Colour #4 - Green - Duplicate Key !
119//    "FF0000FF" => 12u32, // System Colour #5 - Blue - Duplicate Key !
120//    "FFFFFF00" => 13u32, // System Colour #6 - Yellow - Duplicate Key !
121//    "FFFF00FF" => 14u32, // System Colour #7- Magenta - Duplicate Key !
122//    "FF00FFFF" => 15u32, // System Colour #8- Cyan - Duplicate Key !
123    "FF800000" => 16u32, // Standard Colour #9
124    "FF008000" => 17u32, // Standard Colour #10
125    "FF000080" => 18u32, // Standard Colour #11
126    "FF808000" => 19u32, // Standard Colour #12
127    "FF800080" => 20u32, // Standard Colour #13
128    "FF008080" => 21u32, // Standard Colour #14
129    "FFC0C0C0" => 22u32, // Standard Colour #15
130    "FF808080" => 23u32, // Standard Colour #16
131    "FF9999FF" => 24u32, // Chart Fill Colour #17
132    "FF993366" => 25u32, // Chart Fill Colour #18
133    "FFFFFFCC" => 26u32, // Chart Fill Colour #19
134    "FFCCFFFF" => 27u32, // Chart Fill Colour #20
135    "FF660066" => 28u32, // Chart Fill Colour #21
136    "FFFF8080" => 29u32, // Chart Fill Colour #22
137    "FF0066CC" => 30u32, // Chart Fill Colour #23
138    "FFCCCCFF" => 31u32, // Chart Fill Colour #24
139//   "FF000080" => 32u32, // Chart Line Colour #25 - Duplicate Key !
140//   "FFFF00FF" => 33u32, // Chart Line Colour #26 - Duplicate Key !
141//   "FFFFFF00" => 34u32, // Chart Line Colour #27 - Duplicate Key !
142//   "FF00FFFF" => 35u32, // Chart Line Colour #28 - Duplicate Key !
143//   "FF800080" => 36u32, // Chart Line Colour #29 - Duplicate Key !
144//   "FF800000" => 37u32, // Chart Line Colour #30 - Duplicate Key !
145//   "FF008080" => 38u32, // Chart Line Colour #31 - Duplicate Key !
146//   "FF0000FF" => 39u32, // Chart Line Colour #32 - Duplicate Key !
147    "FF00CCFF" => 40u32, // Standard Colour #33
148//   "FFCCFFFF" => 41u32, // Standard Colour #34 - Duplicate Key !
149    "FFCCFFCC" => 42u32, // Standard Colour #35
150    "FFFFFF99" => 43u32, // Standard Colour #36
151    "FF99CCFF" => 44u32, // Standard Colour #37
152    "FFFF99CC" => 45u32, // Standard Colour #38
153    "FFCC99FF" => 46u32, // Standard Colour #39
154    "FFFFCC99" => 47u32, // Standard Colour #40
155    "FF3366FF" => 48u32, // Standard Colour #41
156    "FF33CCCC" => 49u32, // Standard Colour #42
157    "FF99CC00" => 50u32, // Standard Colour #43
158    "FFFFCC00" => 51u32, // Standard Colour #44
159    "FFFF9900" => 52u32, // Standard Colour #45
160    "FFFF6600" => 53u32, // Standard Colour #46
161    "FF666699" => 54u32, // Standard Colour #47
162    "FF969696" => 55u32, // Standard Colour #48
163    "FF003366" => 56u32, // Standard Colour #49
164    "FF339966" => 57u32, // Standard Colour #50
165    "FF003300" => 58u32, // Standard Colour #51
166    "FF333300" => 59u32, // Standard Colour #52
167    "FF993300" => 60u32, // Standard Colour #53
168//    "FF993366" => 61u32, // Standard Colour #54 - Duplicate Key !
169    "FF333399" => 62u32, // Standard Colour #55
170    "FF333333" => 63u32, // Standard Colour #56
171};
172
173#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
174pub struct Color {
175    indexed:     Option<u32>,
176    theme_index: Option<u32>,
177    argb:        Option<ARGB8>,
178    tint:        Option<f64>,
179}
180
181impl Color {
182    // Colors
183    pub const COLOR_BLACK: ARGB8 = ARGB8 {
184        a: 0xFF,
185        r: 0x00,
186        g: 0x00,
187        b: 0x00,
188    };
189    pub const COLOR_BLACK_STR: &'static str = "FF000000";
190    pub const COLOR_BLUE: ARGB8 = ARGB8 {
191        a: 0xFF,
192        r: 0x00,
193        g: 0x00,
194        b: 0xFF,
195    };
196    pub const COLOR_BLUE_STR: &'static str = "FF0000FF";
197    pub const COLOR_DARKBLUE: ARGB8 = ARGB8 {
198        a: 0xFF,
199        r: 0x00,
200        g: 0x00,
201        b: 0x80,
202    };
203    pub const COLOR_DARKBLUE_STR: &'static str = "FF000080";
204    pub const COLOR_DARKGREEN: ARGB8 = ARGB8 {
205        a: 0xFF,
206        r: 0x00,
207        g: 0x80,
208        b: 0x00,
209    };
210    pub const COLOR_DARKGREEN_STR: &'static str = "FF008000";
211    pub const COLOR_DARKRED: ARGB8 = ARGB8 {
212        a: 0xFF,
213        r: 0x80,
214        g: 0x00,
215        b: 0x00,
216    };
217    pub const COLOR_DARKRED_STR: &'static str = "FF800000";
218    pub const COLOR_DARKYELLOW: ARGB8 = ARGB8 {
219        a: 0xFF,
220        r: 0x80,
221        g: 0x80,
222        b: 0x00,
223    };
224    pub const COLOR_DARKYELLOW_STR: &'static str = "FF808000";
225    pub const COLOR_GREEN: ARGB8 = ARGB8 {
226        a: 0xFF,
227        r: 0x00,
228        g: 0xFF,
229        b: 0x00,
230    };
231    pub const COLOR_GREEN_STR: &'static str = "FF00FF00";
232    pub const COLOR_RED: ARGB8 = ARGB8 {
233        a: 0xFF,
234        r: 0xFF,
235        g: 0x00,
236        b: 0x00,
237    };
238    pub const COLOR_RED_STR: &'static str = "FFFF0000";
239    pub const COLOR_WHITE: ARGB8 = ARGB8 {
240        a: 0xFF,
241        r: 0xFF,
242        g: 0xFF,
243        b: 0xFF,
244    };
245    pub const COLOR_WHITE_STR: &'static str = "FFFFFFFF";
246    pub const COLOR_YELLOW: ARGB8 = ARGB8 {
247        a: 0xFF,
248        r: 0xFF,
249        g: 0xFF,
250        b: 0x00,
251    };
252    pub const COLOR_YELLOW_STR: &'static str = "FFFFFF00";
253    pub const NAMED_COLORS: [&'static str; 8] = [
254        "Black", "White", "Red", "Green", "Blue", "Yellow", "Magenta", "Cyan",
255    ];
256
257    /// Convert hex string to ARGB8
258    #[must_use]
259    pub fn hex_to_argb8(hex: &str) -> Option<ARGB8> {
260        if hex.len() == 9 && hex.starts_with('#') {
261            let a = u8::from_str_radix(&hex[1..3], 16).ok()?;
262            let r = u8::from_str_radix(&hex[3..5], 16).ok()?;
263            let g = u8::from_str_radix(&hex[5..7], 16).ok()?;
264            let b = u8::from_str_radix(&hex[7..9], 16).ok()?;
265
266            Some(ARGB8 { a, r, g, b })
267        } else if hex.len() == 8 {
268            let a = u8::from_str_radix(&hex[0..2], 16).ok()?;
269            let r = u8::from_str_radix(&hex[2..4], 16).ok()?;
270            let g = u8::from_str_radix(&hex[4..6], 16).ok()?;
271            let b = u8::from_str_radix(&hex[6..8], 16).ok()?;
272
273            Some(ARGB8 { a, r, g, b })
274        } else if hex.len() == 7 && hex.starts_with('#') {
275            let a = 0xFF;
276            let r = u8::from_str_radix(&hex[1..3], 16).ok()?;
277            let g = u8::from_str_radix(&hex[3..5], 16).ok()?;
278            let b = u8::from_str_radix(&hex[5..7], 16).ok()?;
279
280            Some(ARGB8 { a, r, g, b })
281        } else if hex.len() == 6 {
282            let a = 0xFF;
283            let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
284            let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
285            let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
286
287            Some(ARGB8 { a, r, g, b })
288        } else if (hex.len() == 4 && hex.starts_with('#')) || hex.len() == 3 {
289            // To pass an integration test where the hex string is "#333".
290            // https://github.com/MathNya/umya-spreadsheet/pull/113
291            // https://github.com/MathNya/umya-spreadsheet/pull/250#issuecomment-2566258423
292            // https://www.w3schools.com/css/css_colors_hex.asp
293
294            let padded_hex = hex
295                .replace('#', "")
296                .chars()
297                .map(|c| c.to_string().repeat(2))
298                .collect::<String>();
299            let a = 0xFF;
300            let r = u8::from_str_radix(&padded_hex[0..2], 16).ok()?;
301            let g = u8::from_str_radix(&padded_hex[2..4], 16).ok()?;
302            let b = u8::from_str_radix(&padded_hex[4..6], 16).ok()?;
303
304            Some(ARGB8 { a, r, g, b })
305        } else {
306            None
307        }
308    }
309
310    /// Convert ARGB8 to hex string
311    #[must_use]
312    pub fn argb8_to_hex(argb: ARGB8) -> String {
313        format!("{:02X}{:02X}{:02X}{:02X}", argb.a, argb.r, argb.g, argb.b)
314    }
315
316    #[must_use]
317    pub fn argb_str(&self) -> String {
318        Self::argb8_to_hex(self.argb())
319    }
320
321    #[must_use]
322    #[deprecated(since = "3.0.0", note = "Use argb_str()")]
323    pub fn get_argb_str(&self) -> String {
324        Self::argb8_to_hex(self.argb())
325    }
326
327    #[must_use]
328    pub fn argb(&self) -> ARGB8 {
329        if let Some(idx) = self.indexed {
330            if let Some(v) = INDEX_TO_COLOR.get(&idx) {
331                return *v;
332            }
333        }
334        self.argb.unwrap_or_default()
335    }
336
337    #[must_use]
338    #[deprecated(since = "3.0.0", note = "Use argb()")]
339    pub fn get_argb(&self) -> ARGB8 {
340        self.argb()
341    }
342
343    /// Get Argb.
344    /// Color information based on the theme can also be obtained.
345    /// # Examples
346    /// ```
347    /// let mut book = umya_spreadsheet::new_file();
348    /// let theme = book.theme();
349    /// ```
350    #[must_use]
351    pub fn argb_with_theme(&self, theme: &Theme) -> Cow<'static, str> {
352        if self.indexed.is_some() {
353            return self.argb_str().into();
354        }
355        if let Some(key) = self.theme_index {
356            if let Some(v) = theme
357                .theme_elements()
358                .color_scheme()
359                .color_map()
360                .get(key as usize)
361            {
362                if let Some(tint) = self.tint {
363                    return calc_tint(v, tint).into();
364                }
365                return v.clone().into();
366            }
367        }
368        self.argb_str().clone().into()
369    }
370
371    #[must_use]
372    #[deprecated(since = "3.0.0", note = "Use argb_with_theme()")]
373    pub fn get_argb_with_theme(&self, theme: &Theme) -> Cow<'static, str> {
374        self.argb_with_theme(theme)
375    }
376
377    pub fn set_argb<S: Into<ARGB8>>(&mut self, value: S) -> &mut Self {
378        let argb = value.into();
379        let indexed = COLOR_STR_TO_INDEX.get(Self::argb8_to_hex(argb).as_ref());
380
381        if let Some(v) = indexed {
382            self.indexed = Some(*v);
383            self.argb = None;
384        } else {
385            self.indexed = None;
386            self.argb = Some(argb);
387        }
388        self.theme_index = None;
389        self
390    }
391
392    pub fn set_argb_str<S: AsRef<str>>(&mut self, value: S) -> &mut Self {
393        let argb = Self::hex_to_argb8(value.as_ref()).unwrap();
394        let indexed = COLOR_STR_TO_INDEX.get(value.as_ref());
395
396        if let Some(v) = indexed {
397            self.indexed = Some(*v);
398            self.argb = None;
399        } else {
400            self.indexed = None;
401            self.argb = Some(argb);
402        }
403        self.theme_index = None;
404        self
405    }
406
407    #[inline]
408    #[must_use]
409    pub fn indexed(&self) -> u32 {
410        self.indexed.unwrap_or(0)
411    }
412
413    #[inline]
414    #[must_use]
415    #[deprecated(since = "3.0.0", note = "Use indexed()")]
416    pub fn get_indexed(&self) -> u32 {
417        self.indexed()
418    }
419
420    #[inline]
421    pub fn set_indexed(&mut self, index: u32) -> &mut Self {
422        self.indexed = Some(index);
423        self.theme_index = None;
424        self.argb = None;
425        self
426    }
427
428    #[inline]
429    #[must_use]
430    pub fn theme_index(&self) -> u32 {
431        self.theme_index.unwrap_or(0)
432    }
433
434    #[inline]
435    #[must_use]
436    #[deprecated(since = "3.0.0", note = "Use theme_index()")]
437    pub fn get_theme_index(&self) -> u32 {
438        self.theme_index()
439    }
440
441    #[inline]
442    pub fn set_theme_index(&mut self, index: u32) -> &mut Self {
443        self.indexed = None;
444        self.theme_index = Some(index);
445        self.argb = None;
446        self
447    }
448
449    #[inline]
450    #[must_use]
451    pub fn tint(&self) -> f64 {
452        self.tint.unwrap_or(0.0)
453    }
454
455    #[inline]
456    #[must_use]
457    #[deprecated(since = "3.0.0", note = "Use tint()")]
458    pub fn get_tint(&self) -> f64 {
459        self.tint()
460    }
461
462    #[inline]
463    pub fn set_tint(&mut self, value: f64) -> &mut Color {
464        self.tint = Some(value);
465        self
466    }
467
468    #[inline]
469    pub(crate) fn has_value(&self) -> bool {
470        self.theme_index.is_some()
471            || self.indexed.is_some()
472            || self.argb.is_some()
473            || self.tint.is_some()
474    }
475
476    #[inline]
477    pub(crate) fn hash_code(&self) -> String {
478        format!(
479            "{:x}",
480            md5::Md5::digest(format!(
481                "{}{}{}{}",
482                self.indexed.map_or(String::new(), |v| v.to_string()),
483                self.theme_index.map_or(String::new(), |v| v.to_string()),
484                self.argb.map_or(String::new(), Self::argb8_to_hex),
485                self.tint.map_or(String::new(), |v| v.to_string())
486            ))
487        )
488    }
489
490    #[inline]
491    #[deprecated(since = "3.0.0", note = "Use tint()")]
492    pub(crate) fn get_hash_code(&self) -> String {
493        self.hash_code()
494    }
495
496    // When opened in software such as Excel, it is visually blank.
497    #[inline]
498    pub(crate) fn is_visually_empty(&self) -> bool {
499        !self.has_value()
500    }
501
502    pub(crate) fn set_attributes<R: std::io::BufRead>(
503        &mut self,
504        reader: &mut Reader<R>,
505        e: &BytesStart,
506        empty_flg: bool,
507    ) {
508        for attr in e.attributes().with_checks(false).flatten() {
509            match attr.key.0 {
510                b"indexed" => {
511                    if let Ok(v) = get_attribute_value(&attr) {
512                        if let Ok(num) = v.parse() {
513                            self.indexed = Some(num);
514                        }
515                    }
516                }
517                b"theme" => {
518                    if let Ok(v) = get_attribute_value(&attr) {
519                        if let Ok(num) = v.parse() {
520                            self.theme_index = Some(num);
521                        }
522                    }
523                }
524                b"rgb" => {
525                    if let Ok(v) = get_attribute_value(&attr) {
526                        self.argb = Self::hex_to_argb8(&v);
527                    }
528                }
529                b"tint" => {
530                    if let Ok(v) = get_attribute_value(&attr) {
531                        if let Ok(num) = v.parse() {
532                            self.tint = Some(num);
533                        }
534                    }
535                }
536                _ => {}
537            }
538        }
539
540        if empty_flg {
541            return;
542        }
543
544        let mut buf = Vec::new();
545        loop {
546            match reader.read_event_into(&mut buf) {
547                Ok(Event::End(ref e)) => match e.name().into_inner() {
548                    b"color" | b"fgColor" | b"bgColor" | b"tabColor" => return,
549                    _ => (),
550                },
551                Ok(Event::Eof) => panic!(
552                    "Error: Could not find {} end element",
553                    "color,fgColor,bgColor,tabColor"
554                ),
555                Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
556                _ => (),
557            }
558            buf.clear();
559        }
560    }
561
562    #[inline]
563    pub(crate) fn write_to_color(&self, writer: &mut Writer<Cursor<Vec<u8>>>) {
564        // color
565        self.write_to(writer, "color");
566    }
567
568    #[inline]
569    pub(crate) fn write_to_fg_color(&self, writer: &mut Writer<Cursor<Vec<u8>>>) {
570        // fgColor
571        self.write_to(writer, "fgColor");
572    }
573
574    #[inline]
575    pub(crate) fn write_to_bg_color(&self, writer: &mut Writer<Cursor<Vec<u8>>>) {
576        // bgColor
577        self.write_to(writer, "bgColor");
578    }
579
580    #[inline]
581    pub(crate) fn write_to_tab_color(&self, writer: &mut Writer<Cursor<Vec<u8>>>) {
582        // tabColor
583        self.write_to(writer, "tabColor");
584    }
585
586    fn write_to(&self, writer: &mut Writer<Cursor<Vec<u8>>>, tag_name: &str) {
587        let mut attributes: crate::structs::AttrCollection = Vec::new();
588
589        if let Some(theme_index) = self.theme_index {
590            attributes.push(("theme", theme_index.to_string()).into());
591        } else if let Some(indexed) = self.indexed {
592            attributes.push(("indexed", indexed.to_string()).into());
593        } else if let Some(argb) = self.argb {
594            attributes.push(("rgb", Self::argb8_to_hex(argb)).into());
595        }
596
597        if let Some(tint) = self.tint {
598            attributes.push(("tint", tint.to_string()).into());
599        }
600
601        if !attributes.is_empty() {
602            write_start_tag(writer, tag_name, attributes, true);
603        }
604    }
605}
606
607#[cfg(test)]
608mod tests {
609    use super::*;
610
611    #[test]
612    fn test_hex_conversion() {
613        let hex = "FF123456";
614        let argb = Color::hex_to_argb8(hex).unwrap();
615        assert_eq!(Color::argb8_to_hex(argb), hex);
616    }
617
618    #[test]
619    fn set_value() {
620        let mut obj = Color::default();
621        obj.set_argb_str("F34F8080");
622        assert_eq!(obj.argb_str(), "F34F8080");
623
624        let mut obj = Color::default();
625        obj.set_argb_str("FFFF8080");
626        assert_eq!(obj.indexed(), 29);
627        assert_eq!(obj.argb_str(), "FFFF8080");
628
629        let mut obj = Color::default();
630        let theme = Theme::default_value();
631        obj.set_theme_index(1);
632        assert_eq!(obj.argb_with_theme(&theme), "000000");
633    }
634}