feat: impl sdt writer (#380)

main
bokuweb 2021-12-03 00:56:29 +09:00 committed by GitHub
parent ad229e91ad
commit caa61e9947
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 169 additions and 2 deletions

View File

@ -0,0 +1,16 @@
use docx_rs::*;
pub fn main() -> Result<(), DocxError> {
let path = std::path::Path::new("./output/sdt.docx");
let file = std::fs::File::create(&path).unwrap();
let p = Paragraph::new().add_run(
Run::new()
.add_text("Hello")
.fonts(RunFonts::new().ascii("Arial")),
);
Docx::new()
.add_structured_data_tag(StructuredDataTag::new().add_paragraph(p))
.build()
.pack(file)?;
Ok(())
}

View File

@ -21,6 +21,7 @@ pub enum DocumentChild {
BookmarkEnd(BookmarkEnd),
CommentStart(Box<CommentRangeStart>),
CommentEnd(CommentRangeEnd),
StructuredDataTag(StructuredDataTag),
}
impl Serialize for DocumentChild {
@ -65,6 +66,12 @@ impl Serialize for DocumentChild {
t.serialize_field("data", r)?;
t.end()
}
DocumentChild::StructuredDataTag(ref r) => {
let mut t = serializer.serialize_struct("StructuredDataTag", 2)?;
t.serialize_field("type", "structuredDataTag")?;
t.serialize_field("data", r)?;
t.end()
}
}
}
}
@ -179,6 +186,14 @@ impl Document {
self.section_property = self.section_property.even_footer(h, rid);
self
}
pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self {
if t.has_numbering {
self.has_numbering = true
}
self.children.push(DocumentChild::StructuredDataTag(t));
self
}
}
impl BuildXML for DocumentChild {
@ -190,6 +205,7 @@ impl BuildXML for DocumentChild {
DocumentChild::BookmarkEnd(v) => v.build(),
DocumentChild::CommentStart(v) => v.build(),
DocumentChild::CommentEnd(v) => v.build(),
DocumentChild::StructuredDataTag(v) => v.build(),
}
}
}

View File

@ -23,9 +23,9 @@ mod doc_id;
mod doc_var;
mod drawing;
mod font;
mod footer_reference;
mod grid_span;
mod header_reference;
mod footer_reference;
mod highlight;
mod indent;
mod indent_level;
@ -62,6 +62,7 @@ mod section;
mod section_property;
mod shading;
mod start;
mod structured_data_tag;
mod style;
mod sz;
mod sz_cs;
@ -120,9 +121,9 @@ pub use doc_id::*;
pub use doc_var::*;
pub use drawing::*;
pub use font::*;
pub use footer_reference::*;
pub use grid_span::*;
pub use header_reference::*;
pub use footer_reference::*;
pub use highlight::*;
pub use indent::*;
pub use indent_level::*;
@ -159,6 +160,7 @@ pub use section::*;
pub use section_property::*;
pub use shading::*;
pub use start::*;
pub use structured_data_tag::*;
pub use style::*;
pub use sz::*;
pub use sz_cs::*;

View File

@ -0,0 +1,118 @@
use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize;
use super::*;
use crate::documents::BuildXML;
// use crate::types::*;
use crate::xml_builder::*;
#[derive(Serialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct StructuredDataTag {
pub children: Vec<StructuredDataTagChild>,
pub has_numbering: bool,
}
impl Default for StructuredDataTag {
fn default() -> Self {
Self {
children: Vec::new(),
has_numbering: false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum StructuredDataTagChild {
Run(Box<Run>),
Paragraph(Box<Paragraph>),
}
impl BuildXML for StructuredDataTagChild {
fn build(&self) -> Vec<u8> {
match self {
StructuredDataTagChild::Run(v) => v.build(),
StructuredDataTagChild::Paragraph(v) => v.build(),
}
}
}
impl Serialize for StructuredDataTagChild {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
StructuredDataTagChild::Run(ref r) => {
let mut t = serializer.serialize_struct("Run", 2)?;
t.serialize_field("type", "run")?;
t.serialize_field("data", r)?;
t.end()
}
StructuredDataTagChild::Paragraph(ref r) => {
let mut t = serializer.serialize_struct("Paragraph", 2)?;
t.serialize_field("type", "paragraph")?;
t.serialize_field("data", r)?;
t.end()
}
}
}
}
impl StructuredDataTag {
pub fn new() -> Self {
Default::default()
}
pub fn add_run(mut self, run: Run) -> Self {
self.children
.push(StructuredDataTagChild::Run(Box::new(run)));
self
}
pub fn add_paragraph(mut self, p: Paragraph) -> Self {
if p.has_numbering {
self.has_numbering = true
}
self.children
.push(StructuredDataTagChild::Paragraph(Box::new(p)));
self
}
}
impl BuildXML for StructuredDataTag {
fn build(&self) -> Vec<u8> {
XMLBuilder::new()
.open_structured_tag()
.open_structured_tag_property()
.close()
.open_structured_tag_content()
.add_children(&self.children)
.close()
.close()
.build()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(test)]
use pretty_assertions::assert_eq;
use std::str;
#[test]
fn test_sdt() {
let b = StructuredDataTag::new()
.add_run(Run::new().add_text("Hello"))
.build();
assert_eq!(
str::from_utf8(&b).unwrap(),
r#"<w:sdt>
<w:sdtPr />
<w:sdtContent><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:sdtContent>
</w:sdt>"#
);
}
}

View File

@ -191,6 +191,16 @@ impl Docx {
self
}
pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Docx {
if t.has_numbering {
// If this document has numbering, set numberings.xml to document_rels.
// This is because numberings.xml without numbering cause an error on word online.
self.document_rels.has_numberings = true;
}
self.document = self.document.add_structured_data_tag(t);
self
}
pub fn add_bookmark_start(mut self, id: usize, name: impl Into<String>) -> Docx {
self.document = self.document.add_bookmark_start(id, name);
self

View File

@ -72,6 +72,11 @@ impl XMLBuilder {
open!(open_paragraph, "w:p", "w14:paraId");
open!(open_paragraph_property, "w:pPr");
open!(open_doc_defaults, "w:docDefaults");
open!(open_structured_tag, "w:sdt");
open!(open_structured_tag_content, "w:sdtContent");
open!(open_structured_tag_property, "w:sdtPr");
// i.e. <w:outlineLvl ...>
closed_with_usize!(outline_lvl, "w:outlineLvl");
// i.e. <w:name ... >