1use std::fmt;
2use std::io::Write;
3
4use oxml_core::OxmlError;
5use oxml_core::raw_xml::{capture_element, capture_empty_element};
6use oxml_core::units::{Angle, Percent1000};
7use oxml_core::xml::{R_NS, get_attr, local_name, matches_local_name};
8use quick_xml::events::{BytesEnd, BytesStart, Event};
9use quick_xml::{Reader, Writer, XmlVersion};
10
11use crate::color::{ColorChoice, ColorError};
12use crate::namespace::reject_conflicting_a_prefix;
13use crate::order::OrderedRawChildren;
14
15#[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,
291 pub scaled: Option<bool>,
292 raw_children: OrderedRawChildren,
293}
294
295#[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#[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#[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#[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#[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#[derive(Clone, Debug, Default, Eq, PartialEq)]
609pub struct Blip {
610 pub embed: Option<String>,
611 pub link: Option<String>,
612 raw_children: OrderedRawChildren,
613}
614
615impl Blip {
616 fn from_start(start: &BytesStart<'_>) -> Self {
617 Self {
618 embed: get_attr(start, b"embed"),
619 link: get_attr(start, b"link"),
620 raw_children: OrderedRawChildren::default(),
621 }
622 }
623
624 fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
625 let mut blip = Self::from_start(start);
626 blip.raw_children = capture_all_children(reader, b"blip")?;
627 Ok(blip)
628 }
629
630 fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
631 let mut start = BytesStart::new("a:blip");
632 if self.embed.is_some() || self.link.is_some() {
633 start.push_attribute(("xmlns:r", R_NS));
634 }
635 if let Some(embed) = self.embed.as_deref() {
636 start.push_attribute(("r:embed", embed));
637 }
638 if let Some(link) = self.link.as_deref() {
639 start.push_attribute(("r:link", link));
640 }
641 if self.raw_children.is_empty() {
642 return write_empty(writer, start);
643 }
644 write_start(writer, start)?;
645 emit_raw(writer, self.raw_children.at(0))?;
646 write_end(writer, "a:blip")
647 }
648}
649
650#[derive(Clone, Debug, Eq, PartialEq)]
652pub enum BlipMode {
653 Stretch {
654 fill_rect: Option<RelativeRect>,
655 raw_children: OrderedRawChildren,
656 },
657 Tile(Tile),
658}
659
660#[derive(Clone, Debug, Default, Eq, PartialEq)]
662pub struct Tile {
663 pub translation_x: Option<i64>,
664 pub translation_y: Option<i64>,
665 pub scale_x: Option<Percent1000>,
666 pub scale_y: Option<Percent1000>,
667 pub flip: Option<String>,
668 pub alignment: Option<String>,
669 raw_children: OrderedRawChildren,
670}
671
672#[derive(Clone, Debug, Default, Eq, PartialEq)]
674pub struct BlipFill {
675 pub dpi: Option<u32>,
676 pub rotate_with_shape: Option<bool>,
677 pub blip: Option<Blip>,
678 pub source_rect: Option<RelativeRect>,
679 pub mode: Option<BlipMode>,
680 raw_attributes: Vec<(String, String)>,
681 raw_children: OrderedRawChildren,
682}
683
684impl BlipFill {
685 pub fn from_xml(xml: &[u8]) -> Result<Self> {
687 let mut reader = Reader::from_reader(xml);
688 let mut buffer = Vec::new();
689 loop {
690 match reader
691 .read_event_into(&mut buffer)
692 .map_err(OxmlError::from)?
693 {
694 Event::Start(element)
695 if matches_local_name(element.name().as_ref(), b"blipFill") =>
696 {
697 return Self::from_element(&mut reader, &element);
698 }
699 Event::Empty(element)
700 if matches_local_name(element.name().as_ref(), b"blipFill") =>
701 {
702 return Self::from_start(&element);
703 }
704 Event::Start(element) | Event::Empty(element) => {
705 return Err(unexpected(&element));
706 }
707 Event::Eof => return Err(missing_end("blipFill")),
708 _ => {}
709 }
710 buffer.clear();
711 }
712 }
713
714 fn from_start(start: &BytesStart<'_>) -> Result<Self> {
715 reject_conflicting_a_prefix(start)?;
716 Ok(Self {
717 dpi: optional_parse(start, b"dpi")?,
718 rotate_with_shape: optional_bool(start, b"rotWithShape")?,
719 raw_attributes: capture_unmodelled_attributes(start, &[b"dpi", b"rotWithShape"])?,
720 ..Self::default()
721 })
722 }
723
724 fn from_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Self> {
725 let mut fill = Self::from_start(start)?;
726 let mut buffer = Vec::new();
727 let mut boundary = 0;
728 loop {
729 match reader
730 .read_event_into(&mut buffer)
731 .map_err(OxmlError::from)?
732 {
733 Event::Start(element) if matches_local_name(element.name().as_ref(), b"blip") => {
734 reject_conflicting_a_prefix(&element)?;
735 fill.blip = Some(Blip::from_element(reader, &element)?);
736 boundary = 1;
737 }
738 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"blip") => {
739 reject_conflicting_a_prefix(&element)?;
740 fill.blip = Some(Blip::from_start(&element));
741 boundary = 1;
742 }
743 Event::Start(element)
744 if matches_local_name(element.name().as_ref(), b"srcRect") =>
745 {
746 fill.source_rect = Some(parse_rect_element(reader, &element)?);
747 boundary = 2;
748 }
749 Event::Empty(element)
750 if matches_local_name(element.name().as_ref(), b"srcRect") =>
751 {
752 fill.source_rect = Some(parse_rect(&element)?);
753 boundary = 2;
754 }
755 Event::Start(element)
756 if matches_local_name(element.name().as_ref(), b"stretch") =>
757 {
758 reject_conflicting_a_prefix(&element)?;
759 fill.mode = Some(parse_stretch(reader)?);
760 boundary = 3;
761 }
762 Event::Empty(element)
763 if matches_local_name(element.name().as_ref(), b"stretch") =>
764 {
765 reject_conflicting_a_prefix(&element)?;
766 fill.mode = Some(BlipMode::Stretch {
767 fill_rect: None,
768 raw_children: OrderedRawChildren::default(),
769 });
770 boundary = 3;
771 }
772 Event::Start(element) if matches_local_name(element.name().as_ref(), b"tile") => {
773 fill.mode = Some(BlipMode::Tile(parse_tile(reader, &element)?));
774 boundary = 3;
775 }
776 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"tile") => {
777 fill.mode = Some(BlipMode::Tile(parse_empty_tile(&element)?));
778 boundary = 3;
779 }
780 Event::Start(element) => fill
781 .raw_children
782 .push(boundary, capture_element(reader, &element)?),
783 Event::Empty(element) => fill
784 .raw_children
785 .push(boundary, capture_empty_element(&element)?),
786 Event::End(element) if matches_local_name(element.name().as_ref(), b"blipFill") => {
787 break;
788 }
789 Event::Eof => return Err(missing_end("blipFill")),
790 _ => {}
791 }
792 buffer.clear();
793 }
794 Ok(fill)
795 }
796
797 fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
798 self.write_xml_as(writer, "a:blipFill")
799 }
800
801 pub fn write_xml_as<W: Write>(&self, writer: &mut Writer<W>, name: &str) -> Result<()> {
803 let mut start = BytesStart::new(name);
804 let dpi = self.dpi.map(|value| value.to_string());
805 if let Some(dpi) = dpi.as_deref() {
806 start.push_attribute(("dpi", dpi));
807 }
808 if let Some(rotate) = self.rotate_with_shape.map(bool_text) {
809 start.push_attribute(("rotWithShape", rotate));
810 }
811 push_raw_attributes(&mut start, &self.raw_attributes);
812 if self.blip.is_none()
813 && self.source_rect.is_none()
814 && self.mode.is_none()
815 && self.raw_children.is_empty()
816 {
817 return write_empty(writer, start);
818 }
819 write_start(writer, start)?;
820 emit_raw(writer, self.raw_children.at(0))?;
821 if let Some(blip) = &self.blip {
822 blip.write_xml(writer)?;
823 }
824 emit_raw(writer, self.raw_children.at(1))?;
825 if let Some(rect) = &self.source_rect {
826 write_rect(writer, "a:srcRect", rect)?;
827 }
828 emit_raw(writer, self.raw_children.at(2))?;
829 if let Some(mode) = &self.mode {
830 write_blip_mode(writer, mode)?;
831 }
832 emit_raw(writer, self.raw_children.at(3))?;
833 write_end(writer, name)
834 }
835
836 pub fn raw_children(&self) -> &OrderedRawChildren {
837 &self.raw_children
838 }
839}
840
841fn capture_unmodelled_attributes(
842 start: &BytesStart<'_>,
843 modelled: &[&[u8]],
844) -> Result<Vec<(String, String)>> {
845 let mut raw = Vec::new();
846 for attribute in start.attributes() {
847 let attribute = attribute.map_err(OxmlError::from)?;
848 let key = attribute.key.as_ref();
849 if !key.contains(&b':') && modelled.contains(&key) {
850 continue;
851 }
852 let name = std::str::from_utf8(key)
853 .map_err(OxmlError::from)?
854 .to_owned();
855 let value = attribute
856 .decoded_and_normalized_value(XmlVersion::Implicit1_0, start.decoder())
857 .map_err(OxmlError::from)?
858 .into_owned();
859 raw.push((name, value));
860 }
861 Ok(raw)
862}
863
864fn push_raw_attributes(start: &mut BytesStart<'_>, attributes: &[(String, String)]) {
865 for (name, value) in attributes {
866 start.push_attribute((name.as_str(), value.as_str()));
867 }
868}
869
870fn parse_linear(start: &BytesStart<'_>) -> Result<LinearGradient> {
871 reject_conflicting_a_prefix(start)?;
872 Ok(LinearGradient {
873 angle: Angle(required_i32(start, b"ang")?),
874 scaled: optional_bool(start, b"scaled")?,
875 raw_children: OrderedRawChildren::default(),
876 })
877}
878
879fn parse_linear_element(
880 reader: &mut Reader<&[u8]>,
881 start: &BytesStart<'_>,
882) -> Result<LinearGradient> {
883 let mut linear = parse_linear(start)?;
884 linear.raw_children = capture_all_children(reader, b"lin")?;
885 Ok(linear)
886}
887
888fn parse_empty_path(start: &BytesStart<'_>) -> Result<PathGradient> {
889 reject_conflicting_a_prefix(start)?;
890 let value = required_attr(start, b"path")?;
891 let kind = PathGradientKind::parse(&value).ok_or_else(|| invalid(start, b"path", value))?;
892 Ok(PathGradient {
893 kind,
894 fill_to_rect: None,
895 raw_children: OrderedRawChildren::default(),
896 })
897}
898
899fn parse_path(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<PathGradient> {
900 let mut path = parse_empty_path(start)?;
901 let mut buffer = Vec::new();
902 let mut boundary = 0;
903 loop {
904 match reader
905 .read_event_into(&mut buffer)
906 .map_err(OxmlError::from)?
907 {
908 Event::Start(element) if matches_local_name(element.name().as_ref(), b"fillToRect") => {
909 path.fill_to_rect = Some(parse_rect_element(reader, &element)?);
910 boundary = 1;
911 }
912 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"fillToRect") => {
913 path.fill_to_rect = Some(parse_rect(&element)?);
914 boundary = 1;
915 }
916 Event::Start(element) => path
917 .raw_children
918 .push(boundary, capture_element(reader, &element)?),
919 Event::Empty(element) => path
920 .raw_children
921 .push(boundary, capture_empty_element(&element)?),
922 Event::End(element) if matches_local_name(element.name().as_ref(), b"path") => break,
923 Event::Eof => return Err(missing_end("path")),
924 _ => {}
925 }
926 buffer.clear();
927 }
928 Ok(path)
929}
930
931fn write_geometry<W: Write>(writer: &mut Writer<W>, geometry: &GradientGeometry) -> Result<()> {
932 match geometry {
933 GradientGeometry::Linear(linear) => {
934 let angle = linear.angle.0.to_string();
935 let mut start = BytesStart::new("a:lin");
936 start.push_attribute(("ang", angle.as_str()));
937 if let Some(scaled) = linear.scaled.map(bool_text) {
938 start.push_attribute(("scaled", scaled));
939 }
940 if linear.raw_children.is_empty() {
941 return write_empty(writer, start);
942 }
943 write_start(writer, start)?;
944 emit_raw(writer, linear.raw_children.at(0))?;
945 write_end(writer, "a:lin")
946 }
947 GradientGeometry::Path(path) => {
948 let mut start = BytesStart::new("a:path");
949 start.push_attribute(("path", path.kind.as_str()));
950 if path.fill_to_rect.is_none() && path.raw_children.is_empty() {
951 return write_empty(writer, start);
952 }
953 write_start(writer, start)?;
954 emit_raw(writer, path.raw_children.at(0))?;
955 if let Some(rect) = &path.fill_to_rect {
956 write_rect(writer, "a:fillToRect", rect)?;
957 }
958 emit_raw(writer, path.raw_children.at(1))?;
959 write_end(writer, "a:path")
960 }
961 }
962}
963
964fn parse_stretch(reader: &mut Reader<&[u8]>) -> Result<BlipMode> {
965 let mut fill_rect = None;
966 let mut raw_children = OrderedRawChildren::default();
967 let mut boundary = 0;
968 let mut buffer = Vec::new();
969 loop {
970 match reader
971 .read_event_into(&mut buffer)
972 .map_err(OxmlError::from)?
973 {
974 Event::Start(element) if matches_local_name(element.name().as_ref(), b"fillRect") => {
975 fill_rect = Some(parse_rect_element(reader, &element)?);
976 boundary = 1;
977 }
978 Event::Empty(element) if matches_local_name(element.name().as_ref(), b"fillRect") => {
979 fill_rect = Some(parse_rect(&element)?);
980 boundary = 1;
981 }
982 Event::Start(element) => {
983 raw_children.push(boundary, capture_element(reader, &element)?)
984 }
985 Event::Empty(element) => raw_children.push(boundary, capture_empty_element(&element)?),
986 Event::End(element) if matches_local_name(element.name().as_ref(), b"stretch") => break,
987 Event::Eof => return Err(missing_end("stretch")),
988 _ => {}
989 }
990 buffer.clear();
991 }
992 Ok(BlipMode::Stretch {
993 fill_rect,
994 raw_children,
995 })
996}
997
998fn parse_empty_tile(start: &BytesStart<'_>) -> Result<Tile> {
999 reject_conflicting_a_prefix(start)?;
1000 Ok(Tile {
1001 translation_x: optional_parse(start, b"tx")?,
1002 translation_y: optional_parse(start, b"ty")?,
1003 scale_x: optional_parse::<i32>(start, b"sx")?.map(Percent1000),
1004 scale_y: optional_parse::<i32>(start, b"sy")?.map(Percent1000),
1005 flip: optional_token(start, b"flip", &["none", "x", "y", "xy"])?,
1006 alignment: optional_token(
1007 start,
1008 b"algn",
1009 &["tl", "t", "tr", "l", "ctr", "r", "bl", "b", "br"],
1010 )?,
1011 raw_children: OrderedRawChildren::default(),
1012 })
1013}
1014
1015fn parse_tile(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<Tile> {
1016 let mut tile = parse_empty_tile(start)?;
1017 tile.raw_children = capture_all_children(reader, b"tile")?;
1018 Ok(tile)
1019}
1020
1021fn write_blip_mode<W: Write>(writer: &mut Writer<W>, mode: &BlipMode) -> Result<()> {
1022 match mode {
1023 BlipMode::Stretch {
1024 fill_rect,
1025 raw_children,
1026 } => {
1027 if fill_rect.is_none() && raw_children.is_empty() {
1028 return write_empty(writer, BytesStart::new("a:stretch"));
1029 }
1030 write_start(writer, BytesStart::new("a:stretch"))?;
1031 emit_raw(writer, raw_children.at(0))?;
1032 if let Some(rect) = fill_rect {
1033 write_rect(writer, "a:fillRect", rect)?;
1034 }
1035 emit_raw(writer, raw_children.at(1))?;
1036 write_end(writer, "a:stretch")
1037 }
1038 BlipMode::Tile(tile) => {
1039 let mut start = BytesStart::new("a:tile");
1040 let tx = tile.translation_x.map(|value| value.to_string());
1041 let ty = tile.translation_y.map(|value| value.to_string());
1042 let sx = tile.scale_x.map(|value| value.0.to_string());
1043 let sy = tile.scale_y.map(|value| value.0.to_string());
1044 for (name, value) in [
1045 ("tx", tx.as_deref()),
1046 ("ty", ty.as_deref()),
1047 ("sx", sx.as_deref()),
1048 ("sy", sy.as_deref()),
1049 ] {
1050 if let Some(value) = value {
1051 start.push_attribute((name, value));
1052 }
1053 }
1054 if let Some(flip) = tile.flip.as_deref() {
1055 start.push_attribute(("flip", flip));
1056 }
1057 if let Some(alignment) = tile.alignment.as_deref() {
1058 start.push_attribute(("algn", alignment));
1059 }
1060 if tile.raw_children.is_empty() {
1061 return write_empty(writer, start);
1062 }
1063 write_start(writer, start)?;
1064 emit_raw(writer, tile.raw_children.at(0))?;
1065 write_end(writer, "a:tile")
1066 }
1067 }
1068}
1069
1070fn parse_rect(start: &BytesStart<'_>) -> Result<RelativeRect> {
1071 reject_conflicting_a_prefix(start)?;
1072 Ok(RelativeRect {
1073 left: optional_parse::<i32>(start, b"l")?.map(Percent1000),
1074 top: optional_parse::<i32>(start, b"t")?.map(Percent1000),
1075 right: optional_parse::<i32>(start, b"r")?.map(Percent1000),
1076 bottom: optional_parse::<i32>(start, b"b")?.map(Percent1000),
1077 raw_children: OrderedRawChildren::default(),
1078 })
1079}
1080
1081fn parse_rect_element(reader: &mut Reader<&[u8]>, start: &BytesStart<'_>) -> Result<RelativeRect> {
1082 let mut rect = parse_rect(start)?;
1083 rect.raw_children = capture_all_children(reader, local_name(start.name().as_ref()))?;
1084 Ok(rect)
1085}
1086
1087fn write_rect<W: Write>(writer: &mut Writer<W>, name: &str, rect: &RelativeRect) -> Result<()> {
1088 let left = rect.left.map(|value| value.0.to_string());
1089 let top = rect.top.map(|value| value.0.to_string());
1090 let right = rect.right.map(|value| value.0.to_string());
1091 let bottom = rect.bottom.map(|value| value.0.to_string());
1092 let mut start = BytesStart::new(name);
1093 for (name, value) in [
1094 ("l", left.as_deref()),
1095 ("t", top.as_deref()),
1096 ("r", right.as_deref()),
1097 ("b", bottom.as_deref()),
1098 ] {
1099 if let Some(value) = value {
1100 start.push_attribute((name, value));
1101 }
1102 }
1103 if rect.raw_children.is_empty() {
1104 return write_empty(writer, start);
1105 }
1106 write_start(writer, start)?;
1107 emit_raw(writer, rect.raw_children.at(0))?;
1108 write_end(writer, name)
1109}
1110
1111fn parse_color_container(
1112 reader: &mut Reader<&[u8]>,
1113 end_name: &[u8],
1114) -> Result<(Option<ColorChoice>, OrderedRawChildren)> {
1115 let mut color = None;
1116 let mut raw_children = OrderedRawChildren::default();
1117 let mut boundary = 0;
1118 let mut buffer = Vec::new();
1119 loop {
1120 match reader
1121 .read_event_into(&mut buffer)
1122 .map_err(OxmlError::from)?
1123 {
1124 Event::Start(element) if is_color(element.name().as_ref()) => {
1125 color = Some(ColorChoice::from_xml(reader, &element)?);
1126 boundary = 1;
1127 }
1128 Event::Empty(element) if is_color(element.name().as_ref()) => {
1129 color = Some(ColorChoice::from_empty_xml(&element)?);
1130 boundary = 1;
1131 }
1132 Event::Start(element) => {
1133 raw_children.push(boundary, capture_element(reader, &element)?);
1134 }
1135 Event::Empty(element) => {
1136 raw_children.push(boundary, capture_empty_element(&element)?);
1137 }
1138 Event::End(element) if matches_local_name(element.name().as_ref(), end_name) => break,
1139 Event::Eof => return Err(missing_end(&String::from_utf8_lossy(end_name))),
1140 _ => {}
1141 }
1142 buffer.clear();
1143 }
1144 Ok((color, raw_children))
1145}
1146
1147fn write_color_container<W: Write>(
1148 writer: &mut Writer<W>,
1149 name: &str,
1150 color: Option<&ColorChoice>,
1151 raw_children: &OrderedRawChildren,
1152) -> Result<()> {
1153 write_start(writer, BytesStart::new(name))?;
1154 emit_raw(writer, raw_children.at(0))?;
1155 if let Some(color) = color {
1156 color.to_xml(writer)?;
1157 }
1158 emit_raw(writer, raw_children.at(1))?;
1159 write_end(writer, name)
1160}
1161
1162fn read_color_children(
1163 reader: &mut Reader<&[u8]>,
1164 end_name: &[u8],
1165 color: &mut Option<ColorChoice>,
1166 raw_children: &mut OrderedRawChildren,
1167) -> Result<()> {
1168 let mut buffer = Vec::new();
1169 let mut boundary = 0;
1170 loop {
1171 match reader
1172 .read_event_into(&mut buffer)
1173 .map_err(OxmlError::from)?
1174 {
1175 Event::Start(element) if is_color(element.name().as_ref()) => {
1176 *color = Some(ColorChoice::from_xml(reader, &element)?);
1177 boundary = 1;
1178 }
1179 Event::Empty(element) if is_color(element.name().as_ref()) => {
1180 *color = Some(ColorChoice::from_empty_xml(&element)?);
1181 boundary = 1;
1182 }
1183 Event::Start(element) => {
1184 raw_children.push(boundary, capture_element(reader, &element)?)
1185 }
1186 Event::Empty(element) => raw_children.push(boundary, capture_empty_element(&element)?),
1187 Event::End(element) if matches_local_name(element.name().as_ref(), end_name) => break,
1188 Event::Eof => return Err(missing_end(&String::from_utf8_lossy(end_name))),
1189 _ => {}
1190 }
1191 buffer.clear();
1192 }
1193 Ok(())
1194}
1195
1196fn capture_all_children(reader: &mut Reader<&[u8]>, end_name: &[u8]) -> Result<OrderedRawChildren> {
1197 let mut raw_children = OrderedRawChildren::default();
1198 let mut buffer = Vec::new();
1199 loop {
1200 match reader
1201 .read_event_into(&mut buffer)
1202 .map_err(OxmlError::from)?
1203 {
1204 Event::Start(element) => raw_children.push(0, capture_element(reader, &element)?),
1205 Event::Empty(element) => raw_children.push(0, capture_empty_element(&element)?),
1206 Event::End(element) if matches_local_name(element.name().as_ref(), end_name) => break,
1207 Event::Eof => return Err(missing_end(&String::from_utf8_lossy(end_name))),
1208 _ => {}
1209 }
1210 buffer.clear();
1211 }
1212 Ok(raw_children)
1213}
1214
1215fn is_color(name: &[u8]) -> bool {
1216 matches!(
1217 local_name(name),
1218 b"srgbClr" | b"schemeClr" | b"sysClr" | b"prstClr"
1219 )
1220}
1221
1222fn validate_gradient_position(value: i32) -> Result<()> {
1223 if (0..=100_000).contains(&value) {
1224 Ok(())
1225 } else {
1226 Err(FillError::GradientStopOutOfRange(value))
1227 }
1228}
1229
1230fn required_attr(start: &BytesStart<'_>, name: &[u8]) -> Result<String> {
1231 get_attr(start, name).ok_or_else(|| FillError::MissingAttribute {
1232 element: String::from_utf8_lossy(local_name(start.name().as_ref())).into_owned(),
1233 attribute: String::from_utf8_lossy(name).into_owned(),
1234 })
1235}
1236
1237fn required_i32(start: &BytesStart<'_>, name: &[u8]) -> Result<i32> {
1238 let value = required_attr(start, name)?;
1239 value.parse().map_err(|_| invalid(start, name, value))
1240}
1241
1242fn optional_parse<T: std::str::FromStr>(start: &BytesStart<'_>, name: &[u8]) -> Result<Option<T>> {
1243 get_attr(start, name)
1244 .map(|value| value.parse().map_err(|_| invalid(start, name, value)))
1245 .transpose()
1246}
1247
1248fn optional_bool(start: &BytesStart<'_>, name: &[u8]) -> Result<Option<bool>> {
1249 get_attr(start, name)
1250 .map(|value| match value.as_str() {
1251 "1" | "true" => Ok(true),
1252 "0" | "false" => Ok(false),
1253 _ => Err(invalid(start, name, value)),
1254 })
1255 .transpose()
1256}
1257
1258fn optional_token(start: &BytesStart<'_>, name: &[u8], allowed: &[&str]) -> Result<Option<String>> {
1259 get_attr(start, name)
1260 .map(|value| {
1261 if allowed.contains(&value.as_str()) {
1262 Ok(value)
1263 } else {
1264 Err(invalid(start, name, value))
1265 }
1266 })
1267 .transpose()
1268}
1269
1270fn invalid(start: &BytesStart<'_>, attribute: &[u8], value: String) -> FillError {
1271 FillError::InvalidAttribute {
1272 element: String::from_utf8_lossy(local_name(start.name().as_ref())).into_owned(),
1273 attribute: String::from_utf8_lossy(attribute).into_owned(),
1274 value,
1275 }
1276}
1277
1278fn unexpected(start: &BytesStart<'_>) -> FillError {
1279 FillError::UnexpectedElement(String::from_utf8_lossy(start.name().as_ref()).into_owned())
1280}
1281
1282fn missing_end(name: &str) -> FillError {
1283 FillError::Xml(OxmlError::MissingElement(format!("closing a:{name}")))
1284}
1285
1286const fn bool_text(value: bool) -> &'static str {
1287 if value { "1" } else { "0" }
1288}
1289
1290fn emit_raw<'a, W: Write>(
1291 writer: &mut Writer<W>,
1292 children: impl Iterator<Item = &'a [u8]>,
1293) -> Result<()> {
1294 for child in children {
1295 writer.get_mut().write_all(child).map_err(OxmlError::from)?;
1296 }
1297 Ok(())
1298}
1299
1300fn write_start<W: Write>(writer: &mut Writer<W>, start: BytesStart<'_>) -> Result<()> {
1301 writer
1302 .write_event(Event::Start(start))
1303 .map_err(OxmlError::from)?;
1304 Ok(())
1305}
1306
1307fn write_empty<W: Write>(writer: &mut Writer<W>, start: BytesStart<'_>) -> Result<()> {
1308 writer
1309 .write_event(Event::Empty(start))
1310 .map_err(OxmlError::from)?;
1311 Ok(())
1312}
1313
1314fn write_end<W: Write>(writer: &mut Writer<W>, name: &str) -> Result<()> {
1315 writer
1316 .write_event(Event::End(BytesEnd::new(name)))
1317 .map_err(OxmlError::from)?;
1318 Ok(())
1319}
1320
1321#[cfg(test)]
1322mod tests {
1323 use super::{Fill, FillError, GradientGeometry};
1324
1325 #[test]
1326 fn every_fill_form_round_trips_and_gradient_stops_keep_document_order() {
1327 let cases: &[&[u8]] = &[
1328 br#"<a:noFill/>"#,
1329 br#"<a:solidFill><a:schemeClr val="accent1"/></a:solidFill>"#,
1330 br#"<a:gradFill flip="xy" rotWithShape="1"><a:gsLst><a:gs pos="75000"><a:srgbClr val="FF0000"/></a:gs><a:gs pos="25000"><a:schemeClr val="accent2"/></a:gs></a:gsLst><a:lin ang="5400000" scaled="1"/><a:tileRect l="1000"/></a:gradFill>"#,
1331 br#"<a:gradFill><a:gsLst><a:gs pos="0"><a:srgbClr val="000000"/></a:gs><a:gs pos="100000"><a:srgbClr val="FFFFFF"/></a:gs></a:gsLst><a:path path="circle"><a:fillToRect l="10000" t="10000" r="10000" b="10000"/></a:path></a:gradFill>"#,
1332 br#"<a:pattFill prst="pct10"><a:fgClr><a:srgbClr val="112233"/></a:fgClr><a:bgClr><a:schemeClr val="bg1"/></a:bgClr></a:pattFill>"#,
1333 br#"<a:blipFill dpi="96" rotWithShape="0"><a:blip r:embed="rId7"/><a:srcRect l="1000" t="2000"/><a:stretch><a:fillRect r="3000"/></a:stretch></a:blipFill>"#,
1334 br#"<a:blipFill><a:blip r:link="rId8"/><a:tile tx="10" ty="20" sx="50000" sy="75000" flip="x" algn="ctr"/></a:blipFill>"#,
1335 ];
1336
1337 for xml in cases {
1338 let parsed = Fill::from_xml(xml).unwrap();
1339 let written = parsed.to_xml().unwrap();
1340 assert_eq!(Fill::from_xml(&written).unwrap(), parsed);
1341 }
1342
1343 let Fill::Gradient(gradient) = Fill::from_xml(cases[2]).unwrap() else {
1344 panic!("expected gradient")
1345 };
1346 assert_eq!(
1347 gradient
1348 .stops
1349 .iter()
1350 .map(|stop| stop.position.0)
1351 .collect::<Vec<_>>(),
1352 vec![75_000, 25_000]
1353 );
1354 assert!(matches!(
1355 gradient.geometry,
1356 Some(GradientGeometry::Linear(_))
1357 ));
1358 }
1359
1360 #[test]
1361 fn fill_forms_read_any_prefix_and_write_fixed_a_prefix_in_schema_order() {
1362 let fill = Fill::from_xml(br#"<z:blipFill><z:blip r:embed="rId4"/><z:srcRect l="10"/><z:stretch><z:fillRect t="20"/></z:stretch></z:blipFill>"#).unwrap();
1363 assert_eq!(fill.to_xml().unwrap(), br#"<a:blipFill><a:blip xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="rId4"/><a:srcRect l="10"/><a:stretch><a:fillRect t="20"/></a:stretch></a:blipFill>"#);
1364
1365 let gradient = Fill::from_xml(br#"<q:gradFill><q:gsLst><q:gs pos="0"><q:srgbClr val="ABCDEF"/></q:gs></q:gsLst><q:path path="rect"/></q:gradFill>"#).unwrap();
1366 assert_eq!(gradient.to_xml().unwrap(), br#"<a:gradFill><a:gsLst><a:gs pos="0"><a:srgbClr val="ABCDEF"/></a:gs></a:gsLst><a:path path="rect"/></a:gradFill>"#);
1367 }
1368
1369 #[test]
1370 fn blip_relationship_prefix_is_declared_in_canonical_output() {
1371 let fill = Fill::from_xml(
1372 br#"<a:blipFill><a:blip xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="rId1"/></a:blipFill>"#,
1373 )
1374 .unwrap();
1375 let written = String::from_utf8(fill.to_xml().unwrap()).unwrap();
1376
1377 assert!(written.contains(
1378 "<a:blip xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" r:embed=\"rId1\"/>"
1379 ));
1380 }
1381
1382 #[test]
1383 fn unknown_fill_children_round_trip_byte_for_byte_in_place() {
1384 let xml = br#"<z:gradFill><x:before x:id="1"><x:item>one & two</x:item><!--note--></x:before><z:gsLst><y:first/><z:gs pos="0"><z:srgbClr val="000000"><x:transform value="kept"/></z:srgbClr></z:gs><y:last/></z:gsLst><x:middle/><z:lin ang="0"/><x:after/></z:gradFill>"#;
1385 let written = Fill::from_xml(xml).unwrap().to_xml().unwrap();
1386 assert_eq!(written, br#"<a:gradFill><x:before x:id="1"><x:item>one & two</x:item><!--note--></x:before><a:gsLst><y:first/><a:gs pos="0"><a:srgbClr val="000000"><x:transform value="kept"/></a:srgbClr></a:gs><y:last/></a:gsLst><x:middle/><a:lin ang="0"/><x:after/></a:gradFill>"#);
1387
1388 let blip = br#"<z:blipFill><z:blip r:embed="rId1"><a:alphaModFix amt="50000"/><x:ext x:v="raw"/></z:blip><x:between/><z:tile><x:tileExt/></z:tile></z:blipFill>"#;
1389 assert_eq!(Fill::from_xml(blip).unwrap().to_xml().unwrap(), br#"<a:blipFill><a:blip xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="rId1"><a:alphaModFix amt="50000"/><x:ext x:v="raw"/></a:blip><x:between/><a:tile><x:tileExt/></a:tile></a:blipFill>"#);
1390
1391 let pattern = br#"<z:pattFill><z:fgClr><x:before/><z:srgbClr val="102030"/><x:after x:v="kept"/></z:fgClr></z:pattFill>"#;
1392 assert_eq!(Fill::from_xml(pattern).unwrap().to_xml().unwrap(), br#"<a:pattFill><a:fgClr><x:before/><a:srgbClr val="102030"/><x:after x:v="kept"/></a:fgClr></a:pattFill>"#);
1393
1394 let extension_only_pattern =
1395 br#"<z:pattFill><z:fgClr><x:extension x:v="kept"/></z:fgClr></z:pattFill>"#;
1396 assert_eq!(
1397 Fill::from_xml(extension_only_pattern)
1398 .unwrap()
1399 .to_xml()
1400 .unwrap(),
1401 br#"<a:pattFill><a:fgClr><x:extension x:v="kept"/></a:fgClr></a:pattFill>"#
1402 );
1403
1404 let gradient_leaf_extensions = br#"<z:gradFill><z:lin ang="0"><x:linExt/></z:lin><z:tileRect l="10"><x:tileRectExt/></z:tileRect></z:gradFill>"#;
1405 assert_eq!(Fill::from_xml(gradient_leaf_extensions).unwrap().to_xml().unwrap(), br#"<a:gradFill><a:lin ang="0"><x:linExt/></a:lin><a:tileRect l="10"><x:tileRectExt/></a:tileRect></a:gradFill>"#);
1406
1407 let extension_only_stop_list =
1408 br#"<z:gradFill><z:gsLst><x:extension x:v="kept"/></z:gsLst></z:gradFill>"#;
1409 assert_eq!(
1410 Fill::from_xml(extension_only_stop_list)
1411 .unwrap()
1412 .to_xml()
1413 .unwrap(),
1414 br#"<a:gradFill><a:gsLst><x:extension x:v="kept"/></a:gsLst></a:gradFill>"#
1415 );
1416
1417 let path_leaf_extension = br#"<z:gradFill><z:path path="rect"><z:fillToRect><x:pathRectExt/></z:fillToRect></z:path></z:gradFill>"#;
1418 assert_eq!(Fill::from_xml(path_leaf_extension).unwrap().to_xml().unwrap(), br#"<a:gradFill><a:path path="rect"><a:fillToRect><x:pathRectExt/></a:fillToRect></a:path></a:gradFill>"#);
1419
1420 let blip_leaf_extensions = br#"<z:blipFill><z:srcRect><x:sourceExt/></z:srcRect><z:stretch><z:fillRect><x:fillExt/></z:fillRect></z:stretch></z:blipFill>"#;
1421 assert_eq!(Fill::from_xml(blip_leaf_extensions).unwrap().to_xml().unwrap(), br#"<a:blipFill><a:srcRect><x:sourceExt/></a:srcRect><a:stretch><a:fillRect><x:fillExt/></a:fillRect></a:stretch></a:blipFill>"#);
1422 }
1423
1424 #[test]
1425 fn malformed_fill_values_return_errors_without_panicking() {
1426 let cases: &[&[u8]] = &[
1427 br#"<a:gradFill rotWithShape="maybe"/>"#,
1428 br#"<a:gradFill><a:gsLst><a:gs pos="100001"/></a:gsLst></a:gradFill>"#,
1429 br#"<a:gradFill><a:gsLst><a:gs/></a:gsLst></a:gradFill>"#,
1430 br#"<a:gradFill><a:lin ang="ninety"/></a:gradFill>"#,
1431 br#"<a:gradFill><a:path path="triangle"/></a:gradFill>"#,
1432 br#"<a:blipFill dpi="many"/>"#,
1433 br#"<a:blipFill><a:srcRect l="half"/></a:blipFill>"#,
1434 br#"<a:blipFill><a:tile sx="large"/></a:blipFill>"#,
1435 br#"<a:gradFill flip="diagonal"/>"#,
1436 br#"<a:blipFill><a:tile algn="middle"/></a:blipFill>"#,
1437 ];
1438 for xml in cases {
1439 let result = std::panic::catch_unwind(|| Fill::from_xml(xml));
1440 assert!(
1441 result.is_ok(),
1442 "parser panicked for {}",
1443 String::from_utf8_lossy(xml)
1444 );
1445 assert!(
1446 result.unwrap().is_err(),
1447 "malformed fill parsed successfully"
1448 );
1449 }
1450 assert!(matches!(
1451 Fill::from_xml(cases[1]),
1452 Err(FillError::GradientStopOutOfRange(100_001))
1453 ));
1454 }
1455}