diff --git a/README.md b/README.md index 45c119a654..31104a59ec 100644 --- a/README.md +++ b/README.md @@ -3,55 +3,24 @@
- Generate .docx files with JS/TS very easily + Generate .docx files with JS/TS very easily, written in TS.
===== -[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] [![Known Vulnerabilities][snky-image]][snky-url] - -# docx -> A tool to create Word Documents (.docx) with JS or TS, written in TS. +[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][gemnasium-image]][gemnasium-url] [![Known Vulnerabilities][snky-image]][snky-url] [![Chat on Gitter][gitter-image]][gitter-url] [](https://nodei.co/npm/docx/) -# Table of Contents -- [Install](#) -- [Usage](#) - - [Create simple Word Document](#) - - [Create Paragraph](#) - - [Styles](#) - - [Heading1 - Heading5](#) - - [Title](#) - - [Text Alignment](#) - - [Example](#) - - [Thematic Break (Page Break)](#) - - [Text](#) - - [Typographical Emphasis](#) - - [Bold](#) - - [Italics](#) - - [Underline](#) - - [Break](#) - - [Chaining](#) - - [Bullet Points](#) - - [Tab Stops](#) - - [Left Tab Stop](#) - - [Center Tab Stop](#) - - [Right Tab Stop](#) - - [Max Right Tab Stop](#) - - [Example](#) -- [Exporting](#) - - [Express](#) - - [Standalone .docx file](#) -- [Examples](#) +# docx -# Install +## Install ```sh $ npm install --save docx ``` -# Demo +## Demo ```sh $ npm run demo @@ -59,8 +28,11 @@ $ npm run demo will run the demo app in the `demo` folder, which creates a file called "My Document.docx" in the root of the project +## Guide -# Usage +Please refer to [the Wiki](https://github.com/dolanmiu/docx/wiki) for details on how to use this library, examples and much more! + +## Simple Usage ```js // Used to create docx files @@ -69,264 +41,35 @@ var docx = require('docx'); // Create document var doc = new docx.Document(); +// Add some content in the document +var paragraph = new docx.Paragraph("Some cool text here."); +// Add more text into the paragraph if you wish +paragraph.addText(new docx.TextRun('Lorem Ipsum Foo Bar')); +doc.addParagraph(paragraph); + // Used to export the file into a .docx file +var exporter = new docx.LocalPacker(doc); + +// Or use the express packer to make the file downloadable. // res is express' Response object var exporter = new docx.ExpressPacker(doc, res); -var exporter = new docx.LocalPacker(doc); -``` -## Create simple Word Document -```js -var doc = new docx.Document(); - -var paragraph = new docx.Paragraph(); -var text = new docx.TextRun('Hello World'); -paragraph.addText(text); -doc.addParagraph(paragraph); + +exporter.pack('My First Document'); + +// done! A file called 'My First Document.docx' +// will be in your file system if you used LocalPacker +// Or it will start downloading if you are using Express ``` -### Document properties -You can add properties to the Word document by specifying options, for example: -```js -var doc = new docx.Document({ - creator: 'Dolan Miu', - description: 'My extremely interesting document', - title: 'My Document' -}); -``` - -#### Full list of options: -``` -creator -description -title -subject -keywords -lastModifiedBy -revision -``` - -You can mix and match whatever properties you want, or provide no properties. - -## Create Paragraph -Every text block in OpenXML is organised in paragraphs. You can add more text to the paragraph by doing this: -```js -var paragraph = new docx.Paragraph(), -``` -```js -var text = new docx.TextRun('Lorem Ipsum Foo Bar'); -var paragraph = new docx.Paragraph(); -paragraph.addText(text); -``` -```js -var paragraph = new docx.Paragraph("Short hand notation for adding text."); -``` - -After you create the paragraph, you must add the paragraph into the `document`: -```js -doc.addParagraph(paragraph); -``` - -### Styles -Styles is a very important part of the look of a word document. At the moment, only headings and title is supported, but son the rest will be supported along with custom styles! - - - -#### Heading1 - Heading5 -```js -paragraph.heading1(); -paragraph.heading2(); -paragraph.heading3(); -paragraph.heading4(); -paragraph.heading5(); -``` - -#### Title -```js -paragraph.title(); -``` - -### Text Alignment -To change the text alignment of a paragraph, for center, left, right or justified: -```js -paragraph.center(); -``` -```js -paragraph.left(); -``` -```js -paragraph.right(); -``` -```js -paragraph.justified(); -``` - -#### Example -```js -paragraph.heading1().center(); -``` -The above will create a `heading 1` which is `centered`. - -### Thematic Break -To add a break in the page, simply add `.thematicBreak()` on a paragraph: - -```js -var paragraph = new docx.Paragraph("Amazing Heading").heading1().thematicBreak(); -``` -The above example will create a heading with a page break directly under it. - -### Page Break -To move to a new page (insert a page break), simply add `.pageBreak()` on a paragraph: - -```js -var paragraph = new docx.Paragraph("Amazing Heading").heading1().pageBreak(); -``` -The above example will create a heading and start a new page immediately afterwards. - -## Text -Paragraphs need `text run` objects. To create text: -```js -var text = new docx.TextRun("My awesome text here for my university dissertation"); -paragraph.addText(text); -``` -Text objects have methods inside which changes the way the text is displayed. - -### Typographical Emphasis -More info [here](https://english.stackexchange.com/questions/97081/what-is-the-typography-term-which-refers-to-the-usage-of-bold-italics-and-unde) -#### Bold -```js -text.bold(); -``` - -#### Italics -```js -text.italic(); -``` - -#### Underline -```js -text.underline(); -``` - -#### Strike through -```js -text.strike(); -``` - -#### Double strike through -```js -text.doubleStrike(); -``` - -#### Superscript -```js -text.superScript(); -``` - -#### Subscript -```js -text.subScript(); -``` - -#### All Capitals -```js -text.allCaps(); -``` - -#### Small Capitals -```js -text.smallCaps(); -``` - -### Break -Sometimes you would want to put text underneath another line of text but inside the same paragraph. -```js -text.break(); -``` - -### Chaining -What if you want to create a paragraph which is ***bold*** and ***italic***? -```js -paragraph.bold().italic(); -``` - -## Bullet Points -To make a bullet point, simply make a paragraph into a bullet point: -```js -var text = new docx.TextRun("Bullet points"); -var paragraph = new docx.Paragraph(text).bullet(); - -var text2 = new docx.TextRun("Are awesome"); -var paragraph2 = new docx.Paragraph(text2).bullet(); - -doc.addParagraph(paragraph); -doc.addParagraph(paragraph2); -``` -This will produce: -* Bullet points -* Are awesome - -## Tab Stops -If you do not know why tab stops are useful, then I recommend you do a bit of research. It enables side by side text which is nicely laid out without the need for tables, or constantly pressing space bar. - -**Note**: At the moment, the unit of measurement for a tab stop is counter intuitive for a human. It is using OpenXMLs own measuring system. For example, 2268 roughly translates to 3cm. Therefore in the future, I may consider changing it to percentages or even cm. - - - -Simply call the relevant methods on the paragraph listed below. Then just add a `tab()` method call to a text object. Adding multiple `tabStops` will mean you would have to chain `tab()` until the desired `tabStop` is selected. Example is shown below. - -### Left Tab Stop -```js -paragraph.leftTabStop(2268); -``` -2268 is the distance from the left side. - -### Center Tab Stop -```js -paragraph.centerTabStp(2268); -``` -2268 is the distance from the left side. - -### Right Tab Stop -```js -paragraph.rightTabStop(2268); -``` -2268 is the distance from the left side. - -### Max Right Tab Stop -```js -paragraph.maxRightTabStop(); -``` -This will create a tab stop on the very edge of the right hand side. Handy for right aligning and left aligning text on the same line. - -### Example -```js -var paragraph = new docx.Paragraph().maxRightTabStop(); -var leftText = new docx.TextRun("Hey everyone").bold(); -var rightText = new docx.TextRun("11th November 2015").tab(); -paragraph.addText(leftText); -paragraph.addText(rightText); -``` -The example above will create a left aligned text, and a right aligned text on the same line. The laymans approach to this problem would be to either use text boxes or tables. YUK! - -```js -var paragraph = new docx.Paragraph(); -paragraph.maxRightTabStop(); -paragraph.leftTabStop(1000); -var text = new docx.TextRun("Second tab stop here I come!").tab().tab(); -paragraph.addText(text); -``` -The above shows the use of two tab stops, and how to select/use it. - -# Exporting -Check the Wiki for exporting guide - -# Examples -Check the Wiki for examples +## Examples +Check [the Wiki](https://github.com/dolanmiu/docx/wiki/Examples) for examples. ===== Made with 💖 +Huge thanks to [@felipeochoa](https://github.com/felipeochoa) for awesome contributions to this project + [npm-image]: https://badge.fury.io/js/docx.svg [npm-url]: https://npmjs.org/package/docx [travis-image]: https://travis-ci.org/dolanmiu/docx.svg?branch=master @@ -335,3 +78,8 @@ Made with 💖 [daviddm-url]: https://david-dm.org/dolanmiu/docx [snky-image]: https://snyk.io/test/github/dolanmiu/docx/badge.svg [snky-url]: https://snyk.io/test/github/dolanmiu/docx +[gitter-image]: https://badges.gitter.im/dolanmiu/docx.svg +[gitter-url]: https://gitter.im/docx-lib/Lobby +[gemnasium-image]: https://gemnasium.com/badges/github.com/dolanmiu/docx.svg +[gemnasium-url]: https://gemnasium.com/github.com/dolanmiu/docx + diff --git a/package.json b/package.json index 2cee1629ad..1408a7fdde 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ }, "files": [ "ts", - "build" + "build", + "template" ], "repository": { "type": "git", @@ -28,10 +29,8 @@ "clippy" ], "dependencies": { - "@types/app-root-path": "^1.2.4", "@types/archiver": "^0.15.37", "@types/express": "^4.0.35", - "app-root-path": "^2.0.1", "archiver": "^1.3.0", "xml": "^1.0.1" }, diff --git a/ts/docx/paragraph/index.ts b/ts/docx/paragraph/index.ts index a85c014764..4ae172eb1b 100644 --- a/ts/docx/paragraph/index.ts +++ b/ts/docx/paragraph/index.ts @@ -1,4 +1,5 @@ import { Num } from "../../numbering/num"; +import { Run } from "../run"; import { TextRun } from "../run/text-run"; import { XmlComponent } from "../xml-components"; @@ -24,14 +25,14 @@ export class Paragraph extends XmlComponent { } } - public addText(run: TextRun): Paragraph { + public addRun(run: Run): Paragraph { this.root.push(run); return this; } public createTextRun(text: string): TextRun { const run = new TextRun(text); - this.addText(run); + this.addRun(run); return run; } diff --git a/ts/export/packer/packer.ts b/ts/export/packer/packer.ts index 740b072cd1..e45d1b45bd 100644 --- a/ts/export/packer/packer.ts +++ b/ts/export/packer/packer.ts @@ -1,5 +1,5 @@ -import * as appRoot from "app-root-path"; import * as archiver from "archiver"; +import * as path from "path"; import * as xml from "xml"; import { Document } from "../../docx"; import { Numbering } from "../../numbering"; @@ -8,6 +8,8 @@ import { Styles } from "../../styles"; import { DefaultStylesFactory } from "../../styles/factory"; import { Formatter } from "../formatter"; +const templatePath = path.resolve(__dirname, "../../../template"); + export abstract class Packer { protected archive: any; private formatter: Formatter; @@ -42,12 +44,12 @@ export abstract class Packer { this.archive.pipe(output); this.archive.glob("**", { expand: true, - cwd: appRoot.path + "/template", + cwd: templatePath, }); this.archive.glob("**/.rels", { expand: true, - cwd: appRoot.path + "/template", + cwd: templatePath, }); const xmlDocument = xml(this.formatter.format(this.document)); diff --git a/ts/numbering/level.ts b/ts/numbering/level.ts index 02f0896f21..4aabee238a 100644 --- a/ts/numbering/level.ts +++ b/ts/numbering/level.ts @@ -1,4 +1,6 @@ +import * as paragraph from "../docx/paragraph/formatting"; import { ParagraphProperties } from "../docx/paragraph/properties"; +import * as formatting from "../docx/run/formatting"; import { RunProperties } from "../docx/run/properties"; import { Attributes, XmlAttributeComponent, XmlComponent } from "../docx/xml-components"; @@ -86,4 +88,113 @@ export class Level extends XmlComponent { this.runProperties.push(property); return this; } + + // ---------- Run formatting ---------------------- // + + public size(twips: number): Level { + this.addRunProperty(new formatting.Size(twips)); + return this; + } + + public bold(): Level { + this.addRunProperty(new formatting.Bold()); + return this; + } + + public italics(): Level { + this.addRunProperty(new formatting.Italics()); + return this; + } + + public smallCaps(): Level { + this.addRunProperty(new formatting.SmallCaps()); + return this; + } + + public allCaps(): Level { + this.addRunProperty(new formatting.Caps()); + return this; + } + + public strike(): Level { + this.addRunProperty(new formatting.Strike()); + return this; + } + + public doubleStrike(): Level { + this.addRunProperty(new formatting.DoubleStrike()); + return this; + } + + public subScript(): Level { + this.addRunProperty(new formatting.SubScript()); + return this; + } + + public superScript(): Level { + this.addRunProperty(new formatting.SuperScript()); + return this; + } + + public underline(underlineType?: string, color?: string): Level { + this.addRunProperty(new formatting.Underline(underlineType, color)); + return this; + } + + public color(color: string): Level { + this.addRunProperty(new formatting.Color(color)); + return this; + } + + public font(fontName: string): Level { + this.addRunProperty(new formatting.RunFonts(fontName)); + return this; + } + + // --------------------- Paragraph formatting ------------------------ // + + public center(): Level { + this.addParagraphProperty(new paragraph.Alignment("center")); + return this; + } + + public left(): Level { + this.addParagraphProperty(new paragraph.Alignment("left")); + return this; + } + + public right(): Level { + this.addParagraphProperty(new paragraph.Alignment("right")); + return this; + } + + public justified(): Level { + this.addParagraphProperty(new paragraph.Alignment("both")); + return this; + } + + public thematicBreak(): Level { + this.addParagraphProperty(new paragraph.ThematicBreak()); + return this; + } + + public maxRightTabStop(): Level { + this.addParagraphProperty(new paragraph.MaxRightTabStop()); + return this; + } + + public leftTabStop(position: number): Level { + this.addParagraphProperty(new paragraph.LeftTabStop(position)); + return this; + } + + public indent(left: number, hanging?: number): Level { + this.addParagraphProperty(new paragraph.Indent(left, hanging)); + return this; + } + + public spacing(params: paragraph.ISpacingProperties): Level { + this.addParagraphProperty(new paragraph.Spacing(params)); + return this; + }; } diff --git a/ts/tests/export/formatterTest.ts b/ts/tests/export/formatterTest.ts index 12222c583f..1e69be4e18 100644 --- a/ts/tests/export/formatterTest.ts +++ b/ts/tests/export/formatterTest.ts @@ -29,7 +29,7 @@ describe("Formatter", () => { it("should format simple paragraph with bold text", () => { const paragraph = new docx.Paragraph(); - paragraph.addText(new docx.TextRun("test").bold()); + paragraph.addRun(new docx.TextRun("test").bold()); const newJson = formatter.format(paragraph); assert.isDefined(newJson["w:p"][1]["w:r"][0]["w:rPr"][0]["w:b"][0]._attr["w:val"]); }); diff --git a/ts/tests/numberingTest.ts b/ts/tests/numberingTest.ts index 00bd6afb10..59a71e720e 100644 --- a/ts/tests/numberingTest.ts +++ b/ts/tests/numberingTest.ts @@ -105,5 +105,292 @@ describe("AbstractNumbering", () => { 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(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.") + .maxRightTabStop(); + 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}}]}, + ]}, + ], + }); + }); + }); + + 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: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}}]}, + ], + }); + }); + + 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("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("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"}}]}, + ], + }); + }); + }); }); });