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}