Declarative numbering work
This commit is contained in:
@ -20,11 +20,7 @@ This method is useful for adding different [text](text.md) with different styles
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
children: [
|
children: [new TextRun("Lorem Ipsum Foo Bar"), new TextRun("Hello World"), new SymbolRun("F071")],
|
||||||
new TextRun("Lorem Ipsum Foo Bar"),
|
|
||||||
new TextRun("Hello World"),
|
|
||||||
new SymbolRun("F071"),
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -60,27 +56,27 @@ doc.addSection({
|
|||||||
|
|
||||||
This is the list of options for a paragraph. A detailed explanation is below:
|
This is the list of options for a paragraph. A detailed explanation is below:
|
||||||
|
|
||||||
| Property | Type | Mandatory? | Possible Values |
|
| Property | Type | Mandatory? | Possible Values |
|
||||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------- |
|
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------- |
|
||||||
| [text](#text) | `string` | Optional | |
|
| [text](#text) | `string` | Optional | |
|
||||||
| [heading](#heading) | `HeadingLevel` | Optional | `HEADING_1`, `HEADING_2`, `HEADING_3`, `HEADING_4`, `HEADING_5`, `HEADING_6`, `TITLE` |
|
| [heading](#heading) | `HeadingLevel` | Optional | `HEADING_1`, `HEADING_2`, `HEADING_3`, `HEADING_4`, `HEADING_5`, `HEADING_6`, `TITLE` |
|
||||||
| [border](#border) | `IBorderOptions` | Optional | `top`, `bottom`, `left`, `right`. Each of these are of type IBorderPropertyOptions. Click here for Example |
|
| [border](#border) | `IBorderOptions` | Optional | `top`, `bottom`, `left`, `right`. Each of these are of type IBorderPropertyOptions. Click here for Example |
|
||||||
| [spacing](#spacing) | `ISpacingProperties` | Optional | See below for ISpacingProperties |
|
| [spacing](#spacing) | `ISpacingProperties` | Optional | See below for ISpacingProperties |
|
||||||
| [outlineLevel](#outline-level) | `number` | Optional | |
|
| [outlineLevel](#outline-level) | `number` | Optional | |
|
||||||
| alignment | `AlignmentType` | Optional | |
|
| alignment | `AlignmentType` | Optional | |
|
||||||
| heading | `HeadingLevel` | Optional | |
|
| heading | `HeadingLevel` | Optional | |
|
||||||
| bidirectional | `boolean` | Optional | |
|
| bidirectional | `boolean` | Optional | |
|
||||||
| thematicBreak | `boolean` | Optional | |
|
| thematicBreak | `boolean` | Optional | |
|
||||||
| pageBreakBefore | `boolean` | Optional | |
|
| pageBreakBefore | `boolean` | Optional | |
|
||||||
| contextualSpacing | `boolean` | Optional | |
|
| contextualSpacing | `boolean` | Optional | |
|
||||||
| indent | `IIndentAttributesProperties` | Optional | |
|
| indent | `IIndentAttributesProperties` | Optional | |
|
||||||
| keepLines | `boolean` | Optional | |
|
| keepLines | `boolean` | Optional | |
|
||||||
| keepNext | `boolean` | Optional | |
|
| keepNext | `boolean` | Optional | |
|
||||||
| children | `(TextRun or PictureRun or Hyperlink)[]` | Optional | |
|
| children | `(TextRun or PictureRun or Hyperlink)[]` | Optional | |
|
||||||
| style | `string` | Optional | |
|
| style | `string` | Optional | |
|
||||||
| tabStop | `{ left?: ITabStopOptions; right?: ITabStopOptions; maxRight?: { leader: LeaderType; }; center?: ITabStopOptions }` | Optional | |
|
| tabStop | `{ left?: ITabStopOptions; right?: ITabStopOptions; maxRight?: { leader: LeaderType; }; center?: ITabStopOptions }` | Optional | |
|
||||||
| bullet | `{ level: number }` | Optional | |
|
| bullet | `{ level: number }` | Optional | |
|
||||||
| numbering | `{ num: Num; level: number; custom?: boolean }` | Optional | |
|
| numbering | `{ num: ConcreteNumbering; level: number; custom?: boolean }` | Optional | |
|
||||||
|
|
||||||
## Text
|
## Text
|
||||||
|
|
||||||
@ -252,10 +248,7 @@ To move to a new page (insert a page break):
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const paragraph = new docx.Paragraph({
|
const paragraph = new docx.Paragraph({
|
||||||
children: [
|
children: [new TextRun("Amazing Heading"), new PageBreak()],
|
||||||
new TextRun("Amazing Heading"),
|
|
||||||
new PageBreak(),
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
import { DocumentAttributes } from "../document/document-attributes";
|
import { DocumentAttributes } from "../document/document-attributes";
|
||||||
|
import { IAbstractNumberingOptions } from "../numbering";
|
||||||
import { IStylesOptions } from "../styles";
|
import { IStylesOptions } from "../styles";
|
||||||
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
|
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
|
||||||
|
|
||||||
@ -14,6 +15,7 @@ export interface IPropertiesOptions {
|
|||||||
readonly revision?: string;
|
readonly revision?: string;
|
||||||
readonly externalStyles?: string;
|
readonly externalStyles?: string;
|
||||||
readonly styles?: IStylesOptions;
|
readonly styles?: IStylesOptions;
|
||||||
|
readonly numbering?: IAbstractNumberingOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CoreProperties extends XmlComponent {
|
export class CoreProperties extends XmlComponent {
|
||||||
|
@ -71,7 +71,9 @@ export class File {
|
|||||||
sections: ISectionOptions[] = [],
|
sections: ISectionOptions[] = [],
|
||||||
) {
|
) {
|
||||||
this.coreProperties = new CoreProperties(options);
|
this.coreProperties = new CoreProperties(options);
|
||||||
this.numbering = new Numbering();
|
this.numbering = new Numbering({
|
||||||
|
levels: options.numbering ? options.numbering.levels : [],
|
||||||
|
});
|
||||||
this.docRelationships = new Relationships();
|
this.docRelationships = new Relationships();
|
||||||
this.fileRelationships = new Relationships();
|
this.fileRelationships = new Relationships();
|
||||||
this.appProperties = new AppProperties();
|
this.appProperties = new AppProperties();
|
||||||
|
665
src/file/numbering/abstract-numbering.spec.ts
Normal file
665
src/file/numbering/abstract-numbering.spec.ts
Normal file
@ -0,0 +1,665 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "export/formatter";
|
||||||
|
import { EMPTY_OBJECT } from "file/xml-components";
|
||||||
|
|
||||||
|
import { AlignmentType, TabStopPosition } from "../paragraph";
|
||||||
|
import { UnderlineType } from "../paragraph/run/underline";
|
||||||
|
import { ShadingType } from "../table";
|
||||||
|
import { AbstractNumbering } from "./abstract-numbering";
|
||||||
|
|
||||||
|
describe.only("AbstractNumbering", () => {
|
||||||
|
it("stores its ID at its .id property", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(5, {
|
||||||
|
levels: [],
|
||||||
|
});
|
||||||
|
expect(abstractNumbering.id).to.equal(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#createLevel", () => {
|
||||||
|
it("creates a level with the given characteristics", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 3,
|
||||||
|
format: "lowerLetter",
|
||||||
|
text: "%1)",
|
||||||
|
alignment: AlignmentType.END,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
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, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 3,
|
||||||
|
format: "lowerLetter",
|
||||||
|
text: "%1)",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
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)" } } });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("formatting methods: paragraph properties", () => {
|
||||||
|
it("#indent", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 720 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:ind": { _attr: { "w:left": 720 } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#spacing", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
spacing: { before: 50, after: 150 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:spacing": { _attr: { "w:before": 50, "w:after": 150 } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#center", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:jc": { _attr: { "w:val": "center" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#left", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:jc": { _attr: { "w:val": "left" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#right", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
alignment: AlignmentType.RIGHT,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:jc": { _attr: { "w:val": "right" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#justified", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
alignment: AlignmentType.JUSTIFIED,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:jc": { _attr: { "w:val": "both" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#thematicBreak", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
thematicBreak: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [
|
||||||
|
{
|
||||||
|
"w:pBdr": [
|
||||||
|
{
|
||||||
|
"w:bottom": {
|
||||||
|
_attr: {
|
||||||
|
"w:color": "auto",
|
||||||
|
"w:space": 1,
|
||||||
|
"w:val": "single",
|
||||||
|
"w:sz": 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#leftTabStop", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
leftTabStop: 1200,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [
|
||||||
|
{
|
||||||
|
"w:tabs": [{ "w:tab": { _attr: { "w:val": "left", "w:pos": 1200 } } }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#maxRightTabStop", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
rightTabStop: TabStopPosition.MAX,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [
|
||||||
|
{
|
||||||
|
"w:tabs": [{ "w:tab": { _attr: { "w:val": "right", "w:pos": 9026 } } }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#keepLines", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
keepLines: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:keepLines": EMPTY_OBJECT }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#keepNext", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
keepNext: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:keepNext": EMPTY_OBJECT }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("formatting methods: run properties", () => {
|
||||||
|
it("#size", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
size: 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#smallCaps", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
smallCaps: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:smallCaps": { _attr: { "w:val": true } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#allCaps", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
allCaps: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:caps": { _attr: { "w:val": true } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#strike", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
strike: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:strike": { _attr: { "w:val": true } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#doubleStrike", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
doubleStrike: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:dstrike": { _attr: { "w:val": true } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#subScript", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
subScript: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "subscript" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#superScript", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
superScript: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "superscript" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#font", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
font: "Times",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [
|
||||||
|
{ "w:rFonts": { _attr: { "w:ascii": "Times", "w:cs": "Times", "w:eastAsia": "Times", "w:hAnsi": "Times" } } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#bold", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
bold: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:b": { _attr: { "w:val": true } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#italics", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
italics: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:i": { _attr: { "w:val": true } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#highlight", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
highlight: "005599",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#shadow", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
shadow: {
|
||||||
|
type: ShadingType.PERCENT_10,
|
||||||
|
fill: "00FFFF",
|
||||||
|
color: "FF0000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#underline", () => {
|
||||||
|
it("should set underline to 'single' if no arguments are given", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
underline: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:u": { _attr: { "w:val": "single" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set the style if given", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
underline: {
|
||||||
|
type: UnderlineType.DOUBLE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:u": { _attr: { "w:val": "double" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set the style and color if given", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
underline: {
|
||||||
|
type: UnderlineType.DOUBLE,
|
||||||
|
color: "005599",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:u": { _attr: { "w:val": "double", "w:color": "005599" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#color", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
color: "123456",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:color": { _attr: { "w:val": "123456" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,5 +1,6 @@
|
|||||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
import { Level } from "./level";
|
|
||||||
|
import { ILevelsOptions, Level } from "./level";
|
||||||
import { MultiLevelType } from "./multi-level-type";
|
import { MultiLevelType } from "./multi-level-type";
|
||||||
|
|
||||||
interface IAbstractNumberingAttributesProperties {
|
interface IAbstractNumberingAttributesProperties {
|
||||||
@ -14,10 +15,14 @@ class AbstractNumberingAttributes extends XmlAttributeComponent<IAbstractNumberi
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IAbstractNumberingOptions {
|
||||||
|
readonly levels: ILevelsOptions[];
|
||||||
|
}
|
||||||
|
|
||||||
export class AbstractNumbering extends XmlComponent {
|
export class AbstractNumbering extends XmlComponent {
|
||||||
public readonly id: number;
|
public readonly id: number;
|
||||||
|
|
||||||
constructor(id: number) {
|
constructor(id: number, options: IAbstractNumberingOptions) {
|
||||||
super("w:abstractNum");
|
super("w:abstractNum");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new AbstractNumberingAttributes({
|
new AbstractNumberingAttributes({
|
||||||
@ -27,15 +32,9 @@ export class AbstractNumbering extends XmlComponent {
|
|||||||
);
|
);
|
||||||
this.root.push(new MultiLevelType("hybridMultilevel"));
|
this.root.push(new MultiLevelType("hybridMultilevel"));
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
|
||||||
|
|
||||||
public addLevel(level: Level): void {
|
for (const option of options.levels) {
|
||||||
this.root.push(level);
|
this.root.push(new Level(option));
|
||||||
}
|
}
|
||||||
|
|
||||||
public createLevel(num: number, format: string, text: string, align: string = "start"): Level {
|
|
||||||
const level = new Level(num, format, text, align);
|
|
||||||
this.addLevel(level);
|
|
||||||
return level;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
80
src/file/numbering/concrete-numbering.spec.ts
Normal file
80
src/file/numbering/concrete-numbering.spec.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "export/formatter";
|
||||||
|
|
||||||
|
import { LevelForOverride } from "./level";
|
||||||
|
import { ConcreteNumbering } from "./num";
|
||||||
|
|
||||||
|
describe("ConcreteNumbering", () => {
|
||||||
|
describe("#overrideLevel", () => {
|
||||||
|
let concreteNumbering;
|
||||||
|
beforeEach(() => {
|
||||||
|
concreteNumbering = new ConcreteNumbering(0, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets a new override level for the given level number", () => {
|
||||||
|
concreteNumbering.overrideLevel(3);
|
||||||
|
const tree = new Formatter().format(concreteNumbering);
|
||||||
|
expect(tree["w:num"]).to.include({
|
||||||
|
"w:lvlOverride": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:ilvl": 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:lvl": {
|
||||||
|
_attr: {
|
||||||
|
"w:ilvl": 3,
|
||||||
|
"w15:tentative": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets the startOverride element if start is given", () => {
|
||||||
|
concreteNumbering.overrideLevel(1, 9);
|
||||||
|
const tree = new Formatter().format(concreteNumbering);
|
||||||
|
expect(tree["w:num"]).to.include({
|
||||||
|
"w:lvlOverride": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:ilvl": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:startOverride": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": 9,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:lvl": {
|
||||||
|
_attr: {
|
||||||
|
"w:ilvl": 1,
|
||||||
|
"w15:tentative": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets the lvl element if overrideLevel.Level is accessed", () => {
|
||||||
|
const ol = concreteNumbering.overrideLevel(1);
|
||||||
|
expect(ol.Level).to.be.instanceof(LevelForOverride);
|
||||||
|
const tree = new Formatter().format(concreteNumbering);
|
||||||
|
expect(tree["w:num"]).to.include({
|
||||||
|
"w:lvlOverride": [
|
||||||
|
{ _attr: { "w:ilvl": 1 } },
|
||||||
|
{
|
||||||
|
"w:lvl": { _attr: { "w15:tentative": 1, "w:ilvl": 1 } },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -2,9 +2,7 @@ import { Attributes, XmlAttributeComponent, XmlComponent } from "file/xml-compon
|
|||||||
import {
|
import {
|
||||||
Alignment,
|
Alignment,
|
||||||
AlignmentType,
|
AlignmentType,
|
||||||
IIndentAttributesProperties,
|
|
||||||
Indent,
|
Indent,
|
||||||
ISpacingProperties,
|
|
||||||
KeepLines,
|
KeepLines,
|
||||||
KeepNext,
|
KeepNext,
|
||||||
Spacing,
|
Spacing,
|
||||||
@ -15,7 +13,7 @@ import {
|
|||||||
import { ParagraphProperties } from "../paragraph/properties";
|
import { ParagraphProperties } from "../paragraph/properties";
|
||||||
import * as formatting from "../paragraph/run/formatting";
|
import * as formatting from "../paragraph/run/formatting";
|
||||||
import { RunProperties } from "../paragraph/run/properties";
|
import { RunProperties } from "../paragraph/run/properties";
|
||||||
import { UnderlineType } from "../paragraph/run/underline";
|
import { IParagraphStyleOptions2, IRunStyleOptions } from "../styles/style-options";
|
||||||
|
|
||||||
interface ILevelAttributesProperties {
|
interface ILevelAttributesProperties {
|
||||||
readonly ilvl?: number;
|
readonly ilvl?: number;
|
||||||
@ -63,7 +61,7 @@ class LevelText extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LevelJc extends XmlComponent {
|
class LevelJc extends XmlComponent {
|
||||||
constructor(value: string) {
|
constructor(value: AlignmentType) {
|
||||||
super("w:lvlJc");
|
super("w:lvlJc");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new Attributes({
|
new Attributes({
|
||||||
@ -79,6 +77,19 @@ export enum LevelSuffix {
|
|||||||
TAB = "tab",
|
TAB = "tab",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ILevelsOptions {
|
||||||
|
readonly level: number;
|
||||||
|
readonly format: string;
|
||||||
|
readonly text: string;
|
||||||
|
readonly alignment?: AlignmentType;
|
||||||
|
readonly start?: number;
|
||||||
|
readonly suffix?: LevelSuffix;
|
||||||
|
readonly style?: {
|
||||||
|
readonly run?: IRunStyleOptions;
|
||||||
|
readonly paragraph?: IParagraphStyleOptions2;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class Suffix extends XmlComponent {
|
class Suffix extends XmlComponent {
|
||||||
constructor(value: LevelSuffix) {
|
constructor(value: LevelSuffix) {
|
||||||
super("w:suff");
|
super("w:suff");
|
||||||
@ -94,7 +105,7 @@ export class LevelBase extends XmlComponent {
|
|||||||
private readonly paragraphProperties: ParagraphProperties;
|
private readonly paragraphProperties: ParagraphProperties;
|
||||||
private readonly runProperties: RunProperties;
|
private readonly runProperties: RunProperties;
|
||||||
|
|
||||||
constructor(level: number, start?: number, numberFormat?: string, levelText?: string, lvlJc?: string) {
|
constructor({ level, format, text, alignment = AlignmentType.START, start = 1, style, suffix }: ILevelsOptions) {
|
||||||
super("w:lvl");
|
super("w:lvl");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new LevelAttributes({
|
new LevelAttributes({
|
||||||
@ -103,174 +114,122 @@ export class LevelBase extends XmlComponent {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (start !== undefined) {
|
this.root.push(new Start(start));
|
||||||
this.root.push(new Start(start));
|
this.root.push(new NumberFormat(format));
|
||||||
}
|
this.root.push(new LevelText(text));
|
||||||
if (numberFormat !== undefined) {
|
this.root.push(new LevelJc(alignment));
|
||||||
this.root.push(new NumberFormat(numberFormat));
|
|
||||||
}
|
|
||||||
if (levelText !== undefined) {
|
|
||||||
this.root.push(new LevelText(levelText));
|
|
||||||
}
|
|
||||||
if (lvlJc !== undefined) {
|
|
||||||
this.root.push(new LevelJc(lvlJc));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.paragraphProperties = new ParagraphProperties({});
|
this.paragraphProperties = new ParagraphProperties({});
|
||||||
this.runProperties = new RunProperties();
|
this.runProperties = new RunProperties();
|
||||||
|
|
||||||
this.root.push(this.paragraphProperties);
|
this.root.push(this.paragraphProperties);
|
||||||
this.root.push(this.runProperties);
|
this.root.push(this.runProperties);
|
||||||
}
|
|
||||||
|
|
||||||
public setSuffix(value: LevelSuffix): LevelBase {
|
if (suffix) {
|
||||||
this.root.push(new Suffix(value));
|
this.root.push(new Suffix(suffix));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public addParagraphProperty(property: XmlComponent): Level {
|
if (style) {
|
||||||
this.paragraphProperties.push(property);
|
if (style.run) {
|
||||||
return this;
|
if (style.run.size) {
|
||||||
}
|
this.runProperties.push(new formatting.Size(style.run.size));
|
||||||
|
}
|
||||||
|
|
||||||
public addRunProperty(property: XmlComponent): Level {
|
if (style.run.bold) {
|
||||||
this.runProperties.push(property);
|
this.runProperties.push(new formatting.Bold());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- Run formatting ---------------------- //
|
if (style.run.italics) {
|
||||||
|
this.runProperties.push(new formatting.Italics());
|
||||||
|
}
|
||||||
|
|
||||||
public size(twips: number): Level {
|
if (style.run.smallCaps) {
|
||||||
this.addRunProperty(new formatting.Size(twips));
|
this.runProperties.push(new formatting.SmallCaps());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public bold(): Level {
|
if (style.run.allCaps) {
|
||||||
this.addRunProperty(new formatting.Bold());
|
this.runProperties.push(new formatting.Caps());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public italics(): Level {
|
if (style.run.strike) {
|
||||||
this.addRunProperty(new formatting.Italics());
|
this.runProperties.push(new formatting.Strike());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public smallCaps(): Level {
|
if (style.run.doubleStrike) {
|
||||||
this.addRunProperty(new formatting.SmallCaps());
|
this.runProperties.push(new formatting.DoubleStrike());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public allCaps(): Level {
|
if (style.run.subScript) {
|
||||||
this.addRunProperty(new formatting.Caps());
|
this.runProperties.push(new formatting.SubScript());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public strike(): Level {
|
if (style.run.superScript) {
|
||||||
this.addRunProperty(new formatting.Strike());
|
this.runProperties.push(new formatting.SuperScript());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public doubleStrike(): Level {
|
if (style.run.underline) {
|
||||||
this.addRunProperty(new formatting.DoubleStrike());
|
this.runProperties.push(new formatting.Underline(style.run.underline.type, style.run.underline.color));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public subScript(): Level {
|
if (style.run.color) {
|
||||||
this.addRunProperty(new formatting.SubScript());
|
this.runProperties.push(new formatting.Color(style.run.color));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public superScript(): Level {
|
if (style.run.font) {
|
||||||
this.addRunProperty(new formatting.SuperScript());
|
this.runProperties.push(new formatting.RunFonts(style.run.font));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public underline(underlineType?: UnderlineType, color?: string): Level {
|
if (style.run.highlight) {
|
||||||
this.addRunProperty(new formatting.Underline(underlineType, color));
|
this.runProperties.push(new formatting.Highlight(style.run.highlight));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public color(color: string): Level {
|
if (style.run.shadow) {
|
||||||
this.addRunProperty(new formatting.Color(color));
|
this.runProperties.push(new formatting.Shading(style.run.shadow.type, style.run.shadow.fill, style.run.shadow.color));
|
||||||
return this;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public font(fontName: string): Level {
|
if (style.paragraph) {
|
||||||
this.addRunProperty(new formatting.RunFonts(fontName));
|
if (style.paragraph.alignment) {
|
||||||
return this;
|
this.paragraphProperties.push(new Alignment(style.paragraph.alignment));
|
||||||
}
|
}
|
||||||
|
|
||||||
public highlight(color: string): Level {
|
if (style.paragraph.thematicBreak) {
|
||||||
this.addRunProperty(new formatting.Highlight(color));
|
this.paragraphProperties.push(new ThematicBreak());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public shadow(value: string, fill: string, color: string): Level {
|
if (style.paragraph.rightTabStop) {
|
||||||
this.addRunProperty(new formatting.Shading(value, fill, color));
|
this.paragraphProperties.push(new TabStop(TabStopType.RIGHT, style.paragraph.rightTabStop));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
// --------------------- Paragraph formatting ------------------------ //
|
|
||||||
|
|
||||||
public center(): Level {
|
if (style.paragraph.leftTabStop) {
|
||||||
this.addParagraphProperty(new Alignment(AlignmentType.CENTER));
|
this.paragraphProperties.push(new TabStop(TabStopType.LEFT, style.paragraph.leftTabStop));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public left(): Level {
|
if (style.paragraph.indent) {
|
||||||
this.addParagraphProperty(new Alignment(AlignmentType.LEFT));
|
this.paragraphProperties.push(new Indent(style.paragraph.indent));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public right(): Level {
|
if (style.paragraph.spacing) {
|
||||||
this.addParagraphProperty(new Alignment(AlignmentType.RIGHT));
|
this.paragraphProperties.push(new Spacing(style.paragraph.spacing));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public justified(): Level {
|
if (style.paragraph.keepNext) {
|
||||||
this.addParagraphProperty(new Alignment(AlignmentType.BOTH));
|
this.paragraphProperties.push(new KeepNext());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public thematicBreak(): Level {
|
if (style.paragraph.keepLines) {
|
||||||
this.addParagraphProperty(new ThematicBreak());
|
this.paragraphProperties.push(new KeepLines());
|
||||||
return this;
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public rightTabStop(position: number): Level {
|
|
||||||
return this.addParagraphProperty(new TabStop(TabStopType.RIGHT, position));
|
|
||||||
}
|
|
||||||
|
|
||||||
public leftTabStop(position: number): Level {
|
|
||||||
this.addParagraphProperty(new TabStop(TabStopType.LEFT, position));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public indent(attrs: IIndentAttributesProperties): Level {
|
|
||||||
this.addParagraphProperty(new Indent(attrs));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public spacing(params: ISpacingProperties): Level {
|
|
||||||
this.addParagraphProperty(new Spacing(params));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public keepNext(): Level {
|
|
||||||
this.addParagraphProperty(new KeepNext());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public keepLines(): Level {
|
|
||||||
this.addParagraphProperty(new KeepLines());
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Level extends LevelBase {
|
export class Level extends LevelBase {
|
||||||
// This is the level that sits under abstractNum. We make a
|
// This is the level that sits under abstractNum. We make a
|
||||||
// handful of properties required
|
// handful of properties required
|
||||||
constructor(level: number, numberFormat: string, levelText: string, lvlJc: string) {
|
constructor(options: ILevelsOptions) {
|
||||||
super(level, 1, numberFormat, levelText, lvlJc);
|
super(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ class NumAttributes extends XmlAttributeComponent<INumAttributesProperties> {
|
|||||||
protected readonly xmlKeys = { numId: "w:numId" };
|
protected readonly xmlKeys = { numId: "w:numId" };
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Num extends XmlComponent {
|
export class ConcreteNumbering extends XmlComponent {
|
||||||
public readonly id: number;
|
public readonly id: number;
|
||||||
|
|
||||||
constructor(numId: number, abstractNumId: number) {
|
constructor(numId: number, abstractNumId: number) {
|
||||||
|
@ -2,24 +2,15 @@ import { expect } from "chai";
|
|||||||
|
|
||||||
import { Formatter } from "export/formatter";
|
import { Formatter } from "export/formatter";
|
||||||
|
|
||||||
import { AbstractNumbering } from "./abstract-numbering";
|
|
||||||
import { LevelForOverride } from "./level";
|
|
||||||
import { Num } from "./num";
|
|
||||||
import { Numbering } from "./numbering";
|
import { Numbering } from "./numbering";
|
||||||
|
|
||||||
import { EMPTY_OBJECT } from "file/xml-components";
|
|
||||||
import { TabStopPosition } from "../paragraph";
|
|
||||||
import { UnderlineType } from "../paragraph/run/underline";
|
|
||||||
|
|
||||||
describe("Numbering", () => {
|
describe("Numbering", () => {
|
||||||
let numbering: Numbering;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
numbering = new Numbering();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#constructor", () => {
|
describe("#constructor", () => {
|
||||||
it("creates a default numbering with one abstract and one concrete instance", () => {
|
it("creates a default numbering with one abstract and one concrete instance", () => {
|
||||||
|
const numbering = new Numbering({
|
||||||
|
levels: [],
|
||||||
|
});
|
||||||
|
|
||||||
const tree = new Formatter().format(numbering);
|
const tree = new Formatter().format(numbering);
|
||||||
expect(Object.keys(tree)).to.deep.equal(["w:numbering"]);
|
expect(Object.keys(tree)).to.deep.equal(["w:numbering"]);
|
||||||
const abstractNums = tree["w:numbering"].filter((el) => el["w:abstractNum"]);
|
const abstractNums = tree["w:numbering"].filter((el) => el["w:abstractNum"]);
|
||||||
@ -48,418 +39,4 @@ describe("Numbering", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
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);
|
|
||||||
const serializedN = tree["w:numbering"].find((obj) => obj["w:num"] && obj["w:num"][0]._attr["w:numId"] === n.id);
|
|
||||||
expect(serializedN["w:num"][1]["w:abstractNumId"]._attr["w:val"]).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)" } } });
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("formatting methods: paragraph properties", () => {
|
|
||||||
it("#indent", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerLetter", "%0.").indent({ left: 720 });
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:ind": { _attr: { "w:left": 720 } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#spacing", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerLetter", "%0.").spacing({ before: 50, after: 150 });
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:spacing": { _attr: { "w:before": 50, "w:after": 150 } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#center", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerLetter", "%0.").center();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "center" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#left", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.", "left").left();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "left" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#right", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").right();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "right" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#justified", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").justified();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "both" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#thematicBreak", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").thematicBreak();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [
|
|
||||||
{
|
|
||||||
"w:pBdr": [
|
|
||||||
{
|
|
||||||
"w:bottom": {
|
|
||||||
_attr: {
|
|
||||||
"w:color": "auto",
|
|
||||||
"w:space": 1,
|
|
||||||
"w:val": "single",
|
|
||||||
"w:sz": 6,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#leftTabStop", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").leftTabStop(1200);
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [
|
|
||||||
{
|
|
||||||
"w:tabs": [{ "w:tab": { _attr: { "w:val": "left", "w:pos": 1200 } } }],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#maxRightTabStop", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").rightTabStop(TabStopPosition.MAX);
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [
|
|
||||||
{
|
|
||||||
"w:tabs": [{ "w:tab": { _attr: { "w:val": "right", "w:pos": 9026 } } }],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#keepLines", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").keepLines();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:keepLines": EMPTY_OBJECT }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#keepNext", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").keepNext();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:keepNext": EMPTY_OBJECT }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("formatting methods: run properties", () => {
|
|
||||||
it("#size", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").size(24);
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#smallCaps", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").smallCaps();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:smallCaps": { _attr: { "w:val": true } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#allCaps", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").allCaps();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:caps": { _attr: { "w:val": true } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#strike", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").strike();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:strike": { _attr: { "w:val": true } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#doubleStrike", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").doubleStrike();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:dstrike": { _attr: { "w:val": true } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#subScript", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").subScript();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "subscript" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#superScript", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").superScript();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "superscript" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#font", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").font("Times");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [
|
|
||||||
{ "w:rFonts": { _attr: { "w:ascii": "Times", "w:cs": "Times", "w:eastAsia": "Times", "w:hAnsi": "Times" } } },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#bold", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").bold();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:b": { _attr: { "w:val": true } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#italics", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").italics();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:i": { _attr: { "w:val": true } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#highlight", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").highlight("005599");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#shadow", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").shadow("pct10", "00FFFF", "FF0000");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#underline", () => {
|
|
||||||
it("should set underline to 'single' if no arguments are given", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:u": { _attr: { "w:val": "single" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set the style if given", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline(UnderlineType.DOUBLE);
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:u": { _attr: { "w:val": "double" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set the style and color if given", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline(UnderlineType.DOUBLE, "005599");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:u": { _attr: { "w:val": "double", "w:color": "005599" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#color", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").color("123456");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:color": { _attr: { "w:val": "123456" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("concrete numbering", () => {
|
|
||||||
describe("#overrideLevel", () => {
|
|
||||||
let numbering;
|
|
||||||
let abstractNumbering;
|
|
||||||
let concreteNumbering;
|
|
||||||
beforeEach(() => {
|
|
||||||
numbering = new Numbering();
|
|
||||||
abstractNumbering = numbering.createAbstractNumbering();
|
|
||||||
concreteNumbering = numbering.createConcreteNumbering(abstractNumbering);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets a new override level for the given level number", () => {
|
|
||||||
concreteNumbering.overrideLevel(3);
|
|
||||||
const tree = new Formatter().format(concreteNumbering);
|
|
||||||
expect(tree["w:num"]).to.include({
|
|
||||||
"w:lvlOverride": [
|
|
||||||
{
|
|
||||||
_attr: {
|
|
||||||
"w:ilvl": 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"w:lvl": {
|
|
||||||
_attr: {
|
|
||||||
"w:ilvl": 3,
|
|
||||||
"w15:tentative": 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets the startOverride element if start is given", () => {
|
|
||||||
concreteNumbering.overrideLevel(1, 9);
|
|
||||||
const tree = new Formatter().format(concreteNumbering);
|
|
||||||
expect(tree["w:num"]).to.include({
|
|
||||||
"w:lvlOverride": [
|
|
||||||
{
|
|
||||||
_attr: {
|
|
||||||
"w:ilvl": 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"w:startOverride": {
|
|
||||||
_attr: {
|
|
||||||
"w:val": 9,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"w:lvl": {
|
|
||||||
_attr: {
|
|
||||||
"w:ilvl": 1,
|
|
||||||
"w15:tentative": 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets the lvl element if overrideLevel.Level is accessed", () => {
|
|
||||||
const ol = concreteNumbering.overrideLevel(1);
|
|
||||||
expect(ol.Level).to.be.instanceof(LevelForOverride);
|
|
||||||
const tree = new Formatter().format(concreteNumbering);
|
|
||||||
expect(tree["w:num"]).to.include({
|
|
||||||
"w:lvlOverride": [
|
|
||||||
{ _attr: { "w:ilvl": 1 } },
|
|
||||||
{
|
|
||||||
"w:lvl": { _attr: { "w15:tentative": 1, "w:ilvl": 1 } },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { Indent } from "file/paragraph";
|
// http://officeopenxml.com/WPnumbering.php
|
||||||
|
import { AlignmentType } from "file/paragraph";
|
||||||
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
import { DocumentAttributes } from "../document/document-attributes";
|
import { DocumentAttributes } from "../document/document-attributes";
|
||||||
import { AbstractNumbering } from "./abstract-numbering";
|
import { AbstractNumbering, IAbstractNumberingOptions } from "./abstract-numbering";
|
||||||
import { Num } from "./num";
|
import { ConcreteNumbering } from "./num";
|
||||||
|
|
||||||
export class Numbering extends XmlComponent {
|
export class Numbering extends XmlComponent {
|
||||||
// tslint:disable-next-line:readonly-keyword
|
// tslint:disable-next-line:readonly-keyword
|
||||||
@ -11,7 +13,7 @@ export class Numbering extends XmlComponent {
|
|||||||
private readonly abstractNumbering: XmlComponent[] = [];
|
private readonly abstractNumbering: XmlComponent[] = [];
|
||||||
private readonly concreteNumbering: XmlComponent[] = [];
|
private readonly concreteNumbering: XmlComponent[] = [];
|
||||||
|
|
||||||
constructor() {
|
constructor(options: IAbstractNumberingOptions) {
|
||||||
super("w:numbering");
|
super("w:numbering");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new DocumentAttributes({
|
new DocumentAttributes({
|
||||||
@ -37,39 +39,114 @@ export class Numbering extends XmlComponent {
|
|||||||
|
|
||||||
this.nextId = 0;
|
this.nextId = 0;
|
||||||
|
|
||||||
const abstractNumbering = this.createAbstractNumbering();
|
const abstractNumbering = this.createAbstractNumbering({
|
||||||
|
levels: [
|
||||||
abstractNumbering.createLevel(0, "bullet", "\u25CF", "left").addParagraphProperty(new Indent({ left: 720, hanging: 360 }));
|
{
|
||||||
|
level: 0,
|
||||||
abstractNumbering.createLevel(1, "bullet", "\u25CB", "left").addParagraphProperty(new Indent({ left: 1440, hanging: 360 }));
|
format: "bullet",
|
||||||
|
text: "\u25CF",
|
||||||
abstractNumbering.createLevel(2, "bullet", "\u25A0", "left").addParagraphProperty(new Indent({ left: 2160, hanging: 360 }));
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
abstractNumbering.createLevel(3, "bullet", "\u25CF", "left").addParagraphProperty(new Indent({ left: 2880, hanging: 360 }));
|
paragraph: {
|
||||||
|
indent: { left: 720, hanging: 360 },
|
||||||
abstractNumbering.createLevel(4, "bullet", "\u25CB", "left").addParagraphProperty(new Indent({ left: 3600, hanging: 360 }));
|
},
|
||||||
|
},
|
||||||
abstractNumbering.createLevel(5, "bullet", "\u25A0", "left").addParagraphProperty(new Indent({ left: 4320, hanging: 360 }));
|
},
|
||||||
|
{
|
||||||
abstractNumbering.createLevel(6, "bullet", "\u25CF", "left").addParagraphProperty(new Indent({ left: 5040, hanging: 360 }));
|
level: 1,
|
||||||
|
format: "bullet",
|
||||||
abstractNumbering.createLevel(7, "bullet", "\u25CB", "left").addParagraphProperty(new Indent({ left: 5760, hanging: 360 }));
|
text: "\u25CB",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
abstractNumbering.createLevel(8, "bullet", "\u25A0", "left").addParagraphProperty(new Indent({ left: 6480, hanging: 360 }));
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 1440, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 2,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25A0",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 2160, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 3,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25CF",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 2880, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 4,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25CB",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 3600, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 5,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25A0",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 4320, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 6,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25CF",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 5040, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 7,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25CF",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 5760, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 8,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25CF",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 6480, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
this.createConcreteNumbering(abstractNumbering);
|
this.createConcreteNumbering(abstractNumbering);
|
||||||
}
|
|
||||||
|
|
||||||
public createAbstractNumbering(): AbstractNumbering {
|
const currentAbstractNumbering = this.createAbstractNumbering(options);
|
||||||
const num = new AbstractNumbering(this.nextId++);
|
this.createConcreteNumbering(currentAbstractNumbering);
|
||||||
this.abstractNumbering.push(num);
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
public createConcreteNumbering(abstractNumbering: AbstractNumbering): Num {
|
|
||||||
const num = new Num(this.nextId++, abstractNumbering.id);
|
|
||||||
this.concreteNumbering.push(num);
|
|
||||||
return num;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public prepForXml(): IXmlableObject | undefined {
|
public prepForXml(): IXmlableObject | undefined {
|
||||||
@ -77,4 +154,16 @@ export class Numbering extends XmlComponent {
|
|||||||
this.concreteNumbering.forEach((x) => this.root.push(x));
|
this.concreteNumbering.forEach((x) => this.root.push(x));
|
||||||
return super.prepForXml();
|
return super.prepForXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createConcreteNumbering(abstractNumbering: AbstractNumbering): ConcreteNumbering {
|
||||||
|
const num = new ConcreteNumbering(this.nextId++, abstractNumbering.id);
|
||||||
|
this.concreteNumbering.push(num);
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createAbstractNumbering(options: IAbstractNumberingOptions): AbstractNumbering {
|
||||||
|
const num = new AbstractNumbering(this.nextId++, options);
|
||||||
|
this.abstractNumbering.push(num);
|
||||||
|
return num;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -596,10 +596,15 @@ describe("Paragraph", () => {
|
|||||||
|
|
||||||
describe("#setNumbering", () => {
|
describe("#setNumbering", () => {
|
||||||
it("should add list paragraph style to JSON", () => {
|
it("should add list paragraph style to JSON", () => {
|
||||||
const numbering = new Numbering();
|
const numbering = new Numbering({
|
||||||
const numberedAbstract = numbering.createAbstractNumbering();
|
levels: [
|
||||||
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "start");
|
{
|
||||||
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
|
level: 0,
|
||||||
|
format: "lowerLetter",
|
||||||
|
text: "%1)",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
numbering: {
|
numbering: {
|
||||||
@ -622,9 +627,16 @@ describe("Paragraph", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("it should add numbered properties", () => {
|
it("it should add numbered properties", () => {
|
||||||
const numbering = new Numbering();
|
const numbering = new Numbering({
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerLetter",
|
||||||
|
text: "%1)",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
const numberedAbstract = numbering.createAbstractNumbering();
|
const numberedAbstract = numbering.createAbstractNumbering();
|
||||||
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "start");
|
|
||||||
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
|
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
|
||||||
|
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// http://officeopenxml.com/WPparagraph.php
|
// http://officeopenxml.com/WPparagraph.php
|
||||||
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
||||||
import { Num } from "file/numbering/num";
|
import { ConcreteNumbering } from "file/numbering/num";
|
||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
import { Alignment, AlignmentType } from "./formatting/alignment";
|
import { Alignment, AlignmentType } from "./formatting/alignment";
|
||||||
@ -41,7 +41,7 @@ export interface IParagraphOptions {
|
|||||||
readonly level: number;
|
readonly level: number;
|
||||||
};
|
};
|
||||||
readonly numbering?: {
|
readonly numbering?: {
|
||||||
readonly num: Num;
|
readonly num: ConcreteNumbering;
|
||||||
readonly level: number;
|
readonly level: number;
|
||||||
readonly custom?: boolean;
|
readonly custom?: boolean;
|
||||||
};
|
};
|
||||||
|
39
src/file/styles/style-options.ts
Normal file
39
src/file/styles/style-options.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { AlignmentType, IIndentAttributesProperties, ISpacingProperties, UnderlineType } from "../paragraph";
|
||||||
|
import { ShadingType } from "../table";
|
||||||
|
|
||||||
|
export interface IRunStyleOptions {
|
||||||
|
readonly size?: number;
|
||||||
|
readonly bold?: boolean;
|
||||||
|
readonly italics?: boolean;
|
||||||
|
readonly smallCaps?: boolean;
|
||||||
|
readonly allCaps?: boolean;
|
||||||
|
readonly strike?: boolean;
|
||||||
|
readonly doubleStrike?: boolean;
|
||||||
|
readonly subScript?: boolean;
|
||||||
|
readonly superScript?: boolean;
|
||||||
|
readonly underline?: {
|
||||||
|
readonly type?: UnderlineType;
|
||||||
|
readonly color?: string;
|
||||||
|
};
|
||||||
|
readonly color?: string;
|
||||||
|
readonly font?: string;
|
||||||
|
readonly characterSpacing?: number;
|
||||||
|
readonly highlight?: string;
|
||||||
|
readonly shadow?: {
|
||||||
|
readonly type: ShadingType;
|
||||||
|
readonly fill: string;
|
||||||
|
readonly color: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IParagraphStyleOptions2 {
|
||||||
|
readonly alignment?: AlignmentType;
|
||||||
|
readonly thematicBreak?: boolean;
|
||||||
|
readonly rightTabStop?: number;
|
||||||
|
readonly leftTabStop?: number;
|
||||||
|
readonly indent?: IIndentAttributesProperties;
|
||||||
|
readonly spacing?: ISpacingProperties;
|
||||||
|
readonly keepNext?: boolean;
|
||||||
|
readonly keepLines?: boolean;
|
||||||
|
readonly outlineLevel?: number;
|
||||||
|
}
|
@ -1,21 +1,9 @@
|
|||||||
import {
|
import { Alignment, Indent, KeepLines, KeepNext, OutlineLevel, ParagraphProperties, Spacing, ThematicBreak } from "file/paragraph";
|
||||||
Alignment,
|
import { TabStop, TabStopType } from "file/paragraph/formatting";
|
||||||
AlignmentType,
|
|
||||||
Indent,
|
|
||||||
ISpacingProperties,
|
|
||||||
KeepLines,
|
|
||||||
KeepNext,
|
|
||||||
OutlineLevel,
|
|
||||||
ParagraphProperties,
|
|
||||||
Spacing,
|
|
||||||
ThematicBreak,
|
|
||||||
} from "file/paragraph";
|
|
||||||
import { IIndentAttributesProperties, TabStop, TabStopType } from "file/paragraph/formatting";
|
|
||||||
import * as formatting from "file/paragraph/run/formatting";
|
import * as formatting from "file/paragraph/run/formatting";
|
||||||
import { RunProperties } from "file/paragraph/run/properties";
|
import { RunProperties } from "file/paragraph/run/properties";
|
||||||
import { UnderlineType } from "file/paragraph/run/underline";
|
|
||||||
import { ShadingType } from "file/table";
|
|
||||||
|
|
||||||
|
import { IParagraphStyleOptions2, IRunStyleOptions } from "../style-options";
|
||||||
import { BasedOn, Link, Next, QuickFormat, SemiHidden, UiPriority, UnhideWhenUsed } from "./components";
|
import { BasedOn, Link, Next, QuickFormat, SemiHidden, UiPriority, UnhideWhenUsed } from "./components";
|
||||||
import { Style } from "./style";
|
import { Style } from "./style";
|
||||||
|
|
||||||
@ -27,41 +15,8 @@ export interface IBaseParagraphStyleOptions {
|
|||||||
readonly semiHidden?: boolean;
|
readonly semiHidden?: boolean;
|
||||||
readonly uiPriority?: number;
|
readonly uiPriority?: number;
|
||||||
readonly unhideWhenUsed?: boolean;
|
readonly unhideWhenUsed?: boolean;
|
||||||
readonly run?: {
|
readonly run?: IRunStyleOptions;
|
||||||
readonly size?: number;
|
readonly paragraph?: IParagraphStyleOptions2;
|
||||||
readonly bold?: boolean;
|
|
||||||
readonly italics?: boolean;
|
|
||||||
readonly smallCaps?: boolean;
|
|
||||||
readonly allCaps?: boolean;
|
|
||||||
readonly strike?: boolean;
|
|
||||||
readonly doubleStrike?: boolean;
|
|
||||||
readonly subScript?: boolean;
|
|
||||||
readonly superScript?: boolean;
|
|
||||||
readonly underline?: {
|
|
||||||
readonly type?: UnderlineType;
|
|
||||||
readonly color?: string;
|
|
||||||
};
|
|
||||||
readonly color?: string;
|
|
||||||
readonly font?: string;
|
|
||||||
readonly characterSpacing?: number;
|
|
||||||
readonly highlight?: string;
|
|
||||||
readonly shadow?: {
|
|
||||||
readonly type: ShadingType;
|
|
||||||
readonly fill: string;
|
|
||||||
readonly color: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
readonly paragraph?: {
|
|
||||||
readonly alignment?: AlignmentType;
|
|
||||||
readonly thematicBreak?: boolean;
|
|
||||||
readonly rightTabStop?: number;
|
|
||||||
readonly leftTabStop?: number;
|
|
||||||
readonly indent?: IIndentAttributesProperties;
|
|
||||||
readonly spacing?: ISpacingProperties;
|
|
||||||
readonly keepNext?: boolean;
|
|
||||||
readonly keepLines?: boolean;
|
|
||||||
readonly outlineLevel?: number;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IParagraphStyleOptions extends IBaseParagraphStyleOptions {
|
export interface IParagraphStyleOptions extends IBaseParagraphStyleOptions {
|
||||||
|
Reference in New Issue
Block a user