diff --git a/docx-core/src/documents/elements/style.rs b/docx-core/src/documents/elements/style.rs index 2ab0795..3211c2c 100644 --- a/docx-core/src/documents/elements/style.rs +++ b/docx-core/src/documents/elements/style.rs @@ -1,6 +1,7 @@ use serde::Serialize; use crate::documents::BuildXML; +use crate::types::*; use crate::xml_builder::*; use crate::StyleType; @@ -32,31 +33,35 @@ impl Default for Style { } impl Style { - pub fn new( - style_id: impl Into, - name: impl Into, - style_type: StyleType, - ) -> Self { - let name = Name::new(name.into()); + pub fn new(style_id: impl Into, style_type: StyleType) -> Self { let default = Default::default(); Style { style_id: style_id.into(), style_type, - name, ..default } } + pub fn name(mut self, name: impl Into) -> Self { + self.name = Name::new(name); + self + } + pub fn size(mut self, size: usize) -> Self { self.run_property = self.run_property.size(size); self } - pub fn color(mut self, color: &str) -> Self { + pub fn color(mut self, color: impl Into) -> Self { self.run_property = self.run_property.color(color); self } + pub fn highlight(mut self, color: impl Into) -> Self { + self.run_property = self.run_property.highlight(color); + self + } + pub fn bold(mut self) -> Self { self.run_property = self.run_property.bold(); self @@ -66,6 +71,31 @@ impl Style { self.run_property = self.run_property.italic(); self } + + pub fn underline(mut self, line_type: impl Into) -> Self { + self.run_property = self.run_property.underline(line_type); + self + } + + pub fn vanish(mut self) -> Self { + self.run_property = self.run_property.vanish(); + self + } + + pub fn align(mut self, alignment_type: AlignmentType) -> Self { + self.paragraph_property = self.paragraph_property.align(alignment_type); + self + } + + pub fn indent( + mut self, + left: usize, + special_indent: Option, + end: Option, + ) -> Self { + self.paragraph_property = self.paragraph_property.indent(left, special_indent, end); + self + } } impl BuildXML for Style { @@ -94,7 +124,7 @@ mod tests { #[test] fn test_build() { - let c = Style::new("Heading", "Heading1", StyleType::Paragraph); + let c = Style::new("Heading", StyleType::Paragraph).name("Heading1"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), diff --git a/docx-core/src/documents/styles.rs b/docx-core/src/documents/styles.rs index 84e7fcb..5f34fa1 100644 --- a/docx-core/src/documents/styles.rs +++ b/docx-core/src/documents/styles.rs @@ -35,7 +35,7 @@ impl Default for Styles { impl BuildXML for Styles { fn build(&self) -> Vec { let b = XMLBuilder::new(); - let normal = Style::new("Normal", "Normal", StyleType::Paragraph); + let normal = Style::new("Normal", StyleType::Paragraph).name("Normal"); b.open_styles() .add_child(&self.doc_defaults) .add_child(&normal) @@ -56,7 +56,8 @@ mod tests { #[test] fn test_style() { - let c = Styles::new().add_style(Style::new("Title", "TitleName", StyleType::Paragraph)); + let c = + Styles::new().add_style(Style::new("Title", StyleType::Paragraph).name("TitleName")); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), diff --git a/docx-core/src/reader/attributes/indent.rs b/docx-core/src/reader/attributes/indent.rs new file mode 100644 index 0000000..06f5438 --- /dev/null +++ b/docx-core/src/reader/attributes/indent.rs @@ -0,0 +1,30 @@ +use std::str::FromStr; + +use xml::attribute::OwnedAttribute; + +use crate::types::*; + +use super::super::errors::*; + +pub fn read_indent( + attrs: &[OwnedAttribute], +) -> Result<(usize, Option, Option), ReaderError> { + let mut start = 0; + let mut end: Option = None; + let mut special: Option = None; + + for a in attrs { + let local_name = &a.name.local_name; + if local_name == "left" || local_name == "start" { + start = usize::from_str(&a.value)?; + } else if local_name == "end" || local_name == "right" { + end = Some(usize::from_str(&a.value)?); + } else if local_name == "hanging" { + special = Some(SpecialIndentType::Hanging(usize::from_str(&a.value)?)) + } else if local_name == "firstLine" { + special = Some(SpecialIndentType::FirstLine(usize::from_str(&a.value)?)) + } + } + + Ok((start, end, special)) +} diff --git a/docx-core/src/reader/attributes/mod.rs b/docx-core/src/reader/attributes/mod.rs index c2aeb2c..9f4e854 100644 --- a/docx-core/src/reader/attributes/mod.rs +++ b/docx-core/src/reader/attributes/mod.rs @@ -1,3 +1,5 @@ +mod indent; mod width; +pub use indent::*; pub use width::*; diff --git a/docx-core/src/reader/mod.rs b/docx-core/src/reader/mod.rs index 7d9e0f8..6242fa3 100644 --- a/docx-core/src/reader/mod.rs +++ b/docx-core/src/reader/mod.rs @@ -8,6 +8,8 @@ mod numbering_property; mod paragraph; mod rels; mod run; +mod style; +mod styles; mod table; mod table_cell; mod table_row; diff --git a/docx-core/src/reader/paragraph.rs b/docx-core/src/reader/paragraph.rs index c660d89..c8072df 100644 --- a/docx-core/src/reader/paragraph.rs +++ b/docx-core/src/reader/paragraph.rs @@ -6,6 +6,7 @@ use xml::reader::{EventReader, XmlEvent}; use super::*; +use super::attributes::*; use crate::types::*; impl ElementReader for Paragraph { @@ -79,25 +80,7 @@ impl ElementReader for Paragraph { continue; } XMLElement::Indent => { - let mut start = 0; - let mut end: Option = None; - let mut special: Option = None; - - for a in attributes { - let local_name = &a.name.local_name; - if local_name == "left" || local_name == "start" { - start = usize::from_str(&a.value)?; - } else if local_name == "end" || local_name == "right" { - end = Some(usize::from_str(&a.value)?); - } else if local_name == "hanging" { - special = - Some(SpecialIndentType::Hanging(usize::from_str(&a.value)?)) - } else if local_name == "firstLine" { - special = Some(SpecialIndentType::FirstLine(usize::from_str( - &a.value, - )?)) - } - } + let (start, end, special) = read_indent(&attributes)?; p = p.indent(start, special, end); continue; } diff --git a/docx-core/src/reader/style.rs b/docx-core/src/reader/style.rs new file mode 100644 index 0000000..bf6f95f --- /dev/null +++ b/docx-core/src/reader/style.rs @@ -0,0 +1,77 @@ +use std::io::Read; +use std::str::FromStr; + +use xml::attribute::OwnedAttribute; +use xml::reader::{EventReader, XmlEvent}; + +use super::*; + +use crate::types::*; + +impl ElementReader for Style { + fn read( + r: &mut EventReader, + attrs: &[OwnedAttribute], + ) -> Result { + let mut id = "".to_owned(); + let mut style_type = StyleType::Paragraph; + for a in attrs { + let local_name = &a.name.local_name; + if local_name == "styleId" { + id = a.value.clone(); + } else if local_name == "type" { + style_type = StyleType::from_str(&a.value)?; + } + } + let mut style = Style::new(id, style_type); + loop { + let e = r.next(); + match e { + Ok(XmlEvent::StartElement { + attributes, name, .. + }) => { + let e = XMLElement::from_str(&name.local_name).unwrap(); + match e { + XMLElement::Name => { + style = style.name(&attributes[0].value.clone()); + continue; + } + // pPr + XMLElement::Indent => { + let (start, end, special) = read_indent(&attributes)?; + style = style.indent(start, special, end); + continue; + } + XMLElement::Justification => { + style = style.align(AlignmentType::from_str(&attributes[0].value)?); + continue; + } + // rPr + XMLElement::Bold => style = style.bold(), + XMLElement::Highlight => { + style = style.highlight(attributes[0].value.clone()) + } + XMLElement::Color => style = style.color(attributes[0].value.clone()), + XMLElement::Size => { + style = style.size(usize::from_str(&attributes[0].value)?) + } + XMLElement::Underline => { + style = style.underline(&attributes[0].value.clone()) + } + XMLElement::Italic => style = style.italic(), + XMLElement::Vanish => style = style.vanish(), + _ => {} + } + } + Ok(XmlEvent::EndElement { name, .. }) => { + let e = XMLElement::from_str(&name.local_name).unwrap(); + if let XMLElement::Style = e { + return Ok(style); + } + } + Err(_) => return Err(ReaderError::XMLReadError), + _ => {} + } + } + } +} diff --git a/docx-core/src/reader/styles.rs b/docx-core/src/reader/styles.rs new file mode 100644 index 0000000..2231833 --- /dev/null +++ b/docx-core/src/reader/styles.rs @@ -0,0 +1,64 @@ +use std::io::Read; +use xml::reader::{EventReader, XmlEvent}; + +use super::*; +use crate::reader::{FromXML, ReaderError}; + +use std::str::FromStr; + +impl FromXML for Styles { + fn from_xml(reader: R) -> Result { + let mut parser = EventReader::new(reader); + let mut styles = Self::default(); + loop { + let e = parser.next(); + match e { + Ok(XmlEvent::StartElement { + attributes, name, .. + }) => { + let e = XMLElement::from_str(&name.local_name).unwrap(); + if let XMLElement::Style = e { + let s = Style::read(&mut parser, &attributes)?; + styles = styles.add_style(s); + continue; + } + } + Ok(XmlEvent::EndElement { name, .. }) => { + let e = XMLElement::from_str(&name.local_name).unwrap(); + if let XMLElement::Styles = e { + break; + } + } + Err(_) => return Err(ReaderError::XMLReadError), + _ => {} + } + } + Ok(styles) + } +} + +// #[cfg(test)] +// mod tests { +// +// use super::*; +// #[cfg(test)] +// use pretty_assertions::assert_eq; +// +// #[test] +// fn test_from_xml() { +// let xml = r#" +// +// +// "#; +// let c = Rels::from_xml(xml.as_bytes()).unwrap(); +// let mut rels = Vec::new(); +// rels.push(( +// "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" +// .to_owned(), +// "rId1".to_owned(), +// "docProps/core.xml".to_owned(), +// )); +// assert_eq!(Rels { rels }, c); +// } +// } +// diff --git a/docx-core/src/reader/xml_element.rs b/docx-core/src/reader/xml_element.rs index cfddb5d..93a68a5 100644 --- a/docx-core/src/reader/xml_element.rs +++ b/docx-core/src/reader/xml_element.rs @@ -27,6 +27,7 @@ pub enum XMLElement { Tab, ParagraphStyle, Indent, + Name, Alignment, NumberingProperty, IndentLevel, @@ -58,6 +59,12 @@ pub enum XMLElement { TableCellMargin, TableGrid, GridCol, + Style, + BasedOn, + Next, + VertAlign, + Spacing, + Styles, Unsupported, } @@ -82,6 +89,7 @@ impl FromStr for XMLElement { "iCs" => Ok(XMLElement::ItalicCs), "vanish" => Ok(XMLElement::Vanish), "italic" => Ok(XMLElement::Italic), + "name" => Ok(XMLElement::Name), "tab" => Ok(XMLElement::Tab), "br" => Ok(XMLElement::Break), "ind" => Ok(XMLElement::Indent), @@ -115,6 +123,12 @@ impl FromStr for XMLElement { "tblCellMar" => Ok(XMLElement::TableCellMargin), "tblGrid" => Ok(XMLElement::TableGrid), "gridCol" => Ok(XMLElement::GridCol), + "style" => Ok(XMLElement::Style), + "basedOn" => Ok(XMLElement::BasedOn), + "next" => Ok(XMLElement::Next), + "vertAlign" => Ok(XMLElement::VertAlign), + "spacing" => Ok(XMLElement::Spacing), + "styles" => Ok(XMLElement::Styles), _ => Ok(XMLElement::Unsupported), } } diff --git a/docx-core/src/types/style_type.rs b/docx-core/src/types/style_type.rs index d19d145..15c8c6b 100644 --- a/docx-core/src/types/style_type.rs +++ b/docx-core/src/types/style_type.rs @@ -1,7 +1,9 @@ use std::fmt; use wasm_bindgen::prelude::*; +use super::errors; use serde::Serialize; +use std::str::FromStr; #[wasm_bindgen] #[derive(Debug, Clone, PartialEq, Serialize, Copy)] @@ -19,3 +21,14 @@ impl fmt::Display for StyleType { } } } + +impl FromStr for StyleType { + type Err = errors::TypeError; + fn from_str(s: &str) -> Result { + match s { + "paragraph" => Ok(StyleType::Paragraph), + "character" => Ok(StyleType::Character), + _ => Err(errors::TypeError::FromStrError), + } + } +} diff --git a/fixtures/table_merged_libre_office/word/styles.xml b/fixtures/table_merged_libre_office/word/styles.xml index f4a3277..546e921 100644 --- a/fixtures/table_merged_libre_office/word/styles.xml +++ b/fixtures/table_merged_libre_office/word/styles.xml @@ -1,2 +1,116 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file