1use std::io::Write;
2
3use oxml_core::OxmlError;
4use oxml_core::raw_xml::{capture_element, capture_empty_element};
5use oxml_core::xml::{local_name, matches_local_name};
6use quick_xml::events::{BytesEnd, BytesStart, Event};
7use quick_xml::{Reader, Writer, XmlVersion};
8
9use super::paragraph::{
10 TextNamespaceScope, text_close_generated_child, text_root_spellings_scoped, text_validate_root,
11};
12use crate::order::OrderedRawChildren;
13
14use super::body::{Result, TextError, missing_end};
15use super::paragraph::{
16 CT_TextParagraphProperties, text_capture_opaque_event, text_fragment_scope, text_owned_name,
17 text_root_spellings, text_source_start,
18};
19
20#[allow(non_camel_case_types)]
22#[derive(Clone, Debug, Default)]
23pub struct CT_TextListStyle {
24 pub default_paragraph_properties: Option<CT_TextParagraphProperties>,
25 pub level1: Option<CT_TextParagraphProperties>,
26 pub level2: Option<CT_TextParagraphProperties>,
27 pub level3: Option<CT_TextParagraphProperties>,
28 pub level4: Option<CT_TextParagraphProperties>,
29 pub level5: Option<CT_TextParagraphProperties>,
30 pub level6: Option<CT_TextParagraphProperties>,
31 pub level7: Option<CT_TextParagraphProperties>,
32 pub level8: Option<CT_TextParagraphProperties>,
33 pub level9: Option<CT_TextParagraphProperties>,
34 raw_attributes: Vec<(String, String)>,
35 source_root: Vec<String>,
36 source_tag: String,
37 source_namespaces: Vec<(String, String)>,
38 raw_children: OrderedRawChildren,
39}
40impl PartialEq for CT_TextListStyle {
41 fn eq(&self, other: &Self) -> bool {
42 self.default_paragraph_properties == other.default_paragraph_properties
43 && self.level1 == other.level1
44 && self.level2 == other.level2
45 && self.level3 == other.level3
46 && self.level4 == other.level4
47 && self.level5 == other.level5
48 && self.level6 == other.level6
49 && self.level7 == other.level7
50 && self.level8 == other.level8
51 && self.level9 == other.level9
52 && self
53 .raw_attributes
54 .iter()
55 .filter(|(key, _)| key != "xmlns" && !key.starts_with("xmlns:"))
56 .eq(other
57 .raw_attributes
58 .iter()
59 .filter(|(key, _)| key != "xmlns" && !key.starts_with("xmlns:")))
60 && matches!(
61 (
62 super::paragraph::text_namespace_scope(
63 &text_source_start(&self.source_tag, &self.source_root),
64 &[],
65 ),
66 super::paragraph::text_namespace_scope(
67 &text_source_start(&other.source_tag, &other.source_root),
68 &[],
69 ),
70 ),
71 (Ok(left), Ok(right)) if left.iter().filter(|(key, value)| key != "r" || value != "http://schemas.openxmlformats.org/officeDocument/2006/relationships").map(|(key, value)| (key, value)).collect::<std::collections::BTreeMap<_, _>>()
72 == right.iter().filter(|(key, value)| key != "r" || value != "http://schemas.openxmlformats.org/officeDocument/2006/relationships").map(|(key, value)| (key, value)).collect::<std::collections::BTreeMap<_, _>>()
73 )
74 && self.raw_children == other.raw_children
75 }
76}
77impl Eq for CT_TextListStyle {}
78
79impl CT_TextListStyle {
80 pub fn from_xml(xml: &[u8]) -> Result<Self> {
83 Self::from_xml_with_namespaces(xml, &TextNamespaceScope::default())
84 }
85
86 pub fn from_xml_with_inherited_namespaces(
89 xml: &[u8],
90 inherited: &[(String, String)],
91 ) -> Result<Self> {
92 Self::from_xml_with_namespaces(xml, &TextNamespaceScope::inherited(inherited))
93 }
94
95 pub(crate) fn from_xml_with_namespaces(
96 xml: &[u8],
97 inherited: &TextNamespaceScope,
98 ) -> Result<Self> {
99 Self::from_xml_boxed_with_namespaces(xml, inherited).map(|style| *style)
100 }
101
102 pub(super) fn from_xml_boxed_with_namespaces(
103 xml: &[u8],
104 inherited: &TextNamespaceScope,
105 ) -> Result<Box<Self>> {
106 let mut reader = Reader::from_reader(xml);
107 let mut buffer = Vec::new();
108 loop {
109 match reader
110 .read_event_into(&mut buffer)
111 .map_err(OxmlError::from)?
112 {
113 Event::Start(element) if is_list_style_root(element.name().as_ref()) => {
114 validate_list_style_root(&element)?;
115 return Self::from_element(&mut reader, &element, inherited);
116 }
117 Event::Empty(element) if is_list_style_root(element.name().as_ref()) => {
118 validate_list_style_root(&element)?;
119 let mut style = Self::from_start(&element)?;
120 style.source_namespaces = text_fragment_scope(&element, inherited)?.physical;
121 style.source_root = text_root_spellings_scoped(
122 &element,
123 inherited,
124 &capture_empty_element(&element)?,
125 )?;
126 return Ok(style);
127 }
128 Event::Start(element) | Event::Empty(element) => {
129 return Err(unexpected(&element));
130 }
131 Event::Eof => {
132 return Err(TextError::Xml(OxmlError::MissingElement(
133 "DrawingML list style".to_owned(),
134 )));
135 }
136 _ => {}
137 }
138 buffer.clear();
139 }
140 }
141
142 fn from_start(start: &BytesStart<'_>) -> Result<Box<Self>> {
143 Ok(Box::new(Self {
144 raw_attributes: capture_raw_attributes(start)?,
145 source_root: text_root_spellings(start)?,
146 source_tag: std::str::from_utf8(start.name().as_ref())
147 .map_err(OxmlError::from)?
148 .to_owned(),
149 ..Self::default()
150 }))
151 }
152
153 fn from_element(
154 reader: &mut Reader<&[u8]>,
155 start: &BytesStart<'_>,
156 inherited: &TextNamespaceScope,
157 ) -> Result<Box<Self>> {
158 let mut namespaces = text_fragment_scope(start, inherited)?;
159 namespaces.fixed_list_style_children = true;
160 let mut style = Self::from_start(start)?;
161 style.source_namespaces = namespaces.physical.clone();
162 style.source_root = text_root_spellings_scoped(
163 start,
164 inherited,
165 &capture_element(&mut reader.clone(), start)?,
166 )?;
167 let root_name = local_name(start.name().as_ref()).to_vec();
168 let mut boundary = 0;
169 let mut buffer = Vec::new();
170 loop {
171 match reader
172 .read_event_into(&mut buffer)
173 .map_err(OxmlError::from)?
174 {
175 Event::Start(element) => {
176 let name = text_owned_name(&element, &namespaces)?;
177 if name == b"defPPr" || list_level(&name)?.is_some() {
178 validate_list_style_root(&element)?;
179 }
180 let raw = capture_element(reader, &element)?;
181 style.capture_child(&name, raw, &mut boundary, &namespaces)?;
182 }
183 Event::Empty(element) => {
184 let name = text_owned_name(&element, &namespaces)?;
185 if name == b"defPPr" || list_level(&name)?.is_some() {
186 validate_list_style_root(&element)?;
187 }
188 let raw = capture_empty_element(&element)?;
189 style.capture_child(&name, raw, &mut boundary, &namespaces)?;
190 }
191 Event::End(element)
192 if local_name(element.name().as_ref()) == root_name.as_slice() =>
193 {
194 return Ok(style);
195 }
196 Event::Eof => {
197 return Err(missing_end(
198 std::str::from_utf8(&root_name).unwrap_or("list style"),
199 ));
200 }
201 event @ (Event::Comment(_) | Event::PI(_) | Event::Text(_) | Event::CData(_)) => {
202 style
203 .raw_children
204 .push(boundary, text_capture_opaque_event(event)?);
205 }
206 _ => {}
207 }
208 buffer.clear();
209 }
210 }
211
212 fn capture_child(
213 &mut self,
214 name: &[u8],
215 raw: Vec<u8>,
216 boundary: &mut usize,
217 namespaces: &TextNamespaceScope,
218 ) -> Result<()> {
219 if name == b"defPPr" {
220 if self.default_paragraph_properties.is_some() {
221 return Err(TextError::DuplicateElement("defPPr".to_owned()));
222 }
223 self.default_paragraph_properties = Some(
224 CT_TextParagraphProperties::from_xml_with_namespaces(&raw, namespaces)?,
225 );
226 *boundary = (*boundary).max(1);
227 return Ok(());
228 }
229 if let Some(level) = list_level(name)? {
230 if self.level(level).is_some() {
231 return Err(TextError::DuplicateElement(
232 String::from_utf8_lossy(name).into_owned(),
233 ));
234 }
235 self.set_level(
236 level,
237 CT_TextParagraphProperties::from_xml_with_namespaces(&raw, namespaces)?,
238 );
239 *boundary = (*boundary).max(level + 1);
240 return Ok(());
241 }
242
243 let schema_boundary = match name {
244 b"extLst" => 10,
245 _ => *boundary,
246 };
247 self.raw_children
248 .push((*boundary).max(schema_boundary), raw);
249 *boundary = (*boundary).max(schema_boundary);
250 Ok(())
251 }
252
253 pub fn with_inherited_defaults(&self, defaults: &Self) -> Result<Self> {
255 let mut local = Writer::new(Vec::new());
256 self.write_xml(&mut local)?;
257 let mut inherited = Writer::new(Vec::new());
258 defaults.write_xml(&mut inherited)?;
259 let xml = super::paragraph::text_inherit_property_xml(
260 &local.into_inner(),
261 &self.source_namespaces,
262 &inherited.into_inner(),
263 &defaults.source_namespaces,
264 )?;
265 let mut result = Self::from_xml_with_inherited_namespaces(&xml, &self.source_namespaces)?;
266 for level in 1..=9 {
267 if defaults.default_paragraph_properties.is_none()
268 && defaults.level(level).is_none()
269 && self.default_paragraph_properties.is_none()
270 && self.level(level).is_none()
271 {
272 continue;
273 }
274 let mut properties = defaults
275 .default_paragraph_properties
276 .clone()
277 .unwrap_or_default();
278 for overlay in [
279 defaults.level(level),
280 self.default_paragraph_properties.as_ref(),
281 self.level(level),
282 ]
283 .into_iter()
284 .flatten()
285 {
286 properties = overlay.with_inherited_defaults(&properties)?;
287 }
288 result.set_level(level, properties);
289 }
290 Ok(result)
291 }
292
293 pub fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<()> {
295 self.write_xml_as(writer, "a:lstStyle")
296 }
297
298 pub fn write_xml_as<W: Write>(&self, writer: &mut Writer<W>, tag: &str) -> Result<()> {
300 let mut start =
301 super::paragraph::text_wrapper_start(tag, &self.source_tag, &self.source_root)?;
302 let owned_tag = std::str::from_utf8(start.name().as_ref())
303 .map_err(OxmlError::from)?
304 .to_owned();
305 let tag = owned_tag.as_str();
306 if self.source_root.is_empty() {
307 push_raw_attributes(&mut start, &self.raw_attributes);
308 }
309 let has_modelled = self.default_paragraph_properties.is_some()
310 || (1..=9).any(|level| self.level(level).is_some());
311 if !has_modelled && self.raw_children.is_empty() {
312 return write_empty(writer, start);
313 }
314
315 write_start(writer, start)?;
316 emit_raw(writer, self.raw_children.at(0))?;
317 if let Some(properties) = &self.default_paragraph_properties {
318 {
319 let mut child = Writer::new(Vec::new());
320 properties.write_xml(&mut child, "a:defPPr")?;
321 writer
322 .get_mut()
323 .write_all(&text_close_generated_child(
324 &child.into_inner(),
325 &super::paragraph::text_namespace_scope(
326 &text_source_start(tag, &self.source_root),
327 &[],
328 )?,
329 )?)
330 .map_err(OxmlError::from)?;
331 }
332 }
333 for level in 1..=9 {
334 emit_raw(writer, self.raw_children.at(level))?;
335 if let Some(properties) = self.level(level) {
336 let tag = level_tag(level)
337 .ok_or_else(|| TextError::UnexpectedElement(format!("list level {level}")))?;
338 {
339 let mut child = Writer::new(Vec::new());
340 properties.write_xml(&mut child, tag)?;
341 writer
342 .get_mut()
343 .write_all(&text_close_generated_child(
344 &child.into_inner(),
345 &super::paragraph::text_namespace_scope(
346 &text_source_start(tag, &self.source_root),
347 &[],
348 )?,
349 )?)
350 .map_err(OxmlError::from)?;
351 }
352 }
353 }
354 emit_raw(writer, self.raw_children.at(10))?;
355 write_end(writer, tag)
356 }
357
358 pub fn to_xml(&self) -> Result<Vec<u8>> {
360 let mut writer = Writer::new(Vec::new());
361 self.write_xml(&mut writer)?;
362 Ok(writer.into_inner())
363 }
364
365 pub fn level(&self, level: usize) -> Option<&CT_TextParagraphProperties> {
367 match level {
368 1 => self.level1.as_ref(),
369 2 => self.level2.as_ref(),
370 3 => self.level3.as_ref(),
371 4 => self.level4.as_ref(),
372 5 => self.level5.as_ref(),
373 6 => self.level6.as_ref(),
374 7 => self.level7.as_ref(),
375 8 => self.level8.as_ref(),
376 9 => self.level9.as_ref(),
377 _ => None,
378 }
379 }
380
381 pub fn raw_children(&self) -> &OrderedRawChildren {
382 &self.raw_children
383 }
384
385 fn set_level(&mut self, level: usize, properties: CT_TextParagraphProperties) {
386 let slot = match level {
387 1 => &mut self.level1,
388 2 => &mut self.level2,
389 3 => &mut self.level3,
390 4 => &mut self.level4,
391 5 => &mut self.level5,
392 6 => &mut self.level6,
393 7 => &mut self.level7,
394 8 => &mut self.level8,
395 9 => &mut self.level9,
396 _ => return,
397 };
398 *slot = Some(properties);
399 }
400}
401
402fn validate_list_style_root(start: &BytesStart<'_>) -> Result<()> {
403 let namespaces = super::paragraph::text_namespace_scope(start, &[])?;
404 let name = start.name();
405 let raw = std::str::from_utf8(name.as_ref()).map_err(OxmlError::from)?;
406 let (prefix, local) = raw.split_once(':').unwrap_or(("", raw));
407 if local != "lstStyle"
408 && namespaces.iter().any(|(key, uri)| {
409 key == prefix && uri == "http://schemas.openxmlformats.org/presentationml/2006/main"
410 })
411 {
412 return Ok(());
413 }
414 text_validate_root(start)
415}
416
417fn is_list_style_root(name: &[u8]) -> bool {
418 matches_local_name(name, b"lstStyle")
419 || matches_local_name(name, b"defaultTextStyle")
420 || matches_local_name(name, b"titleStyle")
421 || matches_local_name(name, b"bodyStyle")
422 || matches_local_name(name, b"otherStyle")
423 || matches_local_name(name, b"notesStyle")
424}
425
426fn list_level(name: &[u8]) -> Result<Option<usize>> {
427 let Some(number) = name
428 .strip_prefix(b"lvl")
429 .and_then(|name| name.strip_suffix(b"pPr"))
430 else {
431 return Ok(None);
432 };
433 if !number.is_empty() && number.iter().all(u8::is_ascii_digit) {
434 if number.len() == 1 && (b'1'..=b'9').contains(&number[0]) {
435 return Ok(Some(usize::from(number[0] - b'0')));
436 }
437 return Err(TextError::UnexpectedElement(
438 String::from_utf8_lossy(name).into_owned(),
439 ));
440 }
441 Ok(None)
442}
443
444fn level_tag(level: usize) -> Option<&'static str> {
445 match level {
446 1 => Some("a:lvl1pPr"),
447 2 => Some("a:lvl2pPr"),
448 3 => Some("a:lvl3pPr"),
449 4 => Some("a:lvl4pPr"),
450 5 => Some("a:lvl5pPr"),
451 6 => Some("a:lvl6pPr"),
452 7 => Some("a:lvl7pPr"),
453 8 => Some("a:lvl8pPr"),
454 9 => Some("a:lvl9pPr"),
455 _ => None,
456 }
457}
458
459fn capture_raw_attributes(start: &BytesStart<'_>) -> Result<Vec<(String, String)>> {
460 let mut raw = Vec::new();
461 for attribute in start.attributes() {
462 let attribute = attribute.map_err(OxmlError::from)?;
463 let name = std::str::from_utf8(attribute.key.as_ref())
464 .map_err(OxmlError::from)?
465 .to_owned();
466 let value = attribute
467 .decoded_and_normalized_value(XmlVersion::Implicit1_0, start.decoder())
468 .map_err(OxmlError::from)?
469 .into_owned();
470 raw.push((name, value));
471 }
472 Ok(raw)
473}
474
475fn push_raw_attributes(start: &mut BytesStart<'_>, attributes: &[(String, String)]) {
476 for (name, value) in attributes {
477 start.push_attribute((name.as_str(), value.as_str()));
478 }
479}
480
481fn emit_raw<'a, W: Write>(
482 writer: &mut Writer<W>,
483 children: impl Iterator<Item = &'a [u8]>,
484) -> Result<()> {
485 for child in children {
486 writer.get_mut().write_all(child).map_err(OxmlError::from)?;
487 }
488 Ok(())
489}
490
491fn write_start<W: Write>(writer: &mut Writer<W>, start: BytesStart<'_>) -> Result<()> {
492 writer
493 .write_event(Event::Start(start))
494 .map_err(OxmlError::from)?;
495 Ok(())
496}
497
498fn write_empty<W: Write>(writer: &mut Writer<W>, start: BytesStart<'_>) -> Result<()> {
499 writer
500 .write_event(Event::Empty(start))
501 .map_err(OxmlError::from)?;
502 Ok(())
503}
504
505fn write_end<W: Write>(writer: &mut Writer<W>, tag: &str) -> Result<()> {
506 writer
507 .write_event(Event::End(BytesEnd::new(tag)))
508 .map_err(OxmlError::from)?;
509 Ok(())
510}
511
512fn unexpected(element: &BytesStart<'_>) -> TextError {
513 TextError::UnexpectedElement(String::from_utf8_lossy(element.name().as_ref()).into_owned())
514}