From b0ee0305fb15b97e31100417c2615cefc20ea4b1 Mon Sep 17 00:00:00 2001 From: fmuscolino Date: Tue, 15 Jan 2019 15:39:48 +0100 Subject: [PATCH 1/6] Add OutlineLevel Paragraph property --- src/file/paragraph/links/index.ts | 1 + .../paragraph/links/outline-level.spec.ts | 23 +++++++++++++++++++ src/file/paragraph/links/outline-level.ts | 17 ++++++++++++++ src/file/paragraph/paragraph.spec.ts | 14 +++++++++++ src/file/paragraph/paragraph.ts | 7 +++++- 5 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/file/paragraph/links/outline-level.spec.ts create mode 100644 src/file/paragraph/links/outline-level.ts diff --git a/src/file/paragraph/links/index.ts b/src/file/paragraph/links/index.ts index 09084aa8c7..82715b62e8 100644 --- a/src/file/paragraph/links/index.ts +++ b/src/file/paragraph/links/index.ts @@ -1,2 +1,3 @@ export * from "./hyperlink"; export * from "./bookmark"; +export * from "./outline-level"; diff --git a/src/file/paragraph/links/outline-level.spec.ts b/src/file/paragraph/links/outline-level.spec.ts new file mode 100644 index 0000000000..6ba188fd9d --- /dev/null +++ b/src/file/paragraph/links/outline-level.spec.ts @@ -0,0 +1,23 @@ +import { assert } from "chai"; + +import { Utility } from "tests/utility"; + +import { OutlineLevel } from "./outline-level"; + +describe("ParagraphOutlineLevel", () => { + let outlineLevel: OutlineLevel; + + describe("#constructor()", () => { + it("should create an outlineLevel with given value", () => { + outlineLevel = new OutlineLevel("0"); + const newJson = Utility.jsonify(outlineLevel); + assert.equal(newJson.root[0].root.val, "0"); + }); + + it("should create an outlineLevel with blank val", () => { + outlineLevel = new OutlineLevel(""); + const newJson = Utility.jsonify(outlineLevel); + assert.equal(newJson.root[0].root.val, ""); + }); + }); +}); diff --git a/src/file/paragraph/links/outline-level.ts b/src/file/paragraph/links/outline-level.ts new file mode 100644 index 0000000000..b754290698 --- /dev/null +++ b/src/file/paragraph/links/outline-level.ts @@ -0,0 +1,17 @@ +// http://officeopenxml.com/WPparagraph.php +import { Attributes, XmlComponent } from "file/xml-components"; + +export class OutlineLevel extends XmlComponent { + public readonly level: string; + + constructor(level: string) { + super("w:outlineLvl"); + this.level = level; + this.root.push( + new Attributes({ + val: level, + }), + ); + } + +} diff --git a/src/file/paragraph/paragraph.spec.ts b/src/file/paragraph/paragraph.spec.ts index 2fa6dfedc2..f47850ac4d 100644 --- a/src/file/paragraph/paragraph.spec.ts +++ b/src/file/paragraph/paragraph.spec.ts @@ -666,4 +666,18 @@ describe("Paragraph", () => { }); }); }); + + describe("#outlineLevel", () => { + it("should set paragraph outline level to the given value", () => { + paragraph.outlineLevel("0"); + const tree = new Formatter().format(paragraph); + expect(tree).to.deep.equal({ + "w:p": [ + { + "w:pPr": [{ "w:outlineLvl": [{ _attr: { "w:val": "0" } }] }], + }, + ], + }); + }); + }); }); diff --git a/src/file/paragraph/paragraph.ts b/src/file/paragraph/paragraph.ts index fdcc5ed83b..cfd32322c2 100644 --- a/src/file/paragraph/paragraph.ts +++ b/src/file/paragraph/paragraph.ts @@ -14,7 +14,7 @@ import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spa import { Style } from "./formatting/style"; import { CenterTabStop, LeaderType, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop"; import { NumberProperties } from "./formatting/unordered-list"; -import { Bookmark, Hyperlink } from "./links"; +import { Bookmark, Hyperlink, OutlineLevel } from "./links"; import { ParagraphProperties } from "./properties"; import { PictureRun, Run, SequentialIdentifier, TextRun } from "./run"; @@ -245,4 +245,9 @@ export class Paragraph extends XmlComponent { this.root.push(new SequentialIdentifier(identifier)); return this; } + + public outlineLevel(level: string): Paragraph { + this.properties.push(new OutlineLevel(level)); + return this; + } } From bf8dfe66041b718023da7eb625c8e33f8d2527ac Mon Sep 17 00:00:00 2001 From: filippomuscolino <7989576+filippomuscolino@users.noreply.github.com> Date: Tue, 15 Jan 2019 16:13:20 +0100 Subject: [PATCH 2/6] Fix for prettier --- src/file/paragraph/links/outline-level.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/file/paragraph/links/outline-level.ts b/src/file/paragraph/links/outline-level.ts index b754290698..512638bcc7 100644 --- a/src/file/paragraph/links/outline-level.ts +++ b/src/file/paragraph/links/outline-level.ts @@ -13,5 +13,4 @@ export class OutlineLevel extends XmlComponent { }), ); } - } From 14a1d62148014df4a708232156a9cbe2f1851e06 Mon Sep 17 00:00:00 2001 From: Dolan Date: Tue, 15 Jan 2019 02:09:38 +0000 Subject: [PATCH 3/6] Add Number of pages element --- src/file/paragraph/run/page-number.ts | 8 ++++++++ src/file/paragraph/run/run.ts | 10 +++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/file/paragraph/run/page-number.ts b/src/file/paragraph/run/page-number.ts index 4ae0eaafd7..2fdc44e32b 100644 --- a/src/file/paragraph/run/page-number.ts +++ b/src/file/paragraph/run/page-number.ts @@ -12,3 +12,11 @@ export class Page extends XmlComponent { this.root.push("PAGE"); } } + +export class NumberOfPages extends XmlComponent { + constructor() { + super("w:instrText"); + this.root.push(new TextAttributes({ space: SpaceType.PRESERVE })); + this.root.push("NUMPAGES"); + } +} diff --git a/src/file/paragraph/run/run.ts b/src/file/paragraph/run/run.ts index c03ed8d78e..32777cd31d 100644 --- a/src/file/paragraph/run/run.ts +++ b/src/file/paragraph/run/run.ts @@ -14,7 +14,7 @@ import { SizeComplexScript, Strike, } from "./formatting"; -import { Page } from "./page-number"; +import { NumberOfPages, Page } from "./page-number"; import { RunProperties } from "./properties"; import { RunFonts } from "./run-fonts"; import { SubScript, SuperScript } from "./script"; @@ -84,6 +84,14 @@ export class Run extends XmlComponent { return this; } + public numberOfTotalPages(): Run { + this.root.push(new Begin()); + this.root.push(new NumberOfPages()); + this.root.push(new Separate()); + this.root.push(new End()); + return this; + } + public smallCaps(): Run { this.properties.push(new SmallCaps()); return this; From 4b6d3c3e3c4bdbd5ee402da62adbfafc39eba2ee Mon Sep 17 00:00:00 2001 From: Dolan Date: Tue, 15 Jan 2019 21:40:19 +0000 Subject: [PATCH 4/6] Write tests --- src/file/paragraph/run/page-number.spec.ts | 23 ++++++++++++++++ src/file/paragraph/run/run.spec.ts | 32 ++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/file/paragraph/run/page-number.spec.ts diff --git a/src/file/paragraph/run/page-number.spec.ts b/src/file/paragraph/run/page-number.spec.ts new file mode 100644 index 0000000000..4a9bc39a3e --- /dev/null +++ b/src/file/paragraph/run/page-number.spec.ts @@ -0,0 +1,23 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { NumberOfPages, Page } from "./page-number"; + +describe("Page", () => { + describe("#constructor()", () => { + it("uses the font name for both ascii and hAnsi", () => { + const tree = new Formatter().format(new Page()); + expect(tree).to.deep.equal({ "w:instrText": [{ _attr: { "xml:space": "preserve" } }, "PAGE"] }); + }); + }); +}); + +describe("NumberOfPages", () => { + describe("#constructor()", () => { + it("uses the font name for both ascii and hAnsi", () => { + const tree = new Formatter().format(new NumberOfPages()); + expect(tree).to.deep.equal({ "w:instrText": [{ _attr: { "xml:space": "preserve" } }, "NUMPAGES"] }); + }); + }); +}); diff --git a/src/file/paragraph/run/run.spec.ts b/src/file/paragraph/run/run.spec.ts index bd9f3bf778..b1fbc8794a 100644 --- a/src/file/paragraph/run/run.spec.ts +++ b/src/file/paragraph/run/run.spec.ts @@ -156,6 +156,38 @@ describe("Run", () => { }); }); + describe("#numberOfTotalPages", () => { + it("should set the run to the RTL mode", () => { + run.numberOfTotalPages(); + const tree = new Formatter().format(run); + expect(tree).to.deep.equal({ + "w:r": [ + { "w:rPr": [] }, + { "w:fldChar": [{ _attr: { "w:fldCharType": "begin" } }] }, + { "w:instrText": [{ _attr: { "xml:space": "preserve" } }, "NUMPAGES"] }, + { "w:fldChar": [{ _attr: { "w:fldCharType": "separate" } }] }, + { "w:fldChar": [{ _attr: { "w:fldCharType": "end" } }] }, + ], + }); + }); + }); + + describe("#pageNumber", () => { + it("should set the run to the RTL mode", () => { + run.pageNumber(); + const tree = new Formatter().format(run); + expect(tree).to.deep.equal({ + "w:r": [ + { "w:rPr": [] }, + { "w:fldChar": [{ _attr: { "w:fldCharType": "begin" } }] }, + { "w:instrText": [{ _attr: { "xml:space": "preserve" } }, "PAGE"] }, + { "w:fldChar": [{ _attr: { "w:fldCharType": "separate" } }] }, + { "w:fldChar": [{ _attr: { "w:fldCharType": "end" } }] }, + ], + }); + }); + }); + describe("#style", () => { it("should set the style to the given styleId", () => { run.style("myRunStyle"); From 2a1161d85738608e63b005380fb7517514aecdd5 Mon Sep 17 00:00:00 2001 From: Dolan Date: Wed, 16 Jan 2019 00:13:18 +0000 Subject: [PATCH 5/6] Add documentation --- demo/demo39.ts | 33 +++++++++++++++++++ docs/_sidebar.md | 1 + docs/usage/page-numbers.md | 66 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 demo/demo39.ts create mode 100644 docs/usage/page-numbers.md diff --git a/demo/demo39.ts b/demo/demo39.ts new file mode 100644 index 0000000000..d06aec50b2 --- /dev/null +++ b/demo/demo39.ts @@ -0,0 +1,33 @@ +// Scaling images +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, PageNumberFormat, TextRun } from "../build"; + +const doc = new Document( + {}, + { + pageNumberStart: 1, + pageNumberFormatType: PageNumberFormat.DECIMAL, + }, +); + +doc.Header.createParagraph("Foo Bar corp. ") + .addRun(new TextRun("Page Number ").pageNumber()) + .addRun(new TextRun(" to ").numberOfTotalPages()); + +doc.Footer.createParagraph("Foo Bar corp. ") + .center() + .addRun(new TextRun("Page Number: ").pageNumber()) + .addRun(new TextRun(" to ").numberOfTotalPages()); + +doc.createParagraph("Hello World 1").pageBreak(); +doc.createParagraph("Hello World 2").pageBreak(); +doc.createParagraph("Hello World 3").pageBreak(); +doc.createParagraph("Hello World 4").pageBreak(); +doc.createParagraph("Hello World 5").pageBreak(); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 4b9a626a03..a36b611623 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -17,6 +17,7 @@ * [Numbering](usage/numbering.md) * [Tab Stops](usage/tab-stops.md) * [Table of Contents](usage/table-of-contents.md) + * [Page Numbers](usage/page-numbers.md) * Styling * [Styling with JS](usage/styling-with-js.md) * [Styling with XML](usage/styling-with-xml.md) diff --git a/docs/usage/page-numbers.md b/docs/usage/page-numbers.md new file mode 100644 index 0000000000..f26a9a42b5 --- /dev/null +++ b/docs/usage/page-numbers.md @@ -0,0 +1,66 @@ +# Page Numbers + +> This feature allows you to set page numbers on each page + +?> **Note:** This feature only works on Headers and Footers + +```ts +doc.Header.createParagraph().addRun(new TextRun("Page Number: ").pageNumber()).addRun(new TextRun("to ").numberOfTotalPages()); +``` + +## Current page number + +To get the current page number, call the `.pageNumber()` method on a `TextRun`. Then add the newly created `TextRun` into a paragraph + +```ts +pageNumber(); +``` + +For example: + +```ts +const currentPageRun = new TextRun("Current Page Number: ").pageNumber(); +paragraph.addRun(currentPageRun); +``` + +## Total number of pages + +```ts +numberOfTotalPages(); +``` + +For example: + +```ts +const lastPage = new TextRun("Total Page Number: ").numberOfTotalPages(); +paragraph.addRun(lastPage); +``` + + +## Both + +You can combine the two to get "Page 2 of 10" effect: + +```ts +const currentPageRun = new TextRun("Page ").pageNumber(); +const lastPage = new TextRun("of ").numberOfTotalPages(); + +paragraph.addRun(currentPageRun); +paragraph.addRun(lastPage); +``` + +Or: + +```ts +doc.Header.createParagraph().addRun(new TextRun("Page ").pageNumber()).addRun(new TextRun("of ").numberOfTotalPages()); +``` + +## Examples + +### Simple Example + +Adding page numbers to Header and Footer + +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo39.ts ":include") + +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo39.ts_ From 18a5f22f4c91379ce0cf840b5cfaa8235804f533 Mon Sep 17 00:00:00 2001 From: Dolan Date: Wed, 16 Jan 2019 00:15:27 +0000 Subject: [PATCH 6/6] Version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ba3b2cdc53..57ab21b8ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "docx", - "version": "4.6.0", + "version": "4.7.0", "description": "Generate .docx documents with JavaScript (formerly Office-Clippy)", "main": "build/index.js", "scripts": {