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