Skip to main content

oox/shared/drawingml/
colors.rs

1use super::{
2    sharedstylesheet::ColorMapping,
3    simpletypes::{
4        parse_hex_color_rgb, Angle, FixedPercentage, HexColorRGB, Percentage, PositiveFixedAngle,
5        PositiveFixedPercentage, PositivePercentage, PresetColorVal, SchemeColorVal, SystemColorVal,
6    },
7    util::XmlNodeExt,
8};
9use crate::{
10    error::{MissingAttributeError, MissingChildNodeError, NotGroupMemberError},
11    xml::XmlNode,
12    xsdtypes::{XsdChoice, XsdType},
13};
14use std::error::Error;
15
16pub type Result<T> = ::std::result::Result<T, Box<dyn Error>>;
17
18#[repr(C)]
19#[derive(Debug, Clone, PartialEq)]
20pub enum ColorTransform {
21    /// This element specifies a lighter version of its input color. A 10% tint is 10% of the input color combined with
22    /// 90% white.
23    ///
24    /// # Xml example
25    ///
26    /// The following manipulates the fill from having RGB value RRGGBB = (00, FF, 00)
27    /// to value RRGGBB= (BC, FF, BC)
28    /// ```xml
29    /// <a:solidFill>
30    ///   <a:srgbClr val="00FF00">
31    ///     <a:tint val="50000"/>
32    ///   </a:srgbClr>
33    /// </a:solidFill>
34    /// ```
35    Tint(PositiveFixedPercentage),
36
37    /// This element specifies a darker version of its input color. A 10% shade is 10% of the input color combined with
38    /// 90% black.
39    ///
40    /// # Xml example
41    ///
42    /// The following manipulates the fill from having RGB value RRGGBB = (00, FF, 00)
43    /// to value RRGGBB= (00, BC, 00)
44    /// ```xml
45    /// <a:solidFill>
46    ///   <a:srgbClr val="00FF00">
47    ///     <a:shade val="50000"/>
48    ///   </a:srgbClr>
49    /// </a:solidFill>
50    /// ```
51    Shade(PositiveFixedPercentage),
52
53    /// This element specifies that the color rendered should be the complement of its input color with the complement
54    /// being defined as such. Two colors are called complementary if, when mixed they produce a shade of grey. For
55    /// instance, the complement of red which is RGB (255, 0, 0) is cyan which is RGB (0, 255, 255).
56    ///
57    /// Primary colors and secondary colors are typically paired in this way:
58    /// * red and cyan (where cyan is the mixture of green and blue)
59    /// * green and magenta (where magenta is the mixture of red and blue)
60    /// * blue and yellow (where yellow is the mixture of red and green)
61    ///
62    /// # Xml example
63    ///
64    /// ```xml
65    /// <a:solidFill>
66    ///   <a:srgbClr val="FF0000">
67    ///     <a:comp/>
68    ///   </a:srgbClr>
69    /// </a:solidFill>
70    /// ```
71    Complement,
72
73    /// This element specifies the inverse of its input color.
74    ///
75    /// # Xml example
76    ///
77    /// The inverse of red (1, 0, 0) is cyan (0, 1, 1).
78    ///
79    /// The following represents cyan, the inverse of red:
80    /// ```xml
81    /// <a:solidFill>
82    ///   <a:srgbClr val="FF0000">
83    ///     <a:inv/>
84    ///   </a:srgbClr>
85    /// </a:solidFill>
86    /// ```
87    Inverse,
88
89    /// This element specifies a grayscale of its input color, taking into relative intensities of the red, green, and blue
90    /// primaries.
91    Grayscale,
92
93    /// This element specifies its input color with the specific opacity, but with its color unchanged.
94    ///
95    /// # Xml example
96    ///
97    /// The following represents a green solid fill which is 50% opaque
98    /// ```xml
99    /// <a:solidFill>
100    ///   <a:srgbClr val="00FF00">
101    ///     <a:alpha val="50000"/>
102    ///   </a:srgbClr>
103    /// </a:solidFill>
104    /// ```
105    Alpha(PositiveFixedPercentage),
106
107    /// This element specifies a more or less opaque version of its input color. Increases or decreases the input alpha
108    /// percentage by the specified percentage offset. A 10% alpha offset increases a 50% opacity to 60%. A -10% alpha
109    /// offset decreases a 50% opacity to 40%. The transformed alpha values are limited to a range of 0 to 100%. A 10%
110    /// alpha offset increase to a 100% opaque object still results in 100% opacity.
111    ///
112    /// # Xml example
113    ///
114    /// The following represents a green solid fill which is 90% opaque
115    /// ```xml
116    /// <a:solidFill>
117    ///   <a:srgbClr val="00FF00">
118    ///     <a:alphaOff val="-10000"/>
119    ///   </a:srgbClr>
120    /// </a:solidFill>
121    /// ```
122    AlphaOffset(FixedPercentage),
123
124    /// This element specifies a more or less opaque version of its input color. An alpha modulate never increases the
125    /// alpha beyond 100%. A 200% alpha modulate makes a input color twice as opaque as before. A 50% alpha
126    /// modulate makes a input color half as opaque as before.
127    ///
128    /// # Xml example
129    ///
130    /// The following represents a green solid fill which is 50% opaque
131    /// ```xml
132    /// <a:solidFill>
133    ///   <a:srgbClr val="00FF00">
134    ///     <a:alphaMod val="50000"/>
135    ///   </a:srgbClr>
136    /// </a:solidFill>
137    /// ```
138    AlphaModulate(PositivePercentage),
139
140    /// This element specifies the input color with the specified hue, but with its saturation and luminance unchanged.
141    ///
142    /// # Xml example
143    ///
144    /// The following two solid fills are equivalent.
145    /// ```xml
146    /// <a:solidFill>
147    ///   <a:hslClr hue="14400000" sat="100000" lum="50000">
148    /// </a:solidFill>
149    /// <a:solidFill>
150    ///   <a:hslClr hue="0" sat="100000" lum="50000">
151    ///     <a:hue val="14400000"/>
152    ///   <a:hslClr/>
153    /// </a:solidFill>
154    /// ```
155    Hue(PositiveFixedAngle),
156
157    /// This element specifies the input color with its hue shifted, but with its saturation and luminance unchanged.
158    ///
159    /// # Xml example
160    ///
161    /// The following increases the hue angular value by 10 degrees.
162    /// ```xml
163    /// <a:solidFill>
164    ///   <a:hslClr hue="0" sat="100000" lum="50000"/>
165    ///   <a:hueOff val="600000"/>
166    /// </a:solidFill>
167    /// ```
168    HueOffset(Angle),
169
170    /// This element specifies the input color with its hue modulated by the given percentage. A 50% hue modulate
171    /// decreases the angular hue value by half. A 200% hue modulate doubles the angular hue value.
172    ///
173    /// # Xml example
174    ///
175    /// ```xml
176    /// <a:solidFill>
177    ///   <a:hslClr hue="14400000" sat="100000" lum="50000">
178    ///     <a:hueMod val="50000"/>
179    ///   </a:hslClr>
180    /// </a:solidFill>
181    /// ```
182    HueModulate(PositivePercentage),
183
184    /// This element specifies the input color with the specified saturation, but with its hue and luminance unchanged.
185    /// Typically saturation values fall in the range [0%, 100%].
186    ///
187    /// # Xml example
188    ///
189    /// The following two solid fills are equivalent:
190    /// ```xml
191    /// <a:solidFill>
192    ///   <a:hslClr hue="14400000" sat="100000" lum="50000">
193    /// </a:solidFill>
194    /// <a:solidFill>
195    ///   <a:hslClr hue="14400000" sat="0" lum="50000">
196    ///     <a:sat val="100000"/>
197    ///   <a:hslClr/>
198    /// </a:solidFill>
199    /// ```
200    Saturation(Percentage),
201
202    /// This element specifies the input color with its saturation shifted, but with its hue and luminance unchanged. A
203    /// 10% offset to 20% saturation yields 30% saturation.
204    ///
205    /// # Xml example
206    ///
207    /// The following manipulates the fill from having RGB value RRGGBB = (00, FF, 00)
208    /// to value RRGGBB= (19, E5, 19)
209    /// ```xml
210    /// <a:solidFill>
211    ///   <a:srgbClr val="00FF00">
212    ///     <a:satOff val="-20000"/>
213    ///   </a:srgbClr>
214    /// </a:solidFill>
215    /// ```
216    SaturationOffset(Percentage),
217
218    /// This element specifies the input color with its saturation modulated by the given percentage. A 50% saturation
219    /// modulate reduces the saturation by half. A 200% saturation modulate doubles the saturation.
220    ///
221    /// # Xml example
222    ///
223    /// The following manipulates the fill from having RGB value RRGGBB = (00, FF, 00)
224    /// to value RRGGBB= (66, 99, 66)
225    /// ```xml
226    /// <a:solidFill>
227    ///   <a:srgbClr val="00FF00">
228    ///     <a:satMod val="20000"/>
229    ///   </a:srgbClr>
230    /// </a:solidFill>
231    /// ```
232    SaturationModulate(Percentage),
233
234    /// This element specifies the input color with the specified luminance, but with its hue and saturation unchanged.
235    /// Typically luminance values fall in the range [0%, 100%].
236    ///
237    /// # Xml example
238    ///
239    /// The following two solid fills are equivalent:
240    /// ```xml
241    /// <a:solidFill>
242    ///   <a:hslClr hue="14400000" sat="100000" lum="50000">
243    /// </a:solidFill>
244    /// <a:solidFill>
245    ///   <a:hslClr hue="14400000" sat="100000" lum="0">
246    ///     <a:lum val="50000"/>
247    ///   <a:hslClr/>
248    /// </a:solidFill>
249    /// ```
250    Luminance(Percentage),
251
252    /// This element specifies the input color with its luminance shifted, but with its hue and saturation unchanged.
253    ///
254    /// # Xml example
255    ///
256    /// The following manipulates the fill from having RGB value RRGGBB = (00, FF, 00)
257    /// to value RRGGBB= (00, 99, 00)
258    /// ```xml
259    /// <a:solidFill>
260    ///   <a:srgbClr val="00FF00">
261    ///     <a:lumOff val="-20000"/>
262    ///   </a:srgbClr>
263    /// </a:solidFill>
264    /// ```
265    LuminanceOffset(Percentage),
266
267    /// This element specifies the input color with its luminance modulated by the given percentage. A 50% luminance
268    /// modulate reduces the luminance by half. A 200% luminance modulate doubles the luminance.
269    ///
270    /// # Xml example
271    ///
272    /// The following manipulates the fill from having RGB value RRGGBB = (00, FF, 00)
273    /// to value RRGGBB= (00, 75, 00)
274    /// ```xml
275    /// <a:solidFill>
276    ///   <a:srgbClr val="00FF00">
277    ///     <a:lumMod val="50000"/>
278    ///   </a:srgbClr>
279    /// </a:solidFill>
280    /// ```
281    LuminanceModulate(Percentage),
282
283    /// This element specifies the input color with the specified red component, but with its green and blue color
284    /// components unchanged.
285    ///
286    /// # Xml example
287    ///
288    /// The following manipulates the fill from having RGB value RRGGBB = (00, FF, 00)
289    /// to value RRGGBB= (FF, FF, 00)
290    /// ```xml
291    /// <a:solidFill>
292    ///   <a:srgbClr val="00FF00">
293    ///     <a:red val="100000"/>
294    ///   </a:srgbClr>
295    /// </a:solidFill>
296    /// ```
297    Red(Percentage),
298
299    /// This element specifies the input color with its red component shifted, but with its green and blue color
300    /// components unchanged.
301    ///
302    /// # Xml example
303    ///
304    /// The following manipulates the fill from having RGB value RRGGBB = (FF, 00, 00)
305    /// to value RRGGBB= (CC, 00, 00)
306    /// ```xml
307    /// <a:solidFill>
308    ///   <a:srgbClr val="FF0000">
309    ///     <a:redOff val="-20000"/>
310    ///   </a:srgbClr>
311    /// </a:solidFill>
312    /// ```
313    RedOffset(Percentage),
314
315    /// This element specifies the input color with its red component modulated by the given percentage. A 50% red
316    /// modulate reduces the red component by half. A 200% red modulate doubles the red component.
317    ///
318    /// # Xml example
319    ///
320    /// The following manipulates the fill from having RGB value RRGGBB = (FF, 00, 00)
321    /// to value RRGGBB= (80, 00, 00)
322    /// ```xml
323    /// <a:solidFill>
324    ///   <a:srgbClr val="FF0000">
325    ///     <a:redMod val="50000"/>
326    ///   </a:srgbClr>
327    /// </a:solidFill>
328    /// ```
329    RedModulate(Percentage),
330
331    /// This elements specifies the input color with the specified green component, but with its red and blue color
332    /// components unchanged.
333    ///
334    /// # Xml example
335    ///
336    /// The following manipulates the fill from having RGB value RRGGBB = (00, 00, FF)
337    /// to value RRGGBB= (00, FF, FF)
338    /// ```xml
339    /// <a:solidFill>
340    ///   <a:srgbClr val="0000FF">
341    ///     <a:green val="100000"/>
342    ///   </a:srgbClr>
343    /// </a:solidFill>
344    /// ```
345    Green(Percentage),
346
347    /// This element specifies the input color with its green component shifted, but with its red and blue color
348    /// components unchanged.
349    ///
350    /// # Xml example
351    ///
352    /// The following manipulates the fill from having RGB value RRGGBB = (00, FF, 00)
353    /// to value RRGGBB= (00, CC, 00).
354    /// ```xml
355    /// <a:solidFill>
356    ///   <a:srgbClr val="00FF00">
357    ///     <a:greenOff val="-20000"/>
358    ///   </a:srgbClr>
359    /// </a:solidFill>
360    /// ```
361    GreenOffset(Percentage),
362
363    /// This element specifies the input color with its green component modulated by the given percentage. A 50%
364    /// green modulate reduces the green component by half. A 200% green modulate doubles the green component.
365    ///
366    /// # Xml example
367    ///
368    /// The following manipulates the fill from having RGB value RRGGBB = (00, FF, 00)
369    /// to value RRGGBB= (00, 80, 00)
370    /// ```xml
371    /// <a:solidFill>
372    ///   <a:srgbClr val="00FF00">
373    ///     <a:greenMod val="50000"/>
374    ///   </a:srgbClr>
375    /// </a:solidFill>
376    /// ```
377    GreenModulate(Percentage),
378
379    /// This element specifies the input color with the specific blue component, but with the red and green color
380    /// components unchanged.
381    ///
382    /// # Xml example
383    ///
384    /// The following manipulates the fill from having RGB value RRGGBB = (00, FF, 00)
385    /// to value RRGGBB= (00, FF, FF)
386    /// ```xml
387    /// <a:solidFill>
388    ///   <a:srgbClr val="00FF00">
389    ///     <a:blue val="100000"/>
390    ///   </a:srgbClr>
391    /// </a:solidFill>
392    /// ```
393    Blue(Percentage),
394
395    /// This element specifies the input color with its blue component shifted, but with its red and green color
396    /// components unchanged.
397    ///
398    /// # Xml example
399    ///
400    /// The following manipulates the fill from having RGB value RRGGBB = (00, 00, FF)
401    /// to value RRGGBB= (00, 00, CC)
402    /// ```xml
403    /// <a:solidFill>
404    ///   <a:srgbClr val="00FF00">
405    ///     <a:blueOff val="-20000"/>
406    ///   </a:srgbClr>
407    /// </a:solidFill>
408    /// ```
409    BlueOffset(Percentage),
410
411    /// This element specifies the input color with its blue component modulated by the given percentage. A 50% blue
412    /// modulate reduces the blue component by half. A 200% blue modulate doubles the blue component.
413    ///
414    /// # Xml example
415    ///
416    /// The following manipulates the fill from having RGB value RRGGBB = (00, 00, FF)
417    /// to value RRGGBB= (00, 00, 80)
418    /// ```xml
419    /// <a:solidFill>
420    ///   <a:srgbClr val="0000FF">
421    ///     <a:blueMod val="50000"/>
422    ///   </a:srgbClr>
423    /// </a:solidFill>
424    /// ```
425    BlueModulate(Percentage),
426
427    /// This element specifies that the output color rendered by the generating application should be the sRGB gamma
428    /// shift of the input color.
429    Gamma,
430
431    /// This element specifies that the output color rendered by the generating application should be the inverse sRGB
432    /// gamma shift of the input color.
433    InverseGamma,
434}
435
436impl XsdType for ColorTransform {
437    fn from_xml_element(xml_node: &XmlNode) -> Result<ColorTransform> {
438        match xml_node.local_name() {
439            "tint" => Ok(ColorTransform::Tint(xml_node.parse_val_attribute()?)),
440            "shade" => Ok(ColorTransform::Shade(xml_node.parse_val_attribute()?)),
441            "comp" => Ok(ColorTransform::Complement),
442            "inv" => Ok(ColorTransform::Inverse),
443            "gray" => Ok(ColorTransform::Grayscale),
444            "alpha" => Ok(ColorTransform::Alpha(xml_node.parse_val_attribute()?)),
445            "alphaOff" => Ok(ColorTransform::AlphaOffset(xml_node.parse_val_attribute()?)),
446            "alphaMod" => Ok(ColorTransform::AlphaModulate(xml_node.parse_val_attribute()?)),
447            "hue" => Ok(ColorTransform::Hue(xml_node.parse_val_attribute()?)),
448            "hueOff" => Ok(ColorTransform::HueOffset(xml_node.parse_val_attribute()?)),
449            "hueMod" => Ok(ColorTransform::HueModulate(xml_node.parse_val_attribute()?)),
450            "sat" => Ok(ColorTransform::Saturation(xml_node.parse_val_attribute()?)),
451            "satOff" => Ok(ColorTransform::SaturationOffset(xml_node.parse_val_attribute()?)),
452            "satMod" => Ok(ColorTransform::SaturationModulate(xml_node.parse_val_attribute()?)),
453            "lum" => Ok(ColorTransform::Luminance(xml_node.parse_val_attribute()?)),
454            "lumOff" => Ok(ColorTransform::LuminanceOffset(xml_node.parse_val_attribute()?)),
455            "lumMod" => Ok(ColorTransform::LuminanceModulate(xml_node.parse_val_attribute()?)),
456            "red" => Ok(ColorTransform::Red(xml_node.parse_val_attribute()?)),
457            "redOff" => Ok(ColorTransform::RedOffset(xml_node.parse_val_attribute()?)),
458            "redMod" => Ok(ColorTransform::RedModulate(xml_node.parse_val_attribute()?)),
459            "green" => Ok(ColorTransform::Green(xml_node.parse_val_attribute()?)),
460            "greenOff" => Ok(ColorTransform::GreenOffset(xml_node.parse_val_attribute()?)),
461            "greenMod" => Ok(ColorTransform::GreenModulate(xml_node.parse_val_attribute()?)),
462            "blue" => Ok(ColorTransform::Blue(xml_node.parse_val_attribute()?)),
463            "blueOff" => Ok(ColorTransform::BlueOffset(xml_node.parse_val_attribute()?)),
464            "blueMod" => Ok(ColorTransform::BlueModulate(xml_node.parse_val_attribute()?)),
465            "gamma" => Ok(ColorTransform::Gamma),
466            "invGamma" => Ok(ColorTransform::InverseGamma),
467            _ => Err(NotGroupMemberError::new(xml_node.name.clone(), "EG_ColorTransform").into()),
468        }
469    }
470}
471
472impl XsdChoice for ColorTransform {
473    fn is_choice_member<T: AsRef<str>>(name: T) -> bool {
474        match name.as_ref() {
475            "tint" | "shade" | "comp" | "inv" | "gray" | "alpha" | "alphaOff" | "alphaMod" | "hue" | "hueOff"
476            | "hueMod" | "sat" | "satOff" | "satMod" | "lum" | "lumOff" | "lumMod" | "red" | "redOff" | "redMod"
477            | "green" | "greenOff" | "greenMod" | "blue" | "blueOff" | "blueMod" | "gamma" | "invGamma" => true,
478            _ => false,
479        }
480    }
481}
482
483#[derive(Debug, Clone, PartialEq)]
484pub struct ScRgbColor {
485    /// Specifies the percentage of red.
486    pub r: Percentage,
487
488    /// Specifies the percentage of green.
489    pub g: Percentage,
490
491    /// Specifies the percentage of blue.
492    pub b: Percentage,
493
494    /// Color transforms to apply to this color
495    pub color_transforms: Vec<ColorTransform>,
496}
497
498impl ScRgbColor {
499    pub fn from_xml_element(xml_node: &XmlNode) -> Result<ScRgbColor> {
500        let mut opt_r = None;
501        let mut opt_g = None;
502        let mut opt_b = None;
503
504        for (attr, value) in &xml_node.attributes {
505            match attr.as_str() {
506                "r" => opt_r = Some(value.parse()?),
507                "g" => opt_g = Some(value.parse()?),
508                "b" => opt_b = Some(value.parse()?),
509                _ => (),
510            }
511        }
512
513        let r = opt_r.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "r"))?;
514        let g = opt_g.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "g"))?;
515        let b = opt_b.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "b"))?;
516
517        let color_transforms = xml_node
518            .child_nodes
519            .iter()
520            .filter_map(ColorTransform::try_from_xml_element)
521            .collect::<Result<Vec<_>>>()?;
522
523        Ok(Self {
524            r,
525            g,
526            b,
527            color_transforms,
528        })
529    }
530}
531
532#[derive(Debug, Clone, PartialEq)]
533pub struct SRgbColor {
534    pub value: u32,
535
536    /// Color transforms to apply to this color
537    pub color_transforms: Vec<ColorTransform>,
538}
539
540impl SRgbColor {
541    pub fn from_xml_element(xml_node: &XmlNode) -> Result<SRgbColor> {
542        let value = xml_node
543            .attributes
544            .get("val")
545            .ok_or_else(|| Box::<dyn Error>::from(MissingAttributeError::new(xml_node.name.clone(), "val")))
546            .and_then(|value| u32::from_str_radix(value, 16).map_err(Box::from))?;
547
548        let color_transforms = xml_node
549            .child_nodes
550            .iter()
551            .filter_map(ColorTransform::try_from_xml_element)
552            .collect::<Result<Vec<_>>>()?;
553
554        Ok(Self {
555            value,
556            color_transforms,
557        })
558    }
559}
560
561#[derive(Debug, Clone, PartialEq)]
562pub struct HslColor {
563    /// Specifies the angular value describing the wavelength. Expressed in 1/6000ths of a
564    /// degree.
565    pub hue: PositiveFixedAngle,
566
567    /// Specifies the saturation referring to the purity of the hue. Expressed as a percentage with
568    /// 0% referring to grey, 100% referring to the purest form of the hue.
569    pub saturation: Percentage,
570
571    /// Specifies the luminance referring to the lightness or darkness of the color. Expressed as a
572    /// percentage with 0% referring to maximal dark (black) and 100% referring to maximal
573    /// white.
574    pub luminance: Percentage,
575
576    /// Color transforms to apply to this color
577    pub color_transforms: Vec<ColorTransform>,
578}
579
580impl HslColor {
581    pub fn from_xml_element(xml_node: &XmlNode) -> Result<HslColor> {
582        let mut opt_h = None;
583        let mut opt_s = None;
584        let mut opt_l = None;
585
586        for (attr, value) in &xml_node.attributes {
587            match attr.as_str() {
588                "hue" => opt_h = Some(value.parse::<PositiveFixedAngle>()?),
589                "sat" => opt_s = Some(value.parse()?),
590                "lum" => opt_l = Some(value.parse()?),
591                _ => (),
592            }
593        }
594
595        let hue = opt_h.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "hue"))?;
596        let saturation = opt_s.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "sat"))?;
597        let luminance = opt_l.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "lum"))?;
598
599        let color_transforms = xml_node
600            .child_nodes
601            .iter()
602            .filter_map(ColorTransform::try_from_xml_element)
603            .collect::<Result<Vec<_>>>()?;
604
605        Ok(Self {
606            hue,
607            saturation,
608            luminance,
609            color_transforms,
610        })
611    }
612}
613
614#[derive(Debug, Clone, PartialEq)]
615pub struct SystemColor {
616    /// Specifies the system color value.
617    pub value: SystemColorVal,
618
619    /// Specifies the color value that was last computed by the generating application.
620    pub last_color: Option<HexColorRGB>,
621
622    /// Color transforms to apply to this color
623    pub color_transforms: Vec<ColorTransform>,
624}
625
626impl SystemColor {
627    pub fn from_xml_element(xml_node: &XmlNode) -> Result<SystemColor> {
628        let mut opt_val = None;
629        let mut last_color = None;
630
631        for (attr, value) in &xml_node.attributes {
632            match attr.as_str() {
633                "val" => opt_val = Some(value.parse()?),
634                "lastClr" => last_color = Some(parse_hex_color_rgb(value)?),
635                _ => (),
636            }
637        }
638
639        let value = opt_val.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "val"))?;
640
641        let color_transforms = xml_node
642            .child_nodes
643            .iter()
644            .filter_map(ColorTransform::try_from_xml_element)
645            .collect::<Result<Vec<_>>>()?;
646
647        Ok(Self {
648            value,
649            last_color,
650            color_transforms,
651        })
652    }
653}
654
655#[derive(Debug, Clone, PartialEq)]
656pub struct PresetColor {
657    pub value: PresetColorVal,
658
659    /// Color transforms to apply to this color
660    pub color_transforms: Vec<ColorTransform>,
661}
662
663impl PresetColor {
664    pub fn from_xml_element(xml_node: &XmlNode) -> Result<PresetColor> {
665        let value = xml_node.parse_val_attribute()?;
666
667        let color_transforms = xml_node
668            .child_nodes
669            .iter()
670            .filter_map(ColorTransform::try_from_xml_element)
671            .collect::<Result<Vec<_>>>()?;
672
673        Ok(Self {
674            value,
675            color_transforms,
676        })
677    }
678}
679
680#[derive(Debug, Clone, PartialEq)]
681pub struct SchemeColor {
682    pub value: SchemeColorVal,
683
684    /// Color transforms to apply to this color
685    pub color_transforms: Vec<ColorTransform>,
686}
687
688impl SchemeColor {
689    pub fn from_xml_element(xml_node: &XmlNode) -> Result<SchemeColor> {
690        let value = xml_node
691            .attributes
692            .get("val")
693            .ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "val"))?
694            .parse()?;
695
696        let color_transforms = xml_node
697            .child_nodes
698            .iter()
699            .filter_map(ColorTransform::try_from_xml_element)
700            .collect::<Result<Vec<_>>>()?;
701
702        Ok(Self {
703            value,
704            color_transforms,
705        })
706    }
707}
708
709#[derive(Debug, Clone, PartialEq)]
710pub enum Color {
711    /// This element specifies a color using the red, green, blue RGB color model. Each component, red, green, and blue
712    /// is expressed as a percentage from 0% to 100%. A linear gamma of 1.0 is assumed.
713    ///
714    /// Specifies the level of red as expressed by a percentage offset increase or decrease relative to the input color.
715    ///
716    /// # Xml example
717    ///
718    /// The following represent the same color
719    /// ```xml
720    /// <a:solidFill>
721    ///   <a:scrgbClr r="50000" g="50000" b="50000"/>
722    /// </a:solidFill>
723    /// <a:solidFill>
724    ///   <a:srgbClr val="BCBCBC"/>
725    /// </a:solidFill>
726    /// ```
727    ScRgbColor(ScRgbColor),
728
729    /// This element specifies a color using the red, green, blue RGB color model. Red, green, and blue is expressed as
730    /// sequence of hex digits, RRGGBB. A perceptual gamma of 2.2 is used.
731    ///
732    /// Specifies the level of red as expressed by a percentage offset increase or decrease relative to the input color.
733    ///
734    /// # Xml example
735    ///
736    /// The following represent the same color
737    /// ```xml
738    /// <a:solidFill>
739    ///   <a:scrgbClr r="50000" g="50000" b="50000"/>
740    /// </a:solidFill>
741    /// <a:solidFill>
742    ///   <a:srgbClr val="BCBCBC"/>
743    /// </a:solidFill>
744    /// ```
745    SRgbColor(SRgbColor),
746
747    /// This element specifies a color using the HSL color model. A perceptual gamma of 2.2 is assumed.
748    ///
749    /// Hue refers to the dominant wavelength of color, saturation refers to the purity of its hue, and luminance refers
750    /// to its lightness or darkness.
751    ///
752    /// As with all colors, colors defined with the HSL color model can have color transforms applied to it.
753    ///
754    /// # Xml example
755    ///
756    /// The color blue having RGB value RRGGBB = (00, 00, 80) is equivalent to
757    /// ```xml
758    /// <a:solidFill>
759    ///   <a:hslClr hue="14400000" sat="100000" lum="50000">
760    /// </a:solidFill>
761    /// ```
762    HslColor(HslColor),
763
764    /// This element specifies a color bound to predefined operating system elements.
765    ///
766    /// # Xml example
767    ///
768    /// The following represents the default color used for displaying text in a window.
769    /// ```xml
770    /// <a:solidFill>
771    ///   <a:sysClr val="windowText"/>
772    /// </a:solidFill>
773    /// ```
774    SystemColor(SystemColor),
775
776    /// This element specifies a color bound to a user's theme. As with all elements which define a color, it is possible to
777    /// apply a list of color transforms to the base color defined.
778    ///
779    /// # Xml example
780    ///
781    /// <a:solidFill>
782    ///   <a:schemeClr val="lt1"/>
783    /// </a:solidFill>
784    SchemeColor(SchemeColor),
785
786    /// This element specifies a color which is bound to one of a predefined collection of colors.
787    ///
788    /// # Xml example
789    ///
790    /// The following defines a solid fill bound to the "black" preset color.
791    /// <a:solidFill>
792    ///   <a:prstClr val="black">
793    /// </a:solidFill>
794    PresetColor(PresetColor),
795}
796
797impl XsdType for Color {
798    fn from_xml_element(xml_node: &XmlNode) -> Result<Color> {
799        match xml_node.local_name() {
800            "scrgbClr" => Ok(Color::ScRgbColor(ScRgbColor::from_xml_element(xml_node)?)),
801            "srgbClr" => Ok(Color::SRgbColor(SRgbColor::from_xml_element(xml_node)?)),
802            "hslClr" => Ok(Color::HslColor(HslColor::from_xml_element(xml_node)?)),
803            "sysClr" => Ok(Color::SystemColor(SystemColor::from_xml_element(xml_node)?)),
804            "schemeClr" => Ok(Color::SchemeColor(SchemeColor::from_xml_element(xml_node)?)),
805            "prstClr" => Ok(Color::PresetColor(PresetColor::from_xml_element(xml_node)?)),
806            _ => Err(NotGroupMemberError::new(xml_node.name.clone(), "EG_ColorChoice").into()),
807        }
808    }
809}
810
811impl XsdChoice for Color {
812    fn is_choice_member<T: AsRef<str>>(name: T) -> bool {
813        match name.as_ref() {
814            "scrgbClr" | "srgbClr" | "hslClr" | "sysClr" | "schemeClr" | "prstClr" => true,
815            _ => false,
816        }
817    }
818}
819/// This element defines a custom color. The custom colors are used within a custom color list to define custom
820/// colors that are extra colors that can be appended to a theme. This is useful within corporate scenarios where
821/// there is a set corporate color palette from which to work.
822#[derive(Debug, Clone, PartialEq)]
823pub struct CustomColor {
824    /// The name of the color shown in the color picker.
825    pub name: Option<String>,
826
827    /// The color represented by this custom color.
828    pub color: Color,
829}
830
831impl CustomColor {
832    pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
833        let name = xml_node.attributes.get("name").cloned();
834        let color = xml_node
835            .child_nodes
836            .iter()
837            .find(|child_node| Color::is_choice_member(child_node.local_name()))
838            .ok_or_else(|| Box::<dyn Error>::from(MissingChildNodeError::new(xml_node.name.clone(), "EG_ColorChoice")))
839            .and_then(Color::from_xml_element)?;
840
841        Ok(Self { name, color })
842    }
843}
844
845#[derive(Debug, Clone, PartialEq)]
846pub enum ColorMappingOverride {
847    /// This element is a part of a choice for which color mapping is used within the document.
848    /// If this element is specified, then we specifically use the color mapping defined in the master.
849    UseMaster,
850
851    /// This element provides an override for the color mapping in a document. When defined, this color mapping is
852    /// used in place of the already defined color mapping, or master color mapping. This color mapping is defined in
853    /// the same manner as the other mappings within this document.
854    ///
855    /// # Xml example
856    ///
857    /// ```xml
858    /// <overrideClrMapping bg1="lt1" tx1="dk1" bg2="lt2" tx2="dk2" accent1="accent1"
859    ///   accent2="accent2" accent3="accent3" accent4="accent4" accent5="accent5"
860    ///   accent6="accent6" hlink="hlink" folHlink="folHlink"/>
861    /// ```
862    Override(Box<ColorMapping>),
863}
864
865impl XsdType for ColorMappingOverride {
866    fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
867        match xml_node.local_name() {
868            "masterClrMapping" => Ok(ColorMappingOverride::UseMaster),
869            "overrideClrMapping" => Ok(ColorMappingOverride::Override(Box::new(
870                ColorMapping::from_xml_element(xml_node)?,
871            ))),
872            _ => Err(NotGroupMemberError::new(xml_node.name.clone(), "CT_ColorMappingOverride").into()),
873        }
874    }
875}
876
877impl XsdChoice for ColorMappingOverride {
878    fn is_choice_member<T: AsRef<str>>(name: T) -> bool {
879        match name.as_ref() {
880            "masterClrMapping" | "overrideClrMapping" => true,
881            _ => false,
882        }
883    }
884}