From 35e6e1ecc3031a0caad1c12e207126a734ef9061 Mon Sep 17 00:00:00 2001 From: bokuweb Date: Fri, 15 Nov 2019 18:15:43 +0900 Subject: [PATCH] feat: Support insert / delete --- README.md | 4 + docx-core/src/documents/document.rs | 22 ++--- docx-core/src/documents/elements/delete.rs | 60 +++++++++++++ .../src/documents/elements/delete_text.rs | 41 +++++++++ docx-core/src/documents/elements/insert.rs | 60 +++++++++++++ docx-core/src/documents/elements/mod.rs | 6 ++ docx-core/src/documents/elements/paragraph.rs | 81 +++++++++++------- docx-core/src/documents/elements/run.rs | 9 +- docx-core/src/documents/elements/table.rs | 12 +-- .../src/documents/elements/table_cell.rs | 20 ++--- docx-core/src/documents/elements/table_row.rs | 10 +-- docx-core/src/documents/history_id.rs | 21 +++++ docx-core/src/documents/mod.rs | 10 ++- docx-core/src/xml_builder/elements.rs | 16 ++++ docx-core/tests/lib.rs | 21 +++-- .../history_libre_office/[Content_Types].xml | 3 + fixtures/history_libre_office/_rels/.rels | 3 + .../history_libre_office/docProps/app.xml | 2 + .../history_libre_office/docProps/core.xml | 2 + fixtures/history_libre_office/history.docx | Bin 0 -> 4369 bytes .../word/_rels/document.xml.rels | 3 + .../history_libre_office/word/document.xml | 54 ++++++++++++ .../history_libre_office/word/fontTable.xml | 2 + .../history_libre_office/word/settings.xml | 2 + fixtures/history_libre_office/word/styles.xml | 2 + 25 files changed, 393 insertions(+), 73 deletions(-) create mode 100644 docx-core/src/documents/elements/delete.rs create mode 100644 docx-core/src/documents/elements/delete_text.rs create mode 100644 docx-core/src/documents/elements/insert.rs create mode 100644 docx-core/src/documents/history_id.rs create mode 100644 fixtures/history_libre_office/[Content_Types].xml create mode 100644 fixtures/history_libre_office/_rels/.rels create mode 100644 fixtures/history_libre_office/docProps/app.xml create mode 100644 fixtures/history_libre_office/docProps/core.xml create mode 100644 fixtures/history_libre_office/history.docx create mode 100644 fixtures/history_libre_office/word/_rels/document.xml.rels create mode 100644 fixtures/history_libre_office/word/document.xml create mode 100644 fixtures/history_libre_office/word/fontTable.xml create mode 100644 fixtures/history_libre_office/word/settings.xml create mode 100644 fixtures/history_libre_office/word/styles.xml diff --git a/README.md b/README.md index 936ea23..af04829 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ # docx-rs [![GitHub Actions Status](https://github.com/bokuweb/docx-rs/workflows/Continuous%20Integration/badge.svg)](https://github.com/bokuweb/docx-rs/actions) + +# Features + +- [x] Paragraph diff --git a/docx-core/src/documents/document.rs b/docx-core/src/documents/document.rs index b852ef8..eb3f758 100644 --- a/docx-core/src/documents/document.rs +++ b/docx-core/src/documents/document.rs @@ -3,33 +3,33 @@ use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug)] -pub struct Document { - children: Vec, +pub struct Document<'a> { + children: Vec>, } #[derive(Debug, Clone)] -pub enum DocumentChild { - Paragraph(Paragraph), - Table(Table), +pub enum DocumentChild<'a> { + Paragraph(Paragraph<'a>), + Table(Table<'a>), } -impl Document { - pub fn new() -> Document { +impl<'a> Document<'a> { + pub fn new() -> Document<'a> { Default::default() } - pub fn add_paragraph(mut self, p: Paragraph) -> Self { + pub fn add_paragraph(mut self, p: Paragraph<'a>) -> Self { self.children.push(DocumentChild::Paragraph(p)); self } - pub fn add_table(mut self, t: Table) -> Self { + pub fn add_table(mut self, t: Table<'a>) -> Self { self.children.push(DocumentChild::Table(t)); self } } -impl Default for Document { +impl<'a> Default for Document<'a> { fn default() -> Self { Self { children: Vec::new(), @@ -37,7 +37,7 @@ impl Default for Document { } } -impl BuildXML for Document { +impl<'a> BuildXML for Document<'a> { fn build(&self) -> Vec { let mut b = XMLBuilder::new() .declaration(Some(true)) diff --git a/docx-core/src/documents/elements/delete.rs b/docx-core/src/documents/elements/delete.rs new file mode 100644 index 0000000..c840df2 --- /dev/null +++ b/docx-core/src/documents/elements/delete.rs @@ -0,0 +1,60 @@ +use crate::documents::{BuildXML, HistoryId, Run}; +use crate::xml_builder::*; + +#[derive(Debug, Clone)] +pub struct Delete<'a> { + author: &'a str, + date: &'a str, + run: Run, +} + +impl<'a> Default for Delete<'a> { + fn default() -> Delete<'a> { + Delete { + author: "unnamed", + date: "1970-01-01T00:00:00Z", + run: Run::new(), + } + } +} + +impl<'a> Delete<'a> { + pub fn new() -> Delete<'a> { + Default::default() + } + + pub fn run(mut self, run: Run) -> Delete<'a> { + self.run = run; + self + } +} + +impl<'a> HistoryId for Delete<'a> {} + +impl<'a> BuildXML for Delete<'a> { + fn build(&self) -> Vec { + XMLBuilder::new() + .open_delete(&self.generate(), self.author, self.date) + .add_child(&self.run) + .close() + .build() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[cfg(test)] + use pretty_assertions::assert_eq; + use std::str; + + #[test] + fn test_delete_default() { + let b = Delete::new().build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#""# + ); + } +} diff --git a/docx-core/src/documents/elements/delete_text.rs b/docx-core/src/documents/elements/delete_text.rs new file mode 100644 index 0000000..848f3e7 --- /dev/null +++ b/docx-core/src/documents/elements/delete_text.rs @@ -0,0 +1,41 @@ +use crate::documents::BuildXML; +use crate::xml_builder::*; + +#[derive(Debug, Clone)] +pub struct DeleteText { + text: String, + preserve_space: bool, +} + +impl DeleteText { + pub fn new(text: impl Into) -> DeleteText { + DeleteText { + text: text.into(), + preserve_space: true, + } + } +} + +impl BuildXML for DeleteText { + fn build(&self) -> Vec { + XMLBuilder::new().delete_text(&self.text, true).build() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[cfg(test)] + use pretty_assertions::assert_eq; + use std::str; + + #[test] + fn test_build() { + let b = DeleteText::new("Hello").build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#"Hello"# + ); + } +} diff --git a/docx-core/src/documents/elements/insert.rs b/docx-core/src/documents/elements/insert.rs new file mode 100644 index 0000000..5cba8df --- /dev/null +++ b/docx-core/src/documents/elements/insert.rs @@ -0,0 +1,60 @@ +use crate::documents::{BuildXML, HistoryId, Run}; +use crate::xml_builder::*; + +#[derive(Debug, Clone)] +pub struct Insert<'a> { + author: &'a str, + date: &'a str, + run: Run, +} + +impl<'a> Default for Insert<'a> { + fn default() -> Insert<'a> { + Insert { + author: "unnamed", + date: "1970-01-01T00:00:00Z", + run: Run::new(), + } + } +} + +impl<'a> Insert<'a> { + pub fn new() -> Insert<'a> { + Default::default() + } + + pub fn run(mut self, run: Run) -> Insert<'a> { + self.run = run; + self + } +} + +impl<'a> HistoryId for Insert<'a> {} + +impl<'a> BuildXML for Insert<'a> { + fn build(&self) -> Vec { + XMLBuilder::new() + .open_insert(&self.generate(), self.author, self.date) + .add_child(&self.run) + .close() + .build() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[cfg(test)] + use pretty_assertions::assert_eq; + use std::str; + + #[test] + fn test_ins_default() { + let b = Insert::new().build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#""# + ); + } +} diff --git a/docx-core/src/documents/elements/mod.rs b/docx-core/src/documents/elements/mod.rs index 489ce4e..51e05cd 100644 --- a/docx-core/src/documents/elements/mod.rs +++ b/docx-core/src/documents/elements/mod.rs @@ -4,11 +4,14 @@ mod bold_cs; mod br; mod color; mod default_tab_stop; +mod delete; +mod delete_text; mod doc_defaults; mod font; mod grid_span; mod highlight; mod indent; +mod insert; mod italic; mod italic_cs; mod justification; @@ -48,11 +51,14 @@ pub use bold_cs::*; pub use br::*; pub use color::*; pub use default_tab_stop::*; +pub use delete::*; +pub use delete_text::*; pub use doc_defaults::*; pub use font::*; pub use grid_span::*; pub use highlight::*; pub use indent::*; +pub use insert::*; pub use italic::*; pub use italic_cs::*; pub use justification::*; diff --git a/docx-core/src/documents/elements/paragraph.rs b/docx-core/src/documents/elements/paragraph.rs index 5589ee5..9fcf45c 100644 --- a/docx-core/src/documents/elements/paragraph.rs +++ b/docx-core/src/documents/elements/paragraph.rs @@ -1,67 +1,98 @@ -use super::{ParagraphProperty, Run}; +use super::{Delete, Insert, ParagraphProperty, Run}; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone)] -pub struct Paragraph { - runs: Vec, +pub struct Paragraph<'a> { + children: Vec>, property: ParagraphProperty, attrs: Vec<(String, String)>, } -impl Default for Paragraph { +impl<'a> Default for Paragraph<'a> { fn default() -> Self { Self { - runs: Vec::new(), + children: Vec::new(), property: ParagraphProperty::new(), attrs: Vec::new(), } } } -impl Paragraph { - pub fn new() -> Paragraph { +#[derive(Debug, Clone)] +pub enum ParagraphChild<'a> { + Run(Run), + Insert(Insert<'a>), + Delete(Delete<'a>), +} + +impl<'a> BuildXML for ParagraphChild<'a> { + fn build(&self) -> Vec { + match self { + ParagraphChild::Run(v) => v.build(), + ParagraphChild::Insert(v) => v.build(), + ParagraphChild::Delete(v) => v.build(), + } + } +} + +impl<'a> Paragraph<'a> { + pub fn new() -> Paragraph<'a> { Default::default() } - pub fn add_run(mut self, run: Run) -> Paragraph { - self.runs.push(run); + pub fn add_run(mut self, run: Run) -> Paragraph<'a> { + self.children.push(ParagraphChild::Run(run)); self } - pub fn add_attr(mut self, key: impl Into, val: impl Into) -> Paragraph { + pub fn add_insert(mut self, insert: Insert<'a>) -> Paragraph<'a> { + self.children.push(ParagraphChild::Insert(insert)); + self + } + + pub fn add_delete(mut self, delete: Delete<'a>) -> Paragraph<'a> { + self.children.push(ParagraphChild::Delete(delete)); + self + } + + pub fn add_attr(mut self, key: impl Into, val: impl Into) -> Paragraph<'a> { self.attrs.push((key.into(), val.into())); self } - pub fn align(mut self, alignment_type: AlignmentType) -> Paragraph { + 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 { - self.runs = self.runs.into_iter().map(|r| r.size(size)).collect(); - 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 { + pub fn style(mut self, style_id: &str) -> Paragraph<'a> { self.property = self.property.style(style_id); self } - pub fn indent(mut self, left: usize, special_indent: Option) -> Paragraph { + pub fn indent( + mut self, + left: usize, + special_indent: Option, + ) -> Paragraph<'a> { self.property = self.property.indent(left, special_indent); self } } -impl BuildXML for Paragraph { +impl<'a> BuildXML for Paragraph<'a> { fn build(&self) -> Vec { XMLBuilder::new() .open_paragraph(&self.attrs) .add_child(&self.property) - .add_children(&self.runs) + .add_children(&self.children) .close() .build() } @@ -86,18 +117,6 @@ mod tests { ); } - #[test] - fn test_paragraph_size() { - let b = Paragraph::new() - .add_run(Run::new().add_text("Hello")) - .size(60) - .build(); - assert_eq!( - str::from_utf8(&b).unwrap(), - r#"Hello"# - ); - } - #[test] fn test_custom_attr() { let b = Paragraph::new() diff --git a/docx-core/src/documents/elements/run.rs b/docx-core/src/documents/elements/run.rs index 08a1bad..dc7a58a 100644 --- a/docx-core/src/documents/elements/run.rs +++ b/docx-core/src/documents/elements/run.rs @@ -1,4 +1,4 @@ -use super::{Break, RunProperty, Tab, Text}; +use super::{Break, DeleteText, RunProperty, Tab, Text}; use crate::documents::BuildXML; use crate::types::BreakType; use crate::xml_builder::*; @@ -22,6 +22,7 @@ impl Default for Run { #[derive(Debug, Clone)] pub enum RunChild { Text(Text), + DeleteText(DeleteText), Tab(Tab), Break(Break), } @@ -38,6 +39,11 @@ impl Run { self } + pub fn add_delete_text(mut self, text: &str) -> Run { + self.children.push(RunChild::Text(Text::new(text))); + self + } + pub fn add_tab(mut self) -> Run { self.children.push(RunChild::Tab(Tab::new())); self @@ -81,6 +87,7 @@ impl BuildXML for Run { for c in &self.children { match c { RunChild::Text(t) => b = b.add_child(t), + RunChild::DeleteText(t) => b = b.add_child(t), RunChild::Tab(t) => b = b.add_child(t), RunChild::Break(t) => b = b.add_child(t), } diff --git a/docx-core/src/documents/elements/table.rs b/docx-core/src/documents/elements/table.rs index 2ea2123..697fd4a 100644 --- a/docx-core/src/documents/elements/table.rs +++ b/docx-core/src/documents/elements/table.rs @@ -3,14 +3,14 @@ use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone)] -pub struct Table { +pub struct Table<'a> { property: TableProperty, - rows: Vec, + rows: Vec>, grid: Vec, } -impl Table { - pub fn new(rows: Vec) -> Table { +impl<'a> Table<'a> { + pub fn new(rows: Vec>) -> Table<'a> { let property = TableProperty::new(); let grid = vec![]; Self { @@ -20,13 +20,13 @@ impl Table { } } - pub fn set_grid(mut self, grid: Vec) -> Table { + pub fn set_grid(mut self, grid: Vec) -> Table<'a> { self.grid = grid; self } } -impl BuildXML for Table { +impl<'a> BuildXML for Table<'a> { fn build(&self) -> Vec { let grid = TableGrid::new(self.grid.clone()); let b = XMLBuilder::new() diff --git a/docx-core/src/documents/elements/table_cell.rs b/docx-core/src/documents/elements/table_cell.rs index b580a95..2b98f13 100644 --- a/docx-core/src/documents/elements/table_cell.rs +++ b/docx-core/src/documents/elements/table_cell.rs @@ -4,40 +4,40 @@ use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone)] -pub struct TableCell { +pub struct TableCell<'a> { property: TableCellProperty, - contents: Vec, + contents: Vec>, } #[derive(Debug, Clone)] -pub enum TableCellContent { - Paragraph(Paragraph), +pub enum TableCellContent<'a> { + Paragraph(Paragraph<'a>), } -impl TableCell { - pub fn new() -> TableCell { +impl<'a> TableCell<'a> { + pub fn new() -> TableCell<'a> { let property = TableCellProperty::new(); let contents = vec![]; Self { property, contents } } - pub fn add_paragraph(mut self, p: Paragraph) -> TableCell { + pub fn add_paragraph(mut self, p: Paragraph<'a>) -> TableCell<'a> { self.contents.push(TableCellContent::Paragraph(p)); self } - pub fn vertical_merge(mut self, t: VMergeType) -> TableCell { + pub fn vertical_merge(mut self, t: VMergeType) -> TableCell<'a> { self.property = self.property.vertical_merge(t); self } - pub fn grid_span(mut self, v: usize) -> TableCell { + pub fn grid_span(mut self, v: usize) -> TableCell<'a> { self.property = self.property.grid_span(v); self } } -impl BuildXML for TableCell { +impl<'a> BuildXML for TableCell<'a> { fn build(&self) -> Vec { let b = XMLBuilder::new(); let mut b = b.open_table_cell().add_child(&self.property); diff --git a/docx-core/src/documents/elements/table_row.rs b/docx-core/src/documents/elements/table_row.rs index a525174..bd30478 100644 --- a/docx-core/src/documents/elements/table_row.rs +++ b/docx-core/src/documents/elements/table_row.rs @@ -3,19 +3,19 @@ use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone)] -pub struct TableRow { +pub struct TableRow<'a> { property: TableRowProperty, - cells: Vec, + cells: Vec>, } -impl TableRow { - pub fn new(cells: Vec) -> TableRow { +impl<'a> TableRow<'a> { + pub fn new(cells: Vec>) -> TableRow<'a> { let property = TableRowProperty::new(); Self { property, cells } } } -impl BuildXML for TableRow { +impl<'a> BuildXML for TableRow<'a> { fn build(&self) -> Vec { let b = XMLBuilder::new() .open_table_row() diff --git a/docx-core/src/documents/history_id.rs b/docx-core/src/documents/history_id.rs new file mode 100644 index 0000000..a025ffb --- /dev/null +++ b/docx-core/src/documents/history_id.rs @@ -0,0 +1,21 @@ +#[allow(unused)] +use std::sync::atomic::{AtomicUsize, Ordering}; + +#[allow(dead_code)] +static HISTORY_ID: AtomicUsize = AtomicUsize::new(0); + +#[cfg(not(test))] +pub trait HistoryId { + fn generate(&self) -> String { + let id = HISTORY_ID.load(Ordering::Relaxed); + HISTORY_ID.store(id + 1, Ordering::Relaxed); + format!("{}", id) + } +} + +#[cfg(test)] +pub trait HistoryId { + fn generate(&self) -> &str { + "123" + } +} diff --git a/docx-core/src/documents/mod.rs b/docx-core/src/documents/mod.rs index 03be5b3..421ff86 100644 --- a/docx-core/src/documents/mod.rs +++ b/docx-core/src/documents/mod.rs @@ -5,12 +5,14 @@ mod document; mod document_rels; mod elements; mod font_table; +mod history_id; mod rels; mod settings; mod styles; mod xml_docx; -pub(crate) use build_xml::*; +pub(crate) use build_xml::BuildXML; +pub(crate) use history_id::HistoryId; pub use content_types::*; pub use doc_props::*; @@ -30,7 +32,7 @@ pub struct Docx<'a> { document_rels: DocumentRels, doc_props: DocProps<'a>, styles: Styles, - document: Document, + document: Document<'a>, settings: Settings, font_table: FontTable, } @@ -63,12 +65,12 @@ impl<'a> Docx<'a> { Default::default() } - pub fn add_paragraph(mut self, p: Paragraph) -> Docx<'a> { + pub fn add_paragraph(mut self, p: Paragraph<'a>) -> Docx<'a> { self.document = self.document.add_paragraph(p); self } - pub fn add_table(mut self, t: Table) -> Docx<'a> { + pub fn add_table(mut self, t: Table<'a>) -> Docx<'a> { self.document = self.document.add_table(t); self } diff --git a/docx-core/src/xml_builder/elements.rs b/docx-core/src/xml_builder/elements.rs index 32ae686..74518a4 100644 --- a/docx-core/src/xml_builder/elements.rs +++ b/docx-core/src/xml_builder/elements.rs @@ -22,6 +22,19 @@ impl XMLBuilder { self.writer.write(text).expect(EXPECT_MESSAGE); self.close() } + // i.e. + pub(crate) fn delete_text(mut self, text: &str, preserve_space: bool) -> Self { + let space = if preserve_space { + "preserve" + } else { + "default" + }; + self.writer + .write(XmlEvent::start_element("w:delText").attr("xml:space", space)) + .expect(EXPECT_MESSAGE); + self.writer.write(text).expect(EXPECT_MESSAGE); + self.close() + } // i.e. opened_el!(open_run, "w:r"); opened_el!(open_run_property, "w:rPr"); @@ -148,6 +161,9 @@ impl XMLBuilder { "w:bottom", "w:gutter" ); + + opened_el!(open_insert, "w:ins", "w:id", "w:author", "w:data"); + opened_el!(open_delete, "w:del", "w:id", "w:author", "w:data"); } #[cfg(test)] diff --git a/docx-core/tests/lib.rs b/docx-core/tests/lib.rs index c8372b7..73eb01d 100644 --- a/docx-core/tests/lib.rs +++ b/docx-core/tests/lib.rs @@ -47,11 +47,7 @@ pub fn size() -> Result<(), DocxError> { let path = std::path::Path::new("./tests/output/size.docx"); let file = std::fs::File::create(&path).unwrap(); Docx::new() - .add_paragraph( - Paragraph::new() - .add_run(Run::new().add_text("Hello")) - .size(60), - ) + .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello").size(60))) .add_paragraph( Paragraph::new() .add_run(Run::new().add_text(" Wor").size(50)) @@ -219,3 +215,18 @@ pub fn custom_attr_paragraph() -> Result<(), DocxError> { .pack(file)?; Ok(()) } + +#[test] +pub fn history() -> Result<(), DocxError> { + let path = std::path::Path::new("./tests/output/history.docx"); + let file = std::fs::File::create(&path).unwrap(); + Docx::new() + .add_paragraph( + Paragraph::new() + .add_insert(Insert::new().run(Run::new().add_text("Hello"))) + .add_delete(Delete::new().run(Run::new().add_delete_text("World"))), + ) + .build() + .pack(file)?; + Ok(()) +} diff --git a/fixtures/history_libre_office/[Content_Types].xml b/fixtures/history_libre_office/[Content_Types].xml new file mode 100644 index 0000000..dc111cb --- /dev/null +++ b/fixtures/history_libre_office/[Content_Types].xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/fixtures/history_libre_office/_rels/.rels b/fixtures/history_libre_office/_rels/.rels new file mode 100644 index 0000000..f0b72e7 --- /dev/null +++ b/fixtures/history_libre_office/_rels/.rels @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/fixtures/history_libre_office/docProps/app.xml b/fixtures/history_libre_office/docProps/app.xml new file mode 100644 index 0000000..d248599 --- /dev/null +++ b/fixtures/history_libre_office/docProps/app.xml @@ -0,0 +1,2 @@ + +6LibreOffice/6.0.7.3$Linux_X86_64 LibreOffice_project/00m0$Build-311551 \ No newline at end of file diff --git a/fixtures/history_libre_office/docProps/core.xml b/fixtures/history_libre_office/docProps/core.xml new file mode 100644 index 0000000..dbc6cf3 --- /dev/null +++ b/fixtures/history_libre_office/docProps/core.xml @@ -0,0 +1,2 @@ + +2019-11-15T14:17:59Zja-JP2019-11-15T14:46:47Z6 \ No newline at end of file diff --git a/fixtures/history_libre_office/history.docx b/fixtures/history_libre_office/history.docx new file mode 100644 index 0000000000000000000000000000000000000000..106fbce156d88a4fe3b6a9eb4a53745dc6c7df37 GIT binary patch literal 4369 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

R)e))y% zZ+<8qgsvu2IuR|G!J+LPIP&!xwk_Ha;?Twi(Bu6cV9yS6xB>Q`ab}!7pLlji!OeNU zgBJU$^HbmX?6dY6=L&xZ6ibVLXa7CcoKHV%H*p^OchF(?_$SAG{z+%u*gqG-b>Tml z)${3R9S6>`{SJMi|4sjgfjggnR__1#`ciBI_&+Y?eEQis!u5pT;ZFAV>i^v#&L^Lx g1+LV7hY{s}EIg3z1?;Bq@JO+j0`{2NFXHb02m7Wc2mk;8 literal 0 HcmV?d00001 diff --git a/fixtures/history_libre_office/word/_rels/document.xml.rels b/fixtures/history_libre_office/word/_rels/document.xml.rels new file mode 100644 index 0000000..8a2db8a --- /dev/null +++ b/fixtures/history_libre_office/word/_rels/document.xml.rels @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/fixtures/history_libre_office/word/document.xml b/fixtures/history_libre_office/word/document.xml new file mode 100644 index 0000000..8c2b701 --- /dev/null +++ b/fixtures/history_libre_office/word/document.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + W + + + + + + a + + + + + + rld + + + + + + Hello + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fixtures/history_libre_office/word/fontTable.xml b/fixtures/history_libre_office/word/fontTable.xml new file mode 100644 index 0000000..3916a0e --- /dev/null +++ b/fixtures/history_libre_office/word/fontTable.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/fixtures/history_libre_office/word/settings.xml b/fixtures/history_libre_office/word/settings.xml new file mode 100644 index 0000000..ce04db3 --- /dev/null +++ b/fixtures/history_libre_office/word/settings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/fixtures/history_libre_office/word/styles.xml b/fixtures/history_libre_office/word/styles.xml new file mode 100644 index 0000000..1f8b301 --- /dev/null +++ b/fixtures/history_libre_office/word/styles.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file