From c6a555aa75f112431f00ffca339c7c6e318a22f5 Mon Sep 17 00:00:00 2001 From: bokuweb Date: Thu, 25 Nov 2021 19:42:06 +0900 Subject: [PATCH] Improve spacing (#367) * fix: line spacing interfaces * fix: js interface * fix: js if * fix: reader and testing --- docx-core/examples/reader.rs | 2 +- .../src/documents/elements/line_spacing.rs | 75 +- docx-core/src/documents/elements/paragraph.rs | 19 +- .../documents/elements/paragraph_property.rs | 25 +- .../src/reader/attributes/line_spacing.rs | 48 +- docx-core/src/reader/paragraph_property.rs | 8 +- docx-core/src/xml_builder/elements.rs | 12 + docx-core/tests/lib.rs | 21 +- docx-wasm/js/index.ts | 71 +- docx-wasm/js/paragraph.ts | 314 ++-- docx-wasm/src/lib.rs | 2 + docx-wasm/src/line_spacing.rs | 49 + docx-wasm/src/paragraph.rs | 10 +- .../test/__snapshots__/index.test.js.snap | 1571 ++++++++++++++++- docx-wasm/test/index.test.js | 10 +- fixtures/after_lines/after_lines.docx | Bin 0 -> 11913 bytes test.json | 1252 ++++++++++++- 17 files changed, 3212 insertions(+), 277 deletions(-) create mode 100644 docx-wasm/src/line_spacing.rs create mode 100644 fixtures/after_lines/after_lines.docx diff --git a/docx-core/examples/reader.rs b/docx-core/examples/reader.rs index 457ceaf..50719b9 100644 --- a/docx-core/examples/reader.rs +++ b/docx-core/examples/reader.rs @@ -4,7 +4,7 @@ use std::fs::File; use std::io::{Read, Write}; pub fn main() { - let mut file = File::open("./fixtures/custom/custom.docx").unwrap(); + let mut file = File::open("./spacing.docx").unwrap(); let mut buf = vec![]; file.read_to_end(&mut buf).unwrap(); diff --git a/docx-core/src/documents/elements/line_spacing.rs b/docx-core/src/documents/elements/line_spacing.rs index f6f05b9..80a0112 100644 --- a/docx-core/src/documents/elements/line_spacing.rs +++ b/docx-core/src/documents/elements/line_spacing.rs @@ -4,7 +4,7 @@ use crate::xml_builder::*; use crate::line_spacing_type::LineSpacingType; use serde::*; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct LineSpacing { #[serde(skip_serializing_if = "Option::is_none")] @@ -14,28 +14,45 @@ pub struct LineSpacing { #[serde(skip_serializing_if = "Option::is_none")] after: Option, #[serde(skip_serializing_if = "Option::is_none")] + before_lines: Option, + #[serde(skip_serializing_if = "Option::is_none")] + after_lines: Option, + #[serde(skip_serializing_if = "Option::is_none")] line: Option, } impl LineSpacing { - pub fn new(spacing: Option) -> Self { - Self { - line_rule: spacing, - before: None, - after: None, - line: None, - } + pub fn new() -> Self { + Self::default() } - pub fn before(mut self, before: Option) -> Self { - self.before = before; + + pub fn line_rule(mut self, t: LineSpacingType) -> Self { + self.line_rule = Some(t); self } - pub fn after(mut self, after: Option) -> Self { - self.after = after; + + pub fn before(mut self, before: u32) -> Self { + self.before = Some(before); self } - pub fn line(mut self, line: Option) -> Self { - self.line = line; + + pub fn after(mut self, after: u32) -> Self { + self.after = Some(after); + self + } + + pub fn before_lines(mut self, before: u32) -> Self { + self.before_lines = Some(before); + self + } + + pub fn after_lines(mut self, after: u32) -> Self { + self.after_lines = Some(after); + self + } + + pub fn line(mut self, line: u32) -> Self { + self.line = Some(line); self } } @@ -43,8 +60,15 @@ impl LineSpacing { impl BuildXML for LineSpacing { fn build(&self) -> Vec { let b = XMLBuilder::new(); - b.line_spacing(self.before, self.after, self.line, self.line_rule) - .build() + b.line_spacing( + self.before, + self.after, + self.line, + self.before_lines, + self.after_lines, + self.line_rule, + ) + .build() } } @@ -58,8 +82,9 @@ mod tests { #[test] fn test_spacing() { - let b = LineSpacing::new(Some(LineSpacingType::Auto)) - .line(Some(100)) + let b = LineSpacing::new() + .line_rule(LineSpacingType::Auto) + .line(100) .build(); assert_eq!( str::from_utf8(&b).unwrap(), @@ -67,12 +92,26 @@ mod tests { ); } + #[test] + fn test_spacing_after_lines() { + let b = LineSpacing::new() + .line_rule(LineSpacingType::Auto) + .after_lines(100) + .build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#""# + ); + } + #[test] fn test_spacing_json() { let s = LineSpacing { line_rule: Some(LineSpacingType::Auto), before: None, after: None, + before_lines: None, + after_lines: None, line: Some(100), }; assert_eq!( diff --git a/docx-core/src/documents/elements/paragraph.rs b/docx-core/src/documents/elements/paragraph.rs index c2a21f9..01439ed 100644 --- a/docx-core/src/documents/elements/paragraph.rs +++ b/docx-core/src/documents/elements/paragraph.rs @@ -244,16 +244,8 @@ impl Paragraph { self } - pub fn line_spacing( - mut self, - before: Option, - after: Option, - line: Option, - spacing_type: Option, - ) -> Self { - self.property = self - .property - .line_spacing(before, after, line, spacing_type); + pub fn line_spacing(mut self, spacing: LineSpacing) -> Self { + self.property = self.property.line_spacing(spacing); self } } @@ -334,8 +326,13 @@ mod tests { #[test] fn test_line_spacing_and_character_spacing() { + let spacing = LineSpacing::new() + .line_rule(LineSpacingType::Auto) + .before(20) + .after(30) + .line(200); let b = Paragraph::new() - .line_spacing(Some(20), Some(30), Some(200), Some(LineSpacingType::Auto)) + .line_spacing(spacing) .add_run(Run::new().add_text("Hello")) .build(); assert_eq!( diff --git a/docx-core/src/documents/elements/paragraph_property.rs b/docx-core/src/documents/elements/paragraph_property.rs index 7605678..ec35552 100644 --- a/docx-core/src/documents/elements/paragraph_property.rs +++ b/docx-core/src/documents/elements/paragraph_property.rs @@ -2,7 +2,7 @@ use serde::Serialize; use super::*; use crate::documents::BuildXML; -use crate::types::{AlignmentType, LineSpacingType, SpecialIndentType}; +use crate::types::{AlignmentType, SpecialIndentType}; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] @@ -78,19 +78,8 @@ impl ParagraphProperty { self } - pub fn line_spacing( - mut self, - before: Option, - after: Option, - line: Option, - spacing_type: Option, - ) -> Self { - self.line_spacing = Some( - LineSpacing::new(spacing_type) - .after(after) - .before(before) - .line(line), - ); + pub fn line_spacing(mut self, spacing: LineSpacing) -> Self { + self.line_spacing = Some(spacing); self } @@ -173,6 +162,7 @@ impl BuildXML for ParagraphProperty { mod tests { use super::*; + use crate::types::LineSpacingType; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; @@ -238,9 +228,10 @@ mod tests { #[test] fn test_line_spacing() { let props = ParagraphProperty::new(); - let bytes = props - .line_spacing(None, None, Some(100), Some(LineSpacingType::AtLeast)) - .build(); + let spacing = LineSpacing::new() + .line_rule(LineSpacingType::AtLeast) + .line(100); + let bytes = props.line_spacing(spacing).build(); assert_eq!( str::from_utf8(&bytes).unwrap(), r#""# diff --git a/docx-core/src/reader/attributes/line_spacing.rs b/docx-core/src/reader/attributes/line_spacing.rs index a75a23c..075e313 100644 --- a/docx-core/src/reader/attributes/line_spacing.rs +++ b/docx-core/src/reader/attributes/line_spacing.rs @@ -1,34 +1,34 @@ use crate::line_spacing_type::LineSpacingType; +use crate::LineSpacing; use crate::ReaderError; use std::str::FromStr; use xml::attribute::OwnedAttribute; -pub type LineSpacingResult = Result< - ( - Option, - Option, - Option, - Option, - ), - ReaderError, ->; - -pub fn read_line_spacing(attributes: &[OwnedAttribute]) -> LineSpacingResult { - let mut before: Option = None; - let mut after: Option = None; - let mut line: Option = None; - let mut spacing_type: Option = None; +pub fn read_line_spacing(attributes: &[OwnedAttribute]) -> Result { + let mut spacing = LineSpacing::new(); for a in attributes { let local_name = &a.name.local_name; - if local_name == "before" { - before = Some(u32::from_str(&a.value)?); - } else if local_name == "after" { - after = Some(u32::from_str(&a.value)?); - } else if local_name == "line" { - line = Some(u32::from_str(&a.value)?); - } else if local_name == "lineRule" { - spacing_type = Some(LineSpacingType::from_str(&a.value)?); + match local_name.as_str() { + "before" => { + spacing = spacing.before(u32::from_str(&a.value)?); + } + "after" => { + spacing = spacing.after(u32::from_str(&a.value)?); + } + "line" => { + spacing = spacing.line(u32::from_str(&a.value)?); + } + "lineRule" => { + spacing = spacing.line_rule(LineSpacingType::from_str(&a.value)?); + } + "beforeLines" => { + spacing = spacing.before_lines(u32::from_str(&a.value)?); + } + "afterLines" => { + spacing = spacing.after_lines(u32::from_str(&a.value)?); + } + _ => {} } } - Ok((before, after, line, spacing_type)) + Ok(spacing) } diff --git a/docx-core/src/reader/paragraph_property.rs b/docx-core/src/reader/paragraph_property.rs index 8e66a8f..e704532 100644 --- a/docx-core/src/reader/paragraph_property.rs +++ b/docx-core/src/reader/paragraph_property.rs @@ -40,9 +40,11 @@ impl ElementReader for ParagraphProperty { continue; } XMLElement::Spacing => { - let (before, after, line, spacing_type) = - attributes::line_spacing::read_line_spacing(&attributes)?; - p = p.line_spacing(before, after, line, spacing_type); + if let Ok(spacing) = + attributes::line_spacing::read_line_spacing(&attributes) + { + p = p.line_spacing(spacing); + } continue; } XMLElement::Justification => { diff --git a/docx-core/src/xml_builder/elements.rs b/docx-core/src/xml_builder/elements.rs index 81a0262..38d73d7 100644 --- a/docx-core/src/xml_builder/elements.rs +++ b/docx-core/src/xml_builder/elements.rs @@ -167,11 +167,15 @@ impl XMLBuilder { before: Option, after: Option, line: Option, + before_lines: Option, + after_lines: Option, spacing: Option, ) -> Self { let mut xml_event = XmlEvent::start_element("w:spacing"); let before_val: String; let after_val: String; + let before_lines_val: String; + let after_lines_val: String; let line_val: String; if let Some(before) = before { @@ -182,6 +186,14 @@ impl XMLBuilder { after_val = format!("{}", after); xml_event = xml_event.attr("w:after", &after_val) } + if let Some(before_lines) = before_lines { + before_lines_val = format!("{}", before_lines); + xml_event = xml_event.attr("w:beforeLines", &before_lines_val) + } + if let Some(after_lines) = after_lines { + after_lines_val = format!("{}", after_lines); + xml_event = xml_event.attr("w:afterLines", &after_lines_val) + } if let Some(line) = line { line_val = format!("{}", line); xml_event = xml_event.attr("w:line", &line_val) diff --git a/docx-core/tests/lib.rs b/docx-core/tests/lib.rs index 848a493..9772da1 100644 --- a/docx-core/tests/lib.rs +++ b/docx-core/tests/lib.rs @@ -420,21 +420,36 @@ pub fn date() -> Result<(), DocxError> { pub fn line_spacing() -> Result<(), DocxError> { let path = std::path::Path::new("./tests/output/line_spacing.docx"); let file = std::fs::File::create(&path).unwrap(); + Docx::new() .add_paragraph( Paragraph::new() .add_run(Run::new().add_text(DUMMY)) - .line_spacing(Some(300), None, Some(300), Some(LineSpacingType::Auto)), + .line_spacing( + LineSpacing::new() + .before(300) + .line(300) + .line_rule(LineSpacingType::Auto), + ), ) .add_paragraph( Paragraph::new() .add_run(Run::new().add_text(DUMMY)) - .line_spacing(None, None, Some(300), Some(LineSpacingType::AtLeast)), + .line_spacing( + LineSpacing::new() + .line(300) + .line_rule(LineSpacingType::AtLeast), + ), ) .add_paragraph( Paragraph::new() .add_run(Run::new().add_text(DUMMY).character_spacing(100)) - .line_spacing(None, Some(300), Some(300), Some(LineSpacingType::Exact)), + .line_spacing( + LineSpacing::new() + .after(300) + .line(300) + .line_rule(LineSpacingType::Exact), + ), ) .build() .pack(file)?; diff --git a/docx-wasm/js/index.ts b/docx-wasm/js/index.ts index eff97cc..62861d8 100644 --- a/docx-wasm/js/index.ts +++ b/docx-wasm/js/index.ts @@ -1,4 +1,4 @@ -import { Paragraph } from "./paragraph"; +import { Paragraph, ParagraphProperty } from "./paragraph"; import { Insert } from "./insert"; import { Delete } from "./delete"; import { DeleteText } from "./delete-text"; @@ -339,6 +339,51 @@ export class Docx { return comment; } + buildLineSpacing(p: ParagraphProperty): wasm.LineSpacing | null { + const { lineSpacing } = p; + if (lineSpacing == null) return null; + let kind; + switch (lineSpacing._lineRule) { + case "atLeast": { + kind = wasm.LineSpacingType.AtLeast; + break; + } + case "auto": { + kind = wasm.LineSpacingType.Auto; + break; + } + case "exact": { + kind = wasm.LineSpacingType.Exact; + break; + } + } + let spacing = wasm.createLineSpacing(); + if (lineSpacing._before != null) { + spacing = spacing.before(lineSpacing._before); + } + + if (lineSpacing._after != null) { + spacing = spacing.after(lineSpacing._after); + } + + if (lineSpacing._beforeLines != null) { + spacing = spacing.before_lines(lineSpacing._beforeLines); + } + + if (lineSpacing._afterLines != null) { + spacing = spacing.after_lines(lineSpacing._afterLines); + } + + if (lineSpacing._line != null) { + spacing = spacing.line(lineSpacing._line); + } + + if (kind != null) { + spacing = spacing.line_rule(kind); + } + return spacing; + } + buildParagraph(p: Paragraph) { let paragraph = wasm.createParagraph(); p.children.forEach((child) => { @@ -424,28 +469,10 @@ export class Docx { } if (typeof p.property.lineSpacing !== "undefined") { - const { lineSpacing } = p.property; - let kind; - switch (p.property.lineSpacing.lineRule) { - case "atLeast": { - kind = wasm.LineSpacingType.AtLeast; - break; - } - case "auto": { - kind = wasm.LineSpacingType.Auto; - break; - } - case "exact": { - kind = wasm.LineSpacingType.Exact; - break; - } + const spacing = this.buildLineSpacing(p.property); + if (spacing) { + paragraph = paragraph.line_spacing(spacing); } - paragraph = paragraph.line_spacing( - lineSpacing.before, - lineSpacing.after, - lineSpacing.line, - kind - ); } if (p.property.runProperty.italic) { diff --git a/docx-wasm/js/paragraph.ts b/docx-wasm/js/paragraph.ts index 8a37000..e14f4e0 100644 --- a/docx-wasm/js/paragraph.ts +++ b/docx-wasm/js/paragraph.ts @@ -1,181 +1,205 @@ -import {Run, RunProperty, RunFonts, createDefaultRunProperty} from "./run"; -import {Insert} from "./insert"; -import {Delete} from "./delete"; -import {BookmarkStart} from "./bookmark-start"; -import {BookmarkEnd} from "./bookmark-end"; -import {Comment} from "./comment"; -import {CommentEnd} from "./comment-end"; +import { Run, RunProperty, RunFonts, createDefaultRunProperty } from "./run"; +import { Insert } from "./insert"; +import { Delete } from "./delete"; +import { BookmarkStart } from "./bookmark-start"; +import { BookmarkEnd } from "./bookmark-end"; +import { Comment } from "./comment"; +import { CommentEnd } from "./comment-end"; export type ParagraphChild = - | Run - | Insert - | Delete - | BookmarkStart - | BookmarkEnd - | Comment - | CommentEnd; + | Run + | Insert + | Delete + | BookmarkStart + | BookmarkEnd + | Comment + | CommentEnd; export type AlignmentType = - | "center" - | "left" - | "right" - | "both" - | "justified" - | "distribute" - | "end"; + | "center" + | "left" + | "right" + | "both" + | "justified" + | "distribute" + | "end"; export type SpecialIndentKind = "firstLine" | "hanging"; -export type LineSpacingType = "atLeast" | "auto" | "exact" +export type LineSpacingType = "atLeast" | "auto" | "exact"; + +export class LineSpacing { + _before?: number; + _after?: number; + _beforeLines?: number; + _afterLines?: number; + _line?: number; + _lineRule?: LineSpacingType; + + before(v: number) { + this._before = v; + return this; + } + after(v: number) { + this._after = v; + return this; + } + beforeLines(v: number) { + this._beforeLines = v; + return this; + } + afterLines(v: number) { + this._afterLines = v; + return this; + } + line(v: number) { + this._line = v; + return this; + } + lineRule(v: LineSpacingType) { + this._lineRule = v; + return this; + } +} export type ParagraphProperty = { - align?: AlignmentType; - styleId?: string; - indent?: { - left: number; - specialIndentKind?: SpecialIndentKind; - specialIndentSize?: number; - }; - numbering?: { - id: number; - level: number; - }; - lineSpacing?: { - before?: number; - after?: number; - line?: number; - lineRule?: LineSpacingType; - }; - runProperty: RunProperty; - keepNext: boolean; - keepLines: boolean; - pageBreakBefore: boolean; - windowControl: boolean; + align?: AlignmentType; + styleId?: string; + indent?: { + left: number; + specialIndentKind?: SpecialIndentKind; + specialIndentSize?: number; + }; + numbering?: { + id: number; + level: number; + }; + lineSpacing?: LineSpacing; + runProperty: RunProperty; + keepNext: boolean; + keepLines: boolean; + pageBreakBefore: boolean; + windowControl: boolean; }; export const createDefaultParagraphProperty = (): ParagraphProperty => { - return { - runProperty: createDefaultRunProperty(), - keepNext: false, - keepLines: false, - pageBreakBefore: false, - windowControl: false, - }; + return { + runProperty: createDefaultRunProperty(), + keepNext: false, + keepLines: false, + pageBreakBefore: false, + windowControl: false, + }; }; export class Paragraph { - hasNumberings = false; - children: ParagraphChild[] = []; - property: ParagraphProperty = createDefaultParagraphProperty(); + hasNumberings = false; + children: ParagraphChild[] = []; + property: ParagraphProperty = createDefaultParagraphProperty(); - addRun(run: Run) { - this.children.push(run); - return this; - } + addRun(run: Run) { + this.children.push(run); + return this; + } - addInsert(ins: Insert) { - this.children.push(ins); - return this; - } + addInsert(ins: Insert) { + this.children.push(ins); + return this; + } - addDelete(del: Delete) { - this.children.push(del); - return this; - } + addDelete(del: Delete) { + this.children.push(del); + return this; + } - addBookmarkStart(id: number, name: string) { - this.children.push(new BookmarkStart(id, name)); - return this; - } + addBookmarkStart(id: number, name: string) { + this.children.push(new BookmarkStart(id, name)); + return this; + } - addBookmarkEnd(id: number) { - this.children.push(new BookmarkEnd(id)); - return this; - } + addBookmarkEnd(id: number) { + this.children.push(new BookmarkEnd(id)); + return this; + } - addCommentStart(comment: Comment) { - this.children.push(comment); - return this; - } + addCommentStart(comment: Comment) { + this.children.push(comment); + return this; + } - addCommentEnd(end: CommentEnd) { - this.children.push(end); - return this; - } + addCommentEnd(end: CommentEnd) { + this.children.push(end); + return this; + } - align(type: AlignmentType) { - this.property.align = type; - return this; - } + align(type: AlignmentType) { + this.property.align = type; + return this; + } - style(id: string) { - this.property.styleId = id; - return this; - } + style(id: string) { + this.property.styleId = id; + return this; + } - indent( - left: number, - specialIndentKind?: SpecialIndentKind, - specialIndentSize?: number - ) { - this.property.indent = {left, specialIndentKind, specialIndentSize}; - return this; - } + indent( + left: number, + specialIndentKind?: SpecialIndentKind, + specialIndentSize?: number + ) { + this.property.indent = { left, specialIndentKind, specialIndentSize }; + return this; + } - numbering(id: number, level: number) { - this.hasNumberings = true; - this.property.numbering = {id, level}; - return this; - } + numbering(id: number, level: number) { + this.hasNumberings = true; + this.property.numbering = { id, level }; + return this; + } - lineSpacing(before?: number, after?: number, line?: number, lineRule?: LineSpacingType) { - this.property.lineSpacing = { - before, - after, - line, - lineRule - } - return this; - } + lineSpacing(spacing: LineSpacing) { + this.property.lineSpacing = spacing; + return this; + } - keepNext(v: boolean) { - this.property = {...this.property, keepNext: v}; - return this; - } + keepNext(v: boolean) { + this.property = { ...this.property, keepNext: v }; + return this; + } - keepLines(v: boolean) { - this.property = {...this.property, keepLines: v}; - return this; - } + keepLines(v: boolean) { + this.property = { ...this.property, keepLines: v }; + return this; + } - pageBreakBefore(v: boolean) { - this.property = {...this.property, pageBreakBefore: v}; - return this; - } + pageBreakBefore(v: boolean) { + this.property = { ...this.property, pageBreakBefore: v }; + return this; + } - windowControl(v: boolean) { - this.property = {...this.property, windowControl: v}; - return this; - } + windowControl(v: boolean) { + this.property = { ...this.property, windowControl: v }; + return this; + } - // run property - size(size: number) { - this.property.runProperty = {...this.property.runProperty, size}; - return this; - } + // run property + size(size: number) { + this.property.runProperty = { ...this.property.runProperty, size }; + return this; + } - bold() { - this.property.runProperty = {...this.property.runProperty, bold: true}; - return this; - } + bold() { + this.property.runProperty = { ...this.property.runProperty, bold: true }; + return this; + } - italic() { - this.property.runProperty = {...this.property.runProperty, italic: true}; - return this; - } + italic() { + this.property.runProperty = { ...this.property.runProperty, italic: true }; + return this; + } - fonts(fonts: RunFonts) { - this.property.runProperty = {...this.property.runProperty, fonts}; - return this; - } + fonts(fonts: RunFonts) { + this.property.runProperty = { ...this.property.runProperty, fonts }; + return this; + } } diff --git a/docx-wasm/src/lib.rs b/docx-wasm/src/lib.rs index 8444869..3b3b4cf 100644 --- a/docx-wasm/src/lib.rs +++ b/docx-wasm/src/lib.rs @@ -7,6 +7,7 @@ mod footer; mod insert; mod level; mod level_override; +mod line_spacing; mod numbering; mod page_margin; mod paragraph; @@ -28,6 +29,7 @@ pub use footer::*; pub use insert::*; pub use level::*; pub use level_override::*; +pub use line_spacing::*; pub use numbering::*; pub use page_margin::*; pub use paragraph::*; diff --git a/docx-wasm/src/line_spacing.rs b/docx-wasm/src/line_spacing.rs new file mode 100644 index 0000000..3694977 --- /dev/null +++ b/docx-wasm/src/line_spacing.rs @@ -0,0 +1,49 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +#[derive(Debug)] +pub struct LineSpacing(docx_rs::LineSpacing); + +#[wasm_bindgen(js_name = createLineSpacing)] +pub fn create_line_spacing() -> LineSpacing { + LineSpacing(docx_rs::LineSpacing::new()) +} + +impl LineSpacing { + pub fn take(self) -> docx_rs::LineSpacing { + self.0 + } +} + +#[wasm_bindgen] +impl LineSpacing { + pub fn line_rule(mut self, t: docx_rs::LineSpacingType) -> Self { + self.0 = self.0.line_rule(t); + self + } + + pub fn before(mut self, before: u32) -> Self { + self.0 = self.0.before(before); + self + } + + pub fn after(mut self, after: u32) -> Self { + self.0 = self.0.after(after); + self + } + + pub fn before_lines(mut self, before: u32) -> Self { + self.0 = self.0.before_lines(before); + self + } + + pub fn after_lines(mut self, after: u32) -> Self { + self.0 = self.0.after_lines(after); + self + } + + pub fn line(mut self, line: u32) -> Self { + self.0 = self.0.line(line); + self + } +} diff --git a/docx-wasm/src/paragraph.rs b/docx-wasm/src/paragraph.rs index 4c1f3e7..827ae45 100644 --- a/docx-wasm/src/paragraph.rs +++ b/docx-wasm/src/paragraph.rs @@ -112,14 +112,8 @@ impl Paragraph { self } - pub fn line_spacing( - mut self, - before: Option, - after: Option, - line: Option, - spacing_type: Option, - ) -> Self { - self.0 = self.0.line_spacing(before, after, line, spacing_type); + pub fn line_spacing(mut self, spacing: LineSpacing) -> Self { + self.0 = self.0.line_spacing(spacing.take()); self } diff --git a/docx-wasm/test/__snapshots__/index.test.js.snap b/docx-wasm/test/__snapshots__/index.test.js.snap index 750411a..757264a 100644 --- a/docx-wasm/test/__snapshots__/index.test.js.snap +++ b/docx-wasm/test/__snapshots__/index.test.js.snap @@ -1,5 +1,1572 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`reader should read afterLines docx 1`] = ` +Object { + "comments": Object { + "comments": Array [], + }, + "commentsExtended": Object { + "children": Array [], + }, + "contentType": Object { + "custom_xml_count": 1, + "footer_count": 1, + "types": Object { + "/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml", + "/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml", + "/docProps/core.xml": "application/vnd.openxmlformats-package.core-properties+xml", + "/docProps/custom.xml": "application/vnd.openxmlformats-officedocument.custom-properties+xml", + "/word/_rels/document.xml.rels": "application/vnd.openxmlformats-package.relationships+xml", + "/word/comments.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml", + "/word/commentsExtended.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml", + "/word/document.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", + "/word/fontTable.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml", + "/word/header1.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", + "/word/numbering.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", + "/word/settings.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", + "/word/styles.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", + }, + "web_extension_count": 1, + }, + "customItemProps": Array [], + "customItemRels": Array [], + "customItems": Array [], + "docProps": Object { + "app": Object {}, + "core": Object { + "config": Object { + "created": null, + "creator": null, + "description": null, + "language": null, + "lastModifiedBy": null, + "modified": null, + "revision": null, + "subject": null, + "title": null, + }, + }, + "custom": Object { + "properties": Object {}, + }, + }, + "document": Object { + "children": Array [ + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "第1条 ", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "ほげほげほげ", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "ー", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + ], + "hasNumbering": false, + "id": "78D5F6F5", + "property": Object { + "alignment": null, + "divId": null, + "indent": null, + "keepLines": false, + "keepNext": false, + "lineSpacing": null, + "numberingProperty": null, + "outlineLvl": null, + "pageBreakBefore": false, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "style": null, + "windowControl": false, + }, + }, + "type": "paragraph", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "    こんにちは、こんにちは。", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "Space after", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + ], + "hasNumbering": false, + "id": "47E607A1", + "property": Object { + "alignment": null, + "divId": null, + "indent": null, + "keepLines": false, + "keepNext": false, + "lineSpacing": Object { + "after": 720, + }, + "numberingProperty": null, + "outlineLvl": null, + "pageBreakBefore": false, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "style": null, + "windowControl": false, + }, + }, + "type": "paragraph", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "第", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "2", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "条 ", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "ほげほげほげ", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "ー", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + ], + "hasNumbering": false, + "id": "1D02E472", + "property": Object { + "alignment": null, + "divId": null, + "indent": null, + "keepLines": false, + "keepNext": false, + "lineSpacing": null, + "numberingProperty": null, + "outlineLvl": null, + "pageBreakBefore": false, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "style": null, + "windowControl": false, + }, + }, + "type": "paragraph", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "    こんにちは、こんにちは。", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + ], + "hasNumbering": false, + "id": "1302AFDA", + "property": Object { + "alignment": null, + "divId": null, + "indent": null, + "keepLines": false, + "keepNext": false, + "lineSpacing": null, + "numberingProperty": null, + "outlineLvl": null, + "pageBreakBefore": false, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "style": null, + "windowControl": false, + }, + }, + "type": "paragraph", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "第", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "3", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "条 ", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "ほげほげほげ", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "ー", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + ], + "hasNumbering": false, + "id": "3C9869CF", + "property": Object { + "alignment": null, + "divId": null, + "indent": null, + "keepLines": false, + "keepNext": false, + "lineSpacing": Object { + "before": 720, + "beforeLines": 200, + }, + "numberingProperty": null, + "outlineLvl": null, + "pageBreakBefore": false, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "style": null, + "windowControl": false, + }, + }, + "type": "paragraph", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "    こんにちは、こんにちは", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "。", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "preserveSpace": true, + "text": "Space before", + }, + "type": "text", + }, + ], + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + "type": "run", + }, + ], + "hasNumbering": false, + "id": "3E200AEF", + "property": Object { + "alignment": null, + "divId": null, + "indent": null, + "keepLines": false, + "keepNext": false, + "lineSpacing": null, + "numberingProperty": null, + "outlineLvl": null, + "pageBreakBefore": false, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "style": null, + "windowControl": false, + }, + }, + "type": "paragraph", + }, + Object { + "data": Object { + "children": Array [], + "hasNumbering": false, + "id": "4047C950", + "property": Object { + "alignment": null, + "divId": null, + "indent": null, + "keepLines": false, + "keepNext": false, + "lineSpacing": null, + "numberingProperty": null, + "outlineLvl": null, + "pageBreakBefore": false, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "style": null, + "windowControl": false, + }, + }, + "type": "paragraph", + }, + ], + "hasNumbering": false, + "sectionProperty": Object { + "columns": 425, + "docGrid": Object { + "charSpace": null, + "gridType": "lines", + "linePitch": 360, + }, + "footerReference": null, + "headerReference": Object { + "headerType": "default", + "id": "rId4", + }, + "pageMargin": Object { + "bottom": 1701, + "footer": 992, + "gutter": 0, + "header": 851, + "left": 1701, + "right": 1701, + "top": 1985, + }, + "pageSize": Object { + "h": 16840, + "orient": null, + "w": 11900, + }, + "sectionType": null, + }, + }, + "documentRels": Object { + "customXmlCount": 0, + "footerCount": 0, + "hasComments": false, + "hasNumberings": false, + "imageIds": Array [], + }, + "fontTable": Object {}, + "footer": null, + "header": Object { + "children": Array [], + }, + "media": Array [], + "numberings": Object { + "abstractNums": Array [], + "numberings": Array [], + }, + "rels": Object { + "rels": Array [ + Array [ + "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties", + "rId1", + "docProps/core.xml", + ], + Array [ + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties", + "rId2", + "docProps/app.xml", + ], + Array [ + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", + "rId3", + "word/document.xml", + ], + Array [ + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties", + "rId4", + "docProps/custom.xml", + ], + ], + }, + "settings": Object { + "defaultTabStop": 840, + "docId": "FB0AE6E2-8FB8-3345-A8FC-78CE6F3ABE4E", + "docVars": Array [], + "zoom": 100, + }, + "styles": Object { + "docDefaults": Object { + "runPropertyDefault": Object { + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": 21, + "szCs": 21, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + }, + }, + "styles": Array [ + Object { + "basedOn": null, + "name": "Normal", + "paragraphProperty": Object { + "alignment": "both", + "divId": null, + "indent": null, + "keepLines": false, + "keepNext": false, + "lineSpacing": null, + "numberingProperty": null, + "outlineLvl": null, + "pageBreakBefore": false, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "style": null, + "windowControl": false, + }, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "styleId": "a", + "styleType": "paragraph", + "tableCellProperty": Object { + "borders": null, + "gridSpan": null, + "shading": null, + "textDirection": null, + "verticalAlign": null, + "verticalMerge": null, + "width": null, + }, + "tableProperty": Object { + "borders": Object { + "bottom": Object { + "borderType": "single", + "color": "000000", + "position": "bottom", + "size": 2, + "space": 0, + }, + "insideH": Object { + "borderType": "single", + "color": "000000", + "position": "insideH", + "size": 2, + "space": 0, + }, + "insideV": Object { + "borderType": "single", + "color": "000000", + "position": "insideV", + "size": 2, + "space": 0, + }, + "left": Object { + "borderType": "single", + "color": "000000", + "position": "left", + "size": 2, + "space": 0, + }, + "right": Object { + "borderType": "single", + "color": "000000", + "position": "right", + "size": 2, + "space": 0, + }, + "top": Object { + "borderType": "single", + "color": "000000", + "position": "top", + "size": 2, + "space": 0, + }, + }, + "indent": null, + "justification": "left", + "layout": null, + "margins": Object { + "bottom": Object { + "val": 0, + "widthType": "DXA", + }, + "left": Object { + "val": 55, + "widthType": "DXA", + }, + "right": Object { + "val": 55, + "widthType": "DXA", + }, + "top": Object { + "val": 0, + "widthType": "DXA", + }, + }, + "style": null, + "width": Object { + "width": 0, + "widthType": "Auto", + }, + }, + }, + Object { + "basedOn": null, + "name": "Default Paragraph Font", + "paragraphProperty": Object { + "alignment": null, + "divId": null, + "indent": null, + "keepLines": false, + "keepNext": false, + "lineSpacing": null, + "numberingProperty": null, + "outlineLvl": null, + "pageBreakBefore": false, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "style": null, + "windowControl": false, + }, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "styleId": "a0", + "styleType": "character", + "tableCellProperty": Object { + "borders": null, + "gridSpan": null, + "shading": null, + "textDirection": null, + "verticalAlign": null, + "verticalMerge": null, + "width": null, + }, + "tableProperty": Object { + "borders": Object { + "bottom": Object { + "borderType": "single", + "color": "000000", + "position": "bottom", + "size": 2, + "space": 0, + }, + "insideH": Object { + "borderType": "single", + "color": "000000", + "position": "insideH", + "size": 2, + "space": 0, + }, + "insideV": Object { + "borderType": "single", + "color": "000000", + "position": "insideV", + "size": 2, + "space": 0, + }, + "left": Object { + "borderType": "single", + "color": "000000", + "position": "left", + "size": 2, + "space": 0, + }, + "right": Object { + "borderType": "single", + "color": "000000", + "position": "right", + "size": 2, + "space": 0, + }, + "top": Object { + "borderType": "single", + "color": "000000", + "position": "top", + "size": 2, + "space": 0, + }, + }, + "indent": null, + "justification": "left", + "layout": null, + "margins": Object { + "bottom": Object { + "val": 0, + "widthType": "DXA", + }, + "left": Object { + "val": 55, + "widthType": "DXA", + }, + "right": Object { + "val": 55, + "widthType": "DXA", + }, + "top": Object { + "val": 0, + "widthType": "DXA", + }, + }, + "style": null, + "width": Object { + "width": 0, + "widthType": "Auto", + }, + }, + }, + Object { + "basedOn": null, + "name": "Normal Table", + "paragraphProperty": Object { + "alignment": null, + "divId": null, + "indent": null, + "keepLines": false, + "keepNext": false, + "lineSpacing": null, + "numberingProperty": null, + "outlineLvl": null, + "pageBreakBefore": false, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "style": null, + "windowControl": false, + }, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "styleId": "a1", + "styleType": "table", + "tableCellProperty": Object { + "borders": null, + "gridSpan": null, + "shading": null, + "textDirection": null, + "verticalAlign": null, + "verticalMerge": null, + "width": null, + }, + "tableProperty": Object { + "borders": Object { + "bottom": null, + "insideH": null, + "insideV": null, + "left": null, + "right": null, + "top": null, + }, + "indent": null, + "justification": "left", + "layout": null, + "margins": Object { + "bottom": Object { + "val": 0, + "widthType": "DXA", + }, + "left": Object { + "val": 55, + "widthType": "DXA", + }, + "right": Object { + "val": 55, + "widthType": "DXA", + }, + "top": Object { + "val": 0, + "widthType": "DXA", + }, + }, + "style": null, + "width": Object { + "width": 0, + "widthType": "Auto", + }, + }, + }, + Object { + "basedOn": null, + "name": "No List", + "paragraphProperty": Object { + "alignment": null, + "divId": null, + "indent": null, + "keepLines": false, + "keepNext": false, + "lineSpacing": null, + "numberingProperty": null, + "outlineLvl": null, + "pageBreakBefore": false, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "style": null, + "windowControl": false, + }, + "runProperty": Object { + "bold": null, + "boldCs": null, + "characterSpacing": null, + "color": null, + "del": null, + "fonts": null, + "highlight": null, + "ins": null, + "italic": null, + "italicCs": null, + "sz": null, + "szCs": null, + "textBorder": null, + "underline": null, + "vanish": null, + "vertAlign": null, + }, + "styleId": "a2", + "styleType": "numbering", + "tableCellProperty": Object { + "borders": null, + "gridSpan": null, + "shading": null, + "textDirection": null, + "verticalAlign": null, + "verticalMerge": null, + "width": null, + }, + "tableProperty": Object { + "borders": Object { + "bottom": Object { + "borderType": "single", + "color": "000000", + "position": "bottom", + "size": 2, + "space": 0, + }, + "insideH": Object { + "borderType": "single", + "color": "000000", + "position": "insideH", + "size": 2, + "space": 0, + }, + "insideV": Object { + "borderType": "single", + "color": "000000", + "position": "insideV", + "size": 2, + "space": 0, + }, + "left": Object { + "borderType": "single", + "color": "000000", + "position": "left", + "size": 2, + "space": 0, + }, + "right": Object { + "borderType": "single", + "color": "000000", + "position": "right", + "size": 2, + "space": 0, + }, + "top": Object { + "borderType": "single", + "color": "000000", + "position": "top", + "size": 2, + "space": 0, + }, + }, + "indent": null, + "justification": "left", + "layout": null, + "margins": Object { + "bottom": Object { + "val": 0, + "widthType": "DXA", + }, + "left": Object { + "val": 55, + "widthType": "DXA", + }, + "right": Object { + "val": 55, + "widthType": "DXA", + }, + "top": Object { + "val": 0, + "widthType": "DXA", + }, + }, + "style": null, + "width": Object { + "width": 0, + "widthType": "Auto", + }, + }, + }, + ], + }, + "taskpanes": null, + "taskpanesRels": Object { + "rels": Array [], + }, + "webExtensions": Array [], + "webSettings": Object { + "divs": Array [], + }, +} +`; + exports[`reader should read custom docx 1`] = ` Object { "comments": Object { @@ -15840,6 +17407,7 @@ Object { "keepNext": true, "lineSpacing": Object { "before": 200, + "beforeLines": 200, "line": 360, "lineRule": "Exact", }, @@ -15980,6 +17548,7 @@ Object { "keepNext": true, "lineSpacing": Object { "before": 100, + "beforeLines": 100, "line": 320, "lineRule": "Exact", }, @@ -22269,7 +23838,7 @@ exports[`writer should write line spacing 1`] = ` exports[`writer should write line spacing 2`] = ` " - Hello + Hello " `; diff --git a/docx-wasm/test/index.test.js b/docx-wasm/test/index.test.js index 7e6980c..75c04d3 100644 --- a/docx-wasm/test/index.test.js +++ b/docx-wasm/test/index.test.js @@ -74,6 +74,12 @@ describe("reader", () => { const json = w.readDocx(buffer); expect(json).toMatchSnapshot(); }); + + test("should read afterLines docx", () => { + const buffer = readFileSync("../fixtures/after_lines/after_lines.docx"); + const json = w.readDocx(buffer); + expect(json).toMatchSnapshot(); + }); }); describe("writer", () => { @@ -352,7 +358,9 @@ describe("writer", () => { test("should write line spacing", () => { const p = new w.Paragraph() .addRun(new w.Run().addText("Hello ")) - .lineSpacing(100, "", 100, 1); + .lineSpacing( + new w.LineSpacing().before(100).after(0).line(100).afterLines(400) + ); const buffer = new w.Docx().addParagraph(p).build(); writeFileSync("../output/line_spacing.docx", buffer); const z = new Zip(Buffer.from(buffer)); diff --git a/fixtures/after_lines/after_lines.docx b/fixtures/after_lines/after_lines.docx new file mode 100644 index 0000000000000000000000000000000000000000..80e3cffd35a4234f4b1b599b6e50bef3617d3136 GIT binary patch literal 11913 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