From 8699a970a935278b100de53dd22564c8d609b75d Mon Sep 17 00:00:00 2001 From: bokuweb Date: Thu, 7 Nov 2019 17:31:04 +0900 Subject: [PATCH] hello docx :tada: --- .gitignore | 2 + Cargo.lock | 111 ++++++++++++++++++ docx-core/Cargo.toml | 1 + docx-core/src/documents/doc_props/mod.rs | 13 ++ docx-core/src/documents/document.rs | 32 +++-- docx-core/src/documents/elements/paragraph.rs | 10 +- docx-core/src/documents/mod.rs | 44 ++++++- docx-core/src/documents/xml_document.rs | 17 --- docx-core/src/documents/xml_docx.rs | 0 docx-core/src/lib.rs | 33 ++---- docx-core/src/zipper/mod.rs | 36 ++++++ 11 files changed, 239 insertions(+), 60 deletions(-) delete mode 100644 docx-core/src/documents/xml_document.rs create mode 100644 docx-core/src/documents/xml_docx.rs create mode 100644 docx-core/src/zipper/mod.rs diff --git a/.gitignore b/.gitignore index 3f700eb..da8c19b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ target dist node_modules pkg +*.docx# +test.docx \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c53a4d9..21f774e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "adler32" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ansi_term" version = "0.11.0" @@ -8,6 +13,42 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bzip2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ctor" version = "0.1.12" @@ -28,6 +69,31 @@ version = "0.1.0" dependencies = [ "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "flate2" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "miniz_oxide" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -38,6 +104,11 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "podio" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "pretty_assertions" version = "0.6.1" @@ -65,6 +136,11 @@ dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "syn" version = "1.0.7" @@ -75,6 +151,16 @@ dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "time" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-xid" version = "0.2.0" @@ -104,17 +190,42 @@ name = "xml-rs" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "zip" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] +"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" +"checksum bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6584aa36f5ad4c9247f5323b0a42f37802b37a836f0ad87084d7a33961abe25f" +"checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +"checksum flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ad3c5233c9a940c8719031b423d7e6c16af66e031cb0420b0896f5245bf181d3" +"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" +"checksum miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6f3f74f726ae935c3f514300cc6773a0c9492abc5e972d42ba0c0ebb88757625" "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" +"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd" "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7bedb3320d0f3035594b0b723c8a28d7d336a3eda3881db79e61d676fb644c" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" +"checksum zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3c21bb410afa2bd823a047f5bda3adb62f51074ac7e06263b2c97ecdd47e9fc6" diff --git a/docx-core/Cargo.toml b/docx-core/Cargo.toml index c4f0004..425fd95 100644 --- a/docx-core/Cargo.toml +++ b/docx-core/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] xml-rs = "0.8.0" +zip = "0.5.3" [dev-dependencies] pretty_assertions = "*" \ No newline at end of file diff --git a/docx-core/src/documents/doc_props/mod.rs b/docx-core/src/documents/doc_props/mod.rs index ba08848..ce98499 100644 --- a/docx-core/src/documents/doc_props/mod.rs +++ b/docx-core/src/documents/doc_props/mod.rs @@ -3,6 +3,7 @@ mod core; pub use self::app::*; pub use self::core::*; +use crate::documents::BuildXML; pub(crate) struct DocProps { app: AppProps, @@ -18,4 +19,16 @@ impl DocProps { let core = CoreProps::new(core_config); DocProps { app, core } } + + pub(crate) fn build(&self) -> XMLDocProps { + XMLDocProps { + app: self.app.build(), + core: self.core.build(), + } + } +} + +pub struct XMLDocProps { + pub app: Vec, + pub core: Vec, } diff --git a/docx-core/src/documents/document.rs b/docx-core/src/documents/document.rs index 05a8568..64406e1 100644 --- a/docx-core/src/documents/document.rs +++ b/docx-core/src/documents/document.rs @@ -1,26 +1,41 @@ -use super::{DocDefaults, Style}; +use super::{DocDefaults, Paragraph, Run, Style}; use crate::documents::BuildXML; use crate::xml_builder::*; use crate::StyleType; -pub struct Document {} +pub struct Document { + paragraphs: Vec, +} impl Document { pub fn new() -> Document { Default::default() } + + pub fn add_paragraph(mut self, p: Paragraph) -> Self { + self.paragraphs.push(p); + self + } } impl Default for Document { fn default() -> Self { - Self {} + Self { + paragraphs: Vec::new(), + } } } impl BuildXML for Document { fn build(&self) -> Vec { let b = XMLBuilder::new(); - b.open_document().open_body().close().close().build() + b.declaration(Some(true)) + .open_document() + .open_body() + .add_children(&self.paragraphs) + .close() + .close() + .build() } } @@ -34,11 +49,14 @@ mod tests { #[test] fn test_document() { - let b = Document::new().build(); + let b = Document::new() + .add_paragraph(Paragraph::new().add_run(Run::new("Hello"))) + .build(); assert_eq!( str::from_utf8(&b).unwrap(), - r#" - + r#" + + Hello "# ); } diff --git a/docx-core/src/documents/elements/paragraph.rs b/docx-core/src/documents/elements/paragraph.rs index ee955f2..bfe8043 100644 --- a/docx-core/src/documents/elements/paragraph.rs +++ b/docx-core/src/documents/elements/paragraph.rs @@ -25,11 +25,11 @@ impl Paragraph { impl BuildXML for Paragraph { fn build(&self) -> Vec { - let mut b = XMLBuilder::new().open_paragraph(); - for r in self.runs.iter() { - b = b.add_child(r) - } - b.close().build() + XMLBuilder::new() + .open_paragraph() + .add_children(&self.runs) + .close() + .build() } } diff --git a/docx-core/src/documents/mod.rs b/docx-core/src/documents/mod.rs index e8530e1..0d030cf 100644 --- a/docx-core/src/documents/mod.rs +++ b/docx-core/src/documents/mod.rs @@ -5,11 +5,10 @@ mod document; mod elements; mod rels; mod styles; -mod xml_document; +pub(crate) use crate::xml_builder::*; pub(crate) use build_xml::*; -pub use crate::xml_builder::*; pub use content_types::*; pub use doc_props::*; pub use document::*; @@ -17,21 +16,56 @@ pub use elements::*; pub use rels::*; pub use styles::*; -pub(crate) struct Docx { +pub struct Docx { content_type: ContentTypes, rels: Rels, doc_props: DocProps, + styles: Styles, + document: Document, } -impl Docx { - pub fn new() -> Docx { +impl Default for Docx { + fn default() -> Self { let content_type = ContentTypes::new(); let rels = Rels::new(); let doc_props = DocProps::new(None, None /* TODO: */); + let styles = Styles::new(); + let document = Document::new(); Docx { content_type, rels, doc_props, + styles, + document, + } + } +} + +pub struct XMLDocx { + pub content_type: Vec, + pub rels: Vec, + pub doc_props: XMLDocProps, + pub styles: Vec, + pub document: Vec, +} + +impl Docx { + pub fn new() -> Docx { + Default::default() + } + + pub fn add_paragraph(mut self, p: Paragraph) -> Docx { + self.document = self.document.add_paragraph(p); + self + } + + pub(crate) fn build(&self) -> XMLDocx { + XMLDocx { + content_type: self.content_type.build(), + rels: self.rels.build(), + doc_props: self.doc_props.build(), + styles: self.styles.build(), + document: self.document.build(), } } } diff --git a/docx-core/src/documents/xml_document.rs b/docx-core/src/documents/xml_document.rs deleted file mode 100644 index 2020abf..0000000 --- a/docx-core/src/documents/xml_document.rs +++ /dev/null @@ -1,17 +0,0 @@ -use super::Docx; -use crate::documents::BuildXML; - -pub(crate) struct XMLDocument { - content_type: Vec, - rels: Vec, -} - -impl From for XMLDocument { - fn from(doc: Docx) -> XMLDocument { - let content_type = doc.content_type.build(); - XMLDocument { - content_type, - rels: vec![], - } - } -} diff --git a/docx-core/src/documents/xml_docx.rs b/docx-core/src/documents/xml_docx.rs new file mode 100644 index 0000000..e69de29 diff --git a/docx-core/src/lib.rs b/docx-core/src/lib.rs index d02c1c6..df873b6 100644 --- a/docx-core/src/lib.rs +++ b/docx-core/src/lib.rs @@ -1,34 +1,15 @@ mod documents; mod types; mod xml_builder; +mod zipper; -use documents::*; -use xml_builder::*; - -use std::fs::File; -use std::io::{self, Write}; +pub use documents::*; use types::*; - -use xml::writer::{EmitterConfig, EventWriter, Result, XmlEvent}; +use zipper::*; pub fn simple() { - let doc = Docx::new(); - let mut file = File::create("./dist/output.xml").unwrap(); - // let mut b = Vec::new(); - // let mut w = EmitterConfig::new() - // .write_document_declaration(false) - // .create_writer(&mut b); - // w.write( - // XmlEvent::start_element("?xml") - // .attr("version", "1.0") - // .attr("encoding", "UTF-8"), - // ); - // // w.write("hello world").unwrap(); - // w.write(XmlEvent::end_element()).unwrap(); - // file.write_all(&b).unwrap(); - // file.flush().unwrap(); - // assert_eq!( - // str::from_utf8(&b).unwrap(), - // r#"hello world"# - // ); + let xml = Docx::new() + .add_paragraph(Paragraph::new().add_run(Run::new("Hello"))) + .build(); + zip("./test.docx", xml); } diff --git a/docx-core/src/zipper/mod.rs b/docx-core/src/zipper/mod.rs new file mode 100644 index 0000000..0643258 --- /dev/null +++ b/docx-core/src/zipper/mod.rs @@ -0,0 +1,36 @@ +use crate::XMLDocx; + +use std::io::prelude::*; +use zip::write::FileOptions; + +pub fn zip(filename: &str, xml: XMLDocx) -> zip::result::ZipResult<()> { + let path = std::path::Path::new(filename); + let file = std::fs::File::create(&path).unwrap(); + let mut zip = zip::ZipWriter::new(file); + + zip.add_directory("word/", Default::default())?; + zip.add_directory("word/_rels", Default::default())?; + zip.add_directory("_rels/", Default::default())?; + zip.add_directory("docProps/", Default::default())?; + + let options = FileOptions::default() + .compression_method(zip::CompressionMethod::Stored) + .unix_permissions(0o755); + + zip.start_file("[Content_Types].xml", options)?; + zip.write_all(&xml.content_type)?; + zip.start_file("_rels/.rels", options)?; + zip.write_all(&xml.rels)?; + zip.start_file("docProps/app.xml", options)?; + zip.write_all(&xml.doc_props.app)?; + zip.start_file("docProps/core.xml", options)?; + zip.write_all(&xml.doc_props.core)?; + zip.start_file("word/_rels/document.xml.rels", options)?; + zip.write_all(&xml.rels)?; + zip.start_file("word/document.xml", options)?; + zip.write_all(&xml.document)?; + zip.start_file("word/styles.xml", options)?; + zip.write_all(&xml.styles)?; + zip.finish()?; + Ok(()) +}