103
docs/numbering.md
Normal file
103
docs/numbering.md
Normal file
@ -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);
|
||||
```
|
@ -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 {
|
||||
|
||||
@ -106,7 +107,13 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
public setNumbering(numbering: Num, indentLevel: number): Paragraph {
|
||||
this.properties.push(new Style("ListParagraph"));
|
||||
this.properties.push(new NumberProperties(numbering.id, indentLevel));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,2 +1,3 @@
|
||||
export * from "./docx";
|
||||
export * from "./export";
|
||||
export * from "./export";
|
||||
export { Numbering } from './numbering';
|
||||
|
@ -1,42 +1,50 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
export class AbstractNumbering extends XmlComponent {
|
||||
public id: number;
|
||||
|
||||
constructor(id: number) {
|
||||
super("w:abstractNum");
|
||||
this.root.push(new AbstractNumberingAttributes({
|
||||
abstractNumId: id,
|
||||
restartNumberingAfterBreak: 0
|
||||
restartNumberingAfterBreak: 0,
|
||||
}));
|
||||
this.root.push(new MultiLevelType("hybridMultilevel"));
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
addLevel(level: Level): void {
|
||||
public addLevel(level: Level): void {
|
||||
this.root.push(level);
|
||||
}
|
||||
|
||||
clearVariables() {
|
||||
_.forEach(this.root, element => {
|
||||
public createLevel(num: number, format: string, text: string, align: string="start") {
|
||||
const level = new Level(num, format, text, align);
|
||||
this.addLevel(level);
|
||||
return level;
|
||||
}
|
||||
|
||||
public clearVariables(): void {
|
||||
_.forEach(this.root, (element) => {
|
||||
element.clearVariables();
|
||||
});
|
||||
delete this.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
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;
|
||||
|
||||
constructor() {
|
||||
super("w:numbering");
|
||||
@ -28,65 +29,69 @@ 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",
|
||||
}));
|
||||
|
||||
let abstractNumbering = new AbstractNumbering(0);
|
||||
this.nextId = 0;
|
||||
|
||||
let level0 = new Level(0, "bullet", "•", "left");
|
||||
level0.addParagraphProperty(new Indent(720, 360));
|
||||
level0.addRunProperty(new RunFonts("Symbol", "default"));
|
||||
abstractNumbering.addLevel(level0);
|
||||
const abstractNumbering = this.createAbstractNumbering();
|
||||
|
||||
let 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(0, "bullet", "•", "left")
|
||||
.addParagraphProperty(new Indent(720, 360))
|
||||
.addRunProperty(new RunFonts("Symbol", "default"));
|
||||
|
||||
let level2 = new Level(2, "bullet", "•", "left");
|
||||
level2.addParagraphProperty(new Indent(2160, 360));
|
||||
level2.addRunProperty(new RunFonts("Wingdings", "default"));
|
||||
abstractNumbering.addLevel(level2);
|
||||
abstractNumbering.createLevel(1, "bullet", "o", "left")
|
||||
.addParagraphProperty(new Indent(1440, 360))
|
||||
.addRunProperty(new RunFonts("Courier New", "default"));
|
||||
|
||||
let level3 = new Level(3, "bullet", "•", "left");
|
||||
level3.addParagraphProperty(new Indent(2880, 360));
|
||||
level3.addRunProperty(new RunFonts("Symbol", "default"));
|
||||
abstractNumbering.addLevel(level3);
|
||||
abstractNumbering.createLevel(2, "bullet", "•", "left")
|
||||
.addParagraphProperty(new Indent(2160, 360))
|
||||
.addRunProperty(new RunFonts("Wingdings", "default"));
|
||||
|
||||
let 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(3, "bullet", "•", "left")
|
||||
.addParagraphProperty(new Indent(2880, 360))
|
||||
.addRunProperty(new RunFonts("Symbol", "default"));
|
||||
|
||||
let level5 = new Level(5, "bullet", "•", "left");
|
||||
level5.addParagraphProperty(new Indent(4320, 360));
|
||||
level5.addRunProperty(new RunFonts("Wingdings", "default"));
|
||||
abstractNumbering.addLevel(level5);
|
||||
abstractNumbering.createLevel(4, "bullet", "o", "left")
|
||||
.addParagraphProperty(new Indent(3600, 360))
|
||||
.addRunProperty(new RunFonts("Courier New", "default"));
|
||||
|
||||
let level6 = new Level(6, "bullet", "•", "left");
|
||||
level6.addParagraphProperty(new Indent(5040, 360));
|
||||
level6.addRunProperty(new RunFonts("Symbol", "default"));
|
||||
abstractNumbering.addLevel(level6);
|
||||
abstractNumbering.createLevel(5, "bullet", "•", "left")
|
||||
.addParagraphProperty(new Indent(4320, 360))
|
||||
.addRunProperty(new RunFonts("Wingdings", "default"));
|
||||
|
||||
let level7 = new Level(4, "bullet", "o", "left");
|
||||
level7.addParagraphProperty(new Indent(5760, 360));
|
||||
level7.addRunProperty(new RunFonts("Courier New", "default"));
|
||||
abstractNumbering.addLevel(level7);
|
||||
abstractNumbering.createLevel(6, "bullet", "•", "left")
|
||||
.addParagraphProperty(new Indent(5040, 360))
|
||||
.addRunProperty(new RunFonts("Symbol", "default"));
|
||||
|
||||
let level8 = new Level(5, "bullet", "•", "left");
|
||||
level8.addParagraphProperty(new Indent(6480, 360));
|
||||
level8.addRunProperty(new RunFonts("Wingdings", "default"));
|
||||
abstractNumbering.addLevel(level8);
|
||||
abstractNumbering.createLevel(7, "bullet", "o", "left")
|
||||
.addParagraphProperty(new Indent(5760, 360))
|
||||
.addRunProperty(new RunFonts("Courier New", "default"));
|
||||
|
||||
this.root.push(abstractNumbering);
|
||||
this.root.push(new Num(1, 0));
|
||||
abstractNumbering.createLevel(8, "bullet", "•", "left")
|
||||
.addParagraphProperty(new Indent(6480, 360))
|
||||
.addRunProperty(new RunFonts("Wingdings", "default"));
|
||||
|
||||
this.createConcreteNumbering(abstractNumbering);
|
||||
}
|
||||
|
||||
clearVariables() {
|
||||
public createAbstractNumbering(): AbstractNumbering {
|
||||
const num = new AbstractNumbering(this.nextId++);
|
||||
this.root.push(num);
|
||||
return num;
|
||||
}
|
||||
|
||||
public createConcreteNumbering(abstractNumbering: AbstractNumbering): Num {
|
||||
const num = new Num(this.nextId++, abstractNumbering.id);
|
||||
this.root.push(num);
|
||||
return num;
|
||||
}
|
||||
|
||||
public clearVariables(): void {
|
||||
super.clearVariables();
|
||||
_.forEach(this.root, element => {
|
||||
console.log(element);
|
||||
_.forEach(this.root, (element) => {
|
||||
element.clearVariables();
|
||||
});
|
||||
delete this.nextId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,13 @@ export class Level extends XmlComponent {
|
||||
delete this.runProperties;
|
||||
}
|
||||
|
||||
addParagraphProperty(property: XmlComponent): void {
|
||||
public addParagraphProperty(property: XmlComponent): Level {
|
||||
this.paragraphProperties.push(property);
|
||||
return this;
|
||||
}
|
||||
|
||||
addRunProperty(property: XmlComponent): void {
|
||||
public addRunProperty(property: XmlComponent): Level {
|
||||
this.runProperties.push(property);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,42 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
export class Num extends XmlComponent {
|
||||
public id: number;
|
||||
|
||||
constructor(numId: number, abstractNumId: number) {
|
||||
super("w:num");
|
||||
this.root.push(new NumAttributes({
|
||||
numId: numId
|
||||
numId: numId,
|
||||
}));
|
||||
this.root.push(new AbstractNumId(abstractNumId));
|
||||
this.id = numId;
|
||||
}
|
||||
}
|
||||
|
||||
public clearVariables(): void {
|
||||
super.clearVariables();
|
||||
delete this.id;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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}}]}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,21 +1,109 @@
|
||||
import { assert } from "chai";
|
||||
import { expect } from "chai";
|
||||
import { Formatter } from "../export/formatter";
|
||||
import { Numbering } from "../numbering";
|
||||
import { AbstractNumbering } from "../numbering/abstract-numbering";
|
||||
import { Num } from "../numbering/num";
|
||||
|
||||
function jsonify(obj: Object) {
|
||||
let stringifiedJson = JSON.stringify(obj);
|
||||
return JSON.parse(stringifiedJson);
|
||||
function jsonify(obj: object) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
describe("", () => {
|
||||
describe("Numbering", () => {
|
||||
|
||||
let numbering = new Numbering;
|
||||
let numbering: 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}}]}]},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createAbstractNumbering", () => {
|
||||
it("returns a new AbstractNumbering instance", () => {
|
||||
const a2 = numbering.createAbstractNumbering();
|
||||
expect(a2).to.be.instanceof(AbstractNumbering);
|
||||
});
|
||||
|
||||
it("assigns a unique ID to each abstract numbering it creates", () => {
|
||||
const a2 = numbering.createAbstractNumbering();
|
||||
const a3 = numbering.createAbstractNumbering();
|
||||
expect(a2.id).not.to.equal(a3.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createConcreteNumbering", () => {
|
||||
it("returns a new Num instance with its abstract ID set to the AbstractNumbering's ID", () => {
|
||||
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.createAbstractNumbering();
|
||||
const n = numbering.createConcreteNumbering(a2);
|
||||
const n2 = numbering.createConcreteNumbering(a2);
|
||||
expect(n.id).not.to.equal(n2.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("AbstractNumbering", () => {
|
||||
it("stores its ID at its .id property", () => {
|
||||
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)", "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}}]})
|
||||
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)"}}]})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user