Skip to main content

oxml_drawing/
fill.rs

1use std::fmt;
2use std::io::Write;
3
4use oxml_core::OxmlError;
5use oxml_core::raw_xml::{capture_element, capture_empty_element};
6use oxml_core::units::{Angle, Percent1000};
7use oxml_core::xml::{R_NS, get_attr, local_name, matches_local_name};
8use quick_xml::events::{BytesEnd, BytesStart, Event};
9use quick_xml::{Reader, Writer, XmlVersion};
10
11use crate::color::{ColorChoice, ColorError};
12use crate::namespace::reject_conflicting_a_prefix;
13use crate::order::OrderedRawChildren;
14
15/// Errors produced while parsing or writing DrawingML fills.
16#[derive(Debug)]
17pub enum FillError {
18    Xml(OxmlError),
19    Color(ColorError),
20    UnexpectedElement(String),
21    MissingAttribute {
22        element: String,
23        attribute: String,
24    },
25    InvalidAttribute {
26        element: String,
27        attribute: String,
28        value: String,
29    },
30    GradientStopOutOfRange(i32),
31}
32
33impl fmt::Display for FillError {
34    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
35        match self {
36            Self::Xml(error) => error.fmt(formatter),
37            Self::Color(error) => error.fmt(formatter),
38            Self::UnexpectedElement(element) => {
39                write!(formatter, "unexpected DrawingML fill element: {element}")
40            }
41            Self::MissingAttribute { element, attribute } => {
42                write!(formatter, "DrawingML {element} requires @{attribute}")
43            }
44            Self::InvalidAttribute {
45                element,
46                attribute,
47                value,
48            } => write!(
49                formatter,
50                "DrawingML {element} has invalid @{attribute}: {value}"
51            ),
52            Self::GradientStopOutOfRange(value) => {
53                write!(
54                    formatter,
55                    "DrawingML gradient stop is outside 0 to 100000: {value}"
56                )
57            }
58        }
59    }
60}
61
62impl std::error::Error for FillError {
63    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
64        match self {
65            Self::Xml(error) => Some(error),
66            Self::Color(error) => Some(error),
67            _ => None,
68        }
69    }
70}
71
72impl From<OxmlError> for FillError {
73    fn from(error: OxmlError) -> Self {
74        Self::Xml(error)
75    }
76}
77
78impl From<ColorError> for FillError {
79    fn from(error: ColorError) -> Self {
80        Self::Color(error)
81    }
82}
83
84pub type Result<T> = std::result::Result<T, FillError>;
85
86/// One of the five DrawingML fill families.
87#[derive(Clone, Debug, Eq, PartialEq)]
88pub enum Fill {
89    NoFill(NoFill),
90    Solid(SolidFill),
91    Gradient(GradientFill),
92    Pattern(PatternFill),
93    Blip(BlipFill),
94}
95
96impl Fill {
97    /// Parses one complete fill element with any namespace prefix.
98    pub fn from_xml(xml: &[u8]) -> Result<Self> {
99        let mut reader = Reader::from_reader(xml);
100        let mut buffer = Vec::new();
101        loop {
102            match reader
103                .read_event_into(&mut buffer)
104                .map_err(OxmlError::from)?
105            {
106                Event::Start(element) => return Self::from_element(&mut reader, &element),
107                Event::Empty(element) => return Self::from_empty_element(&element),
108                Event::Eof => {
109                    return Err(FillError::Xml(OxmlError::MissingElement(
110                        "DrawingML fill".to_owned(),
111                    )));
112                }
113                _ => {}
114            }
115            buffer.clear();
116        }
117    }
118
119    /// Parses a fill after the caller has consumed its start event.
120    pub fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
121        reject_conflicting_a_prefix(start)?;
122        match local_name(start.name().as_ref()) {
123            b"noFill" => Ok(Self::NoFill(NoFill::from_element(reader, start)?)),
124            b"solidFill" => Ok(Self::Solid(SolidFill::from_element(reader, start)?)),
125            b"gradFill" => Ok(Self::Gradient(GradientFill::from_element(reader, start)?)),
126            b"pattFill" => Ok(Self::Pattern(PatternFill::from_element(reader, start)?)),
127            b"blipFill" => Ok(Self::Blip(BlipFill::from_element(reader, start)?)),
128            _ => Err(unexpected(start)),
129        }
130    }
131
132    /// Parses a self-closing fill element.
133    pub fn from_empty_element(start: &BytesStart<'_>) -> Result<Self> {
134        reject_conflicting_a_prefix(start)?;
135        match local_name(start.name().as_ref()) {
136            b"noFill" => Ok(Self::NoFill(NoFill::default())),
137            b"solidFill" => Ok(Self::Solid(SolidFill::default())),
138            b"gradFill" => Ok(Self::Gradient(GradientFill::from_start(start)?)),
139            b"pattFill" => Ok(Self::Pattern(PatternFill::from_start(start)?)),
140            b"blipFill" => Ok(Self::Blip(BlipFill::from_start(start)?)),
141            _ => Err(unexpected(start)),
142        }
143    }
144
145    /// Writes this fill with canonical DrawingML prefixes and schema order.
146    pub fn to_xml(&self) -> Result<Vec<u8>> {
147        let mut writer = Writer::new(Vec::new());
148        self.write_xml(&mut writer)?;
149        Ok(writer.into_inner())
150    }
151
152    /// Writes this fill into an existing XML writer.
153    pub fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
154        match self {
155            Self::NoFill(fill) => fill.write_xml(writer),
156            Self::Solid(fill) => fill.write_xml(writer),
157            Self::Gradient(fill) => fill.write_xml(writer),
158            Self::Pattern(fill) => fill.write_xml(writer),
159            Self::Blip(fill) => fill.write_xml(writer),
160        }
161    }
162}
163
164/// An `a:noFill` value, including preserved extension children.
165#[derive(Clone, Debug, Default, Eq, PartialEq)]
166pub struct NoFill {
167    raw_children: OrderedRawChildren,
168}
169
170impl NoFill {
171    fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
172        Ok(Self {
173            raw_children: capture_all_children(reader, local_name(start.name().as_ref()))?,
174        })
175    }
176
177    fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
178        if self.raw_children.is_empty() {
179            write_empty(writer, BytesStart::new("a:noFill"))
180        } else {
181            write_start(writer, BytesStart::new("a:noFill"))?;
182            emit_raw(writer, self.raw_children.at(0))?;
183            write_end(writer, "a:noFill")
184        }
185    }
186
187    pub fn raw_children(&self) -> &OrderedRawChildren {
188        &self.raw_children
189    }
190}
191
192/// A solid fill with its DrawingML colour choice.
193#[derive(Clone, Debug, Default, Eq, PartialEq)]
194pub struct SolidFill {
195    pub color: Option<ColorChoice>,
196    raw_children: OrderedRawChildren,
197}
198
199impl SolidFill {
200    fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
201        let mut value = Self::default();
202        read_color_children(
203            reader,
204            local_name(start.name().as_ref()),
205            &mut value.color,
206            &mut value.raw_children,
207        )?;
208        Ok(value)
209    }
210
211    fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
212        if self.color.is_none() && self.raw_children.is_empty() {
213            return write_empty(writer, BytesStart::new("a:solidFill"));
214        }
215        write_start(writer, BytesStart::new("a:solidFill"))?;
216        emit_raw(writer, self.raw_children.at(0))?;
217        if let Some(color) = &self.color {
218            color.to_xml(writer)?;
219        }
220        emit_raw(writer, self.raw_children.at(1))?;
221        write_end(writer, "a:solidFill")
222    }
223
224    pub fn raw_children(&self) -> &OrderedRawChildren {
225        &self.raw_children
226    }
227}
228
229/// One gradient stop. Stops remain in document order.
230#[derive(Clone, Debug, Eq, PartialEq)]
231pub struct GradientStop {
232    pub position: Percent1000,
233    pub color: Option<ColorChoice>,
234    raw_children: OrderedRawChildren,
235}
236
237impl GradientStop {
238    fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
239        reject_conflicting_a_prefix(start)?;
240        let position = required_i32(start, b"pos")?;
241        validate_gradient_position(position)?;
242        let mut stop = Self {
243            position: Percent1000(position),
244            color: None,
245            raw_children: OrderedRawChildren::default(),
246        };
247        read_color_children(reader, b"gs", &mut stop.color, &mut stop.raw_children)?;
248        Ok(stop)
249    }
250
251    fn from_empty(start: &BytesStart<'_>) -> Result<Self> {
252        reject_conflicting_a_prefix(start)?;
253        let position = required_i32(start, b"pos")?;
254        validate_gradient_position(position)?;
255        Ok(Self {
256            position: Percent1000(position),
257            color: None,
258            raw_children: OrderedRawChildren::default(),
259        })
260    }
261
262    fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
263        validate_gradient_position(self.position.0)?;
264        let position = self.position.0.to_string();
265        let mut start = BytesStart::new("a:gs");
266        start.push_attribute(("pos", position.as_str()));
267        if self.color.is_none() && self.raw_children.is_empty() {
268            return write_empty(writer, start);
269        }
270        write_start(writer, start)?;
271        emit_raw(writer, self.raw_children.at(0))?;
272        if let Some(color) = &self.color {
273            color.to_xml(writer)?;
274        }
275        emit_raw(writer, self.raw_children.at(1))?;
276        write_end(writer, "a:gs")
277    }
278}
279
280/// Gradient geometry for either a linear or path gradient.
281#[derive(Clone, Debug, Eq, PartialEq)]
282pub enum GradientGeometry {
283    Linear(LinearGradient),
284    Path(PathGradient),
285}
286
287/// Linear gradient angle and scaling behavior.
288#[derive(Clone, Debug, Default, Eq, PartialEq)]
289pub struct LinearGradient {
290    pub angle: Angle,
291    pub scaled: Option<bool>,
292    raw_children: OrderedRawChildren,
293}
294
295/// A DrawingML path-gradient shape.
296#[derive(Clone, Copy, Debug, Eq, PartialEq)]
297pub enum PathGradientKind {
298    Shape,
299    Circle,
300    Rectangle,
301}
302
303impl PathGradientKind {
304    fn parse(value: &str) -> Option<Self> {
305        match value {
306            "shape" => Some(Self::Shape),
307            "circle" => Some(Self::Circle),
308            "rect" => Some(Self::Rectangle),
309            _ => None,
310        }
311    }
312
313    const fn as_str(self) -> &'static str {
314        match self {
315            Self::Shape => "shape",
316            Self::Circle => "circle",
317            Self::Rectangle => "rect",
318        }
319    }
320}
321
322/// Path gradient shape and optional focal rectangle.
323#[derive(Clone, Debug, Eq, PartialEq)]
324pub struct PathGradient {
325    pub kind: PathGradientKind,
326    pub fill_to_rect: Option<RelativeRect>,
327    raw_children: OrderedRawChildren,
328}
329
330/// A rectangle expressed as optional percentages from each edge.
331#[derive(Clone, Debug, Default, Eq, PartialEq)]
332pub struct RelativeRect {
333    pub left: Option<Percent1000>,
334    pub top: Option<Percent1000>,
335    pub right: Option<Percent1000>,
336    pub bottom: Option<Percent1000>,
337    raw_children: OrderedRawChildren,
338}
339
340/// A gradient fill with stops in source order.
341#[derive(Clone, Debug, Default, Eq, PartialEq)]
342pub struct GradientFill {
343    pub flip: Option<String>,
344    pub rotate_with_shape: Option<bool>,
345    pub stops: Vec<GradientStop>,
346    pub geometry: Option<GradientGeometry>,
347    pub tile_rect: Option<RelativeRect>,
348    raw_children: OrderedRawChildren,
349    stop_list_raw_children: OrderedRawChildren,
350}
351
352impl GradientFill {
353    fn from_start(start: &BytesStart<'_>) -> Result<Self> {
354        reject_conflicting_a_prefix(start)?;
355        Ok(Self {
356            flip: optional_token(start, b"flip", &["none", "x", "y", "xy"])?,
357            rotate_with_shape: optional_bool(start, b"rotWithShape")?,
358            ..Self::default()
359        })
360    }
361
362    fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
363        let mut fill = Self::from_start(start)?;
364        let mut buffer = Vec::new();
365        let mut boundary = 0;
366        loop {
367            match reader
368                .read_event_into(&mut buffer)
369                .map_err(OxmlError::from)?
370            {
371                Event::Start(element) if matches_local_name(element.name().as_ref(), b"gsLst") => {
372                    reject_conflicting_a_prefix(&element)?;
373                    fill.read_stop_list(reader)?;
374                    boundary = 1;
375                }
376                Event::Start(element) if matches_local_name(element.name().as_ref(), b"lin") => {
377                    fill.geometry = Some(GradientGeometry::Linear(parse_linear_element(
378                        reader, &element,
379                    )?));
380                    boundary = 2;
381                }
382                Event::Empty(element) if matches_local_name(element.name().as_ref(), b"lin") => {
383                    fill.geometry = Some(GradientGeometry::Linear(parse_linear(&element)?));
384                    boundary = 2;
385                }
386                Event::Start(element) if matches_local_name(element.name().as_ref(), b"path") => {
387                    fill.geometry = Some(GradientGeometry::Path(parse_path(reader, &element)?));
388                    boundary = 2;
389                }
390                Event::Empty(element) if matches_local_name(element.name().as_ref(), b"path") => {
391                    fill.geometry = Some(GradientGeometry::Path(parse_empty_path(&element)?));
392                    boundary = 2;
393                }
394                Event::Start(element)
395                    if matches_local_name(element.name().as_ref(), b"tileRect") =>
396                {
397                    fill.tile_rect = Some(parse_rect_element(reader, &element)?);
398                    boundary = 3;
399                }
400                Event::Empty(element)
401                    if matches_local_name(element.name().as_ref(), b"tileRect") =>
402                {
403                    fill.tile_rect = Some(parse_rect(&element)?);
404                    boundary = 3;
405                }
406                Event::Start(element) => fill
407                    .raw_children
408                    .push(boundary, capture_element(reader, &element)?),
409                Event::Empty(element) => fill
410                    .raw_children
411                    .push(boundary, capture_empty_element(&element)?),
412                Event::End(element) if matches_local_name(element.name().as_ref(), b"gradFill") => {
413                    break;
414                }
415                Event::Eof => return Err(missing_end("gradFill")),
416                _ => {}
417            }
418            buffer.clear();
419        }
420        Ok(fill)
421    }
422
423    fn read_stop_list(&mut self, reader: &mut Reader<&[u8]>) -> Result<()> {
424        let mut buffer = Vec::new();
425        loop {
426            match reader
427                .read_event_into(&mut buffer)
428                .map_err(OxmlError::from)?
429            {
430                Event::Start(element) if matches_local_name(element.name().as_ref(), b"gs") => {
431                    self.stops
432                        .push(GradientStop::from_element(reader, &element)?);
433                }
434                Event::Empty(element) if matches_local_name(element.name().as_ref(), b"gs") => {
435                    self.stops.push(GradientStop::from_empty(&element)?);
436                }
437                Event::Start(element) => self
438                    .stop_list_raw_children
439                    .push(self.stops.len(), capture_element(reader, &element)?),
440                Event::Empty(element) => self
441                    .stop_list_raw_children
442                    .push(self.stops.len(), capture_empty_element(&element)?),
443                Event::End(element) if matches_local_name(element.name().as_ref(), b"gsLst") => {
444                    break;
445                }
446                Event::Eof => return Err(missing_end("gsLst")),
447                _ => {}
448            }
449            buffer.clear();
450        }
451        Ok(())
452    }
453
454    fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
455        let mut start = BytesStart::new("a:gradFill");
456        if let Some(flip) = self.flip.as_deref() {
457            start.push_attribute(("flip", flip));
458        }
459        let rotate = self.rotate_with_shape.map(bool_text);
460        if let Some(rotate) = rotate {
461            start.push_attribute(("rotWithShape", rotate));
462        }
463        if self.stops.is_empty()
464            && self.geometry.is_none()
465            && self.tile_rect.is_none()
466            && self.stop_list_raw_children.is_empty()
467            && self.raw_children.is_empty()
468        {
469            return write_empty(writer, start);
470        }
471        write_start(writer, start)?;
472        emit_raw(writer, self.raw_children.at(0))?;
473        if !self.stops.is_empty() || !self.stop_list_raw_children.is_empty() {
474            write_start(writer, BytesStart::new("a:gsLst"))?;
475            for boundary in 0..=self.stops.len() {
476                emit_raw(writer, self.stop_list_raw_children.at(boundary))?;
477                if let Some(stop) = self.stops.get(boundary) {
478                    stop.write_xml(writer)?;
479                }
480            }
481            write_end(writer, "a:gsLst")?;
482        }
483        emit_raw(writer, self.raw_children.at(1))?;
484        if let Some(geometry) = &self.geometry {
485            write_geometry(writer, geometry)?;
486        }
487        emit_raw(writer, self.raw_children.at(2))?;
488        if let Some(rect) = &self.tile_rect {
489            write_rect(writer, "a:tileRect", rect)?;
490        }
491        emit_raw(writer, self.raw_children.at(3))?;
492        write_end(writer, "a:gradFill")
493    }
494
495    pub fn raw_children(&self) -> &OrderedRawChildren {
496        &self.raw_children
497    }
498}
499
500/// Pattern fill foreground, background, and preset identifier.
501#[derive(Clone, Debug, Default, Eq, PartialEq)]
502pub struct PatternFill {
503    pub preset: Option<String>,
504    pub foreground: Option<ColorChoice>,
505    pub background: Option<ColorChoice>,
506    raw_children: OrderedRawChildren,
507    foreground_raw_children: OrderedRawChildren,
508    background_raw_children: OrderedRawChildren,
509}
510
511impl PatternFill {
512    fn from_start(start: &BytesStart<'_>) -> Result<Self> {
513        reject_conflicting_a_prefix(start)?;
514        Ok(Self {
515            preset: get_attr(start, b"prst"),
516            ..Self::default()
517        })
518    }
519
520    fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
521        let mut fill = Self::from_start(start)?;
522        let mut buffer = Vec::new();
523        let mut boundary = 0;
524        loop {
525            match reader
526                .read_event_into(&mut buffer)
527                .map_err(OxmlError::from)?
528            {
529                Event::Start(element) if matches_local_name(element.name().as_ref(), b"fgClr") => {
530                    reject_conflicting_a_prefix(&element)?;
531                    (fill.foreground, fill.foreground_raw_children) =
532                        parse_color_container(reader, b"fgClr")?;
533                    boundary = 1;
534                }
535                Event::Start(element) if matches_local_name(element.name().as_ref(), b"bgClr") => {
536                    reject_conflicting_a_prefix(&element)?;
537                    (fill.background, fill.background_raw_children) =
538                        parse_color_container(reader, b"bgClr")?;
539                    boundary = 2;
540                }
541                Event::Empty(element) if matches_local_name(element.name().as_ref(), b"fgClr") => {
542                    reject_conflicting_a_prefix(&element)?;
543                    boundary = 1
544                }
545                Event::Empty(element) if matches_local_name(element.name().as_ref(), b"bgClr") => {
546                    reject_conflicting_a_prefix(&element)?;
547                    boundary = 2
548                }
549                Event::Start(element) => fill
550                    .raw_children
551                    .push(boundary, capture_element(reader, &element)?),
552                Event::Empty(element) => fill
553                    .raw_children
554                    .push(boundary, capture_empty_element(&element)?),
555                Event::End(element) if matches_local_name(element.name().as_ref(), b"pattFill") => {
556                    break;
557                }
558                Event::Eof => return Err(missing_end("pattFill")),
559                _ => {}
560            }
561            buffer.clear();
562        }
563        Ok(fill)
564    }
565
566    fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
567        let mut start = BytesStart::new("a:pattFill");
568        if let Some(preset) = self.preset.as_deref() {
569            start.push_attribute(("prst", preset));
570        }
571        if self.foreground.is_none()
572            && self.background.is_none()
573            && self.foreground_raw_children.is_empty()
574            && self.background_raw_children.is_empty()
575            && self.raw_children.is_empty()
576        {
577            return write_empty(writer, start);
578        }
579        write_start(writer, start)?;
580        emit_raw(writer, self.raw_children.at(0))?;
581        if self.foreground.is_some() || !self.foreground_raw_children.is_empty() {
582            write_color_container(
583                writer,
584                "a:fgClr",
585                self.foreground.as_ref(),
586                &self.foreground_raw_children,
587            )?;
588        }
589        emit_raw(writer, self.raw_children.at(1))?;
590        if self.background.is_some() || !self.background_raw_children.is_empty() {
591            write_color_container(
592                writer,
593                "a:bgClr",
594                self.background.as_ref(),
595                &self.background_raw_children,
596            )?;
597        }
598        emit_raw(writer, self.raw_children.at(2))?;
599        write_end(writer, "a:pattFill")
600    }
601
602    pub fn raw_children(&self) -> &OrderedRawChildren {
603        &self.raw_children
604    }
605}
606
607/// Picture relationship identifiers and preserved image effects.
608#[derive(Clone, Debug, Default, Eq, PartialEq)]
609pub struct Blip {
610    pub embed: Option<String>,
611    pub link: Option<String>,
612    raw_children: OrderedRawChildren,
613}
614
615impl Blip {
616    fn from_start(start: &BytesStart<'_>) -> Self {
617        Self {
618            embed: get_attr(start, b"embed"),
619            link: get_attr(start, b"link"),
620            raw_children: OrderedRawChildren::default(),
621        }
622    }
623
624    fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
625        let mut blip = Self::from_start(start);
626        blip.raw_children = capture_all_children(reader, b"blip")?;
627        Ok(blip)
628    }
629
630    fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
631        let mut start = BytesStart::new("a:blip");
632        if self.embed.is_some() || self.link.is_some() {
633            start.push_attribute(("xmlns:r", R_NS));
634        }
635        if let Some(embed) = self.embed.as_deref() {
636            start.push_attribute(("r:embed", embed));
637        }
638        if let Some(link) = self.link.as_deref() {
639            start.push_attribute(("r:link", link));
640        }
641        if self.raw_children.is_empty() {
642            return write_empty(writer, start);
643        }
644        write_start(writer, start)?;
645        emit_raw(writer, self.raw_children.at(0))?;
646        write_end(writer, "a:blip")
647    }
648}
649
650/// Picture-fill placement mode.
651#[derive(Clone, Debug, Eq, PartialEq)]
652pub enum BlipMode {
653    Stretch {
654        fill_rect: Option<RelativeRect>,
655        raw_children: OrderedRawChildren,
656    },
657    Tile(Tile),
658}
659
660/// Tiled picture-fill offsets, scales, flip, and alignment.
661#[derive(Clone, Debug, Default, Eq, PartialEq)]
662pub struct Tile {
663    pub translation_x: Option<i64>,
664    pub translation_y: Option<i64>,
665    pub scale_x: Option<Percent1000>,
666    pub scale_y: Option<Percent1000>,
667    pub flip: Option<String>,
668    pub alignment: Option<String>,
669    raw_children: OrderedRawChildren,
670}
671
672/// Picture fill with relationship ids but no package dependency.
673#[derive(Clone, Debug, Default, Eq, PartialEq)]
674pub struct BlipFill {
675    pub dpi: Option<u32>,
676    pub rotate_with_shape: Option<bool>,
677    pub blip: Option<Blip>,
678    pub source_rect: Option<RelativeRect>,
679    pub mode: Option<BlipMode>,
680    raw_attributes: Vec<(String, String)>,
681    raw_children: OrderedRawChildren,
682}
683
684impl BlipFill {
685    /// Parses one complete picture-fill element with any namespace prefix.
686    pub fn from_xml(xml: &[u8]) -> Result<Self> {
687        let mut reader = Reader::from_reader(xml);
688        let mut buffer = Vec::new();
689        loop {
690            match reader
691                .read_event_into(&mut buffer)
692                .map_err(OxmlError::from)?
693            {
694                Event::Start(element)
695                    if matches_local_name(element.name().as_ref(), b"blipFill") =>
696                {
697                    return Self::from_element(&mut reader, &element);
698                }
699                Event::Empty(element)
700                    if matches_local_name(element.name().as_ref(), b"blipFill") =>
701                {
702                    return Self::from_start(&element);
703                }
704                Event::Start(element) | Event::Empty(element) => {
705                    return Err(unexpected(&element));
706                }
707                Event::Eof => return Err(missing_end("blipFill")),
708                _ => {}
709            }
710            buffer.clear();
711        }
712    }
713
714    fn from_start(start: &BytesStart<'_>) -> Result<Self> {
715        reject_conflicting_a_prefix(start)?;
716        Ok(Self {
717            dpi: optional_parse(start, b"dpi")?,
718            rotate_with_shape: optional_bool(start, b"rotWithShape")?,
719            raw_attributes: capture_unmodelled_attributes(start, &[b"dpi", b"rotWithShape"])?,
720            ..Self::default()
721        })
722    }
723
724    fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
725        let mut fill = Self::from_start(start)?;
726        let mut buffer = Vec::new();
727        let mut boundary = 0;
728        loop {
729            match reader
730                .read_event_into(&mut buffer)
731                .map_err(OxmlError::from)?
732            {
733                Event::Start(element) if matches_local_name(element.name().as_ref(), b"blip") => {
734                    reject_conflicting_a_prefix(&element)?;
735                    fill.blip = Some(Blip::from_element(reader, &element)?);
736                    boundary = 1;
737                }
738                Event::Empty(element) if matches_local_name(element.name().as_ref(), b"blip") => {
739                    reject_conflicting_a_prefix(&element)?;
740                    fill.blip = Some(Blip::from_start(&element));
741                    boundary = 1;
742                }
743                Event::Start(element)
744                    if matches_local_name(element.name().as_ref(), b"srcRect") =>
745                {
746                    fill.source_rect = Some(parse_rect_element(reader, &element)?);
747                    boundary = 2;
748                }
749                Event::Empty(element)
750                    if matches_local_name(element.name().as_ref(), b"srcRect") =>
751                {
752                    fill.source_rect = Some(parse_rect(&element)?);
753                    boundary = 2;
754                }
755                Event::Start(element)
756                    if matches_local_name(element.name().as_ref(), b"stretch") =>
757                {
758                    reject_conflicting_a_prefix(&element)?;
759                    fill.mode = Some(parse_stretch(reader)?);
760                    boundary = 3;
761                }
762                Event::Empty(element)
763                    if matches_local_name(element.name().as_ref(), b"stretch") =>
764                {
765                    reject_conflicting_a_prefix(&element)?;
766                    fill.mode = Some(BlipMode::Stretch {
767                        fill_rect: None,
768                        raw_children: OrderedRawChildren::default(),
769                    });
770                    boundary = 3;
771                }
772                Event::Start(element) if matches_local_name(element.name().as_ref(), b"tile") => {
773                    fill.mode = Some(BlipMode::Tile(parse_tile(reader, &element)?));
774                    boundary = 3;
775                }
776                Event::Empty(element) if matches_local_name(element.name().as_ref(), b"tile") => {
777                    fill.mode = Some(BlipMode::Tile(parse_empty_tile(&element)?));
778                    boundary = 3;
779                }
780                Event::Start(element) => fill
781                    .raw_children
782                    .push(boundary, capture_element(reader, &element)?),
783                Event::Empty(element) => fill
784                    .raw_children
785                    .push(boundary, capture_empty_element(&element)?),
786                Event::End(element) if matches_local_name(element.name().as_ref(), b"blipFill") => {
787                    break;
788                }
789                Event::Eof => return Err(missing_end("blipFill")),
790                _ => {}
791            }
792            buffer.clear();
793        }
794        Ok(fill)
795    }
796
797    fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
798        self.write_xml_as(writer, "a:blipFill")
799    }
800
801    /// Writes the picture fill under the caller's required root name.
802    pub fn write_xml_as<W: Write>(&self, writer: &mut Writer<W>, name: &str) -> Result<()> {
803        let mut start = BytesStart::new(name);
804        let dpi = self.dpi.map(|value| value.to_string());
805        if let Some(dpi) = dpi.as_deref() {
806            start.push_attribute(("dpi", dpi));
807        }
808        if let Some(rotate) = self.rotate_with_shape.map(bool_text) {
809            start.push_attribute(("rotWithShape", rotate));
810        }
811        push_raw_attributes(&mut start, &self.raw_attributes);
812        if self.blip.is_none()
813            && self.source_rect.is_none()
814            && self.mode.is_none()
815            && self.raw_children.is_empty()
816        {
817            return write_empty(writer, start);
818        }
819        write_start(writer, start)?;
820        emit_raw(writer, self.raw_children.at(0))?;
821        if let Some(blip) = &self.blip {
822            blip.write_xml(writer)?;
823        }
824        emit_raw(writer, self.raw_children.at(1))?;
825        if let Some(rect) = &self.source_rect {
826            write_rect(writer, "a:srcRect", rect)?;
827        }
828        emit_raw(writer, self.raw_children.at(2))?;
829        if let Some(mode) = &self.mode {
830            write_blip_mode(writer, mode)?;
831        }
832        emit_raw(writer, self.raw_children.at(3))?;
833        write_end(writer, name)
834    }
835
836    pub fn raw_children(&self) -> &OrderedRawChildren {
837        &self.raw_children
838    }
839}
840
841fn capture_unmodelled_attributes(
842    start: &BytesStart<'_>,
843    modelled: &[&[u8]],
844) -> Result<Vec<(String, String)>> {
845    let mut raw = Vec::new();
846    for attribute in start.attributes() {
847        let attribute = attribute.map_err(OxmlError::from)?;
848        let key = attribute.key.as_ref();
849        if !key.contains(&b':') && modelled.contains(&key) {
850            continue;
851        }
852        let name = std::str::from_utf8(key)
853            .map_err(OxmlError::from)?
854            .to_owned();
855        let value = attribute
856            .decoded_and_normalized_value(XmlVersion::Implicit1_0, start.decoder())
857            .map_err(OxmlError::from)?
858            .into_owned();
859        raw.push((name, value));
860    }
861    Ok(raw)
862}
863
864fn push_raw_attributes(start: &mut BytesStart<'_>, attributes: &[(String, String)]) {
865    for (name, value) in attributes {
866        start.push_attribute((name.as_str(), value.as_str()));
867    }
868}
869
870fn parse_linear(start: &BytesStart<'_>) -> Result<LinearGradient> {
871    reject_conflicting_a_prefix(start)?;
872    Ok(LinearGradient {
873        angle: Angle(required_i32(start, b"ang")?),
874        scaled: optional_bool(start, b"scaled")?,
875        raw_children: OrderedRawChildren::default(),
876    })
877}
878
879fn parse_linear_element(
880    reader: &mut Reader<&[u8]>,
881    start: &BytesStart<'_>,
882) -> Result<LinearGradient> {
883    let mut linear = parse_linear(start)?;
884    linear.raw_children = capture_all_children(reader, b"lin")?;
885    Ok(linear)
886}
887
888fn parse_empty_path(start: &BytesStart<'_>) -> Result<PathGradient> {
889    reject_conflicting_a_prefix(start)?;
890    let value = required_attr(start, b"path")?;
891    let kind = PathGradientKind::parse(&value).ok_or_else(|| invalid(start, b"path", value))?;
892    Ok(PathGradient {
893        kind,
894        fill_to_rect: None,
895        raw_children: OrderedRawChildren::default(),
896    })
897}
898
899fn parse_path(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<PathGradient> {
900    let mut path = parse_empty_path(start)?;
901    let mut buffer = Vec::new();
902    let mut boundary = 0;
903    loop {
904        match reader
905            .read_event_into(&mut buffer)
906            .map_err(OxmlError::from)?
907        {
908            Event::Start(element) if matches_local_name(element.name().as_ref(), b"fillToRect") => {
909                path.fill_to_rect = Some(parse_rect_element(reader, &element)?);
910                boundary = 1;
911            }
912            Event::Empty(element) if matches_local_name(element.name().as_ref(), b"fillToRect") => {
913                path.fill_to_rect = Some(parse_rect(&element)?);
914                boundary = 1;
915            }
916            Event::Start(element) => path
917                .raw_children
918                .push(boundary, capture_element(reader, &element)?),
919            Event::Empty(element) => path
920                .raw_children
921                .push(boundary, capture_empty_element(&element)?),
922            Event::End(element) if matches_local_name(element.name().as_ref(), b"path") => break,
923            Event::Eof => return Err(missing_end("path")),
924            _ => {}
925        }
926        buffer.clear();
927    }
928    Ok(path)
929}
930
931fn write_geometry<W: Write>(writer: &mut Writer<W>, geometry: &GradientGeometry) -> Result<()> {
932    match geometry {
933        GradientGeometry::Linear(linear) => {
934            let angle = linear.angle.0.to_string();
935            let mut start = BytesStart::new("a:lin");
936            start.push_attribute(("ang", angle.as_str()));
937            if let Some(scaled) = linear.scaled.map(bool_text) {
938                start.push_attribute(("scaled", scaled));
939            }
940            if linear.raw_children.is_empty() {
941                return write_empty(writer, start);
942            }
943            write_start(writer, start)?;
944            emit_raw(writer, linear.raw_children.at(0))?;
945            write_end(writer, "a:lin")
946        }
947        GradientGeometry::Path(path) => {
948            let mut start = BytesStart::new("a:path");
949            start.push_attribute(("path", path.kind.as_str()));
950            if path.fill_to_rect.is_none() && path.raw_children.is_empty() {
951                return write_empty(writer, start);
952            }
953            write_start(writer, start)?;
954            emit_raw(writer, path.raw_children.at(0))?;
955            if let Some(rect) = &path.fill_to_rect {
956                write_rect(writer, "a:fillToRect", rect)?;
957            }
958            emit_raw(writer, path.raw_children.at(1))?;
959            write_end(writer, "a:path")
960        }
961    }
962}
963
964fn parse_stretch(reader: &mut Reader<&[u8]>) -> Result<BlipMode> {
965    let mut fill_rect = None;
966    let mut raw_children = OrderedRawChildren::default();
967    let mut boundary = 0;
968    let mut buffer = Vec::new();
969    loop {
970        match reader
971            .read_event_into(&mut buffer)
972            .map_err(OxmlError::from)?
973        {
974            Event::Start(element) if matches_local_name(element.name().as_ref(), b"fillRect") => {
975                fill_rect = Some(parse_rect_element(reader, &element)?);
976                boundary = 1;
977            }
978            Event::Empty(element) if matches_local_name(element.name().as_ref(), b"fillRect") => {
979                fill_rect = Some(parse_rect(&element)?);
980                boundary = 1;
981            }
982            Event::Start(element) => {
983                raw_children.push(boundary, capture_element(reader, &element)?)
984            }
985            Event::Empty(element) => raw_children.push(boundary, capture_empty_element(&element)?),
986            Event::End(element) if matches_local_name(element.name().as_ref(), b"stretch") => break,
987            Event::Eof => return Err(missing_end("stretch")),
988            _ => {}
989        }
990        buffer.clear();
991    }
992    Ok(BlipMode::Stretch {
993        fill_rect,
994        raw_children,
995    })
996}
997
998fn parse_empty_tile(start: &BytesStart<'_>) -> Result<Tile> {
999    reject_conflicting_a_prefix(start)?;
1000    Ok(Tile {
1001        translation_x: optional_parse(start, b"tx")?,
1002        translation_y: optional_parse(start, b"ty")?,
1003        scale_x: optional_parse::<i32>(start, b"sx")?.map(Percent1000),
1004        scale_y: optional_parse::<i32>(start, b"sy")?.map(Percent1000),
1005        flip: optional_token(start, b"flip", &["none", "x", "y", "xy"])?,
1006        alignment: optional_token(
1007            start,
1008            b"algn",
1009            &["tl", "t", "tr", "l", "ctr", "r", "bl", "b", "br"],
1010        )?,
1011        raw_children: OrderedRawChildren::default(),
1012    })
1013}
1014
1015fn parse_tile(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Tile> {
1016    let mut tile = parse_empty_tile(start)?;
1017    tile.raw_children = capture_all_children(reader, b"tile")?;
1018    Ok(tile)
1019}
1020
1021fn write_blip_mode<W: Write>(writer: &mut Writer<W>, mode: &BlipMode) -> Result<()> {
1022    match mode {
1023        BlipMode::Stretch {
1024            fill_rect,
1025            raw_children,
1026        } => {
1027            if fill_rect.is_none() && raw_children.is_empty() {
1028                return write_empty(writer, BytesStart::new("a:stretch"));
1029            }
1030            write_start(writer, BytesStart::new("a:stretch"))?;
1031            emit_raw(writer, raw_children.at(0))?;
1032            if let Some(rect) = fill_rect {
1033                write_rect(writer, "a:fillRect", rect)?;
1034            }
1035            emit_raw(writer, raw_children.at(1))?;
1036            write_end(writer, "a:stretch")
1037        }
1038        BlipMode::Tile(tile) => {
1039            let mut start = BytesStart::new("a:tile");
1040            let tx = tile.translation_x.map(|value| value.to_string());
1041            let ty = tile.translation_y.map(|value| value.to_string());
1042            let sx = tile.scale_x.map(|value| value.0.to_string());
1043            let sy = tile.scale_y.map(|value| value.0.to_string());
1044            for (name, value) in [
1045                ("tx", tx.as_deref()),
1046                ("ty", ty.as_deref()),
1047                ("sx", sx.as_deref()),
1048                ("sy", sy.as_deref()),
1049            ] {
1050                if let Some(value) = value {
1051                    start.push_attribute((name, value));
1052                }
1053            }
1054            if let Some(flip) = tile.flip.as_deref() {
1055                start.push_attribute(("flip", flip));
1056            }
1057            if let Some(alignment) = tile.alignment.as_deref() {
1058                start.push_attribute(("algn", alignment));
1059            }
1060            if tile.raw_children.is_empty() {
1061                return write_empty(writer, start);
1062            }
1063            write_start(writer, start)?;
1064            emit_raw(writer, tile.raw_children.at(0))?;
1065            write_end(writer, "a:tile")
1066        }
1067    }
1068}
1069
1070fn parse_rect(start: &BytesStart<'_>) -> Result<RelativeRect> {
1071    reject_conflicting_a_prefix(start)?;
1072    Ok(RelativeRect {
1073        left: optional_parse::<i32>(start, b"l")?.map(Percent1000),
1074        top: optional_parse::<i32>(start, b"t")?.map(Percent1000),
1075        right: optional_parse::<i32>(start, b"r")?.map(Percent1000),
1076        bottom: optional_parse::<i32>(start, b"b")?.map(Percent1000),
1077        raw_children: OrderedRawChildren::default(),
1078    })
1079}
1080
1081fn parse_rect_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<RelativeRect> {
1082    let mut rect = parse_rect(start)?;
1083    rect.raw_children = capture_all_children(reader, local_name(start.name().as_ref()))?;
1084    Ok(rect)
1085}
1086
1087fn write_rect<W: Write>(writer: &mut Writer<W>, name: &str, rect: &RelativeRect) -> Result<()> {
1088    let left = rect.left.map(|value| value.0.to_string());
1089    let top = rect.top.map(|value| value.0.to_string());
1090    let right = rect.right.map(|value| value.0.to_string());
1091    let bottom = rect.bottom.map(|value| value.0.to_string());
1092    let mut start = BytesStart::new(name);
1093    for (name, value) in [
1094        ("l", left.as_deref()),
1095        ("t", top.as_deref()),
1096        ("r", right.as_deref()),
1097        ("b", bottom.as_deref()),
1098    ] {
1099        if let Some(value) = value {
1100            start.push_attribute((name, value));
1101        }
1102    }
1103    if rect.raw_children.is_empty() {
1104        return write_empty(writer, start);
1105    }
1106    write_start(writer, start)?;
1107    emit_raw(writer, rect.raw_children.at(0))?;
1108    write_end(writer, name)
1109}
1110
1111fn parse_color_container(
1112    reader: &mut Reader<&[u8]>,
1113    end_name: &[u8],
1114) -> Result<(Option<ColorChoice>, OrderedRawChildren)> {
1115    let mut color = None;
1116    let mut raw_children = OrderedRawChildren::default();
1117    let mut boundary = 0;
1118    let mut buffer = Vec::new();
1119    loop {
1120        match reader
1121            .read_event_into(&mut buffer)
1122            .map_err(OxmlError::from)?
1123        {
1124            Event::Start(element) if is_color(element.name().as_ref()) => {
1125                color = Some(ColorChoice::from_xml(reader, &element)?);
1126                boundary = 1;
1127            }
1128            Event::Empty(element) if is_color(element.name().as_ref()) => {
1129                color = Some(ColorChoice::from_empty_xml(&element)?);
1130                boundary = 1;
1131            }
1132            Event::Start(element) => {
1133                raw_children.push(boundary, capture_element(reader, &element)?);
1134            }
1135            Event::Empty(element) => {
1136                raw_children.push(boundary, capture_empty_element(&element)?);
1137            }
1138            Event::End(element) if matches_local_name(element.name().as_ref(), end_name) => break,
1139            Event::Eof => return Err(missing_end(&String::from_utf8_lossy(end_name))),
1140            _ => {}
1141        }
1142        buffer.clear();
1143    }
1144    Ok((color, raw_children))
1145}
1146
1147fn write_color_container<W: Write>(
1148    writer: &mut Writer<W>,
1149    name: &str,
1150    color: Option<&ColorChoice>,
1151    raw_children: &OrderedRawChildren,
1152) -> Result<()> {
1153    write_start(writer, BytesStart::new(name))?;
1154    emit_raw(writer, raw_children.at(0))?;
1155    if let Some(color) = color {
1156        color.to_xml(writer)?;
1157    }
1158    emit_raw(writer, raw_children.at(1))?;
1159    write_end(writer, name)
1160}
1161
1162fn read_color_children(
1163    reader: &mut Reader<&[u8]>,
1164    end_name: &[u8],
1165    color: &mut Option<ColorChoice>,
1166    raw_children: &mut OrderedRawChildren,
1167) -> Result<()> {
1168    let mut buffer = Vec::new();
1169    let mut boundary = 0;
1170    loop {
1171        match reader
1172            .read_event_into(&mut buffer)
1173            .map_err(OxmlError::from)?
1174        {
1175            Event::Start(element) if is_color(element.name().as_ref()) => {
1176                *color = Some(ColorChoice::from_xml(reader, &element)?);
1177                boundary = 1;
1178            }
1179            Event::Empty(element) if is_color(element.name().as_ref()) => {
1180                *color = Some(ColorChoice::from_empty_xml(&element)?);
1181                boundary = 1;
1182            }
1183            Event::Start(element) => {
1184                raw_children.push(boundary, capture_element(reader, &element)?)
1185            }
1186            Event::Empty(element) => raw_children.push(boundary, capture_empty_element(&element)?),
1187            Event::End(element) if matches_local_name(element.name().as_ref(), end_name) => break,
1188            Event::Eof => return Err(missing_end(&String::from_utf8_lossy(end_name))),
1189            _ => {}
1190        }
1191        buffer.clear();
1192    }
1193    Ok(())
1194}
1195
1196fn capture_all_children(reader: &mut Reader<&[u8]>, end_name: &[u8]) -> Result<OrderedRawChildren> {
1197    let mut raw_children = OrderedRawChildren::default();
1198    let mut buffer = Vec::new();
1199    loop {
1200        match reader
1201            .read_event_into(&mut buffer)
1202            .map_err(OxmlError::from)?
1203        {
1204            Event::Start(element) => raw_children.push(0, capture_element(reader, &element)?),
1205            Event::Empty(element) => raw_children.push(0, capture_empty_element(&element)?),
1206            Event::End(element) if matches_local_name(element.name().as_ref(), end_name) => break,
1207            Event::Eof => return Err(missing_end(&String::from_utf8_lossy(end_name))),
1208            _ => {}
1209        }
1210        buffer.clear();
1211    }
1212    Ok(raw_children)
1213}
1214
1215fn is_color(name: &[u8]) -> bool {
1216    matches!(
1217        local_name(name),
1218        b"srgbClr" | b"schemeClr" | b"sysClr" | b"prstClr"
1219    )
1220}
1221
1222fn validate_gradient_position(value: i32) -> Result<()> {
1223    if (0..=100_000).contains(&value) {
1224        Ok(())
1225    } else {
1226        Err(FillError::GradientStopOutOfRange(value))
1227    }
1228}
1229
1230fn required_attr(start: &BytesStart<'_>, name: &[u8]) -> Result<String> {
1231    get_attr(start, name).ok_or_else(|| FillError::MissingAttribute {
1232        element: String::from_utf8_lossy(local_name(start.name().as_ref())).into_owned(),
1233        attribute: String::from_utf8_lossy(name).into_owned(),
1234    })
1235}
1236
1237fn required_i32(start: &BytesStart<'_>, name: &[u8]) -> Result<i32> {
1238    let value = required_attr(start, name)?;
1239    value.parse().map_err(|_| invalid(start, name, value))
1240}
1241
1242fn optional_parse<T: std::str::FromStr>(start: &BytesStart<'_>, name: &[u8]) -> Result<Option<T>> {
1243    get_attr(start, name)
1244        .map(|value| value.parse().map_err(|_| invalid(start, name, value)))
1245        .transpose()
1246}
1247
1248fn optional_bool(start: &BytesStart<'_>, name: &[u8]) -> Result<Option<bool>> {
1249    get_attr(start, name)
1250        .map(|value| match value.as_str() {
1251            "1" | "true" => Ok(true),
1252            "0" | "false" => Ok(false),
1253            _ => Err(invalid(start, name, value)),
1254        })
1255        .transpose()
1256}
1257
1258fn optional_token(start: &BytesStart<'_>, name: &[u8], allowed: &[&str]) -> Result<Option<String>> {
1259    get_attr(start, name)
1260        .map(|value| {
1261            if allowed.contains(&value.as_str()) {
1262                Ok(value)
1263            } else {
1264                Err(invalid(start, name, value))
1265            }
1266        })
1267        .transpose()
1268}
1269
1270fn invalid(start: &BytesStart<'_>, attribute: &[u8], value: String) -> FillError {
1271    FillError::InvalidAttribute {
1272        element: String::from_utf8_lossy(local_name(start.name().as_ref())).into_owned(),
1273        attribute: String::from_utf8_lossy(attribute).into_owned(),
1274        value,
1275    }
1276}
1277
1278fn unexpected(start: &BytesStart<'_>) -> FillError {
1279    FillError::UnexpectedElement(String::from_utf8_lossy(start.name().as_ref()).into_owned())
1280}
1281
1282fn missing_end(name: &str) -> FillError {
1283    FillError::Xml(OxmlError::MissingElement(format!("closing a:{name}")))
1284}
1285
1286const fn bool_text(value: bool) -> &'static str {
1287    if value { "1" } else { "0" }
1288}
1289
1290fn emit_raw<'a, W: Write>(
1291    writer: &mut Writer<W>,
1292    children: impl Iterator<Item = &'a [u8]>,
1293) -> Result<()> {
1294    for child in children {
1295        writer.get_mut().write_all(child).map_err(OxmlError::from)?;
1296    }
1297    Ok(())
1298}
1299
1300fn write_start<W: Write>(writer: &mut Writer<W>, start: BytesStart<'_>) -> Result<()> {
1301    writer
1302        .write_event(Event::Start(start))
1303        .map_err(OxmlError::from)?;
1304    Ok(())
1305}
1306
1307fn write_empty<W: Write>(writer: &mut Writer<W>, start: BytesStart<'_>) -> Result<()> {
1308    writer
1309        .write_event(Event::Empty(start))
1310        .map_err(OxmlError::from)?;
1311    Ok(())
1312}
1313
1314fn write_end<W: Write>(writer: &mut Writer<W>, name: &str) -> Result<()> {
1315    writer
1316        .write_event(Event::End(BytesEnd::new(name)))
1317        .map_err(OxmlError::from)?;
1318    Ok(())
1319}
1320
1321#[cfg(test)]
1322mod tests {
1323    use super::{Fill, FillError, GradientGeometry};
1324
1325    #[test]
1326    fn every_fill_form_round_trips_and_gradient_stops_keep_document_order() {
1327        let cases: &[&[u8]] = &[
1328            br#"<a:noFill/>"#,
1329            br#"<a:solidFill><a:schemeClr val="accent1"/></a:solidFill>"#,
1330            br#"<a:gradFill flip="xy" rotWithShape="1"><a:gsLst><a:gs pos="75000"><a:srgbClr val="FF0000"/></a:gs><a:gs pos="25000"><a:schemeClr val="accent2"/></a:gs></a:gsLst><a:lin ang="5400000" scaled="1"/><a:tileRect l="1000"/></a:gradFill>"#,
1331            br#"<a:gradFill><a:gsLst><a:gs pos="0"><a:srgbClr val="000000"/></a:gs><a:gs pos="100000"><a:srgbClr val="FFFFFF"/></a:gs></a:gsLst><a:path path="circle"><a:fillToRect l="10000" t="10000" r="10000" b="10000"/></a:path></a:gradFill>"#,
1332            br#"<a:pattFill prst="pct10"><a:fgClr><a:srgbClr val="112233"/></a:fgClr><a:bgClr><a:schemeClr val="bg1"/></a:bgClr></a:pattFill>"#,
1333            br#"<a:blipFill dpi="96" rotWithShape="0"><a:blip r:embed="rId7"/><a:srcRect l="1000" t="2000"/><a:stretch><a:fillRect r="3000"/></a:stretch></a:blipFill>"#,
1334            br#"<a:blipFill><a:blip r:link="rId8"/><a:tile tx="10" ty="20" sx="50000" sy="75000" flip="x" algn="ctr"/></a:blipFill>"#,
1335        ];
1336
1337        for xml in cases {
1338            let parsed = Fill::from_xml(xml).unwrap();
1339            let written = parsed.to_xml().unwrap();
1340            assert_eq!(Fill::from_xml(&written).unwrap(), parsed);
1341        }
1342
1343        let Fill::Gradient(gradient) = Fill::from_xml(cases[2]).unwrap() else {
1344            panic!("expected gradient")
1345        };
1346        assert_eq!(
1347            gradient
1348                .stops
1349                .iter()
1350                .map(|stop| stop.position.0)
1351                .collect::<Vec<_>>(),
1352            vec![75_000, 25_000]
1353        );
1354        assert!(matches!(
1355            gradient.geometry,
1356            Some(GradientGeometry::Linear(_))
1357        ));
1358    }
1359
1360    #[test]
1361    fn fill_forms_read_any_prefix_and_write_fixed_a_prefix_in_schema_order() {
1362        let fill = Fill::from_xml(br#"<z:blipFill><z:blip r:embed="rId4"/><z:srcRect l="10"/><z:stretch><z:fillRect t="20"/></z:stretch></z:blipFill>"#).unwrap();
1363        assert_eq!(fill.to_xml().unwrap(), br#"<a:blipFill><a:blip xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="rId4"/><a:srcRect l="10"/><a:stretch><a:fillRect t="20"/></a:stretch></a:blipFill>"#);
1364
1365        let gradient = Fill::from_xml(br#"<q:gradFill><q:gsLst><q:gs pos="0"><q:srgbClr val="ABCDEF"/></q:gs></q:gsLst><q:path path="rect"/></q:gradFill>"#).unwrap();
1366        assert_eq!(gradient.to_xml().unwrap(), br#"<a:gradFill><a:gsLst><a:gs pos="0"><a:srgbClr val="ABCDEF"/></a:gs></a:gsLst><a:path path="rect"/></a:gradFill>"#);
1367    }
1368
1369    #[test]
1370    fn blip_relationship_prefix_is_declared_in_canonical_output() {
1371        let fill = Fill::from_xml(
1372            br#"<a:blipFill><a:blip xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="rId1"/></a:blipFill>"#,
1373        )
1374        .unwrap();
1375        let written = String::from_utf8(fill.to_xml().unwrap()).unwrap();
1376
1377        assert!(written.contains(
1378            "<a:blip xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" r:embed=\"rId1\"/>"
1379        ));
1380    }
1381
1382    #[test]
1383    fn unknown_fill_children_round_trip_byte_for_byte_in_place() {
1384        let xml = br#"<z:gradFill><x:before x:id="1"><x:item>one &amp; two</x:item><!--note--></x:before><z:gsLst><y:first/><z:gs pos="0"><z:srgbClr val="000000"><x:transform value="kept"/></z:srgbClr></z:gs><y:last/></z:gsLst><x:middle/><z:lin ang="0"/><x:after/></z:gradFill>"#;
1385        let written = Fill::from_xml(xml).unwrap().to_xml().unwrap();
1386        assert_eq!(written, br#"<a:gradFill><x:before x:id="1"><x:item>one &amp; two</x:item><!--note--></x:before><a:gsLst><y:first/><a:gs pos="0"><a:srgbClr val="000000"><x:transform value="kept"/></a:srgbClr></a:gs><y:last/></a:gsLst><x:middle/><a:lin ang="0"/><x:after/></a:gradFill>"#);
1387
1388        let blip = br#"<z:blipFill><z:blip r:embed="rId1"><a:alphaModFix amt="50000"/><x:ext x:v="raw"/></z:blip><x:between/><z:tile><x:tileExt/></z:tile></z:blipFill>"#;
1389        assert_eq!(Fill::from_xml(blip).unwrap().to_xml().unwrap(), br#"<a:blipFill><a:blip xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="rId1"><a:alphaModFix amt="50000"/><x:ext x:v="raw"/></a:blip><x:between/><a:tile><x:tileExt/></a:tile></a:blipFill>"#);
1390
1391        let pattern = br#"<z:pattFill><z:fgClr><x:before/><z:srgbClr val="102030"/><x:after x:v="kept"/></z:fgClr></z:pattFill>"#;
1392        assert_eq!(Fill::from_xml(pattern).unwrap().to_xml().unwrap(), br#"<a:pattFill><a:fgClr><x:before/><a:srgbClr val="102030"/><x:after x:v="kept"/></a:fgClr></a:pattFill>"#);
1393
1394        let extension_only_pattern =
1395            br#"<z:pattFill><z:fgClr><x:extension x:v="kept"/></z:fgClr></z:pattFill>"#;
1396        assert_eq!(
1397            Fill::from_xml(extension_only_pattern)
1398                .unwrap()
1399                .to_xml()
1400                .unwrap(),
1401            br#"<a:pattFill><a:fgClr><x:extension x:v="kept"/></a:fgClr></a:pattFill>"#
1402        );
1403
1404        let gradient_leaf_extensions = br#"<z:gradFill><z:lin ang="0"><x:linExt/></z:lin><z:tileRect l="10"><x:tileRectExt/></z:tileRect></z:gradFill>"#;
1405        assert_eq!(Fill::from_xml(gradient_leaf_extensions).unwrap().to_xml().unwrap(), br#"<a:gradFill><a:lin ang="0"><x:linExt/></a:lin><a:tileRect l="10"><x:tileRectExt/></a:tileRect></a:gradFill>"#);
1406
1407        let extension_only_stop_list =
1408            br#"<z:gradFill><z:gsLst><x:extension x:v="kept"/></z:gsLst></z:gradFill>"#;
1409        assert_eq!(
1410            Fill::from_xml(extension_only_stop_list)
1411                .unwrap()
1412                .to_xml()
1413                .unwrap(),
1414            br#"<a:gradFill><a:gsLst><x:extension x:v="kept"/></a:gsLst></a:gradFill>"#
1415        );
1416
1417        let path_leaf_extension = br#"<z:gradFill><z:path path="rect"><z:fillToRect><x:pathRectExt/></z:fillToRect></z:path></z:gradFill>"#;
1418        assert_eq!(Fill::from_xml(path_leaf_extension).unwrap().to_xml().unwrap(), br#"<a:gradFill><a:path path="rect"><a:fillToRect><x:pathRectExt/></a:fillToRect></a:path></a:gradFill>"#);
1419
1420        let blip_leaf_extensions = br#"<z:blipFill><z:srcRect><x:sourceExt/></z:srcRect><z:stretch><z:fillRect><x:fillExt/></z:fillRect></z:stretch></z:blipFill>"#;
1421        assert_eq!(Fill::from_xml(blip_leaf_extensions).unwrap().to_xml().unwrap(), br#"<a:blipFill><a:srcRect><x:sourceExt/></a:srcRect><a:stretch><a:fillRect><x:fillExt/></a:fillRect></a:stretch></a:blipFill>"#);
1422    }
1423
1424    #[test]
1425    fn malformed_fill_values_return_errors_without_panicking() {
1426        let cases: &[&[u8]] = &[
1427            br#"<a:gradFill rotWithShape="maybe"/>"#,
1428            br#"<a:gradFill><a:gsLst><a:gs pos="100001"/></a:gsLst></a:gradFill>"#,
1429            br#"<a:gradFill><a:gsLst><a:gs/></a:gsLst></a:gradFill>"#,
1430            br#"<a:gradFill><a:lin ang="ninety"/></a:gradFill>"#,
1431            br#"<a:gradFill><a:path path="triangle"/></a:gradFill>"#,
1432            br#"<a:blipFill dpi="many"/>"#,
1433            br#"<a:blipFill><a:srcRect l="half"/></a:blipFill>"#,
1434            br#"<a:blipFill><a:tile sx="large"/></a:blipFill>"#,
1435            br#"<a:gradFill flip="diagonal"/>"#,
1436            br#"<a:blipFill><a:tile algn="middle"/></a:blipFill>"#,
1437        ];
1438        for xml in cases {
1439            let result = std::panic::catch_unwind(|| Fill::from_xml(xml));
1440            assert!(
1441                result.is_ok(),
1442                "parser panicked for {}",
1443                String::from_utf8_lossy(xml)
1444            );
1445            assert!(
1446                result.unwrap().is_err(),
1447                "malformed fill parsed successfully"
1448            );
1449        }
1450        assert!(matches!(
1451            Fill::from_xml(cases[1]),
1452            Err(FillError::GradientStopOutOfRange(100_001))
1453        ));
1454    }
1455}