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::{A_NS, 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    /// Fixed alpha modulation in thousandths of a percent.
613    pub alpha_modulation_fix: Option<Percent1000>,
614    alpha_modulation_fix_raw_attributes: Vec<(String, String)>,
615    raw_children: OrderedRawChildren,
616}
617
618impl Blip {
619    fn from_start(start: &BytesStart<'_>) -> Self {
620        Self {
621            embed: get_attr(start, b"embed"),
622            link: get_attr(start, b"link"),
623            alpha_modulation_fix: None,
624            alpha_modulation_fix_raw_attributes: Vec::new(),
625            raw_children: OrderedRawChildren::default(),
626        }
627    }
628
629    fn from_element(
630        reader: &mut Reader<&[u8]>,
631        start: &BytesStart<'_>,
632        ancestor: &BytesStart<'_>,
633    ) -> Result<Self> {
634        let mut blip = Self::from_start(start);
635        let mut buffer = Vec::new();
636        let mut boundary = 0;
637        loop {
638            match reader
639                .read_event_into(&mut buffer)
640                .map_err(OxmlError::from)?
641            {
642                Event::Start(element)
643                    if blip.alpha_modulation_fix.is_none()
644                        && is_drawingml_alpha_mod_fix(&element, start, ancestor)? =>
645                {
646                    let (amount, raw_attributes) = parse_alpha_modulation_fix(&element)?;
647                    read_empty_effect_body(reader, &element)?;
648                    blip.alpha_modulation_fix = Some(amount);
649                    blip.alpha_modulation_fix_raw_attributes = raw_attributes;
650                    boundary = 1;
651                }
652                Event::Empty(element)
653                    if blip.alpha_modulation_fix.is_none()
654                        && is_drawingml_alpha_mod_fix(&element, start, ancestor)? =>
655                {
656                    let (amount, raw_attributes) = parse_alpha_modulation_fix(&element)?;
657                    blip.alpha_modulation_fix = Some(amount);
658                    blip.alpha_modulation_fix_raw_attributes = raw_attributes;
659                    boundary = 1;
660                }
661                Event::Start(element) => blip
662                    .raw_children
663                    .push(boundary, capture_element(reader, &element)?),
664                Event::Empty(element) => blip
665                    .raw_children
666                    .push(boundary, capture_empty_element(&element)?),
667                Event::End(element) if matches_local_name(element.name().as_ref(), b"blip") => {
668                    break;
669                }
670                Event::Eof => return Err(missing_end("blip")),
671                _ => {}
672            }
673            buffer.clear();
674        }
675        Ok(blip)
676    }
677
678    fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
679        if let Some(amount) = self.alpha_modulation_fix
680            && !(0..=100_000).contains(&amount.0)
681        {
682            return Err(FillError::InvalidAttribute {
683                element: "alphaModFix".to_owned(),
684                attribute: "amt".to_owned(),
685                value: amount.0.to_string(),
686            });
687        }
688        let mut start = BytesStart::new("a:blip");
689        if self.embed.is_some() || self.link.is_some() {
690            start.push_attribute(("xmlns:r", R_NS));
691        }
692        if let Some(embed) = self.embed.as_deref() {
693            start.push_attribute(("r:embed", embed));
694        }
695        if let Some(link) = self.link.as_deref() {
696            start.push_attribute(("r:link", link));
697        }
698        if self.alpha_modulation_fix.is_none() && self.raw_children.is_empty() {
699            return write_empty(writer, start);
700        }
701        write_start(writer, start)?;
702        emit_raw(writer, self.raw_children.at(0))?;
703        if let Some(amount) = self.alpha_modulation_fix {
704            let amount = amount.0.to_string();
705            let mut effect = BytesStart::new("a:alphaModFix");
706            effect.push_attribute(("amt", amount.as_str()));
707            push_raw_attributes(&mut effect, &self.alpha_modulation_fix_raw_attributes);
708            write_empty(writer, effect)?;
709        }
710        emit_raw(writer, self.raw_children.at(1))?;
711        write_end(writer, "a:blip")
712    }
713}
714
715fn is_drawingml_alpha_mod_fix(
716    element: &BytesStart<'_>,
717    parent: &BytesStart<'_>,
718    ancestor: &BytesStart<'_>,
719) -> Result<bool> {
720    if !matches_local_name(element.name().as_ref(), b"alphaModFix") {
721        return Ok(false);
722    }
723    let qualified = element.name();
724    let qualified = qualified.as_ref();
725    let prefix = qualified
726        .split(|byte| *byte == b':')
727        .next()
728        .filter(|prefix| prefix.len() != qualified.len());
729    let binding = namespace_binding(element, prefix)?
730        .or(namespace_binding(parent, prefix)?)
731        .or(namespace_binding(ancestor, prefix)?);
732    if prefix == Some(b"a") && binding.as_deref().is_some_and(|uri| uri != A_NS) {
733        return Err(OxmlError::InvalidValue(
734            "xmlns:a conflicts with the fixed DrawingML writer namespace".to_owned(),
735        )
736        .into());
737    }
738    Ok(binding.as_deref() == Some(A_NS) || (prefix == Some(b"a") && binding.is_none()))
739}
740
741fn namespace_binding(start: &BytesStart<'_>, prefix: Option<&[u8]>) -> Result<Option<String>> {
742    let name = prefix.map_or_else(
743        || b"xmlns".to_vec(),
744        |prefix| {
745            let mut name = b"xmlns:".to_vec();
746            name.extend_from_slice(prefix);
747            name
748        },
749    );
750    for attribute in start.attributes() {
751        let attribute = attribute.map_err(OxmlError::from)?;
752        if attribute.key.as_ref() == name {
753            return Ok(Some(
754                attribute
755                    .decoded_and_normalized_value(XmlVersion::Implicit1_0, start.decoder())
756                    .map_err(OxmlError::from)?
757                    .into_owned(),
758            ));
759        }
760    }
761    Ok(None)
762}
763
764fn parse_alpha_modulation_fix(
765    element: &BytesStart<'_>,
766) -> Result<(Percent1000, Vec<(String, String)>)> {
767    let mut amount = None;
768    for attribute in element.attributes() {
769        let attribute = attribute.map_err(OxmlError::from)?;
770        if attribute.key.as_ref() == b"amt" {
771            amount = Some(
772                attribute
773                    .decoded_and_normalized_value(XmlVersion::Implicit1_0, element.decoder())
774                    .map_err(OxmlError::from)?
775                    .into_owned(),
776            );
777        }
778    }
779    let amount = amount.unwrap_or_else(|| "100000".to_owned());
780    let parsed = amount
781        .parse::<i32>()
782        .map_err(|_| invalid(element, b"amt", amount.clone()))?;
783    if !(0..=100_000).contains(&parsed) {
784        return Err(invalid(element, b"amt", amount));
785    }
786    Ok((
787        Percent1000(parsed),
788        capture_unmodelled_attributes(element, &[b"amt"])?,
789    ))
790}
791
792fn read_empty_effect_body(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<()> {
793    let mut buffer = Vec::new();
794    loop {
795        match reader
796            .read_event_into(&mut buffer)
797            .map_err(OxmlError::from)?
798        {
799            Event::End(element) if element.name().as_ref() == start.name().as_ref() => {
800                return Ok(());
801            }
802            Event::Text(text) if text.iter().all(u8::is_ascii_whitespace) => {}
803            Event::Start(element) | Event::Empty(element) => return Err(unexpected(&element)),
804            Event::Eof => return Err(missing_end("alphaModFix")),
805            _ => {
806                return Err(OxmlError::InvalidValue(
807                    "a:alphaModFix must not contain content".to_owned(),
808                )
809                .into());
810            }
811        }
812        buffer.clear();
813    }
814}
815
816/// Picture-fill placement mode.
817#[derive(Clone, Debug, Eq, PartialEq)]
818pub enum BlipMode {
819    Stretch {
820        fill_rect: Option<RelativeRect>,
821        raw_children: OrderedRawChildren,
822    },
823    Tile(Tile),
824}
825
826/// Tiled picture-fill offsets, scales, flip, and alignment.
827#[derive(Clone, Debug, Default, Eq, PartialEq)]
828pub struct Tile {
829    pub translation_x: Option<i64>,
830    pub translation_y: Option<i64>,
831    pub scale_x: Option<Percent1000>,
832    pub scale_y: Option<Percent1000>,
833    pub flip: Option<String>,
834    pub alignment: Option<String>,
835    raw_children: OrderedRawChildren,
836}
837
838/// Picture fill with relationship ids but no package dependency.
839#[derive(Clone, Debug, Default, Eq, PartialEq)]
840pub struct BlipFill {
841    pub dpi: Option<u32>,
842    pub rotate_with_shape: Option<bool>,
843    pub blip: Option<Blip>,
844    pub source_rect: Option<RelativeRect>,
845    pub mode: Option<BlipMode>,
846    raw_attributes: Vec<(String, String)>,
847    raw_children: OrderedRawChildren,
848}
849
850impl BlipFill {
851    /// Parses one complete picture-fill element with any namespace prefix.
852    pub fn from_xml(xml: &[u8]) -> Result<Self> {
853        let mut reader = Reader::from_reader(xml);
854        let mut buffer = Vec::new();
855        loop {
856            match reader
857                .read_event_into(&mut buffer)
858                .map_err(OxmlError::from)?
859            {
860                Event::Start(element)
861                    if matches_local_name(element.name().as_ref(), b"blipFill") =>
862                {
863                    return Self::from_element(&mut reader, &element);
864                }
865                Event::Empty(element)
866                    if matches_local_name(element.name().as_ref(), b"blipFill") =>
867                {
868                    return Self::from_start(&element);
869                }
870                Event::Start(element) | Event::Empty(element) => {
871                    return Err(unexpected(&element));
872                }
873                Event::Eof => return Err(missing_end("blipFill")),
874                _ => {}
875            }
876            buffer.clear();
877        }
878    }
879
880    fn from_start(start: &BytesStart<'_>) -> Result<Self> {
881        reject_conflicting_a_prefix(start)?;
882        Ok(Self {
883            dpi: optional_parse(start, b"dpi")?,
884            rotate_with_shape: optional_bool(start, b"rotWithShape")?,
885            raw_attributes: capture_unmodelled_attributes(start, &[b"dpi", b"rotWithShape"])?,
886            ..Self::default()
887        })
888    }
889
890    fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
891        let mut fill = Self::from_start(start)?;
892        let mut buffer = Vec::new();
893        let mut boundary = 0;
894        loop {
895            match reader
896                .read_event_into(&mut buffer)
897                .map_err(OxmlError::from)?
898            {
899                Event::Start(element) if matches_local_name(element.name().as_ref(), b"blip") => {
900                    reject_conflicting_a_prefix(&element)?;
901                    fill.blip = Some(Blip::from_element(reader, &element, start)?);
902                    boundary = 1;
903                }
904                Event::Empty(element) if matches_local_name(element.name().as_ref(), b"blip") => {
905                    reject_conflicting_a_prefix(&element)?;
906                    fill.blip = Some(Blip::from_start(&element));
907                    boundary = 1;
908                }
909                Event::Start(element)
910                    if matches_local_name(element.name().as_ref(), b"srcRect") =>
911                {
912                    fill.source_rect = Some(parse_rect_element(reader, &element)?);
913                    boundary = 2;
914                }
915                Event::Empty(element)
916                    if matches_local_name(element.name().as_ref(), b"srcRect") =>
917                {
918                    fill.source_rect = Some(parse_rect(&element)?);
919                    boundary = 2;
920                }
921                Event::Start(element)
922                    if matches_local_name(element.name().as_ref(), b"stretch") =>
923                {
924                    reject_conflicting_a_prefix(&element)?;
925                    fill.mode = Some(parse_stretch(reader)?);
926                    boundary = 3;
927                }
928                Event::Empty(element)
929                    if matches_local_name(element.name().as_ref(), b"stretch") =>
930                {
931                    reject_conflicting_a_prefix(&element)?;
932                    fill.mode = Some(BlipMode::Stretch {
933                        fill_rect: None,
934                        raw_children: OrderedRawChildren::default(),
935                    });
936                    boundary = 3;
937                }
938                Event::Start(element) if matches_local_name(element.name().as_ref(), b"tile") => {
939                    fill.mode = Some(BlipMode::Tile(parse_tile(reader, &element)?));
940                    boundary = 3;
941                }
942                Event::Empty(element) if matches_local_name(element.name().as_ref(), b"tile") => {
943                    fill.mode = Some(BlipMode::Tile(parse_empty_tile(&element)?));
944                    boundary = 3;
945                }
946                Event::Start(element) => fill
947                    .raw_children
948                    .push(boundary, capture_element(reader, &element)?),
949                Event::Empty(element) => fill
950                    .raw_children
951                    .push(boundary, capture_empty_element(&element)?),
952                Event::End(element) if matches_local_name(element.name().as_ref(), b"blipFill") => {
953                    break;
954                }
955                Event::Eof => return Err(missing_end("blipFill")),
956                _ => {}
957            }
958            buffer.clear();
959        }
960        Ok(fill)
961    }
962
963    fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
964        self.write_xml_as(writer, "a:blipFill")
965    }
966
967    /// Writes the picture fill under the caller's required root name.
968    pub fn write_xml_as<W: Write>(&self, writer: &mut Writer<W>, name: &str) -> Result<()> {
969        let mut start = BytesStart::new(name);
970        let dpi = self.dpi.map(|value| value.to_string());
971        if let Some(dpi) = dpi.as_deref() {
972            start.push_attribute(("dpi", dpi));
973        }
974        if let Some(rotate) = self.rotate_with_shape.map(bool_text) {
975            start.push_attribute(("rotWithShape", rotate));
976        }
977        push_raw_attributes(&mut start, &self.raw_attributes);
978        if self.blip.is_none()
979            && self.source_rect.is_none()
980            && self.mode.is_none()
981            && self.raw_children.is_empty()
982        {
983            return write_empty(writer, start);
984        }
985        write_start(writer, start)?;
986        emit_raw(writer, self.raw_children.at(0))?;
987        if let Some(blip) = &self.blip {
988            blip.write_xml(writer)?;
989        }
990        emit_raw(writer, self.raw_children.at(1))?;
991        if let Some(rect) = &self.source_rect {
992            write_rect(writer, "a:srcRect", rect)?;
993        }
994        emit_raw(writer, self.raw_children.at(2))?;
995        if let Some(mode) = &self.mode {
996            write_blip_mode(writer, mode)?;
997        }
998        emit_raw(writer, self.raw_children.at(3))?;
999        write_end(writer, name)
1000    }
1001
1002    pub fn raw_children(&self) -> &OrderedRawChildren {
1003        &self.raw_children
1004    }
1005}
1006
1007fn capture_unmodelled_attributes(
1008    start: &BytesStart<'_>,
1009    modelled: &[&[u8]],
1010) -> Result<Vec<(String, String)>> {
1011    let mut raw = Vec::new();
1012    for attribute in start.attributes() {
1013        let attribute = attribute.map_err(OxmlError::from)?;
1014        let key = attribute.key.as_ref();
1015        if !key.contains(&b':') && modelled.contains(&key) {
1016            continue;
1017        }
1018        let name = std::str::from_utf8(key)
1019            .map_err(OxmlError::from)?
1020            .to_owned();
1021        let value = attribute
1022            .decoded_and_normalized_value(XmlVersion::Implicit1_0, start.decoder())
1023            .map_err(OxmlError::from)?
1024            .into_owned();
1025        raw.push((name, value));
1026    }
1027    Ok(raw)
1028}
1029
1030fn push_raw_attributes(start: &mut BytesStart<'_>, attributes: &[(String, String)]) {
1031    for (name, value) in attributes {
1032        start.push_attribute((name.as_str(), value.as_str()));
1033    }
1034}
1035
1036fn parse_linear(start: &BytesStart<'_>) -> Result<LinearGradient> {
1037    reject_conflicting_a_prefix(start)?;
1038    Ok(LinearGradient {
1039        angle: Angle(required_i32(start, b"ang")?),
1040        scaled: optional_bool(start, b"scaled")?,
1041        raw_children: OrderedRawChildren::default(),
1042    })
1043}
1044
1045fn parse_linear_element(
1046    reader: &mut Reader<&[u8]>,
1047    start: &BytesStart<'_>,
1048) -> Result<LinearGradient> {
1049    let mut linear = parse_linear(start)?;
1050    linear.raw_children = capture_all_children(reader, b"lin")?;
1051    Ok(linear)
1052}
1053
1054fn parse_empty_path(start: &BytesStart<'_>) -> Result<PathGradient> {
1055    reject_conflicting_a_prefix(start)?;
1056    let value = required_attr(start, b"path")?;
1057    let kind = PathGradientKind::parse(&value).ok_or_else(|| invalid(start, b"path", value))?;
1058    Ok(PathGradient {
1059        kind,
1060        fill_to_rect: None,
1061        raw_children: OrderedRawChildren::default(),
1062    })
1063}
1064
1065fn parse_path(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<PathGradient> {
1066    let mut path = parse_empty_path(start)?;
1067    let mut buffer = Vec::new();
1068    let mut boundary = 0;
1069    loop {
1070        match reader
1071            .read_event_into(&mut buffer)
1072            .map_err(OxmlError::from)?
1073        {
1074            Event::Start(element) if matches_local_name(element.name().as_ref(), b"fillToRect") => {
1075                path.fill_to_rect = Some(parse_rect_element(reader, &element)?);
1076                boundary = 1;
1077            }
1078            Event::Empty(element) if matches_local_name(element.name().as_ref(), b"fillToRect") => {
1079                path.fill_to_rect = Some(parse_rect(&element)?);
1080                boundary = 1;
1081            }
1082            Event::Start(element) => path
1083                .raw_children
1084                .push(boundary, capture_element(reader, &element)?),
1085            Event::Empty(element) => path
1086                .raw_children
1087                .push(boundary, capture_empty_element(&element)?),
1088            Event::End(element) if matches_local_name(element.name().as_ref(), b"path") => break,
1089            Event::Eof => return Err(missing_end("path")),
1090            _ => {}
1091        }
1092        buffer.clear();
1093    }
1094    Ok(path)
1095}
1096
1097fn write_geometry<W: Write>(writer: &mut Writer<W>, geometry: &GradientGeometry) -> Result<()> {
1098    match geometry {
1099        GradientGeometry::Linear(linear) => {
1100            let angle = linear.angle.0.to_string();
1101            let mut start = BytesStart::new("a:lin");
1102            start.push_attribute(("ang", angle.as_str()));
1103            if let Some(scaled) = linear.scaled.map(bool_text) {
1104                start.push_attribute(("scaled", scaled));
1105            }
1106            if linear.raw_children.is_empty() {
1107                return write_empty(writer, start);
1108            }
1109            write_start(writer, start)?;
1110            emit_raw(writer, linear.raw_children.at(0))?;
1111            write_end(writer, "a:lin")
1112        }
1113        GradientGeometry::Path(path) => {
1114            let mut start = BytesStart::new("a:path");
1115            start.push_attribute(("path", path.kind.as_str()));
1116            if path.fill_to_rect.is_none() && path.raw_children.is_empty() {
1117                return write_empty(writer, start);
1118            }
1119            write_start(writer, start)?;
1120            emit_raw(writer, path.raw_children.at(0))?;
1121            if let Some(rect) = &path.fill_to_rect {
1122                write_rect(writer, "a:fillToRect", rect)?;
1123            }
1124            emit_raw(writer, path.raw_children.at(1))?;
1125            write_end(writer, "a:path")
1126        }
1127    }
1128}
1129
1130fn parse_stretch(reader: &mut Reader<&[u8]>) -> Result<BlipMode> {
1131    let mut fill_rect = None;
1132    let mut raw_children = OrderedRawChildren::default();
1133    let mut boundary = 0;
1134    let mut buffer = Vec::new();
1135    loop {
1136        match reader
1137            .read_event_into(&mut buffer)
1138            .map_err(OxmlError::from)?
1139        {
1140            Event::Start(element) if matches_local_name(element.name().as_ref(), b"fillRect") => {
1141                fill_rect = Some(parse_rect_element(reader, &element)?);
1142                boundary = 1;
1143            }
1144            Event::Empty(element) if matches_local_name(element.name().as_ref(), b"fillRect") => {
1145                fill_rect = Some(parse_rect(&element)?);
1146                boundary = 1;
1147            }
1148            Event::Start(element) => {
1149                raw_children.push(boundary, capture_element(reader, &element)?)
1150            }
1151            Event::Empty(element) => raw_children.push(boundary, capture_empty_element(&element)?),
1152            Event::End(element) if matches_local_name(element.name().as_ref(), b"stretch") => break,
1153            Event::Eof => return Err(missing_end("stretch")),
1154            _ => {}
1155        }
1156        buffer.clear();
1157    }
1158    Ok(BlipMode::Stretch {
1159        fill_rect,
1160        raw_children,
1161    })
1162}
1163
1164fn parse_empty_tile(start: &BytesStart<'_>) -> Result<Tile> {
1165    reject_conflicting_a_prefix(start)?;
1166    Ok(Tile {
1167        translation_x: optional_parse(start, b"tx")?,
1168        translation_y: optional_parse(start, b"ty")?,
1169        scale_x: optional_parse::<i32>(start, b"sx")?.map(Percent1000),
1170        scale_y: optional_parse::<i32>(start, b"sy")?.map(Percent1000),
1171        flip: optional_token(start, b"flip", &["none", "x", "y", "xy"])?,
1172        alignment: optional_token(
1173            start,
1174            b"algn",
1175            &["tl", "t", "tr", "l", "ctr", "r", "bl", "b", "br"],
1176        )?,
1177        raw_children: OrderedRawChildren::default(),
1178    })
1179}
1180
1181fn parse_tile(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Tile> {
1182    let mut tile = parse_empty_tile(start)?;
1183    tile.raw_children = capture_all_children(reader, b"tile")?;
1184    Ok(tile)
1185}
1186
1187fn write_blip_mode<W: Write>(writer: &mut Writer<W>, mode: &BlipMode) -> Result<()> {
1188    match mode {
1189        BlipMode::Stretch {
1190            fill_rect,
1191            raw_children,
1192        } => {
1193            if fill_rect.is_none() && raw_children.is_empty() {
1194                return write_empty(writer, BytesStart::new("a:stretch"));
1195            }
1196            write_start(writer, BytesStart::new("a:stretch"))?;
1197            emit_raw(writer, raw_children.at(0))?;
1198            if let Some(rect) = fill_rect {
1199                write_rect(writer, "a:fillRect", rect)?;
1200            }
1201            emit_raw(writer, raw_children.at(1))?;
1202            write_end(writer, "a:stretch")
1203        }
1204        BlipMode::Tile(tile) => {
1205            let mut start = BytesStart::new("a:tile");
1206            let tx = tile.translation_x.map(|value| value.to_string());
1207            let ty = tile.translation_y.map(|value| value.to_string());
1208            let sx = tile.scale_x.map(|value| value.0.to_string());
1209            let sy = tile.scale_y.map(|value| value.0.to_string());
1210            for (name, value) in [
1211                ("tx", tx.as_deref()),
1212                ("ty", ty.as_deref()),
1213                ("sx", sx.as_deref()),
1214                ("sy", sy.as_deref()),
1215            ] {
1216                if let Some(value) = value {
1217                    start.push_attribute((name, value));
1218                }
1219            }
1220            if let Some(flip) = tile.flip.as_deref() {
1221                start.push_attribute(("flip", flip));
1222            }
1223            if let Some(alignment) = tile.alignment.as_deref() {
1224                start.push_attribute(("algn", alignment));
1225            }
1226            if tile.raw_children.is_empty() {
1227                return write_empty(writer, start);
1228            }
1229            write_start(writer, start)?;
1230            emit_raw(writer, tile.raw_children.at(0))?;
1231            write_end(writer, "a:tile")
1232        }
1233    }
1234}
1235
1236fn parse_rect(start: &BytesStart<'_>) -> Result<RelativeRect> {
1237    reject_conflicting_a_prefix(start)?;
1238    Ok(RelativeRect {
1239        left: optional_parse::<i32>(start, b"l")?.map(Percent1000),
1240        top: optional_parse::<i32>(start, b"t")?.map(Percent1000),
1241        right: optional_parse::<i32>(start, b"r")?.map(Percent1000),
1242        bottom: optional_parse::<i32>(start, b"b")?.map(Percent1000),
1243        raw_children: OrderedRawChildren::default(),
1244    })
1245}
1246
1247fn parse_rect_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<RelativeRect> {
1248    let mut rect = parse_rect(start)?;
1249    rect.raw_children = capture_all_children(reader, local_name(start.name().as_ref()))?;
1250    Ok(rect)
1251}
1252
1253fn write_rect<W: Write>(writer: &mut Writer<W>, name: &str, rect: &RelativeRect) -> Result<()> {
1254    let left = rect.left.map(|value| value.0.to_string());
1255    let top = rect.top.map(|value| value.0.to_string());
1256    let right = rect.right.map(|value| value.0.to_string());
1257    let bottom = rect.bottom.map(|value| value.0.to_string());
1258    let mut start = BytesStart::new(name);
1259    for (name, value) in [
1260        ("l", left.as_deref()),
1261        ("t", top.as_deref()),
1262        ("r", right.as_deref()),
1263        ("b", bottom.as_deref()),
1264    ] {
1265        if let Some(value) = value {
1266            start.push_attribute((name, value));
1267        }
1268    }
1269    if rect.raw_children.is_empty() {
1270        return write_empty(writer, start);
1271    }
1272    write_start(writer, start)?;
1273    emit_raw(writer, rect.raw_children.at(0))?;
1274    write_end(writer, name)
1275}
1276
1277fn parse_color_container(
1278    reader: &mut Reader<&[u8]>,
1279    end_name: &[u8],
1280) -> Result<(Option<ColorChoice>, OrderedRawChildren)> {
1281    let mut color = None;
1282    let mut raw_children = OrderedRawChildren::default();
1283    let mut boundary = 0;
1284    let mut buffer = Vec::new();
1285    loop {
1286        match reader
1287            .read_event_into(&mut buffer)
1288            .map_err(OxmlError::from)?
1289        {
1290            Event::Start(element) if is_color(element.name().as_ref()) => {
1291                color = Some(ColorChoice::from_xml(reader, &element)?);
1292                boundary = 1;
1293            }
1294            Event::Empty(element) if is_color(element.name().as_ref()) => {
1295                color = Some(ColorChoice::from_empty_xml(&element)?);
1296                boundary = 1;
1297            }
1298            Event::Start(element) => {
1299                raw_children.push(boundary, capture_element(reader, &element)?);
1300            }
1301            Event::Empty(element) => {
1302                raw_children.push(boundary, capture_empty_element(&element)?);
1303            }
1304            Event::End(element) if matches_local_name(element.name().as_ref(), end_name) => break,
1305            Event::Eof => return Err(missing_end(&String::from_utf8_lossy(end_name))),
1306            _ => {}
1307        }
1308        buffer.clear();
1309    }
1310    Ok((color, raw_children))
1311}
1312
1313fn write_color_container<W: Write>(
1314    writer: &mut Writer<W>,
1315    name: &str,
1316    color: Option<&ColorChoice>,
1317    raw_children: &OrderedRawChildren,
1318) -> Result<()> {
1319    write_start(writer, BytesStart::new(name))?;
1320    emit_raw(writer, raw_children.at(0))?;
1321    if let Some(color) = color {
1322        color.to_xml(writer)?;
1323    }
1324    emit_raw(writer, raw_children.at(1))?;
1325    write_end(writer, name)
1326}
1327
1328fn read_color_children(
1329    reader: &mut Reader<&[u8]>,
1330    end_name: &[u8],
1331    color: &mut Option<ColorChoice>,
1332    raw_children: &mut OrderedRawChildren,
1333) -> Result<()> {
1334    let mut buffer = Vec::new();
1335    let mut boundary = 0;
1336    loop {
1337        match reader
1338            .read_event_into(&mut buffer)
1339            .map_err(OxmlError::from)?
1340        {
1341            Event::Start(element) if is_color(element.name().as_ref()) => {
1342                *color = Some(ColorChoice::from_xml(reader, &element)?);
1343                boundary = 1;
1344            }
1345            Event::Empty(element) if is_color(element.name().as_ref()) => {
1346                *color = Some(ColorChoice::from_empty_xml(&element)?);
1347                boundary = 1;
1348            }
1349            Event::Start(element) => {
1350                raw_children.push(boundary, capture_element(reader, &element)?)
1351            }
1352            Event::Empty(element) => raw_children.push(boundary, capture_empty_element(&element)?),
1353            Event::End(element) if matches_local_name(element.name().as_ref(), end_name) => break,
1354            Event::Eof => return Err(missing_end(&String::from_utf8_lossy(end_name))),
1355            _ => {}
1356        }
1357        buffer.clear();
1358    }
1359    Ok(())
1360}
1361
1362fn capture_all_children(reader: &mut Reader<&[u8]>, end_name: &[u8]) -> Result<OrderedRawChildren> {
1363    let mut raw_children = OrderedRawChildren::default();
1364    let mut buffer = Vec::new();
1365    loop {
1366        match reader
1367            .read_event_into(&mut buffer)
1368            .map_err(OxmlError::from)?
1369        {
1370            Event::Start(element) => raw_children.push(0, capture_element(reader, &element)?),
1371            Event::Empty(element) => raw_children.push(0, capture_empty_element(&element)?),
1372            Event::End(element) if matches_local_name(element.name().as_ref(), end_name) => break,
1373            Event::Eof => return Err(missing_end(&String::from_utf8_lossy(end_name))),
1374            _ => {}
1375        }
1376        buffer.clear();
1377    }
1378    Ok(raw_children)
1379}
1380
1381fn is_color(name: &[u8]) -> bool {
1382    matches!(
1383        local_name(name),
1384        b"srgbClr" | b"schemeClr" | b"sysClr" | b"prstClr"
1385    )
1386}
1387
1388fn validate_gradient_position(value: i32) -> Result<()> {
1389    if (0..=100_000).contains(&value) {
1390        Ok(())
1391    } else {
1392        Err(FillError::GradientStopOutOfRange(value))
1393    }
1394}
1395
1396fn required_attr(start: &BytesStart<'_>, name: &[u8]) -> Result<String> {
1397    get_attr(start, name).ok_or_else(|| FillError::MissingAttribute {
1398        element: String::from_utf8_lossy(local_name(start.name().as_ref())).into_owned(),
1399        attribute: String::from_utf8_lossy(name).into_owned(),
1400    })
1401}
1402
1403fn required_i32(start: &BytesStart<'_>, name: &[u8]) -> Result<i32> {
1404    let value = required_attr(start, name)?;
1405    value.parse().map_err(|_| invalid(start, name, value))
1406}
1407
1408fn optional_parse<T: std::str::FromStr>(start: &BytesStart<'_>, name: &[u8]) -> Result<Option<T>> {
1409    get_attr(start, name)
1410        .map(|value| value.parse().map_err(|_| invalid(start, name, value)))
1411        .transpose()
1412}
1413
1414fn optional_bool(start: &BytesStart<'_>, name: &[u8]) -> Result<Option<bool>> {
1415    get_attr(start, name)
1416        .map(|value| match value.as_str() {
1417            "1" | "true" => Ok(true),
1418            "0" | "false" => Ok(false),
1419            _ => Err(invalid(start, name, value)),
1420        })
1421        .transpose()
1422}
1423
1424fn optional_token(start: &BytesStart<'_>, name: &[u8], allowed: &[&str]) -> Result<Option<String>> {
1425    get_attr(start, name)
1426        .map(|value| {
1427            if allowed.contains(&value.as_str()) {
1428                Ok(value)
1429            } else {
1430                Err(invalid(start, name, value))
1431            }
1432        })
1433        .transpose()
1434}
1435
1436fn invalid(start: &BytesStart<'_>, attribute: &[u8], value: String) -> FillError {
1437    FillError::InvalidAttribute {
1438        element: String::from_utf8_lossy(local_name(start.name().as_ref())).into_owned(),
1439        attribute: String::from_utf8_lossy(attribute).into_owned(),
1440        value,
1441    }
1442}
1443
1444fn unexpected(start: &BytesStart<'_>) -> FillError {
1445    FillError::UnexpectedElement(String::from_utf8_lossy(start.name().as_ref()).into_owned())
1446}
1447
1448fn missing_end(name: &str) -> FillError {
1449    FillError::Xml(OxmlError::MissingElement(format!("closing a:{name}")))
1450}
1451
1452const fn bool_text(value: bool) -> &'static str {
1453    if value { "1" } else { "0" }
1454}
1455
1456fn emit_raw<'a, W: Write>(
1457    writer: &mut Writer<W>,
1458    children: impl Iterator<Item = &'a [u8]>,
1459) -> Result<()> {
1460    for child in children {
1461        writer.get_mut().write_all(child).map_err(OxmlError::from)?;
1462    }
1463    Ok(())
1464}
1465
1466fn write_start<W: Write>(writer: &mut Writer<W>, start: BytesStart<'_>) -> Result<()> {
1467    writer
1468        .write_event(Event::Start(start))
1469        .map_err(OxmlError::from)?;
1470    Ok(())
1471}
1472
1473fn write_empty<W: Write>(writer: &mut Writer<W>, start: BytesStart<'_>) -> Result<()> {
1474    writer
1475        .write_event(Event::Empty(start))
1476        .map_err(OxmlError::from)?;
1477    Ok(())
1478}
1479
1480fn write_end<W: Write>(writer: &mut Writer<W>, name: &str) -> Result<()> {
1481    writer
1482        .write_event(Event::End(BytesEnd::new(name)))
1483        .map_err(OxmlError::from)?;
1484    Ok(())
1485}
1486
1487#[cfg(test)]
1488mod tests {
1489    use super::{Fill, FillError, GradientGeometry};
1490
1491    #[test]
1492    fn every_fill_form_round_trips_and_gradient_stops_keep_document_order() {
1493        let cases: &[&[u8]] = &[
1494            br#"<a:noFill/>"#,
1495            br#"<a:solidFill><a:schemeClr val="accent1"/></a:solidFill>"#,
1496            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>"#,
1497            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>"#,
1498            br#"<a:pattFill prst="pct10"><a:fgClr><a:srgbClr val="112233"/></a:fgClr><a:bgClr><a:schemeClr val="bg1"/></a:bgClr></a:pattFill>"#,
1499            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>"#,
1500            br#"<a:blipFill><a:blip r:link="rId8"/><a:tile tx="10" ty="20" sx="50000" sy="75000" flip="x" algn="ctr"/></a:blipFill>"#,
1501        ];
1502
1503        for xml in cases {
1504            let parsed = Fill::from_xml(xml).unwrap();
1505            let written = parsed.to_xml().unwrap();
1506            assert_eq!(Fill::from_xml(&written).unwrap(), parsed);
1507        }
1508
1509        let Fill::Gradient(gradient) = Fill::from_xml(cases[2]).unwrap() else {
1510            panic!("expected gradient")
1511        };
1512        assert_eq!(
1513            gradient
1514                .stops
1515                .iter()
1516                .map(|stop| stop.position.0)
1517                .collect::<Vec<_>>(),
1518            vec![75_000, 25_000]
1519        );
1520        assert!(matches!(
1521            gradient.geometry,
1522            Some(GradientGeometry::Linear(_))
1523        ));
1524    }
1525
1526    #[test]
1527    fn fill_forms_read_any_prefix_and_write_fixed_a_prefix_in_schema_order() {
1528        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();
1529        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>"#);
1530
1531        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();
1532        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>"#);
1533    }
1534
1535    #[test]
1536    fn blip_relationship_prefix_is_declared_in_canonical_output() {
1537        let fill = Fill::from_xml(
1538            br#"<a:blipFill><a:blip xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="rId1"/></a:blipFill>"#,
1539        )
1540        .unwrap();
1541        let written = String::from_utf8(fill.to_xml().unwrap()).unwrap();
1542
1543        assert!(written.contains(
1544            "<a:blip xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" r:embed=\"rId1\"/>"
1545        ));
1546    }
1547
1548    #[test]
1549    fn unknown_fill_children_round_trip_byte_for_byte_in_place() {
1550        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>"#;
1551        let written = Fill::from_xml(xml).unwrap().to_xml().unwrap();
1552        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>"#);
1553
1554        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>"#;
1555        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>"#);
1556
1557        let pattern = br#"<z:pattFill><z:fgClr><x:before/><z:srgbClr val="102030"/><x:after x:v="kept"/></z:fgClr></z:pattFill>"#;
1558        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>"#);
1559
1560        let extension_only_pattern =
1561            br#"<z:pattFill><z:fgClr><x:extension x:v="kept"/></z:fgClr></z:pattFill>"#;
1562        assert_eq!(
1563            Fill::from_xml(extension_only_pattern)
1564                .unwrap()
1565                .to_xml()
1566                .unwrap(),
1567            br#"<a:pattFill><a:fgClr><x:extension x:v="kept"/></a:fgClr></a:pattFill>"#
1568        );
1569
1570        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>"#;
1571        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>"#);
1572
1573        let extension_only_stop_list =
1574            br#"<z:gradFill><z:gsLst><x:extension x:v="kept"/></z:gsLst></z:gradFill>"#;
1575        assert_eq!(
1576            Fill::from_xml(extension_only_stop_list)
1577                .unwrap()
1578                .to_xml()
1579                .unwrap(),
1580            br#"<a:gradFill><a:gsLst><x:extension x:v="kept"/></a:gsLst></a:gradFill>"#
1581        );
1582
1583        let path_leaf_extension = br#"<z:gradFill><z:path path="rect"><z:fillToRect><x:pathRectExt/></z:fillToRect></z:path></z:gradFill>"#;
1584        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>"#);
1585
1586        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>"#;
1587        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>"#);
1588    }
1589
1590    #[test]
1591    fn blip_alpha_mod_fix_is_typed_bounded_and_schema_ordered() {
1592        let parse = |children: &str| {
1593            let xml = format!(
1594                r#"<a:blipFill xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><a:blip r:embed="rId1">{children}</a:blip></a:blipFill>"#
1595            );
1596            super::BlipFill::from_xml(xml.as_bytes())
1597        };
1598
1599        let fill = parse(
1600            r#"<x:before xmlns:x="urn:producer"/><q:alphaModFix xmlns:q="http://schemas.openxmlformats.org/drawingml/2006/main" amt="30000"/><x:after xmlns:x="urn:producer"/>"#,
1601        )
1602        .unwrap();
1603        let blip = fill.blip.unwrap();
1604        assert_eq!(blip.alpha_modulation_fix, Some(super::Percent1000(30_000)));
1605        let xml = super::Fill::Blip(super::BlipFill {
1606            blip: Some(blip),
1607            ..Default::default()
1608        })
1609        .to_xml()
1610        .unwrap();
1611        let xml = String::from_utf8(xml).unwrap();
1612        let before = xml.find("x:before").unwrap();
1613        let alpha = xml.find("a:alphaModFix").unwrap();
1614        let after = xml.find("x:after").unwrap();
1615        assert!(before < alpha && alpha < after, "{xml}");
1616        assert!(xml.contains("amt=\"30000\""), "{xml}");
1617
1618        let inherited_alias = super::BlipFill::from_xml(
1619            br#"<q:blipFill xmlns:q="http://schemas.openxmlformats.org/drawingml/2006/main"><q:blip><q:alphaModFix amt="25000"/></q:blip></q:blipFill>"#,
1620        )
1621        .unwrap();
1622        assert_eq!(
1623            inherited_alias.blip.unwrap().alpha_modulation_fix,
1624            Some(super::Percent1000(25_000))
1625        );
1626
1627        let qualified_amount =
1628            parse(r#"<a:alphaModFix xmlns:producer="urn:producer" producer:amt="20000"/>"#)
1629                .unwrap();
1630        assert_eq!(
1631            qualified_amount.blip.unwrap().alpha_modulation_fix,
1632            Some(super::Percent1000(100_000))
1633        );
1634
1635        let duplicate = parse(
1636            r#"<a:alphaModFix amt="40000"/><a:alphaModFix producer:keep="yes" xmlns:producer="urn:producer" amt="20000"/>"#,
1637        )
1638        .unwrap();
1639        assert_eq!(
1640            duplicate.blip.as_ref().unwrap().alpha_modulation_fix,
1641            Some(super::Percent1000(40_000))
1642        );
1643        let duplicate_xml = super::Fill::Blip(duplicate).to_xml().unwrap();
1644        let duplicate_xml = String::from_utf8(duplicate_xml).unwrap();
1645        assert_eq!(duplicate_xml.matches("alphaModFix").count(), 2);
1646        assert!(duplicate_xml.contains("producer:keep=\"yes\""));
1647
1648        for invalid in ["-1", "100001", "not-a-number"] {
1649            assert!(parse(&format!(r#"<a:alphaModFix amt="{invalid}"/>"#)).is_err());
1650        }
1651        assert!(parse(r#"<a:alphaModFix amt="30000">content</a:alphaModFix>"#).is_err());
1652        let mut invalid_write = parse(r#"<a:alphaModFix amt="30000"/>"#).unwrap();
1653        invalid_write.blip.as_mut().unwrap().alpha_modulation_fix = Some(super::Percent1000(-1));
1654        assert!(super::Fill::Blip(invalid_write).to_xml().is_err());
1655        let foreign = parse(
1656            r#"<x:alphaModFix xmlns:x="urn:producer" amt="30000" producer:keep="yes" xmlns:producer="urn:producer"/>"#,
1657        )
1658        .unwrap();
1659        assert_eq!(foreign.blip.as_ref().unwrap().alpha_modulation_fix, None);
1660        let foreign_xml = super::Fill::Blip(foreign).to_xml().unwrap();
1661        assert!(
1662            String::from_utf8(foreign_xml)
1663                .unwrap()
1664                .contains("producer:keep=\"yes\"")
1665        );
1666    }
1667
1668    #[test]
1669    fn malformed_fill_values_return_errors_without_panicking() {
1670        let cases: &[&[u8]] = &[
1671            br#"<a:gradFill rotWithShape="maybe"/>"#,
1672            br#"<a:gradFill><a:gsLst><a:gs pos="100001"/></a:gsLst></a:gradFill>"#,
1673            br#"<a:gradFill><a:gsLst><a:gs/></a:gsLst></a:gradFill>"#,
1674            br#"<a:gradFill><a:lin ang="ninety"/></a:gradFill>"#,
1675            br#"<a:gradFill><a:path path="triangle"/></a:gradFill>"#,
1676            br#"<a:blipFill dpi="many"/>"#,
1677            br#"<a:blipFill><a:srcRect l="half"/></a:blipFill>"#,
1678            br#"<a:blipFill><a:tile sx="large"/></a:blipFill>"#,
1679            br#"<a:gradFill flip="diagonal"/>"#,
1680            br#"<a:blipFill><a:tile algn="middle"/></a:blipFill>"#,
1681        ];
1682        for xml in cases {
1683            let result = std::panic::catch_unwind(|| Fill::from_xml(xml));
1684            assert!(
1685                result.is_ok(),
1686                "parser panicked for {}",
1687                String::from_utf8_lossy(xml)
1688            );
1689            assert!(
1690                result.unwrap().is_err(),
1691                "malformed fill parsed successfully"
1692            );
1693        }
1694        assert!(matches!(
1695            Fill::from_xml(cases[1]),
1696            Err(FillError::GradientStopOutOfRange(100_001))
1697        ));
1698    }
1699}