umya_spreadsheet/structs/
color.rs1use 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", "FFFFFFFF", "FFFF0000", "FF00FF00", "FF0000FF", "FFFFFF00", "FFFF00FF", "FF00FFFF", "FF000000", "FFFFFFFF", "FFFF0000", "FF00FF00", "FF0000FF", "FFFFFF00", "FFFF00FF", "FF00FFFF", "FF800000", "FF008000", "FF000080", "FF808000", "FF800080", "FF008080", "FFC0C0C0", "FF808080", "FF9999FF", "FF993366", "FFFFFFCC", "FFCCFFFF", "FF660066", "FFFF8080", "FF0066CC", "FFCCCCFF", "FF000080", "FFFF00FF", "FFFFFF00", "FF00FFFF", "FF800080", "FF800000", "FF008080", "FF0000FF", "FF00CCFF", "FFCCFFFF", "FFCCFFCC", "FFFFFF99", "FF99CCFF", "FFFF99CC", "FFCC99FF", "FFFFCC99", "FF3366FF", "FF33CCCC", "FF99CC00", "FFFFCC00", "FFFF9900", "FFFF6600", "FF666699", "FF969696", "FF003366", "FF339966", "FF003300", "FF333300", "FF993300", "FF993366", "FF333399", "FF333333", ];
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 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 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 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 #[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 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 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 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 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}