From cdb91be925ac9245c0b8a2dbc6b15f4dae769679 Mon Sep 17 00:00:00 2001 From: bokuweb Date: Tue, 11 Jan 2022 00:39:38 +0900 Subject: [PATCH] feat: Support styleWithLevels (#403) --- docx-core/examples/toc_simple.rs | 3 +- docx-core/examples/toc_with_style_level.rs | 49 +++++++++++++++++++ .../documents/elements/table_of_contents.rs | 5 ++ docx-core/src/documents/mod.rs | 48 ++++++++++++------ docx-wasm/js/style.ts | 26 ++++++++++ docx-wasm/js/table-of-contents.ts | 10 ++++ docx-wasm/src/style.rs | 10 ++++ docx-wasm/src/table_of_contents.rs | 8 +++ output/examples/.keep | 0 9 files changed, 144 insertions(+), 15 deletions(-) create mode 100644 docx-core/examples/toc_with_style_level.rs create mode 100644 output/examples/.keep diff --git a/docx-core/examples/toc_simple.rs b/docx-core/examples/toc_simple.rs index 3d205dd..ff480c0 100644 --- a/docx-core/examples/toc_simple.rs +++ b/docx-core/examples/toc_simple.rs @@ -18,7 +18,8 @@ pub fn main() -> Result<(), DocxError> { .add_table_of_contents( TableOfContents::new() .heading_styles_range(1, 3) - .alias("Table of contents"), + .alias("Table of contents") + .auto(), ) .add_paragraph(p1) .add_paragraph(p2) diff --git a/docx-core/examples/toc_with_style_level.rs b/docx-core/examples/toc_with_style_level.rs new file mode 100644 index 0000000..a007463 --- /dev/null +++ b/docx-core/examples/toc_with_style_level.rs @@ -0,0 +1,49 @@ +use docx_rs::*; + +pub fn main() -> Result<(), DocxError> { + let path = std::path::Path::new("./output/examples/toc_with_style_level.docx"); + let file = std::fs::File::create(&path).unwrap(); + + let style1 = Style::new("Heading1", StyleType::Paragraph).name("Heading 1"); + let style2 = Style::new("StyleLevel1", StyleType::Paragraph) + .name("Style Level1") + .based_on("Heading1"); + let style3 = Style::new("StyleLevel4", StyleType::Paragraph) + .name("Style Level4") + .based_on("Heading4"); + let style4 = Style::new("Heading4", StyleType::Paragraph).name("Heading 4"); + + let p1 = Paragraph::new() + .add_run(Run::new().add_text("Hello")) + .style("Heading1") + .page_break_before(true); + + let p2 = Paragraph::new() + .add_run(Run::new().add_text("Foo")) + .style("StyleLevel1") + .page_break_before(true); + + let p3 = Paragraph::new() + .add_run(Run::new().add_text("Bar")) + .style("StyleLevel4") + .page_break_before(true); + Docx::new() + .add_style(style1) + .add_style(style2) + .add_style(style3) + .add_style(style4) + .add_table_of_contents( + TableOfContents::new() + .heading_styles_range(1, 3) + .add_style_with_level(StyleWithLevel::new("StyleLevel1", 1)) + .add_style_with_level(StyleWithLevel::new("StyleLevel4", 4)) + .alias("Table of contents") + .auto(), + ) + .add_paragraph(p1) + .add_paragraph(p2) + .add_paragraph(p3) + .build() + .pack(file)?; + Ok(()) +} diff --git a/docx-core/src/documents/elements/table_of_contents.rs b/docx-core/src/documents/elements/table_of_contents.rs index 00e8eed..2bba4b8 100644 --- a/docx-core/src/documents/elements/table_of_contents.rs +++ b/docx-core/src/documents/elements/table_of_contents.rs @@ -26,6 +26,11 @@ impl TableOfContents { self } + pub fn add_style_with_level(mut self, s: StyleWithLevel) -> Self { + self.instr = self.instr.add_style_with_level(s); + self + } + pub fn hyperlink(mut self) -> Self { self.instr = self.instr.hyperlink(); self diff --git a/docx-core/src/documents/mod.rs b/docx-core/src/documents/mod.rs index a4041b7..6e79cd7 100644 --- a/docx-core/src/documents/mod.rs +++ b/docx-core/src/documents/mod.rs @@ -873,6 +873,19 @@ fn update_document_by_toc( let heading_map = styles.create_heading_style_map(); let mut items = vec![]; let mut children = vec![]; + let style_map: std::collections::HashMap = toc + .instr + .styles_with_levels + .iter() + .map(|sl| sl.0.clone()) + .collect(); + + if toc.instr.heading_styles_range.is_none() && !toc.instr.styles_with_levels.is_empty() { + // INFO: if \t option set without heading styles ranges, Microsoft word does not show ToC items... + return document_children; + } + + let (min, max) = toc.instr.heading_styles_range.unwrap_or((0, 9)); for child in document_children.into_iter() { match child { @@ -884,20 +897,7 @@ fn update_document_by_toc( .map(|p| p.val.to_string()) .and_then(|sid| heading_map.get(&sid)) { - if let Some((min, max)) = toc.instr.heading_styles_range { - if min <= *heading_level && max >= *heading_level { - let toc_key = TocKey::generate(); - items.push( - TableOfContentsItem::new() - .text(paragraph.raw_text()) - .toc_key(&toc_key) - .level(*heading_level), - ); - paragraph = - paragraph.wrap_by_bookmark(generate_bookmark_id(), &toc_key); - } - } else { - // If no heading range is specified, all heading levels used in the document are listed. + if min <= *heading_level && max >= *heading_level { let toc_key = TocKey::generate(); items.push( TableOfContentsItem::new() @@ -912,6 +912,26 @@ fn update_document_by_toc( // TODO: check tc field } } + + // Support \t option. Collect toc items if style id matched. + if let Some(level) = paragraph + .property + .style + .as_ref() + .and_then(|s| style_map.get(&s.val)) + { + if min <= *level && max >= *level { + let toc_key = TocKey::generate(); + items.push( + TableOfContentsItem::new() + .text(paragraph.raw_text()) + .toc_key(&toc_key) + .level(*level), + ); + paragraph = paragraph.wrap_by_bookmark(generate_bookmark_id(), &toc_key); + } + } + children.push(DocumentChild::Paragraph(paragraph)); } DocumentChild::Table(ref _table) => { diff --git a/docx-wasm/js/style.ts b/docx-wasm/js/style.ts index 5d77428..96060b7 100644 --- a/docx-wasm/js/style.ts +++ b/docx-wasm/js/style.ts @@ -38,6 +38,28 @@ export class Style { return this; }; + basedOn = (n: string) => { + this._basedOn = n; + return this; + }; + + // TODO: + // runProperty = (n: RunProperty) => { + // this._runProperty = n; + // return this; + // }; + + // TODO: + // paragraphProperty = (n: ParagraphProperty) => { + // this._paragraphProperty = n; + // return this; + // }; + + tableProperty = (n: TableProperty) => { + this._tableProperty = n; + return this; + }; + buildStyleType = () => { switch (this._styleType) { case "character": @@ -60,6 +82,10 @@ export class Style { s = s.name(this._name); } + if (this._basedOn) { + s = s.based_on(this._basedOn); + } + return s; }; } diff --git a/docx-wasm/js/table-of-contents.ts b/docx-wasm/js/table-of-contents.ts index 704b2e8..4b5f23f 100644 --- a/docx-wasm/js/table-of-contents.ts +++ b/docx-wasm/js/table-of-contents.ts @@ -4,6 +4,7 @@ import { TableOfContentsItem } from "./table-of-contents-item"; export class TableOfContents { _headingStylesRange: [number, number] | null = null; + _styleWithLevels: { styleId: string; level: number }[] = []; _hyperlink = false; _alias = ""; _auto = false; @@ -16,6 +17,11 @@ export class TableOfContents { return this; }; + addStyleWithLevel = (styleId: string, level: number) => { + this._styleWithLevels.push({ styleId, level }); + return this; + }; + hyperlink = () => { this._hyperlink = true; return this; @@ -75,6 +81,10 @@ export class TableOfContents { toc = toc.page_ref_placeholder(this._pageRefPlaceholder); } + for (const sl of this._styleWithLevels) { + toc = toc.add_style_with_level(sl.styleId, sl.level); + } + for (const item of this._items) { toc = toc.add_item(item.buildWasmObject()); } diff --git a/docx-wasm/src/style.rs b/docx-wasm/src/style.rs index 820e6ef..a41360d 100644 --- a/docx-wasm/src/style.rs +++ b/docx-wasm/src/style.rs @@ -81,6 +81,16 @@ impl Style { self } + // pub fn run_property(mut self, p: docx_rs::RunProperty) -> Self { + // self.0.run_property = p; + // self + // } + + // pub fn paragraph_property(mut self, p: docx_rs::ParagraphProperty) -> Self { + // self.0.paragraph_property = p; + // self + // } + pub fn table_property(mut self, p: docx_rs::TableProperty) -> Self { self.0.table_property = p; self diff --git a/docx-wasm/src/table_of_contents.rs b/docx-wasm/src/table_of_contents.rs index 409febe..882eec3 100644 --- a/docx-wasm/src/table_of_contents.rs +++ b/docx-wasm/src/table_of_contents.rs @@ -24,6 +24,14 @@ impl TableOfContents { self } + pub fn add_style_with_level(mut self, style: &str, level: usize) -> Self { + self.0.instr = self + .0 + .instr + .add_style_with_level(docx_rs::StyleWithLevel::new(style, level)); + self + } + pub fn hyperlink(mut self) -> Self { self.0.instr = self.0.instr.hyperlink(); self diff --git a/output/examples/.keep b/output/examples/.keep new file mode 100644 index 0000000..e69de29