Merge branch 'master' into custom-properties

This commit is contained in:
Dolan
2021-03-02 00:01:03 +00:00
committed by GitHub
205 changed files with 6259 additions and 1047 deletions

View File

@ -7,6 +7,7 @@ indent_style = space
indent_size = 4 indent_size = 4
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
end_of_line = lf
[*.md] [*.md]
max_line_length = off max_line_length = off

104
.github/workflows/default.yml vendored Normal file
View File

@ -0,0 +1,104 @@
name: Default
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
- name: Install Dependencies
run: npm ci
- name: Build
run: npm run build
- name: Archive Production Artifact
uses: actions/upload-artifact@master
with:
name: build
path: build
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
- name: Install Dependencies
run: npm ci
- name: Test
run: npm run test.coverage
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
- name: Install Dependencies
run: npm ci
- name: Lint
run: npm run lint
prettier:
name: Prettier
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
- name: Install Dependencies
run: npm ci
- name: Prettier
run: npm run style
demos:
name: Run Demos
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
- name: Install Dependencies
run: npm ci
- name: Download Artifact
uses: actions/download-artifact@master
with:
name: build
path: build
- name: Run demos
run: |
npm run ts-node -- ./demo/1-basic.ts
npm run ts-node -- ./demo/2-declaritive-styles.ts
npm run ts-node -- ./demo/3-numbering-and-bullet-points.ts
npm run ts-node -- ./demo/4-basic-table.ts
npm run ts-node -- ./demo/5-images.ts
npm run ts-node -- ./demo/6-page-borders.ts
npm run ts-node -- ./demo/7-landscape.ts
npm run ts-node -- ./demo/8-header-footer.ts
npm run ts-node -- ./demo/9-images-in-header-and-footer.ts
npm run ts-node -- ./demo/10-my-cv.ts
npm run ts-node -- ./demo/11-declaritive-styles-2.ts
npm run ts-node -- ./demo/12-scaling-images.ts
npm run ts-node -- ./demo/13-xml-styles.ts
npm run ts-node -- ./demo/14-page-numbers.ts
npm run ts-node -- ./demo/15-page-break-before.ts
npm run ts-node -- ./demo/16-multiple-sections.ts
npm run ts-node -- ./demo/17-footnotes.ts
npm run ts-node -- ./demo/18-image-from-buffer.ts
npm run ts-node -- ./demo/19-export-to-base64.ts
npm run ts-node -- ./demo/20-table-cell-borders.ts
npm run ts-node -- ./demo/21-bookmarks.ts
npm run ts-node -- ./demo/22-right-to-left-text.ts
npm run ts-node -- ./demo/23-base64-images.ts
npm run ts-node -- ./demo/24-images-to-table-cell.ts
npm run ts-node -- ./demo/26-paragraph-borders.ts
npm run ts-node -- ./demo/27-declaritive-styles-3.ts
npm run ts-node -- ./demo/28-table-of-contents.ts
npm run ts-node -- ./demo/29-numbered-lists.ts
npm run ts-node -- ./demo/30-template-document.ts
npm run ts-node -- ./demo/31-tables.ts
npm run ts-node -- ./demo/32-merge-and-shade-table-cells.ts
npm run ts-node -- ./demo/33-sequential-captions.ts
npm run ts-node -- ./demo/34-floating-tables.ts

View File

@ -11,6 +11,7 @@
[![NPM version][npm-image]][npm-url] [![NPM version][npm-image]][npm-url]
[![Downloads per month][downloads-image]][downloads-url] [![Downloads per month][downloads-image]][downloads-url]
[![Build Status][travis-image]][travis-url] [![Build Status][travis-image]][travis-url]
[![GitHub Action Workflow Status][github-actions-workflow-image]][github-actions-workflow-url]
[![Dependency Status][daviddm-image]][daviddm-url] [![Dependency Status][daviddm-image]][daviddm-url]
[![Known Vulnerabilities][snky-image]][snky-url] [![Known Vulnerabilities][snky-image]][snky-url]
[![Chat on Gitter][gitter-image]][gitter-url] [![Chat on Gitter][gitter-image]][gitter-url]
@ -67,7 +68,7 @@ Please refer to the [documentation at https://docx.js.org/](https://docx.js.org/
# Examples # Examples
Check the `examples` section in the [documentation](https://docx.js.org/#/examples) and the [demo folder](https://github.com/dolanmiu/docx/tree/master/demo) for examples. Check the [demo folder](https://github.com/dolanmiu/docx/tree/master/demo) for examples.
# Contributing # Contributing
@ -85,6 +86,7 @@ Read the contribution guidelines [here](https://docx.js.org/#/contribution-guide
[<img src="https://i.imgur.com/QEZXU5b.png" alt="drawing" height="50"/>](https://www.beekast.com/) [<img src="https://i.imgur.com/QEZXU5b.png" alt="drawing" height="50"/>](https://www.beekast.com/)
[<img src="https://imgur.com/XVU6aoi.png" alt="drawing" height="50"/>](https://herraizsoto.com/) [<img src="https://imgur.com/XVU6aoi.png" alt="drawing" height="50"/>](https://herraizsoto.com/)
[<img src="https://i.imgur.com/fn1xccG.png" alt="drawing" height="50"/>](http://www.ativer.com.br/) [<img src="https://i.imgur.com/fn1xccG.png" alt="drawing" height="50"/>](http://www.ativer.com.br/)
[<img src="https://i.imgur.com/cmykN7c.png" alt="drawing"/>](https://www.arity.co/)
...and many more! ...and many more!
@ -102,6 +104,8 @@ Made with 💖
[downloads-url]: https://npmjs.org/package/docx [downloads-url]: https://npmjs.org/package/docx
[travis-image]: https://travis-ci.org/dolanmiu/docx.svg?branch=master [travis-image]: https://travis-ci.org/dolanmiu/docx.svg?branch=master
[travis-url]: https://travis-ci.org/dolanmiu/docx [travis-url]: https://travis-ci.org/dolanmiu/docx
[github-actions-workflow-image]: https://github.com/dolanmiu/docx/workflows/Default/badge.svg
[github-actions-workflow-url]: https://github.com/dolanmiu/docx/actions
[daviddm-image]: https://david-dm.org/dolanmiu/docx.svg?theme=shields.io [daviddm-image]: https://david-dm.org/dolanmiu/docx.svg?theme=shields.io
[daviddm-url]: https://david-dm.org/dolanmiu/docx [daviddm-url]: https://david-dm.org/dolanmiu/docx
[snky-image]: https://snyk.io/test/github/dolanmiu/docx/badge.svg [snky-image]: https://snyk.io/test/github/dolanmiu/docx/badge.svg

View File

@ -204,7 +204,10 @@ class DocumentCreator {
alignment: AlignmentType.CENTER, alignment: AlignmentType.CENTER,
children: [ children: [
new TextRun(`Mobile: ${phoneNumber} | LinkedIn: ${profileUrl} | Email: ${email}`), new TextRun(`Mobile: ${phoneNumber} | LinkedIn: ${profileUrl} | Email: ${email}`),
new TextRun("Address: 58 Elm Avenue, Kent ME4 6ER, UK").break(), new TextRun({
text: "Address: 58 Elm Avenue, Kent ME4 6ER, UK",
break: 1,
}),
], ],
}); });
} }

View File

@ -3,6 +3,7 @@
import * as fs from "fs"; import * as fs from "fs";
import { import {
AlignmentType, AlignmentType,
convertInchesToTwip,
Document, Document,
Footer, Footer,
HeadingLevel, HeadingLevel,
@ -18,13 +19,8 @@ import {
const doc = new Document({ const doc = new Document({
styles: { styles: {
paragraphStyles: [ default: {
{ heading1: {
id: "Heading1",
name: "Heading 1",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: { run: {
font: "Calibri", font: "Calibri",
size: 52, size: 52,
@ -40,12 +36,7 @@ const doc = new Document({
spacing: { line: 340 }, spacing: { line: 340 },
}, },
}, },
{ heading2: {
id: "Heading2",
name: "Heading 2",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: { run: {
font: "Calibri", font: "Calibri",
size: 26, size: 26,
@ -55,12 +46,7 @@ const doc = new Document({
spacing: { line: 340 }, spacing: { line: 340 },
}, },
}, },
{ heading3: {
id: "Heading3",
name: "Heading 3",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: { run: {
font: "Calibri", font: "Calibri",
size: 26, size: 26,
@ -70,12 +56,7 @@ const doc = new Document({
spacing: { line: 276 }, spacing: { line: 276 },
}, },
}, },
{ heading4: {
id: "Heading4",
name: "Heading 4",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: { run: {
font: "Calibri", font: "Calibri",
size: 26, size: 26,
@ -85,6 +66,8 @@ const doc = new Document({
alignment: AlignmentType.JUSTIFIED, alignment: AlignmentType.JUSTIFIED,
}, },
}, },
},
paragraphStyles: [
{ {
id: "normalPara", id: "normalPara",
name: "Normal Para", name: "Normal Para",
@ -128,7 +111,7 @@ const doc = new Document({
}, },
paragraph: { paragraph: {
spacing: { line: 276 }, spacing: { line: 276 },
indent: { left: 720 }, indent: { left: convertInchesToTwip(0.5) },
}, },
}, },
{ {
@ -139,12 +122,6 @@ const doc = new Document({
spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 }, spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 },
}, },
}, },
{
id: "ListParagraph",
name: "List Paragraph",
basedOn: "Normal",
quickFormat: true,
},
], ],
}, },
}); });

View File

@ -1,20 +1,25 @@
// Example on how to customise the look at feel using Styles // Example on how to customise the look at feel using Styles
// Import from 'docx' rather than '../build' if you install from npm // Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { AlignmentType, Document, HeadingLevel, Packer, Paragraph, TextRun, UnderlineType } from "../build"; import {
AlignmentType,
convertInchesToTwip,
Document,
HeadingLevel,
LevelFormat,
Packer,
Paragraph,
TextRun,
UnderlineType,
} from "../build";
const doc = new Document({ const doc = new Document({
creator: "Clippy", creator: "Clippy",
title: "Sample Document", title: "Sample Document",
description: "A brief example of using docx", description: "A brief example of using docx",
styles: { styles: {
paragraphStyles: [ default: {
{ heading1: {
id: "Heading1",
name: "Heading 1",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: { run: {
size: 28, size: 28,
bold: true, bold: true,
@ -27,12 +32,7 @@ const doc = new Document({
}, },
}, },
}, },
{ heading2: {
id: "Heading2",
name: "Heading 2",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: { run: {
size: 26, size: 26,
bold: true, bold: true,
@ -48,6 +48,13 @@ const doc = new Document({
}, },
}, },
}, },
listParagraph: {
run: {
color: "#FF0000",
},
},
},
paragraphStyles: [
{ {
id: "aside", id: "aside",
name: "Aside", name: "Aside",
@ -59,7 +66,7 @@ const doc = new Document({
}, },
paragraph: { paragraph: {
indent: { indent: {
left: 720, left: convertInchesToTwip(0.5),
}, },
spacing: { spacing: {
line: 276, line: 276,
@ -75,12 +82,6 @@ const doc = new Document({
spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 }, spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 },
}, },
}, },
{
id: "ListParagraph",
name: "List Paragraph",
basedOn: "Normal",
quickFormat: true,
},
], ],
}, },
numbering: { numbering: {
@ -90,7 +91,7 @@ const doc = new Document({
levels: [ levels: [
{ {
level: 0, level: 0,
format: "lowerLetter", format: LevelFormat.LOWER_LETTER,
text: "%1)", text: "%1)",
alignment: AlignmentType.LEFT, alignment: AlignmentType.LEFT,
}, },
@ -170,6 +171,17 @@ doc.addSection({
}), }),
], ],
}), }),
new Paragraph({
style: "Strong",
children: [
new TextRun({
text: "Strong Style",
}),
new TextRun({
text: " - Very strong.",
}),
],
}),
], ],
}); });

View File

@ -1,7 +1,7 @@
// This demo shows how to create bookmarks then link to them with internal hyperlinks // This demo shows how to create bookmarks then link to them with internal hyperlinks
// Import from 'docx' rather than '../build' if you install from npm // Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { Bookmark, Document, HeadingLevel, HyperlinkRef, HyperlinkType, Packer, PageBreak, Paragraph } from "../build"; import { Bookmark, Document, Footer, HeadingLevel, InternalHyperlink, Packer, PageBreak, Paragraph, TextRun } from "../build";
const LOREM_IPSUM = const LOREM_IPSUM =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mi velit, convallis convallis scelerisque nec, faucibus nec leo. Phasellus at posuere mauris, tempus dignissim velit. Integer et tortor dolor. Duis auctor efficitur mattis. Vivamus ut metus accumsan tellus auctor sollicitudin venenatis et nibh. Cras quis massa ac metus fringilla venenatis. Proin rutrum mauris purus, ut suscipit magna consectetur id. Integer consectetur sollicitudin ante, vitae faucibus neque efficitur in. Praesent ultricies nibh lectus. Mauris pharetra id odio eget iaculis. Duis dictum, risus id pellentesque rutrum, lorem quam malesuada massa, quis ullamcorper turpis urna a diam. Cras vulputate metus vel massa porta ullamcorper. Etiam porta condimentum nulla nec tristique. Sed nulla urna, pharetra non tortor sed, sollicitudin molestie diam. Maecenas enim leo, feugiat eget vehicula id, sollicitudin vitae ante."; "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mi velit, convallis convallis scelerisque nec, faucibus nec leo. Phasellus at posuere mauris, tempus dignissim velit. Integer et tortor dolor. Duis auctor efficitur mattis. Vivamus ut metus accumsan tellus auctor sollicitudin venenatis et nibh. Cras quis massa ac metus fringilla venenatis. Proin rutrum mauris purus, ut suscipit magna consectetur id. Integer consectetur sollicitudin ante, vitae faucibus neque efficitur in. Praesent ultricies nibh lectus. Mauris pharetra id odio eget iaculis. Duis dictum, risus id pellentesque rutrum, lorem quam malesuada massa, quis ullamcorper turpis urna a diam. Cras vulputate metus vel massa porta ullamcorper. Etiam porta condimentum nulla nec tristique. Sed nulla urna, pharetra non tortor sed, sollicitudin molestie diam. Maecenas enim leo, feugiat eget vehicula id, sollicitudin vitae ante.";
@ -10,15 +10,26 @@ const doc = new Document({
creator: "Clippy", creator: "Clippy",
title: "Sample Document", title: "Sample Document",
description: "A brief example of using docx with bookmarks and internal hyperlinks", description: "A brief example of using docx with bookmarks and internal hyperlinks",
hyperlinks: {
myAnchorId: {
text: "Hyperlink",
type: HyperlinkType.INTERNAL,
},
},
}); });
doc.addSection({ doc.addSection({
footers: {
default: new Footer({
children: [
new Paragraph({
children: [
new InternalHyperlink({
child: new TextRun({
text: "Click here!",
style: "Hyperlink",
}),
anchor: "myAnchorId",
}),
],
}),
],
}),
},
children: [ children: [
new Paragraph({ new Paragraph({
heading: HeadingLevel.HEADING_1, heading: HeadingLevel.HEADING_1,
@ -30,7 +41,15 @@ doc.addSection({
children: [new PageBreak()], children: [new PageBreak()],
}), }),
new Paragraph({ new Paragraph({
children: [new HyperlinkRef("myAnchorId")], children: [
new InternalHyperlink({
child: new TextRun({
text: "Anchor Text",
style: "Hyperlink",
}),
anchor: "myAnchorId",
}),
],
}), }),
], ],
}); });

View File

@ -1,7 +1,7 @@
// Custom styles using JavaScript configuration // Custom styles using JavaScript configuration
// Import from 'docx' rather than '../build' if you install from npm // Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { Document, HeadingLevel, Packer, Paragraph, UnderlineType } from "../build"; import { Document, convertInchesToTwip, HeadingLevel, Packer, Paragraph, UnderlineType } from "../build";
const doc = new Document({ const doc = new Document({
styles: { styles: {
@ -17,7 +17,7 @@ const doc = new Document({
}, },
paragraph: { paragraph: {
indent: { indent: {
left: 720, left: convertInchesToTwip(0.5),
}, },
spacing: { spacing: {
line: 276, line: 276,

View File

@ -1,7 +1,7 @@
// Numbered lists // Numbered lists
// Import from 'docx' rather than '../build' if you install from npm // Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { AlignmentType, Document, Packer, Paragraph } from "../build"; import { AlignmentType, convertInchesToTwip, Document, LevelFormat, Packer, Paragraph } from "../build";
const doc = new Document({ const doc = new Document({
numbering: { numbering: {
@ -10,12 +10,12 @@ const doc = new Document({
levels: [ levels: [
{ {
level: 0, level: 0,
format: "upperRoman", format: LevelFormat.UPPER_ROMAN,
text: "%1", text: "%1",
alignment: AlignmentType.START, alignment: AlignmentType.START,
style: { style: {
paragraph: { paragraph: {
indent: { left: 720, hanging: 260 }, indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.18) },
}, },
}, },
}, },
@ -26,18 +26,34 @@ const doc = new Document({
levels: [ levels: [
{ {
level: 0, level: 0,
format: "decimal", format: LevelFormat.DECIMAL,
text: "%1", text: "%1",
alignment: AlignmentType.START, alignment: AlignmentType.START,
style: { style: {
paragraph: { paragraph: {
indent: { left: 720, hanging: 260 }, indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.18) },
}, },
}, },
}, },
], ],
reference: "my-number-numbering-reference", reference: "my-number-numbering-reference",
}, },
{
levels: [
{
level: 0,
format: LevelFormat.DECIMAL_ZERO,
text: "[%1]",
alignment: AlignmentType.START,
style: {
paragraph: {
indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.18) },
},
},
},
],
reference: "padded-numbering-reference",
},
], ],
}, },
}); });
@ -109,6 +125,139 @@ doc.addSection({
level: 0, level: 0,
}, },
}), }),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "test",
numbering: {
reference: "padded-numbering-reference",
level: 0,
},
}),
], ],
}); });

View File

@ -1,7 +1,7 @@
// Numbering and bullet points example // Numbering and bullet points example
// Import from 'docx' rather than '../build' if you install from npm // Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { AlignmentType, Document, Packer, Paragraph } from "../build"; import { AlignmentType, convertInchesToTwip, Document, LevelFormat, Packer, Paragraph } from "../build";
const doc = new Document({ const doc = new Document({
numbering: { numbering: {
@ -11,40 +11,40 @@ const doc = new Document({
levels: [ levels: [
{ {
level: 0, level: 0,
format: "upperRoman", format: LevelFormat.UPPER_ROMAN,
text: "%1", text: "%1",
alignment: AlignmentType.START, alignment: AlignmentType.START,
style: { style: {
paragraph: { paragraph: {
indent: { left: 720, hanging: 260 }, indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.18) },
}, },
}, },
}, },
{ {
level: 1, level: 1,
format: "decimal", format: LevelFormat.DECIMAL,
text: "%2.", text: "%2.",
alignment: AlignmentType.START, alignment: AlignmentType.START,
style: { style: {
paragraph: { paragraph: {
indent: { left: 1440, hanging: 980 }, indent: { left: convertInchesToTwip(1), hanging: convertInchesToTwip(0.68) },
}, },
}, },
}, },
{ {
level: 2, level: 2,
format: "lowerLetter", format: LevelFormat.LOWER_LETTER,
text: "%3)", text: "%3)",
alignment: AlignmentType.START, alignment: AlignmentType.START,
style: { style: {
paragraph: { paragraph: {
indent: { left: 2160, hanging: 1700 }, indent: { left: convertInchesToTwip(1.5), hanging: convertInchesToTwip(1.18) },
}, },
}, },
}, },
{ {
level: 3, level: 3,
format: "upperLetter", format: LevelFormat.UPPER_LETTER,
text: "%4)", text: "%4)",
alignment: AlignmentType.START, alignment: AlignmentType.START,
style: { style: {

View File

@ -2,7 +2,20 @@
// Also includes an example on how to center tables // Also includes an example on how to center tables
// Import from 'docx' rather than '../build' if you install from npm // Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { AlignmentType, BorderStyle, Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build"; import {
AlignmentType,
BorderStyle,
convertInchesToTwip,
Document,
HeadingLevel,
Packer,
Paragraph,
ShadingType,
Table,
TableCell,
TableRow,
WidthType,
} from "../build";
const doc = new Document(); const doc = new Document();
@ -37,10 +50,10 @@ const table2 = new Table({
new TableCell({ new TableCell({
children: [new Paragraph("World")], children: [new Paragraph("World")],
margins: { margins: {
top: 1000, top: convertInchesToTwip(0.69),
bottom: 1000, bottom: convertInchesToTwip(0.69),
left: 1000, left: convertInchesToTwip(0.69),
right: 1000, right: convertInchesToTwip(0.69),
}, },
columnSpan: 3, columnSpan: 3,
}), }),
@ -64,7 +77,7 @@ const table2 = new Table({
size: 100, size: 100,
type: WidthType.AUTO, type: WidthType.AUTO,
}, },
columnWidths: [1000, 1000, 1000], columnWidths: [convertInchesToTwip(0.69), convertInchesToTwip(0.69), convertInchesToTwip(0.69)],
}); });
const table3 = new Table({ const table3 = new Table({
@ -119,14 +132,14 @@ const table3 = new Table({
}), }),
], ],
width: { width: {
size: 7000, size: convertInchesToTwip(4.86),
type: WidthType.DXA, type: WidthType.DXA,
}, },
margins: { margins: {
top: 400, top: convertInchesToTwip(0.27),
bottom: 400, bottom: convertInchesToTwip(0.27),
right: 400, right: convertInchesToTwip(0.27),
left: 400, left: convertInchesToTwip(0.27),
}, },
}); });
@ -355,9 +368,7 @@ const table8 = new Table({
], ],
}), }),
new TableRow({ new TableRow({
children: [ children: [new TableCell({ children: [new Paragraph("4,1")] })],
new TableCell({ children: [new Paragraph("4,1")] }),
],
}), }),
], ],
width: { width: {

View File

@ -1,32 +1,75 @@
// Example on how to add hyperlinks to websites // Example on how to add hyperlinks to websites
// Import from 'docx' rather than '../build' if you install from npm // Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { Document, HyperlinkRef, HyperlinkType, Packer, Paragraph, Media } from "../build"; import { Document, ExternalHyperlink, Footer, Media, Packer, Paragraph, TextRun } from "../build";
const doc = new Document({ const doc = new Document({});
hyperlinks: {
myCoolLink: {
link: "http://www.example.com",
text: "Hyperlink",
type: HyperlinkType.EXTERNAL,
},
myOtherLink: {
link: "http://www.google.com",
text: "Google Link",
type: HyperlinkType.EXTERNAL,
},
},
});
const image1 = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg")); const image1 = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
doc.addSection({ doc.addSection({
footers: {
default: new Footer({
children: [
new Paragraph({
children: [
new TextRun("Click here for the "),
new ExternalHyperlink({
child: new TextRun({
text: "Footer external hyperlink",
style: "Hyperlink",
}),
link: "http://www.example.com",
}),
],
}),
],
}),
},
headers: {
default: new Footer({
children: [
new Paragraph({
children: [
new TextRun("Click here for the "),
new ExternalHyperlink({
child: new TextRun({
text: "Header external hyperlink",
style: "Hyperlink",
}),
link: "http://www.google.com",
}),
],
}),
],
}),
},
children: [ children: [
new Paragraph({ new Paragraph({
children: [new HyperlinkRef("myCoolLink")], children: [
new ExternalHyperlink({
child: new TextRun({
text: "Anchor Text",
style: "Hyperlink",
}),
link: "http://www.example.com",
}),
],
}), }),
new Paragraph({ new Paragraph({
children: [image1, new HyperlinkRef("myOtherLink")], children: [
new ExternalHyperlink({
child: image1,
link: "http://www.google.com",
}),
new ExternalHyperlink({
child: new TextRun({
text: "BBC News Link",
style: "Hyperlink",
}),
link: "https://www.bbc.co.uk/news",
}),
],
}), }),
], ],
}); });

View File

@ -28,6 +28,18 @@ doc.addSection({
}), }),
], ],
}), }),
new Paragraph({
shading: {
type: ShadingType.DIAGONAL_CROSS,
color: "00FFFF",
fill: "FF0000",
},
children: [
new TextRun({
text: "Hello World for entire paragraph",
}),
],
}),
], ],
}), }),
}, },

View File

@ -1,7 +1,19 @@
// Add custom borders to the table itself // Add custom borders and no-borders to the table itself
// Import from 'docx' rather than '../build' if you install from npm // Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { BorderStyle, Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build"; import {
BorderStyle,
Document,
HeadingLevel,
Packer,
Paragraph,
Table,
TableBorders,
TableCell,
TableRow,
TextDirection,
VerticalAlign,
} from "../build";
const doc = new Document(); const doc = new Document();
@ -10,6 +22,28 @@ const table = new Table({
new TableRow({ new TableRow({
children: [ children: [
new TableCell({ new TableCell({
borders: {
top: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
},
bottom: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
},
left: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
},
right: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
},
},
children: [new Paragraph("Hello")], children: [new Paragraph("Hello")],
}), }),
new TableCell({ new TableCell({
@ -30,7 +64,103 @@ const table = new Table({
], ],
}); });
doc.addSection({ children: [table] }); // Using the no-border convenience object. It is the same as writing this manually:
// const borders = {
// top: {
// style: BorderStyle.NONE,
// size: 0,
// color: "auto",
// },
// bottom: {
// style: BorderStyle.NONE,
// size: 0,
// color: "auto",
// },
// left: {
// style: BorderStyle.NONE,
// size: 0,
// color: "auto",
// },
// right: {
// style: BorderStyle.NONE,
// size: 0,
// color: "auto",
// },
// insideHorizontal: {
// style: BorderStyle.NONE,
// size: 0,
// color: "auto",
// },
// insideVertical: {
// style: BorderStyle.NONE,
// size: 0,
// color: "auto",
// },
// };
const noBorderTable = new Table({
borders: TableBorders.NONE,
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph({}), new Paragraph({})],
verticalAlign: VerticalAlign.CENTER,
}),
new TableCell({
children: [new Paragraph({}), new Paragraph({})],
verticalAlign: VerticalAlign.CENTER,
}),
new TableCell({
children: [new Paragraph({ text: "bottom to top" }), new Paragraph({})],
textDirection: TextDirection.BOTTOM_TO_TOP_LEFT_TO_RIGHT,
}),
new TableCell({
children: [new Paragraph({ text: "top to bottom" }), new Paragraph({})],
textDirection: TextDirection.TOP_TO_BOTTOM_RIGHT_TO_LEFT,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [
new Paragraph({
text:
"Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah",
heading: HeadingLevel.HEADING_1,
}),
],
}),
new TableCell({
children: [
new Paragraph({
text: "This text should be in the middle of the cell",
}),
],
verticalAlign: VerticalAlign.CENTER,
}),
new TableCell({
children: [
new Paragraph({
text: "Text above should be vertical from bottom to top",
}),
],
verticalAlign: VerticalAlign.CENTER,
}),
new TableCell({
children: [
new Paragraph({
text: "Text above should be vertical from top to bottom",
}),
],
verticalAlign: VerticalAlign.CENTER,
}),
],
}),
],
});
doc.addSection({ children: [table, new Paragraph("Hello"), noBorderTable] });
Packer.toBuffer(doc).then((buffer) => { Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer); fs.writeFileSync("My Document.docx", buffer);

View File

@ -22,6 +22,7 @@ const image4 = Media.addImage(doc, fs.readFileSync("./demo/images/parrots.bmp"))
const image5 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif")); const image5 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));
const image6 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"), 200, 200, { const image6 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
floating: { floating: {
zIndex: 10,
horizontalPosition: { horizontalPosition: {
offset: 1014400, offset: 1014400,
}, },
@ -33,6 +34,7 @@ const image6 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"), 2
const image7 = Media.addImage(doc, fs.readFileSync("./demo/images/cat.jpg"), 200, 200, { const image7 = Media.addImage(doc, fs.readFileSync("./demo/images/cat.jpg"), 200, 200, {
floating: { floating: {
zIndex: 5,
horizontalPosition: { horizontalPosition: {
relative: HorizontalPositionRelativeFrom.PAGE, relative: HorizontalPositionRelativeFrom.PAGE,
align: HorizontalPositionAlign.RIGHT, align: HorizontalPositionAlign.RIGHT,

View File

@ -15,6 +15,14 @@ const doc = new Document({
italics: true, italics: true,
}, },
}, },
{
id: "strong",
name: "Strong",
basedOn: "Normal",
run: {
bold: true,
},
},
], ],
}, },
}); });
@ -29,6 +37,18 @@ doc.addSection({
}), }),
], ],
}), }),
new Paragraph({
children: [
new TextRun({
text: "First Word",
style: "strong",
}),
new TextRun({
text:
" - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
}),
],
}),
], ],
}); });

146
demo/54-track-revisions.ts Normal file
View File

@ -0,0 +1,146 @@
// Track Revisions aka. "Track Changes"
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import {
AlignmentType,
DeletedTextRun,
Document,
Footer,
FootnoteReferenceRun,
InsertedTextRun,
Packer,
PageNumber,
Paragraph,
ShadingType,
TextRun,
} from "../build";
/*
For reference, see
- https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.insertedrun
- https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.deletedrun
The method `addTrackRevisions()` adds an element `<w:trackRevisions />` to the `settings.xml` file. This specifies that the application shall track *new* revisions made to the existing document.
See also https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.trackrevisions
Note that this setting enables to track *new changes* after teh file is generated, so this example will still show inserted and deleted text runs when you remove it.
*/
const doc = new Document({
footnotes: [
new Paragraph({
children: [
new TextRun("This is a footnote"),
new DeletedTextRun({
text: " with some extra text which was deleted",
id: 0,
author: "Firstname Lastname",
date: "2020-10-06T09:05:00Z",
}),
new InsertedTextRun({
text: " and new content",
id: 1,
author: "Firstname Lastname",
date: "2020-10-06T09:05:00Z",
}),
],
}),
],
features: {
trackRevisions: true,
},
});
const paragraph = new Paragraph({
children: [
new TextRun("This is a simple demo "),
new TextRun({
text: "on how to ",
}),
new InsertedTextRun({
text: "mark a text as an insertion ",
id: 0,
author: "Firstname Lastname",
date: "2020-10-06T09:00:00Z",
}),
new DeletedTextRun({
text: "or a deletion.",
id: 1,
author: "Firstname Lastname",
date: "2020-10-06T09:00:00Z",
}),
],
});
doc.addSection({
properties: {},
children: [
paragraph,
new Paragraph({
children: [
new TextRun("This is a demo "),
new DeletedTextRun({
break: 1,
text: "in order",
color: "red",
bold: true,
size: 24,
font: {
name: "Garamond",
},
shading: {
type: ShadingType.REVERSE_DIAGONAL_STRIPE,
color: "00FFFF",
fill: "FF0000",
},
id: 2,
author: "Firstname Lastname",
date: "2020-10-06T09:00:00Z",
}),
new InsertedTextRun({
text: "to show how to ",
bold: false,
id: 3,
author: "Firstname Lastname",
date: "2020-10-06T09:05:00Z",
}),
new TextRun({
bold: true,
children: ["\tuse Inserted and Deleted TextRuns.", new FootnoteReferenceRun(1)],
}),
],
}),
],
footers: {
default: new Footer({
children: [
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun("Awesome LLC"),
new TextRun({
children: ["Page Number: ", PageNumber.CURRENT],
}),
new DeletedTextRun({
children: [" to ", PageNumber.TOTAL_PAGES],
id: 4,
author: "Firstname Lastname",
date: "2020-10-06T09:05:00Z",
}),
new InsertedTextRun({
children: [" from ", PageNumber.TOTAL_PAGES],
bold: true,
id: 5,
author: "Firstname Lastname",
date: "2020-10-06T09:05:00Z",
}),
],
}),
],
}),
},
});
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

294
demo/55-math.ts Normal file
View File

@ -0,0 +1,294 @@
// Simple example to add text to a document
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import {
Document,
Math,
MathAngledBrackets,
MathCurlyBrackets,
MathFraction,
MathFunction,
MathPreSubSuperScript,
MathRadical,
MathRoundBrackets,
MathRun,
MathSquareBrackets,
MathSubScript,
MathSubSuperScript,
MathSum,
MathSuperScript,
Packer,
Paragraph,
TextRun,
} from "../build";
const doc = new Document();
doc.addSection({
properties: {},
children: [
new Paragraph({
children: [
new Math({
children: [
new MathRun("2+2"),
new MathFraction({
numerator: [new MathRun("hi")],
denominator: [new MathRun("2")],
}),
],
}),
new TextRun({
text: "Foo Bar",
bold: true,
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathFraction({
numerator: [
new MathRun("1"),
new MathRadical({
children: [new MathRun("2")],
}),
],
denominator: [new MathRun("2")],
}),
],
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathSum({
children: [new MathRun("test")],
}),
new MathSum({
children: [
new MathSuperScript({
children: [new MathRun("e")],
superScript: [new MathRun("2")],
}),
],
subScript: [new MathRun("i")],
}),
new MathSum({
children: [
new MathRadical({
children: [new MathRun("i")],
}),
],
subScript: [new MathRun("i")],
superScript: [new MathRun("10")],
}),
],
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathSuperScript({
children: [new MathRun("test")],
superScript: [new MathRun("hello")],
}),
],
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathSubScript({
children: [new MathRun("test")],
subScript: [new MathRun("hello")],
}),
],
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathSubScript({
children: [new MathRun("x")],
subScript: [
new MathSuperScript({
children: [new MathRun("y")],
superScript: [new MathRun("2")],
}),
],
}),
],
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathSubSuperScript({
children: [new MathRun("test")],
superScript: [new MathRun("hello")],
subScript: [new MathRun("world")],
}),
],
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathPreSubSuperScript({
children: [new MathRun("test")],
superScript: [new MathRun("hello")],
subScript: [new MathRun("world")],
}),
],
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathSubScript({
children: [
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
],
subScript: [new MathRun("4")],
}),
],
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathSubScript({
children: [
new MathRadical({
children: [
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
],
degree: [new MathRun("4")],
}),
],
subScript: [new MathRun("x")],
}),
],
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathRadical({
children: [new MathRun("4")],
}),
],
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathFunction({
name: [
new MathSuperScript({
children: [new MathRun("cos")],
superScript: [new MathRun("-1")],
}),
],
children: [new MathRun("100")],
}),
new MathRun("×"),
new MathFunction({
name: [new MathRun("sin")],
children: [new MathRun("360")],
}),
new MathRun("= x"),
],
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathRoundBrackets({
children: [
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
],
}),
new MathSquareBrackets({
children: [
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
],
}),
new MathCurlyBrackets({
children: [
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
],
}),
new MathAngledBrackets({
children: [
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
],
}),
],
}),
],
}),
new Paragraph({
children: [
new Math({
children: [
new MathFraction({
numerator: [
new MathRadical({
children: [new MathRun("4")],
}),
],
denominator: [new MathRun("2a")],
}),
],
}),
],
}),
],
});
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

View File

@ -0,0 +1,33 @@
// Change background colour of whole document
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph, TextRun } from "../build";
const doc = new Document({
background: {
color: "C45911",
},
});
doc.addSection({
properties: {},
children: [
new Paragraph({
children: [
new TextRun("Hello World"),
new TextRun({
text: "Foo Bar",
bold: true,
}),
new TextRun({
text: "\tGithub is the best",
bold: true,
}),
],
}),
],
});
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

View File

@ -0,0 +1,88 @@
// Numbered lists - Add parent number in sub number
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { AlignmentType, convertInchesToTwip, Document, HeadingLevel, LevelFormat, Packer, Paragraph } from "../build";
const doc = new Document({
numbering: {
config: [
{
levels: [
{
level: 0,
format: LevelFormat.DECIMAL,
text: "%1",
alignment: AlignmentType.START,
style: {
paragraph: {
indent: { left: convertInchesToTwip(0.5), hanging: 260 },
},
},
},
{
level: 1,
format: LevelFormat.DECIMAL,
text: "%1.%2",
alignment: AlignmentType.START,
style: {
paragraph: {
indent: { left: 1.25 * convertInchesToTwip(0.5), hanging: 1.25 * 260 },
},
run: {
bold: true,
size: 18,
font: "Times New Roman",
},
},
},
],
reference: "my-number-numbering-reference",
},
],
},
});
doc.addSection({
children: [
new Paragraph({
text: "How to make cake",
heading: HeadingLevel.HEADING_1,
}),
new Paragraph({
text: "Step 1 - Add sugar",
numbering: {
reference: "my-number-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "Step 2 - Add wheat",
numbering: {
reference: "my-number-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "Step 2a - Stir the wheat in a circle",
numbering: {
reference: "my-number-numbering-reference",
level: 1,
},
}),
new Paragraph({
text: "Step 3 - Put in oven",
numbering: {
reference: "my-number-numbering-reference",
level: 0,
},
}),
new Paragraph({
text: "How to make cake",
heading: HeadingLevel.HEADING_1,
}),
],
});
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

93
demo/58-section-types.ts Normal file
View File

@ -0,0 +1,93 @@
// Usage of different Section Types
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph, TextRun, SectionType } from "../build";
const doc = new Document();
doc.addSection({
properties: {},
children: [
new Paragraph({
children: [
new TextRun("Hello World"),
new TextRun({
text: "Foo Bar",
bold: true,
}),
],
}),
],
});
doc.addSection({
properties: {
type: SectionType.CONTINUOUS,
},
children: [
new Paragraph({
children: [
new TextRun("Hello World"),
new TextRun({
text: "Foo Bar",
bold: true,
}),
],
}),
],
});
doc.addSection({
properties: {
type: SectionType.ODD_PAGE,
},
children: [
new Paragraph({
children: [
new TextRun("Hello World"),
new TextRun({
text: "Foo Bar",
bold: true,
}),
],
}),
],
});
doc.addSection({
properties: {
type: SectionType.EVEN_PAGE,
},
children: [
new Paragraph({
children: [
new TextRun("Hello World"),
new TextRun({
text: "Foo Bar",
bold: true,
}),
],
}),
],
});
doc.addSection({
properties: {
type: SectionType.NEXT_PAGE,
},
children: [
new Paragraph({
children: [
new TextRun("Hello World"),
new TextRun({
text: "Foo Bar",
bold: true,
}),
],
}),
],
});
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

View File

@ -53,11 +53,11 @@ Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer); fs.writeFileSync("My Document.docx", buffer);
}); });
// Done! A file called 'My First Document.docx' will be in your file system. // Done! A file called 'My Document.docx' will be in your file system.
``` ```
<p align="center"> <p align="center">
<img alt="clippy the assistant" src="http://i60.tinypic.com/339pvtt.png"> <img alt="clippy the assistant" src="./clippy.png">
</p> </p>
--- ---

View File

@ -20,12 +20,16 @@
* [Tab Stops](usage/tab-stops.md) * [Tab Stops](usage/tab-stops.md)
* [Table of Contents](usage/table-of-contents.md) * [Table of Contents](usage/table-of-contents.md)
* [Page Numbers](usage/page-numbers.md) * [Page Numbers](usage/page-numbers.md)
* [Change Tracking](usage/change-tracking.md)
* [Math](usage/math.md)
* Styling * Styling
* [Styling with JS](usage/styling-with-js.md) * [Styling with JS](usage/styling-with-js.md)
* [Styling with XML](usage/styling-with-xml.md) * [Styling with XML](usage/styling-with-xml.md)
* Exporting * Exporting
* [Packers](usage/packers.md) * [Packers](usage/packers.md)
* Utility
* [Convenience functions](usage/convenience-functions.md)
* [Contribution Guidelines](contribution-guidelines.md) * [Contribution Guidelines](contribution-guidelines.md)

BIN
docs/clippy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/clippy.psd Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -5,12 +5,21 @@
To make a bullet point, simply make a paragraph into a bullet point: To make a bullet point, simply make a paragraph into a bullet point:
```ts ```ts
const text = new TextRun("Bullet points"); doc.addSection({
const paragraph = new Paragraph({ children: [
text: "Bullet points", new Paragraph({
bullet: { text: "Bullet points",
level: 0, // How deep you want the bullet to me bullet: {
}, level: 0 //How deep you want the bullet to be
}
}),
new Paragraph({
text: "Are awesome",
bullet: {
level: 0
}
})
],
}); });
``` ```

View File

@ -0,0 +1,61 @@
# Change Tracking
> Instead of adding a `TextRun` into a `Paragraph`, you can also add an `InsertedTextRun` or `DeletedTextRun` where you need to supply an `id`, `author` and `date` for the change.
```ts
import { Paragraph, TextRun, InsertedTextRun, DeletedTextRun } from "docx";
const paragraph = new Paragraph({
children: [
new TextRun("This is a simple demo "),
new TextRun({
text: "on how to "
}),
new InsertedTextRun({
text: "mark a text as an insertion ",
id: 0,
author: "Firstname Lastname",
date: "2020-10-06T09:00:00Z",
}),
new DeletedTextRun({
text: "or a deletion.",
id: 1,
author: "Firstname Lastname",
date: "2020-10-06T09:00:00Z",
})
],
});
```
Note that for a `InsertedTextRun` and `DeletedTextRun`, it is not possible to simply call it with only a text as in `new TextRun("some text")`, since the additonal fields for change tracking need to be provided. Similar to a normal `TextRun` you can add additional text properties.
```ts
import { Paragraph, TextRun, InsertedTextRun, DeletedTextRun } from "docx";
const paragraph = new Paragraph({
children: [
new TextRun("This is a simple demo"),
new DeletedTextRun({
text: "with a deletion.",
color: "red",
bold: true,
size: 24,
id: 0,
author: "Firstname Lastname",
date: "2020-10-06T09:00:00Z",
})
],
});
```
In addtion to marking text as inserted or deleted, change tracking can also be added via the document settings. This will enable new changes to be tracked as well.
```ts
import { Document } from "docx";
const doc = new Document({
features: {
trackRevisions: true,
},
});
```

View File

@ -0,0 +1,22 @@
# Convenience functions
OOXML and this library mainly uses a unit called twentieths of a point or `twip` for short. a twip is a typographical measurement, defined as 1/20 of a typographical point. One twip is 1/1440 inch, or 17.64 μm. This unit is not intuitive for many users, so some functions were created to help
More info here: https://en.wikipedia.org/wiki/Twip
## Convert Inches to Twip
```ts
import { convertInchesToTwip } from "docx";
const twip = convertInchesToTwip(1); // returns 1440
const twip = convertInchesToTwip(0.5); // returns 720
```
## Convert Millimeters to Twip
```ts
import { convertMillimetersToTwip } from "docx";
const twip = convertMillimetersToTwip(50); // returns 2834
```

View File

@ -30,6 +30,24 @@ const doc = new docx.Document({
* keywords * keywords
* lastModifiedBy * lastModifiedBy
* revision * revision
* externalStyles
* styles
* numbering
* footnotes
* hyperlinks
* background
### Change background color of Document
Set the hex value in the document like so:
```ts
const doc = new docx.Document({
background: {
color: "C45911",
},
});
```
You can mix and match whatever properties you want, or provide no properties. You can mix and match whatever properties you want, or provide no properties.

View File

@ -125,6 +125,7 @@ Full options you can pass into `floating` are:
| lockAnchor | `boolean` | Optional | | lockAnchor | `boolean` | Optional |
| behindDocument | `boolean` | Optional | | behindDocument | `boolean` | Optional |
| layoutInCell | `boolean` | Optional | | layoutInCell | `boolean` | Optional |
| zIndex | `number` | Optional |
`HorizontalPositionOptions` are: `HorizontalPositionOptions` are:

265
docs/usage/math.md Normal file
View File

@ -0,0 +1,265 @@
# Math
!> Math requires an understanding of [Sections](usage/sections.md) and [Paragraphs](usage/paragraph.md).
## Intro
1. To add math, create a `Math` object
2. Add `MathComponents` inside `Math`
3. `MathComponents` can have nested `MathComponents` inside. e.g. A fraction where the numerator is a square root, and the demoninator as another fraction. More on `MathComponents` below
4. Make sure to add the `Math` object inside a `Paragraph`
## Example
```ts
new Math({
children: [
new MathRun("2+2"),
new MathFraction({
numerator: [new MathRun("hi")],
denominator: [new MathRun("2")],
}),
],
}),
```
This will produce:
<p align="center">
<img alt="clippy the assistant" src="images/math-example.png" width="200">
</p>
## Math Components
`MathComponents` are the unit sized building blocks of an equation in `docx`. A `MathComponent` takes in more nested `MathComponents` until you reach `MathRun`, which has no children. `MathRun` is similar to a [TextRun](usage/text.md).
### Math Run
`MathRun` is the most basic `MathComponent`.
#### Example
```ts
new MathRun("2+2");
```
```ts
new MathRun("hello");
```
An example of it being used inside `Math`:
```ts
new Math({
children: [
new MathRun("2"),
new MathRun("+"),
new MathRun("2"),
],
}),
```
### Math Fraction
`MathFractions` require a `numerator` and a `demoninator`, which are both a list of `MathComponents`
#### Example
```ts
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
```
```ts
new MathFraction({
numerator: [
new MathRun("1"),
new MathRadical({
children: [new MathRun("2")],
}),
],
denominator: [new MathRun("2")],
}),
```
An example of it being used inside `Math`:
```ts
new Math({
children: [
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
new MathText("+"),
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
new MathText("= 1"),
],
}),
```
### Sum
A `MathComponent` for `Σ`. It can take a `superScript` and/or `subScript` as arguments to add `MathComponents` (usually limits) on the top and bottom
```ts
new MathSum({
children: [new MathRun("i")],
}),
```
```ts
new MathSum({
children: [
new MathSuperScript({
children: [new MathRun("e")],
superScript: [new MathRun("2")],
})
],
subScript: [new MathRun("i")],
superScript: [new MathRun("10")],
}),
```
### Radicals
A `MathComponent` for the `√` symbol. Examples include, square root, cube root etc. There is an optional `degree` parameter to specify the number of times the radicand is multiplied by itself. For example, `3` for cube root.
```ts
new MathRadical({
children: [new MathRun("2")],
}),
```
Cube root example:
```ts
new MathRadical({
children: [
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
new MathRun('+ 1'),
],
degree: [new MathRun("3")],
}),
```
### Super Script
`MathSuperScripts` are the little numbers written to the top right of numbers or variables. It means the exponent or power if written by itself with the number or variable.
```ts
new MathSuperScript({
children: [new MathRun("x")],
superScript: [new MathRun("2")],
}),
```
An example with cosine:
```ts
new MathSuperScript({
children: [new MathRun("cos")],
superScript: [new MathRun("-1")],
}),
```
### Sub Script
`MathSubScripts` are similar to `MathSuperScripts`, except the little number is written below.
```ts
new MathSubScript({
children: [new MathRun("F")],
subScript: [new MathRun("n-1")],
}),
```
### Sub-Super Script
`MathSubSuperScripts` are a combination of both `MathSuperScript` and `MathSubScript`.
```ts
new MathSubSuperScript({
children: [new MathRun("test")],
superScript: [new MathRun("hello")],
subScript: [new MathRun("world")],
}),
```
### Function
`MathFunctions` are a way of describing what happens to an input variable, in order to get the output result. It takes a `name` parameter to specify the name of the function.
```ts
new MathFunction({
name: [
new MathSuperScript({
children: [new MathRun("cos")],
superScript: [new MathRun("-1")],
}),
],
children: [new MathRun("100")],
}),
```
### Brackets
#### Square brackets
```ts
new MathSquareBrackets({
children: [
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
],
}),
```
#### Round brackets
```ts
new MathRoundBrackets({
children: [
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
],
}),
```
#### Curly brackets
```ts
new MathCurlyBrackets({
children: [
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
],
}),
```
#### Angled brackets
```ts
new MathAngledBrackets({
children: [
new MathFraction({
numerator: [new MathRun("1")],
denominator: [new MathRun("2")],
}),
],
}),
```

View File

@ -142,6 +142,21 @@ const paragraph = new Paragraph({
}); });
``` ```
## Shading
Add color to an entire paragraph block
```ts
const paragraph = new Paragraph({
text: "shading",
shading: {
type: ShadingType.REVERSE_DIAGONAL_STRIPE,
color: "00FFFF",
fill: "FF0000",
},
});
```
## Spacing ## Spacing
Adding spacing between paragraphs Adding spacing between paragraphs

View File

@ -19,3 +19,30 @@ doc.addSection({
], ],
}); });
``` ```
## Properties
You can specify additional properties to the section, by providing a `properties` attribute.
### Section Type
Setting the section type determines how the contents of the section will be placed relative to the previous section. E.g., There are five different types:
- `CONTINUOUS`
- `EVEN_PAGE`
- `NEXT_COLUMN`
- `NEXT_PAGE`
- `ODD_PAGE`
```ts
doc.addSection({
properties: {
type: SectionType.CONTINUOUS,
}
children: [
new Paragraph({
children: [new TextRun("Hello World")],
}),
],
});
```

View File

@ -51,19 +51,6 @@ const table = new Table({
}); });
``` ```
### Pagination
#### Prevent row pagination
To prevent breaking contents of a row across multiple pages, call `cantSplit`:
```ts
const table = new Table({
rows: [],
cantSplit: true,
});
```
## Table Row ## Table Row
A table consists of multiple `table rows`. Table rows have a list of `children` which accepts a list of `table cells` explained below. You can create a simple `table row` like so: A table consists of multiple `table rows`. Table rows have a list of `children` which accepts a list of `table cells` explained below. You can create a simple `table row` like so:
@ -103,7 +90,7 @@ Here is a list of options you can add to the `table row`:
| children | `Array<TableCell>` | Required | | children | `Array<TableCell>` | Required |
| cantSplit | `boolean` | Optional | | cantSplit | `boolean` | Optional |
| tableHeader | `boolean` | Optional | | tableHeader | `boolean` | Optional |
| height | `{ value: number, rule: HeightRule }` | Optional | | height | `{ height: number, rule: HeightRule }` | Optional |
### Repeat row ### Repeat row
@ -116,6 +103,19 @@ const row = new TableRow({
}); });
``` ```
### Pagination
#### Prevent row pagination
To prevent breaking contents of a row across multiple pages, call `cantSplit`:
```ts
const row = new Row({
...,
cantSplit: true,
});
```
## Table Cells ## Table Cells
Cells need to be added in the `table row`, you can create a table cell like: Cells need to be added in the `table row`, you can create a table cell like:

View File

@ -77,40 +77,78 @@ const text = new TextRun({
}); });
``` ```
### Shading and Highlighting
```ts
const text = new TextRun({
text: "shading",
shading: {
type: ShadingType.REVERSE_DIAGONAL_STRIPE,
color: "00FFFF",
fill: "FF0000",
},
});
```
```ts
const text = new TextRun({
text: "highlighting",
highlight: "yellow",
});
```
### Strike through ### Strike through
```ts ```ts
text.strike(); const text = new TextRun({
text: "strike",
strike: true,
});
``` ```
### Double strike through ### Double strike through
```ts ```ts
text.doubleStrike(); const text = new TextRun({
text: "doubleStrike",
doubleStrike: true,
});
``` ```
### Superscript ### Superscript
```ts ```ts
text.superScript(); const text = new TextRun({
text: "superScript",
superScript: true,
});
``` ```
### Subscript ### Subscript
```ts ```ts
text.subScript(); const text = new TextRun({
text: "subScript",
subScript: true,
});
``` ```
### All Capitals ### All Capitals
```ts ```ts
text.allCaps(); const text = new TextRun({
text: "allCaps",
allCaps: true,
});
``` ```
### Small Capitals ### Small Capitals
```ts ```ts
text.smallCaps(); const text = new TextRun({
text: "smallCaps",
smallCaps: true,
});
``` ```
## Break ## Break
@ -118,13 +156,17 @@ text.smallCaps();
Sometimes you would want to put text underneath another line of text but inside the same paragraph. Sometimes you would want to put text underneath another line of text but inside the same paragraph.
```ts ```ts
text.break(); const text = new TextRun({
text: "break",
break: 1,
});
``` ```
## Chaining Adding two breaks:
What if you want to create a paragraph which is **_bold_** and **_italic_**?
```ts ```ts
paragraph.bold().italics(); const text = new TextRun({
text: "break",
break: 2,
});
``` ```

996
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "docx", "name": "docx",
"version": "5.3.0", "version": "5.5.0",
"description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.", "description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
"main": "build/index.js", "main": "build/index.js",
"scripts": { "scripts": {
@ -79,7 +79,7 @@
"mocha-webpack": "^1.0.1", "mocha-webpack": "^1.0.1",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"pre-commit": "^1.2.2", "pre-commit": "^1.2.2",
"prettier": "^2.0.5", "prettier": "^2.1.2",
"prompt": "^1.0.0", "prompt": "^1.0.0",
"replace-in-file": "^3.1.0", "replace-in-file": "^3.1.0",
"request": "^2.88.0", "request": "^2.88.0",
@ -87,11 +87,11 @@
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"shelljs": "^0.8.4", "shelljs": "^0.8.4",
"sinon": "^9.0.2", "sinon": "^9.0.2",
"ts-node": "^8.10.2", "ts-node": "^9.0.0",
"tslint": "^6.1.3", "tslint": "^6.1.3",
"tslint-immutable": "^4.9.0", "tslint-immutable": "^6.0.1",
"typedoc": "^0.16.11", "typedoc": "^0.19.0",
"typescript": "2.9.2", "typescript": "4.2.2",
"webpack": "^3.10.0" "webpack": "^3.10.0"
}, },
"engines": { "engines": {

View File

@ -0,0 +1,18 @@
import { expect } from "chai";
import { convertInchesToTwip, convertMillimetersToTwip } from "./convenience-functions";
describe("Utility", () => {
describe("#convertMillimetersToTwip", () => {
it("should call the underlying header's addChildElement for Paragraph", () => {
expect(convertMillimetersToTwip(1000)).to.equal(56692);
});
});
describe("#convertInchesToTwip", () => {
it("should call the underlying header's addChildElement", () => {
expect(convertInchesToTwip(1)).to.equal(1440);
expect(convertInchesToTwip(0.5)).to.equal(720);
expect(convertInchesToTwip(0.25)).to.equal(360);
});
});
});

View File

@ -0,0 +1,8 @@
// Twip - twentieths of a point
export const convertMillimetersToTwip = (millimeters: number): number => {
return Math.floor((millimeters / 25.4) * 72 * 20);
};
export const convertInchesToTwip = (inches: number): number => {
return Math.floor(inches * 72 * 20);
};

View File

@ -1,8 +1,8 @@
import { IViewWrapper } from "file/document-wrapper";
import { BaseXmlComponent, IXmlableObject } from "file/xml-components"; import { BaseXmlComponent, IXmlableObject } from "file/xml-components";
import { File } from "../file";
export class Formatter { export class Formatter {
public format(input: BaseXmlComponent, file?: File): IXmlableObject { public format(input: BaseXmlComponent, file?: IViewWrapper): IXmlableObject {
const output = input.prepForXml(file); const output = input.prepForXml(file);
if (output) { if (output) {

View File

@ -70,23 +70,23 @@ export class Compiler {
private xmlifyFile(file: File, prettify?: boolean): IXmlifyedFileMapping { private xmlifyFile(file: File, prettify?: boolean): IXmlifyedFileMapping {
file.verifyUpdateFields(); file.verifyUpdateFields();
const documentRelationshipCount = file.DocumentRelationships.RelationshipCount + 1; const documentRelationshipCount = file.Document.Relationships.RelationshipCount + 1;
const documentXmlData = xml(this.formatter.format(file.Document, file), prettify); const documentXmlData = xml(this.formatter.format(file.Document.View, file.Document), prettify);
const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media); const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media);
return { return {
Relationships: { Relationships: {
data: (() => { data: (() => {
documentMediaDatas.forEach((mediaData, i) => { documentMediaDatas.forEach((mediaData, i) => {
file.DocumentRelationships.createRelationship( file.Document.Relationships.createRelationship(
documentRelationshipCount + i, documentRelationshipCount + i,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`, `media/${mediaData.fileName}`,
); );
}); });
return xml(this.formatter.format(file.DocumentRelationships, file), prettify); return xml(this.formatter.format(file.Document.Relationships, file.Document), prettify);
})(), })(),
path: "word/_rels/document.xml.rels", path: "word/_rels/document.xml.rels",
}, },
@ -100,11 +100,11 @@ export class Compiler {
path: "word/document.xml", path: "word/document.xml",
}, },
Styles: { Styles: {
data: xml(this.formatter.format(file.Styles, file), prettify), data: xml(this.formatter.format(file.Styles, file.Document), prettify),
path: "word/styles.xml", path: "word/styles.xml",
}, },
Properties: { Properties: {
data: xml(this.formatter.format(file.CoreProperties, file), { data: xml(this.formatter.format(file.CoreProperties, file.Document), {
declaration: { declaration: {
standalone: "yes", standalone: "yes",
encoding: "UTF-8", encoding: "UTF-8",
@ -113,15 +113,15 @@ export class Compiler {
path: "docProps/core.xml", path: "docProps/core.xml",
}, },
Numbering: { Numbering: {
data: xml(this.formatter.format(file.Numbering, file), prettify), data: xml(this.formatter.format(file.Numbering, file.Document), prettify),
path: "word/numbering.xml", path: "word/numbering.xml",
}, },
FileRelationships: { FileRelationships: {
data: xml(this.formatter.format(file.FileRelationships, file), prettify), data: xml(this.formatter.format(file.FileRelationships, file.Document), prettify),
path: "_rels/.rels", path: "_rels/.rels",
}, },
HeaderRelationships: file.Headers.map((headerWrapper, index) => { HeaderRelationships: file.Headers.map((headerWrapper, index) => {
const xmlData = xml(this.formatter.format(headerWrapper.Header, file), prettify); const xmlData = xml(this.formatter.format(headerWrapper.View, headerWrapper), prettify);
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media); const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
mediaDatas.forEach((mediaData, i) => { mediaDatas.forEach((mediaData, i) => {
@ -133,12 +133,12 @@ export class Compiler {
}); });
return { return {
data: xml(this.formatter.format(headerWrapper.Relationships, file), prettify), data: xml(this.formatter.format(headerWrapper.Relationships, headerWrapper), prettify),
path: `word/_rels/header${index + 1}.xml.rels`, path: `word/_rels/header${index + 1}.xml.rels`,
}; };
}), }),
FooterRelationships: file.Footers.map((footerWrapper, index) => { FooterRelationships: file.Footers.map((footerWrapper, index) => {
const xmlData = xml(this.formatter.format(footerWrapper.Footer, file), prettify); const xmlData = xml(this.formatter.format(footerWrapper.View, footerWrapper), prettify);
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media); const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
mediaDatas.forEach((mediaData, i) => { mediaDatas.forEach((mediaData, i) => {
@ -150,12 +150,12 @@ export class Compiler {
}); });
return { return {
data: xml(this.formatter.format(footerWrapper.Relationships, file), prettify), data: xml(this.formatter.format(footerWrapper.Relationships, footerWrapper), prettify),
path: `word/_rels/footer${index + 1}.xml.rels`, path: `word/_rels/footer${index + 1}.xml.rels`,
}; };
}), }),
Headers: file.Headers.map((headerWrapper, index) => { Headers: file.Headers.map((headerWrapper, index) => {
const tempXmlData = xml(this.formatter.format(headerWrapper.Header, file), prettify); const tempXmlData = xml(this.formatter.format(headerWrapper.View, headerWrapper), prettify);
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media); const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
// TODO: 0 needs to be changed when headers get relationships of their own // TODO: 0 needs to be changed when headers get relationships of their own
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0); const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
@ -166,7 +166,7 @@ export class Compiler {
}; };
}), }),
Footers: file.Footers.map((footerWrapper, index) => { Footers: file.Footers.map((footerWrapper, index) => {
const tempXmlData = xml(this.formatter.format(footerWrapper.Footer, file), prettify); const tempXmlData = xml(this.formatter.format(footerWrapper.View, footerWrapper), prettify);
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media); const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
// TODO: 0 needs to be changed when headers get relationships of their own // TODO: 0 needs to be changed when headers get relationships of their own
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0); const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
@ -177,7 +177,7 @@ export class Compiler {
}; };
}), }),
ContentTypes: { ContentTypes: {
data: xml(this.formatter.format(file.ContentTypes, file), prettify), data: xml(this.formatter.format(file.ContentTypes, file.Document), prettify),
path: "[Content_Types].xml", path: "[Content_Types].xml",
}, },
CustomProperties: { CustomProperties: {
@ -185,15 +185,15 @@ export class Compiler {
path: "docProps/custom.xml", path: "docProps/custom.xml",
}, },
AppProperties: { AppProperties: {
data: xml(this.formatter.format(file.AppProperties, file), prettify), data: xml(this.formatter.format(file.AppProperties, file.Document), prettify),
path: "docProps/app.xml", path: "docProps/app.xml",
}, },
FootNotes: { FootNotes: {
data: xml(this.formatter.format(file.FootNotes, file), prettify), data: xml(this.formatter.format(file.FootNotes, file.Document), prettify),
path: "word/footnotes.xml", path: "word/footnotes.xml",
}, },
Settings: { Settings: {
data: xml(this.formatter.format(file.Settings, file), prettify), data: xml(this.formatter.format(file.Settings, file.Document), prettify),
path: "word/settings.xml", path: "word/settings.xml",
}, },
}; };

View File

@ -1,22 +1,12 @@
import { XmlComponent } from "file/xml-components"; import { XmlComponent } from "file/xml-components";
import { IDocumentBackgroundOptions } from "../document";
import { DocumentAttributes } from "../document/document-attributes"; import { DocumentAttributes } from "../document/document-attributes";
import { INumberingOptions } from "../numbering"; import { INumberingOptions } from "../numbering";
import { HyperlinkType, Paragraph } from "../paragraph"; import { Paragraph } from "../paragraph";
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";
export interface IInternalHyperlinkDefinition {
readonly text: string;
readonly type: HyperlinkType.INTERNAL;
}
export interface IExternalHyperlinkDefinition {
readonly link: string;
readonly text: string;
readonly type: HyperlinkType.EXTERNAL;
}
export interface IPropertiesOptions { export interface IPropertiesOptions {
readonly title?: string; readonly title?: string;
readonly subject?: string; readonly subject?: string;
@ -29,9 +19,11 @@ export interface IPropertiesOptions {
readonly styles?: IStylesOptions; readonly styles?: IStylesOptions;
readonly numbering?: INumberingOptions; readonly numbering?: INumberingOptions;
readonly footnotes?: Paragraph[]; readonly footnotes?: Paragraph[];
readonly hyperlinks?: { readonly background?: IDocumentBackgroundOptions;
readonly [key: string]: IInternalHyperlinkDefinition | IExternalHyperlinkDefinition; readonly features?: {
readonly trackRevisions?: boolean;
}; };
readonly compatabilityModeVersion?: number;
} }
export class CoreProperties extends XmlComponent { export class CoreProperties extends XmlComponent {

View File

@ -0,0 +1,50 @@
import { expect } from "chai";
import * as sinon from "sinon";
import { FooterWrapper } from "./footer-wrapper";
import { Media } from "./media";
import { Paragraph } from "./paragraph";
import { Table, TableCell, TableRow } from "./table";
describe("FooterWrapper", () => {
describe("#add", () => {
it("should call the underlying footer's addParagraph", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.View, "add");
file.add(new Paragraph({}));
expect(spy.called).to.equal(true);
});
it("should call the underlying footer's addParagraph", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.View, "add");
file.add(
new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("hello")],
}),
],
}),
],
}),
);
expect(spy.called).to.equal(true);
});
});
describe("#addChildElement", () => {
it("should call the underlying footer's addChildElement", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.View, "addChildElement");
// tslint:disable-next-line:no-any
file.addChildElement({} as any);
expect(spy.called).to.equal(true);
});
});
});

View File

@ -0,0 +1,27 @@
import { Document, IDocumentOptions } from "./document";
import { Footer } from "./footer";
import { Header } from "./header/header";
import { Relationships } from "./relationships";
export interface IViewWrapper {
readonly View: Document | Footer | Header;
readonly Relationships: Relationships;
}
export class DocumentWrapper implements IViewWrapper {
private readonly document: Document;
private readonly relationships: Relationships;
constructor(options: IDocumentOptions) {
this.document = new Document(options);
this.relationships = new Relationships();
}
public get View(): Document {
return this.document;
}
public get Relationships(): Relationships {
return this.relationships;
}
}

View File

@ -1,6 +1,7 @@
import { IViewWrapper } from "file/document-wrapper";
import { IXmlableObject, XmlComponent } from "file/xml-components"; import { IXmlableObject, XmlComponent } from "file/xml-components";
import { Paragraph, ParagraphProperties, TableOfContents } from "../.."; import { Paragraph, ParagraphProperties, TableOfContents } from "../..";
import { File } from "../../../file";
import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties"; import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties";
export class Body extends XmlComponent { export class Body extends XmlComponent {
@ -25,7 +26,7 @@ export class Body extends XmlComponent {
this.sections.push(new SectionProperties(options)); this.sections.push(new SectionProperties(options));
} }
public prepForXml(file?: File): IXmlableObject | undefined { public prepForXml(file?: IViewWrapper): IXmlableObject | undefined {
if (this.sections.length === 1) { if (this.sections.length === 1) {
this.root.splice(0, 1); this.root.splice(0, 1);
this.root.push(this.sections.pop() as SectionProperties); this.root.push(this.sections.pop() as SectionProperties);

View File

@ -6,3 +6,4 @@ export * from "./page-number";
export * from "./page-border"; export * from "./page-border";
export * from "./line-number"; export * from "./line-number";
export * from "./vertical-align"; export * from "./vertical-align";
export * from "./type";

View File

@ -1,5 +1,6 @@
import { expect } from "chai"; import { expect } from "chai";
import { convertInchesToTwip } from "convenience-functions";
import { Formatter } from "export/formatter"; import { Formatter } from "export/formatter";
import { FooterWrapper } from "file/footer-wrapper"; import { FooterWrapper } from "file/footer-wrapper";
import { HeaderWrapper } from "file/header-wrapper"; import { HeaderWrapper } from "file/header-wrapper";
@ -8,6 +9,7 @@ import { Media } from "file/media";
import { PageBorderOffsetFrom } from "./page-border"; import { PageBorderOffsetFrom } from "./page-border";
import { PageNumberFormat } from "./page-number"; import { PageNumberFormat } from "./page-number";
import { SectionProperties } from "./section-properties"; import { SectionProperties } from "./section-properties";
import { SectionType } from "./type/section-type-attributes";
import { SectionVerticalAlignValue } from "./vertical-align"; import { SectionVerticalAlignValue } from "./vertical-align";
describe("SectionProperties", () => { describe("SectionProperties", () => {
@ -18,10 +20,10 @@ describe("SectionProperties", () => {
const properties = new SectionProperties({ const properties = new SectionProperties({
width: 11906, width: 11906,
height: 16838, height: 16838,
top: 1440, top: convertInchesToTwip(1),
right: 1440, right: convertInchesToTwip(1),
bottom: 1440, bottom: convertInchesToTwip(1),
left: 1440, left: convertInchesToTwip(1),
header: 708, header: 708,
footer: 708, footer: 708,
gutter: 0, gutter: 0,
@ -30,7 +32,7 @@ describe("SectionProperties", () => {
space: 708, space: 708,
count: 1, count: 1,
}, },
linePitch: 360, linePitch: convertInchesToTwip(0.25),
headers: { headers: {
default: new HeaderWrapper(media, 100), default: new HeaderWrapper(media, 100),
}, },
@ -198,5 +200,17 @@ describe("SectionProperties", () => {
const pgNumType = tree["w:sectPr"].find((item) => item["w:pgNumType"] !== undefined); const pgNumType = tree["w:sectPr"].find((item) => item["w:pgNumType"] !== undefined);
expect(pgNumType).to.equal(undefined); expect(pgNumType).to.equal(undefined);
}); });
it("should create section properties with section type", () => {
const properties = new SectionProperties({
type: SectionType.CONTINUOUS,
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
const type = tree["w:sectPr"].find((item) => item["w:type"] !== undefined);
expect(type).to.deep.equal({
"w:type": { _attr: { "w:val": "continuous" } },
});
});
}); });
}); });

View File

@ -1,4 +1,5 @@
// http://officeopenxml.com/WPsection.php // http://officeopenxml.com/WPsection.php
import { convertInchesToTwip } from "convenience-functions";
import { FooterWrapper } from "file/footer-wrapper"; import { FooterWrapper } from "file/footer-wrapper";
import { HeaderWrapper } from "file/header-wrapper"; import { HeaderWrapper } from "file/header-wrapper";
import { XmlComponent } from "file/xml-components"; import { XmlComponent } from "file/xml-components";
@ -18,6 +19,8 @@ import { IPageNumberTypeAttributes, PageNumberType } from "./page-number";
import { PageSize } from "./page-size/page-size"; import { PageSize } from "./page-size/page-size";
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes"; import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
import { TitlePage } from "./title-page/title-page"; import { TitlePage } from "./title-page/title-page";
import { Type } from "./type/section-type";
import { SectionType } from "./type/section-type-attributes";
import { ISectionVerticalAlignAttributes, SectionVerticalAlign } from "./vertical-align"; import { ISectionVerticalAlignAttributes, SectionVerticalAlign } from "./vertical-align";
export interface IHeaderFooterGroup<T> { export interface IHeaderFooterGroup<T> {
@ -52,6 +55,7 @@ export type SectionPropertiesOptions = IPageSizeAttributes &
readonly space?: number; readonly space?: number;
readonly count?: number; readonly count?: number;
}; };
readonly type?: SectionType;
}; };
// Need to decouple this from the attributes // Need to decouple this from the attributes
@ -64,10 +68,10 @@ export class SectionProperties extends XmlComponent {
const { const {
width = 11906, width = 11906,
height = 16838, height = 16838,
top = 1440, top = convertInchesToTwip(1),
right = 1440, right = convertInchesToTwip(1),
bottom = 1440, bottom = convertInchesToTwip(1),
left = 1440, left = convertInchesToTwip(1),
header = 708, header = 708,
footer = 708, footer = 708,
gutter = 0, gutter = 0,
@ -90,6 +94,7 @@ export class SectionProperties extends XmlComponent {
pageBorderLeft, pageBorderLeft,
titlePage = false, titlePage = false,
verticalAlign, verticalAlign,
type,
} = options; } = options;
this.options = options; this.options = options;
@ -128,6 +133,10 @@ export class SectionProperties extends XmlComponent {
if (verticalAlign) { if (verticalAlign) {
this.root.push(new SectionVerticalAlign(verticalAlign)); this.root.push(new SectionVerticalAlign(verticalAlign));
} }
if (type) {
this.root.push(new Type(type));
}
} }
private addHeaders(headers?: IHeaderFooterGroup<HeaderWrapper>): void { private addHeaders(headers?: IHeaderFooterGroup<HeaderWrapper>): void {
@ -136,7 +145,7 @@ export class SectionProperties extends XmlComponent {
this.root.push( this.root.push(
new HeaderReference({ new HeaderReference({
headerType: HeaderReferenceType.DEFAULT, headerType: HeaderReferenceType.DEFAULT,
headerId: headers.default.Header.ReferenceId, headerId: headers.default.View.ReferenceId,
}), }),
); );
} }
@ -145,7 +154,7 @@ export class SectionProperties extends XmlComponent {
this.root.push( this.root.push(
new HeaderReference({ new HeaderReference({
headerType: HeaderReferenceType.FIRST, headerType: HeaderReferenceType.FIRST,
headerId: headers.first.Header.ReferenceId, headerId: headers.first.View.ReferenceId,
}), }),
); );
} }
@ -154,7 +163,7 @@ export class SectionProperties extends XmlComponent {
this.root.push( this.root.push(
new HeaderReference({ new HeaderReference({
headerType: HeaderReferenceType.EVEN, headerType: HeaderReferenceType.EVEN,
headerId: headers.even.Header.ReferenceId, headerId: headers.even.View.ReferenceId,
}), }),
); );
} }
@ -167,7 +176,7 @@ export class SectionProperties extends XmlComponent {
this.root.push( this.root.push(
new FooterReference({ new FooterReference({
footerType: FooterReferenceType.DEFAULT, footerType: FooterReferenceType.DEFAULT,
footerId: footers.default.Footer.ReferenceId, footerId: footers.default.View.ReferenceId,
}), }),
); );
} }
@ -176,7 +185,7 @@ export class SectionProperties extends XmlComponent {
this.root.push( this.root.push(
new FooterReference({ new FooterReference({
footerType: FooterReferenceType.FIRST, footerType: FooterReferenceType.FIRST,
footerId: footers.first.Footer.ReferenceId, footerId: footers.first.View.ReferenceId,
}), }),
); );
} }
@ -185,7 +194,7 @@ export class SectionProperties extends XmlComponent {
this.root.push( this.root.push(
new FooterReference({ new FooterReference({
footerType: FooterReferenceType.EVEN, footerType: FooterReferenceType.EVEN,
footerId: footers.even.Footer.ReferenceId, footerId: footers.even.View.ReferenceId,
}), }),
); );
} }

View File

@ -0,0 +1,2 @@
export * from "./section-type";
export * from "./section-type-attributes";

View File

@ -0,0 +1,17 @@
import { XmlAttributeComponent } from "file/xml-components";
export enum SectionType {
CONTINUOUS = "continuous",
EVEN_PAGE = "evenPage",
NEXT_COLUMN = "nextColumn",
NEXT_PAGE = "nextPage",
ODD_PAGE = "oddPage",
}
export class SectionTypeAttributes extends XmlAttributeComponent<{
readonly val: SectionType;
}> {
protected readonly xmlKeys = {
val: "w:val",
};
}

View File

@ -0,0 +1,35 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { Type } from "./section-type";
import { SectionType } from "./section-type-attributes";
describe("Type", () => {
it("should create with even page section type", () => {
const sectionType = new Type(SectionType.EVEN_PAGE);
const tree = new Formatter().format(sectionType);
expect(tree).to.deep.equal({
"w:type": {
_attr: {
"w:val": "evenPage",
},
},
});
});
it("should create with continuous section type", () => {
const sectionType = new Type(SectionType.CONTINUOUS);
const tree = new Formatter().format(sectionType);
expect(tree).to.deep.equal({
"w:type": {
_attr: {
"w:val": "continuous",
},
},
});
});
});

View File

@ -0,0 +1,10 @@
// http://officeopenxml.com/WPsection.php
import { XmlComponent } from "file/xml-components";
import { SectionType, SectionTypeAttributes } from "./section-type-attributes";
export class Type extends XmlComponent {
constructor(value: SectionType) {
super("w:type");
this.root.push(new SectionTypeAttributes({ val: value }));
}
}

View File

@ -0,0 +1,53 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { DocumentBackground } from "./document-background";
describe("DocumentBackground", () => {
describe("#constructor()", () => {
it("should create a DocumentBackground with no options and set color to auto", () => {
const documentBackground = new DocumentBackground({});
const tree = new Formatter().format(documentBackground);
expect(tree).to.deep.equal({
"w:background": {
_attr: {
"w:color": "FFFFFF",
},
},
});
});
it("should create a DocumentBackground with no options and set color to value", () => {
const documentBackground = new DocumentBackground({ color: "ffff00" });
const tree = new Formatter().format(documentBackground);
expect(tree).to.deep.equal({
"w:background": {
_attr: {
"w:color": "ffff00",
},
},
});
});
it("should create a DocumentBackground with no options and set other values", () => {
const documentBackground = new DocumentBackground({
color: "ffff00",
themeColor: "test",
themeShade: "test",
themeTint: "test",
});
const tree = new Formatter().format(documentBackground);
expect(tree).to.deep.equal({
"w:background": {
_attr: {
"w:color": "ffff00",
"w:themeColor": "test",
"w:themeShade": "test",
"w:themeTint": "test",
},
},
});
});
});
});

View File

@ -0,0 +1,39 @@
// http://officeopenxml.com/WPdocument.php
// http://www.datypic.com/sc/ooxml/e-w_background-1.html
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export class DocumentBackgroundAttributes extends XmlAttributeComponent<{
readonly color: string;
readonly themeColor?: string;
readonly themeShade?: string;
readonly themeTint?: string;
}> {
protected readonly xmlKeys = {
color: "w:color",
themeColor: "w:themeColor",
themeShade: "w:themeShade",
themeTint: "w:themeTint",
};
}
export interface IDocumentBackgroundOptions {
readonly color?: string;
readonly themeColor?: string;
readonly themeShade?: string;
readonly themeTint?: string;
}
export class DocumentBackground extends XmlComponent {
constructor(options: IDocumentBackgroundOptions) {
super("w:background");
this.root.push(
new DocumentBackgroundAttributes({
color: options.color ? options.color : "FFFFFF",
themeColor: options.themeColor,
themeShade: options.themeShade,
themeTint: options.themeTint,
}),
);
}
}

View File

@ -0,0 +1 @@
export * from "./document-background";

View File

@ -8,7 +8,7 @@ describe("Document", () => {
let document: Document; let document: Document;
beforeEach(() => { beforeEach(() => {
document = new Document(); document = new Document({ background: {} });
}); });
describe("#constructor()", () => { describe("#constructor()", () => {
@ -38,6 +38,13 @@ describe("Document", () => {
"mc:Ignorable": "w14 w15 wp14", "mc:Ignorable": "w14 w15 wp14",
}, },
}, },
{
"w:background": {
_attr: {
"w:color": "FFFFFF",
},
},
},
{ "w:body": {} }, { "w:body": {} },
], ],
}); });

View File

@ -1,15 +1,20 @@
// http://officeopenxml.com/WPdocument.php // http://officeopenxml.com/WPdocument.php
import { XmlComponent } from "file/xml-components"; import { XmlComponent } from "file/xml-components";
import { Hyperlink, Paragraph } from "../paragraph"; import { ConcreteHyperlink, Paragraph } from "../paragraph";
import { Table } from "../table"; import { Table } from "../table";
import { TableOfContents } from "../table-of-contents"; import { TableOfContents } from "../table-of-contents";
import { Body } from "./body"; import { Body } from "./body";
import { DocumentAttributes } from "./document-attributes"; import { DocumentAttributes } from "./document-attributes";
import { DocumentBackground, IDocumentBackgroundOptions } from "./document-background";
export interface IDocumentOptions {
readonly background: IDocumentBackgroundOptions;
}
export class Document extends XmlComponent { export class Document extends XmlComponent {
private readonly body: Body; private readonly body: Body;
constructor() { constructor(options: IDocumentOptions) {
super("w:document"); super("w:document");
this.root.push( this.root.push(
new DocumentAttributes({ new DocumentAttributes({
@ -33,10 +38,11 @@ export class Document extends XmlComponent {
}), }),
); );
this.body = new Body(); this.body = new Body();
this.root.push(new DocumentBackground(options.background));
this.root.push(this.body); this.root.push(this.body);
} }
public add(item: Paragraph | Table | TableOfContents | Hyperlink): Document { public add(item: Paragraph | Table | TableOfContents | ConcreteHyperlink): Document {
this.body.push(item); this.body.push(item);
return this; return this;
} }

View File

@ -1,3 +1,4 @@
export * from "./document"; export * from "./document";
export * from "./document-attributes"; export * from "./document-attributes";
export * from "./body"; export * from "./body";
export * from "./document-background";

View File

@ -218,5 +218,150 @@ describe("Anchor", () => {
const textWrap = newJson.root[6]; const textWrap = newJson.root[6];
assert.equal(textWrap.rootKey, "wp:wrapTopAndBottom"); assert.equal(textWrap.rootKey, "wp:wrapTopAndBottom");
}); });
it("should create a Drawing with a margin", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
margins: {
top: 10,
left: 10,
bottom: 10,
right: 10,
},
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
distT: 10,
distB: 10,
distL: 10,
distR: 10,
});
});
it("should create a Drawing with a default margin", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
margins: {},
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
distT: 0,
distB: 0,
distL: 0,
distR: 0,
});
});
it("should create a Drawing with allowOverlap being false", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
allowOverlap: false,
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
allowOverlap: "0",
});
});
it("should create a Drawing with behindDocument being true", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
behindDocument: true,
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
behindDoc: "1",
});
});
it("should create a Drawing with locked being true", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
lockAnchor: true,
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
locked: "1",
});
});
it("should create a Drawing with locked being false", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
layoutInCell: false,
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
layoutInCell: "0",
});
});
it("should create a Drawing with a certain z-index", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
zIndex: 120,
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
relativeHeight: 120,
});
});
}); });
}); });

View File

@ -11,42 +11,32 @@ import { Extent } from "./../extent/extent";
import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties"; import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties";
import { AnchorAttributes } from "./anchor-attributes"; import { AnchorAttributes } from "./anchor-attributes";
const defaultOptions: IFloating = {
allowOverlap: true,
behindDocument: false,
lockAnchor: false,
layoutInCell: true,
verticalPosition: {},
horizontalPosition: {},
};
export class Anchor extends XmlComponent { export class Anchor extends XmlComponent {
constructor(mediaData: IMediaData, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) { constructor(mediaData: IMediaData, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) {
super("wp:anchor"); super("wp:anchor");
const floating = { const floating: IFloating = {
margins: { allowOverlap: true,
top: 0, behindDocument: false,
bottom: 0, lockAnchor: false,
left: 0, layoutInCell: true,
right: 0, verticalPosition: {},
}, horizontalPosition: {},
...defaultOptions,
...drawingOptions.floating, ...drawingOptions.floating,
}; };
this.root.push( this.root.push(
new AnchorAttributes({ new AnchorAttributes({
distT: floating.margins.top || 0, distT: floating.margins ? floating.margins.top || 0 : 0,
distB: floating.margins.bottom || 0, distB: floating.margins ? floating.margins.bottom || 0 : 0,
distL: floating.margins.left || 0, distL: floating.margins ? floating.margins.left || 0 : 0,
distR: floating.margins.right || 0, distR: floating.margins ? floating.margins.right || 0 : 0,
simplePos: "0", // note: word doesn't fully support - so we use 0 simplePos: "0", // note: word doesn't fully support - so we use 0
allowOverlap: floating.allowOverlap === true ? "1" : "0", allowOverlap: floating.allowOverlap === true ? "1" : "0",
behindDoc: floating.behindDocument === true ? "1" : "0", behindDoc: floating.behindDocument === true ? "1" : "0",
locked: floating.lockAnchor === true ? "1" : "0", locked: floating.lockAnchor === true ? "1" : "0",
layoutInCell: floating.layoutInCell === true ? "1" : "0", layoutInCell: floating.layoutInCell === true ? "1" : "0",
relativeHeight: dimensions.emus.y, relativeHeight: floating.zIndex ? floating.zIndex : dimensions.emus.y,
}), }),
); );

View File

@ -1,4 +1,5 @@
// http://officeopenxml.com/drwPicFloating-position.php // http://officeopenxml.com/drwPicFloating-position.php
// http://officeopenxml.com/drwPicFloating.php
import { ITextWrapping } from "../text-wrap"; import { ITextWrapping } from "../text-wrap";
export enum HorizontalPositionRelativeFrom { export enum HorizontalPositionRelativeFrom {
@ -67,4 +68,5 @@ export interface IFloating {
readonly layoutInCell?: boolean; readonly layoutInCell?: boolean;
readonly margins?: IMargins; readonly margins?: IMargins;
readonly wrap?: ITextWrapping; readonly wrap?: ITextWrapping;
readonly zIndex?: number;
} }

View File

@ -12,7 +12,7 @@ export class Inline extends XmlComponent {
private readonly extent: Extent; private readonly extent: Extent;
private readonly graphic: Graphic; private readonly graphic: Graphic;
constructor(readonly mediaData: IMediaData, private readonly dimensions: IMediaDataDimensions) { constructor(mediaData: IMediaData, private readonly dimensions: IMediaDataDimensions) {
super("wp:inline"); super("wp:inline");
this.root.push( this.root.push(

View File

@ -5,7 +5,7 @@ import { Formatter } from "export/formatter";
import { File } from "./file"; import { File } from "./file";
import { Footer, Header } from "./header"; import { Footer, Header } from "./header";
import { HyperlinkRef, Paragraph } from "./paragraph"; import { Paragraph } from "./paragraph";
import { Table, TableCell, TableRow } from "./table"; import { Table, TableCell, TableRow } from "./table";
import { TableOfContents } from "./table-of-contents"; import { TableOfContents } from "./table-of-contents";
@ -18,7 +18,7 @@ describe("File", () => {
children: [], children: [],
}); });
const tree = new Formatter().format(doc.Document.Body); const tree = new Formatter().format(doc.Document.View.Body);
expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][0]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][0]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default");
@ -37,7 +37,7 @@ describe("File", () => {
children: [], children: [],
}); });
const tree = new Formatter().format(doc.Document.Body); const tree = new Formatter().format(doc.Document.View.Body);
expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][0]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][0]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default");
@ -56,7 +56,7 @@ describe("File", () => {
children: [], children: [],
}); });
const tree = new Formatter().format(doc.Document.Body); const tree = new Formatter().format(doc.Document.View.Body);
expect(tree["w:body"][0]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first"); expect(tree["w:body"][0]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first");
expect(tree["w:body"][0]["w:sectPr"][7]["w:footerReference"]._attr["w:type"]).to.equal("first"); expect(tree["w:body"][0]["w:sectPr"][7]["w:footerReference"]._attr["w:type"]).to.equal("first");
@ -79,7 +79,7 @@ describe("File", () => {
children: [], children: [],
}); });
const tree = new Formatter().format(doc.Document.Body); const tree = new Formatter().format(doc.Document.View.Body);
expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][0]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first"); expect(tree["w:body"][0]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first");
@ -97,7 +97,7 @@ describe("File", () => {
}, },
]); ]);
const tree = new Formatter().format(doc.Document.Body); const tree = new Formatter().format(doc.Document.View.Body);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:body": [ "w:body": [
@ -164,22 +164,12 @@ describe("File", () => {
], ],
}); });
}); });
it("should add hyperlink child", () => {
const doc = new File(undefined, undefined, [
{
children: [new HyperlinkRef("test")],
},
]);
expect(doc.HyperlinkCache).to.deep.equal({});
});
}); });
describe("#addSection", () => { describe("#addSection", () => {
it("should call the underlying document's add a Paragraph", () => { it("should call the underlying document's add a Paragraph", () => {
const file = new File(); const file = new File();
const spy = sinon.spy(file.Document, "add"); const spy = sinon.spy(file.Document.View, "add");
file.addSection({ file.addSection({
children: [new Paragraph({})], children: [new Paragraph({})],
}); });
@ -187,19 +177,9 @@ describe("File", () => {
expect(spy.called).to.equal(true); expect(spy.called).to.equal(true);
}); });
it("should add hyperlink child", () => {
const doc = new File();
doc.addSection({
children: [new HyperlinkRef("test")],
});
expect(doc.HyperlinkCache).to.deep.equal({});
});
it("should call the underlying document's add when adding a Table", () => { it("should call the underlying document's add when adding a Table", () => {
const file = new File(); const file = new File();
const spy = sinon.spy(file.Document, "add"); const spy = sinon.spy(file.Document.View, "add");
file.addSection({ file.addSection({
children: [ children: [
new Table({ new Table({
@ -221,7 +201,7 @@ describe("File", () => {
it("should call the underlying document's add when adding an Image (paragraph)", () => { it("should call the underlying document's add when adding an Image (paragraph)", () => {
const file = new File(); const file = new File();
const spy = sinon.spy(file.Document, "add"); const spy = sinon.spy(file.Document.View, "add");
// tslint:disable-next-line:no-any // tslint:disable-next-line:no-any
file.addSection({ file.addSection({
children: [new Paragraph("")], children: [new Paragraph("")],
@ -234,7 +214,7 @@ describe("File", () => {
describe("#addSection", () => { describe("#addSection", () => {
it("should call the underlying document's add", () => { it("should call the underlying document's add", () => {
const file = new File(); const file = new File();
const spy = sinon.spy(file.Document, "add"); const spy = sinon.spy(file.Document.View, "add");
file.addSection({ file.addSection({
children: [new TableOfContents()], children: [new TableOfContents()],
}); });
@ -243,11 +223,16 @@ describe("File", () => {
}); });
}); });
describe("#HyperlinkCache", () => { describe("#addTrackRevisionsFeature", () => {
it("should initially have empty hyperlink cache", () => { it("should call the underlying document's add", () => {
const file = new File(); const file = new File({
features: {
trackRevisions: true,
},
});
expect(file.HyperlinkCache).to.deep.equal({}); // tslint:disable-next-line: no-unused-expression no-string-literal
expect(file.Settings["trackRevisions"]).to.exist;
}); });
}); });

View File

@ -1,9 +1,8 @@
import * as shortid from "shortid";
import { AppProperties } from "./app-properties/app-properties"; import { AppProperties } from "./app-properties/app-properties";
import { ContentTypes } from "./content-types/content-types"; import { ContentTypes } from "./content-types/content-types";
import { CoreProperties, IPropertiesOptions } from "./core-properties"; import { CoreProperties, IPropertiesOptions } from "./core-properties";
import { CustomProperties, ICustomPropertyOptions } from "./custom-properties"; import { CustomProperties, ICustomPropertyOptions } from "./custom-properties";
import { Document } from "./document"; import { DocumentWrapper } from "./document-wrapper";
import { import {
FooterReferenceType, FooterReferenceType,
HeaderReferenceType, HeaderReferenceType,
@ -18,9 +17,8 @@ import { Footer, Header } from "./header";
import { HeaderWrapper, IDocumentHeader } from "./header-wrapper"; import { HeaderWrapper, IDocumentHeader } from "./header-wrapper";
import { Media } from "./media"; import { Media } from "./media";
import { Numbering } from "./numbering"; import { Numbering } from "./numbering";
import { Hyperlink, HyperlinkRef, HyperlinkType, Paragraph } from "./paragraph"; import { Paragraph } from "./paragraph";
import { Relationships } from "./relationships"; import { Relationships } from "./relationships";
import { TargetModeType } from "./relationships/relationship/relationship";
import { Settings } from "./settings"; import { Settings } from "./settings";
import { Styles } from "./styles"; import { Styles } from "./styles";
import { ExternalStylesFactory } from "./styles/external-styles-factory"; import { ExternalStylesFactory } from "./styles/external-styles-factory";
@ -42,17 +40,16 @@ export interface ISectionOptions {
readonly size?: IPageSizeAttributes; readonly size?: IPageSizeAttributes;
readonly margins?: IPageMarginAttributes; readonly margins?: IPageMarginAttributes;
readonly properties?: SectionPropertiesOptions; readonly properties?: SectionPropertiesOptions;
readonly children: (Paragraph | Table | TableOfContents | HyperlinkRef)[]; readonly children: (Paragraph | Table | TableOfContents)[];
} }
export class File { export class File {
// tslint:disable-next-line:readonly-keyword // tslint:disable-next-line:readonly-keyword
private currentRelationshipId: number = 1; private currentRelationshipId: number = 1;
private readonly document: Document; private readonly documentWrapper: DocumentWrapper;
private readonly headers: IDocumentHeader[] = []; private readonly headers: IDocumentHeader[] = [];
private readonly footers: IDocumentFooter[] = []; private readonly footers: IDocumentFooter[] = [];
private readonly docRelationships: Relationships;
private readonly coreProperties: CoreProperties; private readonly coreProperties: CoreProperties;
private readonly numbering: Numbering; private readonly numbering: Numbering;
private readonly media: Media; private readonly media: Media;
@ -63,7 +60,6 @@ export class File {
private readonly customProperties: CustomProperties; private readonly customProperties: CustomProperties;
private readonly appProperties: AppProperties; private readonly appProperties: AppProperties;
private readonly styles: Styles; private readonly styles: Styles;
private readonly hyperlinkCache: { readonly [key: string]: Hyperlink } = {};
constructor( constructor(
options: IPropertiesOptions = { options: IPropertiesOptions = {
@ -83,14 +79,16 @@ export class File {
config: [], config: [],
}, },
); );
this.docRelationships = new Relationships(); // this.documentWrapper.Relationships = new Relationships();
this.fileRelationships = new Relationships(); this.fileRelationships = new Relationships();
this.customProperties = new CustomProperties(customProperties); this.customProperties = new CustomProperties(customProperties);
this.appProperties = new AppProperties(); this.appProperties = new AppProperties();
this.footNotes = new FootNotes(); this.footNotes = new FootNotes();
this.contentTypes = new ContentTypes(); this.contentTypes = new ContentTypes();
this.document = new Document(); this.documentWrapper = new DocumentWrapper({ background: options.background || {} });
this.settings = new Settings(); this.settings = new Settings({
compatabilityModeVersion: options.compatabilityModeVersion,
});
this.media = fileProperties.template && fileProperties.template.media ? fileProperties.template.media : new Media(); this.media = fileProperties.template && fileProperties.template.media ? fileProperties.template.media : new Media();
@ -110,7 +108,7 @@ export class File {
this.styles = stylesFactory.newInstance(options.externalStyles); this.styles = stylesFactory.newInstance(options.externalStyles);
} else if (options.styles) { } else if (options.styles) {
const stylesFactory = new DefaultStylesFactory(); const stylesFactory = new DefaultStylesFactory();
const defaultStyles = stylesFactory.newInstance(); const defaultStyles = stylesFactory.newInstance(options.styles.default);
this.styles = new Styles({ this.styles = new Styles({
...defaultStyles, ...defaultStyles,
...options.styles, ...options.styles,
@ -135,16 +133,10 @@ export class File {
} }
for (const section of sections) { for (const section of sections) {
this.document.Body.addSection(section.properties ? section.properties : {}); this.documentWrapper.View.Body.addSection(section.properties ? section.properties : {});
for (const child of section.children) { for (const child of section.children) {
if (child instanceof HyperlinkRef) { this.documentWrapper.View.add(child);
const hyperlink = this.hyperlinkCache[child.id];
this.document.add(hyperlink);
continue;
}
this.document.add(child);
} }
} }
@ -154,25 +146,10 @@ export class File {
} }
} }
if (options.hyperlinks) { if (options.features) {
const cache = {}; if (options.features.trackRevisions) {
this.settings.addTrackRevisions();
for (const key in options.hyperlinks) {
if (!options.hyperlinks[key]) {
continue;
}
const hyperlinkRef = options.hyperlinks[key];
const hyperlink =
hyperlinkRef.type === HyperlinkType.EXTERNAL
? this.createHyperlink(hyperlinkRef.link, hyperlinkRef.text)
: this.createInternalHyperLink(key, hyperlinkRef.text);
cache[key] = hyperlink;
} }
this.hyperlinkCache = cache;
} }
} }
@ -184,7 +161,7 @@ export class File {
properties, properties,
children, children,
}: ISectionOptions): void { }: ISectionOptions): void {
this.document.Body.addSection({ this.documentWrapper.View.Body.addSection({
...properties, ...properties,
headers: { headers: {
default: headers.default ? this.createHeader(headers.default) : this.createHeader(new Header()), default: headers.default ? this.createHeader(headers.default) : this.createHeader(new Header()),
@ -201,40 +178,16 @@ export class File {
}); });
for (const child of children) { for (const child of children) {
if (child instanceof HyperlinkRef) { this.documentWrapper.View.add(child);
const hyperlink = this.hyperlinkCache[child.id];
this.document.add(hyperlink);
continue;
}
this.document.add(child);
} }
} }
public verifyUpdateFields(): void { public verifyUpdateFields(): void {
if (this.document.getTablesOfContents().length) { if (this.documentWrapper.View.getTablesOfContents().length) {
this.settings.addUpdateFields(); this.settings.addUpdateFields();
} }
} }
private createHyperlink(link: string, text: string = link): Hyperlink {
const hyperlink = new Hyperlink(text, shortid.generate().toLowerCase());
this.docRelationships.createRelationship(
hyperlink.linkId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
link,
TargetModeType.EXTERNAL,
);
return hyperlink;
}
private createInternalHyperLink(anchor: string, text: string = anchor): Hyperlink {
const hyperlink = new Hyperlink(text, shortid.generate().toLowerCase(), anchor);
// NOTE: unlike File#createHyperlink(), since the link is to an internal bookmark
// we don't need to create a new relationship.
return hyperlink;
}
private createHeader(header: Header): HeaderWrapper { private createHeader(header: Header): HeaderWrapper {
const wrapper = new HeaderWrapper(this.media, this.currentRelationshipId++); const wrapper = new HeaderWrapper(this.media, this.currentRelationshipId++);
@ -259,8 +212,8 @@ export class File {
private addHeaderToDocument(header: HeaderWrapper, type: HeaderReferenceType = HeaderReferenceType.DEFAULT): void { private addHeaderToDocument(header: HeaderWrapper, type: HeaderReferenceType = HeaderReferenceType.DEFAULT): void {
this.headers.push({ header, type }); this.headers.push({ header, type });
this.docRelationships.createRelationship( this.documentWrapper.Relationships.createRelationship(
header.Header.ReferenceId, header.View.ReferenceId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
`header${this.headers.length}.xml`, `header${this.headers.length}.xml`,
); );
@ -269,8 +222,8 @@ export class File {
private addFooterToDocument(footer: FooterWrapper, type: FooterReferenceType = FooterReferenceType.DEFAULT): void { private addFooterToDocument(footer: FooterWrapper, type: FooterReferenceType = FooterReferenceType.DEFAULT): void {
this.footers.push({ footer, type }); this.footers.push({ footer, type });
this.docRelationships.createRelationship( this.documentWrapper.Relationships.createRelationship(
footer.Footer.ReferenceId, footer.View.ReferenceId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
`footer${this.footers.length}.xml`, `footer${this.footers.length}.xml`,
); );
@ -299,30 +252,30 @@ export class File {
"docProps/custom.xml", "docProps/custom.xml",
); );
this.docRelationships.createRelationship( this.documentWrapper.Relationships.createRelationship(
this.currentRelationshipId++, this.currentRelationshipId++,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
"styles.xml", "styles.xml",
); );
this.docRelationships.createRelationship( this.documentWrapper.Relationships.createRelationship(
this.currentRelationshipId++, this.currentRelationshipId++,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering",
"numbering.xml", "numbering.xml",
); );
this.docRelationships.createRelationship( this.documentWrapper.Relationships.createRelationship(
this.currentRelationshipId++, this.currentRelationshipId++,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
"footnotes.xml", "footnotes.xml",
); );
this.docRelationships.createRelationship( this.documentWrapper.Relationships.createRelationship(
this.currentRelationshipId++, this.currentRelationshipId++,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings",
"settings.xml", "settings.xml",
); );
} }
public get Document(): Document { public get Document(): DocumentWrapper {
return this.document; return this.documentWrapper;
} }
public get Styles(): Styles { public get Styles(): Styles {
@ -341,10 +294,6 @@ export class File {
return this.media; return this.media;
} }
public get DocumentRelationships(): Relationships {
return this.docRelationships;
}
public get FileRelationships(): Relationships { public get FileRelationships(): Relationships {
return this.fileRelationships; return this.fileRelationships;
} }
@ -376,8 +325,4 @@ export class File {
public get Settings(): Settings { public get Settings(): Settings {
return this.settings; return this.settings;
} }
public get HyperlinkCache(): { readonly [key: string]: Hyperlink } {
return this.hyperlinkCache;
}
} }

View File

@ -10,7 +10,7 @@ describe("FooterWrapper", () => {
describe("#add", () => { describe("#add", () => {
it("should call the underlying footer's addParagraph", () => { it("should call the underlying footer's addParagraph", () => {
const file = new FooterWrapper(new Media(), 1); const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Footer, "add"); const spy = sinon.spy(file.View, "add");
file.add(new Paragraph({})); file.add(new Paragraph({}));
expect(spy.called).to.equal(true); expect(spy.called).to.equal(true);
@ -18,7 +18,7 @@ describe("FooterWrapper", () => {
it("should call the underlying footer's addParagraph", () => { it("should call the underlying footer's addParagraph", () => {
const file = new FooterWrapper(new Media(), 1); const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Footer, "add"); const spy = sinon.spy(file.View, "add");
file.add( file.add(
new Table({ new Table({
rows: [ rows: [
@ -40,7 +40,7 @@ describe("FooterWrapper", () => {
describe("#addChildElement", () => { describe("#addChildElement", () => {
it("should call the underlying footer's addChildElement", () => { it("should call the underlying footer's addChildElement", () => {
const file = new FooterWrapper(new Media(), 1); const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Footer, "addChildElement"); const spy = sinon.spy(file.View, "addChildElement");
// tslint:disable-next-line:no-any // tslint:disable-next-line:no-any
file.addChildElement({} as any); file.addChildElement({} as any);

View File

@ -1,6 +1,7 @@
import { XmlComponent } from "file/xml-components"; import { XmlComponent } from "file/xml-components";
import { FooterReferenceType } from "./document"; import { FooterReferenceType } from "./document";
import { IViewWrapper } from "./document-wrapper";
import { Footer } from "./footer/footer"; import { Footer } from "./footer/footer";
import { Media } from "./media"; import { Media } from "./media";
import { Paragraph } from "./paragraph"; import { Paragraph } from "./paragraph";
@ -12,7 +13,7 @@ export interface IDocumentFooter {
readonly type: FooterReferenceType; readonly type: FooterReferenceType;
} }
export class FooterWrapper { export class FooterWrapper implements IViewWrapper {
private readonly footer: Footer; private readonly footer: Footer;
private readonly relationships: Relationships; private readonly relationships: Relationships;
@ -29,7 +30,7 @@ export class FooterWrapper {
this.footer.addChildElement(childElement); this.footer.addChildElement(childElement);
} }
public get Footer(): Footer { public get View(): Footer {
return this.footer; return this.footer;
} }

View File

@ -10,7 +10,7 @@ describe("HeaderWrapper", () => {
describe("#add", () => { describe("#add", () => {
it("should call the underlying header's addChildElement for Paragraph", () => { it("should call the underlying header's addChildElement for Paragraph", () => {
const wrapper = new HeaderWrapper(new Media(), 1); const wrapper = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(wrapper.Header, "add"); const spy = sinon.spy(wrapper.View, "add");
wrapper.add(new Paragraph({})); wrapper.add(new Paragraph({}));
expect(spy.called).to.equal(true); expect(spy.called).to.equal(true);
@ -18,7 +18,7 @@ describe("HeaderWrapper", () => {
it("should call the underlying header's addChildElement for Table", () => { it("should call the underlying header's addChildElement for Table", () => {
const wrapper = new HeaderWrapper(new Media(), 1); const wrapper = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(wrapper.Header, "add"); const spy = sinon.spy(wrapper.View, "add");
wrapper.add( wrapper.add(
new Table({ new Table({
rows: [ rows: [
@ -40,7 +40,7 @@ describe("HeaderWrapper", () => {
describe("#addChildElement", () => { describe("#addChildElement", () => {
it("should call the underlying header's addChildElement", () => { it("should call the underlying header's addChildElement", () => {
const file = new HeaderWrapper(new Media(), 1); const file = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(file.Header, "addChildElement"); const spy = sinon.spy(file.View, "addChildElement");
// tslint:disable-next-line:no-any // tslint:disable-next-line:no-any
file.addChildElement({} as any); file.addChildElement({} as any);

View File

@ -1,6 +1,7 @@
import { XmlComponent } from "file/xml-components"; import { XmlComponent } from "file/xml-components";
import { HeaderReferenceType } from "./document"; import { HeaderReferenceType } from "./document";
import { IViewWrapper } from "./document-wrapper";
import { Header } from "./header/header"; import { Header } from "./header/header";
import { Media } from "./media"; import { Media } from "./media";
import { Paragraph } from "./paragraph"; import { Paragraph } from "./paragraph";
@ -12,7 +13,7 @@ export interface IDocumentHeader {
readonly type: HeaderReferenceType; readonly type: HeaderReferenceType;
} }
export class HeaderWrapper { export class HeaderWrapper implements IViewWrapper {
private readonly header: Header; private readonly header: Header;
private readonly relationships: Relationships; private readonly relationships: Relationships;
@ -31,7 +32,7 @@ export class HeaderWrapper {
this.header.addChildElement(childElement); this.header.addChildElement(childElement);
} }
public get Header(): Header { public get View(): Header {
return this.header; return this.header;
} }

View File

@ -13,3 +13,4 @@ export * from "./header-wrapper";
export * from "./footer-wrapper"; export * from "./footer-wrapper";
export * from "./header"; export * from "./header";
export * from "./footnotes"; export * from "./footnotes";
export * from "./track-revision";

View File

@ -1,14 +1,12 @@
import { IDrawingOptions } from "../drawing"; import { IDrawingOptions } from "../drawing";
import { File } from "../file"; import { File } from "../file";
import { FooterWrapper } from "../footer-wrapper";
import { HeaderWrapper } from "../header-wrapper";
import { PictureRun } from "../paragraph"; import { PictureRun } from "../paragraph";
import { IMediaData } from "./data"; import { IMediaData } from "./data";
// import { Image } from "./image"; // import { Image } from "./image";
export class Media { export class Media {
public static addImage( public static addImage(
file: File | HeaderWrapper | FooterWrapper, file: File,
buffer: Buffer | string | Uint8Array | ArrayBuffer, buffer: Buffer | string | Uint8Array | ArrayBuffer,
width?: number, width?: number,
height?: number, height?: number,
@ -82,7 +80,7 @@ export class Media {
return imageData; return imageData;
} }
public get Array(): IMediaData[] { public get Array(): readonly IMediaData[] {
const array = new Array<IMediaData>(); const array = new Array<IMediaData>();
this.map.forEach((data) => { this.map.forEach((data) => {

View File

@ -7,7 +7,7 @@ import { AlignmentType, EmphasisMarkType, TabStopPosition } from "../paragraph";
import { UnderlineType } from "../paragraph/run/underline"; import { UnderlineType } from "../paragraph/run/underline";
import { ShadingType } from "../table"; import { ShadingType } from "../table";
import { AbstractNumbering } from "./abstract-numbering"; import { AbstractNumbering } from "./abstract-numbering";
import { LevelSuffix } from "./level"; import { LevelFormat, LevelSuffix } from "./level";
describe("AbstractNumbering", () => { describe("AbstractNumbering", () => {
it("stores its ID at its .id property", () => { it("stores its ID at its .id property", () => {
@ -20,7 +20,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 3, level: 3,
format: "lowerLetter", format: LevelFormat.LOWER_LETTER,
text: "%1)", text: "%1)",
alignment: AlignmentType.END, alignment: AlignmentType.END,
}, },
@ -29,7 +29,7 @@ describe("AbstractNumbering", () => {
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "end" } } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "end" } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": "lowerLetter" } } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": LevelFormat.LOWER_LETTER } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } });
}); });
@ -37,7 +37,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 3, level: 3,
format: "lowerLetter", format: LevelFormat.LOWER_LETTER,
text: "%1)", text: "%1)",
}, },
]); ]);
@ -45,7 +45,7 @@ describe("AbstractNumbering", () => {
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "start" } } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "start" } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": "lowerLetter" } } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": LevelFormat.LOWER_LETTER } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } });
}); });
@ -53,7 +53,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 3, level: 3,
format: "lowerLetter", format: LevelFormat.LOWER_LETTER,
text: "%1)", text: "%1)",
alignment: AlignmentType.END, alignment: AlignmentType.END,
suffix: LevelSuffix.SPACE, suffix: LevelSuffix.SPACE,
@ -68,7 +68,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
paragraph: { paragraph: {
@ -87,7 +87,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
paragraph: { paragraph: {
@ -106,7 +106,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
paragraph: { paragraph: {
@ -125,7 +125,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
paragraph: { paragraph: {
@ -144,7 +144,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
paragraph: { paragraph: {
@ -163,7 +163,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
paragraph: { paragraph: {
@ -182,7 +182,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
paragraph: { paragraph: {
@ -216,7 +216,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
paragraph: { paragraph: {
@ -239,7 +239,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
paragraph: { paragraph: {
@ -262,7 +262,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
paragraph: { paragraph: {
@ -281,7 +281,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
paragraph: { paragraph: {
@ -324,7 +324,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { size, sizeComplexScript }, run: { size, sizeComplexScript },
@ -340,7 +340,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -359,7 +359,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -378,7 +378,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -398,7 +398,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -417,7 +417,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -436,7 +436,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -455,7 +455,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -485,7 +485,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -533,7 +533,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { bold, boldComplexScript }, run: { bold, boldComplexScript },
@ -566,7 +566,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { italics, italicsComplexScript }, run: { italics, italicsComplexScript },
@ -604,7 +604,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { highlight, highlightComplexScript }, run: { highlight, highlightComplexScript },
@ -682,7 +682,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { shadow, shading, shadingComplexScript }, run: { shadow, shading, shadingComplexScript },
@ -699,7 +699,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -718,7 +718,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -739,7 +739,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -763,7 +763,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -782,7 +782,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {
@ -804,7 +804,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
format: "lowerRoman", format: LevelFormat.LOWER_ROMAN,
text: "%0.", text: "%0.",
style: { style: {
run: { run: {

View File

@ -1,8 +1,26 @@
// http://officeopenxml.com/WPnumbering-numFmt.php
import { Attributes, XmlAttributeComponent, XmlComponent } from "file/xml-components"; import { Attributes, XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { AlignmentType } from "../paragraph/formatting"; import { AlignmentType } from "../paragraph/formatting";
import { IParagraphStylePropertiesOptions, ParagraphProperties } from "../paragraph/properties"; import { IParagraphStylePropertiesOptions, ParagraphProperties } from "../paragraph/properties";
import { IRunStylePropertiesOptions, RunProperties } from "../paragraph/run/properties"; import { IRunStylePropertiesOptions, RunProperties } from "../paragraph/run/properties";
export enum LevelFormat {
BULLET = "bullet",
CARDINAL_TEXT = "cardinalText",
CHICAGO = "chicago",
DECIMAL = "decimal",
DECIMAL_ENCLOSED_CIRCLE = "decimalEnclosedCircle",
DECIMAL_ENCLOSED_FULLSTOP = "decimalEnclosedFullstop",
DECIMAL_ENCLOSED_PARENTHESES = "decimalEnclosedParen",
DECIMAL_ZERO = "decimalZero",
LOWER_LETTER = "lowerLetter",
LOWER_ROMAN = "lowerRoman",
NONE = "none",
ORDINAL_TEXT = "ordinalText",
UPPER_LETTER = "upperLetter",
UPPER_ROMAN = "upperRoman",
}
interface ILevelAttributesProperties { interface ILevelAttributesProperties {
readonly ilvl?: number; readonly ilvl?: number;
readonly tentative?: number; readonly tentative?: number;
@ -67,7 +85,7 @@ export enum LevelSuffix {
export interface ILevelsOptions { export interface ILevelsOptions {
readonly level: number; readonly level: number;
readonly format?: string; readonly format?: LevelFormat;
readonly text?: string; readonly text?: string;
readonly alignment?: AlignmentType; readonly alignment?: AlignmentType;
readonly start?: number; readonly start?: number;

View File

@ -1,10 +1,11 @@
// http://officeopenxml.com/WPnumbering.php // http://officeopenxml.com/WPnumbering.php
import { convertInchesToTwip } from "convenience-functions";
import { AlignmentType } from "file/paragraph"; 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 } from "./abstract-numbering";
import { ILevelsOptions } from "./level"; import { ILevelsOptions, LevelFormat } from "./level";
import { ConcreteNumbering } from "./num"; import { ConcreteNumbering } from "./num";
export interface INumberingOptions { export interface INumberingOptions {
@ -50,100 +51,100 @@ export class Numbering extends XmlComponent {
const abstractNumbering = this.createAbstractNumbering([ const abstractNumbering = this.createAbstractNumbering([
{ {
level: 0, level: 0,
format: "bullet", format: LevelFormat.BULLET,
text: "\u25CF", text: "\u25CF",
alignment: AlignmentType.LEFT, alignment: AlignmentType.LEFT,
style: { style: {
paragraph: { paragraph: {
indent: { left: 720, hanging: 360 }, indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.25) },
}, },
}, },
}, },
{ {
level: 1, level: 1,
format: "bullet", format: LevelFormat.BULLET,
text: "\u25CB", text: "\u25CB",
alignment: AlignmentType.LEFT, alignment: AlignmentType.LEFT,
style: { style: {
paragraph: { paragraph: {
indent: { left: 1440, hanging: 360 }, indent: { left: convertInchesToTwip(1), hanging: convertInchesToTwip(0.25) },
}, },
}, },
}, },
{ {
level: 2, level: 2,
format: "bullet", format: LevelFormat.BULLET,
text: "\u25A0", text: "\u25A0",
alignment: AlignmentType.LEFT, alignment: AlignmentType.LEFT,
style: { style: {
paragraph: { paragraph: {
indent: { left: 2160, hanging: 360 }, indent: { left: 2160, hanging: convertInchesToTwip(0.25) },
}, },
}, },
}, },
{ {
level: 3, level: 3,
format: "bullet", format: LevelFormat.BULLET,
text: "\u25CF", text: "\u25CF",
alignment: AlignmentType.LEFT, alignment: AlignmentType.LEFT,
style: { style: {
paragraph: { paragraph: {
indent: { left: 2880, hanging: 360 }, indent: { left: 2880, hanging: convertInchesToTwip(0.25) },
}, },
}, },
}, },
{ {
level: 4, level: 4,
format: "bullet", format: LevelFormat.BULLET,
text: "\u25CB", text: "\u25CB",
alignment: AlignmentType.LEFT, alignment: AlignmentType.LEFT,
style: { style: {
paragraph: { paragraph: {
indent: { left: 3600, hanging: 360 }, indent: { left: 3600, hanging: convertInchesToTwip(0.25) },
}, },
}, },
}, },
{ {
level: 5, level: 5,
format: "bullet", format: LevelFormat.BULLET,
text: "\u25A0", text: "\u25A0",
alignment: AlignmentType.LEFT, alignment: AlignmentType.LEFT,
style: { style: {
paragraph: { paragraph: {
indent: { left: 4320, hanging: 360 }, indent: { left: 4320, hanging: convertInchesToTwip(0.25) },
}, },
}, },
}, },
{ {
level: 6, level: 6,
format: "bullet", format: LevelFormat.BULLET,
text: "\u25CF", text: "\u25CF",
alignment: AlignmentType.LEFT, alignment: AlignmentType.LEFT,
style: { style: {
paragraph: { paragraph: {
indent: { left: 5040, hanging: 360 }, indent: { left: 5040, hanging: convertInchesToTwip(0.25) },
}, },
}, },
}, },
{ {
level: 7, level: 7,
format: "bullet", format: LevelFormat.BULLET,
text: "\u25CF", text: "\u25CF",
alignment: AlignmentType.LEFT, alignment: AlignmentType.LEFT,
style: { style: {
paragraph: { paragraph: {
indent: { left: 5760, hanging: 360 }, indent: { left: 5760, hanging: convertInchesToTwip(0.25) },
}, },
}, },
}, },
{ {
level: 8, level: 8,
format: "bullet", format: LevelFormat.BULLET,
text: "\u25CF", text: "\u25CF",
alignment: AlignmentType.LEFT, alignment: AlignmentType.LEFT,
style: { style: {
paragraph: { paragraph: {
indent: { left: 6480, hanging: 360 }, indent: { left: 6480, hanging: convertInchesToTwip(0.25) },
}, },
}, },
}, },

View File

@ -3,3 +3,4 @@ export * from "./paragraph";
export * from "./properties"; export * from "./properties";
export * from "./run"; export * from "./run";
export * from "./links"; export * from "./links";
export * from "./math";

View File

@ -2,14 +2,20 @@ import { expect } from "chai";
import { Formatter } from "export/formatter"; import { Formatter } from "export/formatter";
import { Hyperlink } from "./"; import { TextRun } from "../run";
import { HyperlinkRef } from "./hyperlink"; import { ConcreteHyperlink, ExternalHyperlink, InternalHyperlink } from "./hyperlink";
describe("Hyperlink", () => { describe("ConcreteHyperlink", () => {
let hyperlink: Hyperlink; let hyperlink: ConcreteHyperlink;
beforeEach(() => { beforeEach(() => {
hyperlink = new Hyperlink("https://example.com", "superid"); hyperlink = new ConcreteHyperlink(
new TextRun({
text: "https://example.com",
style: "Hyperlink",
}),
"superid",
);
}); });
describe("#constructor()", () => { describe("#constructor()", () => {
@ -35,7 +41,14 @@ describe("Hyperlink", () => {
describe("with optional anchor parameter", () => { describe("with optional anchor parameter", () => {
beforeEach(() => { beforeEach(() => {
hyperlink = new Hyperlink("Anchor Text", "superid2", "anchor"); hyperlink = new ConcreteHyperlink(
new TextRun({
text: "Anchor Text",
style: "Hyperlink",
}),
"superid2",
"anchor",
);
}); });
it("should create an internal link with anchor tag", () => { it("should create an internal link with anchor tag", () => {
@ -61,10 +74,53 @@ describe("Hyperlink", () => {
}); });
}); });
describe("HyperlinkRef", () => { describe("ExternalHyperlink", () => {
describe("#constructor()", () => { describe("#constructor()", () => {
const hyperlinkRef = new HyperlinkRef("test-id"); it("should create", () => {
const externalHyperlink = new ExternalHyperlink({
child: new TextRun("test"),
link: "http://www.google.com",
});
expect(hyperlinkRef.id).to.equal("test-id"); expect(externalHyperlink.options.link).to.equal("http://www.google.com");
});
});
});
describe("InternalHyperlink", () => {
describe("#constructor()", () => {
it("should create", () => {
const internalHyperlink = new InternalHyperlink({
child: new TextRun("test"),
anchor: "test-id",
});
const tree = new Formatter().format(internalHyperlink);
expect(tree).to.deep.equal({
"w:hyperlink": [
{
_attr: {
"w:anchor": "test-id",
"w:history": 1,
},
},
{
"w:r": [
{
"w:t": [
{
_attr: {
"xml:space": "preserve",
},
},
"test",
],
},
],
},
],
});
});
}); });
}); });

View File

@ -1,6 +1,9 @@
// http://officeopenxml.com/WPhyperlink.php // http://officeopenxml.com/WPhyperlink.php
import * as shortid from "shortid";
import { XmlComponent } from "file/xml-components"; import { XmlComponent } from "file/xml-components";
import { TextRun } from "../run";
import { ParagraphChild } from "../paragraph";
import { HyperlinkAttributes, IHyperlinkAttributesProperties } from "./hyperlink-attributes"; import { HyperlinkAttributes, IHyperlinkAttributesProperties } from "./hyperlink-attributes";
export enum HyperlinkType { export enum HyperlinkType {
@ -8,15 +11,10 @@ export enum HyperlinkType {
EXTERNAL = "EXTERNAL", EXTERNAL = "EXTERNAL",
} }
export class HyperlinkRef { export class ConcreteHyperlink extends XmlComponent {
constructor(public readonly id: string) {}
}
export class Hyperlink extends XmlComponent {
public readonly linkId: string; public readonly linkId: string;
private readonly textRun: TextRun;
constructor(text: string, relationshipId: string, anchor?: string) { constructor(child: ParagraphChild, relationshipId: string, anchor?: string) {
super("w:hyperlink"); super("w:hyperlink");
this.linkId = relationshipId; this.linkId = relationshipId;
@ -29,14 +27,16 @@ export class Hyperlink extends XmlComponent {
const attributes = new HyperlinkAttributes(props); const attributes = new HyperlinkAttributes(props);
this.root.push(attributes); this.root.push(attributes);
this.textRun = new TextRun({ this.root.push(child);
text: text,
style: "Hyperlink",
});
this.root.push(this.textRun);
}
public get TextRun(): TextRun {
return this.textRun;
} }
} }
export class InternalHyperlink extends ConcreteHyperlink {
constructor(options: { readonly child: ParagraphChild; readonly anchor: string }) {
super(options.child, shortid.generate().toLowerCase(), options.anchor);
}
}
export class ExternalHyperlink {
constructor(public readonly options: { readonly child: ParagraphChild; readonly link: string }) {}
}

View File

@ -0,0 +1,4 @@
export * from "./math-round-brackets";
export * from "./math-square-brackets";
export * from "./math-curly-brackets";
export * from "./math-angled-brackets";

View File

@ -0,0 +1,51 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathAngledBrackets } from "./math-angled-brackets";
describe("MathAngledBrackets", () => {
describe("#constructor()", () => {
it("should create a MathAngledBrackets with correct root key", () => {
const mathAngledBrackets = new MathAngledBrackets({
children: [new MathRun("60")],
});
const tree = new Formatter().format(mathAngledBrackets);
expect(tree).to.deep.equal({
"m:d": [
{
"m:dPr": [
{
"m:begChr": {
_attr: {
"m:val": "〈",
},
},
},
{
"m:endChr": {
_attr: {
"m:val": "〉",
},
},
},
],
},
{
"m:e": [
{
"m:r": [
{
"m:t": ["60"],
},
],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,20 @@
// http://www.datypic.com/sc/ooxml/e-m_d-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
import { MathBase } from "../n-ary";
import { MathBracketProperties } from "./math-bracket-properties";
export class MathAngledBrackets extends XmlComponent {
constructor(options: { readonly children: MathComponent[] }) {
super("m:d");
this.root.push(
new MathBracketProperties({
beginningCharacter: "〈",
endingCharacter: "〉",
}),
);
this.root.push(new MathBase(options.children));
}
}

View File

@ -0,0 +1,22 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathBeginningCharacter } from "./math-beginning-character";
describe("MathBeginningCharacter", () => {
describe("#constructor()", () => {
it("should create a MathBeginningCharacter with correct root key", () => {
const mathBeginningCharacter = new MathBeginningCharacter("[");
const tree = new Formatter().format(mathBeginningCharacter);
expect(tree).to.deep.equal({
"m:begChr": {
_attr: {
"m:val": "[",
},
},
});
});
});
});

View File

@ -0,0 +1,14 @@
// http://www.datypic.com/sc/ooxml/e-m_begChr-1.html
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
class MathBeginningCharacterAttributes extends XmlAttributeComponent<{ readonly character: string }> {
protected readonly xmlKeys = { character: "m:val" };
}
export class MathBeginningCharacter extends XmlComponent {
constructor(character: string) {
super("m:begChr");
this.root.push(new MathBeginningCharacterAttributes({ character }));
}
}

View File

@ -0,0 +1,45 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathBracketProperties } from "./math-bracket-properties";
describe("MathBracketProperties", () => {
describe("#constructor()", () => {
it("should create a MathBracketProperties with correct root key", () => {
const mathBracketProperties = new MathBracketProperties();
const tree = new Formatter().format(mathBracketProperties);
expect(tree).to.deep.equal({
"m:dPr": {},
});
});
it("should create a MathBracketProperties with correct root key and add brackets", () => {
const mathBracketProperties = new MathBracketProperties({
beginningCharacter: "[",
endingCharacter: "]",
});
const tree = new Formatter().format(mathBracketProperties);
expect(tree).to.deep.equal({
"m:dPr": [
{
"m:begChr": {
_attr: {
"m:val": "[",
},
},
},
{
"m:endChr": {
_attr: {
"m:val": "]",
},
},
},
],
});
});
});
});

View File

@ -0,0 +1,16 @@
// http://www.datypic.com/sc/ooxml/e-m_dPr-1.html
import { XmlComponent } from "file/xml-components";
import { MathBeginningCharacter } from "./math-beginning-character";
import { MathEndingCharacter } from "./math-ending-char";
export class MathBracketProperties extends XmlComponent {
constructor(options?: { readonly beginningCharacter: string; readonly endingCharacter: string }) {
super("m:dPr");
if (!!options) {
this.root.push(new MathBeginningCharacter(options.beginningCharacter));
this.root.push(new MathEndingCharacter(options.endingCharacter));
}
}
}

View File

@ -0,0 +1,51 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathCurlyBrackets } from "./math-curly-brackets";
describe("MathCurlyBrackets", () => {
describe("#constructor()", () => {
it("should create a MathCurlyBrackets with correct root key", () => {
const mathCurlyBrackets = new MathCurlyBrackets({
children: [new MathRun("60")],
});
const tree = new Formatter().format(mathCurlyBrackets);
expect(tree).to.deep.equal({
"m:d": [
{
"m:dPr": [
{
"m:begChr": {
_attr: {
"m:val": "{",
},
},
},
{
"m:endChr": {
_attr: {
"m:val": "}",
},
},
},
],
},
{
"m:e": [
{
"m:r": [
{
"m:t": ["60"],
},
],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,20 @@
// http://www.datypic.com/sc/ooxml/e-m_d-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
import { MathBase } from "../n-ary";
import { MathBracketProperties } from "./math-bracket-properties";
export class MathCurlyBrackets extends XmlComponent {
constructor(options: { readonly children: MathComponent[] }) {
super("m:d");
this.root.push(
new MathBracketProperties({
beginningCharacter: "{",
endingCharacter: "}",
}),
);
this.root.push(new MathBase(options.children));
}
}

View File

@ -0,0 +1,14 @@
// http://www.datypic.com/sc/ooxml/e-m_endChr-1.html
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
class MathEndingCharacterAttributes extends XmlAttributeComponent<{ readonly character: string }> {
protected readonly xmlKeys = { character: "m:val" };
}
export class MathEndingCharacter extends XmlComponent {
constructor(character: string) {
super("m:endChr");
this.root.push(new MathEndingCharacterAttributes({ character }));
}
}

View File

@ -0,0 +1,22 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathEndingCharacter } from "./math-ending-char";
describe("MathEndingCharacter", () => {
describe("#constructor()", () => {
it("should create a MathEndingCharacter with correct root key", () => {
const mathEndingCharacter = new MathEndingCharacter("]");
const tree = new Formatter().format(mathEndingCharacter);
expect(tree).to.deep.equal({
"m:endChr": {
_attr: {
"m:val": "]",
},
},
});
});
});
});

View File

@ -0,0 +1,36 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathRoundBrackets } from "./math-round-brackets";
describe("MathRoundBrackets", () => {
describe("#constructor()", () => {
it("should create a MathRoundBrackets with correct root key", () => {
const mathRoundBrackets = new MathRoundBrackets({
children: [new MathRun("60")],
});
const tree = new Formatter().format(mathRoundBrackets);
expect(tree).to.deep.equal({
"m:d": [
{
"m:dPr": {},
},
{
"m:e": [
{
"m:r": [
{
"m:t": ["60"],
},
],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,15 @@
// http://www.datypic.com/sc/ooxml/e-m_d-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
import { MathBase } from "../n-ary";
import { MathBracketProperties } from "./math-bracket-properties";
export class MathRoundBrackets extends XmlComponent {
constructor(options: { readonly children: MathComponent[] }) {
super("m:d");
this.root.push(new MathBracketProperties());
this.root.push(new MathBase(options.children));
}
}

View File

@ -0,0 +1,51 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathSquareBrackets } from "./math-square-brackets";
describe("MathSquareBrackets", () => {
describe("#constructor()", () => {
it("should create a MathSquareBrackets with correct root key", () => {
const mathSquareBrackets = new MathSquareBrackets({
children: [new MathRun("60")],
});
const tree = new Formatter().format(mathSquareBrackets);
expect(tree).to.deep.equal({
"m:d": [
{
"m:dPr": [
{
"m:begChr": {
_attr: {
"m:val": "[",
},
},
},
{
"m:endChr": {
_attr: {
"m:val": "]",
},
},
},
],
},
{
"m:e": [
{
"m:r": [
{
"m:t": ["60"],
},
],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,20 @@
// http://www.datypic.com/sc/ooxml/e-m_d-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
import { MathBase } from "../n-ary";
import { MathBracketProperties } from "./math-bracket-properties";
export class MathSquareBrackets extends XmlComponent {
constructor(options: { readonly children: MathComponent[] }) {
super("m:d");
this.root.push(
new MathBracketProperties({
beginningCharacter: "[",
endingCharacter: "]",
}),
);
this.root.push(new MathBase(options.children));
}
}

View File

@ -0,0 +1,3 @@
export * from "./math-fraction";
export * from "./math-denominator";
export * from "./math-numerator";

View File

@ -0,0 +1,26 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathDenominator } from "./math-denominator";
describe("MathDenominator", () => {
describe("#constructor()", () => {
it("should create a MathDenominator with correct root key", () => {
const mathDenominator = new MathDenominator([new MathRun("2+2")]);
const tree = new Formatter().format(mathDenominator);
expect(tree).to.deep.equal({
"m:den": [
{
"m:r": [
{
"m:t": ["2+2"],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,13 @@
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
export class MathDenominator extends XmlComponent {
constructor(children: MathComponent[]) {
super("m:den");
for (const child of children) {
this.root.push(child);
}
}
}

View File

@ -0,0 +1,44 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathFraction } from "./math-fraction";
describe("MathFraction", () => {
describe("#constructor()", () => {
it("should create a MathFraction with correct root key", () => {
const mathFraction = new MathFraction({
numerator: [new MathRun("2")],
denominator: [new MathRun("2")],
});
const tree = new Formatter().format(mathFraction);
expect(tree).to.deep.equal({
"m:f": [
{
"m:num": [
{
"m:r": [
{
"m:t": ["2"],
},
],
},
],
},
{
"m:den": [
{
"m:r": [
{
"m:t": ["2"],
},
],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,19 @@
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
import { MathDenominator } from "./math-denominator";
import { MathNumerator } from "./math-numerator";
export interface IMathFractionOptions {
readonly numerator: MathComponent[];
readonly denominator: MathComponent[];
}
export class MathFraction extends XmlComponent {
constructor(options: IMathFractionOptions) {
super("m:f");
this.root.push(new MathNumerator(options.numerator));
this.root.push(new MathDenominator(options.denominator));
}
}

View File

@ -0,0 +1,26 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathNumerator } from "./math-numerator";
describe("MathNumerator", () => {
describe("#constructor()", () => {
it("should create a MathNumerator with correct root key", () => {
const mathNumerator = new MathNumerator([new MathRun("2+2")]);
const tree = new Formatter().format(mathNumerator);
expect(tree).to.deep.equal({
"m:num": [
{
"m:r": [
{
"m:t": ["2+2"],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,13 @@
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
export class MathNumerator extends XmlComponent {
constructor(children: MathComponent[]) {
super("m:num");
for (const child of children) {
this.root.push(child);
}
}
}

View File

@ -0,0 +1,3 @@
export * from "./math-function";
export * from "./math-function-name";
export * from "./math-function-properties";

Some files were not shown because too many files have changed in this diff Show More