Compare commits

..

68 Commits
5.2.1 ... 5.3.0

Author SHA1 Message Date
a6eb8e01df Merge pull request #604 from dolanmiu/feat/prettier-update
Update tslint to v6
2020-08-01 18:04:47 +01:00
8c45c30753 Merge pull request #598 from dolanmiu/dependabot/npm_and_yarn/rimraf-3.0.2
build(deps-dev): bump rimraf from 2.6.3 to 3.0.2
2020-08-01 17:58:31 +01:00
d18cfbc26f Update tslint to v6 2020-08-01 17:58:16 +01:00
f4af5f9e33 Merge pull request #603 from dolanmiu/feat/prettier-update
Update prettier
2020-08-01 17:55:38 +01:00
69adbbc1c1 Bump node version 2020-08-01 17:49:25 +01:00
4f6a9f734c Update prettier 2020-08-01 17:40:57 +01:00
8b78f2d200 Version bump 2020-08-01 17:28:11 +01:00
26a3fa6946 Merge pull request #596 from SpringMT/master
feat: Add decimalFullWidth numbering type
2020-08-01 17:25:12 +01:00
f955a18936 build(deps-dev): bump rimraf from 2.6.3 to 3.0.2
Bumps [rimraf](https://github.com/isaacs/rimraf) from 2.6.3 to 3.0.2.
- [Release notes](https://github.com/isaacs/rimraf/releases)
- [Changelog](https://github.com/isaacs/rimraf/blob/master/CHANGELOG.md)
- [Commits](https://github.com/isaacs/rimraf/compare/v2.6.3...v3.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-01 01:59:37 +00:00
ee1a7818b6 Merge pull request #600 from dolanmiu/dependabot/npm_and_yarn/ts-node-8.10.2
build(deps-dev): bump ts-node from 7.0.1 to 8.10.2
2020-08-01 02:57:50 +01:00
8e455f1097 Merge pull request #601 from dolanmiu/dependabot/npm_and_yarn/nyc-15.1.0
build(deps-dev): bump nyc from 14.1.1 to 15.1.0
2020-08-01 02:56:06 +01:00
827c46cf47 build(deps-dev): bump nyc from 14.1.1 to 15.1.0
Bumps [nyc](https://github.com/istanbuljs/nyc) from 14.1.1 to 15.1.0.
- [Release notes](https://github.com/istanbuljs/nyc/releases)
- [Changelog](https://github.com/istanbuljs/nyc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/istanbuljs/nyc/compare/v14.1.1...v15.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-31 09:08:10 +00:00
61cbee829d build(deps-dev): bump ts-node from 7.0.1 to 8.10.2
Bumps [ts-node](https://github.com/TypeStrong/ts-node) from 7.0.1 to 8.10.2.
- [Release notes](https://github.com/TypeStrong/ts-node/releases)
- [Commits](https://github.com/TypeStrong/ts-node/compare/v7.0.1...v8.10.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-31 09:06:34 +00:00
f0ad1e9194 feat: Add decimalFullWidth numbering type 2020-07-31 16:36:56 +09:00
bbd339f0e4 Merge pull request #571 from dolanmiu/dependabot/npm_and_yarn/sinon-9.0.2
Bump sinon from 5.1.1 to 9.0.2
2020-07-30 13:23:18 +01:00
858af64dc3 Improve coverage threshold 2020-07-30 13:07:48 +01:00
01e34c1b28 Bump sinon from 5.1.1 to 9.0.2
Bumps [sinon](https://github.com/sinonjs/sinon) from 5.1.1 to 9.0.2.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v5.1.1...v9.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-30 10:48:07 +00:00
41acb475a9 Update documentation 2020-07-30 11:42:55 +01:00
20978f8c24 Merge pull request #578 from dolanmiu/dependabot/npm_and_yarn/types/request-promise-4.1.46
Bump @types/request-promise from 4.1.44 to 4.1.46
2020-07-30 10:09:50 +01:00
265a3dbe21 Merge pull request #592 from dolanmiu/dependabot/npm_and_yarn/types/jszip-3.4.1
Bump @types/jszip from 3.1.6 to 3.4.1
2020-07-30 10:09:21 +01:00
627f5b4bf0 Merge pull request #593 from dolanmiu/dependabot/npm_and_yarn/types/node-14.0.27
Bump @types/node from 14.0.23 to 14.0.27
2020-07-30 10:08:55 +01:00
1538c1aaeb Merge pull request #594 from dolanmiu/dependabot/npm_and_yarn/types/webpack-4.41.21
Bump @types/webpack from 4.4.34 to 4.41.21
2020-07-30 10:08:32 +01:00
839661e5f8 Bump @types/webpack from 4.4.34 to 4.41.21
Bumps [@types/webpack](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/webpack) from 4.4.34 to 4.41.21.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/webpack)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-30 08:50:39 +00:00
fff6244597 Bump @types/node from 14.0.23 to 14.0.27
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.0.23 to 14.0.27.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-30 08:49:24 +00:00
b2280f64a1 Bump @types/jszip from 3.1.6 to 3.4.1
Bumps [@types/jszip](https://github.com/Stuk/jszip) from 3.1.6 to 3.4.1.
- [Release notes](https://github.com/Stuk/jszip/releases)
- [Changelog](https://github.com/Stuk/jszip/blob/master/CHANGES.md)
- [Commits](https://github.com/Stuk/jszip/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-30 08:48:47 +00:00
21d53c41d0 Bump @types/request-promise from 4.1.44 to 4.1.46
Bumps [@types/request-promise](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/request-promise) from 4.1.44 to 4.1.46.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/request-promise)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-30 01:44:26 +00:00
6e94bbb5e5 Merge pull request #588 from dolanmiu/dependabot/npm_and_yarn/typedoc-0.16.11
Bump typedoc from 0.11.1 to 0.16.11
2020-07-30 02:41:09 +01:00
7d655cd3f7 Merge pull request #589 from dolanmiu/dependabot/npm_and_yarn/shelljs-0.8.4
Bump shelljs from 0.7.8 to 0.8.4
2020-07-30 02:40:43 +01:00
00821677c8 Merge pull request #591 from dolanmiu/dependabot/npm_and_yarn/elliptic-6.5.3
[Security] Bump elliptic from 6.5.0 to 6.5.3
2020-07-30 02:40:28 +01:00
784de3e430 [Security] Bump elliptic from 6.5.0 to 6.5.3
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.0 to 6.5.3. **This update includes a security fix.**
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.0...v6.5.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-29 21:32:19 +00:00
3213c4838d Bump shelljs from 0.7.8 to 0.8.4
Bumps [shelljs](https://github.com/shelljs/shelljs) from 0.7.8 to 0.8.4.
- [Release notes](https://github.com/shelljs/shelljs/releases)
- [Changelog](https://github.com/shelljs/shelljs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/shelljs/shelljs/compare/v0.7.8...v0.8.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-28 08:43:57 +00:00
1b9bc8eb1d Bump typedoc from 0.11.1 to 0.16.11
Bumps [typedoc](https://github.com/TypeStrong/TypeDoc) from 0.11.1 to 0.16.11.
- [Release notes](https://github.com/TypeStrong/TypeDoc/releases)
- [Commits](https://github.com/TypeStrong/TypeDoc/compare/v0.11.1...v0.16.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-28 08:43:10 +00:00
bf58d0b864 Merge pull request #581 from dolanmiu/dependabot/npm_and_yarn/docsify-cli-4.4.1
Bump docsify-cli from 4.3.0 to 4.4.1
2020-07-27 19:38:06 +01:00
28ca8392ed Merge pull request #577 from dolanmiu/dependabot/npm_and_yarn/request-2.88.2
Bump request from 2.88.0 to 2.88.2
2020-07-27 19:37:53 +01:00
5238c55bc2 Bump docsify-cli from 4.3.0 to 4.4.1
Bumps [docsify-cli](https://github.com/QingWei-Li/docsify-cli) from 4.3.0 to 4.4.1.
- [Release notes](https://github.com/QingWei-Li/docsify-cli/releases)
- [Changelog](https://github.com/docsifyjs/docsify-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/QingWei-Li/docsify-cli/compare/v4.3.0...v4.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 09:10:52 +00:00
901f10c387 Bump request from 2.88.0 to 2.88.2
Bumps [request](https://github.com/request/request) from 2.88.0 to 2.88.2.
- [Release notes](https://github.com/request/request/releases)
- [Changelog](https://github.com/request/request/blob/master/CHANGELOG.md)
- [Commits](https://github.com/request/request/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 09:10:49 +00:00
defa22ffe5 Merge pull request #585 from dolanmiu/dependabot/npm_and_yarn/request-promise-4.2.6
Bump request-promise from 4.2.4 to 4.2.6
2020-07-27 10:08:50 +01:00
84919c0cc0 Update README.md 2020-07-27 09:56:30 +01:00
1e8ca123b0 Bump request-promise from 4.2.4 to 4.2.6
Bumps [request-promise](https://github.com/request/request-promise) from 4.2.4 to 4.2.6.
- [Release notes](https://github.com/request/request-promise/releases)
- [Commits](https://github.com/request/request-promise/compare/v4.2.4...v4.2.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-22 08:57:50 +00:00
3ae7c6aedf Merge pull request #568 from dolanmiu/dependabot/npm_and_yarn/jszip-3.5.0
Bump jszip from 3.2.2 to 3.5.0
2020-07-18 03:00:56 +01:00
6a3ed4bbf8 Merge pull request #567 from dolanmiu/dependabot/npm_and_yarn/handlebars-4.7.6
[Security] Bump handlebars from 4.5.3 to 4.7.6
2020-07-18 02:56:33 +01:00
e632d323c9 Merge pull request #570 from dolanmiu/dependabot/npm_and_yarn/types/mocha-8.0.0
Bump @types/mocha from 2.2.48 to 8.0.0
2020-07-18 02:56:09 +01:00
d2b35ab8f2 Merge pull request #566 from dolanmiu/dependabot/npm_and_yarn/types/node-14.0.23
Bump @types/node from 14.0.5 to 14.0.23
2020-07-18 02:55:32 +01:00
e60f39df41 Merge pull request #572 from dolanmiu/dependabot/npm_and_yarn/lodash-4.17.19
[Security] Bump lodash from 4.17.15 to 4.17.19
2020-07-18 02:55:09 +01:00
076431e04d Merge pull request #574 from dolanmiu/dependabot/npm_and_yarn/types/sinon-9.0.4
Bump @types/sinon from 4.3.3 to 9.0.4
2020-07-18 02:54:47 +01:00
75ab44403c Merge pull request #575 from dolanmiu/dependabot/npm_and_yarn/glob-7.1.6
Bump glob from 7.1.4 to 7.1.6
2020-07-18 02:54:28 +01:00
e2d6097819 Bump glob from 7.1.4 to 7.1.6
Bumps [glob](https://github.com/isaacs/node-glob) from 7.1.4 to 7.1.6.
- [Release notes](https://github.com/isaacs/node-glob/releases)
- [Changelog](https://github.com/isaacs/node-glob/blob/master/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v7.1.4...v7.1.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-17 08:43:39 +00:00
ab12ff1257 Bump @types/sinon from 4.3.3 to 9.0.4
Bumps [@types/sinon](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/sinon) from 4.3.3 to 9.0.4.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/sinon)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-17 08:42:58 +00:00
25a0212f4e [Security] Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19. **This update includes a security fix.**
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-17 08:41:22 +00:00
a8993f14d6 Bump @types/mocha from 2.2.48 to 8.0.0
Bumps [@types/mocha](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/mocha) from 2.2.48 to 8.0.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/mocha)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-16 20:26:36 +00:00
1834cd86da Bump jszip from 3.2.2 to 3.5.0
Bumps [jszip](https://github.com/Stuk/jszip) from 3.2.2 to 3.5.0.
- [Release notes](https://github.com/Stuk/jszip/releases)
- [Changelog](https://github.com/Stuk/jszip/blob/master/CHANGES.md)
- [Commits](https://github.com/Stuk/jszip/compare/v3.2.2...v3.5.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-16 20:25:38 +00:00
437de27ed8 [Security] Bump handlebars from 4.5.3 to 4.7.6
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.5.3 to 4.7.6. **This update includes a security fix.**
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.5.3...v4.7.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-16 20:25:17 +00:00
96f08482da Bump @types/node from 14.0.5 to 14.0.23
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.0.5 to 14.0.23.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-16 20:24:56 +00:00
edec2eca7a Add another React example 2020-07-16 16:08:50 +01:00
9202524d83 Fix Vue.js stylistics 2020-07-16 14:15:47 +01:00
0461907533 Add VueJS example 2020-07-16 14:13:55 +01:00
445a2896d2 Add React example 2020-07-16 12:35:47 +01:00
e2d8f1b6b1 Merge pull request #562 from wangfengming/master
Feature: refine paragraph/run properties options
2020-07-15 10:30:24 +01:00
7baa696a76 :feat: coverage ignore file src/import-dotx/import-dotx.ts 2020-07-15 14:11:59 +08:00
3f7ca6bbff :test: document defaults & numbering suffix 2020-07-15 14:11:26 +08:00
36e1c5fe6a :feat: refined defaults 2020-07-15 14:10:37 +08:00
75a03f1576 :test: update test for complex script 2020-07-15 12:46:46 +08:00
437e83ab78 :feat: refine paragraph/run properties options 2020-07-14 15:42:45 +08:00
b8232f7a02 Version bump 2020-07-11 19:34:29 +01:00
49eadb0efc Merge pull request #559 from wangfengming/master
:fix: `rowSpan` can't work when column index out of range
2020-07-09 01:29:06 +01:00
40dc90e585 :fix: insert the continue cell properly 2020-07-08 12:32:01 +08:00
0de302d192 :typo: update comments 2020-07-08 11:26:24 +08:00
80bab95f6c :fix: rowSpan can't work when column index out of range 2020-07-08 10:55:15 +08:00
52 changed files with 3186 additions and 2896 deletions

2
.nvmrc
View File

@ -1 +1 @@
v8
v10

11
.nycrc
View File

@ -1,14 +1,15 @@
{
"check-coverage": true,
"lines": 93.53,
"functions": 89.63,
"branches": 88.57,
"statements": 93.34,
"lines": 96.81,
"functions": 93.80,
"branches": 92.63,
"statements": 96.80,
"include": [
"src/**/*.ts"
],
"exclude": [
"src/**/*.spec.ts"
"src/**/*.spec.ts",
"src/import-dotx/import-dotx.ts"
],
"reporter": [
"lcov",

View File

@ -1,6 +1,6 @@
language: node_js
node_js:
- 9
- 10
install:
- npm install
- npm install -g codecov

View File

@ -34,6 +34,15 @@ Here is an example of `docx` working in `Angular`:
* https://stackblitz.com/edit/angular-afvxtz
Here is an example of `docx` working in `React`:
* https://stackblitz.com/edit/react-ts-qq25sp
* https://stackblitz.com/edit/react-ts-qdqu7z (adding images to Word Document)
Here is an example of `docx` working in `Vue.js`:
* https://stackblitz.com/edit/vuejs-docx
## Node
Press `endpoint` on the `RunKit` website:
@ -50,7 +59,7 @@ Press `endpoint` on the `RunKit` website:
* https://runkit.com/dolanmiu/docx-demo8 - Header and Footer
* https://runkit.com/dolanmiu/docx-demo10 - **My CV generated with docx**
More [here](https://docx.js.org/#/examples) and [here](https://github.com/dolanmiu/docx/tree/master/demo)
More [here](https://github.com/dolanmiu/docx/tree/master/demo)
# How to use & Documentation

View File

@ -287,6 +287,7 @@ const table7 = new Table({
}),
new TableCell({
children: [new Paragraph("0,3")],
rowSpan: 3,
}),
],
}),
@ -296,9 +297,6 @@ const table7 = new Table({
children: [new Paragraph("1,0")],
columnSpan: 2,
}),
new TableCell({
children: [new Paragraph("1,3")],
}),
],
}),
new TableRow({
@ -311,9 +309,6 @@ const table7 = new Table({
children: [new Paragraph("2,2")],
rowSpan: 2,
}),
new TableCell({
children: [new Paragraph("2,3")],
}),
],
}),
new TableRow({
@ -336,6 +331,41 @@ const table7 = new Table({
},
});
const table8 = new Table({
rows: [
new TableRow({
children: [
new TableCell({ children: [new Paragraph("1,1")] }),
new TableCell({ children: [new Paragraph("1,2")] }),
new TableCell({ children: [new Paragraph("1,3")] }),
new TableCell({ children: [new Paragraph("1,4")], rowSpan: 4, borders }),
],
}),
new TableRow({
children: [
new TableCell({ children: [new Paragraph("2,1")] }),
new TableCell({ children: [new Paragraph("2,2")] }),
new TableCell({ children: [new Paragraph("2,3")], rowSpan: 3 }),
],
}),
new TableRow({
children: [
new TableCell({ children: [new Paragraph("3,1")] }),
new TableCell({ children: [new Paragraph("3,2")], rowSpan: 2 }),
],
}),
new TableRow({
children: [
new TableCell({ children: [new Paragraph("4,1")] }),
],
}),
],
width: {
size: 100,
type: WidthType.PERCENTAGE,
},
});
doc.addSection({
children: [
table,
@ -357,6 +387,8 @@ doc.addSection({
table6,
new Paragraph("Merging columns 4"),
table7,
new Paragraph("Merging columns 5"),
table8,
],
});

View File

@ -1,13 +1,3 @@
<p align="center">
<img alt="clippy the assistant" src="https://i.imgur.com/37uBGhO.gif">
</p>
<p align="center">
Easily generate .docx files with JS/TS. Works for Node and on the Browser. :100:
</p>
---
# Welcome
## Installation
@ -66,12 +56,6 @@ Packer.toBuffer(doc).then((buffer) => {
// Done! A file called 'My First Document.docx' will be in your file system.
```
## Honoured Mentions
[@felipeochoa](https://github.com/felipeochoa)
[@h4buli](https://github.com/h4buli)
<p align="center">
<img alt="clippy the assistant" src="http://i60.tinypic.com/339pvtt.png">
</p>

10
docs/_coverpage.md Normal file
View File

@ -0,0 +1,10 @@
<img src="https://i.imgur.com/37uBGhO.gif" alt="drawing" style="width:200px;"/>
> Easily generate .docx files with JS/TS. Works for Node and on the Browser. :100:
- Simple, declarative API
- 50+ usage examples
- Battle tested, mature, 95%+ coverage
[GitHub](https://github.com/dolanmiu/docx)
[Get Started](#Welcome)

View File

@ -1,6 +1,6 @@
* [Getting Started](/)
* [Examples](examples.md)
* [Examples](https://github.com/dolanmiu/docx/tree/master/demo)
* API

View File

@ -1,25 +1,23 @@
# Contribution Guidelines
* Include documentation reference(s) at the top of each file:
- Include documentation reference(s) at the top of each file:
```ts
// http://officeopenxml.com/WPdocument.php
```
* Follow Prettier standards, and consider using the [Prettier VSCode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) plugin.
- Follow Prettier standards, and consider using the [Prettier VSCode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) plugin.
* Follow the `TSLint` rules
- Follow the `TSLint` rules
## Always think about the user
The number one pillar for contribution to `docx` is to **ALWAYS** think about how the user will use `docx`.
Put yourself in their position, and imagine how they would feel about your feature you wrote.
1. Is it easy to use?
2. Has it been documented well?
3. Is it intuitive?
4. Is it consistent with the rest of the API?
4. Is it declarative?
5. Is it fun to use?
## Good Commit Names
@ -27,6 +25,7 @@ Put yourself in their position, and imagine how they would feel about your featu
Please write good commit messages when making a commit: https://chris.beams.io/posts/git-commit/
**Do not:**
```
c // What?
rtl // Adding acryonyms without explaining anything else is not helpful
@ -35,34 +34,6 @@ demo updated // Getting better, but capitalize the first letter
Unesesary coment removed // Make sure to use correct spelling
```
## No leaky components in API interface
> This mainly applies to the API the end user will consume.
Try to make method parameters of the outside API accept primitives, or `json` objects, so that child components are created **inside** the component, rather than being **injected** in.
This is so that:
1. Imports are much cleaner for the end user, no need for:
```ts
import { ChildComponent } from "./my-feature/sub-component/deeper/.../my-deep.component";
```
2. This is what I consider "leakage". The code is aware of the underlying implementation of the component.
3. It means the end user does not need to import and create the child component to be injected.
**Do not**
`TableFloatProperties` is a class. The outside world would have to `new` up the object, and inject it in like so:
```ts
public float(tableFloatProperties: TableFloatProperties): Table
```
```ts
table.float(new TableFloatProperties(...));
```
**Do**
`ITableFloatOptions` is an interface for a JSON of primitives. The end user would need to pass in a json object and not need to worry about the internals:
@ -71,30 +42,28 @@ This is so that:
public float(tableFloatOptions: ITableFloatOptions): Table
```
## Delcariative API
Make sure the API is declarative, so no _method calling_ or _mutation_. This is a design decision, consistent with the rest of the project. There are benefits to delcariative code over other styles of code, explained here: https://dzone.com/articles/why-declarative-coding-makes-you-a-better-programm
**Do not:**
```ts
table.float({...});
const paragraph = doc.createParagraph();
const text = paragraph.createText();
text.contents = "Hello World";
```
## Add vs Create
This is just a guideline, and the rules can sometimes be broken.
* Use `create` if the method `new`'s up an element inside:
**Do**
```ts
public createParagraph() {
const paragraph = new Paragraph();
this.root.push(paragraph);
}
```
* Use `add` if you add the element into the method as a parameter.
*Note:* This may look like its breaking the previous guideline, but it has semantically different meanings. The previous one is using data to construct an object, whereas this one is simply adding elements into the document:
```ts
public add(paragraph: Paragraph) {
this.root.push(paragraph);
}
doc.addSection({
children: [
new Paragraph({
children: [new TextRun("Hello World")],
}),
],
});
```
## Getters and Setters
@ -107,7 +76,7 @@ public get Level() {
}
```
There is no performance advantage by doing this. It means we don't need to prefix all private variables with the ugly `_`:
This is the convention of this project. There is no performance advantage by doing this. It means we don't need to prefix all private variables with `_`:
**Do not:**
@ -121,30 +90,6 @@ private get _level: string;
private get level: string;
```
## Temporal Methods
Some methods are `non-temporal`, which means regardless of when you call the method, it will have the same affect on the document. For example, setting the width of a table at the end of the document will have the same effect as setting the width at the start:
```ts
table.setWidth(1000); // now removed as of version 5.0.0
```
Whereas some methods are `temporal`, which means depending on the time-frame they are called, it would produce a difference result. For example, moving `createParagraph()` around your code will physically alter the document.
```ts
doc.createParagraph("hello");
```
If a method is `non-temporal`, put it in the objects `constructor`. For example:
```ts
const table = new Table(width: number);
```
`Non-temporal` methods are usually methods which can only be used one time and one time only. For example, `.float()`. It does not make sense to call `.float()` again if its already floating.
I am not sure what the real term is, but this will do.
## Interfaces over type alias
Do not use `type`, but rather use `Interfaces`. `type` cannot be extended, and a class cannot implement it.
@ -152,14 +97,14 @@ Do not use `type`, but rather use `Interfaces`. `type` cannot be extended, and a
> "In general, use what you want ( type alias / interface ) just be consistent"
> "always use interface for public API's definition when authoring a library or 3rd party ambient type definitions"
>
> * https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c
> - https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c
`Interface` is generally preferred over `type`: https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types
**Do not:**
```ts
type RelationshipFileInfo = { id: number, target: string };
type RelationshipFileInfo = { id: number; target: string };
```
**Do:**
@ -193,26 +138,26 @@ enum WeaponType = {
## Spell correctly, in full and in American English
I am not sure where these habits in software development come from, but I do not believe it is beneficial:
**Do not:**
```ts
readdy // misspelling
perm // abbreviation
conf // abbreviation
cnty // abbreviation
relationFile // abbreviation
colour // U.K. English
readdy; // misspelling
perm; // abbreviation
conf; // abbreviation
cnty; // abbreviation
relationFile; // abbreviation
colour; // U.K. English
```
**Do:**
```ts
ready
permission
config
country
relationshipFile
color
ready;
permission;
config;
country;
relationshipFile;
color;
```
## Keep files small (within reason)

View File

@ -1,219 +0,0 @@
# Examples
> All examples can run independently and can be found in the `/demo` folder of the project
All the examples below can be ran locally, to do so, run the following command:
```sh
npm run demo
```
This command will run the `demo selector app` in the `/demo` folder. It will prompt you to select a demo number, which will run a demo from that folder.
## Simple
A simple hello world of the `docx` library:
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo1.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo1.ts_
## Styles
### Styling with JS
This example shows how to customise the look and feel of a document using JS configuration
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo2.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo2.ts_
### Styling with XML
This example shows how to customise the look and feel of a document using XML configuration
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo13.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts_
## Numbering
This example shows many levels of numbering
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo3.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo3.ts_
## Table
Example of simple table
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
### Styling table borders
Styling the borders of a table
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
## Images
### Add image to the document
Importing Images from file system path
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
### Add images to header and footer
Example showing how to add image to headers and footers
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
### Scaling images
Example showing how to scale images
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo12.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo12.ts_
### Add Image to media before adding to document
This is the best way to add an image to a document because you can add the same image in two locations without increasing document size by re-using the same image
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo23.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo23.ts_
### Add image to table
As before, to add an image to a table, you would need to add it to the `Media` object first
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
### Images using Base64 URI
If you want to use a Base64 image instead
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo18.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo18.ts_
## Margins
Example showing how to set custom margins
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo6.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo6.ts_
## Orientation
Example showing how to set the document to `landscape` or `portrait`
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo7.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo7.ts_
## Headers & Footers
Example showing how to add headers and footers
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo8.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo8.ts_
## Multiple headers and footers
Check out `Sections` for this feature
## Page Breaks
### Normal page breaks
Example showing how to page break
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo14.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo14.ts_
### Page break before
Example showing how to page break before like in Word
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo15.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo15.ts_
## Sections
Example of how sections work. Sections allow multiple headers and footers, and `landscape`/`portrait` inside the same document.
Also you can have different page number formats and starts for different sections.
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo16.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo16.ts_
## Footnotes
Example of how to add footnotes. Good for references
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo17.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo17.ts_
## Packers
## Buffer output
Example showing how to use the Buffer packer and then write that buffer to the file system
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo19.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo19.ts_
## Bookmarks
Example showing how to make bookmarks to make internal hyperlinks within the document
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo21.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo21.ts_
## Bidirectional text
Example showing how to use bidirectional text for certain languages such as Hebrew
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo22.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo22.ts_
## Showcase
### My CV
Example showing how to add headers and footers
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo10.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo10.ts_
### Style and Images
This example shows how to customise the look and feel of a document and add images
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo11.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo11.ts_

View File

@ -1,12 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta charset="UTF-8" />
<title>docx - Generate .docx documents with JavaScript</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Generate .docx documents with JavaScript">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta name="description" content="Generate .docx documents with JavaScript" />
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/style.min.css"
@ -19,22 +18,19 @@
<div id="app"></div>
<script>
window.$docsify = {
name: 'docx',
repo: 'https://github.com/dolanmiu/docx',
name: "docx",
repo: "https://github.com/dolanmiu/docx",
loadSidebar: true,
subMaxLevel: 2,
search: 'auto',
}
search: "auto",
coverpage: true,
};
</script>
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
<script src="//unpkg.com/docsify/lib/plugins/emoji.min.js"></script>
<script src="https://unpkg.com/docsify-copy-code@2"></script>
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-typescript.min.js"></script>
<script
src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js"
type="text/javascript">
</script>
<script src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js" type="text/javascript"></script>
</body>
</html>

View File

@ -2,11 +2,7 @@
> Packers are the way in which `docx` turns your code into `.docx` format. It is completely decoupled from the `docx.Document`.
Packers in `version 4` and above are now one single `Packer`. It works in both a node and browser environment (Angular etc). Now, the packer returns a `Buffer`, `Blob` or `base64 string`. It is up to you to take that and persist it with node's `fs`, send it down as a downloadable file, or anything else you wish. As of version 4, this library will not have options to export to PDF.
## Version 5
Packers in `version 5` and above are now static methods on `Packer`.
Packers works in both a node and browser environment (Angular etc). Now, the packer returns a `Buffer`, `Blob` or `base64 string`. It is up to you to take that and persist it with node's `fs`, send it down as a downloadable file, or anything else you wish. As of `version 4+`, this library will not have options to export to PDF.
### Export as Buffer
@ -36,117 +32,3 @@ Packer.toBlob(doc).then((blob) => {
saveAs(blob, "example.docx");
});
```
## Version 4
The `Packer` in `version 4` requires an instance of `Packer`, so be sure to `new` it.
### Export as Buffer
This will return a NodeJS `Buffer`. If this is used in the browser, it will return a `UInt8Array` instead.
```ts
const packer = new docx.Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});
```
### Export as a `base64` string
```ts
const packer = new docx.Packer();
packer.toBase64String(doc).then((string) => {
console.log(string);
});
```
### Export as Blob
This is useful if you want to send it as an downloadable in a browser environment.
```ts
const packer = new docx.Packer();
packer.toBlob(doc).then((blob) => {
// saveAs from FileSaver will download the file
saveAs(blob, "example.docx");
});
```
## Version 3 and below
### File System Packer
```ts
const docx = require("docx");
const doc = new docx.Document();
const exporter = new docx.LocalPacker(doc);
exporter.pack("My Document");
// Word Document is in file system
```
### Buffer Packer
```ts
const docx = require("docx");
const doc = new docx.Document();
const exporter = new docx.BufferPacker(doc);
const buffer = exporter.pack();
```
### Stream Packer
Creates a `node` `Readable` stream
```ts
const docx = require("docx");
const doc = new docx.Document();
const exporter = new docx.StreamPacker(doc);
const stream = exporter.pack();
```
### Express Packer
The old express packer is now deprecated and may disappear soon, so you should upgrade.
The reason for this is because it means this project needs to know about and use `express`, which for a Word document generator, does not sound right. Seperation of concerns.
It will still be usable (for now), but it is ill advised.
I used the express exporter in my [website](http://www.dolan.bio).
The recommended way is to use the `StreamPacker` and handle the `express` magic outside of the library:
```ts
const docx = require("docx");
const doc = new docx.Document();
const exporter = new docx.StreamPacker(doc);
const stream = exporter.pack();
// Express' response object
res.attachment("yourfile.xlsx");
stream.pipe(res);
```
where `res` is the response object obtained through the Express router. It is that simple. The file will begin downloading in the browser.
### PDF Exporting
You can export your word document as a PDF file like so:
```ts
const exporter = new docx.LocalPacker(doc);
exporter.packPdf("My Document");
// Express
const exporter = new docx.ExpressPacker(doc, res);
exporter.packPdf("My Document");
```

3194
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "docx",
"version": "5.2.1",
"version": "5.3.0",
"description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
"main": "build/index.js",
"scripts": {
@ -64,10 +64,10 @@
"homepage": "https://github.com/dolanmiu/docx#readme",
"devDependencies": {
"@types/chai": "^3.4.35",
"@types/mocha": "^2.2.39",
"@types/mocha": "^8.0.0",
"@types/request-promise": "^4.1.42",
"@types/shortid": "0.0.29",
"@types/sinon": "^4.3.1",
"@types/sinon": "^9.0.4",
"@types/webpack": "^4.4.24",
"awesome-typescript-loader": "^3.4.1",
"chai": "^3.5.0",
@ -77,24 +77,24 @@
"jszip": "^3.1.5",
"mocha": "^5.2.0",
"mocha-webpack": "^1.0.1",
"nyc": "^14.1.1",
"nyc": "^15.1.0",
"pre-commit": "^1.2.2",
"prettier": "^1.15.2",
"prettier": "^2.0.5",
"prompt": "^1.0.0",
"replace-in-file": "^3.1.0",
"request": "^2.88.0",
"request-promise": "^4.2.2",
"rimraf": "^2.5.2",
"shelljs": "^0.7.7",
"sinon": "^5.0.7",
"ts-node": "^7.0.1",
"tslint": "^5.11.0",
"rimraf": "^3.0.2",
"shelljs": "^0.8.4",
"sinon": "^9.0.2",
"ts-node": "^8.10.2",
"tslint": "^6.1.3",
"tslint-immutable": "^4.9.0",
"typedoc": "^0.11.1",
"typedoc": "^0.16.11",
"typescript": "2.9.2",
"webpack": "^3.10.0"
},
"engines": {
"node": ">=8"
"node": ">=10"
}
}

View File

@ -45,7 +45,7 @@ export class Body extends XmlComponent {
private createSectionParagraph(section: SectionProperties): Paragraph {
const paragraph = new Paragraph({});
const properties = new ParagraphProperties({});
properties.addChildElement(section);
properties.push(section);
paragraph.addChildElement(properties);
return paragraph;
}

View File

@ -14,6 +14,7 @@ export enum PageNumberFormat {
ORDINAL_TEXT = "ordinalText",
UPPER_LETTER = "upperLetter",
UPPER_ROMAN = "upperRoman",
DECIMAL_FULL_WIDTH = "decimalFullWidth",
}
export interface IPageNumberTypeAttributes {

View File

@ -41,7 +41,7 @@ export interface ISectionOptions {
readonly size?: IPageSizeAttributes;
readonly margins?: IPageMarginAttributes;
readonly properties?: SectionPropertiesOptions;
readonly children: Array<Paragraph | Table | TableOfContents | HyperlinkRef>;
readonly children: (Paragraph | Table | TableOfContents | HyperlinkRef)[];
}
export class File {

View File

@ -2,7 +2,7 @@ import { Paragraph } from "./paragraph";
import { Table } from "./table";
export interface IHeaderOptions {
readonly children: Array<Paragraph | Table>;
readonly children: (Paragraph | Table)[];
}
export class Header {

View File

@ -21,14 +21,7 @@ export class Media {
private static generateId(): string {
// https://gist.github.com/6174/6062387
return (
Math.random()
.toString(36)
.substring(2, 15) +
Math.random()
.toString(36)
.substring(2, 15)
);
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
}
private readonly map: Map<string, IMediaData>;

View File

@ -7,6 +7,7 @@ import { AlignmentType, EmphasisMarkType, TabStopPosition } from "../paragraph";
import { UnderlineType } from "../paragraph/run/underline";
import { ShadingType } from "../table";
import { AbstractNumbering } from "./abstract-numbering";
import { LevelSuffix } from "./level";
describe("AbstractNumbering", () => {
it("stores its ID at its .id property", () => {
@ -48,6 +49,20 @@ describe("AbstractNumbering", () => {
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } });
});
it("has suffix", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 3,
format: "lowerLetter",
text: "%1)",
alignment: AlignmentType.END,
suffix: LevelSuffix.SPACE,
},
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:suff": { _attr: { "w:val": "space" } } });
});
describe("formatting methods: paragraph properties", () => {
it("#indent", () => {
const abstractNumbering = new AbstractNumbering(1, [
@ -283,22 +298,41 @@ describe("AbstractNumbering", () => {
});
describe("formatting methods: run properties", () => {
it("#size", () => {
const sizeTests = [
{
size: 24,
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
},
{
size: 24,
sizeComplexScript: true,
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
},
{
size: 24,
sizeComplexScript: false,
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }],
},
{
size: 24,
sizeComplexScript: 26,
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 26 } } }],
},
];
sizeTests.forEach(({ size, sizeComplexScript, expected }) => {
it(`#size ${size} cs ${sizeComplexScript}`, () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
text: "%0.",
style: {
run: {
size: 24,
},
run: { size, sizeComplexScript },
},
},
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }],
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": expected });
});
});
@ -478,83 +512,185 @@ describe("AbstractNumbering", () => {
});
});
it("#bold", () => {
const abstractNumbering = new AbstractNumbering(1, [
const boldTests = [
{
level: 0,
format: "lowerRoman",
text: "%0.",
style: {
run: {
bold: true,
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
},
{
bold: true,
boldComplexScript: true,
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
},
{
bold: true,
boldComplexScript: false,
expected: [{ "w:b": { _attr: { "w:val": true } } }],
},
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [{ "w:b": { _attr: { "w:val": true } } }],
});
});
it("#italics", () => {
];
boldTests.forEach(({ bold, boldComplexScript, expected }) => {
it(`#bold ${bold} cs ${boldComplexScript}`, () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
text: "%0.",
style: {
run: {
run: { bold, boldComplexScript },
},
},
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": expected });
});
});
const italicsTests = [
{
italics: true,
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
},
{
italics: true,
italicsComplexScript: true,
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
},
{
italics: true,
italicsComplexScript: false,
expected: [{ "w:i": { _attr: { "w:val": true } } }],
},
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [{ "w:i": { _attr: { "w:val": true } } }],
});
});
it("#highlight", () => {
];
italicsTests.forEach(({ italics, italicsComplexScript, expected }) => {
it(`#italics ${italics} cs ${italicsComplexScript}`, () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
text: "%0.",
style: {
run: {
run: { italics, italicsComplexScript },
},
},
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": expected });
});
});
const highlightTests = [
{
highlight: "005599",
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
},
{
highlight: "005599",
highlightComplexScript: true,
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
},
{
highlight: "005599",
highlightComplexScript: false,
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
},
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
});
});
it("#shadow", () => {
{
highlight: "005599",
highlightComplexScript: "550099",
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "550099" } } }],
},
];
highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => {
it(`#highlight ${highlight} cs ${highlightComplexScript}`, () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
text: "%0.",
style: {
run: {
run: { highlight, highlightComplexScript },
},
},
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": expected });
});
});
const shadingTests = [
{
shadow: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
expected: [
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
expected: [
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
shadingComplexScript: true,
expected: [
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
shadingComplexScript: false,
expected: [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
shadingComplexScript: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "00FF00",
},
expected: [
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "00FF00" } } },
],
},
];
shadingTests.forEach(({ shadow, shading, shadingComplexScript, expected }) => {
it("#shadow correctly", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
text: "%0.",
style: {
run: { shadow, shading, shadingComplexScript },
},
},
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": expected });
});
});

View File

@ -1,19 +1,7 @@
import { Attributes, XmlAttributeComponent, XmlComponent } from "file/xml-components";
import {
Alignment,
AlignmentType,
Indent,
KeepLines,
KeepNext,
Spacing,
TabStop,
TabStopType,
ThematicBreak,
} from "../paragraph/formatting";
import { ParagraphProperties } from "../paragraph/properties";
import * as formatting from "../paragraph/run/formatting";
import { RunProperties } from "../paragraph/run/properties";
import { IParagraphStyleOptions2, IRunStyleOptions } from "../styles/style-options";
import { AlignmentType } from "../paragraph/formatting";
import { IParagraphStylePropertiesOptions, ParagraphProperties } from "../paragraph/properties";
import { IRunStylePropertiesOptions, RunProperties } from "../paragraph/run/properties";
interface ILevelAttributesProperties {
readonly ilvl?: number;
@ -85,8 +73,8 @@ export interface ILevelsOptions {
readonly start?: number;
readonly suffix?: LevelSuffix;
readonly style?: {
readonly run?: IRunStyleOptions;
readonly paragraph?: IParagraphStyleOptions2;
readonly run?: IRunStylePropertiesOptions;
readonly paragraph?: IParagraphStylePropertiesOptions;
};
}
@ -125,8 +113,8 @@ export class LevelBase extends XmlComponent {
this.root.push(new LevelText(text));
}
this.paragraphProperties = new ParagraphProperties({});
this.runProperties = new RunProperties();
this.paragraphProperties = new ParagraphProperties(style && style.paragraph);
this.runProperties = new RunProperties(style && style.run);
this.root.push(this.paragraphProperties);
this.root.push(this.runProperties);
@ -134,104 +122,6 @@ export class LevelBase extends XmlComponent {
if (suffix) {
this.root.push(new Suffix(suffix));
}
if (style) {
if (style.run) {
if (style.run.size) {
this.runProperties.push(new formatting.Size(style.run.size));
}
if (style.run.bold) {
this.runProperties.push(new formatting.Bold());
}
if (style.run.italics) {
this.runProperties.push(new formatting.Italics());
}
if (style.run.smallCaps) {
this.runProperties.push(new formatting.SmallCaps());
}
if (style.run.allCaps) {
this.runProperties.push(new formatting.Caps());
}
if (style.run.strike) {
this.runProperties.push(new formatting.Strike());
}
if (style.run.doubleStrike) {
this.runProperties.push(new formatting.DoubleStrike());
}
if (style.run.subScript) {
this.runProperties.push(new formatting.SubScript());
}
if (style.run.superScript) {
this.runProperties.push(new formatting.SuperScript());
}
if (style.run.underline) {
this.runProperties.push(new formatting.Underline(style.run.underline.type, style.run.underline.color));
}
if (style.run.emphasisMark) {
this.runProperties.push(new formatting.EmphasisMark(style.run.emphasisMark.type));
}
if (style.run.color) {
this.runProperties.push(new formatting.Color(style.run.color));
}
if (style.run.font) {
this.runProperties.push(new formatting.RunFonts(style.run.font));
}
if (style.run.highlight) {
this.runProperties.push(new formatting.Highlight(style.run.highlight));
}
if (style.run.shadow) {
this.runProperties.push(new formatting.Shading(style.run.shadow.type, style.run.shadow.fill, style.run.shadow.color));
}
}
if (style.paragraph) {
if (style.paragraph.alignment) {
this.paragraphProperties.push(new Alignment(style.paragraph.alignment));
}
if (style.paragraph.thematicBreak) {
this.paragraphProperties.push(new ThematicBreak());
}
if (style.paragraph.rightTabStop) {
this.paragraphProperties.push(new TabStop(TabStopType.RIGHT, style.paragraph.rightTabStop));
}
if (style.paragraph.leftTabStop) {
this.paragraphProperties.push(new TabStop(TabStopType.LEFT, style.paragraph.leftTabStop));
}
if (style.paragraph.indent) {
this.paragraphProperties.push(new Indent(style.paragraph.indent));
}
if (style.paragraph.spacing) {
this.paragraphProperties.push(new Spacing(style.paragraph.spacing));
}
if (style.paragraph.keepNext) {
this.paragraphProperties.push(new KeepNext());
}
if (style.paragraph.keepLines) {
this.paragraphProperties.push(new KeepLines());
}
}
}
}
}

View File

@ -8,10 +8,10 @@ import { ILevelsOptions } from "./level";
import { ConcreteNumbering } from "./num";
export interface INumberingOptions {
readonly config: Array<{
readonly config: {
readonly levels: ILevelsOptions[];
readonly reference: string;
}>;
}[];
}
export class Numbering extends XmlComponent {

View File

@ -542,14 +542,8 @@ describe("Paragraph", () => {
},
});
const tree = new Formatter().format(paragraph);
expect(tree)
.to.have.property("w:p")
.which.is.an("array")
.which.has.length.at.least(1);
expect(tree["w:p"][0])
.to.have.property("w:pPr")
.which.is.an("array")
.which.has.length.at.least(1);
expect(tree).to.have.property("w:p").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]).to.have.property("w:pPr").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]["w:pPr"][0]).to.deep.equal({
"w:pStyle": { _attr: { "w:val": "ListParagraph" } },
});
@ -562,14 +556,8 @@ describe("Paragraph", () => {
},
});
const tree = new Formatter().format(paragraph);
expect(tree)
.to.have.property("w:p")
.which.is.an("array")
.which.has.length.at.least(1);
expect(tree["w:p"][0])
.to.have.property("w:pPr")
.which.is.an("array")
.which.has.length.at.least(1);
expect(tree).to.have.property("w:p").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]).to.have.property("w:pPr").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]["w:pPr"][0]).to.deep.equal({
"w:pStyle": { _attr: { "w:val": "ListParagraph" } },
});
@ -582,14 +570,8 @@ describe("Paragraph", () => {
},
});
const tree = new Formatter().format(paragraph);
expect(tree)
.to.have.property("w:p")
.which.is.an("array")
.which.has.length.at.least(1);
expect(tree["w:p"][0])
.to.have.property("w:pPr")
.which.is.an("array")
.which.has.length.at.least(2);
expect(tree).to.have.property("w:p").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]).to.have.property("w:pPr").which.is.an("array").which.has.length.at.least(2);
expect(tree["w:p"][0]["w:pPr"][1]).to.deep.equal({
"w:numPr": [{ "w:ilvl": { _attr: { "w:val": 1 } } }, { "w:numId": { _attr: { "w:val": 1 } } }],
});
@ -605,14 +587,8 @@ describe("Paragraph", () => {
},
});
const tree = new Formatter().format(paragraph);
expect(tree)
.to.have.property("w:p")
.which.is.an("array")
.which.has.length.at.least(1);
expect(tree["w:p"][0])
.to.have.property("w:pPr")
.which.is.an("array")
.which.has.length.at.least(1);
expect(tree).to.have.property("w:p").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]).to.have.property("w:pPr").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]["w:pPr"][0]).to.deep.equal({
"w:pStyle": { _attr: { "w:val": "ListParagraph" } },
});

View File

@ -3,51 +3,23 @@ import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run"
import { IXmlableObject, XmlComponent } from "file/xml-components";
import { File } from "../file";
import { Alignment, AlignmentType } from "./formatting/alignment";
import { Bidirectional } from "./formatting/bidirectional";
import { IBorderOptions, ThematicBreak } from "./formatting/border";
import { IIndentAttributesProperties, Indent } from "./formatting/indent";
import { KeepLines, KeepNext } from "./formatting/keep";
import { PageBreak, PageBreakBefore } from "./formatting/page-break";
import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spacing";
import { HeadingLevel, Style } from "./formatting/style";
import { LeaderType, TabStop, TabStopPosition, TabStopType } from "./formatting/tab-stop";
import { NumberProperties } from "./formatting/unordered-list";
import { Bookmark, HyperlinkRef, OutlineLevel } from "./links";
import { ParagraphProperties } from "./properties";
import { PageBreak } from "./formatting/page-break";
import { Bookmark, HyperlinkRef } from "./links";
import { IParagraphPropertiesOptions, ParagraphProperties } from "./properties";
import { PictureRun, Run, SequentialIdentifier, SymbolRun, TextRun } from "./run";
export interface IParagraphOptions {
export interface IParagraphOptions extends IParagraphPropertiesOptions {
readonly text?: string;
readonly border?: IBorderOptions;
readonly spacing?: ISpacingProperties;
readonly outlineLevel?: number;
readonly alignment?: AlignmentType;
readonly heading?: HeadingLevel;
readonly bidirectional?: boolean;
readonly thematicBreak?: boolean;
readonly pageBreakBefore?: boolean;
readonly contextualSpacing?: boolean;
readonly indent?: IIndentAttributesProperties;
readonly keepLines?: boolean;
readonly keepNext?: boolean;
readonly tabStops?: Array<{
readonly position: number | TabStopPosition;
readonly type: TabStopType;
readonly leader?: LeaderType;
}>;
readonly style?: string;
readonly bullet?: {
readonly level: number;
};
readonly numbering?: {
readonly reference: string;
readonly level: number;
readonly custom?: boolean;
};
readonly children?: Array<
TextRun | PictureRun | SymbolRun | Bookmark | PageBreak | SequentialIdentifier | FootnoteReferenceRun | HyperlinkRef
>;
readonly children?: (
| TextRun
| PictureRun
| SymbolRun
| Bookmark
| PageBreak
| SequentialIdentifier
| FootnoteReferenceRun
| HyperlinkRef
)[];
}
export class Paragraph extends XmlComponent {
@ -70,9 +42,7 @@ export class Paragraph extends XmlComponent {
return;
}
this.properties = new ParagraphProperties({
border: options.border,
});
this.properties = new ParagraphProperties(options);
this.root.push(this.properties);
@ -80,72 +50,6 @@ export class Paragraph extends XmlComponent {
this.root.push(new TextRun(options.text));
}
if (options.spacing) {
this.properties.push(new Spacing(options.spacing));
}
if (options.outlineLevel !== undefined) {
this.properties.push(new OutlineLevel(options.outlineLevel));
}
if (options.alignment) {
this.properties.push(new Alignment(options.alignment));
}
if (options.heading) {
this.properties.push(new Style(options.heading));
}
if (options.bidirectional) {
this.properties.push(new Bidirectional());
}
if (options.thematicBreak) {
this.properties.push(new ThematicBreak());
}
if (options.pageBreakBefore) {
this.properties.push(new PageBreakBefore());
}
if (options.contextualSpacing) {
this.properties.push(new ContextualSpacing(options.contextualSpacing));
}
if (options.indent) {
this.properties.push(new Indent(options.indent));
}
if (options.keepLines) {
this.properties.push(new KeepLines());
}
if (options.keepNext) {
this.properties.push(new KeepNext());
}
if (options.tabStops) {
for (const tabStop of options.tabStops) {
this.properties.push(new TabStop(tabStop.type, tabStop.position, tabStop.leader));
}
}
if (options.style) {
this.properties.push(new Style(options.style));
}
if (options.bullet) {
this.properties.push(new Style("ListParagraph"));
this.properties.push(new NumberProperties(1, options.bullet.level));
}
if (options.numbering) {
if (!options.numbering.custom) {
this.properties.push(new Style("ListParagraph"));
}
this.properties.push(new NumberProperties(options.numbering.reference, options.numbering.level));
}
if (options.children) {
for (const child of options.children) {
if (child instanceof Bookmark) {

View File

@ -1,19 +1,136 @@
// http://officeopenxml.com/WPparagraphProperties.php
import { IgnoreIfEmptyXmlComponent, XmlComponent } from "file/xml-components";
import { Alignment, AlignmentType } from "./formatting/alignment";
import { Bidirectional } from "./formatting/bidirectional";
import { Border, IBorderOptions, ThematicBreak } from "./formatting/border";
import { IIndentAttributesProperties, Indent } from "./formatting/indent";
import { KeepLines, KeepNext } from "./formatting/keep";
import { PageBreakBefore } from "./formatting/page-break";
import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spacing";
import { HeadingLevel, Style } from "./formatting/style";
import { LeaderType, TabStop, TabStopPosition, TabStopType } from "./formatting/tab-stop";
import { NumberProperties } from "./formatting/unordered-list";
import { OutlineLevel } from "./links";
import { Border, IBorderOptions } from "./formatting/border";
export interface IParagraphStylePropertiesOptions {
readonly alignment?: AlignmentType;
readonly thematicBreak?: boolean;
readonly contextualSpacing?: boolean;
readonly rightTabStop?: number;
readonly leftTabStop?: number;
readonly indent?: IIndentAttributesProperties;
readonly spacing?: ISpacingProperties;
readonly keepNext?: boolean;
readonly keepLines?: boolean;
readonly outlineLevel?: number;
}
interface IParagraphPropertiesOptions {
export interface IParagraphPropertiesOptions extends IParagraphStylePropertiesOptions {
readonly border?: IBorderOptions;
readonly heading?: HeadingLevel;
readonly bidirectional?: boolean;
readonly pageBreakBefore?: boolean;
readonly tabStops?: {
readonly position: number | TabStopPosition;
readonly type: TabStopType;
readonly leader?: LeaderType;
}[];
readonly style?: string;
readonly bullet?: {
readonly level: number;
};
readonly numbering?: {
readonly reference: string;
readonly level: number;
readonly custom?: boolean;
};
}
export class ParagraphProperties extends IgnoreIfEmptyXmlComponent {
constructor(options: IParagraphPropertiesOptions) {
constructor(options?: IParagraphPropertiesOptions) {
super("w:pPr");
if (!options) {
return;
}
if (options.border) {
this.push(new Border(options.border));
}
if (options.spacing) {
this.push(new Spacing(options.spacing));
}
if (options.outlineLevel !== undefined) {
this.push(new OutlineLevel(options.outlineLevel));
}
if (options.alignment) {
this.push(new Alignment(options.alignment));
}
if (options.heading) {
this.push(new Style(options.heading));
}
if (options.bidirectional) {
this.push(new Bidirectional());
}
if (options.thematicBreak) {
this.push(new ThematicBreak());
}
if (options.pageBreakBefore) {
this.push(new PageBreakBefore());
}
if (options.contextualSpacing) {
this.push(new ContextualSpacing(options.contextualSpacing));
}
if (options.indent) {
this.push(new Indent(options.indent));
}
if (options.keepLines) {
this.push(new KeepLines());
}
if (options.keepNext) {
this.push(new KeepNext());
}
if (options.tabStops) {
for (const tabStop of options.tabStops) {
this.push(new TabStop(tabStop.type, tabStop.position, tabStop.leader));
}
}
if (options.style) {
this.push(new Style(options.style));
}
if (options.bullet) {
this.push(new Style("ListParagraph"));
this.push(new NumberProperties(1, options.bullet.level));
}
if (options.numbering) {
if (!options.numbering.custom) {
this.push(new Style("ListParagraph"));
}
this.push(new NumberProperties(options.numbering.reference, options.numbering.level));
}
if (options.rightTabStop) {
this.push(new TabStop(TabStopType.RIGHT, options.rightTabStop));
}
if (options.leftTabStop) {
this.push(new TabStop(TabStopType.LEFT, options.leftTabStop));
}
}
public push(item: XmlComponent): void {

View File

@ -1,13 +0,0 @@
import { XmlComponent } from "file/xml-components";
export class SmallCaps extends XmlComponent {
constructor() {
super("w:smallCaps");
}
}
export class Caps extends XmlComponent {
constructor() {
super("w:caps");
}
}

View File

@ -115,17 +115,6 @@ export class Imprint extends XmlComponent {
}
}
/* export class Shadow extends XmlComponent {
constructor() {
super("w:shadow");
this.root.push(
new Attributes({
val: true,
}),
);
}
} */
export class SmallCaps extends XmlComponent {
constructor() {
super("w:smallCaps");

View File

@ -1,4 +1,5 @@
export * from "./run";
export * from "./properties";
export * from "./text-run";
export * from "./symbol-run";
export * from "./picture-run";

View File

@ -1,8 +1,183 @@
import { ShadingType } from "file/table";
import { IgnoreIfEmptyXmlComponent, XmlComponent } from "file/xml-components";
import { EmphasisMark, EmphasisMarkType } from "./emphasis-mark";
import {
Bold,
BoldComplexScript,
Caps,
CharacterSpacing,
Color,
DoubleStrike,
Highlight,
HighlightComplexScript,
Italics,
ItalicsComplexScript,
RightToLeft,
Shading,
ShadowComplexScript,
Size,
SizeComplexScript,
SmallCaps,
Strike,
} from "./formatting";
import { IFontAttributesProperties, RunFonts } from "./run-fonts";
import { SubScript, SuperScript } from "./script";
import { Style } from "./style";
import { Underline, UnderlineType } from "./underline";
interface IFontOptions {
readonly name: string;
readonly hint?: string;
}
export interface IRunStylePropertiesOptions {
readonly bold?: boolean;
readonly boldComplexScript?: boolean;
readonly italics?: boolean;
readonly italicsComplexScript?: boolean;
readonly underline?: {
readonly color?: string;
readonly type?: UnderlineType;
};
readonly emphasisMark?: {
readonly type?: EmphasisMarkType;
};
readonly color?: string;
readonly size?: number;
readonly sizeComplexScript?: boolean | number;
readonly rightToLeft?: boolean;
readonly smallCaps?: boolean;
readonly allCaps?: boolean;
readonly strike?: boolean;
readonly doubleStrike?: boolean;
readonly subScript?: boolean;
readonly superScript?: boolean;
readonly font?: string | IFontOptions | IFontAttributesProperties;
readonly highlight?: string;
readonly highlightComplexScript?: boolean | string;
readonly characterSpacing?: number;
readonly shading?: {
readonly type: ShadingType;
readonly fill: string;
readonly color: string;
};
readonly shadingComplexScript?: boolean | IRunStylePropertiesOptions["shading"];
readonly shadow?: IRunStylePropertiesOptions["shading"];
}
export interface IRunPropertiesOptions extends IRunStylePropertiesOptions {
readonly style?: string;
}
export class RunProperties extends IgnoreIfEmptyXmlComponent {
constructor() {
constructor(options?: IRunPropertiesOptions) {
super("w:rPr");
if (!options) {
return;
}
if (options.bold) {
this.push(new Bold());
}
if ((options.boldComplexScript === undefined && options.bold) || options.boldComplexScript) {
this.push(new BoldComplexScript());
}
if (options.italics) {
this.push(new Italics());
}
if ((options.italicsComplexScript === undefined && options.italics) || options.italicsComplexScript) {
this.push(new ItalicsComplexScript());
}
if (options.underline) {
this.push(new Underline(options.underline.type, options.underline.color));
}
if (options.emphasisMark) {
this.push(new EmphasisMark(options.emphasisMark.type));
}
if (options.color) {
this.push(new Color(options.color));
}
if (options.size) {
this.push(new Size(options.size));
}
const szCs =
options.sizeComplexScript === undefined || options.sizeComplexScript === true ? options.size : options.sizeComplexScript;
if (szCs) {
this.push(new SizeComplexScript(szCs));
}
if (options.rightToLeft) {
this.push(new RightToLeft());
}
if (options.smallCaps) {
this.push(new SmallCaps());
}
if (options.allCaps) {
this.push(new Caps());
}
if (options.strike) {
this.push(new Strike());
}
if (options.doubleStrike) {
this.push(new DoubleStrike());
}
if (options.subScript) {
this.push(new SubScript());
}
if (options.superScript) {
this.push(new SuperScript());
}
if (options.style) {
this.push(new Style(options.style));
}
if (options.font) {
if (typeof options.font === "string") {
this.push(new RunFonts(options.font));
} else if ("name" in options.font) {
this.push(new RunFonts(options.font.name, options.font.hint));
} else {
this.push(new RunFonts(options.font));
}
}
if (options.highlight) {
this.push(new Highlight(options.highlight));
}
const highlightCs =
options.highlightComplexScript === undefined || options.highlightComplexScript === true
? options.highlight
: options.highlightComplexScript;
if (highlightCs) {
this.push(new HighlightComplexScript(highlightCs));
}
if (options.characterSpacing) {
this.push(new CharacterSpacing(options.characterSpacing));
}
const shading = options.shading || options.shadow;
if (shading) {
this.push(new Shading(shading.type, shading.fill, shading.color));
}
const shdCs =
options.shadingComplexScript === undefined || options.shadingComplexScript === true ? shading : options.shadingComplexScript;
if (shdCs) {
this.push(new ShadowComplexScript(shdCs.type, shdCs.fill, shdCs.color));
}
}
public push(item: XmlComponent): void {

View File

@ -116,7 +116,7 @@ describe("Run", () => {
});
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [{ "w:rPr": [{ "w:smallCaps": {} }] }],
"w:r": [{ "w:rPr": [{ "w:smallCaps": { _attr: { "w:val": true } } }] }],
});
});
});
@ -128,7 +128,7 @@ describe("Run", () => {
});
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [{ "w:rPr": [{ "w:caps": {} }] }],
"w:r": [{ "w:rPr": [{ "w:caps": { _attr: { "w:val": true } } }] }],
});
});
});

View File

@ -1,70 +1,16 @@
// http://officeopenxml.com/WPtext.php
import { ShadingType } from "file/table";
import { XmlComponent } from "file/xml-components";
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
import { FieldInstruction } from "file/table-of-contents/field-instruction";
import { Break } from "./break";
import { Caps, SmallCaps } from "./caps";
import { EmphasisMark, EmphasisMarkType } from "./emphasis-mark";
import { Begin, End, Separate } from "./field";
import {
Bold,
BoldComplexScript,
Color,
DoubleStrike,
Highlight,
HighlightComplexScript,
Italics,
ItalicsComplexScript,
RightToLeft,
Shading,
ShadowComplexScript,
Size,
SizeComplexScript,
Strike,
} from "./formatting";
import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number";
import { RunProperties } from "./properties";
import { IRunPropertiesOptions, RunProperties } from "./properties";
import { Text } from "./run-components/text";
import { IFontAttributesProperties, RunFonts } from "./run-fonts";
import { SubScript, SuperScript } from "./script";
import { Style } from "./style";
import { Underline, UnderlineType } from "./underline";
interface IFontOptions {
readonly name: string;
readonly hint?: string;
}
export interface IRunOptions {
readonly bold?: true;
readonly italics?: true;
readonly underline?: {
readonly color?: string;
readonly type?: UnderlineType;
};
readonly emphasisMark?: {
readonly type?: EmphasisMarkType;
};
readonly color?: string;
readonly size?: number;
readonly rightToLeft?: boolean;
readonly smallCaps?: boolean;
readonly allCaps?: boolean;
readonly strike?: boolean;
readonly doubleStrike?: boolean;
readonly subScript?: boolean;
readonly superScript?: boolean;
readonly style?: string;
readonly font?: IFontOptions | IFontAttributesProperties;
readonly highlight?: string;
readonly shading?: {
readonly type: ShadingType;
readonly fill: string;
readonly color: string;
};
readonly children?: Array<Begin | FieldInstruction | Separate | End | PageNumber | FootnoteReferenceRun | string>;
export interface IRunOptions extends IRunPropertiesOptions {
readonly children?: (Begin | FieldInstruction | Separate | End | PageNumber | FootnoteReferenceRun | string)[];
readonly text?: string;
}
@ -79,86 +25,9 @@ export class Run extends XmlComponent {
constructor(options: IRunOptions) {
super("w:r");
this.properties = new RunProperties();
this.properties = new RunProperties(options);
this.root.push(this.properties);
if (options.bold) {
this.properties.push(new Bold());
this.properties.push(new BoldComplexScript());
}
if (options.italics) {
this.properties.push(new Italics());
this.properties.push(new ItalicsComplexScript());
}
if (options.underline) {
this.properties.push(new Underline(options.underline.type, options.underline.color));
}
if (options.emphasisMark) {
this.properties.push(new EmphasisMark(options.emphasisMark.type));
}
if (options.color) {
this.properties.push(new Color(options.color));
}
if (options.size) {
this.properties.push(new Size(options.size));
this.properties.push(new SizeComplexScript(options.size));
}
if (options.rightToLeft) {
this.properties.push(new RightToLeft());
}
if (options.smallCaps) {
this.properties.push(new SmallCaps());
}
if (options.allCaps) {
this.properties.push(new Caps());
}
if (options.strike) {
this.properties.push(new Strike());
}
if (options.doubleStrike) {
this.properties.push(new DoubleStrike());
}
if (options.subScript) {
this.properties.push(new SubScript());
}
if (options.superScript) {
this.properties.push(new SuperScript());
}
if (options.style) {
this.properties.push(new Style(options.style));
}
if (options.font) {
if ("name" in options.font) {
this.properties.push(new RunFonts(options.font.name, options.font.hint));
} else {
this.properties.push(new RunFonts(options.font));
}
}
if (options.highlight) {
this.properties.push(new Highlight(options.highlight));
this.properties.push(new HighlightComplexScript(options.highlight));
}
if (options.shading) {
this.properties.push(new Shading(options.shading.type, options.shading.fill, options.shading.color));
this.properties.push(new ShadowComplexScript(options.shading.type, options.shading.fill, options.shading.color));
}
if (options.children) {
for (const child of options.children) {
if (typeof child === "string") {

View File

@ -0,0 +1,45 @@
import { expect } from "chai";
import { DocumentDefaults } from "./document-defaults";
import { Formatter } from "export/formatter";
describe("DocumentDefaults", () => {
it("#constructor", () => {
const defaults = new DocumentDefaults({
paragraph: { spacing: { line: 240 } },
run: { color: "808080" },
});
const tree = new Formatter().format(defaults);
expect(tree).to.deep.equal({
"w:docDefaults": [
{
"w:rPrDefault": [
{
"w:rPr": [
{
"w:color": { _attr: { "w:val": "808080" } },
},
],
},
],
},
{
"w:pPrDefault": [
{
"w:pPr": [
{
"w:spacing": {
_attr: {
"w:line": 240,
},
},
},
],
},
],
},
],
});
});
});

View File

@ -0,0 +1,25 @@
import { IParagraphStylePropertiesOptions } from "file/paragraph/properties";
import { IRunStylePropertiesOptions } from "file/paragraph/run/properties";
import { XmlComponent } from "file/xml-components";
import { ParagraphPropertiesDefaults } from "./paragraph-properties";
import { RunPropertiesDefaults } from "./run-properties";
export interface IDocumentDefaultsOptions {
readonly paragraph?: IParagraphStylePropertiesOptions;
readonly run?: IRunStylePropertiesOptions;
}
export class DocumentDefaults extends XmlComponent {
private readonly runPropertiesDefaults: RunPropertiesDefaults;
private readonly paragraphPropertiesDefaults: ParagraphPropertiesDefaults;
constructor(options?: IDocumentDefaultsOptions) {
super("w:docDefaults");
this.runPropertiesDefaults = new RunPropertiesDefaults(options && options.run);
this.paragraphPropertiesDefaults = new ParagraphPropertiesDefaults(options && options.paragraph);
this.root.push(this.runPropertiesDefaults);
this.root.push(this.paragraphPropertiesDefaults);
}
}

View File

@ -1,16 +1,3 @@
import { XmlComponent } from "file/xml-components";
import { ParagraphPropertiesDefaults } from "./paragraph-properties";
import { RunPropertiesDefaults } from "./run-properties";
export class DocumentDefaults extends XmlComponent {
private readonly runPropertiesDefaults: RunPropertiesDefaults;
private readonly paragraphPropertiesDefaults: ParagraphPropertiesDefaults;
constructor() {
super("w:docDefaults");
this.runPropertiesDefaults = new RunPropertiesDefaults();
this.paragraphPropertiesDefaults = new ParagraphPropertiesDefaults();
this.root.push(this.runPropertiesDefaults);
this.root.push(this.paragraphPropertiesDefaults);
}
}
export * from "./paragraph-properties";
export * from "./run-properties";
export * from "./document-defaults";

View File

@ -1,9 +1,9 @@
import { ParagraphProperties } from "file/paragraph/properties";
import { IParagraphStylePropertiesOptions, ParagraphProperties } from "file/paragraph/properties";
import { XmlComponent } from "file/xml-components";
export class ParagraphPropertiesDefaults extends XmlComponent {
constructor() {
constructor(options?: IParagraphStylePropertiesOptions) {
super("w:pPrDefault");
this.root.push(new ParagraphProperties({}));
this.root.push(new ParagraphProperties(options));
}
}

View File

@ -1,25 +1,12 @@
import { Size, SizeComplexScript } from "file/paragraph/run/formatting";
import { RunProperties } from "file/paragraph/run/properties";
import { IFontAttributesProperties, RunFonts } from "file/paragraph/run/run-fonts";
import { IRunStylePropertiesOptions, RunProperties } from "file/paragraph/run/properties";
import { XmlComponent } from "file/xml-components";
export class RunPropertiesDefaults extends XmlComponent {
private readonly properties: RunProperties;
constructor() {
constructor(options?: IRunStylePropertiesOptions) {
super("w:rPrDefault");
this.properties = new RunProperties();
this.properties = new RunProperties(options);
this.root.push(this.properties);
}
public size(size: number): RunPropertiesDefaults {
this.properties.push(new Size(size));
this.properties.push(new SizeComplexScript(size));
return this;
}
public font(font: string | IFontAttributesProperties): RunPropertiesDefaults {
this.properties.push(new RunFonts(font));
return this;
}
}

View File

@ -1,4 +1,4 @@
export * from "./styles";
export * from "./style/character-style";
export * from "./style/paragraph-style";
export * from "./style-options";
export * from "./defaults";

View File

@ -1,56 +0,0 @@
import {
AlignmentType,
EmphasisMarkType,
IFontAttributesProperties,
IIndentAttributesProperties,
ISpacingProperties,
UnderlineType,
} from "../paragraph";
import { ShadingType } from "../table";
export interface IRunStyleOptions {
readonly size?: number;
readonly bold?: boolean;
readonly italics?: boolean;
readonly smallCaps?: boolean;
readonly allCaps?: boolean;
readonly strike?: boolean;
readonly doubleStrike?: boolean;
readonly subScript?: boolean;
readonly superScript?: boolean;
readonly underline?: {
readonly type?: UnderlineType;
readonly color?: string;
};
readonly emphasisMark?: {
readonly type?: EmphasisMarkType;
};
readonly color?: string;
readonly font?: string | IFontAttributesProperties;
readonly characterSpacing?: number;
readonly highlight?: string;
readonly shadow?: {
readonly type: ShadingType;
readonly fill: string;
readonly color: string;
};
}
export interface IParagraphStyleOptions2 {
readonly alignment?: AlignmentType;
readonly thematicBreak?: boolean;
readonly contextualSpacing?: boolean;
readonly rightTabStop?: number;
readonly leftTabStop?: number;
readonly indent?: IIndentAttributesProperties;
readonly spacing?: ISpacingProperties;
readonly keepNext?: boolean;
readonly keepLines?: boolean;
readonly outlineLevel?: number;
}
// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432
/**
* @ignore
*/
export const WORKAROUND4 = "";

View File

@ -334,19 +334,39 @@ describe("CharacterStyle", () => {
});
describe("formatting methods: run properties", () => {
it("#size", () => {
const sizeTests = [
{
size: 24,
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
},
{
size: 24,
sizeComplexScript: true,
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
},
{
size: 24,
sizeComplexScript: false,
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }],
},
{
size: 24,
sizeComplexScript: 26,
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 26 } } }],
},
];
sizeTests.forEach(({ size, sizeComplexScript, expected }) => {
it(`#size ${size} cs ${sizeComplexScript}`, () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
size: 24,
},
run: { size, sizeComplexScript },
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
"w:rPr": expected,
},
{
"w:uiPriority": {
@ -361,6 +381,7 @@ describe("CharacterStyle", () => {
],
});
});
});
describe("#underline", () => {
it("should set underline to 'single' if no arguments are given", () => {
@ -577,19 +598,34 @@ describe("CharacterStyle", () => {
});
});
it("#bold", () => {
const boldTests = [
{
bold: true,
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
},
{
bold: true,
boldComplexScript: true,
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
},
{
bold: true,
boldComplexScript: false,
expected: [{ "w:b": { _attr: { "w:val": true } } }],
},
];
boldTests.forEach(({ bold, boldComplexScript, expected }) => {
it(`#bold ${bold} cs ${boldComplexScript}`, () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
bold: true,
},
run: { bold, boldComplexScript },
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:b": { _attr: { "w:val": true } } }],
"w:rPr": expected,
},
{
"w:uiPriority": {
@ -604,20 +640,36 @@ describe("CharacterStyle", () => {
],
});
});
});
it("#italics", () => {
const italicsTests = [
{
italics: true,
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
},
{
italics: true,
italicsComplexScript: true,
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
},
{
italics: true,
italicsComplexScript: false,
expected: [{ "w:i": { _attr: { "w:val": true } } }],
},
];
italicsTests.forEach(({ italics, italicsComplexScript, expected }) => {
it(`#italics ${italics} cs ${italicsComplexScript}`, () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
italics: true,
},
run: { italics, italicsComplexScript },
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:i": { _attr: { "w:val": true } } }],
"w:rPr": expected,
},
{
"w:uiPriority": {
@ -632,6 +684,7 @@ describe("CharacterStyle", () => {
],
});
});
});
it("#link", () => {
const style = new CharacterStyle({ id: "myStyleId", link: "MyLink" });
@ -673,19 +726,39 @@ describe("CharacterStyle", () => {
});
});
it("#highlight", () => {
const highlightTests = [
{
highlight: "005599",
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
},
{
highlight: "005599",
highlightComplexScript: true,
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
},
{
highlight: "005599",
highlightComplexScript: false,
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
},
{
highlight: "005599",
highlightComplexScript: "550099",
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "550099" } } }],
},
];
highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => {
it(`#highlight ${highlight} cs ${highlightComplexScript}`, () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
highlight: "005599",
},
run: { highlight, highlightComplexScript },
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
"w:rPr": expected,
},
{
"w:uiPriority": {
@ -700,34 +773,81 @@ describe("CharacterStyle", () => {
],
});
});
});
it("#shadow", () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
const shadingTests = [
{
shadow: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
expected: [
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
expected: [
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
shadingComplexScript: true,
expected: [
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
shadingComplexScript: false,
expected: [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
shadingComplexScript: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "00FF00",
},
expected: [
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "00FF00" } } },
],
},
];
shadingTests.forEach(({ shadow, shading, shadingComplexScript, expected }) => {
it("#shadow correctly", () => {
const style = new CharacterStyle({
id: "myStyleId",
run: { shadow, shading, shadingComplexScript },
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [
{
"w:shd": {
_attr: {
"w:val": "pct10",
"w:fill": "00FFFF",
"w:color": "FF0000",
},
},
},
],
"w:rPr": expected,
},
{
"w:uiPriority": {
@ -744,3 +864,4 @@ describe("CharacterStyle", () => {
});
});
});
});

View File

@ -1,8 +1,4 @@
import { EmphasisMarkType } from "file/paragraph/run/emphasis-mark";
import * as formatting from "file/paragraph/run/formatting";
import { RunProperties } from "file/paragraph/run/properties";
import { IFontAttributesProperties } from "file/paragraph/run/run-fonts";
import { UnderlineType } from "file/paragraph/run/underline";
import { IRunStylePropertiesOptions, RunProperties } from "file/paragraph/run/properties";
import { BasedOn, Link, SemiHidden, UiPriority, UnhideWhenUsed } from "./components";
import { Style } from "./style";
@ -11,33 +7,7 @@ export interface IBaseCharacterStyleOptions {
readonly basedOn?: string;
readonly link?: string;
readonly semiHidden?: boolean;
readonly run?: {
readonly size?: number;
readonly bold?: boolean;
readonly italics?: boolean;
readonly smallCaps?: boolean;
readonly allCaps?: boolean;
readonly strike?: boolean;
readonly doubleStrike?: boolean;
readonly subScript?: boolean;
readonly superScript?: boolean;
readonly underline?: {
readonly type?: UnderlineType;
readonly color?: string;
};
readonly emphasisMark?: {
readonly type?: EmphasisMarkType;
};
readonly color?: string;
readonly font?: string | IFontAttributesProperties;
readonly characterSpacing?: number;
readonly highlight?: string;
readonly shadow?: {
readonly type: string;
readonly fill: string;
readonly color: string;
};
};
readonly run?: IRunStylePropertiesOptions;
}
export interface ICharacterStyleOptions extends IBaseCharacterStyleOptions {
@ -50,7 +20,9 @@ export class CharacterStyle extends Style {
constructor(options: ICharacterStyleOptions) {
super({ type: "character", styleId: options.id }, options.name);
this.runProperties = new RunProperties();
this.runProperties = new RunProperties(options.run);
this.root.push(this.runProperties);
this.root.push(new UiPriority(99));
this.root.push(new UnhideWhenUsed());
@ -66,72 +38,5 @@ export class CharacterStyle extends Style {
if (options.semiHidden) {
this.root.push(new SemiHidden());
}
if (options.run) {
if (options.run.size) {
this.runProperties.push(new formatting.Size(options.run.size));
this.runProperties.push(new formatting.SizeComplexScript(options.run.size));
}
if (options.run.bold) {
this.runProperties.push(new formatting.Bold());
}
if (options.run.italics) {
this.runProperties.push(new formatting.Italics());
}
if (options.run.smallCaps) {
this.runProperties.push(new formatting.SmallCaps());
}
if (options.run.allCaps) {
this.runProperties.push(new formatting.Caps());
}
if (options.run.strike) {
this.runProperties.push(new formatting.Strike());
}
if (options.run.doubleStrike) {
this.runProperties.push(new formatting.DoubleStrike());
}
if (options.run.subScript) {
this.runProperties.push(new formatting.SubScript());
}
if (options.run.superScript) {
this.runProperties.push(new formatting.SuperScript());
}
if (options.run.underline) {
this.runProperties.push(new formatting.Underline(options.run.underline.type, options.run.underline.color));
}
if (options.run.emphasisMark) {
this.runProperties.push(new formatting.EmphasisMark(options.run.emphasisMark.type));
}
if (options.run.color) {
this.runProperties.push(new formatting.Color(options.run.color));
}
if (options.run.font) {
this.runProperties.push(new formatting.RunFonts(options.run.font));
}
if (options.run.characterSpacing) {
this.runProperties.push(new formatting.CharacterSpacing(options.run.characterSpacing));
}
if (options.run.highlight) {
this.runProperties.push(new formatting.Highlight(options.run.highlight));
}
if (options.run.shadow) {
this.runProperties.push(new formatting.Shading(options.run.shadow.type, options.run.shadow.fill, options.run.shadow.color));
}
}
}
}

View File

@ -358,21 +358,37 @@ describe("ParagraphStyle", () => {
});
describe("formatting methods: run properties", () => {
it("#size", () => {
const sizeTests = [
{
size: 24,
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
},
{
size: 24,
sizeComplexScript: true,
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
},
{
size: 24,
sizeComplexScript: false,
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }],
},
{
size: 24,
sizeComplexScript: 26,
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 26 } } }],
},
];
sizeTests.forEach(({ size, sizeComplexScript, expected }) => {
it(`#size ${size} cs ${sizeComplexScript}`, () => {
const style = new ParagraphStyle({
id: "myStyleId",
run: {
size: 24,
},
run: { size, sizeComplexScript },
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
},
],
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:rPr": expected }],
});
});
});
@ -543,89 +559,169 @@ describe("ParagraphStyle", () => {
});
});
it("#bold", () => {
const style = new ParagraphStyle({
id: "myStyleId",
run: {
const boldTests = [
{
bold: true,
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
},
{
bold: true,
boldComplexScript: true,
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
},
{
bold: true,
boldComplexScript: false,
expected: [{ "w:b": { _attr: { "w:val": true } } }],
},
];
boldTests.forEach(({ bold, boldComplexScript, expected }) => {
it(`#bold ${bold} cs ${boldComplexScript}`, () => {
const style = new ParagraphStyle({
id: "myStyleId",
run: { bold, boldComplexScript },
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:b": { _attr: { "w:val": true } } }],
},
],
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:rPr": expected }],
});
});
});
it("#italics", () => {
const style = new ParagraphStyle({
id: "myStyleId",
run: {
const italicsTests = [
{
italics: true,
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
},
{
italics: true,
italicsComplexScript: true,
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
},
{
italics: true,
italicsComplexScript: false,
expected: [{ "w:i": { _attr: { "w:val": true } } }],
},
];
italicsTests.forEach(({ italics, italicsComplexScript, expected }) => {
it(`#italics ${italics} cs ${italicsComplexScript}`, () => {
const style = new ParagraphStyle({
id: "myStyleId",
run: { italics, italicsComplexScript },
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:i": { _attr: { "w:val": true } } }],
},
],
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:rPr": expected }],
});
});
});
it("#highlight", () => {
const style = new ParagraphStyle({
id: "myStyleId",
run: {
const highlightTests = [
{
highlight: "005599",
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
},
{
highlight: "005599",
highlightComplexScript: true,
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
},
{
highlight: "005599",
highlightComplexScript: false,
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
},
{
highlight: "005599",
highlightComplexScript: "550099",
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "550099" } } }],
},
];
highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => {
it(`#highlight ${highlight} cs ${highlightComplexScript}`, () => {
const style = new ParagraphStyle({
id: "myStyleId",
run: { highlight, highlightComplexScript },
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
},
],
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:rPr": expected }],
});
});
});
it("#shadow", () => {
const style = new ParagraphStyle({
id: "myStyleId",
run: {
const shadingTests = [
{
shadow: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
expected: [
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
expected: [
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
shadingComplexScript: true,
expected: [
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
shadingComplexScript: false,
expected: [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
shadingComplexScript: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "00FF00",
},
expected: [
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "00FF00" } } },
],
},
];
shadingTests.forEach(({ shadow, shading, shadingComplexScript, expected }) => {
it("#shadow correctly", () => {
const style = new ParagraphStyle({
id: "myStyleId",
run: { shadow, shading, shadingComplexScript },
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
{
"w:rPr": [
{
"w:shd": {
_attr: {
"w:val": "pct10",
"w:fill": "00FFFF",
"w:color": "FF0000",
},
},
},
],
},
],
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:rPr": expected }],
});
});
});

View File

@ -1,19 +1,6 @@
import {
Alignment,
ContextualSpacing,
Indent,
KeepLines,
KeepNext,
OutlineLevel,
ParagraphProperties,
Spacing,
ThematicBreak,
} from "file/paragraph";
import { TabStop, TabStopType } from "file/paragraph/formatting";
import * as formatting from "file/paragraph/run/formatting";
import { IParagraphStylePropertiesOptions, IRunStylePropertiesOptions, ParagraphProperties } from "file/paragraph";
import { RunProperties } from "file/paragraph/run/properties";
import { IParagraphStyleOptions2, IRunStyleOptions } from "../style-options";
import { BasedOn, Link, Next, QuickFormat, SemiHidden, UiPriority, UnhideWhenUsed } from "./components";
import { Style } from "./style";
@ -25,22 +12,25 @@ export interface IBaseParagraphStyleOptions {
readonly semiHidden?: boolean;
readonly uiPriority?: number;
readonly unhideWhenUsed?: boolean;
readonly run?: IRunStyleOptions;
readonly paragraph?: IParagraphStyleOptions2;
readonly paragraph?: IParagraphStylePropertiesOptions;
readonly run?: IRunStylePropertiesOptions;
}
export interface IParagraphStyleOptions extends IBaseParagraphStyleOptions {
readonly id: string;
readonly name?: string;
}
export class ParagraphStyle extends Style {
private readonly paragraphProperties: ParagraphProperties;
private readonly runProperties: RunProperties;
constructor(options: IParagraphStyleOptions) {
super({ type: "paragraph", styleId: options.id }, options.name);
this.paragraphProperties = new ParagraphProperties({});
this.runProperties = new RunProperties();
this.paragraphProperties = new ParagraphProperties(options.paragraph);
this.runProperties = new RunProperties(options.run);
this.root.push(this.paragraphProperties);
this.root.push(this.runProperties);
@ -71,114 +61,5 @@ export class ParagraphStyle extends Style {
if (options.unhideWhenUsed) {
this.root.push(new UnhideWhenUsed());
}
if (options.run) {
if (options.run.size) {
this.runProperties.push(new formatting.Size(options.run.size));
this.runProperties.push(new formatting.SizeComplexScript(options.run.size));
}
if (options.run.bold) {
this.runProperties.push(new formatting.Bold());
}
if (options.run.italics) {
this.runProperties.push(new formatting.Italics());
}
if (options.run.smallCaps) {
this.runProperties.push(new formatting.SmallCaps());
}
if (options.run.allCaps) {
this.runProperties.push(new formatting.Caps());
}
if (options.run.strike) {
this.runProperties.push(new formatting.Strike());
}
if (options.run.doubleStrike) {
this.runProperties.push(new formatting.DoubleStrike());
}
if (options.run.subScript) {
this.runProperties.push(new formatting.SubScript());
}
if (options.run.superScript) {
this.runProperties.push(new formatting.SuperScript());
}
if (options.run.underline) {
this.runProperties.push(new formatting.Underline(options.run.underline.type, options.run.underline.color));
}
if (options.run.emphasisMark) {
this.runProperties.push(new formatting.EmphasisMark(options.run.emphasisMark.type));
}
if (options.run.color) {
this.runProperties.push(new formatting.Color(options.run.color));
}
if (options.run.font) {
this.runProperties.push(new formatting.RunFonts(options.run.font));
}
if (options.run.characterSpacing) {
this.runProperties.push(new formatting.CharacterSpacing(options.run.characterSpacing));
}
if (options.run.highlight) {
this.runProperties.push(new formatting.Highlight(options.run.highlight));
}
if (options.run.shadow) {
this.runProperties.push(new formatting.Shading(options.run.shadow.type, options.run.shadow.fill, options.run.shadow.color));
}
}
if (options.paragraph) {
if (options.paragraph.alignment) {
this.paragraphProperties.push(new Alignment(options.paragraph.alignment));
}
if (options.paragraph.thematicBreak) {
this.paragraphProperties.push(new ThematicBreak());
}
if (options.paragraph.contextualSpacing) {
this.paragraphProperties.push(new ContextualSpacing(options.paragraph.contextualSpacing));
}
if (options.paragraph.rightTabStop) {
this.paragraphProperties.push(new TabStop(TabStopType.RIGHT, options.paragraph.rightTabStop));
}
if (options.paragraph.leftTabStop) {
this.paragraphProperties.push(new TabStop(TabStopType.LEFT, options.paragraph.leftTabStop));
}
if (options.paragraph.indent) {
this.paragraphProperties.push(new Indent(options.paragraph.indent));
}
if (options.paragraph.spacing) {
this.paragraphProperties.push(new Spacing(options.paragraph.spacing));
}
if (options.paragraph.keepNext) {
this.paragraphProperties.push(new KeepNext());
}
if (options.paragraph.keepLines) {
this.paragraphProperties.push(new KeepLines());
}
if (options.paragraph.outlineLevel) {
this.paragraphProperties.push(new OutlineLevel(options.paragraph.outlineLevel));
}
}
}
}

View File

@ -9,7 +9,7 @@ export interface IStylesOptions {
readonly initialStyles?: BaseXmlComponent;
readonly paragraphStyles?: IParagraphStyleOptions[];
readonly characterStyles?: ICharacterStyleOptions[];
readonly importedStyles?: Array<XmlComponent | ParagraphStyle | CharacterStyle | ImportedXmlComponent>;
readonly importedStyles?: (XmlComponent | ParagraphStyle | CharacterStyle | ImportedXmlComponent)[];
}
export class Styles extends XmlComponent {

View File

@ -44,7 +44,7 @@ export interface ITableCellOptions {
readonly color: string;
};
};
readonly children: Array<Paragraph | Table>;
readonly children: (Paragraph | Table)[];
}
export class TableCell extends XmlComponent {
@ -70,6 +70,9 @@ export class TableCell extends XmlComponent {
if (options.verticalMerge) {
this.properties.addVerticalMerge(options.verticalMerge);
} else if (options.rowSpan && options.rowSpan > 1) {
// if cell already have a `verticalMerge`, don't handle `rowSpan`
this.properties.addVerticalMerge(VerticalMergeType.RESTART);
}
if (options.margins) {
@ -84,10 +87,6 @@ export class TableCell extends XmlComponent {
this.properties.addGridSpan(options.columnSpan);
}
if (options.rowSpan && options.rowSpan > 1) {
this.properties.addVerticalMerge(VerticalMergeType.RESTART);
}
if (options.width) {
this.properties.setWidth(options.width.size, options.width.type);
}

View File

@ -271,7 +271,8 @@ describe("TableRow", () => {
});
expect(tableRow.columnIndexToRootIndex(8, true)).to.equal(5);
expect(() => tableRow.columnIndexToRootIndex(9, true)).to.throw(`cell 'columnIndex' should not great than 8`);
// for column 10, just place the new cell at the end of row
expect(tableRow.columnIndexToRootIndex(10, true)).to.equal(5);
});
});
});

View File

@ -83,11 +83,15 @@ export class TableRow extends XmlComponent {
let colIdx = 0;
// Offset because properties is also in root.
let rootIdx = 1;
const endRootIndex = allowEndNewCell ? this.root.length : this.root.length - 1;
while (colIdx <= columnIndex) {
if (rootIdx > endRootIndex) {
if (rootIdx >= this.root.length) {
if (allowEndNewCell) {
// for inserting verticalMerge CONTINUE cell at end of row
return this.root.length;
} else {
throw new Error(`cell 'columnIndex' should not great than ${colIdx - 1}`);
}
}
const cell = this.root[rootIdx] as TableCell;
rootIdx += 1;
colIdx += (cell && cell.options.columnSpan) || 1;

View File

@ -268,10 +268,7 @@ describe("Table", () => {
layout: TableLayoutType.FIXED,
});
const tree = new Formatter().format(table);
expect(tree)
.to.have.property("w:tbl")
.which.is.an("array")
.with.has.length.at.least(1);
expect(tree).to.have.property("w:tbl").which.is.an("array").with.has.length.at.least(1);
expect(tree["w:tbl"][0]).to.deep.equal({
"w:tblPr": [DEFAULT_TABLE_PROPERTIES, BORDERS, WIDTHS, { "w:tblLayout": { _attr: { "w:type": "fixed" } } }],
});
@ -291,10 +288,7 @@ describe("Table", () => {
alignment: AlignmentType.CENTER,
});
const tree = new Formatter().format(table);
expect(tree)
.to.have.property("w:tbl")
.which.is.an("array")
.with.has.length.at.least(1);
expect(tree).to.have.property("w:tbl").which.is.an("array").with.has.length.at.least(1);
expect(tree["w:tbl"][0]).to.deep.equal({
"w:tblPr": [DEFAULT_TABLE_PROPERTIES, BORDERS, WIDTHS, { "w:jc": { _attr: { "w:val": "center" } } }],
});
@ -318,10 +312,7 @@ describe("Table", () => {
layout: TableLayoutType.FIXED,
});
const tree = new Formatter().format(table);
expect(tree)
.to.have.property("w:tbl")
.which.is.an("array")
.with.has.length.at.least(1);
expect(tree).to.have.property("w:tbl").which.is.an("array").with.has.length.at.least(1);
expect(tree["w:tbl"][0]).to.deep.equal({
"w:tblPr": [
DEFAULT_TABLE_PROPERTIES,
@ -355,14 +346,10 @@ describe("Table", () => {
],
});
const tree = new Formatter().format(table);
expect(tree)
.to.have.property("w:tbl")
.which.is.an("array");
expect(tree).to.have.property("w:tbl").which.is.an("array");
const row = tree["w:tbl"].find((x) => x["w:tr"]);
expect(row).not.to.be.undefined;
expect(row["w:tr"])
.to.be.an("array")
.which.has.length.at.least(1);
expect(row["w:tr"]).to.be.an("array").which.has.length.at.least(1);
expect(row["w:tr"].find((x) => x["w:tc"])).to.deep.equal({
"w:tc": [
{
@ -487,10 +474,7 @@ describe("Table", () => {
},
});
const tree = new Formatter().format(table);
expect(tree)
.to.have.property("w:tbl")
.which.is.an("array")
.with.has.length.at.least(1);
expect(tree).to.have.property("w:tbl").which.is.an("array").with.has.length.at.least(1);
expect(tree["w:tbl"][0]).to.deep.equal({
"w:tblPr": [
DEFAULT_TABLE_PROPERTIES,

View File

@ -79,25 +79,26 @@ export class Table extends XmlComponent {
}
rows.forEach((row, rowIndex) => {
row.cells.forEach((cell, cellIndex) => {
if (rowIndex === rows.length - 1) {
// don't process the end row
return;
}
let columnIndex = 0;
row.cells.forEach((cell) => {
// Row Span has to be added in this method and not the constructor because it needs to know information about the column which happens after Table Cell construction
// Row Span of 1 will crash word as it will add RESTART and not a corresponding CONTINUE
if (cell.options.rowSpan && cell.options.rowSpan > 1) {
const columnIndex = row.rootIndexToColumnIndex(cellIndex + 1);
const startRowIndex = rowIndex + 1;
const endRowIndex = rowIndex + (cell.options.rowSpan - 1);
for (let i = startRowIndex; i <= endRowIndex; i++) {
rows[i].addCellToColumnIndex(
new TableCell({
const continueCell = new TableCell({
// the inserted CONTINUE cell has rowSpan, and will be handled when process the next row
rowSpan: cell.options.rowSpan - 1,
columnSpan: cell.options.columnSpan,
borders: cell.options.borders,
children: [],
verticalMerge: VerticalMergeType.CONTINUE,
}),
columnIndex,
);
}
});
rows[rowIndex + 1].addCellToColumnIndex(continueCell, columnIndex);
}
columnIndex += cell.options.columnSpan || 1;
});
});

View File

@ -6,7 +6,7 @@ export const EMPTY_OBJECT = Object.seal({});
export abstract class XmlComponent extends BaseXmlComponent {
// tslint:disable-next-line:readonly-keyword no-any
protected root: Array<BaseXmlComponent | string | any>;
protected root: (BaseXmlComponent | string | any)[];
constructor(rootKey: string) {
super(rootKey);

View File

@ -17,8 +17,8 @@ const schemeToType = {
};
interface IDocumentRefs {
readonly headers: Array<{ readonly id: number; readonly type: HeaderReferenceType }>;
readonly footers: Array<{ readonly id: number; readonly type: FooterReferenceType }>;
readonly headers: { readonly id: number; readonly type: HeaderReferenceType }[];
readonly footers: { readonly id: number; readonly type: FooterReferenceType }[];
}
enum RelationshipType {
@ -99,7 +99,7 @@ export class ImportDotx {
return { type: reference.type, footer: wrapper };
})
.filter((x) => !!x) as Array<Promise<IDocumentFooter>>;
.filter((x) => !!x) as Promise<IDocumentFooter>[];
return Promise.all(result);
}
@ -134,7 +134,7 @@ export class ImportDotx {
return { type: reference.type, header: wrapper };
})
.filter((x) => !!x) as Array<Promise<IDocumentHeader>>;
.filter((x) => !!x) as Promise<IDocumentHeader>[];
return Promise.all(result);
}