From b1c8b2beb83a7f1cc333c194c587edb2cf8faa79 Mon Sep 17 00:00:00 2001 From: felipe Date: Fri, 14 Apr 2017 11:01:47 +0200 Subject: [PATCH] add xml:space="preserve" to text runs In MS Word 2015 (and possibly others), leading and trailing spaces are ignored in text runs. This means that calling TextRun with leading/trailing space would result in a document that didn't include those spaces. The fix here (per http://officeopenxml.com/WPtext.php) is to include an extra attribute on the "w:t" element that forces word to recognize those spaces. --- ts/docx/run/run-components/text.ts | 8 ++++++-- ts/tests/docx/document/documentTest.ts | 2 +- ts/tests/docx/paragraph/paragraphTests.ts | 2 +- ts/tests/docx/run/run-components/text.ts | 22 ++++++++++++++++++++++ ts/tests/docx/run/textRunTests.ts | 11 +++++++---- ts/tests/docx/table/testTable.ts | 11 +++++++---- 6 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 ts/tests/docx/run/run-components/text.ts diff --git a/ts/docx/run/run-components/text.ts b/ts/docx/run/run-components/text.ts index 48f6495331..a07cf8f618 100644 --- a/ts/docx/run/run-components/text.ts +++ b/ts/docx/run/run-components/text.ts @@ -1,9 +1,13 @@ -import { XmlComponent } from "../../xml-components"; +import { XmlAttributeComponent, XmlComponent } from "../../xml-components"; + +class TextAttributes extends XmlAttributeComponent<{space: "default" | "preserve"}> { + protected xmlKeys = {space: "xml:space"}; +} export class Text extends XmlComponent { - constructor(text: string) { super("w:t"); + this.root.push(new TextAttributes({space: "preserve"})); if (text) { this.root.push(text); } diff --git a/ts/tests/docx/document/documentTest.ts b/ts/tests/docx/document/documentTest.ts index 9531768e2d..dd311a85ea 100644 --- a/ts/tests/docx/document/documentTest.ts +++ b/ts/tests/docx/document/documentTest.ts @@ -41,7 +41,7 @@ describe("Document", () => { expect(body[0]).to.have.property("w:p").which.includes({ "w:r": [ {"w:rPr": []}, - {"w:t": ["sample paragraph text"]}, + {"w:t": [{_attr: {"xml:space": "preserve"}}, "sample paragraph text"]}, ], }); }); diff --git a/ts/tests/docx/paragraph/paragraphTests.ts b/ts/tests/docx/paragraph/paragraphTests.ts index 0482116bad..8562965d61 100644 --- a/ts/tests/docx/paragraph/paragraphTests.ts +++ b/ts/tests/docx/paragraph/paragraphTests.ts @@ -39,7 +39,7 @@ describe("Paragraph", () => { expect(tree).to.be.an("array").which.includes({ "w:r": [ {"w:rPr": []}, - {"w:t": ["this is a test run"]}, + {"w:t": [{_attr: {"xml:space": "preserve"}}, "this is a test run"]}, ], }); }); diff --git a/ts/tests/docx/run/run-components/text.ts b/ts/tests/docx/run/run-components/text.ts new file mode 100644 index 0000000000..2b536a3f1f --- /dev/null +++ b/ts/tests/docx/run/run-components/text.ts @@ -0,0 +1,22 @@ +import { expect } from "chai"; +import { Text } from "../../../../docx/run/run-components/text"; +import { Formatter } from "../../../../export/formatter"; + +describe("Text", () => { + describe("#constructor", () => { + it("creates an empty text run if no text is given", () => { + const t = new Text(""); + const f = new Formatter().format(t); + expect(f).to.deep.equal({"w:t": [{_attr: {"xml:space": "preserve"}}]}); + }); + + it("adds the passed in text to the component", () => { + const t = new Text(" this is\n text"); + const f = new Formatter().format(t); + expect(f).to.deep.equal({"w:t": [ + {_attr: {"xml:space": "preserve"}}, + " this is\n text", + ]}); + }); + }); +}); diff --git a/ts/tests/docx/run/textRunTests.ts b/ts/tests/docx/run/textRunTests.ts index 4b0f12f3a0..7aacf69507 100644 --- a/ts/tests/docx/run/textRunTests.ts +++ b/ts/tests/docx/run/textRunTests.ts @@ -1,6 +1,6 @@ -import { assert } from "chai"; +import { expect } from "chai"; import { TextRun } from "../../../docx/run/text-run"; -import { Utility } from "../../utility"; +import { Formatter } from "../../../export/formatter"; describe("TextRun", () => { let run: TextRun; @@ -9,8 +9,11 @@ describe("TextRun", () => { it("should add text into run", () => { run = new TextRun("test"); - const newJson = Utility.jsonify(run); - assert.equal(newJson.root[1].root, "test"); + const f = new Formatter().format(run); + expect(f).to.deep.equal({"w:r": [ + {"w:rPr": []}, + {"w:t": [{_attr: {"xml:space": "preserve"}}, "test"]}, + ]}); }); }); }); diff --git a/ts/tests/docx/table/testTable.ts b/ts/tests/docx/table/testTable.ts index a88cc1b79b..32631a605f 100644 --- a/ts/tests/docx/table/testTable.ts +++ b/ts/tests/docx/table/testTable.ts @@ -36,7 +36,7 @@ describe("Table", () => { {"w:tcPr": []}, {"w:p": [ {"w:pPr": []}, - {"w:r": [{"w:rPr": []}, {"w:t": [c]}]}, + {"w:r": [{"w:rPr": []}, {"w:t": [{_attr: {"xml:space": "preserve"}}, c]}]}, ]}, ]}); expect(tree).to.deep.equal({ @@ -65,7 +65,7 @@ describe("Table", () => { {"w:tcPr": []}, {"w:p": [ {"w:pPr": []}, - {"w:r": [{"w:rPr": []}, {"w:t": [c]}]}, + {"w:r": [{"w:rPr": []}, {"w:t": [{_attr: {"xml:space": "preserve"}}, c]}]}, ]}, ]}); expect(tree).to.deep.equal({ @@ -153,7 +153,7 @@ describe("Table", () => { {"w:tcPr": []}, {"w:p": [ {"w:pPr": []}, - {"w:r": [{"w:rPr": []}, {"w:t": ["Hello"]}]}, + {"w:r": [{"w:rPr": []}, {"w:t": [{_attr: {"xml:space": "preserve"}}, "Hello"]}]}, ]}, ], }); @@ -175,7 +175,10 @@ describe("Table", () => { {"w:tcPr": []}, {"w:p": [ {"w:pPr": []}, - {"w:r": [{"w:rPr": []}, {"w:t": ["Test paragraph"]}]}, + {"w:r": [ + {"w:rPr": []}, + {"w:t": [{_attr: {"xml:space": "preserve"}}, "Test paragraph"]}, + ]}, ]}, ], });