From 52c4e3cabd23b203e878105411df40c9e41ca7b7 Mon Sep 17 00:00:00 2001 From: felipe Date: Tue, 7 Mar 2017 19:14:57 +0100 Subject: [PATCH 01/20] allow NumberProperties to specify custom id/level This change allows callers to create custom numbering styles and attach them to paragraphs --- ts/docx/paragraph/index.ts | 2 +- ts/docx/paragraph/unordered-list.ts | 6 +++--- ts/tests/docx/paragraph/unorderedListTests.ts | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ts/docx/paragraph/index.ts b/ts/docx/paragraph/index.ts index 064226f0e6..9a6e27dcf9 100644 --- a/ts/docx/paragraph/index.ts +++ b/ts/docx/paragraph/index.ts @@ -106,7 +106,7 @@ export class Paragraph extends XmlComponent { bullet(): Paragraph { this.properties.push(new Style("ListParagraph")); - this.properties.push(new NumberProperties()); + this.properties.push(new NumberProperties(1, 0)); return this; } } \ No newline at end of file diff --git a/ts/docx/paragraph/unordered-list.ts b/ts/docx/paragraph/unordered-list.ts index ec270cd899..9a6611b273 100644 --- a/ts/docx/paragraph/unordered-list.ts +++ b/ts/docx/paragraph/unordered-list.ts @@ -3,10 +3,10 @@ import {Style} from "./style"; export class NumberProperties extends XmlComponent { - constructor() { + constructor(numberId: number, indentLevel: number) { super("w:numPr"); - this.root.push(new IndentLevel(0)); - this.root.push(new NumberId(1)); + this.root.push(new IndentLevel(indentLevel)); + this.root.push(new NumberId(numberId)); } } diff --git a/ts/tests/docx/paragraph/unorderedListTests.ts b/ts/tests/docx/paragraph/unorderedListTests.ts index 6a94ee4d6c..ab1b14f474 100644 --- a/ts/tests/docx/paragraph/unorderedListTests.ts +++ b/ts/tests/docx/paragraph/unorderedListTests.ts @@ -10,7 +10,7 @@ describe("NumberProperties", () => { let numberProperties: NumberProperties; beforeEach(() => { - numberProperties = new NumberProperties(); + numberProperties = new NumberProperties(5, 10); }); describe("#constructor()", () => { @@ -22,11 +22,13 @@ describe("NumberProperties", () => { it("should create a Page Break with a Indent Level inside", () => { let newJson = jsonify(numberProperties); assert.equal(newJson.root[0].rootKey, "w:ilvl"); + assert.equal(newJson.root[0].root[0].root.val, 10); }); it("should create a Page Break with a Number Id inside", () => { let newJson = jsonify(numberProperties); assert.equal(newJson.root[1].rootKey, "w:numId"); + assert.equal(newJson.root[1].root[0].root.val, 5); }); }); }); \ No newline at end of file From f822bbb26f8aef923ee03f998f80b3b38ac90983 Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 16:24:44 +0100 Subject: [PATCH 02/20] fix typos in default numbering definition --- ts/numbering/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/numbering/index.ts b/ts/numbering/index.ts index d32f8767e1..97efca9945 100644 --- a/ts/numbering/index.ts +++ b/ts/numbering/index.ts @@ -68,12 +68,12 @@ export class Numbering extends MultiPropertyXmlComponent { level6.addRunProperty(new RunFonts("Symbol", "default")); abstractNumbering.addLevel(level6); - let level7 = new Level(4, "bullet", "o", "left"); + let level7 = new Level(7, "bullet", "o", "left"); level7.addParagraphProperty(new Indent(5760, 360)); level7.addRunProperty(new RunFonts("Courier New", "default")); abstractNumbering.addLevel(level7); - let level8 = new Level(5, "bullet", "•", "left"); + let level8 = new Level(8, "bullet", "•", "left"); level8.addParagraphProperty(new Indent(6480, 360)); level8.addRunProperty(new RunFonts("Wingdings", "default")); abstractNumbering.addLevel(level8); From ef1407e640c21ddde427b0bd8dc53a13477416e7 Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:07:54 +0100 Subject: [PATCH 03/20] add a test for numbering --- ts/tests/numberingTest.ts | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/ts/tests/numberingTest.ts b/ts/tests/numberingTest.ts index 404460133a..4b7f2df866 100644 --- a/ts/tests/numberingTest.ts +++ b/ts/tests/numberingTest.ts @@ -1,21 +1,47 @@ -import { assert } from "chai"; +import { expect } from "chai"; import { Numbering } from "../numbering"; +import { Formatter } from '../export/formatter'; function jsonify(obj: Object) { let stringifiedJson = JSON.stringify(obj); return JSON.parse(stringifiedJson); } -describe("", () => { +describe("Numbering", () => { let numbering = new Numbering; beforeEach(() => { numbering = new Numbering(); }); - describe("#methodName()", () => { - it("should ", () => { + describe("#constructor", () => { + it("creates a default numbering with one abstract and one concrete instance", () => { + const tree = new Formatter().format(numbering); + expect(Object.keys(tree)).to.deep.equal(['w:numbering']); + const abstractNums = tree['w:numbering'].filter(el => el['w:abstractNum']); + expect(abstractNums).to.have.lengthOf(1); + expect(abstractNums[0]['w:abstractNum']).to.deep.include.members([ + {_attr: {"w:abstractNumId": 0, "w15:restartNumberingAfterBreak": 0}}, + {"w:multiLevelType": [{"_attr": {"w:val": "hybridMultilevel"}}]}, + ]); + abstractNums.filter(el => el['w:lvl']).forEach((el, ix) => { + expect(Object.keys(el)).to.have.lengthOf(1); + expect(Object.keys(el['w:lvl']).sort()).to.deep.equal([ + "_attr", "w:start", "w:lvlJc", "w:numFmt", "w:pPr", "w:rPr" + ]) + expect(el['w:lvl']).to.have.deep.members([ + {"_attr": {"w:ilvl": ix, "w15:tentative": 1}}, + {"w:start": [{"_attr": {"w:val": 1}}]}, + {"w:lvlJc": [{"_attr": {"w:val": "left"}}]}, + {"w:numFmt": [{"_attr": {"w:val": "bullet"}}]}, + ]); + // Once chai 4.0.0 lands and #644 is resolved, we can add the following to the test: + // {"w:lvlText": [{"_attr": {"w:val": "•"}}]}, + // {"w:rPr": [{"w:rFonts": [{"_attr": {"w:ascii": "Symbol", "w:hAnsi": "Symbol", "w:hint": "default"}}]}]}, + // {"w:pPr": [{"_attr": {}}, + // {"w:ind": [{"_attr": {"w:left": 720, "w:hanging": 360}}]}]}, + }) }); }); }); \ No newline at end of file From 5511024ca6095df49b622f9a240bdee448ec3851 Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:08:44 +0100 Subject: [PATCH 04/20] fix run-fonts for new numbering test (and linter warnings) --- ts/numbering/run-fonts.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ts/numbering/run-fonts.ts b/ts/numbering/run-fonts.ts index cc5c640702..dc4a8cd77b 100644 --- a/ts/numbering/run-fonts.ts +++ b/ts/numbering/run-fonts.ts @@ -1,6 +1,6 @@ -import {XmlComponent, XmlAttributeComponent} from "../docx/xml-components"; +import {XmlAttributeComponent, XmlComponent} from "../docx/xml-components"; -interface RunFontAttributesProperties { +interface IRunFontAttributesProperties { ascii: string; hAnsi: string; hint: string; @@ -8,10 +8,11 @@ interface RunFontAttributesProperties { class RunFontAttributes extends XmlAttributeComponent { - constructor(properties: RunFontAttributesProperties) { + constructor(properties: IRunFontAttributesProperties) { super({ - left: "w:left", - hanging: "w:hanging" + ascii: "w:ascii", + hAnsi: "w:hAnsi", + hint: "w:hint", }, properties); } } @@ -19,11 +20,11 @@ class RunFontAttributes extends XmlAttributeComponent { export class RunFonts extends XmlComponent { constructor(ascii: string, hint: string) { - super("w:ind"); + super("w:rFonts"); this.root.push(new RunFontAttributes({ ascii: ascii, hAnsi: ascii, - hint: hint + hint: hint, })); } -} \ No newline at end of file +} From 38138049fe8f97506f0fa6906b901f9c1fd81b8f Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:09:48 +0100 Subject: [PATCH 05/20] fix abstract-number linter warnings --- ts/numbering/abstract-numbering.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/ts/numbering/abstract-numbering.ts b/ts/numbering/abstract-numbering.ts index 1839c25389..7a23c337c5 100644 --- a/ts/numbering/abstract-numbering.ts +++ b/ts/numbering/abstract-numbering.ts @@ -1,20 +1,19 @@ -import {XmlComponent} from "../docx/xml-components"; -import {XmlAttributeComponent} from "../docx/xml-components"; -import {Level} from "./level"; -import {MultiLevelType} from "./multi-level-type"; import * as _ from "lodash"; +import { XmlAttributeComponent, XmlComponent } from "../docx/xml-components"; +import { Level } from "./level"; +import { MultiLevelType } from "./multi-level-type"; -interface AbstractNumberingAttributesProperties { +interface IAbstractNumberingAttributesProperties { abstractNumId?: number; restartNumberingAfterBreak?: number; } class AbstractNumberingAttributes extends XmlAttributeComponent { - constructor(properties: AbstractNumberingAttributesProperties) { + constructor(properties: IAbstractNumberingAttributesProperties) { super({ abstractNumId: "w:abstractNumId", - restartNumberingAfterBreak: "w15:restartNumberingAfterBreak" + restartNumberingAfterBreak: "w15:restartNumberingAfterBreak", }, properties); } } @@ -25,18 +24,18 @@ export class AbstractNumbering extends XmlComponent { super("w:abstractNum"); this.root.push(new AbstractNumberingAttributes({ abstractNumId: id, - restartNumberingAfterBreak: 0 + restartNumberingAfterBreak: 0, })); this.root.push(new MultiLevelType("hybridMultilevel")); } - addLevel(level: Level): void { + public addLevel(level: Level): void { this.root.push(level); } - clearVariables() { - _.forEach(this.root, element => { + public clearVariables(): void { + _.forEach(this.root, (element) => { element.clearVariables(); }); } -} \ No newline at end of file +} From f5e81f1dfcc9888accb4da481935cc4b4868a8f3 Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:13:27 +0100 Subject: [PATCH 06/20] fix numbering/level linter warnings --- ts/numbering/level.ts | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/ts/numbering/level.ts b/ts/numbering/level.ts index 32c2625b02..113027833b 100644 --- a/ts/numbering/level.ts +++ b/ts/numbering/level.ts @@ -1,19 +1,18 @@ -import {XmlComponent, Attributes, MultiPropertyXmlComponent} from "../docx/xml-components"; -import {XmlAttributeComponent} from "../docx/xml-components"; -import {RunProperties} from "../docx/run/properties"; -import {ParagraphProperties} from "../docx/paragraph/properties"; +import { ParagraphProperties } from "../docx/paragraph/properties"; +import { RunProperties } from "../docx/run/properties"; +import { Attributes, MultiPropertyXmlComponent, XmlAttributeComponent, XmlComponent } from "../docx/xml-components"; -interface LevelAttributesProperties { +interface ILevelAttributesProperties { ilvl?: number; tentative?: number; } class LevelAttributes extends XmlAttributeComponent { - constructor(properties: LevelAttributesProperties) { + constructor(properties: ILevelAttributesProperties) { super({ ilvl: "w:ilvl", - tentative: "w15:tentative" + tentative: "w15:tentative", }, properties); } } @@ -23,7 +22,7 @@ class Start extends XmlComponent { constructor(value: number) { super("w:start"); this.root.push(new Attributes({ - val: value + val: value, })); } } @@ -33,7 +32,7 @@ class NumberFormat extends XmlComponent { constructor(value: string) { super("w:numFmt"); this.root.push(new Attributes({ - val: value + val: value, })); } } @@ -43,7 +42,7 @@ class LevelText extends XmlComponent { constructor(value: string) { super("w:lvlText"); this.root.push(new Attributes({ - val: value + val: value, })); } } @@ -53,7 +52,7 @@ class LevelJc extends XmlComponent { constructor(value: string) { super("w:lvlJc"); this.root.push(new Attributes({ - val: value + val: value, })); } } @@ -66,7 +65,7 @@ export class Level extends XmlComponent { super("w:lvl"); this.root.push(new LevelAttributes({ ilvl: level, - tentative: 1 + tentative: 1, })); this.root.push(new Start(1)); @@ -81,7 +80,7 @@ export class Level extends XmlComponent { this.root.push(this.runProperties); } - clearVariables(): void { + public clearVariables(): void { this.paragraphProperties.clearVariables(); this.runProperties.clearVariables(); @@ -89,11 +88,11 @@ export class Level extends XmlComponent { delete this.runProperties; } - addParagraphProperty(property: XmlComponent): void { + public addParagraphProperty(property: XmlComponent): void { this.paragraphProperties.push(property); } - addRunProperty(property: XmlComponent): void { + public addRunProperty(property: XmlComponent): void { this.runProperties.push(property); } -} \ No newline at end of file +} From 28e11a9b08eca39955bd8d962a8e1d42966c4a0f Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:14:36 +0100 Subject: [PATCH 07/20] fix numbering/multi-level-type linter warnings --- ts/numbering/multi-level-type.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ts/numbering/multi-level-type.ts b/ts/numbering/multi-level-type.ts index 359b988e9c..d987a9c652 100644 --- a/ts/numbering/multi-level-type.ts +++ b/ts/numbering/multi-level-type.ts @@ -1,11 +1,11 @@ -import {XmlComponent, Attributes} from "../docx/xml-components"; +import {Attributes, XmlComponent} from "../docx/xml-components"; export class MultiLevelType extends XmlComponent { constructor(value: string) { super("w:multiLevelType"); this.root.push(new Attributes({ - val: value + val: value, })); } -} \ No newline at end of file +} From ae70c2dfde2268f53f99714f5e560f0367b7fcd4 Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:15:46 +0100 Subject: [PATCH 08/20] fix numbering/indent.ts linter warnings --- ts/numbering/indent.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ts/numbering/indent.ts b/ts/numbering/indent.ts index 05477f5d20..38163fe812 100644 --- a/ts/numbering/indent.ts +++ b/ts/numbering/indent.ts @@ -1,4 +1,4 @@ -import {XmlComponent, XmlAttributeComponent} from "../docx/xml-components"; +import {XmlAttributeComponent, XmlComponent} from "../docx/xml-components"; interface IndentAttributesProperties { left: number; @@ -10,7 +10,7 @@ class IndentAttributes extends XmlAttributeComponent { constructor(properties: IndentAttributesProperties) { super({ left: "w:left", - hanging: "w:hanging" + hanging: "w:hanging", }, properties); } } @@ -21,7 +21,7 @@ export class Indent extends XmlComponent { super("w:ind"); this.root.push(new IndentAttributes({ left: left, - hanging: hanging + hanging: hanging, })); } -} \ No newline at end of file +} From 6e6a06eba4a7972f89cac31fa2242fbf70edcaa1 Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:16:51 +0100 Subject: [PATCH 09/20] fix numbering/num.ts linter warnings --- ts/numbering/num.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ts/numbering/num.ts b/ts/numbering/num.ts index 2889b456ef..5a432732cc 100644 --- a/ts/numbering/num.ts +++ b/ts/numbering/num.ts @@ -1,24 +1,24 @@ -import {XmlComponent, Attributes, XmlAttributeComponent} from "../docx/xml-components"; +import { Attributes, XmlAttributeComponent, XmlComponent } from "../docx/xml-components"; class AbstractNumId extends XmlComponent { constructor(value: number) { super("w:abstractNumId"); this.root.push(new Attributes({ - val: value + val: value, })); } } -interface NumAttributesProperties { +interface INumAttributesProperties { numId: number; } class NumAttributes extends XmlAttributeComponent { - constructor(properties: NumAttributesProperties) { + constructor(properties: INumAttributesProperties) { super({ - numId: "w:numId" + numId: "w:numId", }, properties); } } @@ -28,8 +28,8 @@ export class Num extends XmlComponent { constructor(numId: number, abstractNumId: number) { super("w:num"); this.root.push(new NumAttributes({ - numId: numId + numId: numId, })); this.root.push(new AbstractNumId(abstractNumId)); } -} \ No newline at end of file +} From 82998d4f6b0aa7289b01cf34a1714b31ff87efd6 Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:18:12 +0100 Subject: [PATCH 10/20] record ID in AbstractNumbering --- ts/numbering/abstract-numbering.ts | 3 +++ ts/tests/numberingTest.ts | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/ts/numbering/abstract-numbering.ts b/ts/numbering/abstract-numbering.ts index 7a23c337c5..8856d0b1e3 100644 --- a/ts/numbering/abstract-numbering.ts +++ b/ts/numbering/abstract-numbering.ts @@ -19,6 +19,7 @@ class AbstractNumberingAttributes extends XmlAttributeComponent { } export class AbstractNumbering extends XmlComponent { + public id: number; constructor(id: number) { super("w:abstractNum"); @@ -27,6 +28,7 @@ export class AbstractNumbering extends XmlComponent { restartNumberingAfterBreak: 0, })); this.root.push(new MultiLevelType("hybridMultilevel")); + this.id = id; } public addLevel(level: Level): void { @@ -37,5 +39,6 @@ export class AbstractNumbering extends XmlComponent { _.forEach(this.root, (element) => { element.clearVariables(); }); + delete this.id; } } diff --git a/ts/tests/numberingTest.ts b/ts/tests/numberingTest.ts index 4b7f2df866..402c6057a0 100644 --- a/ts/tests/numberingTest.ts +++ b/ts/tests/numberingTest.ts @@ -1,5 +1,6 @@ import { expect } from "chai"; import { Numbering } from "../numbering"; +import { AbstractNumbering } from "../numbering/abstract-numbering"; import { Formatter } from '../export/formatter'; function jsonify(obj: Object) { @@ -44,4 +45,11 @@ describe("Numbering", () => { }) }); }); +}); + +describe("AbstractNumbering", () => { + it("stores its ID at its .id property", () => { + const abstractNumbering = new AbstractNumbering(5); + expect(abstractNumbering.id).to.equal(5); + }); }); \ No newline at end of file From 709ff257022a0e8fcda0f9553af203b9258b542b Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:40:39 +0100 Subject: [PATCH 11/20] add #addAbstractNumbering and #addConcreteNumbering to Numbering --- ts/numbering/index.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/ts/numbering/index.ts b/ts/numbering/index.ts index 97efca9945..a23b6078af 100644 --- a/ts/numbering/index.ts +++ b/ts/numbering/index.ts @@ -8,6 +8,7 @@ import {Num} from "./num"; import * as _ from "lodash"; export class Numbering extends MultiPropertyXmlComponent { + private nextId: number; constructor() { super("w:numbering"); @@ -31,7 +32,9 @@ export class Numbering extends MultiPropertyXmlComponent { Ignorable: "w14 w15 wp14" })); - let abstractNumbering = new AbstractNumbering(0); + this.nextId = 0; + + let abstractNumbering = this.addAbstractNumbering(); let level0 = new Level(0, "bullet", "•", "left"); level0.addParagraphProperty(new Indent(720, 360)); @@ -78,8 +81,19 @@ export class Numbering extends MultiPropertyXmlComponent { level8.addRunProperty(new RunFonts("Wingdings", "default")); abstractNumbering.addLevel(level8); - this.root.push(abstractNumbering); - this.root.push(new Num(1, 0)); + this.addConcreteNumbering(abstractNumbering); + } + + public addAbstractNumbering(): AbstractNumbering { + const num = new AbstractNumbering(this.nextId++); + this.root.push(num); + return num; + } + + public addConcreteNumbering(abstractNumbering: AbstractNumbering): Num { + const num = new Num(this.nextId++, abstractNumbering.id); + this.root.push(num); + return num; } clearVariables() { @@ -88,5 +102,6 @@ export class Numbering extends MultiPropertyXmlComponent { console.log(element); element.clearVariables(); }); + delete this.nextId; } } \ No newline at end of file From 6b702c4bd687828198949e22a3a701900028aa9a Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:28:44 +0100 Subject: [PATCH 12/20] add ID property to Num and test Numbering methods --- ts/numbering/num.ts | 7 +++++++ ts/tests/numberingTest.ts | 31 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/ts/numbering/num.ts b/ts/numbering/num.ts index 5a432732cc..f127817654 100644 --- a/ts/numbering/num.ts +++ b/ts/numbering/num.ts @@ -24,6 +24,7 @@ class NumAttributes extends XmlAttributeComponent { } export class Num extends XmlComponent { + public id: number; constructor(numId: number, abstractNumId: number) { super("w:num"); @@ -31,5 +32,11 @@ export class Num extends XmlComponent { numId: numId, })); this.root.push(new AbstractNumId(abstractNumId)); + this.id = numId; + } + + public clearVariables(): void { + super.clearVariables(); + delete this.id; } } diff --git a/ts/tests/numberingTest.ts b/ts/tests/numberingTest.ts index 402c6057a0..932b58b5d1 100644 --- a/ts/tests/numberingTest.ts +++ b/ts/tests/numberingTest.ts @@ -1,6 +1,7 @@ import { expect } from "chai"; import { Numbering } from "../numbering"; import { AbstractNumbering } from "../numbering/abstract-numbering"; +import { Num } from "../numbering/num"; import { Formatter } from '../export/formatter'; function jsonify(obj: Object) { @@ -45,6 +46,36 @@ describe("Numbering", () => { }) }); }); + + describe("#addAbstractNumbering", () => { + it("returns a new AbstractNumbering instance", () => { + const a2 = numbering.addAbstractNumbering(); + expect(a2).to.be.instanceof(AbstractNumbering); + }); + + it("assigns a unique ID to each abstract numbering it creates", () => { + const a2 = numbering.addAbstractNumbering(); + const a3 = numbering.addAbstractNumbering(); + expect(a2.id).not.to.equal(a3.id); + }); + }); + + describe("#addConcreteNumbering", () => { + it("returns a new Num instance with its abstract ID set to the AbstractNumbering's ID", () => { + const a2 = numbering.addAbstractNumbering(); + const n = numbering.addConcreteNumbering(a2); + expect(n).to.be.instanceof(Num); + const tree = new Formatter().format(numbering); + expect(n.id).to.equal(a2.id); + }); + + it("assigns a unique ID to each concrete numbering it creates", () => { + const a2 = numbering.addAbstractNumbering(); + const n = numbering.addConcreteNumbering(a2); + const n2 = numbering.addConcreteNumbering(a2); + expect(n.id).not.to.equal(n2.id); + }); + }); }); describe("AbstractNumbering", () => { From 0a70d932a03db4b6bd7304bb03173cec50ad862c Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:37:14 +0100 Subject: [PATCH 13/20] fix numberingTest linter warnings --- ts/tests/numberingTest.ts | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/ts/tests/numberingTest.ts b/ts/tests/numberingTest.ts index 932b58b5d1..fef296f69d 100644 --- a/ts/tests/numberingTest.ts +++ b/ts/tests/numberingTest.ts @@ -1,17 +1,16 @@ import { expect } from "chai"; +import { Formatter } from "../export/formatter"; import { Numbering } from "../numbering"; import { AbstractNumbering } from "../numbering/abstract-numbering"; import { Num } from "../numbering/num"; -import { Formatter } from '../export/formatter'; -function jsonify(obj: Object) { - let stringifiedJson = JSON.stringify(obj); - return JSON.parse(stringifiedJson); +function jsonify(obj: object) { + return JSON.parse(JSON.stringify(obj)); } describe("Numbering", () => { - let numbering = new Numbering; + let numbering: Numbering; beforeEach(() => { numbering = new Numbering(); }); @@ -19,31 +18,31 @@ describe("Numbering", () => { describe("#constructor", () => { it("creates a default numbering with one abstract and one concrete instance", () => { const tree = new Formatter().format(numbering); - expect(Object.keys(tree)).to.deep.equal(['w:numbering']); - const abstractNums = tree['w:numbering'].filter(el => el['w:abstractNum']); + expect(Object.keys(tree)).to.deep.equal(["w:numbering"]); + const abstractNums = tree["w:numbering"].filter((el) => el["w:abstractNum"]); expect(abstractNums).to.have.lengthOf(1); - expect(abstractNums[0]['w:abstractNum']).to.deep.include.members([ + expect(abstractNums[0]["w:abstractNum"]).to.deep.include.members([ {_attr: {"w:abstractNumId": 0, "w15:restartNumberingAfterBreak": 0}}, - {"w:multiLevelType": [{"_attr": {"w:val": "hybridMultilevel"}}]}, + {"w:multiLevelType": [{_attr: {"w:val": "hybridMultilevel"}}]}, ]); - abstractNums.filter(el => el['w:lvl']).forEach((el, ix) => { + abstractNums.filter((el) => el["w:lvl"]).forEach((el, ix) => { expect(Object.keys(el)).to.have.lengthOf(1); - expect(Object.keys(el['w:lvl']).sort()).to.deep.equal([ - "_attr", "w:start", "w:lvlJc", "w:numFmt", "w:pPr", "w:rPr" - ]) - expect(el['w:lvl']).to.have.deep.members([ - {"_attr": {"w:ilvl": ix, "w15:tentative": 1}}, - {"w:start": [{"_attr": {"w:val": 1}}]}, - {"w:lvlJc": [{"_attr": {"w:val": "left"}}]}, - {"w:numFmt": [{"_attr": {"w:val": "bullet"}}]}, + expect(Object.keys(el["w:lvl"]).sort()).to.deep.equal([ + "_attr", "w:start", "w:lvlJc", "w:numFmt", "w:pPr", "w:rPr", + ]); + expect(el["w:lvl"]).to.have.deep.members([ + {_attr: {"w:ilvl": ix, "w15:tentative": 1}}, + {"w:start": [{_attr: {"w:val": 1}}]}, + {"w:lvlJc": [{_attr: {"w:val": "left"}}]}, + {"w:numFmt": [{_attr: {"w:val": "bullet"}}]}, ]); // Once chai 4.0.0 lands and #644 is resolved, we can add the following to the test: // {"w:lvlText": [{"_attr": {"w:val": "•"}}]}, // {"w:rPr": [{"w:rFonts": [{"_attr": {"w:ascii": "Symbol", "w:hAnsi": "Symbol", "w:hint": "default"}}]}]}, // {"w:pPr": [{"_attr": {}}, // {"w:ind": [{"_attr": {"w:left": 720, "w:hanging": 360}}]}]}, - }) + }); }); }); @@ -83,4 +82,4 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(5); expect(abstractNumbering.id).to.equal(5); }); -}); \ No newline at end of file +}); From 0b2de737de0d48e442b1159e70bc452321d87c7c Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:39:37 +0100 Subject: [PATCH 14/20] fix linter warnings in numbering/index.ts --- ts/numbering/index.ts | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/ts/numbering/index.ts b/ts/numbering/index.ts index a23b6078af..93acc3e3b6 100644 --- a/ts/numbering/index.ts +++ b/ts/numbering/index.ts @@ -1,11 +1,11 @@ -import {MultiPropertyXmlComponent} from "../docx/xml-components"; -import {DocumentAttributes} from "../docx/document/document-attributes"; -import {AbstractNumbering} from "./abstract-numbering"; -import {Level} from "./level"; -import {Indent} from "./indent"; -import {RunFonts} from "./run-fonts"; -import {Num} from "./num"; import * as _ from "lodash"; +import { DocumentAttributes } from "../docx/document/document-attributes"; +import { MultiPropertyXmlComponent } from "../docx/xml-components"; +import { AbstractNumbering } from "./abstract-numbering"; +import { Indent } from "./indent"; +import { Level } from "./level"; +import { Num } from "./num"; +import { RunFonts } from "./run-fonts"; export class Numbering extends MultiPropertyXmlComponent { private nextId: number; @@ -29,54 +29,54 @@ export class Numbering extends MultiPropertyXmlComponent { wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk", wne: "http://schemas.microsoft.com/office/word/2006/wordml", wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape", - Ignorable: "w14 w15 wp14" + Ignorable: "w14 w15 wp14", })); this.nextId = 0; - let abstractNumbering = this.addAbstractNumbering(); + const abstractNumbering = this.addAbstractNumbering(); - let level0 = new Level(0, "bullet", "•", "left"); + const level0 = new Level(0, "bullet", "•", "left"); level0.addParagraphProperty(new Indent(720, 360)); level0.addRunProperty(new RunFonts("Symbol", "default")); abstractNumbering.addLevel(level0); - let level1 = new Level(1, "bullet", "o", "left"); + const level1 = new Level(1, "bullet", "o", "left"); level1.addParagraphProperty(new Indent(1440, 360)); level1.addRunProperty(new RunFonts("Courier New", "default")); abstractNumbering.addLevel(level1); - let level2 = new Level(2, "bullet", "•", "left"); + const level2 = new Level(2, "bullet", "•", "left"); level2.addParagraphProperty(new Indent(2160, 360)); level2.addRunProperty(new RunFonts("Wingdings", "default")); abstractNumbering.addLevel(level2); - let level3 = new Level(3, "bullet", "•", "left"); + const level3 = new Level(3, "bullet", "•", "left"); level3.addParagraphProperty(new Indent(2880, 360)); level3.addRunProperty(new RunFonts("Symbol", "default")); abstractNumbering.addLevel(level3); - let level4 = new Level(4, "bullet", "o", "left"); + const level4 = new Level(4, "bullet", "o", "left"); level4.addParagraphProperty(new Indent(3600, 360)); level4.addRunProperty(new RunFonts("Courier New", "default")); abstractNumbering.addLevel(level4); - let level5 = new Level(5, "bullet", "•", "left"); + const level5 = new Level(5, "bullet", "•", "left"); level5.addParagraphProperty(new Indent(4320, 360)); level5.addRunProperty(new RunFonts("Wingdings", "default")); abstractNumbering.addLevel(level5); - let level6 = new Level(6, "bullet", "•", "left"); + const level6 = new Level(6, "bullet", "•", "left"); level6.addParagraphProperty(new Indent(5040, 360)); level6.addRunProperty(new RunFonts("Symbol", "default")); abstractNumbering.addLevel(level6); - let level7 = new Level(7, "bullet", "o", "left"); + const level7 = new Level(7, "bullet", "o", "left"); level7.addParagraphProperty(new Indent(5760, 360)); level7.addRunProperty(new RunFonts("Courier New", "default")); abstractNumbering.addLevel(level7); - let level8 = new Level(8, "bullet", "•", "left"); + const level8 = new Level(8, "bullet", "•", "left"); level8.addParagraphProperty(new Indent(6480, 360)); level8.addRunProperty(new RunFonts("Wingdings", "default")); abstractNumbering.addLevel(level8); @@ -96,12 +96,11 @@ export class Numbering extends MultiPropertyXmlComponent { return num; } - clearVariables() { + public clearVariables(): void { super.clearVariables(); - _.forEach(this.root, element => { - console.log(element); + _.forEach(this.root, (element) => { element.clearVariables(); }); delete this.nextId; } -} \ No newline at end of file +} From a3d2323254967b80cb13012d54b0e345761ab24c Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 17:49:41 +0100 Subject: [PATCH 15/20] rename methods from addXXX to createXXX addXXX sounds like the argument should be XXX --- ts/numbering/index.ts | 8 ++++---- ts/tests/numberingTest.ts | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/ts/numbering/index.ts b/ts/numbering/index.ts index 93acc3e3b6..d840e8cfe5 100644 --- a/ts/numbering/index.ts +++ b/ts/numbering/index.ts @@ -34,7 +34,7 @@ export class Numbering extends MultiPropertyXmlComponent { this.nextId = 0; - const abstractNumbering = this.addAbstractNumbering(); + const abstractNumbering = this.createAbstractNumbering(); const level0 = new Level(0, "bullet", "•", "left"); level0.addParagraphProperty(new Indent(720, 360)); @@ -81,16 +81,16 @@ export class Numbering extends MultiPropertyXmlComponent { level8.addRunProperty(new RunFonts("Wingdings", "default")); abstractNumbering.addLevel(level8); - this.addConcreteNumbering(abstractNumbering); + this.createConcreteNumbering(abstractNumbering); } - public addAbstractNumbering(): AbstractNumbering { + public createAbstractNumbering(): AbstractNumbering { const num = new AbstractNumbering(this.nextId++); this.root.push(num); return num; } - public addConcreteNumbering(abstractNumbering: AbstractNumbering): Num { + public createConcreteNumbering(abstractNumbering: AbstractNumbering): Num { const num = new Num(this.nextId++, abstractNumbering.id); this.root.push(num); return num; diff --git a/ts/tests/numberingTest.ts b/ts/tests/numberingTest.ts index fef296f69d..bdd1452073 100644 --- a/ts/tests/numberingTest.ts +++ b/ts/tests/numberingTest.ts @@ -46,32 +46,32 @@ describe("Numbering", () => { }); }); - describe("#addAbstractNumbering", () => { + describe("#createAbstractNumbering", () => { it("returns a new AbstractNumbering instance", () => { - const a2 = numbering.addAbstractNumbering(); + const a2 = numbering.createAbstractNumbering(); expect(a2).to.be.instanceof(AbstractNumbering); }); it("assigns a unique ID to each abstract numbering it creates", () => { - const a2 = numbering.addAbstractNumbering(); - const a3 = numbering.addAbstractNumbering(); + const a2 = numbering.createAbstractNumbering(); + const a3 = numbering.createAbstractNumbering(); expect(a2.id).not.to.equal(a3.id); }); }); - describe("#addConcreteNumbering", () => { + describe("#createConcreteNumbering", () => { it("returns a new Num instance with its abstract ID set to the AbstractNumbering's ID", () => { - const a2 = numbering.addAbstractNumbering(); - const n = numbering.addConcreteNumbering(a2); + const a2 = numbering.createAbstractNumbering(); + const n = numbering.createConcreteNumbering(a2); expect(n).to.be.instanceof(Num); const tree = new Formatter().format(numbering); expect(n.id).to.equal(a2.id); }); it("assigns a unique ID to each concrete numbering it creates", () => { - const a2 = numbering.addAbstractNumbering(); - const n = numbering.addConcreteNumbering(a2); - const n2 = numbering.addConcreteNumbering(a2); + const a2 = numbering.createAbstractNumbering(); + const n = numbering.createConcreteNumbering(a2); + const n2 = numbering.createConcreteNumbering(a2); expect(n.id).not.to.equal(n2.id); }); }); From 1fe7ab90f259159d1cb4ef3a11abb8638c841cc8 Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 18:03:24 +0100 Subject: [PATCH 16/20] add createLevel method to AbstractNumbering --- ts/numbering/abstract-numbering.ts | 6 ++++++ ts/tests/numberingTest.ts | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/ts/numbering/abstract-numbering.ts b/ts/numbering/abstract-numbering.ts index 8856d0b1e3..370eb75e14 100644 --- a/ts/numbering/abstract-numbering.ts +++ b/ts/numbering/abstract-numbering.ts @@ -35,6 +35,12 @@ export class AbstractNumbering extends XmlComponent { this.root.push(level); } + public createLevel(num: number, format: string, text: string, align: string) { + const level = new Level(num, format, text, align); + this.addLevel(level); + return level; + } + public clearVariables(): void { _.forEach(this.root, (element) => { element.clearVariables(); diff --git a/ts/tests/numberingTest.ts b/ts/tests/numberingTest.ts index bdd1452073..4eeff4b41d 100644 --- a/ts/tests/numberingTest.ts +++ b/ts/tests/numberingTest.ts @@ -82,4 +82,17 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(5); expect(abstractNumbering.id).to.equal(5); }); + + describe("#createLevel", () => { + it("creates a level with the given characteristics", () => { + const abstractNumbering = new AbstractNumbering(1); + const level = abstractNumbering.createLevel(3, "lowerLetter", "%1)", "start"); + const tree = new Formatter().format(level); + expect(tree['w:lvl']).to.include({_attr: {"w:ilvl": 3, "w15:tentative": 1}}) + expect(tree['w:lvl']).to.include({"w:start": [{_attr: {"w:val": 1}}]}) + expect(tree['w:lvl']).to.include({"w:lvlJc": [{_attr: {"w:val": "start"}}]}) + expect(tree['w:lvl']).to.include({"w:numFmt": [{_attr: {"w:val": "lowerLetter"}}]}) + expect(tree['w:lvl']).to.include({"w:lvlText": [{"_attr": {"w:val": "%1)"}}]}) + }); + }); }); From 4b300e4def90acda563f7a9f365226f7a19a664c Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 18:03:39 +0100 Subject: [PATCH 17/20] add fluent interface for defining abstract numbering levels --- ts/numbering/index.ts | 63 +++++++++++++++++++------------------------ ts/numbering/level.ts | 6 +++-- 2 files changed, 31 insertions(+), 38 deletions(-) diff --git a/ts/numbering/index.ts b/ts/numbering/index.ts index d840e8cfe5..b813c665cf 100644 --- a/ts/numbering/index.ts +++ b/ts/numbering/index.ts @@ -36,50 +36,41 @@ export class Numbering extends MultiPropertyXmlComponent { const abstractNumbering = this.createAbstractNumbering(); - const level0 = new Level(0, "bullet", "•", "left"); - level0.addParagraphProperty(new Indent(720, 360)); - level0.addRunProperty(new RunFonts("Symbol", "default")); - abstractNumbering.addLevel(level0); + abstractNumbering.createLevel(0, "bullet", "•", "left") + .addParagraphProperty(new Indent(720, 360)) + .addRunProperty(new RunFonts("Symbol", "default")); - const level1 = new Level(1, "bullet", "o", "left"); - level1.addParagraphProperty(new Indent(1440, 360)); - level1.addRunProperty(new RunFonts("Courier New", "default")); - abstractNumbering.addLevel(level1); + abstractNumbering.createLevel(1, "bullet", "o", "left") + .addParagraphProperty(new Indent(1440, 360)) + .addRunProperty(new RunFonts("Courier New", "default")); - const level2 = new Level(2, "bullet", "•", "left"); - level2.addParagraphProperty(new Indent(2160, 360)); - level2.addRunProperty(new RunFonts("Wingdings", "default")); - abstractNumbering.addLevel(level2); + abstractNumbering.createLevel(2, "bullet", "•", "left") + .addParagraphProperty(new Indent(2160, 360)) + .addRunProperty(new RunFonts("Wingdings", "default")); - const level3 = new Level(3, "bullet", "•", "left"); - level3.addParagraphProperty(new Indent(2880, 360)); - level3.addRunProperty(new RunFonts("Symbol", "default")); - abstractNumbering.addLevel(level3); + abstractNumbering.createLevel(3, "bullet", "•", "left") + .addParagraphProperty(new Indent(2880, 360)) + .addRunProperty(new RunFonts("Symbol", "default")); - const level4 = new Level(4, "bullet", "o", "left"); - level4.addParagraphProperty(new Indent(3600, 360)); - level4.addRunProperty(new RunFonts("Courier New", "default")); - abstractNumbering.addLevel(level4); + abstractNumbering.createLevel(4, "bullet", "o", "left") + .addParagraphProperty(new Indent(3600, 360)) + .addRunProperty(new RunFonts("Courier New", "default")); - const level5 = new Level(5, "bullet", "•", "left"); - level5.addParagraphProperty(new Indent(4320, 360)); - level5.addRunProperty(new RunFonts("Wingdings", "default")); - abstractNumbering.addLevel(level5); + abstractNumbering.createLevel(5, "bullet", "•", "left") + .addParagraphProperty(new Indent(4320, 360)) + .addRunProperty(new RunFonts("Wingdings", "default")); - const level6 = new Level(6, "bullet", "•", "left"); - level6.addParagraphProperty(new Indent(5040, 360)); - level6.addRunProperty(new RunFonts("Symbol", "default")); - abstractNumbering.addLevel(level6); + abstractNumbering.createLevel(6, "bullet", "•", "left") + .addParagraphProperty(new Indent(5040, 360)) + .addRunProperty(new RunFonts("Symbol", "default")); - const level7 = new Level(7, "bullet", "o", "left"); - level7.addParagraphProperty(new Indent(5760, 360)); - level7.addRunProperty(new RunFonts("Courier New", "default")); - abstractNumbering.addLevel(level7); + abstractNumbering.createLevel(7, "bullet", "o", "left") + .addParagraphProperty(new Indent(5760, 360)) + .addRunProperty(new RunFonts("Courier New", "default")); - const level8 = new Level(8, "bullet", "•", "left"); - level8.addParagraphProperty(new Indent(6480, 360)); - level8.addRunProperty(new RunFonts("Wingdings", "default")); - abstractNumbering.addLevel(level8); + abstractNumbering.createLevel(8, "bullet", "•", "left") + .addParagraphProperty(new Indent(6480, 360)) + .addRunProperty(new RunFonts("Wingdings", "default")); this.createConcreteNumbering(abstractNumbering); } diff --git a/ts/numbering/level.ts b/ts/numbering/level.ts index 113027833b..54937cbf63 100644 --- a/ts/numbering/level.ts +++ b/ts/numbering/level.ts @@ -88,11 +88,13 @@ export class Level extends XmlComponent { delete this.runProperties; } - public addParagraphProperty(property: XmlComponent): void { + public addParagraphProperty(property: XmlComponent): Level { this.paragraphProperties.push(property); + return this; } - public addRunProperty(property: XmlComponent): void { + public addRunProperty(property: XmlComponent): Level { this.runProperties.push(property); + return this; } } From 0e9532d835a53cdab9bd55d8963728f4b98680a7 Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 18:23:00 +0100 Subject: [PATCH 18/20] added #setNumbering method to paragraph --- ts/docx/paragraph/index.ts | 9 ++++- ts/tests/docx/paragraph/paragraphTests.ts | 46 ++++++++++++++++++++++- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/ts/docx/paragraph/index.ts b/ts/docx/paragraph/index.ts index 9a6e27dcf9..9b1c0cc08d 100644 --- a/ts/docx/paragraph/index.ts +++ b/ts/docx/paragraph/index.ts @@ -6,6 +6,7 @@ import {ParagraphProperties} from "./properties"; import {MaxRightTabStop, LeftTabStop} from "./tab-stop"; import {Style} from "./style"; import {NumberProperties} from "./unordered-list"; +import { Num } from "../../numbering/num"; class Alignment extends XmlComponent { @@ -109,4 +110,10 @@ export class Paragraph extends XmlComponent { this.properties.push(new NumberProperties(1, 0)); return this; } -} \ No newline at end of file + + public setNumbering(numbering: Num, indentLevel: number): Paragraph { + this.properties.push(new Style("ListParagraph")); + this.properties.push(new NumberProperties(numbering.id, indentLevel)); + return this; + } +} diff --git a/ts/tests/docx/paragraph/paragraphTests.ts b/ts/tests/docx/paragraph/paragraphTests.ts index cda3632e33..81fb18564d 100644 --- a/ts/tests/docx/paragraph/paragraphTests.ts +++ b/ts/tests/docx/paragraph/paragraphTests.ts @@ -1,5 +1,7 @@ import * as docx from "../../../docx"; -import { assert } from "chai"; +import { Formatter } from "../../../export/formatter"; +import { Numbering } from "../../../numbering"; +import { assert, expect } from "chai"; function jsonify(obj: Object) { let stringifiedJson = JSON.stringify(obj); @@ -113,4 +115,44 @@ describe("Paragraph", () => { assert.isDefined(newJson.root[0].root[2]); }); }); -}); \ No newline at end of file + + describe("#setNumbering", () => { + it("should add list paragraph style to JSON", () => { + const numbering = new Numbering(); + const numberedAbstract = numbering.createAbstractNumbering(); + numberedAbstract.createLevel(0, "lowerLetter", "%1)", "start"); + const letterNumbering = numbering.createConcreteNumbering(numberedAbstract); + + paragraph.setNumbering(letterNumbering, 0); + let newJson = jsonify(paragraph); + assert.equal(newJson.root[0].root[1].root[0].root.val, "ListParagraph"); + }); + + it("it should add numbered properties", () => { + const numbering = new Numbering(); + const numberedAbstract = numbering.createAbstractNumbering(); + numberedAbstract.createLevel(0, "lowerLetter", "%1)", "start"); + const letterNumbering = numbering.createConcreteNumbering(numberedAbstract); + + paragraph.setNumbering(letterNumbering, 0); + const tree = new Formatter().format(paragraph); + console.log(JSON.stringify(tree, null, 2)); + expect(tree).to.deep.equal({ + "w:p": [ + { + "w:pPr": [ + {"_attr": {}}, + {"w:pStyle": [{"_attr": {"w:val": "ListParagraph"}}]}, + { + "w:numPr": [ + {"w:ilvl": [{"_attr": {"w:val": 0}}]}, + {"w:numId": [{"_attr": {"w:val": letterNumbering.id}}]} + ] + }, + ], + }, + ] + }) + }); + }); +}); From cee515e43f7995f486c2ec9bd841326197692e17 Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 19:07:44 +0100 Subject: [PATCH 19/20] added Numbering to top-level exports and added docs on usage --- docs/numbering.md | 103 ++++++++++++++++++++++++++++++++++++++++++++++ ts/index.ts | 3 +- 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 docs/numbering.md diff --git a/docs/numbering.md b/docs/numbering.md new file mode 100644 index 0000000000..12cce9480f --- /dev/null +++ b/docs/numbering.md @@ -0,0 +1,103 @@ +# Bullets and numbering + +DOCX is quite flexible in its bullets and numbering system, allowing +the user great freedom in how bullets and numbers are to be styled and +displayed. E.g., numbers can be shown using Arabic numerals, roman +numerals, or even ordinal words ("one", "two", "three", ...). The +format also supports re-using bullets/numbering styles throughout the +document, so that different lists using the same style need not +redefine them. + +Because of this flexibility, bullets and numbering in DOCX involves a +couple of moving pieces: + +1. Document-level bullets/numbering definitions (abstract) +2. Document-level bullets/numbering definitions (concrete) +3. Paragraph-level bullets/numbering selection + + +## Document-level bullets/numbering definitions (abstract) + +Every document contains a set of abstract bullets/numbering +definitions which define the formatting and layout of paragraphs using +those bullets/numbering. An abstract numbering system defines how +bullets/numbers are to be shown for lists, including any sublists that +may be used. Thus each abstract definition includes a series of +*levels* which form a sequence starting at 0 indicating the top-level +list look and increasing from there to descibe the sublists, then +sub-sublists, etc. Each level includes the following properties: + +- **level**: This its 0-based index in the defintion stack +- **numberFormat**: This indicates how the bullet or number should be + generated. Options include `bullet` (meaning don't count), `decimal` + (arabic numerals), `upperRoman`, `lowerRoman`, `hex`, and many + more. +- **levelText**: This is a format string using the output of the + `numberFormat` function and generating a string to insert before + every item in the list. You may use `%1`, `%2`, ... to reference the + numbers from each numbering level before this one. Thus a level + text of `%d)` with a number format of `lowerLetter` would result in + the sequence "a)", "b)", ... +- and a few others, which you can see in the OXML spec section 17.9.6 + + +## Document-level bullets/numbering defintions (concrete) + +Concrete definitions are sort of like concrete subclasses of the +abstract defintions. They indicate their parent and are allowed to +override certain level definitions. Thus two lists that differ only in +how sub-sub-lists are to be displayed can share the same abstract +numbering definition and have slightly different concrete definitions. + + +## Paragraph-level bullets/numbering selection + +In order to use a bullets/numbering definition (which must be +concrete), paragraphs need to select it, similar to applying a CSS +class to an element, using both the concrete numbering definition ID +and the level number that the paragraph should be at. Additionally, MS +Word and LibreOffice typically apply a "ListParagraph" style to +paragraphs that are being numbered. + + +## Using bullets/numbering in `docx` + +`docx` includes a pre-defined bullet style which you can add to your +paragraphs using `para.bullets()`. If you require different bullet +styles or numbering of any kind, you'll have to use the +`docx.Numbering` class. + +First you need to create a new numbering container class and use it to +create your abstract numbering style, define your levels, and creat +your concreate numbering style: + +```js +const numbering = new Numbering(); + +const abstractNum = numbering.createAbstractNumbering(); +abstractNum.createLevel(0, "upperRoman", "%1", "start") + .addParagraphProperty(new Indent(720, 260)); +abstractNum.createLevel(1, "decimal", "%2.", "start") + .addParagraphProperty(new Indent(1440, 980)); +abstractNum.createLevel(2, "lowerLetter", "%3)", "start") + .addParagraphProperty(new Indent(2160, 1700)); + +const concrete = numbering.createConcreteNumbering(numberedAbstract); +``` + +You can then apply your concrete style to paragraphs using their +`#setNumbering` method: + +```js +topLevelP.setNumbering(concrete, 0); +subP.setNumbering(concrete, 1); +subSubP.setNumbering(concrete, 2); +``` + +Finally, you need to let your exporter know about your numbering +styles when you're ready to render the document: + +```js +const packer = new Packer(doc, undefined, undefined, numbering); +packer.pack(myOutput); +``` diff --git a/ts/index.ts b/ts/index.ts index b7537fd38f..800814ea0b 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -1,2 +1,3 @@ export * from "./docx"; -export * from "./export"; \ No newline at end of file +export * from "./export"; +export { Numbering } from './numbering'; From f7b90d4ff397bf3128818e457e0d730c799d3390 Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 8 Mar 2017 19:18:24 +0100 Subject: [PATCH 20/20] add default argument "start" to createLevel --- ts/numbering/abstract-numbering.ts | 2 +- ts/tests/numberingTest.ts | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ts/numbering/abstract-numbering.ts b/ts/numbering/abstract-numbering.ts index 370eb75e14..6e344df114 100644 --- a/ts/numbering/abstract-numbering.ts +++ b/ts/numbering/abstract-numbering.ts @@ -35,7 +35,7 @@ export class AbstractNumbering extends XmlComponent { this.root.push(level); } - public createLevel(num: number, format: string, text: string, align: string) { + public createLevel(num: number, format: string, text: string, align: string="start") { const level = new Level(num, format, text, align); this.addLevel(level); return level; diff --git a/ts/tests/numberingTest.ts b/ts/tests/numberingTest.ts index 4eeff4b41d..7b562567a8 100644 --- a/ts/tests/numberingTest.ts +++ b/ts/tests/numberingTest.ts @@ -86,7 +86,18 @@ describe("AbstractNumbering", () => { describe("#createLevel", () => { it("creates a level with the given characteristics", () => { const abstractNumbering = new AbstractNumbering(1); - const level = abstractNumbering.createLevel(3, "lowerLetter", "%1)", "start"); + const level = abstractNumbering.createLevel(3, "lowerLetter", "%1)", "end"); + const tree = new Formatter().format(level); + expect(tree['w:lvl']).to.include({_attr: {"w:ilvl": 3, "w15:tentative": 1}}) + expect(tree['w:lvl']).to.include({"w:start": [{_attr: {"w:val": 1}}]}) + expect(tree['w:lvl']).to.include({"w:lvlJc": [{_attr: {"w:val": "end"}}]}) + expect(tree['w:lvl']).to.include({"w:numFmt": [{_attr: {"w:val": "lowerLetter"}}]}) + expect(tree['w:lvl']).to.include({"w:lvlText": [{"_attr": {"w:val": "%1)"}}]}) + }); + + it("uses 'start' as the default alignment", () => { + const abstractNumbering = new AbstractNumbering(1); + const level = abstractNumbering.createLevel(3, "lowerLetter", "%1)"); const tree = new Formatter().format(level); expect(tree['w:lvl']).to.include({_attr: {"w:ilvl": 3, "w15:tentative": 1}}) expect(tree['w:lvl']).to.include({"w:start": [{_attr: {"w:val": 1}}]})