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#[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#[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 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 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 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 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 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#[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#[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#[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#[derive(Clone, Debug, Eq, PartialEq)]
282pub enum GradientGeometry {
283 Linear(LinearGradient),
284 Path(PathGradient),
285}
286
287#[derive(Clone, Debug, Default, Eq, PartialEq)]
289pub struct LinearGradient {
290 pub angle: Angle,
293 pub scaled: Option<bool>,
294 angle_omitted: bool,
296 raw_children: OrderedRawChildren,
297}
298
299#[derive(Clone, Copy, Debug, Eq, PartialEq)]
301pub enum PathGradientKind {
302 Shape,
303 Circle,
304 Rectangle,
305}
306
307impl PathGradientKind {
308 fn parse(value: &str) -> Option<Self> {
309 match value {
310 "shape" => Some(Self::Shape),
311 "circle" => Some(Self::Circle),
312 "rect" => Some(Self::Rectangle),
313 _ => None,
314 }
315 }
316
317 const fn as_str(self) -> &'static str {
318 match self {
319 Self::Shape => "shape",
320 Self::Circle => "circle",
321 Self::Rectangle => "rect",
322 }
323 }
324}
325
326#[derive(Clone, Debug, Eq, PartialEq)]
328pub struct PathGradient {
329 pub kind: PathGradientKind,
332 pub fill_to_rect: Option<RelativeRect>,
333 kind_omitted: bool,
335 raw_children: OrderedRawChildren,
336}
337
338#[derive(Clone, Debug, Default, Eq, PartialEq)]
340pub struct RelativeRect {
341 pub left: Option<Percent1000>,
342 pub top: Option<Percent1000>,
343 pub right: Option<Percent1000>,
344 pub bottom: Option<Percent1000>,
345 raw_children: OrderedRawChildren,
346}
347
348#[derive(Clone, Debug, Default, Eq, PartialEq)]
350pub struct GradientFill {
351 pub flip: Option<String>,
352 pub rotate_with_shape: Option<bool>,
353 pub stops: Vec<GradientStop>,
354 pub geometry: Option<GradientGeometry>,
355 pub tile_rect: Option<RelativeRect>,
356 raw_children: OrderedRawChildren,
357 stop_list_raw_children: OrderedRawChildren,
358}
359
360impl GradientFill {
361 fn from_start(start: &BytesStart<'_>) -> Result<Self> {
362 reject_conflicting_a_prefix(start)?;
363 Ok(Self {
364 flip: optional_token(start, b"flip", &["none", "x", "y", "xy"])?,
365 rotate_with_shape: optional_bool(start, b"rotWithShape")?,
366 ..Self::default()
367 })
368 }
369
370 fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
371 let mut fill = Self::from_start(start)?;
372 let mut buffer = Vec::new();
373 let mut boundary = 0;
374 loop {
375 match reader
376 .read_event_into(&mut buffer)
377 .map_err(OxmlError::from)?
378 {
379 Event::Start(element) if matches_local_name(element.name().as_ref(), b"gsLst") => {
380 reject_conflicting_a_prefix(&element)?;
381 fill.read_stop_list(reader)?;
382 boundary = 1;
383 }
384 Event::Start(element) if matches_local_name(element.name().as_ref(), b"lin") => {
385 fill.geometry = Some(GradientGeometry::Linear(parse_linear_element(
386 reader, &element,
387 )?));
388 boundary = 2;
389 }
390 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"lin") => {
391 fill.geometry = Some(GradientGeometry::Linear(parse_linear(&element)?));
392 boundary = 2;
393 }
394 Event::Start(element) if matches_local_name(element.name().as_ref(), b"path") => {
395 fill.geometry = Some(GradientGeometry::Path(parse_path(reader, &element)?));
396 boundary = 2;
397 }
398 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"path") => {
399 fill.geometry = Some(GradientGeometry::Path(parse_empty_path(&element)?));
400 boundary = 2;
401 }
402 Event::Start(element)
403 if matches_local_name(element.name().as_ref(), b"tileRect") =>
404 {
405 fill.tile_rect = Some(parse_rect_element(reader, &element)?);
406 boundary = 3;
407 }
408 Event::Empty(element)
409 if matches_local_name(element.name().as_ref(), b"tileRect") =>
410 {
411 fill.tile_rect = Some(parse_rect(&element)?);
412 boundary = 3;
413 }
414 Event::Start(element) => fill
415 .raw_children
416 .push(boundary, capture_element(reader, &element)?),
417 Event::Empty(element) => fill
418 .raw_children
419 .push(boundary, capture_empty_element(&element)?),
420 Event::End(element) if matches_local_name(element.name().as_ref(), b"gradFill") => {
421 break;
422 }
423 Event::Eof => return Err(missing_end("gradFill")),
424 _ => {}
425 }
426 buffer.clear();
427 }
428 Ok(fill)
429 }
430
431 fn read_stop_list(&mut self, reader: &mut Reader<&[u8]>) -> Result<()> {
432 let mut buffer = Vec::new();
433 loop {
434 match reader
435 .read_event_into(&mut buffer)
436 .map_err(OxmlError::from)?
437 {
438 Event::Start(element) if matches_local_name(element.name().as_ref(), b"gs") => {
439 self.stops
440 .push(GradientStop::from_element(reader, &element)?);
441 }
442 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"gs") => {
443 self.stops.push(GradientStop::from_empty(&element)?);
444 }
445 Event::Start(element) => self
446 .stop_list_raw_children
447 .push(self.stops.len(), capture_element(reader, &element)?),
448 Event::Empty(element) => self
449 .stop_list_raw_children
450 .push(self.stops.len(), capture_empty_element(&element)?),
451 Event::End(element) if matches_local_name(element.name().as_ref(), b"gsLst") => {
452 break;
453 }
454 Event::Eof => return Err(missing_end("gsLst")),
455 _ => {}
456 }
457 buffer.clear();
458 }
459 Ok(())
460 }
461
462 fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
463 let mut start = BytesStart::new("a:gradFill");
464 if let Some(flip) = self.flip.as_deref() {
465 start.push_attribute(("flip", flip));
466 }
467 let rotate = self.rotate_with_shape.map(bool_text);
468 if let Some(rotate) = rotate {
469 start.push_attribute(("rotWithShape", rotate));
470 }
471 if self.stops.is_empty()
472 && self.geometry.is_none()
473 && self.tile_rect.is_none()
474 && self.stop_list_raw_children.is_empty()
475 && self.raw_children.is_empty()
476 {
477 return write_empty(writer, start);
478 }
479 write_start(writer, start)?;
480 emit_raw(writer, self.raw_children.at(0))?;
481 if !self.stops.is_empty() || !self.stop_list_raw_children.is_empty() {
482 write_start(writer, BytesStart::new("a:gsLst"))?;
483 for boundary in 0..=self.stops.len() {
484 emit_raw(writer, self.stop_list_raw_children.at(boundary))?;
485 if let Some(stop) = self.stops.get(boundary) {
486 stop.write_xml(writer)?;
487 }
488 }
489 write_end(writer, "a:gsLst")?;
490 }
491 emit_raw(writer, self.raw_children.at(1))?;
492 if let Some(geometry) = &self.geometry {
493 write_geometry(writer, geometry)?;
494 }
495 emit_raw(writer, self.raw_children.at(2))?;
496 if let Some(rect) = &self.tile_rect {
497 write_rect(writer, "a:tileRect", rect)?;
498 }
499 emit_raw(writer, self.raw_children.at(3))?;
500 write_end(writer, "a:gradFill")
501 }
502
503 pub fn raw_children(&self) -> &OrderedRawChildren {
504 &self.raw_children
505 }
506}
507
508#[derive(Clone, Debug, Default, Eq, PartialEq)]
510pub struct PatternFill {
511 pub preset: Option<String>,
512 pub foreground: Option<ColorChoice>,
513 pub background: Option<ColorChoice>,
514 raw_children: OrderedRawChildren,
515 foreground_raw_children: OrderedRawChildren,
516 background_raw_children: OrderedRawChildren,
517}
518
519impl PatternFill {
520 fn from_start(start: &BytesStart<'_>) -> Result<Self> {
521 reject_conflicting_a_prefix(start)?;
522 Ok(Self {
523 preset: get_attr(start, b"prst"),
524 ..Self::default()
525 })
526 }
527
528 fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
529 let mut fill = Self::from_start(start)?;
530 let mut buffer = Vec::new();
531 let mut boundary = 0;
532 loop {
533 match reader
534 .read_event_into(&mut buffer)
535 .map_err(OxmlError::from)?
536 {
537 Event::Start(element) if matches_local_name(element.name().as_ref(), b"fgClr") => {
538 reject_conflicting_a_prefix(&element)?;
539 (fill.foreground, fill.foreground_raw_children) =
540 parse_color_container(reader, b"fgClr")?;
541 boundary = 1;
542 }
543 Event::Start(element) if matches_local_name(element.name().as_ref(), b"bgClr") => {
544 reject_conflicting_a_prefix(&element)?;
545 (fill.background, fill.background_raw_children) =
546 parse_color_container(reader, b"bgClr")?;
547 boundary = 2;
548 }
549 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"fgClr") => {
550 reject_conflicting_a_prefix(&element)?;
551 boundary = 1
552 }
553 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"bgClr") => {
554 reject_conflicting_a_prefix(&element)?;
555 boundary = 2
556 }
557 Event::Start(element) => fill
558 .raw_children
559 .push(boundary, capture_element(reader, &element)?),
560 Event::Empty(element) => fill
561 .raw_children
562 .push(boundary, capture_empty_element(&element)?),
563 Event::End(element) if matches_local_name(element.name().as_ref(), b"pattFill") => {
564 break;
565 }
566 Event::Eof => return Err(missing_end("pattFill")),
567 _ => {}
568 }
569 buffer.clear();
570 }
571 Ok(fill)
572 }
573
574 fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
575 let mut start = BytesStart::new("a:pattFill");
576 if let Some(preset) = self.preset.as_deref() {
577 start.push_attribute(("prst", preset));
578 }
579 if self.foreground.is_none()
580 && self.background.is_none()
581 && self.foreground_raw_children.is_empty()
582 && self.background_raw_children.is_empty()
583 && self.raw_children.is_empty()
584 {
585 return write_empty(writer, start);
586 }
587 write_start(writer, start)?;
588 emit_raw(writer, self.raw_children.at(0))?;
589 if self.foreground.is_some() || !self.foreground_raw_children.is_empty() {
590 write_color_container(
591 writer,
592 "a:fgClr",
593 self.foreground.as_ref(),
594 &self.foreground_raw_children,
595 )?;
596 }
597 emit_raw(writer, self.raw_children.at(1))?;
598 if self.background.is_some() || !self.background_raw_children.is_empty() {
599 write_color_container(
600 writer,
601 "a:bgClr",
602 self.background.as_ref(),
603 &self.background_raw_children,
604 )?;
605 }
606 emit_raw(writer, self.raw_children.at(2))?;
607 write_end(writer, "a:pattFill")
608 }
609
610 pub fn raw_children(&self) -> &OrderedRawChildren {
611 &self.raw_children
612 }
613}
614
615#[derive(Clone, Debug, Default, Eq, PartialEq)]
617pub struct Blip {
618 pub embed: Option<String>,
619 pub link: Option<String>,
620 pub alpha_modulation_fix: Option<Percent1000>,
622 alpha_modulation_fix_raw_attributes: Vec<(String, String)>,
623 raw_children: OrderedRawChildren,
624}
625
626impl Blip {
627 fn from_start(start: &BytesStart<'_>) -> Self {
628 Self {
629 embed: get_attr(start, b"embed"),
630 link: get_attr(start, b"link"),
631 alpha_modulation_fix: None,
632 alpha_modulation_fix_raw_attributes: Vec::new(),
633 raw_children: OrderedRawChildren::default(),
634 }
635 }
636
637 fn from_element(
638 reader: &mut Reader<&[u8]>,
639 start: &BytesStart<'_>,
640 ancestor: &BytesStart<'_>,
641 ) -> Result<Self> {
642 let mut blip = Self::from_start(start);
643 let mut buffer = Vec::new();
644 let mut boundary = 0;
645 loop {
646 match reader
647 .read_event_into(&mut buffer)
648 .map_err(OxmlError::from)?
649 {
650 Event::Start(element)
651 if blip.alpha_modulation_fix.is_none()
652 && is_drawingml_alpha_mod_fix(&element, start, ancestor)? =>
653 {
654 let (amount, raw_attributes) = parse_alpha_modulation_fix(&element)?;
655 read_empty_effect_body(reader, &element)?;
656 blip.alpha_modulation_fix = Some(amount);
657 blip.alpha_modulation_fix_raw_attributes = raw_attributes;
658 boundary = 1;
659 }
660 Event::Empty(element)
661 if blip.alpha_modulation_fix.is_none()
662 && is_drawingml_alpha_mod_fix(&element, start, ancestor)? =>
663 {
664 let (amount, raw_attributes) = parse_alpha_modulation_fix(&element)?;
665 blip.alpha_modulation_fix = Some(amount);
666 blip.alpha_modulation_fix_raw_attributes = raw_attributes;
667 boundary = 1;
668 }
669 Event::Start(element) => blip
670 .raw_children
671 .push(boundary, capture_element(reader, &element)?),
672 Event::Empty(element) => blip
673 .raw_children
674 .push(boundary, capture_empty_element(&element)?),
675 Event::End(element) if matches_local_name(element.name().as_ref(), b"blip") => {
676 break;
677 }
678 Event::Eof => return Err(missing_end("blip")),
679 _ => {}
680 }
681 buffer.clear();
682 }
683 Ok(blip)
684 }
685
686 fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
687 if let Some(amount) = self.alpha_modulation_fix
688 && !(0..=100_000).contains(&amount.0)
689 {
690 return Err(FillError::InvalidAttribute {
691 element: "alphaModFix".to_owned(),
692 attribute: "amt".to_owned(),
693 value: amount.0.to_string(),
694 });
695 }
696 let mut start = BytesStart::new("a:blip");
697 if self.embed.is_some() || self.link.is_some() {
698 start.push_attribute(("xmlns:r", R_NS));
699 }
700 if let Some(embed) = self.embed.as_deref() {
701 start.push_attribute(("r:embed", embed));
702 }
703 if let Some(link) = self.link.as_deref() {
704 start.push_attribute(("r:link", link));
705 }
706 if self.alpha_modulation_fix.is_none() && self.raw_children.is_empty() {
707 return write_empty(writer, start);
708 }
709 write_start(writer, start)?;
710 emit_raw(writer, self.raw_children.at(0))?;
711 if let Some(amount) = self.alpha_modulation_fix {
712 let amount = amount.0.to_string();
713 let mut effect = BytesStart::new("a:alphaModFix");
714 effect.push_attribute(("amt", amount.as_str()));
715 push_raw_attributes(&mut effect, &self.alpha_modulation_fix_raw_attributes);
716 write_empty(writer, effect)?;
717 }
718 emit_raw(writer, self.raw_children.at(1))?;
719 write_end(writer, "a:blip")
720 }
721}
722
723fn is_drawingml_alpha_mod_fix(
724 element: &BytesStart<'_>,
725 parent: &BytesStart<'_>,
726 ancestor: &BytesStart<'_>,
727) -> Result<bool> {
728 if !matches_local_name(element.name().as_ref(), b"alphaModFix") {
729 return Ok(false);
730 }
731 let qualified = element.name();
732 let qualified = qualified.as_ref();
733 let prefix = qualified
734 .split(|byte| *byte == b':')
735 .next()
736 .filter(|prefix| prefix.len() != qualified.len());
737 let binding = namespace_binding(element, prefix)?
738 .or(namespace_binding(parent, prefix)?)
739 .or(namespace_binding(ancestor, prefix)?);
740 if prefix == Some(b"a") && binding.as_deref().is_some_and(|uri| uri != A_NS) {
741 return Err(OxmlError::InvalidValue(
742 "xmlns:a conflicts with the fixed DrawingML writer namespace".to_owned(),
743 )
744 .into());
745 }
746 Ok(binding.as_deref() == Some(A_NS) || (prefix == Some(b"a") && binding.is_none()))
747}
748
749fn namespace_binding(start: &BytesStart<'_>, prefix: Option<&[u8]>) -> Result<Option<String>> {
750 let name = prefix.map_or_else(
751 || b"xmlns".to_vec(),
752 |prefix| {
753 let mut name = b"xmlns:".to_vec();
754 name.extend_from_slice(prefix);
755 name
756 },
757 );
758 for attribute in start.attributes() {
759 let attribute = attribute.map_err(OxmlError::from)?;
760 if attribute.key.as_ref() == name {
761 return Ok(Some(
762 attribute
763 .decoded_and_normalized_value(XmlVersion::Implicit1_0, start.decoder())
764 .map_err(OxmlError::from)?
765 .into_owned(),
766 ));
767 }
768 }
769 Ok(None)
770}
771
772fn parse_alpha_modulation_fix(
773 element: &BytesStart<'_>,
774) -> Result<(Percent1000, Vec<(String, String)>)> {
775 let mut amount = None;
776 for attribute in element.attributes() {
777 let attribute = attribute.map_err(OxmlError::from)?;
778 if attribute.key.as_ref() == b"amt" {
779 amount = Some(
780 attribute
781 .decoded_and_normalized_value(XmlVersion::Implicit1_0, element.decoder())
782 .map_err(OxmlError::from)?
783 .into_owned(),
784 );
785 }
786 }
787 let amount = amount.unwrap_or_else(|| "100000".to_owned());
788 let parsed = amount
789 .parse::<i32>()
790 .map_err(|_| invalid(element, b"amt", amount.clone()))?;
791 if !(0..=100_000).contains(&parsed) {
792 return Err(invalid(element, b"amt", amount));
793 }
794 Ok((
795 Percent1000(parsed),
796 capture_unmodelled_attributes(element, &[b"amt"])?,
797 ))
798}
799
800fn read_empty_effect_body(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<()> {
801 let mut buffer = Vec::new();
802 loop {
803 match reader
804 .read_event_into(&mut buffer)
805 .map_err(OxmlError::from)?
806 {
807 Event::End(element) if element.name().as_ref() == start.name().as_ref() => {
808 return Ok(());
809 }
810 Event::Text(text) if text.iter().all(u8::is_ascii_whitespace) => {}
811 Event::Start(element) | Event::Empty(element) => return Err(unexpected(&element)),
812 Event::Eof => return Err(missing_end("alphaModFix")),
813 _ => {
814 return Err(OxmlError::InvalidValue(
815 "a:alphaModFix must not contain content".to_owned(),
816 )
817 .into());
818 }
819 }
820 buffer.clear();
821 }
822}
823
824#[derive(Clone, Debug, Eq, PartialEq)]
826pub enum BlipMode {
827 Stretch {
828 fill_rect: Option<RelativeRect>,
829 raw_children: OrderedRawChildren,
830 },
831 Tile(Tile),
832}
833
834#[derive(Clone, Debug, Default, Eq, PartialEq)]
836pub struct Tile {
837 pub translation_x: Option<i64>,
838 pub translation_y: Option<i64>,
839 pub scale_x: Option<Percent1000>,
840 pub scale_y: Option<Percent1000>,
841 pub flip: Option<String>,
842 pub alignment: Option<String>,
843 raw_children: OrderedRawChildren,
844}
845
846#[derive(Clone, Debug, Default, Eq, PartialEq)]
848pub struct BlipFill {
849 pub dpi: Option<u32>,
850 pub rotate_with_shape: Option<bool>,
851 pub blip: Option<Blip>,
852 pub source_rect: Option<RelativeRect>,
853 pub mode: Option<BlipMode>,
854 raw_attributes: Vec<(String, String)>,
855 raw_children: OrderedRawChildren,
856}
857
858impl BlipFill {
859 pub fn from_xml(xml: &[u8]) -> Result<Self> {
861 let mut reader = Reader::from_reader(xml);
862 let mut buffer = Vec::new();
863 loop {
864 match reader
865 .read_event_into(&mut buffer)
866 .map_err(OxmlError::from)?
867 {
868 Event::Start(element)
869 if matches_local_name(element.name().as_ref(), b"blipFill") =>
870 {
871 return Self::from_element(&mut reader, &element);
872 }
873 Event::Empty(element)
874 if matches_local_name(element.name().as_ref(), b"blipFill") =>
875 {
876 return Self::from_start(&element);
877 }
878 Event::Start(element) | Event::Empty(element) => {
879 return Err(unexpected(&element));
880 }
881 Event::Eof => return Err(missing_end("blipFill")),
882 _ => {}
883 }
884 buffer.clear();
885 }
886 }
887
888 fn from_start(start: &BytesStart<'_>) -> Result<Self> {
889 reject_conflicting_a_prefix(start)?;
890 Ok(Self {
891 dpi: optional_parse(start, b"dpi")?,
892 rotate_with_shape: optional_bool(start, b"rotWithShape")?,
893 raw_attributes: capture_unmodelled_attributes(start, &[b"dpi", b"rotWithShape"])?,
894 ..Self::default()
895 })
896 }
897
898 fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
899 let mut fill = Self::from_start(start)?;
900 let mut buffer = Vec::new();
901 let mut boundary = 0;
902 loop {
903 match reader
904 .read_event_into(&mut buffer)
905 .map_err(OxmlError::from)?
906 {
907 Event::Start(element) if matches_local_name(element.name().as_ref(), b"blip") => {
908 reject_conflicting_a_prefix(&element)?;
909 fill.blip = Some(Blip::from_element(reader, &element, start)?);
910 boundary = 1;
911 }
912 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"blip") => {
913 reject_conflicting_a_prefix(&element)?;
914 fill.blip = Some(Blip::from_start(&element));
915 boundary = 1;
916 }
917 Event::Start(element)
918 if matches_local_name(element.name().as_ref(), b"srcRect") =>
919 {
920 fill.source_rect = Some(parse_rect_element(reader, &element)?);
921 boundary = 2;
922 }
923 Event::Empty(element)
924 if matches_local_name(element.name().as_ref(), b"srcRect") =>
925 {
926 fill.source_rect = Some(parse_rect(&element)?);
927 boundary = 2;
928 }
929 Event::Start(element)
930 if matches_local_name(element.name().as_ref(), b"stretch") =>
931 {
932 reject_conflicting_a_prefix(&element)?;
933 fill.mode = Some(parse_stretch(reader)?);
934 boundary = 3;
935 }
936 Event::Empty(element)
937 if matches_local_name(element.name().as_ref(), b"stretch") =>
938 {
939 reject_conflicting_a_prefix(&element)?;
940 fill.mode = Some(BlipMode::Stretch {
941 fill_rect: None,
942 raw_children: OrderedRawChildren::default(),
943 });
944 boundary = 3;
945 }
946 Event::Start(element) if matches_local_name(element.name().as_ref(), b"tile") => {
947 fill.mode = Some(BlipMode::Tile(parse_tile(reader, &element)?));
948 boundary = 3;
949 }
950 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"tile") => {
951 fill.mode = Some(BlipMode::Tile(parse_empty_tile(&element)?));
952 boundary = 3;
953 }
954 Event::Start(element) => fill
955 .raw_children
956 .push(boundary, capture_element(reader, &element)?),
957 Event::Empty(element) => fill
958 .raw_children
959 .push(boundary, capture_empty_element(&element)?),
960 Event::End(element) if matches_local_name(element.name().as_ref(), b"blipFill") => {
961 break;
962 }
963 Event::Eof => return Err(missing_end("blipFill")),
964 _ => {}
965 }
966 buffer.clear();
967 }
968 Ok(fill)
969 }
970
971 fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
972 self.write_xml_as(writer, "a:blipFill")
973 }
974
975 pub fn write_xml_as<W: Write>(&self, writer: &mut Writer<W>, name: &str) -> Result<()> {
977 let mut start = BytesStart::new(name);
978 let dpi = self.dpi.map(|value| value.to_string());
979 if let Some(dpi) = dpi.as_deref() {
980 start.push_attribute(("dpi", dpi));
981 }
982 if let Some(rotate) = self.rotate_with_shape.map(bool_text) {
983 start.push_attribute(("rotWithShape", rotate));
984 }
985 push_raw_attributes(&mut start, &self.raw_attributes);
986 if self.blip.is_none()
987 && self.source_rect.is_none()
988 && self.mode.is_none()
989 && self.raw_children.is_empty()
990 {
991 return write_empty(writer, start);
992 }
993 write_start(writer, start)?;
994 emit_raw(writer, self.raw_children.at(0))?;
995 if let Some(blip) = &self.blip {
996 blip.write_xml(writer)?;
997 }
998 emit_raw(writer, self.raw_children.at(1))?;
999 if let Some(rect) = &self.source_rect {
1000 write_rect(writer, "a:srcRect", rect)?;
1001 }
1002 emit_raw(writer, self.raw_children.at(2))?;
1003 if let Some(mode) = &self.mode {
1004 write_blip_mode(writer, mode)?;
1005 }
1006 emit_raw(writer, self.raw_children.at(3))?;
1007 write_end(writer, name)
1008 }
1009
1010 pub fn raw_children(&self) -> &OrderedRawChildren {
1011 &self.raw_children
1012 }
1013}
1014
1015fn capture_unmodelled_attributes(
1016 start: &BytesStart<'_>,
1017 modelled: &[&[u8]],
1018) -> Result<Vec<(String, String)>> {
1019 let mut raw = Vec::new();
1020 for attribute in start.attributes() {
1021 let attribute = attribute.map_err(OxmlError::from)?;
1022 let key = attribute.key.as_ref();
1023 if !key.contains(&b':') && modelled.contains(&key) {
1024 continue;
1025 }
1026 let name = std::str::from_utf8(key)
1027 .map_err(OxmlError::from)?
1028 .to_owned();
1029 let value = attribute
1030 .decoded_and_normalized_value(XmlVersion::Implicit1_0, start.decoder())
1031 .map_err(OxmlError::from)?
1032 .into_owned();
1033 raw.push((name, value));
1034 }
1035 Ok(raw)
1036}
1037
1038fn push_raw_attributes(start: &mut BytesStart<'_>, attributes: &[(String, String)]) {
1039 for (name, value) in attributes {
1040 start.push_attribute((name.as_str(), value.as_str()));
1041 }
1042}
1043
1044fn parse_linear(start: &BytesStart<'_>) -> Result<LinearGradient> {
1045 reject_conflicting_a_prefix(start)?;
1046 let angle = optional_parse::<i32>(start, b"ang")?;
1047 Ok(LinearGradient {
1048 angle: Angle(angle.unwrap_or(0)),
1049 scaled: optional_bool(start, b"scaled")?,
1050 angle_omitted: angle.is_none(),
1051 raw_children: OrderedRawChildren::default(),
1052 })
1053}
1054
1055fn parse_linear_element(
1056 reader: &mut Reader<&[u8]>,
1057 start: &BytesStart<'_>,
1058) -> Result<LinearGradient> {
1059 let mut linear = parse_linear(start)?;
1060 linear.raw_children = capture_all_children(reader, b"lin")?;
1061 Ok(linear)
1062}
1063
1064fn parse_empty_path(start: &BytesStart<'_>) -> Result<PathGradient> {
1065 reject_conflicting_a_prefix(start)?;
1066 let (kind, kind_omitted) = match get_attr(start, b"path") {
1067 Some(value) => (
1068 PathGradientKind::parse(&value).ok_or_else(|| invalid(start, b"path", value))?,
1069 false,
1070 ),
1071 None => (PathGradientKind::Rectangle, true),
1072 };
1073 Ok(PathGradient {
1074 kind,
1075 fill_to_rect: None,
1076 kind_omitted,
1077 raw_children: OrderedRawChildren::default(),
1078 })
1079}
1080
1081fn parse_path(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<PathGradient> {
1082 let mut path = parse_empty_path(start)?;
1083 let mut buffer = Vec::new();
1084 let mut boundary = 0;
1085 loop {
1086 match reader
1087 .read_event_into(&mut buffer)
1088 .map_err(OxmlError::from)?
1089 {
1090 Event::Start(element) if matches_local_name(element.name().as_ref(), b"fillToRect") => {
1091 path.fill_to_rect = Some(parse_rect_element(reader, &element)?);
1092 boundary = 1;
1093 }
1094 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"fillToRect") => {
1095 path.fill_to_rect = Some(parse_rect(&element)?);
1096 boundary = 1;
1097 }
1098 Event::Start(element) => path
1099 .raw_children
1100 .push(boundary, capture_element(reader, &element)?),
1101 Event::Empty(element) => path
1102 .raw_children
1103 .push(boundary, capture_empty_element(&element)?),
1104 Event::End(element) if matches_local_name(element.name().as_ref(), b"path") => break,
1105 Event::Eof => return Err(missing_end("path")),
1106 _ => {}
1107 }
1108 buffer.clear();
1109 }
1110 Ok(path)
1111}
1112
1113fn write_geometry<W: Write>(writer: &mut Writer<W>, geometry: &GradientGeometry) -> Result<()> {
1114 match geometry {
1115 GradientGeometry::Linear(linear) => {
1116 let angle = linear.angle.0.to_string();
1117 let mut start = BytesStart::new("a:lin");
1118 if !linear.angle_omitted || linear.angle.0 != 0 {
1119 start.push_attribute(("ang", angle.as_str()));
1120 }
1121 if let Some(scaled) = linear.scaled.map(bool_text) {
1122 start.push_attribute(("scaled", scaled));
1123 }
1124 if linear.raw_children.is_empty() {
1125 return write_empty(writer, start);
1126 }
1127 write_start(writer, start)?;
1128 emit_raw(writer, linear.raw_children.at(0))?;
1129 write_end(writer, "a:lin")
1130 }
1131 GradientGeometry::Path(path) => {
1132 let mut start = BytesStart::new("a:path");
1133 if !path.kind_omitted || path.kind != PathGradientKind::Rectangle {
1134 start.push_attribute(("path", path.kind.as_str()));
1135 }
1136 if path.fill_to_rect.is_none() && path.raw_children.is_empty() {
1137 return write_empty(writer, start);
1138 }
1139 write_start(writer, start)?;
1140 emit_raw(writer, path.raw_children.at(0))?;
1141 if let Some(rect) = &path.fill_to_rect {
1142 write_rect(writer, "a:fillToRect", rect)?;
1143 }
1144 emit_raw(writer, path.raw_children.at(1))?;
1145 write_end(writer, "a:path")
1146 }
1147 }
1148}
1149
1150fn parse_stretch(reader: &mut Reader<&[u8]>) -> Result<BlipMode> {
1151 let mut fill_rect = None;
1152 let mut raw_children = OrderedRawChildren::default();
1153 let mut boundary = 0;
1154 let mut buffer = Vec::new();
1155 loop {
1156 match reader
1157 .read_event_into(&mut buffer)
1158 .map_err(OxmlError::from)?
1159 {
1160 Event::Start(element) if matches_local_name(element.name().as_ref(), b"fillRect") => {
1161 fill_rect = Some(parse_rect_element(reader, &element)?);
1162 boundary = 1;
1163 }
1164 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"fillRect") => {
1165 fill_rect = Some(parse_rect(&element)?);
1166 boundary = 1;
1167 }
1168 Event::Start(element) => {
1169 raw_children.push(boundary, capture_element(reader, &element)?)
1170 }
1171 Event::Empty(element) => raw_children.push(boundary, capture_empty_element(&element)?),
1172 Event::End(element) if matches_local_name(element.name().as_ref(), b"stretch") => break,
1173 Event::Eof => return Err(missing_end("stretch")),
1174 _ => {}
1175 }
1176 buffer.clear();
1177 }
1178 Ok(BlipMode::Stretch {
1179 fill_rect,
1180 raw_children,
1181 })
1182}
1183
1184fn parse_empty_tile(start: &BytesStart<'_>) -> Result<Tile> {
1185 reject_conflicting_a_prefix(start)?;
1186 Ok(Tile {
1187 translation_x: optional_parse(start, b"tx")?,
1188 translation_y: optional_parse(start, b"ty")?,
1189 scale_x: optional_parse::<i32>(start, b"sx")?.map(Percent1000),
1190 scale_y: optional_parse::<i32>(start, b"sy")?.map(Percent1000),
1191 flip: optional_token(start, b"flip", &["none", "x", "y", "xy"])?,
1192 alignment: optional_token(
1193 start,
1194 b"algn",
1195 &["tl", "t", "tr", "l", "ctr", "r", "bl", "b", "br"],
1196 )?,
1197 raw_children: OrderedRawChildren::default(),
1198 })
1199}
1200
1201fn parse_tile(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Tile> {
1202 let mut tile = parse_empty_tile(start)?;
1203 tile.raw_children = capture_all_children(reader, b"tile")?;
1204 Ok(tile)
1205}
1206
1207fn write_blip_mode<W: Write>(writer: &mut Writer<W>, mode: &BlipMode) -> Result<()> {
1208 match mode {
1209 BlipMode::Stretch {
1210 fill_rect,
1211 raw_children,
1212 } => {
1213 if fill_rect.is_none() && raw_children.is_empty() {
1214 return write_empty(writer, BytesStart::new("a:stretch"));
1215 }
1216 write_start(writer, BytesStart::new("a:stretch"))?;
1217 emit_raw(writer, raw_children.at(0))?;
1218 if let Some(rect) = fill_rect {
1219 write_rect(writer, "a:fillRect", rect)?;
1220 }
1221 emit_raw(writer, raw_children.at(1))?;
1222 write_end(writer, "a:stretch")
1223 }
1224 BlipMode::Tile(tile) => {
1225 let mut start = BytesStart::new("a:tile");
1226 let tx = tile.translation_x.map(|value| value.to_string());
1227 let ty = tile.translation_y.map(|value| value.to_string());
1228 let sx = tile.scale_x.map(|value| value.0.to_string());
1229 let sy = tile.scale_y.map(|value| value.0.to_string());
1230 for (name, value) in [
1231 ("tx", tx.as_deref()),
1232 ("ty", ty.as_deref()),
1233 ("sx", sx.as_deref()),
1234 ("sy", sy.as_deref()),
1235 ] {
1236 if let Some(value) = value {
1237 start.push_attribute((name, value));
1238 }
1239 }
1240 if let Some(flip) = tile.flip.as_deref() {
1241 start.push_attribute(("flip", flip));
1242 }
1243 if let Some(alignment) = tile.alignment.as_deref() {
1244 start.push_attribute(("algn", alignment));
1245 }
1246 if tile.raw_children.is_empty() {
1247 return write_empty(writer, start);
1248 }
1249 write_start(writer, start)?;
1250 emit_raw(writer, tile.raw_children.at(0))?;
1251 write_end(writer, "a:tile")
1252 }
1253 }
1254}
1255
1256fn parse_rect(start: &BytesStart<'_>) -> Result<RelativeRect> {
1257 reject_conflicting_a_prefix(start)?;
1258 Ok(RelativeRect {
1259 left: optional_parse::<i32>(start, b"l")?.map(Percent1000),
1260 top: optional_parse::<i32>(start, b"t")?.map(Percent1000),
1261 right: optional_parse::<i32>(start, b"r")?.map(Percent1000),
1262 bottom: optional_parse::<i32>(start, b"b")?.map(Percent1000),
1263 raw_children: OrderedRawChildren::default(),
1264 })
1265}
1266
1267fn parse_rect_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<RelativeRect> {
1268 let mut rect = parse_rect(start)?;
1269 rect.raw_children = capture_all_children(reader, local_name(start.name().as_ref()))?;
1270 Ok(rect)
1271}
1272
1273fn write_rect<W: Write>(writer: &mut Writer<W>, name: &str, rect: &RelativeRect) -> Result<()> {
1274 let left = rect.left.map(|value| value.0.to_string());
1275 let top = rect.top.map(|value| value.0.to_string());
1276 let right = rect.right.map(|value| value.0.to_string());
1277 let bottom = rect.bottom.map(|value| value.0.to_string());
1278 let mut start = BytesStart::new(name);
1279 for (name, value) in [
1280 ("l", left.as_deref()),
1281 ("t", top.as_deref()),
1282 ("r", right.as_deref()),
1283 ("b", bottom.as_deref()),
1284 ] {
1285 if let Some(value) = value {
1286 start.push_attribute((name, value));
1287 }
1288 }
1289 if rect.raw_children.is_empty() {
1290 return write_empty(writer, start);
1291 }
1292 write_start(writer, start)?;
1293 emit_raw(writer, rect.raw_children.at(0))?;
1294 write_end(writer, name)
1295}
1296
1297fn parse_color_container(
1298 reader: &mut Reader<&[u8]>,
1299 end_name: &[u8],
1300) -> Result<(Option<ColorChoice>, OrderedRawChildren)> {
1301 let mut color = None;
1302 let mut raw_children = OrderedRawChildren::default();
1303 let mut boundary = 0;
1304 let mut buffer = Vec::new();
1305 loop {
1306 match reader
1307 .read_event_into(&mut buffer)
1308 .map_err(OxmlError::from)?
1309 {
1310 Event::Start(element) if is_color(element.name().as_ref()) => {
1311 color = Some(ColorChoice::from_xml(reader, &element)?);
1312 boundary = 1;
1313 }
1314 Event::Empty(element) if is_color(element.name().as_ref()) => {
1315 color = Some(ColorChoice::from_empty_xml(&element)?);
1316 boundary = 1;
1317 }
1318 Event::Start(element) => {
1319 raw_children.push(boundary, capture_element(reader, &element)?);
1320 }
1321 Event::Empty(element) => {
1322 raw_children.push(boundary, capture_empty_element(&element)?);
1323 }
1324 Event::End(element) if matches_local_name(element.name().as_ref(), end_name) => break,
1325 Event::Eof => return Err(missing_end(&String::from_utf8_lossy(end_name))),
1326 _ => {}
1327 }
1328 buffer.clear();
1329 }
1330 Ok((color, raw_children))
1331}
1332
1333fn write_color_container<W: Write>(
1334 writer: &mut Writer<W>,
1335 name: &str,
1336 color: Option<&ColorChoice>,
1337 raw_children: &OrderedRawChildren,
1338) -> Result<()> {
1339 write_start(writer, BytesStart::new(name))?;
1340 emit_raw(writer, raw_children.at(0))?;
1341 if let Some(color) = color {
1342 color.to_xml(writer)?;
1343 }
1344 emit_raw(writer, raw_children.at(1))?;
1345 write_end(writer, name)
1346}
1347
1348fn read_color_children(
1349 reader: &mut Reader<&[u8]>,
1350 end_name: &[u8],
1351 color: &mut Option<ColorChoice>,
1352 raw_children: &mut OrderedRawChildren,
1353) -> Result<()> {
1354 let mut buffer = Vec::new();
1355 let mut boundary = 0;
1356 loop {
1357 match reader
1358 .read_event_into(&mut buffer)
1359 .map_err(OxmlError::from)?
1360 {
1361 Event::Start(element) if is_color(element.name().as_ref()) => {
1362 *color = Some(ColorChoice::from_xml(reader, &element)?);
1363 boundary = 1;
1364 }
1365 Event::Empty(element) if is_color(element.name().as_ref()) => {
1366 *color = Some(ColorChoice::from_empty_xml(&element)?);
1367 boundary = 1;
1368 }
1369 Event::Start(element) => {
1370 raw_children.push(boundary, capture_element(reader, &element)?)
1371 }
1372 Event::Empty(element) => raw_children.push(boundary, capture_empty_element(&element)?),
1373 Event::End(element) if matches_local_name(element.name().as_ref(), end_name) => break,
1374 Event::Eof => return Err(missing_end(&String::from_utf8_lossy(end_name))),
1375 _ => {}
1376 }
1377 buffer.clear();
1378 }
1379 Ok(())
1380}
1381
1382fn capture_all_children(reader: &mut Reader<&[u8]>, end_name: &[u8]) -> Result<OrderedRawChildren> {
1383 let mut raw_children = OrderedRawChildren::default();
1384 let mut buffer = Vec::new();
1385 loop {
1386 match reader
1387 .read_event_into(&mut buffer)
1388 .map_err(OxmlError::from)?
1389 {
1390 Event::Start(element) => raw_children.push(0, capture_element(reader, &element)?),
1391 Event::Empty(element) => raw_children.push(0, capture_empty_element(&element)?),
1392 Event::End(element) if matches_local_name(element.name().as_ref(), end_name) => break,
1393 Event::Eof => return Err(missing_end(&String::from_utf8_lossy(end_name))),
1394 _ => {}
1395 }
1396 buffer.clear();
1397 }
1398 Ok(raw_children)
1399}
1400
1401fn is_color(name: &[u8]) -> bool {
1402 matches!(
1403 local_name(name),
1404 b"srgbClr" | b"schemeClr" | b"sysClr" | b"prstClr"
1405 )
1406}
1407
1408fn validate_gradient_position(value: i32) -> Result<()> {
1409 if (0..=100_000).contains(&value) {
1410 Ok(())
1411 } else {
1412 Err(FillError::GradientStopOutOfRange(value))
1413 }
1414}
1415
1416fn required_attr(start: &BytesStart<'_>, name: &[u8]) -> Result<String> {
1417 get_attr(start, name).ok_or_else(|| FillError::MissingAttribute {
1418 element: String::from_utf8_lossy(local_name(start.name().as_ref())).into_owned(),
1419 attribute: String::from_utf8_lossy(name).into_owned(),
1420 })
1421}
1422
1423fn required_i32(start: &BytesStart<'_>, name: &[u8]) -> Result<i32> {
1424 let value = required_attr(start, name)?;
1425 value.parse().map_err(|_| invalid(start, name, value))
1426}
1427
1428fn optional_parse<T: std::str::FromStr>(start: &BytesStart<'_>, name: &[u8]) -> Result<Option<T>> {
1429 get_attr(start, name)
1430 .map(|value| value.parse().map_err(|_| invalid(start, name, value)))
1431 .transpose()
1432}
1433
1434fn optional_bool(start: &BytesStart<'_>, name: &[u8]) -> Result<Option<bool>> {
1435 get_attr(start, name)
1436 .map(|value| match value.as_str() {
1437 "1" | "true" => Ok(true),
1438 "0" | "false" => Ok(false),
1439 _ => Err(invalid(start, name, value)),
1440 })
1441 .transpose()
1442}
1443
1444fn optional_token(start: &BytesStart<'_>, name: &[u8], allowed: &[&str]) -> Result<Option<String>> {
1445 get_attr(start, name)
1446 .map(|value| {
1447 if allowed.contains(&value.as_str()) {
1448 Ok(value)
1449 } else {
1450 Err(invalid(start, name, value))
1451 }
1452 })
1453 .transpose()
1454}
1455
1456fn invalid(start: &BytesStart<'_>, attribute: &[u8], value: String) -> FillError {
1457 FillError::InvalidAttribute {
1458 element: String::from_utf8_lossy(local_name(start.name().as_ref())).into_owned(),
1459 attribute: String::from_utf8_lossy(attribute).into_owned(),
1460 value,
1461 }
1462}
1463
1464fn unexpected(start: &BytesStart<'_>) -> FillError {
1465 FillError::UnexpectedElement(String::from_utf8_lossy(start.name().as_ref()).into_owned())
1466}
1467
1468fn missing_end(name: &str) -> FillError {
1469 FillError::Xml(OxmlError::MissingElement(format!("closing a:{name}")))
1470}
1471
1472const fn bool_text(value: bool) -> &'static str {
1473 if value { "1" } else { "0" }
1474}
1475
1476fn emit_raw<'a, W: Write>(
1477 writer: &mut Writer<W>,
1478 children: impl Iterator<Item = &'a [u8]>,
1479) -> Result<()> {
1480 for child in children {
1481 writer.get_mut().write_all(child).map_err(OxmlError::from)?;
1482 }
1483 Ok(())
1484}
1485
1486fn write_start<W: Write>(writer: &mut Writer<W>, start: BytesStart<'_>) -> Result<()> {
1487 writer
1488 .write_event(Event::Start(start))
1489 .map_err(OxmlError::from)?;
1490 Ok(())
1491}
1492
1493fn write_empty<W: Write>(writer: &mut Writer<W>, start: BytesStart<'_>) -> Result<()> {
1494 writer
1495 .write_event(Event::Empty(start))
1496 .map_err(OxmlError::from)?;
1497 Ok(())
1498}
1499
1500fn write_end<W: Write>(writer: &mut Writer<W>, name: &str) -> Result<()> {
1501 writer
1502 .write_event(Event::End(BytesEnd::new(name)))
1503 .map_err(OxmlError::from)?;
1504 Ok(())
1505}
1506
1507#[cfg(test)]
1508mod tests {
1509 use super::{
1510 Angle, Fill, FillError, GradientFill, GradientGeometry, LinearGradient, PathGradientKind,
1511 };
1512
1513 #[test]
1514 fn every_fill_form_round_trips_and_gradient_stops_keep_document_order() {
1515 let cases: &[&[u8]] = &[
1516 br#"<a:noFill/>"#,
1517 br#"<a:solidFill><a:schemeClr val="accent1"/></a:solidFill>"#,
1518 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>"#,
1519 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>"#,
1520 br#"<a:pattFill prst="pct10"><a:fgClr><a:srgbClr val="112233"/></a:fgClr><a:bgClr><a:schemeClr val="bg1"/></a:bgClr></a:pattFill>"#,
1521 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>"#,
1522 br#"<a:blipFill><a:blip r:link="rId8"/><a:tile tx="10" ty="20" sx="50000" sy="75000" flip="x" algn="ctr"/></a:blipFill>"#,
1523 ];
1524
1525 for xml in cases {
1526 let parsed = Fill::from_xml(xml).unwrap();
1527 let written = parsed.to_xml().unwrap();
1528 assert_eq!(Fill::from_xml(&written).unwrap(), parsed);
1529 }
1530
1531 let Fill::Gradient(gradient) = Fill::from_xml(cases[2]).unwrap() else {
1532 panic!("expected gradient")
1533 };
1534 assert_eq!(
1535 gradient
1536 .stops
1537 .iter()
1538 .map(|stop| stop.position.0)
1539 .collect::<Vec<_>>(),
1540 vec![75_000, 25_000]
1541 );
1542 assert!(matches!(
1543 gradient.geometry,
1544 Some(GradientGeometry::Linear(_))
1545 ));
1546 }
1547
1548 #[test]
1549 fn gradient_geometry_without_angle_or_path_round_trips_without_adding_them() {
1550 let cases: &[&[u8]] = &[
1551 br#"<a:gradFill><a:lin scaled="0"/></a:gradFill>"#,
1552 br#"<a:gradFill><a:lin/></a:gradFill>"#,
1553 br#"<a:gradFill><a:path/></a:gradFill>"#,
1554 br#"<a:gradFill><a:path><a:fillToRect l="50000" t="50000" r="50000" b="50000"/></a:path></a:gradFill>"#,
1555 ];
1556 for xml in cases {
1557 let parsed = Fill::from_xml(xml).unwrap();
1558 assert_eq!(parsed.to_xml().unwrap(), *xml);
1559 }
1560
1561 let Fill::Gradient(mut gradient) = Fill::from_xml(cases[0]).unwrap() else {
1564 panic!("expected gradient")
1565 };
1566 let Some(GradientGeometry::Linear(linear)) = &mut gradient.geometry else {
1567 panic!("expected linear geometry")
1568 };
1569 assert_eq!(linear.angle, Angle(0));
1570 assert_eq!(linear.scaled, Some(false));
1571 linear.angle = Angle(5_400_000);
1572 assert_eq!(
1573 Fill::Gradient(gradient).to_xml().unwrap(),
1574 br#"<a:gradFill><a:lin ang="5400000" scaled="0"/></a:gradFill>"#
1575 );
1576
1577 let Fill::Gradient(mut gradient) = Fill::from_xml(cases[3]).unwrap() else {
1578 panic!("expected gradient")
1579 };
1580 let Some(GradientGeometry::Path(path)) = &mut gradient.geometry else {
1581 panic!("expected path geometry")
1582 };
1583 assert_eq!(path.kind, PathGradientKind::Rectangle);
1584 assert!(path.fill_to_rect.is_some());
1585 path.kind = PathGradientKind::Circle;
1586 assert_eq!(
1587 Fill::Gradient(gradient).to_xml().unwrap(),
1588 br#"<a:gradFill><a:path path="circle"><a:fillToRect l="50000" t="50000" r="50000" b="50000"/></a:path></a:gradFill>"#
1589 );
1590
1591 for xml in [
1593 br#"<a:gradFill><a:lin ang="0" scaled="0"/></a:gradFill>"#.as_slice(),
1594 br#"<a:gradFill><a:path path="rect"/></a:gradFill>"#,
1595 ] {
1596 assert_eq!(Fill::from_xml(xml).unwrap().to_xml().unwrap(), xml);
1597 }
1598 let built = GradientFill {
1599 geometry: Some(GradientGeometry::Linear(LinearGradient::default())),
1600 ..GradientFill::default()
1601 };
1602 assert_eq!(
1603 Fill::Gradient(built).to_xml().unwrap(),
1604 br#"<a:gradFill><a:lin ang="0"/></a:gradFill>"#
1605 );
1606 }
1607
1608 #[test]
1609 fn fill_forms_read_any_prefix_and_write_fixed_a_prefix_in_schema_order() {
1610 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();
1611 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>"#);
1612
1613 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();
1614 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>"#);
1615 }
1616
1617 #[test]
1618 fn blip_relationship_prefix_is_declared_in_canonical_output() {
1619 let fill = Fill::from_xml(
1620 br#"<a:blipFill><a:blip xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="rId1"/></a:blipFill>"#,
1621 )
1622 .unwrap();
1623 let written = String::from_utf8(fill.to_xml().unwrap()).unwrap();
1624
1625 assert!(written.contains(
1626 "<a:blip xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" r:embed=\"rId1\"/>"
1627 ));
1628 }
1629
1630 #[test]
1631 fn unknown_fill_children_round_trip_byte_for_byte_in_place() {
1632 let xml = br#"<z:gradFill><x:before x:id="1"><x:item>one & 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>"#;
1633 let written = Fill::from_xml(xml).unwrap().to_xml().unwrap();
1634 assert_eq!(written, br#"<a:gradFill><x:before x:id="1"><x:item>one & 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>"#);
1635
1636 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>"#;
1637 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>"#);
1638
1639 let pattern = br#"<z:pattFill><z:fgClr><x:before/><z:srgbClr val="102030"/><x:after x:v="kept"/></z:fgClr></z:pattFill>"#;
1640 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>"#);
1641
1642 let extension_only_pattern =
1643 br#"<z:pattFill><z:fgClr><x:extension x:v="kept"/></z:fgClr></z:pattFill>"#;
1644 assert_eq!(
1645 Fill::from_xml(extension_only_pattern)
1646 .unwrap()
1647 .to_xml()
1648 .unwrap(),
1649 br#"<a:pattFill><a:fgClr><x:extension x:v="kept"/></a:fgClr></a:pattFill>"#
1650 );
1651
1652 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>"#;
1653 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>"#);
1654
1655 let extension_only_stop_list =
1656 br#"<z:gradFill><z:gsLst><x:extension x:v="kept"/></z:gsLst></z:gradFill>"#;
1657 assert_eq!(
1658 Fill::from_xml(extension_only_stop_list)
1659 .unwrap()
1660 .to_xml()
1661 .unwrap(),
1662 br#"<a:gradFill><a:gsLst><x:extension x:v="kept"/></a:gsLst></a:gradFill>"#
1663 );
1664
1665 let path_leaf_extension = br#"<z:gradFill><z:path path="rect"><z:fillToRect><x:pathRectExt/></z:fillToRect></z:path></z:gradFill>"#;
1666 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>"#);
1667
1668 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>"#;
1669 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>"#);
1670 }
1671
1672 #[test]
1673 fn blip_alpha_mod_fix_is_typed_bounded_and_schema_ordered() {
1674 let parse = |children: &str| {
1675 let xml = format!(
1676 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>"#
1677 );
1678 super::BlipFill::from_xml(xml.as_bytes())
1679 };
1680
1681 let fill = parse(
1682 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"/>"#,
1683 )
1684 .unwrap();
1685 let blip = fill.blip.unwrap();
1686 assert_eq!(blip.alpha_modulation_fix, Some(super::Percent1000(30_000)));
1687 let xml = super::Fill::Blip(super::BlipFill {
1688 blip: Some(blip),
1689 ..Default::default()
1690 })
1691 .to_xml()
1692 .unwrap();
1693 let xml = String::from_utf8(xml).unwrap();
1694 let before = xml.find("x:before").unwrap();
1695 let alpha = xml.find("a:alphaModFix").unwrap();
1696 let after = xml.find("x:after").unwrap();
1697 assert!(before < alpha && alpha < after, "{xml}");
1698 assert!(xml.contains("amt=\"30000\""), "{xml}");
1699
1700 let inherited_alias = super::BlipFill::from_xml(
1701 br#"<q:blipFill xmlns:q="http://schemas.openxmlformats.org/drawingml/2006/main"><q:blip><q:alphaModFix amt="25000"/></q:blip></q:blipFill>"#,
1702 )
1703 .unwrap();
1704 assert_eq!(
1705 inherited_alias.blip.unwrap().alpha_modulation_fix,
1706 Some(super::Percent1000(25_000))
1707 );
1708
1709 let qualified_amount =
1710 parse(r#"<a:alphaModFix xmlns:producer="urn:producer" producer:amt="20000"/>"#)
1711 .unwrap();
1712 assert_eq!(
1713 qualified_amount.blip.unwrap().alpha_modulation_fix,
1714 Some(super::Percent1000(100_000))
1715 );
1716
1717 let duplicate = parse(
1718 r#"<a:alphaModFix amt="40000"/><a:alphaModFix producer:keep="yes" xmlns:producer="urn:producer" amt="20000"/>"#,
1719 )
1720 .unwrap();
1721 assert_eq!(
1722 duplicate.blip.as_ref().unwrap().alpha_modulation_fix,
1723 Some(super::Percent1000(40_000))
1724 );
1725 let duplicate_xml = super::Fill::Blip(duplicate).to_xml().unwrap();
1726 let duplicate_xml = String::from_utf8(duplicate_xml).unwrap();
1727 assert_eq!(duplicate_xml.matches("alphaModFix").count(), 2);
1728 assert!(duplicate_xml.contains("producer:keep=\"yes\""));
1729
1730 for invalid in ["-1", "100001", "not-a-number"] {
1731 assert!(parse(&format!(r#"<a:alphaModFix amt="{invalid}"/>"#)).is_err());
1732 }
1733 assert!(parse(r#"<a:alphaModFix amt="30000">content</a:alphaModFix>"#).is_err());
1734 let mut invalid_write = parse(r#"<a:alphaModFix amt="30000"/>"#).unwrap();
1735 invalid_write.blip.as_mut().unwrap().alpha_modulation_fix = Some(super::Percent1000(-1));
1736 assert!(super::Fill::Blip(invalid_write).to_xml().is_err());
1737 let foreign = parse(
1738 r#"<x:alphaModFix xmlns:x="urn:producer" amt="30000" producer:keep="yes" xmlns:producer="urn:producer"/>"#,
1739 )
1740 .unwrap();
1741 assert_eq!(foreign.blip.as_ref().unwrap().alpha_modulation_fix, None);
1742 let foreign_xml = super::Fill::Blip(foreign).to_xml().unwrap();
1743 assert!(
1744 String::from_utf8(foreign_xml)
1745 .unwrap()
1746 .contains("producer:keep=\"yes\"")
1747 );
1748 }
1749
1750 #[test]
1751 fn malformed_fill_values_return_errors_without_panicking() {
1752 let cases: &[&[u8]] = &[
1753 br#"<a:gradFill rotWithShape="maybe"/>"#,
1754 br#"<a:gradFill><a:gsLst><a:gs pos="100001"/></a:gsLst></a:gradFill>"#,
1755 br#"<a:gradFill><a:gsLst><a:gs/></a:gsLst></a:gradFill>"#,
1756 br#"<a:gradFill><a:lin ang="ninety"/></a:gradFill>"#,
1757 br#"<a:gradFill><a:path path="triangle"/></a:gradFill>"#,
1758 br#"<a:blipFill dpi="many"/>"#,
1759 br#"<a:blipFill><a:srcRect l="half"/></a:blipFill>"#,
1760 br#"<a:blipFill><a:tile sx="large"/></a:blipFill>"#,
1761 br#"<a:gradFill flip="diagonal"/>"#,
1762 br#"<a:blipFill><a:tile algn="middle"/></a:blipFill>"#,
1763 ];
1764 for xml in cases {
1765 let result = std::panic::catch_unwind(|| Fill::from_xml(xml));
1766 assert!(
1767 result.is_ok(),
1768 "parser panicked for {}",
1769 String::from_utf8_lossy(xml)
1770 );
1771 assert!(
1772 result.unwrap().is_err(),
1773 "malformed fill parsed successfully"
1774 );
1775 }
1776 assert!(matches!(
1777 Fill::from_xml(cases[1]),
1778 Err(FillError::GradientStopOutOfRange(100_001))
1779 ));
1780 }
1781}