From 9ba3ef0aea187bfd8978ea8ce38ff1e03bea933a Mon Sep 17 00:00:00 2001 From: bokuweb Date: Thu, 5 Dec 2019 15:44:18 +0900 Subject: [PATCH] feat: Support comment only paragraph --- docx-core/src/documents/comment_id.rs | 21 -------- docx-core/src/documents/comments.rs | 36 ++++++++------ docx-core/src/documents/content_types.rs | 4 ++ docx-core/src/documents/document.rs | 2 +- docx-core/src/documents/document_rels.rs | 7 ++- docx-core/src/documents/elements/comment.rs | 19 ++++++- .../documents/elements/comment_range_end.rs | 15 +++++- .../documents/elements/comment_range_start.rs | 15 ++++-- docx-core/src/documents/elements/paragraph.rs | 49 ++++++++++++++++--- docx-core/src/documents/elements/run.rs | 15 ++---- docx-core/src/documents/elements/text.rs | 10 ++-- docx-core/src/documents/mod.rs | 26 +++++++++- docx-core/src/documents/xml_docx.rs | 1 + docx-core/src/xml_builder/elements.rs | 10 +++- docx-core/src/zipper/mod.rs | 2 + docx-core/tests/lib.rs | 22 +++++++++ docx-wasm/src/lib.rs | 2 +- fixtures/comment/[Content_Types].xml | 11 ++++- fixtures/comment/_rels/.rels | 5 +- fixtures/comment/docProps/app.xml | 12 ++++- fixtures/comment/docProps/core.xml | 16 +++++- fixtures/comment/word/_rels/document.xml.rels | 6 ++- 22 files changed, 228 insertions(+), 78 deletions(-) delete mode 100644 docx-core/src/documents/comment_id.rs diff --git a/docx-core/src/documents/comment_id.rs b/docx-core/src/documents/comment_id.rs deleted file mode 100644 index 626af01..0000000 --- a/docx-core/src/documents/comment_id.rs +++ /dev/null @@ -1,21 +0,0 @@ -#[allow(unused)] -use std::sync::atomic::{AtomicUsize, Ordering}; - -#[allow(dead_code)] -static COMMENT_ID: AtomicUsize = AtomicUsize::new(0); - -#[cfg(not(test))] -pub trait CommentId { - fn generate(&self) -> String { - let id = COMMENT_ID.load(Ordering::Relaxed); - COMMENT_ID.store(id + 1, Ordering::Relaxed); - format!("{}", id) - } -} - -#[cfg(test)] -pub trait CommentId { - fn generate(&self) -> &str { - "123" - } -} diff --git a/docx-core/src/documents/comments.rs b/docx-core/src/documents/comments.rs index 31c0db9..2ab8d21 100644 --- a/docx-core/src/documents/comments.rs +++ b/docx-core/src/documents/comments.rs @@ -1,36 +1,40 @@ -use super::{Paragraph, Table}; +use super::Comment; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug)] -pub struct Comments {} +pub struct Comments<'a> { + comments: Vec>, +} -impl Comments { - pub fn new() -> Comments { +impl<'a> Comments<'a> { + pub fn new() -> Self { Default::default() } -} - -impl Default for Comments { - fn default() -> Self { - Self {} + pub(crate) fn add_comments(&mut self, comments: Vec>) { + self.comments = comments; } } -impl BuildXML for Comments { +impl<'a> Default for Comments<'a> { + fn default() -> Self { + Self { comments: vec![] } + } +} + +impl<'a> BuildXML for Comments<'a> { fn build(&self) -> Vec { - XMLBuilder::new() - .declaration(Some(true)) - .open_comments() - .close() - .build() + let mut b = XMLBuilder::new().declaration(Some(true)).open_comments(); + for c in &self.comments { + b = b.add_child(c) + } + b.close().build() } } #[cfg(test)] mod tests { - use super::super::Run; use super::*; #[cfg(test)] use pretty_assertions::assert_eq; diff --git a/docx-core/src/documents/content_types.rs b/docx-core/src/documents/content_types.rs index 4fbeb49..b0361c8 100644 --- a/docx-core/src/documents/content_types.rs +++ b/docx-core/src/documents/content_types.rs @@ -47,6 +47,10 @@ impl BuildXML for ContentTypes { "/word/styles.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", ) + .add_override( + "/word/comments.xml", + "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml", + ) .close() .build() } diff --git a/docx-core/src/documents/document.rs b/docx-core/src/documents/document.rs index eb3f758..e7e13bc 100644 --- a/docx-core/src/documents/document.rs +++ b/docx-core/src/documents/document.rs @@ -4,7 +4,7 @@ use crate::xml_builder::*; #[derive(Debug)] pub struct Document<'a> { - children: Vec>, + pub(crate) children: Vec>, } #[derive(Debug, Clone)] diff --git a/docx-core/src/documents/document_rels.rs b/docx-core/src/documents/document_rels.rs index dc873e8..ccbfe97 100644 --- a/docx-core/src/documents/document_rels.rs +++ b/docx-core/src/documents/document_rels.rs @@ -22,11 +22,16 @@ impl BuildXML for DocumentRels { ) .relationship( "rId2", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", + "comments.xml", + ) + .relationship( + "rId3", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable", "fontTable.xml", ) .relationship( - "rId3", + "rId4", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings", "settings.xml", ) diff --git a/docx-core/src/documents/elements/comment.rs b/docx-core/src/documents/elements/comment.rs index de912d1..7703e45 100644 --- a/docx-core/src/documents/elements/comment.rs +++ b/docx-core/src/documents/elements/comment.rs @@ -28,16 +28,31 @@ impl<'a> Comment<'a> { } } + pub fn author(mut self, author: &'a str) -> Comment<'a> { + self.author = author; + self + } + + pub fn date(mut self, date: &'a str) -> Comment<'a> { + self.date = date; + self + } + pub fn paragraph(mut self, p: Paragraph<'a>) -> Comment<'a> { self.paragraph = p; self } + + pub fn id(&self) -> &'a str { + self.id + } } impl<'a> BuildXML for Comment<'a> { fn build(&self) -> Vec { XMLBuilder::new() - .open_comment(&self.id, self.author, self.date) + .open_comment(&self.id, self.author, self.date, "") + .add_child(&self.paragraph) .close() .build() } @@ -56,7 +71,7 @@ mod tests { let b = Comment::new("123").build(); assert_eq!( str::from_utf8(&b).unwrap(), - r#""# + r#""# ); } } diff --git a/docx-core/src/documents/elements/comment_range_end.rs b/docx-core/src/documents/elements/comment_range_end.rs index 6b27946..56ebaf0 100644 --- a/docx-core/src/documents/elements/comment_range_end.rs +++ b/docx-core/src/documents/elements/comment_range_end.rs @@ -15,7 +15,15 @@ impl<'a> CommentRangeEnd<'a> { impl<'a> BuildXML for CommentRangeEnd<'a> { fn build(&self) -> Vec { let b = XMLBuilder::new(); - b.comment_range_end(&self.id).build() + b.open_run() + .open_run_property() + .close() + .close() + .comment_range_end(&self.id) + .open_run() + .comment_reference(&self.id) + .close() + .build() } } @@ -33,7 +41,10 @@ mod tests { let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), - r#""# + r#" + + +"# ); } } diff --git a/docx-core/src/documents/elements/comment_range_start.rs b/docx-core/src/documents/elements/comment_range_start.rs index 68f3dcc..3a37d16 100644 --- a/docx-core/src/documents/elements/comment_range_start.rs +++ b/docx-core/src/documents/elements/comment_range_start.rs @@ -1,14 +1,23 @@ +use super::Comment; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone)] pub struct CommentRangeStart<'a> { id: &'a str, + comment: Comment<'a>, } impl<'a> CommentRangeStart<'a> { - pub fn new(id: &'a str) -> CommentRangeStart<'a> { - CommentRangeStart { id } + pub fn new(comment: Comment<'a>) -> CommentRangeStart<'a> { + CommentRangeStart { + id: comment.id(), + comment, + } + } + + pub(crate) fn comment(&self) -> Comment<'a> { + self.comment.clone() } } @@ -29,7 +38,7 @@ mod tests { #[test] fn test_comment_range_start() { - let c = CommentRangeStart::new("mockid"); + let c = CommentRangeStart::new(Comment::new("mockid")); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), diff --git a/docx-core/src/documents/elements/paragraph.rs b/docx-core/src/documents/elements/paragraph.rs index ab83433..41849c0 100644 --- a/docx-core/src/documents/elements/paragraph.rs +++ b/docx-core/src/documents/elements/paragraph.rs @@ -1,11 +1,11 @@ -use super::{BookmarkEnd, BookmarkStart, Delete, Insert, ParagraphProperty, Run}; +use super::*; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone)] pub struct Paragraph<'a> { - children: Vec>, + pub(crate) children: Vec>, property: ParagraphProperty, attrs: Vec<(String, String)>, } @@ -27,6 +27,8 @@ pub enum ParagraphChild<'a> { Delete(Delete<'a>), BookmarkStart(BookmarkStart<'a>), BookmarkEnd(BookmarkEnd<'a>), + CommentStart(CommentRangeStart<'a>), + CommentEnd(CommentRangeEnd<'a>), } impl<'a> BuildXML for ParagraphChild<'a> { @@ -37,6 +39,8 @@ impl<'a> BuildXML for ParagraphChild<'a> { ParagraphChild::Delete(v) => v.build(), ParagraphChild::BookmarkStart(v) => v.build(), ParagraphChild::BookmarkEnd(v) => v.build(), + ParagraphChild::CommentStart(v) => v.build(), + ParagraphChild::CommentEnd(v) => v.build(), } } } @@ -46,6 +50,10 @@ impl<'a> Paragraph<'a> { Default::default() } + pub fn children(&self) -> &Vec { + &self.children + } + pub fn add_run(mut self, run: Run<'a>) -> Paragraph<'a> { self.children.push(ParagraphChild::Run(run)); self @@ -78,16 +86,25 @@ impl<'a> Paragraph<'a> { self } + pub fn add_comment_start(mut self, comment: Comment<'a>) -> Paragraph<'a> { + self.children + .push(ParagraphChild::CommentStart(CommentRangeStart::new( + comment, + ))); + self + } + + pub fn add_comment_end(mut self, id: &'a str) -> Paragraph<'a> { + self.children + .push(ParagraphChild::CommentEnd(CommentRangeEnd::new(id))); + self + } + pub fn align(mut self, alignment_type: AlignmentType) -> Paragraph<'a> { self.property = self.property.align(alignment_type); self } - // pub fn size(mut self, size: usize) -> Paragraph<'a> { - // self.children = self.children.into_iter().map(|r| r.size(size)).collect(); - // self - // } - pub fn style(mut self, style_id: &str) -> Paragraph<'a> { self.property = self.property.style(style_id); self @@ -157,4 +174,22 @@ mod tests { r#"Hello"# ); } + + #[test] + fn test_comment() { + let b = Paragraph::new() + .add_comment_start( + Comment::new("1234-5678"), // .paragraph(Paragraph::new().add_run(Run::new().add_text("Comment"))), + ) + .add_run(Run::new().add_text("Hello")) + .add_comment_end("1234-5678") + .build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#"Hello + + +"# + ); + } } diff --git a/docx-core/src/documents/elements/run.rs b/docx-core/src/documents/elements/run.rs index 58beaed..09c4c20 100644 --- a/docx-core/src/documents/elements/run.rs +++ b/docx-core/src/documents/elements/run.rs @@ -1,4 +1,4 @@ -use super::{Break, Comment, DeleteText, RunProperty, Tab, Text}; +use super::{Break, DeleteText, RunProperty, Tab, Text}; use crate::documents::BuildXML; use crate::types::BreakType; use crate::xml_builder::*; @@ -6,8 +6,7 @@ use crate::xml_builder::*; #[derive(Debug, Clone)] pub struct Run<'a> { run_property: RunProperty, - comment: Option>, - children: Vec, + children: Vec>, } impl<'a> Default for Run<'a> { @@ -16,14 +15,13 @@ impl<'a> Default for Run<'a> { Self { run_property, children: vec![], - comment: None, } } } #[derive(Debug, Clone)] -pub enum RunChild { - Text(Text), +pub enum RunChild<'a> { + Text(Text<'a>), DeleteText(DeleteText), Tab(Tab), Break(Break), @@ -85,11 +83,6 @@ impl<'a> Run<'a> { self.run_property = self.run_property.underline(line_type); self } - - pub fn comment(mut self, line_type: &'a str) -> Run<'a> { - // self.run_property = self.run_property.underline(line_type); - self - } } impl<'a> BuildXML for Run<'a> { diff --git a/docx-core/src/documents/elements/text.rs b/docx-core/src/documents/elements/text.rs index 65e0ad6..4c6ac2d 100644 --- a/docx-core/src/documents/elements/text.rs +++ b/docx-core/src/documents/elements/text.rs @@ -2,13 +2,13 @@ use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone)] -pub struct Text { - text: String, +pub struct Text<'a> { + text: &'a str, preserve_space: bool, } -impl Text { - pub fn new(text: impl Into) -> Text { +impl<'a> Text<'a> { + pub fn new(text: &'a str) -> Text { Text { text: text.into(), preserve_space: true, @@ -16,7 +16,7 @@ impl Text { } } -impl BuildXML for Text { +impl<'a> BuildXML for Text<'a> { fn build(&self) -> Vec { XMLBuilder::new().text(&self.text, true).build() } diff --git a/docx-core/src/documents/mod.rs b/docx-core/src/documents/mod.rs index eb84ddb..9322355 100644 --- a/docx-core/src/documents/mod.rs +++ b/docx-core/src/documents/mod.rs @@ -35,7 +35,7 @@ pub struct Docx<'a> { doc_props: DocProps<'a>, styles: Styles, document: Document<'a>, - comments: Comments, + comments: Comments<'a>, settings: Settings, font_table: FontTable, } @@ -80,16 +80,38 @@ impl<'a> Docx<'a> { self } - pub fn build(&self) -> XMLDocx { + pub fn build(&mut self) -> XMLDocx { + self.update_comments(); 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(), + comments: self.comments.build(), document_rels: self.document_rels.build(), settings: self.settings.build(), font_table: self.font_table.build(), } } + + fn update_comments(&mut self) { + let mut comments: Vec> = vec![]; + for child in &self.document.children { + match child { + DocumentChild::Paragraph(p) => { + for child in &p.children { + match child { + ParagraphChild::CommentStart(c) => { + comments.push(c.comment()); + } + _ => {} + } + } + } + _ => {} + } + } + self.comments.add_comments(comments); + } } diff --git a/docx-core/src/documents/xml_docx.rs b/docx-core/src/documents/xml_docx.rs index 3b256d9..d7fb082 100644 --- a/docx-core/src/documents/xml_docx.rs +++ b/docx-core/src/documents/xml_docx.rs @@ -11,6 +11,7 @@ pub struct XMLDocx { pub doc_props: XMLDocProps, pub styles: Vec, pub document: Vec, + pub comments: Vec, pub document_rels: Vec, pub settings: Vec, pub font_table: Vec, diff --git a/docx-core/src/xml_builder/elements.rs b/docx-core/src/xml_builder/elements.rs index 788dd14..05b0811 100644 --- a/docx-core/src/xml_builder/elements.rs +++ b/docx-core/src/xml_builder/elements.rs @@ -173,7 +173,15 @@ impl XMLBuilder { closed_el!(comment_range_start, "w:commentRangeStart", "w:id"); closed_el!(comment_range_end, "w:commentRangeEnd", "w:id"); - opened_el!(open_comment, "w:comment", "w:id", "w:author", "w:date"); + closed_el!(comment_reference, "w:commentReference", "w:id"); + opened_el!( + open_comment, + "w:comment", + "w:id", + "w:author", + "w:date", + "w:initials" + ); } #[cfg(test)] diff --git a/docx-core/src/zipper/mod.rs b/docx-core/src/zipper/mod.rs index 6ee606f..c894fe7 100644 --- a/docx-core/src/zipper/mod.rs +++ b/docx-core/src/zipper/mod.rs @@ -37,6 +37,8 @@ where zip.write_all(&xml.settings)?; zip.start_file("word/fontTable.xml", options)?; zip.write_all(&xml.font_table)?; + zip.start_file("word/comments.xml", options)?; + zip.write_all(&xml.comments)?; zip.finish()?; Ok(()) } diff --git a/docx-core/tests/lib.rs b/docx-core/tests/lib.rs index 7a1cc50..c7ece58 100644 --- a/docx-core/tests/lib.rs +++ b/docx-core/tests/lib.rs @@ -256,3 +256,25 @@ pub fn highlight() -> Result<(), DocxError> { .pack(file)?; Ok(()) } + +#[test] +pub fn comments() -> Result<(), DocxError> { + let path = std::path::Path::new("./tests/output/comments.docx"); + let file = std::fs::File::create(&path).unwrap(); + Docx::new() + .add_paragraph( + Paragraph::new() + .add_comment_start( + Comment::new("1") + .author("bokuweb") + .date("2019-01-01T00:00:00Z") + .paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))), + ) + .add_run(Run::new().add_text("Hello").highlight("cyan")) + .add_run(Run::new().add_text(" World!").highlight("yellow")) + .add_comment_end("1"), + ) + .build() + .pack(file)?; + Ok(()) +} diff --git a/docx-wasm/src/lib.rs b/docx-wasm/src/lib.rs index a5f536e..d94d491 100644 --- a/docx-wasm/src/lib.rs +++ b/docx-wasm/src/lib.rs @@ -20,7 +20,7 @@ impl Docx { self } - pub fn build(&self) -> Result, JsValue> { + pub fn build(&mut self) -> Result, JsValue> { let buf = Vec::new(); let mut cur = std::io::Cursor::new(buf); let res = self.0.build().pack(&mut cur); diff --git a/fixtures/comment/[Content_Types].xml b/fixtures/comment/[Content_Types].xml index 1f64908..3c86d5b 100644 --- a/fixtures/comment/[Content_Types].xml +++ b/fixtures/comment/[Content_Types].xml @@ -1,3 +1,12 @@ - + + + + + + + + + + \ No newline at end of file diff --git a/fixtures/comment/_rels/.rels b/fixtures/comment/_rels/.rels index f0b72e7..e13b32e 100644 --- a/fixtures/comment/_rels/.rels +++ b/fixtures/comment/_rels/.rels @@ -1,3 +1,6 @@ - + + + + \ No newline at end of file diff --git a/fixtures/comment/docProps/app.xml b/fixtures/comment/docProps/app.xml index 8962e3b..1ca052a 100644 --- a/fixtures/comment/docProps/app.xml +++ b/fixtures/comment/docProps/app.xml @@ -1,2 +1,12 @@ -1LibreOffice/6.0.7.3$Linux_X86_64 LibreOffice_project/00m0$Build-31528331 \ No newline at end of file + + + 1 + LibreOffice/6.0.7.3$Linux_X86_64 LibreOffice_project/00m0$Build-3 + 1 + 5 + 28 + 33 + 1 + \ No newline at end of file diff --git a/fixtures/comment/docProps/core.xml b/fixtures/comment/docProps/core.xml index f329c85..5e0c785 100644 --- a/fixtures/comment/docProps/core.xml +++ b/fixtures/comment/docProps/core.xml @@ -1,2 +1,16 @@ -2019-12-04T16:57:40Zja-JP2019-12-04T18:22:46Z2 \ No newline at end of file + + 2019-12-04T16:57:40Z + + + ja-JP + + 2019-12-04T18:22:46Z + 2 + + + \ No newline at end of file diff --git a/fixtures/comment/word/_rels/document.xml.rels b/fixtures/comment/word/_rels/document.xml.rels index f8607e1..45ff3f8 100644 --- a/fixtures/comment/word/_rels/document.xml.rels +++ b/fixtures/comment/word/_rels/document.xml.rels @@ -1,3 +1,7 @@ - + + + + + \ No newline at end of file