hello docx 🎉

main
bokuweb 2019-11-07 17:31:04 +09:00
parent d4f08f9703
commit 8699a970a9
11 changed files with 239 additions and 60 deletions

2
.gitignore vendored
View File

@ -3,3 +3,5 @@ target
dist
node_modules
pkg
*.docx#
test.docx

111
Cargo.lock generated
View File

@ -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"

View File

@ -8,6 +8,7 @@ edition = "2018"
[dependencies]
xml-rs = "0.8.0"
zip = "0.5.3"
[dev-dependencies]
pretty_assertions = "*"

View File

@ -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<u8>,
pub core: Vec<u8>,
}

View File

@ -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<Paragraph>,
}
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<u8> {
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#"<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" mc:Ignorable="w14 wp14">
<w:body />
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" mc:Ignorable="w14 wp14">
<w:body><w:p><w:r><w:rPr /><w:t>Hello</w:t></w:r></w:p></w:body>
</w:document>"#
);
}

View File

@ -25,11 +25,11 @@ impl Paragraph {
impl BuildXML for Paragraph {
fn build(&self) -> Vec<u8> {
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()
}
}

View File

@ -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<u8>,
pub rels: Vec<u8>,
pub doc_props: XMLDocProps,
pub styles: Vec<u8>,
pub document: Vec<u8>,
}
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(),
}
}
}

View File

@ -1,17 +0,0 @@
use super::Docx;
use crate::documents::BuildXML;
pub(crate) struct XMLDocument {
content_type: Vec<u8>,
rels: Vec<u8>,
}
impl From<Docx> for XMLDocument {
fn from(doc: Docx) -> XMLDocument {
let content_type = doc.content_type.build();
XMLDocument {
content_type,
rels: vec![],
}
}
}

View File

@ -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#"<h:hello xmlns:h="urn:hello-world">hello world</h:hello>"#
// );
let xml = Docx::new()
.add_paragraph(Paragraph::new().add_run(Run::new("Hello")))
.build();
zip("./test.docx", xml);
}

View File

@ -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(())
}