Implement streaming writers (#765)

* Make XMLBuilder generic

* Reduce allocations at XmlData display impl

* Implement streaming writers

- Extend BuildXML trait, add the streaming method
- Remove impls for Box<Ty>, as they can be implemented on the trait level
- Rewrite build() methods in chaining style, backed by apply_* helpers
- Remove quite a few allocations, though numbers still produce them
- Add spaces between children nodes, fix tests

* Add rustfmt.toml and format code

* Fix clippy warnings

* Expose the BuildXML trait without displaying its methods in hints
main
Igor Strebz 2024-11-05 05:22:32 +03:00 committed by GitHub
parent 72637a4dc0
commit 238d2bc3fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
186 changed files with 3595 additions and 4500 deletions

View File

@ -1,3 +1,38 @@
use crate::xml_builder::XMLBuilder;
use std::io::Write;
pub trait BuildXML { pub trait BuildXML {
fn build(&self) -> Vec<u8>; /// Write XML to the output stream.
#[doc(hidden)]
fn build_to<W: Write>(
&self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>>;
#[doc(hidden)]
fn build(&self) -> Vec<u8> {
self.build_to(XMLBuilder::new(Vec::new()).into_inner().unwrap())
.expect("should write to buf")
.into_inner()
}
}
impl<'a, T: BuildXML> BuildXML for &'a T {
/// Building XML from `&T` is the same as from `T`.
fn build_to<W: Write>(
&self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
(*self).build_to(stream)
}
}
impl<T: BuildXML> BuildXML for Box<T> {
/// Building XML from `Box<T>` is the same as from `T`.
fn build_to<W: Write>(
&self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
(**self).build_to(stream)
}
} }

View File

@ -1,6 +1,7 @@
use super::Comment; use super::Comment;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use serde::Serialize; use serde::Serialize;
@ -29,12 +30,16 @@ impl Comments {
} }
impl BuildXML for Comments { impl BuildXML for Comments {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut b = XMLBuilder::new().declaration(Some(true)).open_comments(); &self,
for c in &self.comments { stream: xml::writer::EventWriter<W>,
b = b.add_child(c) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
} XMLBuilder::from(stream)
b.close().build() .declaration(Some(true))?
.open_comments()?
.add_children(&self.comments)?
.close()?
.into_inner()
} }
} }
@ -51,8 +56,7 @@ mod tests {
let b = Comments::new().build(); let b = Comments::new().build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?> r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:comments xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" mc:Ignorable="w14 wp14" />"#
<w:comments xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" mc:Ignorable="w14 wp14" />"#
); );
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use super::*; use super::*;
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -22,14 +23,15 @@ impl CommentsExtended {
} }
impl BuildXML for CommentsExtended { impl BuildXML for CommentsExtended {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut b = XMLBuilder::new(); &self,
b = b.open_comments_extended(); stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
for c in &self.children { XMLBuilder::from(stream)
b = b.add_child(c) .open_comments_extended()?
} .add_children(&self.children)?
b.close().build() .close()?
.into_inner()
} }
} }

View File

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::io::Read; use std::io::{Read, Write};
use xml::reader::{EventReader, XmlEvent}; use xml::reader::{EventReader, XmlEvent};
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -153,28 +153,26 @@ impl Default for ContentTypes {
} }
impl BuildXML for ContentTypes { impl BuildXML for ContentTypes {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
let mut b = b stream: xml::writer::EventWriter<W>,
.declaration(None) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.open_types("http://schemas.openxmlformats.org/package/2006/content-types"); XMLBuilder::from(stream)
.declaration(None)?
b = b .open_types("http://schemas.openxmlformats.org/package/2006/content-types")?
.add_default("png", "image/png") .add_default("png", "image/png")?
.add_default("jpeg", "image/jpeg") .add_default("jpeg", "image/jpeg")?
.add_default("jpg", "image/jpg") .add_default("jpg", "image/jpg")?
.add_default("bmp", "image/bmp") .add_default("bmp", "image/bmp")?
.add_default("gif", "image/gif") .add_default("gif", "image/gif")?
.add_default( .add_default(
"rels", "rels",
"application/vnd.openxmlformats-package.relationships+xml", "application/vnd.openxmlformats-package.relationships+xml",
) )?
.add_default("xml", "application/xml"); .add_default("xml", "application/xml")?
.apply_each(self.types.iter(), |(k, v), b| b.add_override(k, v))?
for (k, v) in self.types.iter() { .close()?
b = b.add_override(k, v); .into_inner()
}
b.close().build()
} }
} }
@ -213,8 +211,7 @@ mod tests {
#[test] #[test]
fn test_from_xml() { fn test_from_xml() {
let xml = r#"<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"> let xml = r#"<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" PartName="/word/document.xml"></Override></Types>"#;
<Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" PartName="/word/document.xml"></Override></Types>"#;
let c = ContentTypes::from_xml(xml.as_bytes()).unwrap(); let c = ContentTypes::from_xml(xml.as_bytes()).unwrap();
let mut types = BTreeMap::new(); let mut types = BTreeMap::new();
types.insert( types.insert(

View File

@ -1,7 +1,9 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::XMLBuilder;
use crate::{ParseXmlError, XmlDocument}; use crate::{ParseXmlError, XmlDocument};
use serde::ser::SerializeSeq; use serde::ser::SerializeSeq;
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use std::str::FromStr; use std::str::FromStr;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -29,8 +31,13 @@ impl Serialize for CustomItem {
} }
impl BuildXML for CustomItem { impl BuildXML for CustomItem {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
self.0.to_string().as_bytes().to_vec() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
let mut b = XMLBuilder::from(stream);
write!(b.inner_mut()?, "{}", self.0)?;
b.into_inner()
} }
} }
@ -44,17 +51,13 @@ mod tests {
#[test] #[test]
fn test_custom_xml() { fn test_custom_xml() {
let c = CustomItem::from_str( let c = CustomItem::from_str(
r#"<ds:datastoreItem ds:itemID="{06AC5857-5C65-A94A-BCEC-37356A209BC3}" xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/customXml"> r#"<ds:datastoreItem ds:itemID="{06AC5857-5C65-A94A-BCEC-37356A209BC3}" xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/customXml"><ds:schemaRefs><ds:schemaRef ds:uri="https://hoge.com"></ds:schemaRef></ds:schemaRefs></ds:datastoreItem>"#,
<ds:schemaRefs>
<ds:schemaRef ds:uri="https://hoge.com"></ds:schemaRef></ds:schemaRefs></ds:datastoreItem>"#,
) )
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
c.0.to_string(), c.0.to_string(),
r#"<ds:datastoreItem ds:itemID="{06AC5857-5C65-A94A-BCEC-37356A209BC3}" xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/customXml"> r#"<ds:datastoreItem ds:itemID="{06AC5857-5C65-A94A-BCEC-37356A209BC3}" xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/customXml"><ds:schemaRefs><ds:schemaRef ds:uri="https://hoge.com"></ds:schemaRef></ds:schemaRefs></ds:datastoreItem>"#
<ds:schemaRefs>
<ds:schemaRef ds:uri="https://hoge.com"></ds:schemaRef></ds:schemaRefs></ds:datastoreItem>"#
); );
assert_eq!( assert_eq!(
serde_json::to_string(&c).unwrap(), serde_json::to_string(&c).unwrap(),

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -15,16 +16,19 @@ impl CustomItemProperty {
} }
impl BuildXML for CustomItemProperty { impl BuildXML for CustomItemProperty {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut b = XMLBuilder::new(); &self,
b = b.declaration(Some(false)); stream: xml::writer::EventWriter<W>,
b = b ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.declaration(Some(false))?
.open_data_store_item( .open_data_store_item(
"http://schemas.openxmlformats.org/officeDocument/2006/customXml", "http://schemas.openxmlformats.org/officeDocument/2006/customXml",
&format!("{{{}}}", self.id), &format!("{{{}}}", self.id),
) )?
.open_data_store_schema_refs() .open_data_store_schema_refs()?
.close(); .close()?
b.close().build() .close()?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -21,21 +22,21 @@ impl CustomItemRels {
} }
impl BuildXML for CustomItemRels { impl BuildXML for CustomItemRels {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut b = XMLBuilder::new(); &self,
b = b stream: xml::writer::EventWriter<W>,
.declaration(Some(true)) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.open_relationships("http://schemas.openxmlformats.org/package/2006/relationships"); XMLBuilder::from(stream)
.declaration(Some(true))?
for id in 0..self.custom_item_count { .open_relationships("http://schemas.openxmlformats.org/package/2006/relationships")?
let id = id + 1; .apply_each(0..self.custom_item_count, |id, b| {
b = b.relationship( b.relationship(
&format!("rId{}", id), &format!("rId{}", id + 1),
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXmlProps", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXmlProps",
&format!("itemProps{}.xml", id), &format!("itemProps{}.xml", id + 1),
) )
} })?
.close()?
b.close().build() .into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -14,13 +15,18 @@ impl AppProps {
} }
impl BuildXML for AppProps { impl BuildXML for AppProps {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
let base = b.declaration(Some(true)).open_properties( stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.declaration(Some(true))?
.open_properties(
"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties", "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",
"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes",
); )?
base.close().build() .close()?
.into_inner()
} }
} }
@ -38,8 +44,7 @@ mod tests {
let b = c.build(); let b = c.build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?> r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes" />"#
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes" />"#
); );
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -56,58 +57,42 @@ impl CorePropsConfig {
} }
impl BuildXML for CoreProps { impl BuildXML for CoreProps {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
let base = b.declaration(Some(true)).open_core_properties( stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.declaration(Some(true))?
.open_core_properties(
"http://schemas.openxmlformats.org/package/2006/metadata/core-properties", "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
"http://purl.org/dc/elements/1.1/", "http://purl.org/dc/elements/1.1/",
"http://purl.org/dc/terms/", "http://purl.org/dc/terms/",
"http://purl.org/dc/dcmitype/", "http://purl.org/dc/dcmitype/",
"http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/2001/XMLSchema-instance",
); )?
let convert = |v: usize| format!("{}", v);
let mut base = base
.dcterms_created( .dcterms_created(
"dcterms:W3CDTF", "dcterms:W3CDTF",
self.config self.config
.created .created
.as_ref() .as_deref()
.map_or_else(|| "1970-01-01T00:00:00Z", |v| v), .unwrap_or("1970-01-01T00:00:00Z"),
) )?
.dc_creator( .dc_creator(self.config.creator.as_deref().unwrap_or("unknown"))?
self.config .cp_last_modified_by(self.config.last_modified_by.as_deref().unwrap_or("unknown"))?
.creator
.as_ref()
.map_or_else(|| "unknown", |v| v),
)
.cp_last_modified_by(
self.config
.last_modified_by
.as_ref()
.map_or_else(|| "unknown", |v| v),
)
.dcterms_modified( .dcterms_modified(
"dcterms:W3CDTF", "dcterms:W3CDTF",
self.config self.config
.modified .modified
.as_ref() .as_deref()
.map_or_else(|| "1970-01-01T00:00:00Z", |v| v), .unwrap_or("1970-01-01T00:00:00Z"),
) )?
.cp_revision(&self.config.revision.map_or_else(|| "1".to_owned(), convert)); .cp_revision(&self.config.revision.unwrap_or(1).to_string())?
if let Some(v) = self.config.description.as_ref() { .apply_opt(self.config.description.as_ref(), |v, b| b.dc_description(v))?
base = base.dc_description(v); .apply_opt(self.config.language.as_ref(), |v, b| b.dc_language(v))?
} .apply_opt(self.config.subject.as_ref(), |v, b| b.dc_subject(v))?
if let Some(v) = self.config.language.as_ref() { .apply_opt(self.config.title.as_ref(), |v, b| b.dc_title(v))?
base = base.dc_language(v); .close()?
} .into_inner()
if let Some(v) = self.config.subject.as_ref() {
base = base.dc_subject(v);
}
if let Some(v) = self.config.title.as_ref() {
base = base.dc_title(v);
}
base.close().build()
} }
} }
@ -135,14 +120,7 @@ mod tests {
let b = c.build(); let b = c.build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?> r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dcterms:created xsi:type="dcterms:W3CDTF">1970-01-01T00:00:00Z</dcterms:created><dc:creator>unknown</dc:creator><cp:lastModifiedBy>unknown</cp:lastModifiedBy><dcterms:modified xsi:type="dcterms:W3CDTF">1970-01-01T00:00:00Z</dcterms:modified><cp:revision>1</cp:revision></cp:coreProperties>"#
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dcterms:created xsi:type="dcterms:W3CDTF">1970-01-01T00:00:00Z</dcterms:created>
<dc:creator>unknown</dc:creator>
<cp:lastModifiedBy>unknown</cp:lastModifiedBy>
<dcterms:modified xsi:type="dcterms:W3CDTF">1970-01-01T00:00:00Z</dcterms:modified>
<cp:revision>1</cp:revision>
</cp:coreProperties>"#
); );
} }
@ -162,18 +140,7 @@ mod tests {
let b = c.build(); let b = c.build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?> r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dcterms:created xsi:type="dcterms:W3CDTF">2019-01-01</dcterms:created><dc:creator>foo</dc:creator><cp:lastModifiedBy>go</cp:lastModifiedBy><dcterms:modified xsi:type="dcterms:W3CDTF">2019-01-01</dcterms:modified><cp:revision>1</cp:revision><dc:description>bar</dc:description><dc:language>en</dc:language><dc:subject>subject</dc:subject><dc:title>title</dc:title></cp:coreProperties>"#
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dcterms:created xsi:type="dcterms:W3CDTF">2019-01-01</dcterms:created>
<dc:creator>foo</dc:creator>
<cp:lastModifiedBy>go</cp:lastModifiedBy>
<dcterms:modified xsi:type="dcterms:W3CDTF">2019-01-01</dcterms:modified>
<cp:revision>1</cp:revision>
<dc:description>bar</dc:description>
<dc:language>en</dc:language>
<dc:subject>subject</dc:subject>
<dc:title>title</dc:title>
</cp:coreProperties>"#
); );
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -21,26 +22,28 @@ impl CustomProps {
} }
impl BuildXML for CustomProps { impl BuildXML for CustomProps {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
let mut base = b.declaration(Some(true)).open_custom_properties( stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.declaration(Some(true))?
.open_custom_properties(
"http://schemas.openxmlformats.org/officeDocument/2006/custom-properties", "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties",
"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes",
); )?
.apply_each(self.properties.iter().enumerate(), |(i, (key, item)), b| {
for (i, (key, item)) in self.properties.iter().enumerate() { b.open_property(
base = base
.open_property(
"{D5CDD505-2E9C-101B-9397-08002B2CF9AE}", "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}",
// I can not found spec about this id. // I can not find spec about this id.
// It is invalid if pid started by 1.... // It is invalid if pid starts from 1...
&format!("{}", i + 2), &format!("{}", i + 2),
key, key,
) )?
.lpwstr(item) .lpwstr(item)?
.close() .close()
} })?
.close()?
base.close().build() .into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use serde::ser::{SerializeStruct, Serializer}; use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use super::*; use super::*;
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -236,31 +237,37 @@ impl Document {
} }
impl BuildXML for DocumentChild { impl BuildXML for DocumentChild {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
&self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
match self { match self {
DocumentChild::Paragraph(v) => v.build(), DocumentChild::Paragraph(v) => v.build_to(stream),
DocumentChild::Table(v) => v.build(), DocumentChild::Table(v) => v.build_to(stream),
DocumentChild::BookmarkStart(v) => v.build(), DocumentChild::BookmarkStart(v) => v.build_to(stream),
DocumentChild::BookmarkEnd(v) => v.build(), DocumentChild::BookmarkEnd(v) => v.build_to(stream),
DocumentChild::CommentStart(v) => v.build(), DocumentChild::CommentStart(v) => v.build_to(stream),
DocumentChild::CommentEnd(v) => v.build(), DocumentChild::CommentEnd(v) => v.build_to(stream),
DocumentChild::StructuredDataTag(v) => v.build(), DocumentChild::StructuredDataTag(v) => v.build_to(stream),
DocumentChild::TableOfContents(v) => v.build(), DocumentChild::TableOfContents(v) => v.build_to(stream),
} }
} }
} }
impl BuildXML for Document { impl BuildXML for Document {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
.declaration(Some(true)) stream: xml::writer::EventWriter<W>,
.open_document() ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.open_body() XMLBuilder::from(stream)
.add_children(&self.children) .declaration(Some(true))?
.add_child(&self.section_property) .open_document()?
.close() .open_body()?
.close() .add_children(&self.children)?
.build() .add_child(&self.section_property)?
.close()?
.close()?
.into_inner()
} }
} }
@ -280,11 +287,7 @@ mod tests {
.build(); .build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?> r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" mc:Ignorable="w14 wp14"><w:body><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p><w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" /></w:sectPr></w:body></w:document>"#
<w:document xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" mc:Ignorable="w14 wp14">
<w:body><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p><w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" />
</w:sectPr></w:body>
</w:document>"#
); );
} }
@ -294,12 +297,7 @@ mod tests {
let b = Document::new().add_table_of_contents(toc).build(); let b = Document::new().add_table_of_contents(toc).build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?> r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" mc:Ignorable="w14 wp14"><w:body><w:sdt><w:sdtPr><w:rPr /></w:sdtPr><w:sdtContent><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:fldChar w:fldCharType="begin" w:dirty="true" /><w:instrText>TOC \o &quot;1-3&quot;</w:instrText><w:fldChar w:fldCharType="separate" w:dirty="false" /></w:r></w:p><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:fldChar w:fldCharType="end" w:dirty="false" /></w:r></w:p></w:sdtContent></w:sdt><w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" /></w:sectPr></w:body></w:document>"#
<w:document xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" mc:Ignorable="w14 wp14">
<w:body><w:sdt><w:sdtPr><w:rPr /></w:sdtPr><w:sdtContent><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:fldChar w:fldCharType="begin" w:dirty="true" /><w:instrText>TOC \o &quot;1-3&quot;</w:instrText><w:fldChar w:fldCharType="separate" w:dirty="false" /></w:r></w:p><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:fldChar w:fldCharType="end" w:dirty="false" /></w:r></w:p></w:sdtContent>
</w:sdt><w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" />
</w:sectPr></w:body>
</w:document>"#
); );
} }
@ -311,11 +309,7 @@ mod tests {
.build(); .build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?> r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" mc:Ignorable="w14 wp14"><w:body><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p><w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="2" /></w:sectPr></w:body></w:document>"#
<w:document xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" mc:Ignorable="w14 wp14">
<w:body><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p><w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="2" />
</w:sectPr></w:body>
</w:document>"#
); );
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use super::*; use super::*;
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -45,97 +46,91 @@ impl DocumentRels {
} }
impl BuildXML for DocumentRels { impl BuildXML for DocumentRels {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut b = XMLBuilder::new(); &self,
b = b stream: xml::writer::EventWriter<W>,
.declaration(None) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.open_relationships("http://schemas.openxmlformats.org/package/2006/relationships") XMLBuilder::from(stream)
.declaration(None)?
.open_relationships("http://schemas.openxmlformats.org/package/2006/relationships")?
.relationship( .relationship(
"rId1", "rId1",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
"styles.xml", "styles.xml",
) )?
.relationship( .relationship(
"rId2", "rId2",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable",
"fontTable.xml", "fontTable.xml",
) )?
.relationship( .relationship(
"rId3", "rId3",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings",
"settings.xml", "settings.xml",
) )?
.relationship( .relationship(
"rId5", "rId5",
"http://schemas.microsoft.com/office/2011/relationships/commentsExtended", "http://schemas.microsoft.com/office/2011/relationships/commentsExtended",
"commentsExtended.xml", "commentsExtended.xml",
); )?
.apply_if(self.has_comments, |b| {
if self.has_comments { b.relationship(
b = b.relationship(
"rId6", "rId6",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
"comments.xml", "comments.xml",
) )
} })?
.apply_if(self.has_numberings, |b| {
if self.has_numberings { b.relationship(
b = b.relationship(
"rId7", "rId7",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering",
"numbering.xml", "numbering.xml",
) )
} })?
.apply_if(self.has_footnotes, |b| {
if self.has_footnotes { b.relationship(
b = b.relationship(
"rId8", "rId8",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
"footnotes.xml", "footnotes.xml",
) )
} })?
.apply_each(0..self.header_count, |i, b| {
for i in 0..self.header_count { b.relationship(
b = b.relationship(
&create_header_rid(i + 1), &create_header_rid(i + 1),
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
&format!("header{}.xml", i + 1), &format!("header{}.xml", i + 1),
) )
} })?
.apply_each(0..self.footer_count, |i, b| {
for i in 0..self.footer_count { b.relationship(
b = b.relationship(
&create_footer_rid(i + 1), &create_footer_rid(i + 1),
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
&format!("footer{}.xml", i + 1), &format!("footer{}.xml", i + 1),
) )
} })?
.apply_each(0..self.custom_xml_count, |i, b| {
for i in 0..self.custom_xml_count { b.relationship(
b = b.relationship(
&format!("rId{}", i + 8), &format!("rId{}", i + 8),
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml",
&format!("../customXml/item{}.xml", i + 1), &format!("../customXml/item{}.xml", i + 1),
) )
} })?
.apply_each(self.images.iter(), |(id, path), b| {
for (id, path) in self.images.iter() { b.relationship(
b = b.relationship(
id, id,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
path, path,
) )
} })?
.apply_each(self.hyperlinks.iter(), |(id, path, r#type), b| {
for (id, path, r#type) in self.hyperlinks.iter() { b.relationship_with_mode(
b = b.relationship_with_mode(
id, id,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
path, path,
r#type, r#type,
) )
} })?
.close()?
b.close().build() .into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use super::*; use super::*;
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -22,13 +23,15 @@ impl AGraphic {
} }
impl BuildXML for AGraphic { impl BuildXML for AGraphic {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
let mut b = b.open_graphic("http://schemas.openxmlformats.org/drawingml/2006/main"); stream: xml::writer::EventWriter<W>,
for child in &self.children { ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
b = b.add_child(child); XMLBuilder::from(stream)
} .open_graphic("http://schemas.openxmlformats.org/drawingml/2006/main")?
b.close().build() .add_children(&self.children)?
.close()?
.into_inner()
} }
} }

View File

@ -1,6 +1,7 @@
use super::*; use super::*;
use serde::ser::{SerializeStruct, Serializer}; use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use std::str::FromStr; use std::str::FromStr;
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -95,15 +96,17 @@ impl AGraphicData {
} }
impl BuildXML for AGraphicData { impl BuildXML for AGraphicData {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
let mut b = b.open_graphic_data(self.data_type.to_uri()); stream: xml::writer::EventWriter<W>,
for c in &self.children { ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
match c { XMLBuilder::from(stream)
GraphicDataChild::Shape(t) => b = b.add_child(t), .open_graphic_data(self.data_type.to_uri())?
GraphicDataChild::Pic(t) => b = b.add_child(t), .apply_each(&self.children, |ch, b| match ch {
} GraphicDataChild::Shape(t) => b.add_child(t),
} GraphicDataChild::Pic(t) => b.add_child(t),
b.close().build() })?
.close()?
.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use crate::documents::{BuildXML, Level}; use crate::documents::{BuildXML, Level};
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use serde::Serialize; use serde::Serialize;
@ -39,14 +40,15 @@ impl AbstractNumbering {
} }
impl BuildXML for AbstractNumbering { impl BuildXML for AbstractNumbering {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let id = format!("{}", self.id); &self,
let mut b = XMLBuilder::new(); stream: xml::writer::EventWriter<W>,
b = b.open_abstract_num(&id); ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
for l in &self.levels { XMLBuilder::from(stream)
b = b.add_child(l); .open_abstract_num(&self.id.to_string())?
} .add_children(&self.levels)?
b.close().build() .close()?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -13,9 +14,13 @@ impl AdjustRightInd {
} }
impl BuildXML for AdjustRightInd { impl BuildXML for AdjustRightInd {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.adjust_right_ind(self.0).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.adjust_right_ind(self.0)?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::escape::escape; use crate::escape::escape;
@ -27,9 +28,11 @@ impl Serialize for BasedOn {
} }
impl BuildXML for BasedOn { impl BuildXML for BasedOn {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.based_on(&self.val).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).based_on(&self.val)?.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -35,8 +36,10 @@ impl Serialize for Bold {
} }
impl BuildXML for Bold { impl BuildXML for Bold {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.b().build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).b()?.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -34,8 +35,10 @@ impl Serialize for BoldCs {
} }
impl BuildXML for BoldCs { impl BuildXML for BoldCs {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.b_cs().build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).b_cs()?.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -15,9 +16,13 @@ impl BookmarkEnd {
} }
impl BuildXML for BookmarkEnd { impl BuildXML for BookmarkEnd {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.bookmark_end(&format!("{}", self.id)).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.bookmark_end(&self.id.to_string())?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -19,10 +20,13 @@ impl BookmarkStart {
} }
impl BuildXML for BookmarkStart { impl BuildXML for BookmarkStart {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.bookmark_start(&format!("{}", self.id), &self.name) stream: xml::writer::EventWriter<W>,
.build() ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.bookmark_start(&self.id.to_string(), &self.name)?
.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use serde::ser::{Serialize, SerializeStruct, Serializer}; use serde::ser::{Serialize, SerializeStruct, Serializer};
use serde::Deserialize; use serde::Deserialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
@ -17,9 +18,13 @@ impl Break {
} }
impl BuildXML for Break { impl BuildXML for Break {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.br(&self.break_type.to_string()).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.br(&self.break_type.to_string())?
.into_inner()
} }
} }

View File

@ -1,13 +1,16 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::{xml_builder::XMLBuilder, BuildXML}; use crate::{xml_builder::XMLBuilder, BuildXML};
#[derive(Debug, Clone, PartialEq, Eq, Default)] #[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct CantSplit {} pub struct CantSplit {}
impl BuildXML for CantSplit { impl BuildXML for CantSplit {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.cant_split().build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).cant_split()?.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use std::io::Write;
use crate::{xml_builder::XMLBuilder, BuildXML}; use crate::{xml_builder::XMLBuilder, BuildXML};
@ -37,8 +38,12 @@ impl Serialize for Caps {
} }
impl BuildXML for Caps { impl BuildXML for Caps {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.caps(&self.val.to_string()).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.caps(&self.val.to_string())?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
@ -66,25 +67,26 @@ impl CellMargins {
} }
impl BuildXML for CellMargins { impl BuildXML for CellMargins {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut b = XMLBuilder::new().open_cell_margins(); &self,
stream: xml::writer::EventWriter<W>,
if let Some(ref top) = self.top { ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
b = b.margin_top(top.val as i32, top.width_type); XMLBuilder::from(stream)
} .open_cell_margins()?
.apply_opt(self.top.as_ref(), |top, b| {
if let Some(ref left) = self.left { b.margin_top(top.val as i32, top.width_type)
b = b.margin_left(left.val as i32, left.width_type); })?
} .apply_opt(self.left.as_ref(), |left, b| {
b.margin_left(left.val as i32, left.width_type)
if let Some(ref bottom) = self.bottom { })?
b = b.margin_bottom(bottom.val as i32, bottom.width_type); .apply_opt(self.bottom.as_ref(), |bottom, b| {
} b.margin_bottom(bottom.val as i32, bottom.width_type)
})?
if let Some(ref right) = self.right { .apply_opt(self.right.as_ref(), |right, b| {
b = b.margin_right(right.val as i32, right.width_type); b.margin_right(right.val as i32, right.width_type)
} })?
b.close().build() .close()?
.into_inner()
} }
} }
@ -101,9 +103,7 @@ mod tests {
let b = CellMargins::new().margin_top(10, WidthType::Dxa).build(); let b = CellMargins::new().margin_top(10, WidthType::Dxa).build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:tcMar> r#"<w:tcMar><w:top w:w="10" w:type="dxa" /></w:tcMar>"#
<w:top w:w="10" w:type="dxa" />
</w:tcMar>"#
); );
} }
} }

View File

@ -1,5 +1,6 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use serde::*; use serde::*;
@ -16,9 +17,11 @@ impl CharacterSpacing {
} }
impl BuildXML for CharacterSpacing { impl BuildXML for CharacterSpacing {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.spacing(self.value).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).spacing(self.value)?.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -15,8 +16,11 @@ impl Color {
} }
impl BuildXML for Color { impl BuildXML for Color {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new().color(&self.val).build() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).color(&self.val)?.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use serde::ser::{SerializeStruct, Serializer}; use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -93,21 +94,27 @@ impl Comment {
} }
impl BuildXML for CommentChild { impl BuildXML for CommentChild {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
&self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
match self { match self {
CommentChild::Paragraph(v) => v.build(), CommentChild::Paragraph(v) => v.build_to(stream),
CommentChild::Table(v) => v.build(), CommentChild::Table(v) => v.build_to(stream),
} }
} }
} }
impl BuildXML for Comment { impl BuildXML for Comment {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
.open_comment(&format!("{}", self.id), &self.author, &self.date, "") stream: xml::writer::EventWriter<W>,
.add_children(&self.children) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.close() XMLBuilder::from(stream)
.build() .open_comment(&self.id.to_string(), &self.author, &self.date, "")?
.add_children(&self.children)?
.close()?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -32,10 +33,13 @@ impl CommentExtended {
} }
impl BuildXML for CommentExtended { impl BuildXML for CommentExtended {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
.comment_extended(&self.paragraph_id, self.done, &self.parent_paragraph_id) stream: xml::writer::EventWriter<W>,
.build() ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.comment_extended(&self.paragraph_id, self.done, &self.parent_paragraph_id)?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -15,17 +16,20 @@ impl CommentRangeEnd {
} }
impl BuildXML for CommentRangeEnd { impl BuildXML for CommentRangeEnd {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.open_run() stream: xml::writer::EventWriter<W>,
.open_run_property() ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.close() XMLBuilder::from(stream)
.close() .open_run()?
.comment_range_end(&format!("{}", self.id)) .open_run_property()?
.open_run() .close()?
.comment_reference(&format!("{}", self.id)) .close()?
.close() .comment_range_end(&format!("{}", self.id))?
.build() .open_run()?
.comment_reference(&format!("{}", self.id))?
.close()?
.into_inner()
} }
} }
@ -43,13 +47,7 @@ mod tests {
let b = c.build(); let b = c.build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:r> r#"<w:r><w:rPr /></w:r><w:commentRangeEnd w:id="1" /><w:r><w:commentReference w:id="1" /></w:r>"#
<w:rPr />
</w:r>
<w:commentRangeEnd w:id="1" />
<w:r>
<w:commentReference w:id="1" />
</w:r>"#
); );
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use super::Comment; use super::Comment;
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -32,16 +33,13 @@ impl CommentRangeStart {
} }
impl BuildXML for CommentRangeStart { impl BuildXML for CommentRangeStart {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.comment_range_start(&format!("{}", self.id)).build() stream: xml::writer::EventWriter<W>,
} ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
} XMLBuilder::from(stream)
.comment_range_start(&self.id.to_string())?
impl BuildXML for Box<CommentRangeStart> { .into_inner()
fn build(&self) -> Vec<u8> {
let b = XMLBuilder::new();
b.comment_range_start(&format!("{}", self.id)).build()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -32,14 +33,17 @@ impl DataBinding {
} }
impl BuildXML for DataBinding { impl BuildXML for DataBinding {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.data_binding( .data_binding(
self.xpath.as_ref(), self.xpath.as_ref(),
self.prefix_mappings.as_ref(), self.prefix_mappings.as_ref(),
self.store_item_id.as_ref(), self.store_item_id.as_ref(),
) )?
.build() .into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
@ -15,9 +16,13 @@ impl DefaultTabStop {
} }
impl BuildXML for DefaultTabStop { impl BuildXML for DefaultTabStop {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.default_tab_stop(self.val).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.default_tab_stop(self.val)?
.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use serde::ser::{SerializeStruct, Serializer}; use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::xml_builder::*; use crate::xml_builder::*;
use crate::{documents::*, escape}; use crate::{documents::*, escape};
@ -97,18 +98,20 @@ impl Delete {
impl HistoryId for Delete {} impl HistoryId for Delete {}
impl BuildXML for Delete { impl BuildXML for Delete {
#[allow(clippy::needless_borrow)] fn build_to<W: Write>(
fn build(&self) -> Vec<u8> { &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
let id = self.generate(); let id = self.generate();
let mut b = XMLBuilder::new().open_delete(&id, &self.author, &self.date); XMLBuilder::from(stream)
for c in &self.children { .open_delete(&id, &self.author, &self.date)?
match c { .apply_each(&self.children, |ch, b| match ch {
DeleteChild::Run(t) => b = b.add_child(t), DeleteChild::Run(t) => b.add_child(t),
DeleteChild::CommentStart(c) => b = b.add_child(c), DeleteChild::CommentStart(c) => b.add_child(&c),
DeleteChild::CommentEnd(c) => b = b.add_child(c), DeleteChild::CommentEnd(c) => b.add_child(c),
} })?
} .close()?
b.close().build() .into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use serde::ser::{SerializeStruct, Serializer}; use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -14,25 +15,21 @@ pub enum DeleteInstrText {
} }
impl BuildXML for DeleteInstrText { impl BuildXML for DeleteInstrText {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let instr = match self { &self,
DeleteInstrText::TOC(toc) => toc.build(), stream: xml::writer::EventWriter<W>,
DeleteInstrText::TC(tc) => tc.build(), ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
DeleteInstrText::PAGEREF(page_ref) => page_ref.build(), XMLBuilder::from(stream)
.open_delete_instr_text()?
.apply(|b| match self {
DeleteInstrText::TOC(toc) => b.add_child(toc),
DeleteInstrText::TC(tc) => b.add_child(tc),
DeleteInstrText::PAGEREF(page_ref) => b.add_child(page_ref),
DeleteInstrText::HYPERLINK(_link) => todo!(), DeleteInstrText::HYPERLINK(_link) => todo!(),
DeleteInstrText::Unsupported(s) => s.as_bytes().to_vec(), DeleteInstrText::Unsupported(s) => b.plain_text(s),
}; })?
XMLBuilder::new() .close()?
.open_delete_instr_text() .into_inner()
.add_bytes(&instr)
.close()
.build()
}
}
impl BuildXML for Box<DeleteInstrText> {
fn build(&self) -> Vec<u8> {
self.as_ref().build()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::escape::escape; use crate::escape::escape;
@ -28,8 +29,13 @@ impl DeleteText {
} }
impl BuildXML for DeleteText { impl BuildXML for DeleteText {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new().delete_text(&self.text, true).build() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.delete_text(&self.text, true)?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::{documents::BuildXML, RunProperty}; use crate::{documents::BuildXML, RunProperty};
use crate::{xml_builder::*, LineSpacing, ParagraphProperty, ParagraphPropertyDefault}; use crate::{xml_builder::*, LineSpacing, ParagraphProperty, ParagraphPropertyDefault};
@ -61,13 +62,16 @@ impl Default for DocDefaults {
} }
impl BuildXML for DocDefaults { impl BuildXML for DocDefaults {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.open_doc_defaults() stream: xml::writer::EventWriter<W>,
.add_child(&self.run_property_default) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.add_child(&self.paragraph_property_default) XMLBuilder::from(stream)
.close() .open_doc_defaults()?
.build() .add_child(&self.run_property_default)?
.add_child(&self.paragraph_property_default)?
.close()?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
@ -52,9 +53,12 @@ impl Default for DocGrid {
} }
impl BuildXML for DocGrid { impl BuildXML for DocGrid {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.doc_grid(&self.grid_type, self.line_pitch, self.char_space) stream: xml::writer::EventWriter<W>,
.build() ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.doc_grid(&self.grid_type, self.line_pitch, self.char_space)?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -24,9 +25,11 @@ impl Serialize for DocId {
} }
impl BuildXML for DocId { impl BuildXML for DocId {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
let id = format!("{{{}}}", self.id); let id = format!("{{{}}}", self.id);
b.doc_id(&id).build() XMLBuilder::from(stream).doc_id(&id)?.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -19,8 +20,12 @@ impl DocVar {
} }
impl BuildXML for DocVar { impl BuildXML for DocVar {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.doc_var(&self.name, &self.val).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.doc_var(&self.name, &self.val)?
.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use super::*; use super::*;
use serde::{ser::*, Serialize}; use serde::{ser::*, Serialize};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
@ -55,16 +56,13 @@ impl Drawing {
} }
} }
impl BuildXML for Box<Drawing> {
fn build(&self) -> Vec<u8> {
self.as_ref().build()
}
}
impl BuildXML for Drawing { impl BuildXML for Drawing {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
let mut b = b.open_drawing(); stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
let b = XMLBuilder::from(stream);
let mut b = b.open_drawing()?;
match &self.data { match &self.data {
Some(DrawingData::Pic(p)) => { Some(DrawingData::Pic(p)) => {
@ -74,7 +72,7 @@ impl BuildXML for Drawing {
&format!("{}", p.dist_b), &format!("{}", p.dist_b),
&format!("{}", p.dist_l), &format!("{}", p.dist_l),
&format!("{}", p.dist_r), &format!("{}", p.dist_r),
) )?
} else { } else {
b = b b = b
.open_wp_anchor( .open_wp_anchor(
@ -88,32 +86,32 @@ impl BuildXML for Drawing {
"0", "0",
if p.layout_in_cell { "1" } else { "0" }, if p.layout_in_cell { "1" } else { "0" },
&format!("{}", p.relative_height), &format!("{}", p.relative_height),
) )?
.simple_pos( .simple_pos(
&format!("{}", p.simple_pos_x), &format!("{}", p.simple_pos_x),
&format!("{}", p.simple_pos_y), &format!("{}", p.simple_pos_y),
) )?
.open_position_h(&format!("{}", p.relative_from_h)); .open_position_h(&format!("{}", p.relative_from_h))?;
match p.position_h { match p.position_h {
DrawingPosition::Offset(x) => { DrawingPosition::Offset(x) => {
let x = format!("{}", x as u32); let x = format!("{}", x as u32);
b = b.pos_offset(&x).close(); b = b.pos_offset(&x)?.close()?;
} }
DrawingPosition::Align(x) => { DrawingPosition::Align(x) => {
b = b.align(&x.to_string()).close(); b = b.align(&x.to_string())?.close()?;
} }
} }
b = b.open_position_v(&format!("{}", p.relative_from_v)); b = b.open_position_v(&format!("{}", p.relative_from_v))?;
match p.position_v { match p.position_v {
DrawingPosition::Offset(y) => { DrawingPosition::Offset(y) => {
let y = format!("{}", y as u32); let y = format!("{}", y as u32);
b = b.pos_offset(&y).close(); b = b.pos_offset(&y)?.close()?;
} }
DrawingPosition::Align(a) => { DrawingPosition::Align(a) => {
b = b.align(&a.to_string()).close(); b = b.align(&a.to_string())?.close()?;
} }
} }
} }
@ -123,33 +121,35 @@ impl BuildXML for Drawing {
b = b b = b
// Please see 20.4.2.7 extent (Drawing Object Size) // Please see 20.4.2.7 extent (Drawing Object Size)
// One inch equates to 914400 EMUs and a centimeter is 360000 // One inch equates to 914400 EMUs and a centimeter is 360000
.wp_extent(&w, &h) .wp_extent(&w, &h)?
.wp_effect_extent("0", "0", "0", "0"); .wp_effect_extent("0", "0", "0", "0")?;
if p.allow_overlap { if p.allow_overlap {
b = b.wrap_none(); b = b.wrap_none()?;
} else if p.position_type == DrawingPositionType::Anchor { } else if p.position_type == DrawingPositionType::Anchor {
b = b.wrap_square("bothSides"); b = b.wrap_square("bothSides")?;
} }
b = b b = b
.wp_doc_pr("1", "Figure") .wp_doc_pr("1", "Figure")?
.open_wp_c_nv_graphic_frame_pr() .open_wp_c_nv_graphic_frame_pr()?
.a_graphic_frame_locks( .a_graphic_frame_locks(
"http://schemas.openxmlformats.org/drawingml/2006/main", "http://schemas.openxmlformats.org/drawingml/2006/main",
"1", "1",
) )?
.close() .close()?
.open_a_graphic("http://schemas.openxmlformats.org/drawingml/2006/main") .open_a_graphic("http://schemas.openxmlformats.org/drawingml/2006/main")?
.open_a_graphic_data("http://schemas.openxmlformats.org/drawingml/2006/picture") .open_a_graphic_data(
.add_child(&p.clone()) "http://schemas.openxmlformats.org/drawingml/2006/picture",
.close() )?
.close(); .add_child(&p.clone())?
.close()?
.close()?;
} }
Some(DrawingData::TextBox(_t)) => unimplemented!("TODO: Support textBox writer"), Some(DrawingData::TextBox(_t)) => unimplemented!("TODO: Support textBox writer"),
None => { None => {
unimplemented!() unimplemented!()
} }
} }
b.close().close().build() b.close()?.close()?.into_inner()
} }
} }
@ -167,42 +167,7 @@ mod tests {
let d = Drawing::new().pic(pic).build(); let d = Drawing::new().pic(pic).build();
assert_eq!( assert_eq!(
str::from_utf8(&d).unwrap(), str::from_utf8(&d).unwrap(),
r#"<w:drawing> r#"<w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="3048000" cy="2286000" /><wp:effectExtent b="0" l="0" r="0" t="0" /><wp:docPr id="1" name="Figure" /><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1" /></wp:cNvGraphicFramePr><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="0" name="" /><pic:cNvPicPr><a:picLocks noChangeAspect="1" noChangeArrowheads="1" /></pic:cNvPicPr></pic:nvPicPr><pic:blipFill><a:blip r:embed="rIdImage123" /><a:srcRect /><a:stretch><a:fillRect /></a:stretch></pic:blipFill><pic:spPr bwMode="auto"><a:xfrm rot="0"><a:off x="0" y="0" /><a:ext cx="3048000" cy="2286000" /></a:xfrm><a:prstGeom prst="rect"><a:avLst /></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing>"#
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="3048000" cy="2286000" />
<wp:effectExtent b="0" l="0" r="0" t="0" />
<wp:docPr id="1" name="Figure" />
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1" />
</wp:cNvGraphicFramePr>
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="0" name="" />
<pic:cNvPicPr>
<a:picLocks noChangeAspect="1" noChangeArrowheads="1" />
</pic:cNvPicPr>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rIdImage123" />
<a:srcRect />
<a:stretch>
<a:fillRect />
</a:stretch>
</pic:blipFill>
<pic:spPr bwMode="auto">
<a:xfrm rot="0">
<a:off x="0" y="0" />
<a:ext cx="3048000" cy="2286000" />
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst />
</a:prstGeom>
</pic:spPr>
</pic:pic></a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>"#
); );
} }
@ -212,43 +177,7 @@ mod tests {
let d = Drawing::new().pic(pic).build(); let d = Drawing::new().pic(pic).build();
assert_eq!( assert_eq!(
str::from_utf8(&d).unwrap(), str::from_utf8(&d).unwrap(),
r#"<w:drawing> r#"<w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="3048000" cy="2286000" /><wp:effectExtent b="0" l="0" r="0" t="0" /><wp:wrapNone /><wp:docPr id="1" name="Figure" /><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1" /></wp:cNvGraphicFramePr><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="0" name="" /><pic:cNvPicPr><a:picLocks noChangeAspect="1" noChangeArrowheads="1" /></pic:cNvPicPr></pic:nvPicPr><pic:blipFill><a:blip r:embed="rIdImage123" /><a:srcRect /><a:stretch><a:fillRect /></a:stretch></pic:blipFill><pic:spPr bwMode="auto"><a:xfrm rot="0"><a:off x="0" y="0" /><a:ext cx="3048000" cy="2286000" /></a:xfrm><a:prstGeom prst="rect"><a:avLst /></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing>"#
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="3048000" cy="2286000" />
<wp:effectExtent b="0" l="0" r="0" t="0" />
<wp:wrapNone />
<wp:docPr id="1" name="Figure" />
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1" />
</wp:cNvGraphicFramePr>
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="0" name="" />
<pic:cNvPicPr>
<a:picLocks noChangeAspect="1" noChangeArrowheads="1" />
</pic:cNvPicPr>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rIdImage123" />
<a:srcRect />
<a:stretch>
<a:fillRect />
</a:stretch>
</pic:blipFill>
<pic:spPr bwMode="auto">
<a:xfrm rot="0">
<a:off x="0" y="0" />
<a:ext cx="3048000" cy="2286000" />
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst />
</a:prstGeom>
</pic:spPr>
</pic:pic></a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>"#
); );
} }
@ -261,50 +190,7 @@ mod tests {
let d = Drawing::new().pic(pic).build(); let d = Drawing::new().pic(pic).build();
assert_eq!( assert_eq!(
str::from_utf8(&d).unwrap(), str::from_utf8(&d).unwrap(),
r#"<w:drawing> r#"<w:drawing><wp:anchor distT="0" distB="0" distL="0" distR="0" simplePos="0" allowOverlap="0" behindDoc="0" locked="0" layoutInCell="0" relativeHeight="190500"><wp:simplePos x="0" y="0" /><wp:positionH relativeFrom="column"><wp:align>right</wp:align></wp:positionH><wp:positionV relativeFrom="paragraph"><wp:posOffset>0</wp:posOffset></wp:positionV><wp:extent cx="3048000" cy="2286000" /><wp:effectExtent b="0" l="0" r="0" t="0" /><wp:wrapSquare wrapText="bothSides" /><wp:docPr id="1" name="Figure" /><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1" /></wp:cNvGraphicFramePr><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="0" name="" /><pic:cNvPicPr><a:picLocks noChangeAspect="1" noChangeArrowheads="1" /></pic:cNvPicPr></pic:nvPicPr><pic:blipFill><a:blip r:embed="rIdImage123" /><a:srcRect /><a:stretch><a:fillRect /></a:stretch></pic:blipFill><pic:spPr bwMode="auto"><a:xfrm rot="0"><a:off x="0" y="0" /><a:ext cx="3048000" cy="2286000" /></a:xfrm><a:prstGeom prst="rect"><a:avLst /></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:anchor></w:drawing>"#
<wp:anchor distT="0" distB="0" distL="0" distR="0" simplePos="0" allowOverlap="0" behindDoc="0" locked="0" layoutInCell="0" relativeHeight="190500">
<wp:simplePos x="0" y="0" />
<wp:positionH relativeFrom="column">
<wp:align>right</wp:align>
</wp:positionH>
<wp:positionV relativeFrom="paragraph">
<wp:posOffset>0</wp:posOffset>
</wp:positionV>
<wp:extent cx="3048000" cy="2286000" />
<wp:effectExtent b="0" l="0" r="0" t="0" />
<wp:wrapSquare wrapText="bothSides" />
<wp:docPr id="1" name="Figure" />
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1" />
</wp:cNvGraphicFramePr>
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="0" name="" />
<pic:cNvPicPr>
<a:picLocks noChangeAspect="1" noChangeArrowheads="1" />
</pic:cNvPicPr>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rIdImage123" />
<a:srcRect />
<a:stretch>
<a:fillRect />
</a:stretch>
</pic:blipFill>
<pic:spPr bwMode="auto">
<a:xfrm rot="0">
<a:off x="0" y="0" />
<a:ext cx="3048000" cy="2286000" />
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst />
</a:prstGeom>
</pic:spPr>
</pic:pic></a:graphicData>
</a:graphic>
</wp:anchor>
</w:drawing>"#
); );
} }
@ -319,50 +205,7 @@ mod tests {
let d = Drawing::new().pic(pic).build(); let d = Drawing::new().pic(pic).build();
assert_eq!( assert_eq!(
str::from_utf8(&d).unwrap(), str::from_utf8(&d).unwrap(),
r#"<w:drawing> r#"<w:drawing><wp:anchor distT="0" distB="0" distL="0" distR="0" simplePos="0" allowOverlap="0" behindDoc="0" locked="0" layoutInCell="0" relativeHeight="190500"><wp:simplePos x="0" y="0" /><wp:positionH relativeFrom="margin"><wp:posOffset>2857500</wp:posOffset></wp:positionH><wp:positionV relativeFrom="margin"><wp:posOffset>3810000</wp:posOffset></wp:positionV><wp:extent cx="3048000" cy="2286000" /><wp:effectExtent b="0" l="0" r="0" t="0" /><wp:wrapSquare wrapText="bothSides" /><wp:docPr id="1" name="Figure" /><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1" /></wp:cNvGraphicFramePr><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="0" name="" /><pic:cNvPicPr><a:picLocks noChangeAspect="1" noChangeArrowheads="1" /></pic:cNvPicPr></pic:nvPicPr><pic:blipFill><a:blip r:embed="rIdImage123" /><a:srcRect /><a:stretch><a:fillRect /></a:stretch></pic:blipFill><pic:spPr bwMode="auto"><a:xfrm rot="0"><a:off x="0" y="0" /><a:ext cx="3048000" cy="2286000" /></a:xfrm><a:prstGeom prst="rect"><a:avLst /></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:anchor></w:drawing>"#
<wp:anchor distT="0" distB="0" distL="0" distR="0" simplePos="0" allowOverlap="0" behindDoc="0" locked="0" layoutInCell="0" relativeHeight="190500">
<wp:simplePos x="0" y="0" />
<wp:positionH relativeFrom="margin">
<wp:posOffset>2857500</wp:posOffset>
</wp:positionH>
<wp:positionV relativeFrom="margin">
<wp:posOffset>3810000</wp:posOffset>
</wp:positionV>
<wp:extent cx="3048000" cy="2286000" />
<wp:effectExtent b="0" l="0" r="0" t="0" />
<wp:wrapSquare wrapText="bothSides" />
<wp:docPr id="1" name="Figure" />
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1" />
</wp:cNvGraphicFramePr>
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="0" name="" />
<pic:cNvPicPr>
<a:picLocks noChangeAspect="1" noChangeArrowheads="1" />
</pic:cNvPicPr>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rIdImage123" />
<a:srcRect />
<a:stretch>
<a:fillRect />
</a:stretch>
</pic:blipFill>
<pic:spPr bwMode="auto">
<a:xfrm rot="0">
<a:off x="0" y="0" />
<a:ext cx="3048000" cy="2286000" />
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst />
</a:prstGeom>
</pic:spPr>
</pic:pic></a:graphicData>
</a:graphic>
</wp:anchor>
</w:drawing>"#
); );
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::types::*; use crate::types::*;
@ -28,13 +29,16 @@ impl FieldChar {
} }
impl BuildXML for FieldChar { impl BuildXML for FieldChar {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.field_character( .field_character(
&format!("{}", self.field_char_type), &format!("{}", self.field_char_type),
&format!("{}", &self.dirty), &format!("{}", &self.dirty),
) )?
.build() .into_inner()
} }
} }

View File

@ -1,6 +1,7 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
#[derive(Debug)] #[derive(Debug)]
pub struct Font<'a> { pub struct Font<'a> {
@ -22,14 +23,17 @@ impl<'a> Font<'a> {
} }
impl<'a> BuildXML for Font<'a> { impl<'a> BuildXML for Font<'a> {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.open_font(self.name) stream: xml::writer::EventWriter<W>,
.charset(self.charset) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.family(self.family) XMLBuilder::from(stream)
.pitch(&self.pitch.to_string()) .open_font(self.name)?
.close() .charset(self.charset)?
.build() .family(self.family)?
.pitch(&self.pitch.to_string())?
.close()?
.into_inner()
} }
} }
@ -47,11 +51,7 @@ mod tests {
let b = c.build(); let b = c.build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:font w:name="Arial"> r#"<w:font w:name="Arial"><w:charset w:val="00" /><w:family w:val="swiss" /><w:pitch w:val="variable" /></w:font>"#
<w:charset w:val="00" />
<w:family w:val="swiss" />
<w:pitch w:val="variable" />
</w:font>"#
); );
} }
} }

View File

@ -1,5 +1,6 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use serde::Serialize; use serde::Serialize;
@ -20,9 +21,12 @@ impl FooterReference {
} }
impl BuildXML for FooterReference { impl BuildXML for FooterReference {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
.footer_reference(&self.footer_type, &self.id) stream: xml::writer::EventWriter<W>,
.build() ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.footer_reference(&self.footer_type, &self.id)?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -47,7 +48,10 @@ impl From<&FootnoteReference> for Footnote {
} }
impl BuildXML for Footnote { impl BuildXML for Footnote {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
&self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
// To ensure docx compatible XML serialization for footnotes, we default to an empty paragraph. // To ensure docx compatible XML serialization for footnotes, we default to an empty paragraph.
let mut footnote = self.clone(); let mut footnote = self.clone();
if self.content == vec![] { if self.content == vec![] {
@ -55,11 +59,11 @@ impl BuildXML for Footnote {
footnote.add_content(Paragraph::new()); footnote.add_content(Paragraph::new());
} }
XMLBuilder::new() XMLBuilder::from(stream)
.open_footnote(&format!("{}", self.id)) .open_footnote(&format!("{}", self.id))?
.add_children(&footnote.content) .add_children(&footnote.content)?
.close() .close()?
.build() .into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::ser::{Serialize, SerializeStruct, Serializer}; use serde::ser::{Serialize, SerializeStruct, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::{xml_builder::*, Footnote, Paragraph}; use crate::{xml_builder::*, Footnote, Paragraph};
@ -35,8 +36,13 @@ impl From<Footnote> for FootnoteReference {
} }
impl BuildXML for FootnoteReference { impl BuildXML for FootnoteReference {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new().footnote_reference(self.id).build() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.footnote_reference(self.id)?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -120,9 +121,11 @@ impl FrameProperty {
} }
impl BuildXML for FrameProperty { impl BuildXML for FrameProperty {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.frame_property(self).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).frame_property(self)?.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -15,8 +16,11 @@ impl GridSpan {
} }
impl BuildXML for GridSpan { impl BuildXML for GridSpan {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new().grid_span(self.val).build() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).grid_span(self.val)?.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use serde::Serialize; use serde::Serialize;
@ -29,9 +30,12 @@ impl HeaderReference {
} }
impl BuildXML for HeaderReference { impl BuildXML for HeaderReference {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
.header_reference(&self.header_type, &self.id) stream: xml::writer::EventWriter<W>,
.build() ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.header_reference(&self.header_type, &self.id)?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -15,8 +16,11 @@ impl Highlight {
} }
impl BuildXML for Highlight { impl BuildXML for Highlight {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new().highlight(&self.val).build() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).highlight(&self.val)?.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use super::*; use super::*;
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -98,25 +99,26 @@ impl Hyperlink {
} }
impl BuildXML for Hyperlink { impl BuildXML for Hyperlink {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut b = XMLBuilder::new(); &self,
match self.link { stream: xml::writer::EventWriter<W>,
HyperlinkData::Anchor { ref anchor } => { ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
b = b.open_hyperlink( XMLBuilder::from(stream)
.apply(|b| match self.link {
HyperlinkData::Anchor { ref anchor } => b.open_hyperlink(
None, None,
Some(anchor.clone()).as_ref(), Some(anchor.clone()).as_ref(),
Some(self.history.unwrap_or(1)), Some(self.history.unwrap_or(1)),
) ),
} HyperlinkData::External { ref rid, .. } => b.open_hyperlink(
HyperlinkData::External { ref rid, .. } => {
b = b.open_hyperlink(
Some(rid.clone()).as_ref(), Some(rid.clone()).as_ref(),
None, None,
Some(self.history.unwrap_or(1)), Some(self.history.unwrap_or(1)),
) ),
} })?
}; .add_children(&self.children)?
b.add_children(&self.children).close().build() .close()?
.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use serde::ser::{SerializeStruct, Serializer}; use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
@ -51,15 +52,18 @@ impl Indent {
} }
impl BuildXML for Indent { impl BuildXML for Indent {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.indent( .indent(
self.start, self.start,
self.special_indent, self.special_indent,
self.end.unwrap_or_default(), self.end.unwrap_or_default(),
self.start_chars, self.start_chars,
) )?
.build() .into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct IndentLevel { pub struct IndentLevel {
@ -13,9 +14,13 @@ impl IndentLevel {
} }
impl BuildXML for IndentLevel { impl BuildXML for IndentLevel {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.indent_level(self.val).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.indent_level(self.val)?
.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use serde::ser::{SerializeStruct, Serializer}; use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use super::*; use super::*;
@ -15,12 +16,15 @@ pub enum InsertChild {
} }
impl BuildXML for InsertChild { impl BuildXML for InsertChild {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
&self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
match self { match self {
InsertChild::Run(v) => v.build(), InsertChild::Run(v) => v.build_to(stream),
InsertChild::Delete(v) => v.build(), InsertChild::Delete(v) => v.build_to(stream),
InsertChild::CommentStart(v) => v.build(), InsertChild::CommentStart(v) => v.build_to(stream),
InsertChild::CommentEnd(v) => v.build(), InsertChild::CommentEnd(v) => v.build_to(stream),
} }
} }
} }
@ -140,13 +144,15 @@ impl Insert {
impl HistoryId for Insert {} impl HistoryId for Insert {}
impl BuildXML for Insert { impl BuildXML for Insert {
#[allow(clippy::needless_borrow)] fn build_to<W: Write>(
fn build(&self) -> Vec<u8> { &self,
XMLBuilder::new() stream: xml::writer::EventWriter<W>,
.open_insert(&self.generate(), &self.author, &self.date) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.add_children(&self.children) XMLBuilder::from(stream)
.close() .open_insert(&self.generate(), &self.author, &self.date)?
.build() .add_children(&self.children)?
.close()?
.into_inner()
} }
} }

View File

@ -1,6 +1,8 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::xml_builder::XMLBuilder;
#[derive(Serialize, Debug, Clone, PartialEq, Default)] #[derive(Serialize, Debug, Clone, PartialEq, Default)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -13,8 +15,12 @@ impl InstrNUMPAGES {
} }
impl BuildXML for InstrNUMPAGES { impl BuildXML for InstrNUMPAGES {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let instr = "NUMPAGES".to_owned(); &self,
instr.into() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.plain_text("NUMPAGES")?
.into_inner()
} }
} }

View File

@ -1,6 +1,8 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::xml_builder::XMLBuilder;
#[derive(Serialize, Debug, Clone, PartialEq, Default)] #[derive(Serialize, Debug, Clone, PartialEq, Default)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -13,8 +15,10 @@ impl InstrPAGE {
} }
impl BuildXML for InstrPAGE { impl BuildXML for InstrPAGE {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let instr = "PAGE".to_owned(); &self,
instr.into() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).plain_text("PAGE")?.into_inner()
} }
} }

View File

@ -1,6 +1,8 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::xml_builder::XMLBuilder;
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_PAGEREFPAGEREF_topic_ID0EHXK1.html // https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_PAGEREFPAGEREF_topic_ID0EHXK1.html
#[derive(Serialize, Debug, Clone, PartialEq, Default)] #[derive(Serialize, Debug, Clone, PartialEq, Default)]
@ -31,18 +33,16 @@ impl InstrPAGEREF {
} }
impl BuildXML for InstrPAGEREF { impl BuildXML for InstrPAGEREF {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut instr = format!("PAGEREF {}", self.page_ref); &self,
stream: xml::writer::EventWriter<W>,
if self.relative_position { ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
instr = format!("{} \\p", instr); XMLBuilder::from(stream)
} .plain_text("PAGEREF ")?
.plain_text(&self.page_ref)?
if self.hyperlink { .apply_if(self.relative_position, |b| b.plain_text(" \\p"))?
instr = format!("{} \\h", instr); .apply_if(self.hyperlink, |b| b.plain_text(" \\h"))?
} .into_inner()
instr.into()
} }
} }
@ -78,9 +78,6 @@ mod tests {
#[test] #[test]
fn test_page_ref() { fn test_page_ref() {
let b = InstrPAGEREF::new("_Toc00000000").hyperlink().build(); let b = InstrPAGEREF::new("_Toc00000000").hyperlink().build();
assert_eq!( assert_eq!(str::from_utf8(&b).unwrap(), r#"PAGEREF _Toc00000000 \h"#);
str::from_utf8(&b).unwrap(),
r#"PAGEREF _Toc00000000 \h"#
);
} }
} }

View File

@ -1,6 +1,8 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::xml_builder::XMLBuilder;
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_TCTC_topic_ID0EU2N1.html // https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_TCTC_topic_ID0EU2N1.html
#[derive(Serialize, Debug, Clone, PartialEq, Default)] #[derive(Serialize, Debug, Clone, PartialEq, Default)]
@ -39,22 +41,28 @@ impl InstrTC {
} }
impl BuildXML for InstrTC { impl BuildXML for InstrTC {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut instr = format!("TC {}", self.text); &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
let mut b = XMLBuilder::from(stream);
let raw = b.inner_mut()?;
write!(raw, "TC {}", self.text)?;
if let Some(ref t) = self.item_type_identifier { if let Some(ref t) = self.item_type_identifier {
instr = format!("{} \\f {}", instr, t); write!(raw, " \\f {}", t)?;
} }
if let Some(level) = self.level { if let Some(level) = self.level {
instr = format!("{} \\l {}", instr, level); write!(raw, " \\l {}", level)?;
} }
if self.omits_page_number { if self.omits_page_number {
instr = format!("{} \\n", instr); write!(raw, " \\n")?;
} }
instr.into() b.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use serde::ser::{SerializeStruct, Serializer}; use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -16,21 +17,23 @@ pub enum InstrText {
} }
impl BuildXML for Box<InstrText> { impl BuildXML for Box<InstrText> {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let instr = match self.as_ref() { &self,
InstrText::TOC(toc) => toc.build(), stream: xml::writer::EventWriter<W>,
InstrText::TC(tc) => tc.build(), ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
InstrText::PAGEREF(page_ref) => page_ref.build(), XMLBuilder::from(stream)
InstrText::PAGE(page) => page.build(), .open_instr_text()?
InstrText::NUMPAGES(page) => page.build(), .apply(|b| match self.as_ref() {
InstrText::TOC(toc) => b.add_child(toc),
InstrText::TC(tc) => b.add_child(tc),
InstrText::PAGEREF(page_ref) => b.add_child(page_ref),
InstrText::PAGE(page) => b.add_child(page),
InstrText::NUMPAGES(page) => b.add_child(page),
InstrText::HYPERLINK(_link) => todo!(), InstrText::HYPERLINK(_link) => todo!(),
InstrText::Unsupported(s) => s.as_bytes().to_vec(), InstrText::Unsupported(s) => b.plain_text(s),
}; })?
XMLBuilder::new() .close()?
.open_instr_text() .into_inner()
.add_bytes(&instr)
.close()
.build()
} }
} }
@ -86,6 +89,7 @@ impl Serialize for InstrText {
} }
} }
#[allow(unused_allocation)]
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -1,6 +1,8 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::xml_builder::XMLBuilder;
#[derive(Serialize, Debug, Clone, PartialEq, Default)] #[derive(Serialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))]
@ -159,57 +161,63 @@ impl InstrToC {
} }
impl BuildXML for InstrToC { impl BuildXML for InstrToC {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut instr = "TOC".to_string(); &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
let mut b = XMLBuilder::from(stream);
let raw = b.inner_mut()?;
write!(raw, "TOC")?;
// \a // \a
if let Some(ref t) = self.caption_label { if let Some(ref t) = self.caption_label {
instr = format!("{} \\a &quot;{}&quot;", instr, t); write!(raw, " \\a &quot;{}&quot;", t)?;
} }
// \b // \b
if let Some(ref t) = self.entry_bookmark_name { if let Some(ref t) = self.entry_bookmark_name {
instr = format!("{} \\b &quot;{}&quot;", instr, t); write!(raw, " \\b &quot;{}&quot;", t)?;
} }
// \c // \c
if let Some(ref t) = self.caption_label_including_numbers { if let Some(ref t) = self.caption_label_including_numbers {
instr = format!("{} \\c &quot;{}&quot;", instr, t); write!(raw, " \\c &quot;{}&quot;", t)?;
} }
// \d // \d
if let Some(ref t) = self.sequence_and_page_numbers_separator { if let Some(ref t) = self.sequence_and_page_numbers_separator {
instr = format!("{} \\d &quot;{}&quot;", instr, t); write!(raw, " \\d &quot;{}&quot;", t)?;
} }
// \f // \f
if let Some(ref t) = self.tc_field_identifier { if let Some(ref t) = self.tc_field_identifier {
instr = format!("{} \\f &quot;{}&quot;", instr, t); write!(raw, " \\f &quot;{}&quot;", t)?;
} }
// \l // \l
if let Some(range) = self.tc_field_level_range { if let Some(range) = self.tc_field_level_range {
instr = format!("{} \\l &quot;{}-{}&quot;", instr, range.0, range.1); write!(raw, " \\l &quot;{}-{}&quot;", range.0, range.1)?;
} }
// \n // \n
if let Some(range) = self.omit_page_numbers_level_range { if let Some(range) = self.omit_page_numbers_level_range {
instr = format!("{} \\n &quot;{}-{}&quot;", instr, range.0, range.1); write!(raw, " \\n &quot;{}-{}&quot;", range.0, range.1)?;
} }
// \o // \o
if let Some(range) = self.heading_styles_range { if let Some(range) = self.heading_styles_range {
instr = format!("{} \\o &quot;{}-{}&quot;", instr, range.0, range.1); write!(raw, " \\o &quot;{}-{}&quot;", range.0, range.1)?;
} }
// \p // \p
if let Some(ref t) = self.entry_and_page_number_separator { if let Some(ref t) = self.entry_and_page_number_separator {
instr = format!("{} \\p &quot;{}&quot;", instr, t); write!(raw, " \\p &quot;{}&quot;", t)?;
} }
// \s // \s
if let Some(ref t) = self.seq_field_identifier_for_prefix { if let Some(ref t) = self.seq_field_identifier_for_prefix {
instr = format!("{} \\s &quot;{}&quot;", instr, t); write!(raw, " \\s &quot;{}&quot;", t)?;
} }
// \t // \t
@ -220,35 +228,35 @@ impl BuildXML for InstrToC {
.map(|s| format!("{},{}", (s.0).0, (s.0).1)) .map(|s| format!("{},{}", (s.0).0, (s.0).1))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(","); .join(",");
instr = format!("{} \\t &quot;{}&quot;", instr, s); write!(raw, " \\t &quot;{}&quot;", s)?;
} }
// \h // \h
if self.hyperlink { if self.hyperlink {
instr = format!("{} \\h", instr); write!(raw, " \\h")?;
} }
// \u // \u
if self.use_applied_paragraph_line_level { if self.use_applied_paragraph_line_level {
instr = format!("{} \\u", instr); write!(raw, " \\u")?;
} }
// \w // \w
if self.preserve_tab { if self.preserve_tab {
instr = format!("{} \\w", instr); write!(raw, " \\w")?;
} }
// \x // \x
if self.preserve_new_line { if self.preserve_new_line {
instr = format!("{} \\x", instr); write!(raw, " \\x")?;
} }
// \z // \z
if self.hide_tab_and_page_numbers_in_webview { if self.hide_tab_and_page_numbers_in_webview {
instr = format!("{} \\z", instr); write!(raw, " \\z")?;
} }
instr.into() b.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -13,9 +14,11 @@ impl IsLgl {
} }
impl BuildXML for IsLgl { impl BuildXML for IsLgl {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.is_lgl().build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).is_lgl()?.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -35,8 +36,10 @@ impl Serialize for Italic {
} }
impl BuildXML for Italic { impl BuildXML for Italic {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.i().build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).i()?.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -35,8 +36,10 @@ impl Serialize for ItalicCs {
} }
impl BuildXML for ItalicCs { impl BuildXML for ItalicCs {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.i_cs().build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).i_cs()?.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -23,9 +24,13 @@ impl Justification {
} }
impl BuildXML for Justification { impl BuildXML for Justification {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.justification(&self.val).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.justification(&self.val)?
.into_inner()
} }
} }

View File

@ -1,6 +1,7 @@
use crate::documents::*; use crate::documents::*;
use crate::types::*; use crate::types::*;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use serde::Serialize; use serde::Serialize;
@ -125,24 +126,26 @@ impl Level {
} }
impl BuildXML for Level { impl BuildXML for Level {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut b = XMLBuilder::new() &self,
.open_level(&format!("{}", self.level)) stream: xml::writer::EventWriter<W>,
.add_child(&self.start) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.add_child(&self.format) XMLBuilder::from(stream)
.add_child(&self.text) .open_level(&format!("{}", self.level))?
.add_child(&self.jc) .add_child(&self.start)?
.add_child(&self.paragraph_property) .add_child(&self.format)?
.add_child(&self.run_property) .add_child(&self.text)?
.add_optional_child(&self.pstyle) .add_child(&self.jc)?
.add_optional_child(&self.level_restart) .add_child(&self.paragraph_property)?
.add_optional_child(&self.is_lgl); .add_child(&self.run_property)?
.add_optional_child(&self.pstyle)?
if self.suffix != LevelSuffixType::Tab { .add_optional_child(&self.level_restart)?
b = b.suffix(&self.suffix.to_string()); .add_optional_child(&self.is_lgl)?
} .apply_if(self.suffix != LevelSuffixType::Tab, |b| {
b.suffix(&self.suffix.to_string())
b.close().build() })?
.close()?
.into_inner()
} }
} }
@ -199,8 +202,7 @@ mod tests {
.build(); .build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:lvl w:ilvl="1"><w:start w:val="1" /><w:numFmt w:val="decimal" /><w:lvlText w:val="%4." /><w:lvlJc w:val="left" /><w:pPr><w:rPr /></w:pPr><w:rPr /><w:suff w:val="space" /> r#"<w:lvl w:ilvl="1"><w:start w:val="1" /><w:numFmt w:val="decimal" /><w:lvlText w:val="%4." /><w:lvlJc w:val="left" /><w:pPr><w:rPr /></w:pPr><w:rPr /><w:suff w:val="space" /></w:lvl>"#
</w:lvl>"#
); );
} }
#[test] #[test]

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -15,9 +16,13 @@ impl LevelJc {
} }
impl BuildXML for LevelJc { impl BuildXML for LevelJc {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.level_justification(&self.val).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.level_justification(&self.val)?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use super::*; use super::*;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -38,17 +39,18 @@ impl LevelOverride {
} }
impl BuildXML for LevelOverride { impl BuildXML for LevelOverride {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut b = XMLBuilder::new(); &self,
b = b.open_level_override(&format!("{}", self.level)); stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
b = b.add_optional_child(&self.override_level); XMLBuilder::from(stream)
.open_level_override(&format!("{}", self.level))?
if let Some(start) = self.override_start { .add_optional_child(&self.override_level)?
b = b.start_override(&format!("{}", start)); .apply_opt(self.override_start, |start, b| {
} b.start_override(&format!("{}", start))
})?
b.close().build() .close()?
.into_inner()
} }
} }
@ -66,9 +68,7 @@ mod tests {
let b = c.build(); let b = c.build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:lvlOverride w:ilvl="1"> r#"<w:lvlOverride w:ilvl="1"><w:startOverride w:val="2" /></w:lvlOverride>"#
<w:startOverride w:val="2" />
</w:lvlOverride>"#
); );
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -15,10 +16,13 @@ impl LevelRestart {
} }
impl BuildXML for LevelRestart { impl BuildXML for LevelRestart {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
let v = format!("{}", &self.val); stream: xml::writer::EventWriter<W>,
b.level_restart(&v).build() ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.level_restart(&format!("{}", &self.val))?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -15,9 +16,11 @@ impl LevelText {
} }
impl BuildXML for LevelText { impl BuildXML for LevelText {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.level_text(&self.val).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).level_text(&self.val)?.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use crate::line_spacing_type::LineSpacingType; use crate::line_spacing_type::LineSpacingType;
use serde::*; use serde::*;
@ -58,17 +59,20 @@ impl LineSpacing {
} }
impl BuildXML for LineSpacing { impl BuildXML for LineSpacing {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.line_spacing( stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.line_spacing(
self.before, self.before,
self.after, self.after,
self.line, self.line,
self.before_lines, self.before_lines,
self.after_lines, self.after_lines,
self.line_rule, self.line_rule,
) )?
.build() .into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::escape::escape; use crate::escape::escape;
@ -27,8 +28,10 @@ impl Serialize for Link {
} }
impl BuildXML for Link { impl BuildXML for Link {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.link(&self.val).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).link(&self.val)?.into_inner()
} }
} }

View File

@ -1,7 +1,8 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use serde::Serialize; use serde::Serialize;
use std::io::Write;
#[derive(Debug, Clone, Serialize, PartialEq, Default)] #[derive(Debug, Default, Clone, PartialEq, Serialize)]
pub struct McFallback {} pub struct McFallback {}
impl McFallback { impl McFallback {
@ -11,8 +12,11 @@ impl McFallback {
} }
impl BuildXML for McFallback { impl BuildXML for McFallback {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
&self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
// Ignore for now // Ignore for now
vec![] Ok(stream)
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use std::str::FromStr; use std::str::FromStr;
@ -36,9 +37,11 @@ impl Name {
} }
impl BuildXML for Name { impl BuildXML for Name {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.name(&self.name).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).name(&self.name)?.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -24,9 +25,11 @@ impl Serialize for Next {
} }
impl BuildXML for Next { impl BuildXML for Next {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.next(&self.val).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).next(&self.val)?.into_inner()
} }
} }

View File

@ -1,8 +1,8 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::types::*; use crate::types::*;
use crate::xml_builder::*;
#[derive(Serialize, Debug, Clone, PartialEq)] #[derive(Serialize, Debug, Clone, PartialEq)]
pub struct NumPages { pub struct NumPages {
@ -21,29 +21,20 @@ impl NumPages {
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
}
fn inner_build(&self) -> Vec<u8> { impl BuildXML for NumPages {
let b = XMLBuilder::new(); fn build_to<W: Write>(
let r = Run::new() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
Run::new()
.add_field_char(FieldCharType::Begin, false) .add_field_char(FieldCharType::Begin, false)
.add_instr_text(InstrText::NUMPAGES(self.instr.clone())) .add_instr_text(InstrText::NUMPAGES(self.instr.clone()))
.add_field_char(FieldCharType::Separate, false) .add_field_char(FieldCharType::Separate, false)
.add_text("1") .add_text("1")
.add_field_char(FieldCharType::End, false); .add_field_char(FieldCharType::End, false)
.build_to(stream)
b.add_child(&r).build()
}
}
impl BuildXML for NumPages {
fn build(&self) -> Vec<u8> {
self.inner_build()
}
}
impl BuildXML for Box<NumPages> {
fn build(&self) -> Vec<u8> {
self.inner_build()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -15,9 +16,13 @@ impl NumberFormat {
} }
impl BuildXML for NumberFormat { impl BuildXML for NumberFormat {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.number_format(&self.val).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.number_format(&self.val)?
.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use super::*; use super::*;
use serde::Serialize; use serde::Serialize;
@ -33,15 +34,18 @@ impl Numbering {
} }
impl BuildXML for Numbering { impl BuildXML for Numbering {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
let id = format!("{}", self.id); let id = format!("{}", self.id);
let abs_id = format!("{}", self.abstract_num_id); let abs_id = format!("{}", self.abstract_num_id);
b.open_num(&id) XMLBuilder::from(stream)
.abstract_num_id(&abs_id) .open_num(&id)?
.add_children(&self.level_overrides) .abstract_num_id(&abs_id)?
.close() .add_children(&self.level_overrides)?
.build() .close()?
.into_inner()
} }
} }
@ -59,9 +63,7 @@ mod tests {
let b = c.build(); let b = c.build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:num w:numId="0"> r#"<w:num w:numId="0"><w:abstractNumId w:val="2" /></w:num>"#
<w:abstractNumId w:val="2" />
</w:num>"#
); );
} }
#[test] #[test]
@ -74,12 +76,7 @@ mod tests {
let b = c.overrides(overrides).build(); let b = c.overrides(overrides).build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:num w:numId="0"> r#"<w:num w:numId="0"><w:abstractNumId w:val="2" /><w:lvlOverride w:ilvl="0"><w:startOverride w:val="1" /></w:lvlOverride><w:lvlOverride w:ilvl="1"><w:startOverride w:val="1" /></w:lvlOverride></w:num>"#
<w:abstractNumId w:val="2" /><w:lvlOverride w:ilvl="0">
<w:startOverride w:val="1" />
</w:lvlOverride><w:lvlOverride w:ilvl="1">
<w:startOverride w:val="1" />
</w:lvlOverride></w:num>"#
); );
} }

View File

@ -1,5 +1,6 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use serde::Serialize; use serde::Serialize;
@ -15,9 +16,11 @@ impl NumberingId {
} }
impl BuildXML for NumberingId { impl BuildXML for NumberingId {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.num_id(self.id).build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).num_id(self.id)?.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use serde::ser::{SerializeStruct, Serializer}; use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use super::{IndentLevel, NumberingId}; use super::{IndentLevel, NumberingId};
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -29,13 +30,16 @@ impl NumberingProperty {
} }
impl BuildXML for NumberingProperty { impl BuildXML for NumberingProperty {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.open_numbering_property() stream: xml::writer::EventWriter<W>,
.add_optional_child(&self.id) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.add_optional_child(&self.level) XMLBuilder::from(stream)
.close() .open_numbering_property()?
.build() .add_optional_child(&self.id)?
.add_optional_child(&self.level)?
.close()?
.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use serde::*; use serde::*;
@ -16,11 +17,14 @@ impl OutlineLvl {
} }
impl BuildXML for OutlineLvl { impl BuildXML for OutlineLvl {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
.outline_lvl(self.v) stream: xml::writer::EventWriter<W>,
// .close() ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.build() XMLBuilder::from(stream)
.outline_lvl(self.v)?
// .close()?
.into_inner()
} }
} }

View File

@ -1,6 +1,7 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::PageMargin; use crate::types::PageMargin;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
// These values were based on microsoft office word2019 windows edition. // These values were based on microsoft office word2019 windows edition.
// <w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0"/> // <w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0"/>
@ -53,8 +54,11 @@ impl PageMargin {
} }
impl BuildXML for PageMargin { impl BuildXML for PageMargin {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.page_margin( .page_margin(
&format!("{}", self.top), &format!("{}", self.top),
&format!("{}", self.right), &format!("{}", self.right),
@ -63,8 +67,8 @@ impl BuildXML for PageMargin {
&format!("{}", self.header), &format!("{}", self.header),
&format!("{}", self.footer), &format!("{}", self.footer),
&format!("{}", self.gutter), &format!("{}", self.gutter),
) )?
.build() .into_inner()
} }
} }

View File

@ -1,8 +1,8 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::types::*; use crate::types::*;
use crate::xml_builder::*;
#[derive(Serialize, Debug, Clone, PartialEq)] #[derive(Serialize, Debug, Clone, PartialEq)]
pub struct PageNum { pub struct PageNum {
@ -21,29 +21,20 @@ impl PageNum {
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
}
fn inner_build(&self) -> Vec<u8> { impl BuildXML for PageNum {
let b = XMLBuilder::new(); fn build_to<W: Write>(
let r = Run::new() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
Run::new()
.add_field_char(FieldCharType::Begin, false) .add_field_char(FieldCharType::Begin, false)
.add_instr_text(InstrText::PAGE(self.instr.clone())) .add_instr_text(InstrText::PAGE(self.instr.clone()))
.add_field_char(FieldCharType::Separate, false) .add_field_char(FieldCharType::Separate, false)
.add_text("1") .add_text("1")
.add_field_char(FieldCharType::End, false); .add_field_char(FieldCharType::End, false)
.build_to(stream)
b.add_child(&r).build()
}
}
impl BuildXML for PageNum {
fn build(&self) -> Vec<u8> {
self.inner_build()
}
}
impl BuildXML for Box<PageNum> {
fn build(&self) -> Vec<u8> {
self.inner_build()
} }
} }

View File

@ -1,6 +1,7 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use serde::Serialize; use serde::Serialize;
use std::io::Write;
#[derive(Debug, Clone, PartialEq, Serialize, Default)] #[derive(Debug, Clone, PartialEq, Serialize, Default)]
#[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))]
@ -34,9 +35,12 @@ impl PageNumType {
} }
impl BuildXML for PageNumType { impl BuildXML for PageNumType {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
.page_num_type(self.start, self.chap_style.clone()) stream: xml::writer::EventWriter<W>,
.build() ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.page_num_type(self.start, self.chap_style.clone())?
.into_inner()
} }
} }

View File

@ -1,6 +1,7 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use serde::Serialize; use serde::Serialize;
@ -54,20 +55,19 @@ impl PageSize {
} }
impl BuildXML for PageSize { impl BuildXML for PageSize {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
if let Some(orient) = self.orient { &self,
XMLBuilder::new() stream: xml::writer::EventWriter<W>,
.page_size_with_orient( ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
&format!("{}", self.w), let w = format!("{}", self.w);
&format!("{}", self.h), let h = format!("{}", self.h);
&orient.to_string(),
) XMLBuilder::from(stream)
.build() .apply(|b| match self.orient {
} else { None => b.page_size(&w, &h),
XMLBuilder::new() Some(orient) => b.page_size_with_orient(&w, &h, &orient.to_string()),
.page_size(&format!("{}", self.w), &format!("{}", self.h)) })?
.build() .into_inner()
}
} }
} }

View File

@ -1,5 +1,6 @@
use serde::ser::{SerializeStruct, Serializer}; use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use super::*; use super::*;
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -42,19 +43,22 @@ pub enum ParagraphChild {
} }
impl BuildXML for ParagraphChild { impl BuildXML for ParagraphChild {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
&self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
match self { match self {
ParagraphChild::Run(v) => v.build(), ParagraphChild::Run(v) => v.build_to(stream),
ParagraphChild::Insert(v) => v.build(), ParagraphChild::Insert(v) => v.build_to(stream),
ParagraphChild::Delete(v) => v.build(), ParagraphChild::Delete(v) => v.build_to(stream),
ParagraphChild::Hyperlink(v) => v.build(), ParagraphChild::Hyperlink(v) => v.build_to(stream),
ParagraphChild::BookmarkStart(v) => v.build(), ParagraphChild::BookmarkStart(v) => v.build_to(stream),
ParagraphChild::BookmarkEnd(v) => v.build(), ParagraphChild::BookmarkEnd(v) => v.build_to(stream),
ParagraphChild::CommentStart(v) => v.build(), ParagraphChild::CommentStart(v) => v.build_to(stream),
ParagraphChild::CommentEnd(v) => v.build(), ParagraphChild::CommentEnd(v) => v.build_to(stream),
ParagraphChild::StructuredDataTag(v) => v.build(), ParagraphChild::StructuredDataTag(v) => v.build_to(stream),
ParagraphChild::PageNum(v) => v.build(), ParagraphChild::PageNum(v) => v.build_to(stream),
ParagraphChild::NumPages(v) => v.build(), ParagraphChild::NumPages(v) => v.build_to(stream),
} }
} }
} }
@ -493,19 +497,16 @@ impl Paragraph {
} }
impl BuildXML for Paragraph { impl BuildXML for Paragraph {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
.open_paragraph(&self.id) stream: xml::writer::EventWriter<W>,
.add_child(&self.property) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.add_children(&self.children) XMLBuilder::from(stream)
.close() .open_paragraph(&self.id)?
.build() .add_child(&self.property)?
} .add_children(&self.children)?
} .close()?
.into_inner()
impl BuildXML for Box<Paragraph> {
fn build(&self) -> Vec<u8> {
Paragraph::build(self)
} }
} }
@ -550,13 +551,7 @@ mod tests {
.build(); .build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:commentRangeStart w:id="1" /><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r><w:r> r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:commentRangeStart w:id="1" /><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r><w:r><w:rPr /></w:r><w:commentRangeEnd w:id="1" /><w:r><w:commentReference w:id="1" /></w:r></w:p>"#
<w:rPr />
</w:r>
<w:commentRangeEnd w:id="1" />
<w:r>
<w:commentReference w:id="1" />
</w:r></w:p>"#
); );
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
@ -81,22 +82,24 @@ impl ParagraphBorder {
} }
impl BuildXML for ParagraphBorder { impl BuildXML for ParagraphBorder {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let base = XMLBuilder::new(); &self,
let base = { stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
let val = self.val.to_string(); let val = self.val.to_string();
let space = self.space.to_string(); let space = self.space.to_string();
let size = self.size.to_string(); let size = self.size.to_string();
match self.position { let func = match self.position {
ParagraphBorderPosition::Top => base.paragraph_border_top(&val, &space, &size, &self.color), ParagraphBorderPosition::Top => XMLBuilder::paragraph_border_top,
ParagraphBorderPosition::Left => base.paragraph_border_left(&val, &space, &size, &self.color), ParagraphBorderPosition::Left => XMLBuilder::paragraph_border_left,
ParagraphBorderPosition::Bottom => base.paragraph_border_bottom(&val, &space, &size, &self.color), ParagraphBorderPosition::Bottom => XMLBuilder::paragraph_border_bottom,
ParagraphBorderPosition::Right => base.paragraph_border_right(&val, &space, &size, &self.color), ParagraphBorderPosition::Right => XMLBuilder::paragraph_border_right,
ParagraphBorderPosition::Between => base.paragraph_border_between(&val, &space, &size, &self.color), ParagraphBorderPosition::Between => XMLBuilder::paragraph_border_between,
ParagraphBorderPosition::Bar => base.paragraph_border_bar(&val, &space, &size, &self.color), ParagraphBorderPosition::Bar => XMLBuilder::paragraph_border_bar,
}
}; };
base.build() XMLBuilder::from(stream)
.apply(|b| func(b, &val, &space, &size, &self.color))?
.into_inner()
} }
} }
@ -111,7 +114,6 @@ pub struct ParagraphBorders {
bar: Option<ParagraphBorder>, bar: Option<ParagraphBorder>,
} }
impl Default for ParagraphBorders { impl Default for ParagraphBorders {
fn default() -> Self { fn default() -> Self {
ParagraphBorders { ParagraphBorders {
@ -168,26 +170,32 @@ impl ParagraphBorders {
pub fn clear_all(mut self) -> Self { pub fn clear_all(mut self) -> Self {
self.left = Some(ParagraphBorder::new(ParagraphBorderPosition::Left).val(BorderType::Nil)); self.left = Some(ParagraphBorder::new(ParagraphBorderPosition::Left).val(BorderType::Nil));
self.right = Some(ParagraphBorder::new(ParagraphBorderPosition::Right).val(BorderType::Nil)); self.right =
Some(ParagraphBorder::new(ParagraphBorderPosition::Right).val(BorderType::Nil));
self.top = Some(ParagraphBorder::new(ParagraphBorderPosition::Top).val(BorderType::Nil)); self.top = Some(ParagraphBorder::new(ParagraphBorderPosition::Top).val(BorderType::Nil));
self.bottom = Some(ParagraphBorder::new(ParagraphBorderPosition::Bottom).val(BorderType::Nil)); self.bottom =
self.between = Some(ParagraphBorder::new(ParagraphBorderPosition::Between).val(BorderType::Nil)); Some(ParagraphBorder::new(ParagraphBorderPosition::Bottom).val(BorderType::Nil));
self.between =
Some(ParagraphBorder::new(ParagraphBorderPosition::Between).val(BorderType::Nil));
self.bar = Some(ParagraphBorder::new(ParagraphBorderPosition::Bar).val(BorderType::Nil)); self.bar = Some(ParagraphBorder::new(ParagraphBorderPosition::Bar).val(BorderType::Nil));
self self
} }
} }
impl BuildXML for ParagraphBorders { impl BuildXML for ParagraphBorders {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new() &self,
.open_paragraph_borders() stream: xml::writer::EventWriter<W>,
.add_optional_child(&self.left) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.add_optional_child(&self.right) XMLBuilder::from(stream)
.add_optional_child(&self.top) .open_paragraph_borders()?
.add_optional_child(&self.bottom) .add_optional_child(&self.left)?
.add_optional_child(&self.between) .add_optional_child(&self.right)?
.add_optional_child(&self.bar) .add_optional_child(&self.top)?
.close() .add_optional_child(&self.bottom)?
.build() .add_optional_child(&self.between)?
.add_optional_child(&self.bar)?
.close()?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use super::*; use super::*;
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -201,68 +202,39 @@ impl ParagraphProperty {
} }
} }
fn inner_build(p: &ParagraphProperty) -> Vec<u8> {
let mut b = XMLBuilder::new()
.open_paragraph_property()
.add_child(&p.run_property)
.add_optional_child(&p.style)
.add_optional_child(&p.numbering_property)
.add_optional_child(&p.frame_property)
.add_optional_child(&p.alignment)
.add_optional_child(&p.indent)
.add_optional_child(&p.line_spacing)
.add_optional_child(&p.outline_lvl)
.add_optional_child(&p.paragraph_property_change)
.add_optional_child(&p.borders)
.add_optional_child(&p.text_alignment)
.add_optional_child(&p.adjust_right_ind);
if let Some(v) = p.snap_to_grid {
b = b.snap_to_grid(v)
}
if let Some(v) = p.keep_next {
if v {
b = b.keep_next()
}
}
if let Some(v) = p.keep_lines {
if v {
b = b.keep_lines()
}
}
if let Some(v) = p.page_break_before {
if v {
b = b.page_break_before()
}
}
if let Some(v) = p.widow_control {
b = b.widow_control(if v { "1" } else { "0" })
}
if !p.tabs.is_empty() {
b = b.open_tabs();
for t in p.tabs.iter() {
b = b.tab(t.val, t.leader, t.pos);
}
b = b.close();
}
b.close().build()
}
impl BuildXML for ParagraphProperty { impl BuildXML for ParagraphProperty {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
inner_build(self) &self,
} stream: xml::writer::EventWriter<W>,
} ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
impl BuildXML for Box<ParagraphProperty> { .open_paragraph_property()?
fn build(&self) -> Vec<u8> { .add_child(&self.run_property)?
inner_build(self) .add_optional_child(&self.style)?
.add_optional_child(&self.numbering_property)?
.add_optional_child(&self.frame_property)?
.add_optional_child(&self.alignment)?
.add_optional_child(&self.indent)?
.add_optional_child(&self.line_spacing)?
.add_optional_child(&self.outline_lvl)?
.add_optional_child(&self.paragraph_property_change)?
.add_optional_child(&self.borders)?
.add_optional_child(&self.text_alignment)?
.add_optional_child(&self.adjust_right_ind)?
.apply_opt(self.snap_to_grid, |v, b| b.snap_to_grid(v))?
.apply_if(self.keep_next, |b| b.keep_next())?
.apply_if(self.keep_lines, |b| b.keep_lines())?
.apply_if(self.page_break_before, |b| b.page_break_before())?
.apply_opt(self.widow_control, |flag, b| {
b.widow_control(if flag { "1" } else { "0" })
})?
.apply_if(!self.tabs.is_empty(), |b| {
b.open_tabs()?
.apply_each(&self.tabs, |tab, b| b.tab(tab.val, tab.leader, tab.pos))?
.close()
})?
.close()?
.into_inner()
} }
} }
@ -307,8 +279,7 @@ mod tests {
let b = c.keep_next(true).build(); let b = c.keep_next(true).build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:pPr><w:rPr /><w:keepNext /> r#"<w:pPr><w:rPr /><w:keepNext /></w:pPr>"#
</w:pPr>"#
); );
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::escape; use crate::escape;
@ -17,7 +18,7 @@ impl Default for ParagraphPropertyChange {
Self { Self {
author: "unnamed".to_owned(), author: "unnamed".to_owned(),
date: "1970-01-01T00:00:00Z".to_owned(), date: "1970-01-01T00:00:00Z".to_owned(),
property: Box::new(ParagraphProperty::default()), property: Default::default(),
} }
} }
} }
@ -48,14 +49,16 @@ impl ParagraphPropertyChange {
impl ParagraphPropertyChangeId for ParagraphPropertyChange {} impl ParagraphPropertyChangeId for ParagraphPropertyChange {}
impl BuildXML for ParagraphPropertyChange { impl BuildXML for ParagraphPropertyChange {
#[allow(clippy::needless_borrow)] fn build_to<W: Write>(
fn build(&self) -> Vec<u8> { &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
let id = self.generate(); let id = self.generate();
XMLBuilder::new() XMLBuilder::from(stream)
.open_paragraph_property_change(&id, &self.author, &self.date) .open_paragraph_property_change(&id, &self.author, &self.date)?
.add_child(&self.property) .add_child(&self.property)?
.close() .close()?
.build() .into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use super::*; use super::*;
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -34,12 +35,15 @@ impl Default for ParagraphPropertyDefault {
} }
impl BuildXML for ParagraphPropertyDefault { impl BuildXML for ParagraphPropertyDefault {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.open_paragraph_property_default() stream: xml::writer::EventWriter<W>,
.add_child(&self.paragraph_property) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.close() XMLBuilder::from(stream)
.build() .open_paragraph_property_default()?
.add_child(&self.paragraph_property)?
.close()?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::escape::escape; use crate::escape::escape;
@ -36,8 +37,13 @@ impl ParagraphStyle {
} }
impl BuildXML for ParagraphStyle { impl BuildXML for ParagraphStyle {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new().paragraph_style(&self.val).build() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.paragraph_style(&self.val)?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::*; use crate::documents::*;
use crate::types::*; use crate::types::*;
@ -13,7 +14,7 @@ pub struct Pic {
// For writer only // For writer only
#[serde(skip_serializing_if = "Vec::is_empty")] #[serde(skip_serializing_if = "Vec::is_empty")]
pub image: Vec<u8>, pub image: Vec<u8>,
// unit is emu // (width, height). unit is emu
pub size: (u32, u32), pub size: (u32, u32),
pub position_type: DrawingPositionType, pub position_type: DrawingPositionType,
/// Specifies that this object shall be positioned using the positioning information in the /// Specifies that this object shall be positioned using the positioning information in the
@ -203,35 +204,36 @@ impl Pic {
} }
impl BuildXML for Pic { impl BuildXML for Pic {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
let w = format!("{}", self.size.0); stream: xml::writer::EventWriter<W>,
let h = format!("{}", self.size.1); ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
b.open_pic("http://schemas.openxmlformats.org/drawingml/2006/picture") XMLBuilder::from(stream)
.open_pic_nv_pic_pr() .open_pic("http://schemas.openxmlformats.org/drawingml/2006/picture")?
.pic_c_nv_pr("0", "") .open_pic_nv_pic_pr()?
.open_pic_c_nv_pic_pr() .pic_c_nv_pr("0", "")?
.a_pic_locks("1", "1") .open_pic_c_nv_pic_pr()?
.close() .a_pic_locks("1", "1")?
.close() .close()?
.open_blip_fill() .close()?
.a_blip(&self.id) .open_blip_fill()?
.a_src_rect() .a_blip(&self.id)?
.open_a_stretch() .a_src_rect()?
.a_fill_rect() .open_a_stretch()?
.close() .a_fill_rect()?
.close() .close()?
.open_pic_sp_pr("auto") .close()?
.open_a_xfrm_with_rot(&format!("{}", (self.rot as u32) * 60 * 1000)) .open_pic_sp_pr("auto")?
.a_off("0", "0") .open_a_xfrm_with_rot(&format!("{}", (self.rot as u32) * 60 * 1000))?
.a_ext(&w, &h) .a_off("0", "0")?
.close() .a_ext(&format!("{}", self.size.0), &format!("{}", self.size.1))?
.open_a_prst_geom("rect") .close()?
.a_av_lst() .open_a_prst_geom("rect")?
.close() .a_av_lst()?
.close() .close()?
.close() .close()?
.build() .close()?
.into_inner()
} }
} }
@ -248,30 +250,7 @@ mod tests {
let b = Pic::new_with_dimensions(Vec::new(), 320, 240).build(); let b = Pic::new_with_dimensions(Vec::new(), 320, 240).build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"> r#"<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="0" name="" /><pic:cNvPicPr><a:picLocks noChangeAspect="1" noChangeArrowheads="1" /></pic:cNvPicPr></pic:nvPicPr><pic:blipFill><a:blip r:embed="rIdImage123" /><a:srcRect /><a:stretch><a:fillRect /></a:stretch></pic:blipFill><pic:spPr bwMode="auto"><a:xfrm rot="0"><a:off x="0" y="0" /><a:ext cx="3048000" cy="2286000" /></a:xfrm><a:prstGeom prst="rect"><a:avLst /></a:prstGeom></pic:spPr></pic:pic>"#
<pic:nvPicPr>
<pic:cNvPr id="0" name="" />
<pic:cNvPicPr>
<a:picLocks noChangeAspect="1" noChangeArrowheads="1" />
</pic:cNvPicPr>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rIdImage123" />
<a:srcRect />
<a:stretch>
<a:fillRect />
</a:stretch>
</pic:blipFill>
<pic:spPr bwMode="auto">
<a:xfrm rot="0">
<a:off x="0" y="0" />
<a:ext cx="3048000" cy="2286000" />
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst />
</a:prstGeom>
</pic:spPr>
</pic:pic>"#
); );
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
@ -54,9 +55,12 @@ impl PositionalTab {
} }
impl BuildXML for PositionalTab { impl BuildXML for PositionalTab {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.ptab(self.alignment, self.relative_to, self.leader) stream: xml::writer::EventWriter<W>,
.build() ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.ptab(self.alignment, self.relative_to, self.leader)?
.into_inner()
} }
} }

View File

@ -1,5 +1,6 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
//17.7.4.14 //17.7.4.14
// qFormat (Primary Style) // qFormat (Primary Style)
@ -16,11 +17,12 @@ impl QFormat {
} }
} }
impl BuildXML for QFormat { impl BuildXML for QFormat {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.q_format().build() stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).q_format()?.into_inner()
} }
} }

View File

@ -1,6 +1,7 @@
use super::*; use super::*;
use serde::ser::{SerializeStruct, Serializer}; use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
@ -328,33 +329,45 @@ impl Run {
} }
} }
impl BuildXML for Run { impl BuildXML for RunChild {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
let mut b = b.open_run().add_child(&self.run_property); stream: xml::writer::EventWriter<W>,
for c in &self.children { ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
match c { match self {
RunChild::Text(t) => b = b.add_child(t), RunChild::Text(t) => t.build_to(stream),
RunChild::Sym(t) => b = b.add_child(t), RunChild::Sym(t) => t.build_to(stream),
RunChild::DeleteText(t) => b = b.add_child(t), RunChild::DeleteText(t) => t.build_to(stream),
RunChild::Tab(t) => b = b.add_child(t), RunChild::Tab(t) => t.build_to(stream),
RunChild::PTab(t) => b = b.add_child(t), RunChild::PTab(t) => t.build_to(stream),
RunChild::Break(t) => b = b.add_child(t), RunChild::Break(t) => t.build_to(stream),
RunChild::Drawing(t) => b = b.add_child(t), RunChild::Drawing(t) => t.build_to(stream),
RunChild::Shape(_t) => { RunChild::Shape(_t) => {
todo!("Support shape writer.") todo!("Support shape writer.")
} }
RunChild::CommentStart(c) => b = b.add_child(c), RunChild::CommentStart(c) => c.build_to(stream),
RunChild::CommentEnd(c) => b = b.add_child(c), RunChild::CommentEnd(c) => c.build_to(stream),
RunChild::FieldChar(c) => b = b.add_child(c), RunChild::FieldChar(c) => c.build_to(stream),
RunChild::InstrText(c) => b = b.add_child(c), RunChild::InstrText(c) => c.build_to(stream),
RunChild::DeleteInstrText(c) => b = b.add_child(c), RunChild::DeleteInstrText(c) => c.build_to(stream),
RunChild::InstrTextString(_) => unreachable!(), RunChild::InstrTextString(_) => unreachable!(),
RunChild::FootnoteReference(c) => b = b.add_child(c), RunChild::FootnoteReference(c) => c.build_to(stream),
RunChild::Shading(s) => b = b.add_child(s), RunChild::Shading(s) => s.build_to(stream),
} }
} }
b.close().build() }
impl BuildXML for Run {
fn build_to<W: Write>(
&self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.open_run()?
.add_child(&self.run_property)?
.add_children(&self.children)?
.close()?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::escape::escape; use crate::escape::escape;
@ -96,9 +97,12 @@ impl RunFonts {
} }
impl BuildXML for RunFonts { impl BuildXML for RunFonts {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.run_fonts( stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream)
.run_fonts(
self.ascii.as_ref(), self.ascii.as_ref(),
self.hi_ansi.as_ref(), self.hi_ansi.as_ref(),
self.cs.as_ref(), self.cs.as_ref(),
@ -108,8 +112,8 @@ impl BuildXML for RunFonts {
self.cs_theme.as_ref(), self.cs_theme.as_ref(),
self.east_asia_theme.as_ref(), self.east_asia_theme.as_ref(),
self.hint.as_ref(), self.hint.as_ref(),
) )?
.build() .into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use super::*; use super::*;
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -171,33 +172,36 @@ impl RunProperty {
} }
impl BuildXML for RunProperty { impl BuildXML for RunProperty {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.open_run_property() stream: xml::writer::EventWriter<W>,
.add_optional_child(&self.sz) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.add_optional_child(&self.sz_cs) XMLBuilder::from(stream)
.add_optional_child(&self.color) .open_run_property()?
.add_optional_child(&self.bold) .add_optional_child(&self.sz)?
.add_optional_child(&self.bold_cs) .add_optional_child(&self.sz_cs)?
.add_optional_child(&self.caps) .add_optional_child(&self.color)?
.add_optional_child(&self.italic) .add_optional_child(&self.bold)?
.add_optional_child(&self.italic_cs) .add_optional_child(&self.bold_cs)?
.add_optional_child(&self.strike) .add_optional_child(&self.caps)?
.add_optional_child(&self.highlight) .add_optional_child(&self.italic)?
.add_optional_child(&self.underline) .add_optional_child(&self.italic_cs)?
.add_optional_child(&self.vanish) .add_optional_child(&self.strike)?
.add_optional_child(&self.spec_vanish) .add_optional_child(&self.highlight)?
.add_optional_child(&self.fonts) .add_optional_child(&self.underline)?
.add_optional_child(&self.text_border) .add_optional_child(&self.vanish)?
.add_optional_child(&self.ins) .add_optional_child(&self.spec_vanish)?
.add_optional_child(&self.del) .add_optional_child(&self.fonts)?
.add_optional_child(&self.vert_align) .add_optional_child(&self.text_border)?
.add_optional_child(&self.character_spacing) .add_optional_child(&self.ins)?
.add_optional_child(&self.style) .add_optional_child(&self.del)?
.add_optional_child(&self.positional_tab) .add_optional_child(&self.vert_align)?
.add_optional_child(&self.shading) .add_optional_child(&self.character_spacing)?
.close() .add_optional_child(&self.style)?
.build() .add_optional_child(&self.positional_tab)?
.add_optional_child(&self.shading)?
.close()?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::io::Write;
use super::*; use super::*;
use crate::documents::BuildXML; use crate::documents::BuildXML;
@ -44,12 +45,15 @@ impl Default for RunPropertyDefault {
} }
impl BuildXML for RunPropertyDefault { impl BuildXML for RunPropertyDefault {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let b = XMLBuilder::new(); &self,
b.open_run_property_default() stream: xml::writer::EventWriter<W>,
.add_child(&self.run_property) ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.close() XMLBuilder::from(stream)
.build() .open_run_property_default()?
.add_child(&self.run_property)?
.close()?
.into_inner()
} }
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::io::Write;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::escape::escape; use crate::escape::escape;
@ -26,8 +27,11 @@ impl RunStyle {
} }
impl BuildXML for RunStyle { impl BuildXML for RunStyle {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
XMLBuilder::new().run_style(&self.val).build() &self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
XMLBuilder::from(stream).run_style(&self.val)?.into_inner()
} }
} }

View File

@ -1,6 +1,7 @@
use super::*; use super::*;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
use std::io::Write;
use serde::Serialize; use serde::Serialize;
@ -25,16 +26,18 @@ impl Default for Section {
} }
impl BuildXML for Section { impl BuildXML for Section {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
&self,
stream: xml::writer::EventWriter<W>,
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
let id = crate::generate_para_id(); let id = crate::generate_para_id();
XMLBuilder::from(stream)
XMLBuilder::new() .open_paragraph(&id)?
.open_paragraph(&id) .open_paragraph_property()?
.open_paragraph_property() .add_child(&self.property)?
.add_child(&self.property) .close()?
.close() .close()?
.close() .into_inner()
.build()
} }
} }
@ -52,10 +55,7 @@ mod tests {
let b = c.build(); let b = c.build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:p w14:paraId="12345678"> r#"<w:p w14:paraId="12345678"><w:pPr><w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" /></w:sectPr></w:pPr></w:p>"#
<w:pPr><w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" />
</w:sectPr></w:pPr>
</w:p>"#
); );
} }
} }

View File

@ -3,6 +3,7 @@ use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
use crate::xml_builder::*; use crate::xml_builder::*;
use crate::{Footer, Header}; use crate::{Footer, Header};
use std::io::Write;
use serde::Serialize; use serde::Serialize;
@ -197,34 +198,30 @@ impl Default for SectionProperty {
} }
impl BuildXML for SectionProperty { impl BuildXML for SectionProperty {
fn build(&self) -> Vec<u8> { fn build_to<W: Write>(
let mut b = XMLBuilder::new(); &self,
b = b stream: xml::writer::EventWriter<W>,
.open_section_property() ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
.add_child(&self.page_size) XMLBuilder::from(stream)
.add_child(&self.page_margin) .open_section_property()?
.columns(&format!("{}", &self.space), &format!("{}", &self.columns)) .add_child(&self.page_size)?
.add_optional_child(&self.doc_grid) .add_child(&self.page_margin)?
.add_optional_child(&self.header_reference) .columns(&format!("{}", &self.space), &format!("{}", &self.columns))?
.add_optional_child(&self.first_header_reference) .add_optional_child(&self.doc_grid)?
.add_optional_child(&self.even_header_reference) .add_optional_child(&self.header_reference)?
.add_optional_child(&self.footer_reference) .add_optional_child(&self.first_header_reference)?
.add_optional_child(&self.first_footer_reference) .add_optional_child(&self.even_header_reference)?
.add_optional_child(&self.even_footer_reference) .add_optional_child(&self.footer_reference)?
.add_optional_child(&self.page_num_type); .add_optional_child(&self.first_footer_reference)?
.add_optional_child(&self.even_footer_reference)?
if !self.text_direction.eq("lrTb") { .add_optional_child(&self.page_num_type)?
b = b.text_direction(&self.text_direction); .apply_if(self.text_direction != "lrTb", |b| {
} b.text_direction(&self.text_direction)
if let Some(t) = self.section_type { })?
b = b.type_tag(&t.to_string()); .apply_opt(self.section_type, |t, b| b.type_tag(&t.to_string()))?
} .apply_if(self.title_pg, |b| b.title_pg())?
.close()?
if self.title_pg { .into_inner()
b = b.title_pg();
}
b.close().build()
} }
} }
@ -243,9 +240,7 @@ mod tests {
let b = c.build(); let b = c.build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" /> r#"<w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" /><w:textDirection w:val="tbRl" /></w:sectPr>"#
<w:textDirection w:val="tbRl" />
</w:sectPr>"#
) )
} }
@ -255,8 +250,7 @@ mod tests {
let b = c.build(); let b = c.build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" /> r#"<w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" /></w:sectPr>"#
</w:sectPr>"#
); );
} }
@ -276,9 +270,7 @@ mod tests {
let b = c.build(); let b = c.build();
assert_eq!( assert_eq!(
str::from_utf8(&b).unwrap(), str::from_utf8(&b).unwrap(),
r#"<w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" /> r#"<w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" /><w:titlePg /></w:sectPr>"#
<w:titlePg />
</w:sectPr>"#
); );
} }
} }

Some files were not shown because too many files have changed in this diff Show More