feat: Support comment only paragraph

main
bokuweb 2019-12-05 15:44:18 +09:00
parent e524d6630e
commit 9ba3ef0aea
22 changed files with 228 additions and 78 deletions

View File

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

View File

@ -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<Comment<'a>>,
}
impl Comments {
pub fn new() -> Comments {
impl<'a> Comments<'a> {
pub fn new() -> Self {
Default::default()
}
pub(crate) fn add_comments(&mut self, comments: Vec<Comment<'a>>) {
self.comments = comments;
}
}
impl Default for Comments {
impl<'a> Default for Comments<'a> {
fn default() -> Self {
Self {}
Self { comments: vec![] }
}
}
impl BuildXML for Comments {
impl<'a> BuildXML for Comments<'a> {
fn build(&self) -> Vec<u8> {
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;

View File

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

View File

@ -4,7 +4,7 @@ use crate::xml_builder::*;
#[derive(Debug)]
pub struct Document<'a> {
children: Vec<DocumentChild<'a>>,
pub(crate) children: Vec<DocumentChild<'a>>,
}
#[derive(Debug, Clone)]

View File

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

View File

@ -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<u8> {
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#"<w:comment w:id="123" w:author="unnamed" w:date="1970-01-01T00:00:00Z" />"#
r#"<w:comment w:id="123" w:author="unnamed" w:date="1970-01-01T00:00:00Z" w:initials=""><w:p><w:pPr><w:pStyle w:val="Normal" /><w:rPr /></w:pPr></w:p></w:comment>"#
);
}
}

View File

@ -15,7 +15,15 @@ impl<'a> CommentRangeEnd<'a> {
impl<'a> BuildXML for CommentRangeEnd<'a> {
fn build(&self) -> Vec<u8> {
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#"<w:commentRangeEnd w:id="mockid" />"#
r#"<w:commentRangeEnd w:id="mockid" />
<w:r>
<w:commentReference w:id="mockid" />
</w:r>"#
);
}
}

View File

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

View File

@ -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<ParagraphChild<'a>>,
pub(crate) children: Vec<ParagraphChild<'a>>,
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<ParagraphChild> {
&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#"<w:p><w:pPr><w:pStyle w:val="Normal" /><w:rPr /></w:pPr><w:bookmarkStart w:id="1234-5678" w:name="article" /><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r><w:bookmarkEnd w:id="1234-5678" /></w:p>"#
);
}
#[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#"<w:p><w:pPr><w:pStyle w:val="Normal" /><w:rPr /></w:pPr><w:commentRangeStart w:id="1234-5678" /><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r><w:commentRangeEnd w:id="1234-5678" />
<w:r>
<w:commentReference w:id="1234-5678" />
</w:r></w:p>"#
);
}
}

View File

@ -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<Comment<'a>>,
children: Vec<RunChild>,
children: Vec<RunChild<'a>>,
}
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> {

View File

@ -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<String>) -> 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<u8> {
XMLBuilder::new().text(&self.text, true).build()
}

View File

@ -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<Comment<'a>> = 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);
}
}

View File

@ -11,6 +11,7 @@ pub struct XMLDocx {
pub doc_props: XMLDocProps,
pub styles: Vec<u8>,
pub document: Vec<u8>,
pub comments: Vec<u8>,
pub document_rels: Vec<u8>,
pub settings: Vec<u8>,
pub font_table: Vec<u8>,

View File

@ -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)]

View File

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

View File

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

View File

@ -20,7 +20,7 @@ impl Docx {
self
}
pub fn build(&self) -> Result<Vec<u8>, JsValue> {
pub fn build(&mut self) -> Result<Vec<u8>, JsValue> {
let buf = Vec::new();
let mut cur = std::io::Cursor::new(buf);
let res = self.0.build().pack(&mut cur);

View File

@ -1,3 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Override PartName="/_rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/><Override PartName="/word/fontTable.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"/><Override PartName="/word/_rels/document.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Override PartName="/_rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
<Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/>
<Override PartName="/word/fontTable.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"/>
<Override PartName="/word/_rels/document.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
</Types>

View File

@ -1,3 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>

View File

@ -1,2 +1,12 @@
<?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"><Template></Template><TotalTime>1</TotalTime><Application>LibreOffice/6.0.7.3$Linux_X86_64 LibreOffice_project/00m0$Build-3</Application><Pages>1</Pages><Words>5</Words><Characters>28</Characters><CharactersWithSpaces>33</CharactersWithSpaces><Paragraphs>1</Paragraphs></Properties>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"
xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
<Template></Template>
<TotalTime>1</TotalTime>
<Application>LibreOffice/6.0.7.3$Linux_X86_64 LibreOffice_project/00m0$Build-3</Application>
<Pages>1</Pages>
<Words>5</Words>
<Characters>28</Characters>
<CharactersWithSpaces>33</CharactersWithSpaces>
<Paragraphs>1</Paragraphs>
</Properties>

View File

@ -1,2 +1,16 @@
<?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-12-04T16:57:40Z</dcterms:created><dc:creator></dc:creator><dc:description></dc:description><dc:language>ja-JP</dc:language><cp:lastModifiedBy></cp:lastModifiedBy><dcterms:modified xsi:type="dcterms:W3CDTF">2019-12-04T18:22:46Z</dcterms:modified><cp:revision>2</cp:revision><dc:subject></dc:subject><dc: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-12-04T16:57:40Z</dcterms:created>
<dc:creator></dc:creator>
<dc:description></dc:description>
<dc:language>ja-JP</dc:language>
<cp:lastModifiedBy></cp:lastModifiedBy>
<dcterms:modified xsi:type="dcterms:W3CDTF">2019-12-04T18:22:46Z</dcterms:modified>
<cp:revision>2</cp:revision>
<dc:subject></dc:subject>
<dc:title></dc:title>
</cp:coreProperties>

View File

@ -1,3 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" Target="comments.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" Target="comments.xml"/>
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/>
<Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/>
</Relationships>