Compare commits
339 Commits
Author | SHA1 | Date | |
---|---|---|---|
9e8d67c4a9 | |||
cbbc4a80e7 | |||
1f51fd7a31 | |||
120d97bc64 | |||
a531713214 | |||
564a055316 | |||
6c29c4fb1f | |||
552580bc47 | |||
0998d43b8f | |||
b08354494c | |||
f54b9a197d | |||
2d4412ce51 | |||
72c32378c5 | |||
0c79c0ac83 | |||
1dfc27ba08 | |||
58dc6fe389 | |||
34c3285426 | |||
a5afce458d | |||
2f687125d1 | |||
5c1a731314 | |||
622331ad24 | |||
3a593a53d3 | |||
31a9111667 | |||
373c890354 | |||
723d76d06f | |||
a24d745d75 | |||
90891cfafd | |||
29f890918c | |||
b0f8f8ddbd | |||
9c89c1ab59 | |||
7a9cb92955 | |||
77edf8862b | |||
bb1604cd8f | |||
816cb54b14 | |||
920bd3c175 | |||
a2a01edc24 | |||
193d0c4239 | |||
d8cc11c5ab | |||
b874051f32 | |||
272e2496f4 | |||
43c199c725 | |||
0e975b3d66 | |||
c6ab47e838 | |||
48c17d51bb | |||
df2315ae4a | |||
e67fd9cb2b | |||
639842332f | |||
2cb7d44a77 | |||
6cd62418a5 | |||
f3ba11b21c | |||
51b1e08a35 | |||
b0d0041ff9 | |||
55c5dd818a | |||
3bebe0ad59 | |||
c5c67fd92a | |||
1486d3de56 | |||
6d8eea40be | |||
0922d49cd3 | |||
f76471f553 | |||
973177676e | |||
5aa878b901 | |||
b99d1fc129 | |||
1590f9ac70 | |||
6c772b956c | |||
9d09ff3518 | |||
d9ad69b7cd | |||
e23d230acb | |||
5ca872cb07 | |||
b179facca2 | |||
db60270e1f | |||
3e2130bc80 | |||
1b336785b4 | |||
6eed0fe0f5 | |||
2f8f69b0c4 | |||
4580a65a84 | |||
661c3e21e4 | |||
32646f8806 | |||
231428852e | |||
46ddf77342 | |||
441afe8c97 | |||
ca9ce01f56 | |||
2b9ce0febd | |||
18760387db | |||
28dabb06cb | |||
50fc9b6274 | |||
367518d504 | |||
efd89f24e6 | |||
4fd2b6f1d3 | |||
5ae541a40d | |||
c9fb9a827d | |||
918faf59c3 | |||
3a9fa49fbb | |||
218a08d793 | |||
416a239708 | |||
d1bdbd397a | |||
a6077b8f16 | |||
8f133ff93a | |||
1f12e159ef | |||
83a7f4664d | |||
728aefc4a7 | |||
40730548bb | |||
d6c2c96757 | |||
b22f565dd0 | |||
41eeac1b05 | |||
18a5f22f4c | |||
2a1161d857 | |||
4b6d3c3e3c | |||
14a1d62148 | |||
1f98d9461b | |||
8dc590746b | |||
de5f5c9a77 | |||
788205b14d | |||
ca5f6a56a5 | |||
bf8dfe6604 | |||
b0ee0305fb | |||
8f6984580a | |||
383c6d769f | |||
9f38a4f48a | |||
475a132ce0 | |||
4111413074 | |||
50209bb435 | |||
b37d2c141d | |||
d19cdcae0c | |||
3f652764e9 | |||
7b5cf690e2 | |||
d674523139 | |||
db7f27a88c | |||
677300e34f | |||
da0fa86345 | |||
78b310e1dd | |||
4541d7c977 | |||
9271b2d11a | |||
942c6d028c | |||
f717126145 | |||
abd5ace85c | |||
a37ff90bd7 | |||
9998ddfcc9 | |||
12ed54c9fd | |||
612c024b1f | |||
12ad545fe8 | |||
6d0f6a61d7 | |||
e3bcad6d3c | |||
f7c372a85c | |||
bb277a4a76 | |||
52b78a583e | |||
20ab36191b | |||
dbfb80e660 | |||
421f4471de | |||
04ea2b2dc9 | |||
93a2404343 | |||
149cda9a3b | |||
b9465b2a20 | |||
ab07f2ecbe | |||
c70c147afe | |||
8db52212ab | |||
d021a3995e | |||
1ea347ed21 | |||
0725e54271 | |||
3b6aca2451 | |||
249e44532f | |||
d3af3663ec | |||
3a1a21e498 | |||
e5bfa99b92 | |||
bf18154fcf | |||
5efc9e383b | |||
6505119d3b | |||
600f97de29 | |||
a1c21d2143 | |||
39066fb5f2 | |||
8fd99052fb | |||
4e671eb512 | |||
2677c4a4ec | |||
e19890e27a | |||
28556f277b | |||
50e08f198c | |||
7639b60b15 | |||
765a9686d8 | |||
ff443aa7c4 | |||
28fc328cb3 | |||
083f121683 | |||
2f57d3be84 | |||
7b47dadede | |||
8ad5485347 | |||
8df78e45d9 | |||
39cef5e61d | |||
5552f9d834 | |||
d13795696d | |||
f640f17fa6 | |||
494a32d45d | |||
8040a455be | |||
f091cff7c9 | |||
401ef7336b | |||
18134519be | |||
7980f14efb | |||
40d6a41305 | |||
9cfd835171 | |||
95a9f592eb | |||
61411fd0f3 | |||
a84eb16392 | |||
3355a6f472 | |||
071a8ea9f7 | |||
ea3777d28b | |||
3346b97ee7 | |||
2b834a75a8 | |||
21df53d547 | |||
9143c1c2c1 | |||
83cab7563d | |||
b05fbe7f6e | |||
e6a57738f4 | |||
cc6e35165a | |||
7791ddf76e | |||
4742cf0f3f | |||
8858970491 | |||
f7d18bfead | |||
0ce1b7fa15 | |||
4633592711 | |||
54697ab6b1 | |||
1eed844b9a | |||
dd89fe2463 | |||
9c66db97ff | |||
fea6afdfe4 | |||
6ec2e742ef | |||
3f80b054fc | |||
96d81873d8 | |||
c429ae9920 | |||
6b4e769f48 | |||
b67a9de0e9 | |||
ac9f65a068 | |||
7e8ebb2af2 | |||
f53fe2f881 | |||
6fdd88527a | |||
b6f431e14d | |||
23dee01f06 | |||
5532f91423 | |||
c849d5f3e5 | |||
3f3fd05cb1 | |||
a5bedf9a5b | |||
9d9dd62f00 | |||
a466578467 | |||
3a9420fedf | |||
8ac19a83b2 | |||
9f0b2f7074 | |||
e02ac43c07 | |||
97f76fb62c | |||
3508fd97ec | |||
90f6f68693 | |||
733775d3b9 | |||
f3aa6a9203 | |||
ffdcc7baca | |||
a1e20f4c9a | |||
ccffdad4c0 | |||
2fb5845501 | |||
ddd84a1765 | |||
048ae6a58c | |||
fafa54e4c9 | |||
60dbb32e9e | |||
52b07fd9cb | |||
f6a13aed86 | |||
2da3ba0262 | |||
e08c7cbbfb | |||
a6de5d8a21 | |||
8ec5bc05e0 | |||
6e0c12afb3 | |||
466e880bfc | |||
00a20b7cfc | |||
f27c95191b | |||
c140d2c37c | |||
3a42f2a2f0 | |||
00efedaa09 | |||
72e3d229dc | |||
b12d6ef484 | |||
8ee6fd3e67 | |||
808c5b00a0 | |||
4de6b51e76 | |||
0a8feca6ab | |||
4ca81df401 | |||
8b463b3bb6 | |||
17d696e33a | |||
2f59867db6 | |||
bf1f702e5a | |||
1cff104bae | |||
4805efad2e | |||
12e2ae9e91 | |||
c07b5cf709 | |||
0684738ec2 | |||
f2b50478bf | |||
fc71ebdfef | |||
10114bb12d | |||
7cd8864fb9 | |||
e6d4741955 | |||
4d7387524c | |||
5fe4405d76 | |||
baf0f17bd6 | |||
8e911698a5 | |||
0eb36be053 | |||
79dffc873a | |||
26ee12759c | |||
2e00634bc4 | |||
d63e6bf6b1 | |||
a95366e54e | |||
1c376abeb6 | |||
353d888abd | |||
b6bd532295 | |||
c5b004166d | |||
2f43600daf | |||
b39c7ce323 | |||
5b00279996 | |||
d1d1e01aff | |||
980bc597e2 | |||
a05c5edd49 | |||
985452f5f4 | |||
482674b3b3 | |||
fcbfed9068 | |||
a9167b4809 | |||
f1b176670c | |||
11ce9a5206 | |||
146d0ad9e5 | |||
385ad92331 | |||
571f8b526b | |||
7688aa99f6 | |||
4994bca34c | |||
1a3603dbfb | |||
aedfca377f | |||
7926f6c189 | |||
e9a007d446 | |||
e382dbff84 | |||
e08be3d7a4 | |||
03c4190c2c | |||
12c1f82efe | |||
b1711ae293 | |||
a367951d07 | |||
c55f82c425 | |||
d1044d262e | |||
010fde6258 | |||
8d83219bb6 | |||
a710483918 | |||
4f8ecf631b | |||
a6a8012b39 | |||
d5b6225a90 |
6
.gitignore
vendored
6
.gitignore
vendored
@ -57,3 +57,9 @@ yarn.lock
|
||||
|
||||
# Documents
|
||||
My Document.docx
|
||||
|
||||
# Temporary folder
|
||||
tmp
|
||||
|
||||
# nyc
|
||||
.nyc_output
|
||||
|
25
.nycrc
Normal file
25
.nycrc
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"check-coverage": true,
|
||||
"lines": 87.54,
|
||||
"functions": 83.61,
|
||||
"branches": 72.57,
|
||||
"statements": 87.32,
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts"
|
||||
],
|
||||
"reporter": [
|
||||
"lcov",
|
||||
"text",
|
||||
"json"
|
||||
],
|
||||
"extension": [
|
||||
".ts"
|
||||
],
|
||||
"cache": true,
|
||||
"all": true,
|
||||
"instrument": false,
|
||||
"sourceMap": true
|
||||
}
|
17
.travis.yml
17
.travis.yml
@ -3,12 +3,14 @@ node_js:
|
||||
- 9
|
||||
install:
|
||||
- npm install
|
||||
- npm install -g codecov
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
- npm run test.coverage
|
||||
- npm run style
|
||||
- npm run build
|
||||
- npm run ts-node -- ./demo/demo1.ts
|
||||
- npm run e2e "My Document.docx"
|
||||
- npm run ts-node -- ./demo/demo2.ts
|
||||
- npm run ts-node -- ./demo/demo3.ts
|
||||
- npm run ts-node -- ./demo/demo4.ts
|
||||
@ -18,6 +20,7 @@ script:
|
||||
- npm run ts-node -- ./demo/demo8.ts
|
||||
- npm run ts-node -- ./demo/demo9.ts
|
||||
- npm run ts-node -- ./demo/demo10.ts
|
||||
- npm run e2e "My Document.docx"
|
||||
- npm run ts-node -- ./demo/demo11.ts
|
||||
- npm run ts-node -- ./demo/demo12.ts
|
||||
- npm run ts-node -- ./demo/demo13.ts
|
||||
@ -32,12 +35,24 @@ script:
|
||||
- npm run ts-node -- ./demo/demo22.ts
|
||||
- npm run ts-node -- ./demo/demo23.ts
|
||||
- npm run ts-node -- ./demo/demo24.ts
|
||||
# - npm run ts-node -- ./demo/demo25.ts
|
||||
- npm run ts-node -- ./demo/demo26.ts
|
||||
- npm run ts-node -- ./demo/demo27.ts
|
||||
- npm run ts-node -- ./demo/demo28.ts
|
||||
- npm run ts-node -- ./demo/demo29.ts
|
||||
- npm run ts-node -- ./demo/demo30.ts
|
||||
- npm run ts-node -- ./demo/demo31.ts
|
||||
- npm run ts-node -- ./demo/demo32.ts
|
||||
# - npm run e2e "My Document.docx" // Need to fix
|
||||
- npm run ts-node -- ./demo/demo33.ts
|
||||
- npm run ts-node -- ./demo/demo34.ts
|
||||
after_failure:
|
||||
- "cat /home/travis/builds/dolanmiu/docx/npm-debug.log"
|
||||
after_success:
|
||||
- npm run typedoc
|
||||
- echo "docx.js.org" > docs/.nojekyll
|
||||
- echo "docx.js.org" > docs/CNAME
|
||||
- codecov
|
||||
deploy:
|
||||
provider: pages
|
||||
skip-cleanup: true
|
||||
|
24
.vscode/tasks.json
vendored
Normal file
24
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "typescript",
|
||||
"tsconfig": "tsconfig.json",
|
||||
"option": "watch",
|
||||
"problemMatcher": [
|
||||
"$tsc-watch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "ts-node",
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
22
README.md
22
README.md
@ -15,6 +15,7 @@
|
||||
[![Known Vulnerabilities][snky-image]][snky-url]
|
||||
[![Chat on Gitter][gitter-image]][gitter-url]
|
||||
[![PRs Welcome][pr-image]][pr-url]
|
||||
[![codecov][codecov-image]][codecov-url]
|
||||
|
||||
<p align="center">
|
||||
<img src="https://i.imgur.com/H5FA1Qy.gif" alt="drawing" width="800"/>
|
||||
@ -24,11 +25,15 @@
|
||||
|
||||
## Browser
|
||||
|
||||
Here are examples of `docx` being used with basic `HTML/JS` in a browser environment.
|
||||
Here are examples of `docx` being used with basic `HTML/JS` in a browser environment:
|
||||
|
||||
* https://codepen.io/anon/pen/dqoVgQ
|
||||
* https://jsfiddle.net/3xhezb5w/2
|
||||
|
||||
Here is an example of `docx` working in `Angular`:
|
||||
|
||||
* https://stackblitz.com/edit/angular-afvxtz
|
||||
|
||||
## Node
|
||||
|
||||
Press `endpoint` on the `RunKit` website:
|
||||
@ -59,8 +64,17 @@ Check the `examples` section in the [documentation](https://docx.js.org/#/exampl
|
||||
|
||||
Read the contribution guidelines [here](https://docx.js.org/#/contribution-guidelines).
|
||||
|
||||
# Used by
|
||||
|
||||
[<img src="https://i.imgur.com/zy5qWmI.png" alt="drawing" height="200"/>](https://hfour.com/)
|
||||
[<img src="https://i.imgur.com/OYP5tgS.png" alt="drawing" height="200"/>](https://fuzzproductions.com/)
|
||||
[<img src="https://i.imgur.com/zUDMfZ3.png" alt="drawing" height="200"/>](https://www.mettzer.com/)
|
||||
|
||||
---
|
||||
|
||||
[![patreon][patreon-image]][patreon-url]
|
||||
[![browserstack][browserstack-image]][browserstack-url]
|
||||
|
||||
Made with 💖
|
||||
|
||||
[npm-image]: https://badge.fury.io/js/docx.svg
|
||||
@ -77,3 +91,9 @@ Made with 💖
|
||||
[gitter-url]: https://gitter.im/docx-lib/Lobby
|
||||
[pr-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
|
||||
[pr-url]: http://makeapullrequest.com
|
||||
[codecov-image]: https://codecov.io/gh/dolanmiu/docx/branch/master/graph/badge.svg
|
||||
[codecov-url]: https://codecov.io/gh/dolanmiu/docx
|
||||
[patreon-image]: https://user-images.githubusercontent.com/2917613/51251459-4e880480-1991-11e9-92bf-38b96675a9e2.png
|
||||
[patreon-url]: https://www.patreon.com/dolanmiu
|
||||
[browserstack-image]: https://user-images.githubusercontent.com/2917613/54233552-128e9d00-4505-11e9-88fb-025a4e04007c.png
|
||||
[browserstack-url]: https://www.browserstack.com
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Add images to header and footer
|
||||
// Generate a CV
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph, TextRun } from "../build";
|
||||
@ -233,7 +233,7 @@ class DocumentCreator {
|
||||
|
||||
public createRoleText(roleText: string): Paragraph {
|
||||
const paragraph = new Paragraph();
|
||||
const role = new TextRun(roleText).italic();
|
||||
const role = new TextRun(roleText).italics();
|
||||
|
||||
paragraph.addRun(role);
|
||||
|
||||
|
@ -84,8 +84,7 @@ doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph")
|
||||
.basedOn("Normal");
|
||||
|
||||
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||
doc
|
||||
.createParagraph("HEADING")
|
||||
doc.createParagraph("HEADING")
|
||||
.heading1()
|
||||
.center();
|
||||
|
||||
@ -107,12 +106,15 @@ doc.createParagraph("Sir,").style("normalPara");
|
||||
|
||||
doc.createParagraph("BRIEF DESCRIPTION").style("normalPara");
|
||||
|
||||
const table = new Table(4, 4);
|
||||
const table = new Table({
|
||||
rows: 4,
|
||||
columns: 4,
|
||||
});
|
||||
table
|
||||
.getRow(0)
|
||||
.getCell(0)
|
||||
.addContent(new Paragraph("Pole No."));
|
||||
// table.Properties.width = 10000;
|
||||
.addParagraph(new Paragraph("Pole No."));
|
||||
|
||||
doc.addTable(table);
|
||||
|
||||
const arrboth = [
|
||||
@ -127,10 +129,7 @@ const arrboth = [
|
||||
];
|
||||
|
||||
arrboth.forEach((item) => {
|
||||
const para = doc.createParagraph();
|
||||
para.addImage(doc.createImage(fs.readFileSync(item.image)));
|
||||
// para.Properties.width = 60;
|
||||
// para.Properties.height = 90;
|
||||
doc.createImage(fs.readFileSync(item.image));
|
||||
doc.createParagraph(item.comment).style("normalPara2");
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Multiple sections and headers
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, PageNumberFormat, PageOrientation, Paragraph } from "../build";
|
||||
import { Document, Packer, PageNumberFormat, PageOrientation, Paragraph, TextRun } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
@ -15,8 +15,12 @@ const footer = doc.createFooter();
|
||||
footer.createParagraph("Footer on another page");
|
||||
|
||||
doc.addSection({
|
||||
headerId: header.Header.ReferenceId,
|
||||
footerId: footer.Footer.ReferenceId,
|
||||
headers: {
|
||||
default: header,
|
||||
},
|
||||
footers: {
|
||||
default: footer,
|
||||
},
|
||||
pageNumberStart: 1,
|
||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||
});
|
||||
@ -24,8 +28,12 @@ doc.addSection({
|
||||
doc.createParagraph("hello");
|
||||
|
||||
doc.addSection({
|
||||
headerId: header.Header.ReferenceId,
|
||||
footerId: footer.Footer.ReferenceId,
|
||||
headers: {
|
||||
default: header,
|
||||
},
|
||||
footers: {
|
||||
default: footer,
|
||||
},
|
||||
pageNumberStart: 1,
|
||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||
orientation: PageOrientation.LANDSCAPE,
|
||||
@ -33,6 +41,42 @@ doc.addSection({
|
||||
|
||||
doc.createParagraph("hello in landscape");
|
||||
|
||||
const header2 = doc.createHeader();
|
||||
const pageNumber = new TextRun("Page number: ").pageNumber();
|
||||
header2.createParagraph().addRun(pageNumber);
|
||||
|
||||
doc.addSection({
|
||||
headers: {
|
||||
default: header2,
|
||||
},
|
||||
orientation: PageOrientation.PORTRAIT,
|
||||
});
|
||||
|
||||
doc.createParagraph("Page number in the header must be 2, because it continues from the previous section.");
|
||||
|
||||
doc.addSection({
|
||||
headers: {
|
||||
default: header2,
|
||||
},
|
||||
pageNumberFormatType: PageNumberFormat.UPPER_ROMAN,
|
||||
orientation: PageOrientation.PORTRAIT,
|
||||
});
|
||||
|
||||
doc.createParagraph(
|
||||
"Page number in the header must be III, because it continues from the previous section, but is defined as upper roman.",
|
||||
);
|
||||
|
||||
doc.addSection({
|
||||
headers: {
|
||||
default: header2,
|
||||
},
|
||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||
pageNumberStart: 25,
|
||||
orientation: PageOrientation.PORTRAIT,
|
||||
});
|
||||
|
||||
doc.createParagraph("Page number in the header must be 25, because it is defined to start at 25 and to be decimal in this section.");
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
|
@ -5,11 +5,14 @@ import { BorderStyle, Document, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable(4, 4);
|
||||
const table = doc.createTable({
|
||||
rows: 4,
|
||||
columns: 4,
|
||||
});
|
||||
table
|
||||
.getCell(2, 2)
|
||||
.addContent(new Paragraph("Hello"))
|
||||
.CellProperties.Borders.addTopBorder(BorderStyle.DASH_DOT_STROKED, 3, "red")
|
||||
.addParagraph(new Paragraph("Hello"))
|
||||
.Borders.addTopBorder(BorderStyle.DASH_DOT_STROKED, 3, "red")
|
||||
.addBottomBorder(BorderStyle.DOUBLE, 3, "blue")
|
||||
.addStartBorder(BorderStyle.DOT_DOT_DASH, 3, "green")
|
||||
.addEndBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000");
|
||||
|
@ -16,7 +16,7 @@ paragraph2.addRun(textRun2);
|
||||
doc.addParagraph(paragraph2);
|
||||
|
||||
const paragraph3 = new Paragraph().bidirectional();
|
||||
const textRun3 = new TextRun("שלום עולם").italic().rightToLeft();
|
||||
const textRun3 = new TextRun("שלום עולם").italics().rightToLeft();
|
||||
paragraph3.addRun(textRun3);
|
||||
doc.addParagraph(paragraph3);
|
||||
|
||||
|
@ -8,11 +8,11 @@ const doc = new Document();
|
||||
const paragraph = new Paragraph("Hello World");
|
||||
doc.addParagraph(paragraph);
|
||||
|
||||
const image = Media.addImage(doc, "./demo/images/image1.jpeg");
|
||||
const image2 = Media.addImage(doc, "./demo/images/dog.png");
|
||||
const image3 = Media.addImage(doc, "./demo/images/cat.jpg");
|
||||
const image4 = Media.addImage(doc, "./demo/images/parrots.bmp");
|
||||
const image5 = Media.addImage(doc, "./demo/images/pizza.gif");
|
||||
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||
const image2 = Media.addImage(doc, fs.readFileSync("./demo/images/dog.png"));
|
||||
const image3 = Media.addImage(doc, fs.readFileSync("./demo/images/cat.jpg"));
|
||||
const image4 = Media.addImage(doc, fs.readFileSync("./demo/images/parrots.bmp"));
|
||||
const image5 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));
|
||||
|
||||
const imageBase64Data = `iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAACzVBMVEUAAAAAAAAAAAAAAAA/AD8zMzMqKiokJCQfHx8cHBwZGRkuFxcqFSonJyckJCQiIiIfHx8eHh4cHBwoGhomGSYkJCQhISEfHx8eHh4nHR0lHBwkGyQjIyMiIiIgICAfHx8mHh4lHh4kHR0jHCMiGyIhISEgICAfHx8lHx8kHh4jHR0hHCEhISEgICAlHx8kHx8jHh4jHh4iHSIhHCEhISElICAkHx8jHx8jHh4iHh4iHSIhHSElICAkICAjHx8jHx8iHh4iHh4hHiEhHSEkICAjHx8iHx8iHx8hHh4hHiEkHSEjHSAjHx8iHx8iHx8hHh4kHiEkHiEjHSAiHx8hHx8hHh4kHiEjHiAjHSAiHx8iHx8hHx8kHh4jHiEjHiAjHiAiICAiHx8kHx8jHh4jHiEjHiAiHiAiHSAiHx8jHx8jHx8jHiAiHiAiHiAiHSAiHx8jHx8jHx8iHiAiHiAiHiAjHx8jHx8jHx8jHx8iHiAiHiAiHiAjHx8jHx8jHx8iHx8iHSAiHiAjHiAjHx8jHx8hHx8iHx8iHyAiHiAjHiAjHiAjHh4hHx8iHx8iHx8iHyAjHSAjHiAjHiAjHh4hHx8iHx8iHx8jHyAjHiAhHh4iHx8iHx8jHyAjHSAjHSAhHiAhHh4iHx8iHx8jHx8jHyAjHSAjHSAiHh4iHh4jHx8jHx8jHyAjHyAhHSAhHSAiHh4iHh4jHx8jHx8jHyAhHyAhHSAiHSAiHh4jHh4jHx8jHx8jHyAhHyAhHSAiHSAjHR4jHh4jHx8jHx8hHyAhHyAiHSAjHSAjHR4jHh4jHx8hHx8hHyAhHyAiHyAjHSAjHR4jHR4hHh4hHx8hHyAiHyAjHyAjHSAjHR4jHR4hHh4hHx8hHyAjHyAjHyAjHSAjHR4hHR4hHR4hHx8iHyAjHyAjHyAjHSAhHR4hHR4hHR4hHx8jHyAjHyAjHyAjHyC9S2xeAAAA7nRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFxgZGhscHR4fICEiIyQlJicoKSorLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZISUpLTE1OUFFSU1RVVllaW1xdXmBhYmNkZWZnaGprbG1ub3Byc3R1dnd4eXp8fn+AgYKDhIWGiImKi4yNj5CRkpOUlZaXmJmam5ydnp+goaKjpKaoqqusra6vsLGys7S1tri5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+fkZpVQAABcBJREFUGBntwftjlQMcBvDnnLNL22qzJjWlKLHFVogyty3SiFq6EZliqZGyhnSxsLlMRahYoZKRFcul5dKFCatYqWZaNKvWtrPz/A2+7/b27qRzec/lPfvl/XxgMplMJpPJZDKZAtA9HJ3ppnIez0KnSdtC0RCNznHdJrbrh85wdSlVVRaEXuoGamYi5K5430HNiTiEWHKJg05eRWgNfKeV7RxbqUhGKPV/207VupQ8is0IoX5vtFC18SqEHaK4GyHTZ2kzVR8PBTCO4oANIZL4ShNVZcOhKKeYg9DoWdhI1ec3os2VFI0JCIUez5+i6st0qJZRrEAIJCw+QdW223BG/EmKwTBc/IJ/qfp2FDrkUnwFo8U9dZyqnaPhxLqfYjyM1S3vb6p+GGOBszsojoTDSDFz6qj66R4LzvYJxVMwUNRjf1H1ywQr/megg2RzLximy8waqvbda8M5iijegVEiHjlM1W/3h+FcXesphsMY4dMOUnUgOxyuPEzxPQwRNvV3qg5Nj4BreyimwADWe/dRVTMjEm6MoGLzGwtystL6RyOY3qSqdlYU3FpLZw1VW0sK5943MvUCKwJ1noNtjs6Ohge76Zq9ZkfpigU5WWkDYuCfbs1U5HWFR8/Qq4a9W0uK5k4ZmdrTCl8spGIePLPlbqqsc1Afe83O0hULc8alDYiBd7ZyitYMeBfR55rR2fOKP6ioPk2dGvZ+UVI0d8rtqT2tcCexlqK2F3wRn5Q+YVbBqrLKOupkr9lZujAOrmS0UpTb4JeIPkNHZ+cXr6uoPk2vyuBSPhWLEKj45PQJuQWryyqP0Z14uGLdROHIRNBEXDR09EP5r62rOHCazhrD4VKPwxTH+sIA3ZPTJ+YuWV22n+IruHFDC8X2CBjnPoolcGc2FYUwzmsUWXDHsoGKLBhmN0VvuBVfTVE/AAbpaid5CB4MbaLY1QXGuIViLTyZQcVyGGMuxWPwaA0Vk2GI9RRp8Ci2iuLkIBjhT5LNUfAspZFiTwyC72KK7+DNg1SsRvCNp3gZXq2k4iEEXSHFJHgVXUlxejCCbTvFAHiXdIJiXxyCK7KJ5FHoMZGK9xBcwyg2QpdlVMxEUM2iyIMuXXZQNF+HswxMsSAAJRQjoE//eoqDCXBSTO6f1xd+O0iyNRY6jaWi1ALNYCocZROj4JdEikroVkjFk9DcStXxpdfCD2MoXodu4RUU9ptxxmXssOfxnvDVcxRTod9FxyhqLoAqis5aPhwTDp9spRgEH2Q6KLbYoKqlaKTm6Isp0C/sJMnjFvhiERXPQvUNRe9p29lhR04CdBpC8Sl8YiuncIxEuzUUg4Dkgj+paVozygY9plPMh28SaymO9kabAopREGF3vt9MzeFFl8G7lRSZ8FFGK8XX4VA8QjEd7XrM3M0OXz8YCy+qKBLgq3wqnofiTorF0Ax56Rg1J1elW+BBAsVe+My6iYq7IK6keBdOIseV2qn5Pb8f3MqkWAXf9ThM8c8lAOIotuFsF875lRrH5klRcG0+xcPwQ1oLxfeRAP4heQTnGL78X2rqlw2DK59SXAV/zKaiGMAuko5InCt68mcOan5+ohf+z1pP8lQY/GHZQMV4YD3FpXDp4qerqbF/lBWBswyi+AL+ia+maLgcRRQj4IYlY/UpauqKBsPJAxQF8NM1TRQ/RudSPAD34rK3scOuR8/HGcspxsJfOVS8NZbiGXiUtPgINU3v3WFDmx8pEuG3EiqKKVbCC1vm2iZqap5LAtCtleQf8F9sFYWDohzeJczYyQ4V2bEZFGsQgJRGqqqhS2phHTWn9lDkIhBTqWqxQZ+IsRvtdHY9AvI2VX2hW68nfqGmuQsCEl3JdjfCF8OW1bPdtwhQ0gm2mQzfRE3a7KCYj0BNZJs8+Kxf/r6WtTEI2FIqlsMfFgRB5A6KUnSe/vUkX0AnuvUIt8SjM1m6wWQymUwmk8lkMgXRf5vi8rLQxtUhAAAAAElFTkSuQmCC`;
|
||||
const image6 = Media.addImage(doc, Buffer.from(imageBase64Data, "base64"), 100, 100);
|
||||
|
@ -5,11 +5,14 @@ import { Document, Media, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable(4, 4);
|
||||
table.getCell(2, 2).addContent(new Paragraph("Hello"));
|
||||
const table = doc.createTable({
|
||||
rows: 4,
|
||||
columns: 4,
|
||||
});
|
||||
table.getCell(2, 2).addParagraph(new Paragraph("Hello"));
|
||||
|
||||
const image = Media.addImage(doc, "./demo/images/image1.jpeg");
|
||||
table.getCell(1, 1).addContent(image.Paragraph);
|
||||
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||
table.getCell(1, 1).addParagraph(image.Paragraph);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
// Custom styles using JavaScript configuration
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer } from "../build";
|
||||
|
||||
@ -30,5 +32,4 @@ const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
console.log("Document created successfully at project root!");
|
||||
});
|
||||
|
43
demo/demo28.ts
Normal file
43
demo/demo28.ts
Normal file
@ -0,0 +1,43 @@
|
||||
// Table of contents
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { File, Packer, Paragraph, StyleLevel, TableOfContents } from "../build";
|
||||
|
||||
const doc = new File();
|
||||
|
||||
// The first argument is an ID you use to apply the style to paragraphs
|
||||
// The second argument is a human-friendly name to show in the UI
|
||||
doc.Styles.createParagraphStyle("MySpectacularStyle", "My Spectacular Style")
|
||||
.basedOn("Heading1")
|
||||
.next("Heading1")
|
||||
.color("990000")
|
||||
.italics();
|
||||
|
||||
// WordprocessingML docs for TableOfContents can be found here:
|
||||
// http://officeopenxml.com/WPtableOfContents.php
|
||||
|
||||
// Let's define the properties for generate a TOC for heading 1-5 and MySpectacularStyle,
|
||||
// making the entries be hyperlinks for the paragraph
|
||||
const toc = new TableOfContents("Summary", {
|
||||
hyperlink: true,
|
||||
headingStyleRange: "1-5",
|
||||
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)],
|
||||
});
|
||||
|
||||
doc.addTableOfContents(toc);
|
||||
|
||||
doc.addParagraph(new Paragraph("Header #1").heading1().pageBreakBefore());
|
||||
doc.addParagraph(new Paragraph("I'm a little text very nicely written.'"));
|
||||
|
||||
doc.addParagraph(new Paragraph("Header #2").heading1().pageBreakBefore());
|
||||
doc.addParagraph(new Paragraph("I'm a other text very nicely written.'"));
|
||||
doc.addParagraph(new Paragraph("Header #2.1").heading2());
|
||||
doc.addParagraph(new Paragraph("I'm a another text very nicely written.'"));
|
||||
|
||||
doc.addParagraph(new Paragraph("My Spectacular Style #1").style("MySpectacularStyle").pageBreakBefore());
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
46
demo/demo29.ts
Normal file
46
demo/demo29.ts
Normal file
@ -0,0 +1,46 @@
|
||||
// Numbered lists
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Indent, Numbering, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const numbering = new Numbering();
|
||||
|
||||
const abstractNum = numbering.createAbstractNumbering();
|
||||
abstractNum.createLevel(0, "upperRoman", "%1", "start").addParagraphProperty(new Indent({ left: 720, hanging: 260 }));
|
||||
|
||||
const concrete = numbering.createConcreteNumbering(abstractNum);
|
||||
|
||||
const item1 = new Paragraph("line with contextual spacing");
|
||||
const item2 = new Paragraph("line with contextual spacing");
|
||||
const item3 = new Paragraph("line without contextual spacing");
|
||||
const item4 = new Paragraph("line without contextual spacing");
|
||||
|
||||
item1
|
||||
.setNumbering(concrete, 0)
|
||||
.spacing({ before: 200 })
|
||||
.contextualSpacing(true);
|
||||
item2
|
||||
.setNumbering(concrete, 0)
|
||||
.spacing({ before: 200 })
|
||||
.contextualSpacing(true);
|
||||
item3
|
||||
.setNumbering(concrete, 0)
|
||||
.spacing({ before: 200 })
|
||||
.contextualSpacing(false);
|
||||
item4
|
||||
.setNumbering(concrete, 0)
|
||||
.spacing({ before: 200 })
|
||||
.contextualSpacing(false);
|
||||
|
||||
doc.addParagraph(item1);
|
||||
doc.addParagraph(item2);
|
||||
doc.addParagraph(item3);
|
||||
doc.addParagraph(item4);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
31
demo/demo30.ts
Normal file
31
demo/demo30.ts
Normal file
@ -0,0 +1,31 @@
|
||||
// Example on how to use a template document
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, ImportDotx, Packer, Paragraph } from "../build";
|
||||
|
||||
const importDotx = new ImportDotx();
|
||||
const filePath = "./demo/dotx/template.dotx";
|
||||
|
||||
fs.readFile(filePath, (err, data) => {
|
||||
if (err) {
|
||||
throw new Error(`Failed to read file ${filePath}.`);
|
||||
}
|
||||
|
||||
importDotx.extract(data).then((templateDocument) => {
|
||||
// This any needs fixing
|
||||
const sectionProps = {
|
||||
titlePage: templateDocument.titlePageIsDefined,
|
||||
} as any;
|
||||
|
||||
const doc = new Document(undefined, sectionProps, {
|
||||
template: templateDocument,
|
||||
});
|
||||
const paragraph = new Paragraph("Hello World");
|
||||
doc.addParagraph(paragraph);
|
||||
|
||||
const packer = new Packer();
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
||||
});
|
||||
});
|
29
demo/demo31.ts
Normal file
29
demo/demo31.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// Example of how you would create a table and add data to it
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph, VerticalAlign } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
});
|
||||
table
|
||||
.getCell(1, 1)
|
||||
.addParagraph(new Paragraph("This text should be in the middle of the cell"))
|
||||
.setVerticalAlign(VerticalAlign.CENTER);
|
||||
|
||||
table
|
||||
.getCell(1, 0)
|
||||
.addParagraph(
|
||||
new Paragraph(
|
||||
"Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah",
|
||||
).heading1(),
|
||||
);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
101
demo/demo32.ts
Normal file
101
demo/demo32.ts
Normal file
@ -0,0 +1,101 @@
|
||||
// Example of how you would merge cells together
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph, ShadingType, WidthType } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
let table = doc.createTable({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
});
|
||||
|
||||
table.getCell(0, 0).addParagraph(new Paragraph("Hello"));
|
||||
table.getRow(0).mergeCells(0, 1);
|
||||
|
||||
doc.createParagraph("Another table").heading2();
|
||||
|
||||
table = doc.createTable({
|
||||
rows: 2,
|
||||
columns: 3,
|
||||
width: 100,
|
||||
widthUnitType: WidthType.AUTO,
|
||||
columnWidths: [1000, 1000, 1000],
|
||||
});
|
||||
table
|
||||
.getCell(0, 0)
|
||||
.addParagraph(new Paragraph("World"))
|
||||
.setMargins({
|
||||
top: 1000,
|
||||
bottom: 1000,
|
||||
left: 1000,
|
||||
right: 1000,
|
||||
});
|
||||
table.getRow(0).mergeCells(0, 2);
|
||||
|
||||
doc.createParagraph("Another table").heading2();
|
||||
|
||||
table = doc.createTable({
|
||||
rows: 2,
|
||||
columns: 4,
|
||||
width: 7000,
|
||||
widthUnitType: WidthType.DXA,
|
||||
margins: {
|
||||
top: 400,
|
||||
bottom: 400,
|
||||
right: 400,
|
||||
left: 400,
|
||||
},
|
||||
});
|
||||
table.getCell(0, 0).addParagraph(new Paragraph("Foo"));
|
||||
table.getCell(0, 1).addParagraph(new Paragraph("v"));
|
||||
|
||||
table
|
||||
.getCell(1, 0)
|
||||
.addParagraph(new Paragraph("Bar1"))
|
||||
.setShading({
|
||||
fill: "b79c2f",
|
||||
val: ShadingType.REVERSE_DIAGONAL_STRIPE,
|
||||
color: "auto",
|
||||
});
|
||||
table
|
||||
.getCell(1, 1)
|
||||
.addParagraph(new Paragraph("Bar2"))
|
||||
.setShading({
|
||||
fill: "42c5f4",
|
||||
val: ShadingType.PERCENT_95,
|
||||
color: "auto",
|
||||
});
|
||||
table
|
||||
.getCell(1, 2)
|
||||
.addParagraph(new Paragraph("Bar3"))
|
||||
.setShading({
|
||||
fill: "880aa8",
|
||||
val: ShadingType.PERCENT_10,
|
||||
color: "e2df0b",
|
||||
});
|
||||
table
|
||||
.getCell(1, 3)
|
||||
.addParagraph(new Paragraph("Bar4"))
|
||||
.setShading({
|
||||
fill: "FF0000",
|
||||
val: ShadingType.CLEAR,
|
||||
color: "auto",
|
||||
});
|
||||
|
||||
table.getRow(0).mergeCells(0, 3);
|
||||
|
||||
doc.createParagraph("hi");
|
||||
|
||||
doc.createTable({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
width: 100,
|
||||
widthUnitType: WidthType.PERCENTAGE,
|
||||
});
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
22
demo/demo33.ts
Normal file
22
demo/demo33.ts
Normal file
@ -0,0 +1,22 @@
|
||||
// Sequential Captions
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph, TextRun } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const paragraph = new Paragraph("Hello World 1->").addSequentialIdentifier("Caption").addRun(new TextRun(" text after sequencial caption 2->")).addSequentialIdentifier("Caption");
|
||||
const paragraph2 = new Paragraph("Hello World 1->").addSequentialIdentifier("Label").addRun(new TextRun(" text after sequencial caption 2->")).addSequentialIdentifier("Label");
|
||||
const paragraph3 = new Paragraph("Hello World 1->").addSequentialIdentifier("Another").addRun(new TextRun(" text after sequencial caption 3->")).addSequentialIdentifier("Label");
|
||||
const paragraph4 = new Paragraph("Hello World 2->").addSequentialIdentifier("Another").addRun(new TextRun(" text after sequencial caption 4->")).addSequentialIdentifier("Label");
|
||||
|
||||
doc.addParagraph(paragraph);
|
||||
doc.addParagraph(paragraph2);
|
||||
doc.addParagraph(paragraph3);
|
||||
doc.addParagraph(paragraph4);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
29
demo/demo34.ts
Normal file
29
demo/demo34.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// Example of how you would create a table with float positions
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph, RelativeHorizontalPosition, RelativeVerticalPosition, TableAnchorType, WidthType } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
float: {
|
||||
horizontalAnchor: TableAnchorType.MARGIN,
|
||||
verticalAnchor: TableAnchorType.MARGIN,
|
||||
relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
|
||||
relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
|
||||
},
|
||||
width: 4535,
|
||||
widthUnitType: WidthType.DXA,
|
||||
});
|
||||
table.setFixedWidthLayout();
|
||||
|
||||
table.getCell(0, 0).addParagraph(new Paragraph("Hello"));
|
||||
table.getRow(0).mergeCells(0, 1);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
18
demo/demo35.ts
Normal file
18
demo/demo35.ts
Normal file
@ -0,0 +1,18 @@
|
||||
// Example on how to add hyperlinks to websites
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
const paragraph = new Paragraph();
|
||||
const link = doc.createHyperlink("http://www.example.com", "Hyperlink");
|
||||
|
||||
link.bold();
|
||||
paragraph.addHyperLink(link);
|
||||
doc.addParagraph(paragraph);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
26
demo/demo36.ts
Normal file
26
demo/demo36.ts
Normal file
@ -0,0 +1,26 @@
|
||||
// Add image to table cell
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Media, Packer, Table } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||
|
||||
const table = new Table({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
});
|
||||
table.getCell(1, 1).addParagraph(image.Paragraph);
|
||||
|
||||
// doc.createParagraph("Hello World");
|
||||
doc.addTable(table);
|
||||
|
||||
// doc.Header.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||
doc.Header.addTable(table);
|
||||
// doc.Footer.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
18
demo/demo37.ts
Normal file
18
demo/demo37.ts
Normal file
@ -0,0 +1,18 @@
|
||||
// Add images to header and footer
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Media, Packer } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||
doc.createParagraph("Hello World");
|
||||
|
||||
doc.Header.addImage(image);
|
||||
doc.Header.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||
doc.Header.createImage(fs.readFileSync("./demo/images/image1.jpeg"));
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
44
demo/demo38.ts
Normal file
44
demo/demo38.ts
Normal file
@ -0,0 +1,44 @@
|
||||
// Example of how to "wrap" text around an image
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
// import { Document, Packer, Paragraph } from "../build";
|
||||
import { Document, Packer, TextWrappingSide, TextWrappingType } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
doc.createParagraph(
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vehicula nec nulla vitae efficitur. Ut interdum mauris eu ipsum rhoncus, nec pharetra velit placerat. Sed vehicula libero ac urna molestie, id pharetra est pellentesque. Praesent iaculis vehicula fringilla. Duis pretium gravida orci eu vestibulum. Mauris tincidunt ipsum dolor, ut ornare dolor pellentesque id. Integer in nulla gravida, lacinia ante non, commodo ex. Vivamus vulputate nisl id lectus finibus vulputate. Ut et nisl mi. Cras fermentum augue arcu, ac accumsan elit euismod id. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed ac posuere nisi. Pellentesque tincidunt vehicula bibendum. Phasellus eleifend viverra nisl.",
|
||||
);
|
||||
|
||||
doc.createParagraph(
|
||||
"Proin ac purus faucibus, porttitor magna ut, cursus nisl. Vivamus ante purus, porta accumsan nibh eget, eleifend dignissim odio. Integer sed dictum est, aliquam lacinia justo. Donec ultrices auctor venenatis. Etiam interdum et elit nec elementum. Pellentesque nec viverra mauris. Etiam suscipit leo nec velit fringilla mattis. Pellentesque justo lacus, sodales eu condimentum in, dapibus finibus lacus. Morbi vitae nibh sit amet sem molestie feugiat. In non porttitor enim.",
|
||||
);
|
||||
|
||||
doc.createParagraph(
|
||||
"Ut eget diam cursus quam accumsan interdum at id ante. Ut mollis mollis arcu, eu scelerisque dui tempus in. Quisque aliquam, augue quis ornare aliquam, ex purus ultrices mauris, ut porta dolor dolor nec justo. Nunc a tempus odio, eu viverra arcu. Suspendisse vitae nibh nec mi pharetra tempus. Mauris ut ullamcorper sapien, et sagittis sapien. Vestibulum in urna metus. In scelerisque, massa id bibendum tempus, quam orci rutrum turpis, a feugiat nisi ligula id metus. Praesent id dictum purus. Proin interdum ipsum nulla.",
|
||||
);
|
||||
|
||||
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
|
||||
floating: {
|
||||
horizontalPosition: {
|
||||
offset: 2014400,
|
||||
},
|
||||
verticalPosition: {
|
||||
offset: 2014400,
|
||||
},
|
||||
wrap: {
|
||||
type: TextWrappingType.SQUARE,
|
||||
side: TextWrappingSide.BOTH_SIDES,
|
||||
},
|
||||
margins: {
|
||||
top: 201440,
|
||||
bottom: 201440,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
33
demo/demo39.ts
Normal file
33
demo/demo39.ts
Normal file
@ -0,0 +1,33 @@
|
||||
// Example how to display page numbers
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, PageNumberFormat, TextRun } from "../build";
|
||||
|
||||
const doc = new Document(
|
||||
{},
|
||||
{
|
||||
pageNumberStart: 1,
|
||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||
},
|
||||
);
|
||||
|
||||
doc.Header.createParagraph("Foo Bar corp. ")
|
||||
.addRun(new TextRun("Page Number ").pageNumber())
|
||||
.addRun(new TextRun(" to ").numberOfTotalPages());
|
||||
|
||||
doc.Footer.createParagraph("Foo Bar corp. ")
|
||||
.center()
|
||||
.addRun(new TextRun("Page Number: ").pageNumber())
|
||||
.addRun(new TextRun(" to ").numberOfTotalPages());
|
||||
|
||||
doc.createParagraph("Hello World 1").pageBreak();
|
||||
doc.createParagraph("Hello World 2").pageBreak();
|
||||
doc.createParagraph("Hello World 3").pageBreak();
|
||||
doc.createParagraph("Hello World 4").pageBreak();
|
||||
doc.createParagraph("Hello World 5").pageBreak();
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
@ -5,8 +5,11 @@ import { Document, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable(4, 4);
|
||||
table.getCell(2, 2).addContent(new Paragraph("Hello"));
|
||||
const table = doc.createTable({
|
||||
rows: 4,
|
||||
columns: 4,
|
||||
});
|
||||
table.getCell(2, 2).addParagraph(new Paragraph("Hello"));
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
|
26
demo/demo40.ts
Normal file
26
demo/demo40.ts
Normal file
@ -0,0 +1,26 @@
|
||||
// Example demonstrating line numbers.
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, LineNumberRestartFormat, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document(
|
||||
{},
|
||||
{
|
||||
lineNumberCountBy: 1,
|
||||
lineNumberRestart: LineNumberRestartFormat.CONTINUOUS,
|
||||
},
|
||||
);
|
||||
|
||||
doc.addParagraph(new Paragraph("Hello").heading1());
|
||||
doc.createParagraph(
|
||||
"Himenaeos duis luctus nullam fermentum lobortis potenti vivamus non dis, sed facilisis ultricies scelerisque aenean risus hac senectus. Adipiscing id venenatis justo ante gravida placerat, ac curabitur dis pellentesque proin bibendum risus, aliquam porta taciti vulputate primis. Tortor ipsum fermentum quam vel convallis primis nisl praesent tincidunt, lobortis quisque felis vitae condimentum class ut sem nam, aenean potenti pretium ac amet lacinia himenaeos mi. Aliquam nisl turpis hendrerit est morbi malesuada, augue interdum mus inceptos curabitur tristique, parturient feugiat sodales nulla facilisi. Aliquam non pulvinar purus nulla ex integer, velit faucibus vitae at bibendum quam, risus elit aenean adipiscing posuere.",
|
||||
);
|
||||
doc.createParagraph(
|
||||
"Sed laoreet id mattis egestas nam mollis elit lacinia convallis dui tincidunt ultricies habitant, pharetra per maximus interdum neque tempor risus efficitur morbi imperdiet senectus. Lectus laoreet senectus finibus inceptos donec potenti fermentum, ultrices eleifend odio suscipit magnis tellus maximus nibh, ac sit nullam eget felis himenaeos. Diam class sem magnis aenean commodo faucibus id proin mi, nullam sodales nec mus parturient ornare ad inceptos velit hendrerit, bibendum placerat eleifend integer facilisis urna dictumst suspendisse.",
|
||||
);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
51
demo/demo41.ts
Normal file
51
demo/demo41.ts
Normal file
@ -0,0 +1,51 @@
|
||||
// Multiple cells merging in the same table
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable({
|
||||
rows: 13,
|
||||
columns: 6,
|
||||
});
|
||||
let row = 0;
|
||||
table.getCell(row, 0).addContent(new Paragraph("0,0"));
|
||||
table.getCell(row, 1).addContent(new Paragraph("0,1"));
|
||||
table.getCell(row, 3).addContent(new Paragraph("0,3"));
|
||||
table.getCell(row, 4).addContent(new Paragraph("0,4"));
|
||||
table.getRow(row).mergeCells(4, 5);
|
||||
table.getRow(row).mergeCells(1, 2);
|
||||
row = 1;
|
||||
table.getCell(row, 0).addContent(new Paragraph("1,0"));
|
||||
table.getCell(row, 2).addContent(new Paragraph("1,2"));
|
||||
table.getCell(row, 4).addContent(new Paragraph("1,4"));
|
||||
table.getRow(row).mergeCells(4, 5);
|
||||
table.getRow(row).mergeCells(2, 3);
|
||||
table.getRow(row).mergeCells(0, 1);
|
||||
|
||||
row = 2;
|
||||
table.getCell(row, 0).addContent(new Paragraph("2,0"));
|
||||
table.getCell(row, 1).addContent(new Paragraph("2,1"));
|
||||
table.getCell(row, 2).addContent(new Paragraph("2,2"));
|
||||
table.getCell(row, 3).addContent(new Paragraph("2,3"));
|
||||
table.getCell(row, 4).addContent(new Paragraph("2,4"));
|
||||
table.getRow(row).mergeCells(4, 5);
|
||||
table.getRow(row).mergeCells(1, 2);
|
||||
row = 3;
|
||||
table.getCell(row, 0).addContent(new Paragraph("3,0"));
|
||||
table.getCell(row, 1).addContent(new Paragraph("3,1"));
|
||||
table.getCell(row, 2).addContent(new Paragraph("3,2"));
|
||||
table.getCell(row, 3).addContent(new Paragraph("3,3"));
|
||||
table.getCell(row, 4).addContent(new Paragraph("3,4"));
|
||||
table.getCell(row, 5).addContent(new Paragraph("3,5"));
|
||||
row = 4;
|
||||
table.getCell(row, 0).addContent(new Paragraph("4,0"));
|
||||
table.getCell(row, 5).addContent(new Paragraph("4,5"));
|
||||
table.getRow(row).mergeCells(0, 4);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
20
demo/demo43.ts
Normal file
20
demo/demo43.ts
Normal file
@ -0,0 +1,20 @@
|
||||
// Add image to table cell
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable({
|
||||
rows: 4,
|
||||
columns: 4,
|
||||
});
|
||||
table.getCell(2, 2).addParagraph(new Paragraph("Hello"));
|
||||
table.getColumn(3).mergeCells(1, 2);
|
||||
// table.getCell(3, 2).addParagraph(new Paragraph("Hello"));
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
36
demo/demo44.ts
Normal file
36
demo/demo44.ts
Normal file
@ -0,0 +1,36 @@
|
||||
// Sections with multiple columns
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
doc.addSection({
|
||||
column: {
|
||||
width: 708,
|
||||
count: 2,
|
||||
},
|
||||
});
|
||||
|
||||
doc.createParagraph("This text will be split into 2 columns on a page.");
|
||||
doc.createParagraph(
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
|
||||
);
|
||||
|
||||
doc.addSection({
|
||||
column: {
|
||||
width: 708,
|
||||
count: 2,
|
||||
},
|
||||
});
|
||||
|
||||
doc.createParagraph("This text will be split into 3 columns on a page.");
|
||||
doc.createParagraph(
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
|
||||
);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
@ -1,7 +1,8 @@
|
||||
// Example of how to add images to the document - You can use Buffers, UInt8Arrays or Base64 strings
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph } from "../build";
|
||||
// import { Document, Packer, Paragraph } from "../build";
|
||||
import { Document, HorizontalPositionAlign, HorizontalPositionRelativeFrom, Packer, Paragraph, VerticalPositionAlign, VerticalPositionRelativeFrom} from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
@ -13,6 +14,29 @@ doc.createImage(fs.readFileSync("./demo/images/dog.png").toString("base64"));
|
||||
doc.createImage(fs.readFileSync("./demo/images/cat.jpg"));
|
||||
doc.createImage(fs.readFileSync("./demo/images/parrots.bmp"));
|
||||
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
|
||||
floating: {
|
||||
horizontalPosition: {
|
||||
offset: 1014400,
|
||||
},
|
||||
verticalPosition: {
|
||||
offset: 1014400,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
doc.createImage(fs.readFileSync("./demo/images/cat.jpg"), 200, 200, {
|
||||
floating: {
|
||||
horizontalPosition: {
|
||||
relative: HorizontalPositionRelativeFrom.PAGE,
|
||||
align: HorizontalPositionAlign.RIGHT,
|
||||
},
|
||||
verticalPosition: {
|
||||
relative: VerticalPositionRelativeFrom.PAGE,
|
||||
align: VerticalPositionAlign.BOTTOM,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
|
BIN
demo/dotx/template.dotx
Normal file
BIN
demo/dotx/template.dotx
Normal file
Binary file not shown.
@ -29,6 +29,7 @@ import * as docx from "docx";
|
||||
## Basic Usage
|
||||
|
||||
```js
|
||||
var fs = require("fs");
|
||||
var docx = require("docx");
|
||||
|
||||
// Create document
|
||||
@ -41,11 +42,12 @@ paragraph.addRun(new docx.TextRun("Lorem Ipsum Foo Bar"));
|
||||
doc.addParagraph(paragraph);
|
||||
|
||||
// Used to export the file into a .docx file
|
||||
var exporter = new docx.LocalPacker(doc);
|
||||
var packer = new docx.Packer();
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My First Document.docx", buffer);
|
||||
});
|
||||
|
||||
exporter.pack("My First Document");
|
||||
|
||||
// Done! A file called 'My First Document.docx' will be in your file system if you used LocalPacker
|
||||
// Done! A file called 'My First Document.docx' will be in your file system.
|
||||
```
|
||||
|
||||
## Honoured Mentions
|
||||
|
@ -15,7 +15,10 @@
|
||||
* [Headers & Footers](usage/headers-and-footers.md)
|
||||
* [Bullet Points](usage/bullet-points.md)
|
||||
* [Numbering](usage/numbering.md)
|
||||
* [Tables](usage/tables.md)
|
||||
* [Tab Stops](usage/tab-stops.md)
|
||||
* [Table of Contents](usage/table-of-contents.md)
|
||||
* [Page Numbers](usage/page-numbers.md)
|
||||
* Styling
|
||||
* [Styling with JS](usage/styling-with-js.md)
|
||||
* [Styling with XML](usage/styling-with-xml.md)
|
||||
|
@ -1,7 +1,5 @@
|
||||
# Contribution Guidelines
|
||||
|
||||
## Writing Code
|
||||
|
||||
* Include documentation reference(s) at the top of each file:
|
||||
|
||||
```js
|
||||
@ -12,6 +10,71 @@
|
||||
|
||||
* 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?
|
||||
5. Is it fun to use?
|
||||
|
||||
## Good Commit Names
|
||||
|
||||
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
|
||||
works! // Glad its working, but the message is not helpful
|
||||
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:
|
||||
```js
|
||||
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:
|
||||
|
||||
```js
|
||||
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:
|
||||
|
||||
```js
|
||||
public float(tableFloatOptions: ITableFloatOptions): Table
|
||||
```
|
||||
|
||||
```ts
|
||||
table.float({...});
|
||||
```
|
||||
|
||||
## Add vs Create
|
||||
|
||||
This is just a guideline, and the rules can sometimes be broken.
|
||||
@ -25,7 +88,8 @@ This is just a guideline, and the rules can sometimes be broken.
|
||||
}
|
||||
```
|
||||
|
||||
* Use `add` if you add the element into the method as a parameter:
|
||||
* 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:
|
||||
|
||||
```js
|
||||
public addParagraph(paragraph: Paragraph) {
|
||||
@ -39,7 +103,7 @@ Getters and Setters are done with a capital letter like so:
|
||||
|
||||
```js
|
||||
public get Level() {
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
@ -57,6 +121,110 @@ 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.
|
||||
|
||||
> "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
|
||||
|
||||
`Interface` is generally preferred over `type`: https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types
|
||||
|
||||
**Do not:**
|
||||
|
||||
```js
|
||||
type RelationshipFileInfo = { id: number, target: string };
|
||||
```
|
||||
|
||||
**Do:**
|
||||
|
||||
```js
|
||||
interface IRelationshipFileInfo {
|
||||
id: number;
|
||||
target: string;
|
||||
}
|
||||
```
|
||||
|
||||
## String enums vs type
|
||||
|
||||
To take full advantage of TypeScript's typing system, its best to use `string enums`:
|
||||
|
||||
**Do not:**
|
||||
|
||||
```js
|
||||
type WeaponType = "bow" | "sword" | "wand";
|
||||
```
|
||||
|
||||
**Do:**
|
||||
|
||||
```js
|
||||
enum WeaponType = {
|
||||
BOW = "bow",
|
||||
SWORD = "sword",
|
||||
WAND = "wand",
|
||||
}
|
||||
```
|
||||
|
||||
## 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:**
|
||||
```js
|
||||
readdy // misspelling
|
||||
perm // abbreviation
|
||||
conf // abbreviation
|
||||
cnty // abbreviation
|
||||
relationFile // abbreviation
|
||||
colour // U.K. English
|
||||
```
|
||||
|
||||
**Do:**
|
||||
```js
|
||||
ready
|
||||
permission
|
||||
config
|
||||
country
|
||||
relationshipFile
|
||||
color
|
||||
```
|
||||
|
||||
## Keep files small (within reason)
|
||||
|
||||
To minimize merge conflicts, reduce complexity, and improve readability, keep the files small.
|
||||
|
||||
## Name files and folders with `/foo-bar/kebab-case.ts`
|
||||
|
||||
To be consistent and in-line with the project, name files `like-this.ts`.
|
||||
|
||||
https://stackoverflow.com/questions/7273316/what-is-the-javascript-filename-naming-convention
|
||||
|
||||
## Testing
|
||||
|
||||
Please write a test of every file you make and suffix it with `.spec.ts`.
|
||||
@ -78,3 +246,5 @@ describe("ClassName", () => {
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Try not to use the `tests/utility.ts` file as this is being deprecated.
|
||||
|
@ -14,7 +14,7 @@ This command will run the `demo selector app` in the `/demo` folder. It will pro
|
||||
|
||||
A simple hello world of the `docx` library:
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo1.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo1.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo1.ts_
|
||||
|
||||
@ -24,7 +24,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo1.ts_
|
||||
|
||||
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")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo2.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo2.ts_
|
||||
|
||||
@ -32,7 +32,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo2.ts_
|
||||
|
||||
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")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo13.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts_
|
||||
|
||||
@ -40,7 +40,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts_
|
||||
|
||||
This example shows many levels of numbering
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo3.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo3.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo3.ts_
|
||||
|
||||
@ -48,7 +48,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo3.ts_
|
||||
|
||||
Example of simple table
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
|
||||
|
||||
@ -56,7 +56,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
|
||||
|
||||
Styling the borders of a table
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
|
||||
|
||||
@ -66,7 +66,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
|
||||
|
||||
Importing Images from file system path
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
|
||||
|
||||
@ -74,7 +74,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
|
||||
|
||||
Example showing how to add image to headers and footers
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
|
||||
|
||||
@ -82,7 +82,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
|
||||
|
||||
Example showing how to scale images
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo12.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo12.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo12.ts_
|
||||
|
||||
@ -90,7 +90,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo12.ts_
|
||||
|
||||
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")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo23.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo23.ts_
|
||||
|
||||
@ -98,7 +98,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo23.ts_
|
||||
|
||||
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")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
|
||||
|
||||
@ -106,15 +106,15 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
|
||||
|
||||
If you want to use a Base64 image instead
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo18.ts ":include")
|
||||
[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 margains
|
||||
Example showing how to set custom margins
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo6.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo6.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo6.ts_
|
||||
|
||||
@ -122,7 +122,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo6.ts_
|
||||
|
||||
Example showing how to set the document to `landscape` or `portrait`
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo7.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo7.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo7.ts_
|
||||
|
||||
@ -130,7 +130,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo7.ts_
|
||||
|
||||
Example showing how to add headers and footers
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo8.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo8.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo8.ts_
|
||||
|
||||
@ -144,7 +144,7 @@ Check out `Sections` for this feature
|
||||
|
||||
Example showing how to page break
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo14.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo14.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo14.ts_
|
||||
|
||||
@ -152,15 +152,16 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo14.ts_
|
||||
|
||||
Example showing how to page break before like in Word
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo15.ts ":include")
|
||||
[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
|
||||
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")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo16.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo16.ts_
|
||||
|
||||
@ -168,7 +169,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo16.ts_
|
||||
|
||||
Example of how to add footnotes. Good for references
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo17.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo17.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo17.ts_
|
||||
|
||||
@ -178,7 +179,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo17.ts_
|
||||
|
||||
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")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo19.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo19.ts_
|
||||
|
||||
@ -187,7 +188,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo19.ts_
|
||||
|
||||
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")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo21.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo21.ts_
|
||||
|
||||
@ -195,7 +196,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo21.ts_
|
||||
|
||||
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")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo22.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo22.ts_
|
||||
|
||||
@ -205,7 +206,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo22.ts_
|
||||
|
||||
Example showing how to add headers and footers
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo10.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo10.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo10.ts_
|
||||
|
||||
@ -213,6 +214,6 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo10.ts_
|
||||
|
||||
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")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo11.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo11.ts_
|
||||
|
@ -25,6 +25,7 @@
|
||||
<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>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -5,7 +5,7 @@
|
||||
To create a new document, it is very easy:
|
||||
|
||||
```js
|
||||
var doc = new docx.Document();
|
||||
const doc = new docx.Document();
|
||||
```
|
||||
|
||||
## Document properties
|
||||
@ -13,7 +13,7 @@ var doc = new docx.Document();
|
||||
You can add properties to the Word document by specifying options, for example:
|
||||
|
||||
```js
|
||||
var doc = new docx.Document({
|
||||
const doc = new docx.Document({
|
||||
creator: "Dolan Miu",
|
||||
description: "My extremely interesting document",
|
||||
title: "My Document",
|
||||
@ -22,14 +22,18 @@ var doc = new docx.Document({
|
||||
|
||||
### Full list of options:
|
||||
|
||||
```
|
||||
creator
|
||||
description
|
||||
title
|
||||
subject
|
||||
keywords
|
||||
lastModifiedBy
|
||||
revision
|
||||
```
|
||||
|
||||
* creator
|
||||
* description
|
||||
* title
|
||||
* subject
|
||||
* keywords
|
||||
* lastModifiedBy
|
||||
* revision
|
||||
|
||||
You can mix and match whatever properties you want, or provide no properties.
|
||||
|
||||
### units for positioning
|
||||
|
||||
Various parts of the API require positioning arguments. The units are "20ths of a point" from the [OOXML](http://officeopenxml.com/index.php) specification.
|
||||
See [Lars Corneliussen's blog post](https://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/) for more information and how to convert units.
|
@ -23,7 +23,7 @@ doc.Header.createImage([BUFFER_OF_YOUR_IMAGE]);
|
||||
doc.Footer.createImage([BUFFER_OF_YOUR_IMAGE]);
|
||||
```
|
||||
|
||||
Refer to `demo8.js` for more information
|
||||
Refer to [`demo8.ts`](https://github.com/dolanmiu/docx/blob/master/demo/demo8.ts) for more information.
|
||||
|
||||
## Multiple Headers and Footers
|
||||
|
||||
@ -37,8 +37,12 @@ Also all the supported section properties are implemented according to: http://o
|
||||
|
||||
// Add new section with another header and footer
|
||||
doc.addSection({
|
||||
headerId: header.Header.ReferenceId,
|
||||
footerId: footer.Footer.ReferenceId,
|
||||
headers: {
|
||||
default: header
|
||||
},
|
||||
footers: {
|
||||
default: footer
|
||||
},
|
||||
pageNumberStart: 1,
|
||||
pageNumberFormatType: docx.PageNumberFormat.DECIMAL,
|
||||
});
|
||||
|
47
docs/usage/hyperlinks.md
Normal file
47
docs/usage/hyperlinks.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Hyperlinks
|
||||
|
||||
There are two types of hyperlinks: internal (pointing to a bookmark inside the document) and external (pointing to an external url).
|
||||
|
||||
## Internal
|
||||
|
||||
To create an internal hyperlink you need first to create a bookmark (the paragraph that will be the destination of the hyperlink) with `doc.createBookmark(anchor, text)`.
|
||||
|
||||
A bookmark is composed of an anchor (an identifier) and the text displayed. After creating a bookmark just add it to a paragraph with `paragraph.addBookmark(bookmark)`
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const paragraph = this.doc.createParagraph();
|
||||
const bookmark = this.doc.createBookmark('anchorForChapter1', 'This is chapter1');
|
||||
paragraph.addBookmark(bookmark);
|
||||
```
|
||||
|
||||
Then you can create an hyperlink pointing to that bookmark with `doc.createInternalHyperLink(anchor,text)`:
|
||||
|
||||
```ts
|
||||
const paragraph = this.doc.createParagraph();
|
||||
const link = this.doc.createInternalHyperLink('anchorForChapter1', 'This is a link to chapter1');
|
||||
paragraph.addHyperLink(link);
|
||||
```
|
||||
|
||||
## External
|
||||
|
||||
To create an external hyperlink you just need to specify the url and the text of the link, then add it to a paragraph with `doc.createHyperlink(url, text)`:
|
||||
|
||||
```ts
|
||||
const paragraph = this.doc.createParagraph();
|
||||
const link = this.doc.createHyperlink('https://docx.js.org', 'This is an external link');
|
||||
paragraph.addHyperLink(link);
|
||||
```
|
||||
|
||||
|
||||
## Styling an hyperlink
|
||||
|
||||
It is possible to set the style of the text of an hyperlink. This can be done applying run formatting on `TextRun` property of the hyperlink.
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
const link = this.doc.createHyperlink('https://docx.js.org', 'This is an external link');
|
||||
link.TextRun.bold().italics()
|
||||
```
|
@ -1,156 +1,231 @@
|
||||
# Images
|
||||
|
||||
## Intro
|
||||
To create a `floating` image on top of text:
|
||||
|
||||
Adding images is very simple
|
||||
|
||||
Simply call the `createImage` method:
|
||||
|
||||
```js
|
||||
const image = doc.createImage([BUFFER_OF_YOUR_IMAGE]);
|
||||
```
|
||||
|
||||
`docx` supports `jpeg`, `jpg`, `bmp`, `gif` and `png`
|
||||
|
||||
Check `demo5.js` for an example
|
||||
|
||||
## Positioning
|
||||
|
||||
Images can be:
|
||||
|
||||
* floating position of images
|
||||
* Wrapped around the text
|
||||
* Inline
|
||||
|
||||
By default, picture are exported as `INLINE` elements.
|
||||
|
||||
In Word this is found in:
|
||||
|
||||

|
||||
|
||||
### Usage
|
||||
|
||||
The `PictureRun` element support various options to define the positioning of the element in the document.
|
||||
|
||||
```js
|
||||
interface DrawingOptions {
|
||||
position?: PlacementPosition;
|
||||
textWrapping?: TextWrapping;
|
||||
floating?: Floating;
|
||||
}
|
||||
```
|
||||
|
||||
can be passed when creating `PictureRun()` for example:
|
||||
|
||||
```js
|
||||
const imageData = document.createImage(buffer, 903, 1149);
|
||||
|
||||
new docx.PictureRun(imageData, {
|
||||
position: docx.PlacementPosition.FLOATING,
|
||||
```ts
|
||||
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
|
||||
floating: {
|
||||
horizontalPosition: {
|
||||
relative: HorizontalPositionRelativeFrom.PAGE,
|
||||
align: HorizontalPositionAlign.LEFT,
|
||||
offset: 1014400,
|
||||
},
|
||||
verticalPosition: {
|
||||
relative: VerticalPositionRelativeFrom.PAGE,
|
||||
align: VerticalPositionAlign.TOP,
|
||||
offset: 1014400,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
So, the first thing is to define the placement position: `INLINE` or `FLOATING`. Inline is the default one so there is no need to pass drawing options for inline.
|
||||
By default with no arguments, its an `inline` image:
|
||||
|
||||
When placement position is FLOATING then we can use two options:
|
||||
|
||||
### Wrap text
|
||||
|
||||
for `drawingOptions.textWrapping` we can define various options. `textWrapping` has the following properties:
|
||||
|
||||
```js
|
||||
interface TextWrapping {
|
||||
textWrapStyle: TextWrapStyle;
|
||||
wrapTextOption?: WrapTextOption;
|
||||
distanceFromText?: Distance;
|
||||
}
|
||||
|
||||
enum TextWrapStyle {
|
||||
NONE,
|
||||
SQUARE,
|
||||
TIGHT,
|
||||
TOP_AND_BOTTOM,
|
||||
}
|
||||
|
||||
enum WrapTextOption {
|
||||
BOTH_SIDES = "bothSides",
|
||||
LEFT = "left",
|
||||
RIGHT = "right",
|
||||
LARGEST = "largest",
|
||||
}
|
||||
```ts
|
||||
doc.createImage(fs.readFileSync("./demo/images/parrots.bmp"));
|
||||
```
|
||||
|
||||
### Floating position
|
||||
You can also create images manually and add them later:
|
||||
|
||||
When we want to position the image relative or absolute then we need to use option `drawingOptions.floating`:
|
||||
|
||||
```js
|
||||
export interface Floating {
|
||||
horizontalPosition: HorizontalPositionOptions;
|
||||
verticalPosition: VerticalPositionOptions;
|
||||
allowOverlap?: boolean;
|
||||
lockAnchor?: boolean;
|
||||
behindDocument?: boolean;
|
||||
layoutInCell?: boolean;
|
||||
}
|
||||
|
||||
export interface HorizontalPositionOptions {
|
||||
relative: HorizontalPositionRelativeFrom;
|
||||
align?: HorizontalPositionAlign;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
export interface VerticalPositionOptions {
|
||||
relative: VerticalPositionRelativeFrom;
|
||||
align?: VerticalPositionAlign;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
export enum HorizontalPositionRelativeFrom {
|
||||
CHARACTER = "character",
|
||||
COLUMN = "column",
|
||||
INSIDE_MARGIN = "insideMargin",
|
||||
LEFT_MARGIN = "leftMargin",
|
||||
MARGIN = "margin",
|
||||
OUTSIDE_MARGIN = "outsideMargin",
|
||||
PAGE = "page",
|
||||
RIGHT_MARGIN = "rightMargin",
|
||||
}
|
||||
|
||||
export enum VerticalPositionRelativeFrom {
|
||||
BOTTOM_MARGIN = "bottomMargin",
|
||||
INSIDE_MARGIN = "insideMargin",
|
||||
LINE = "line",
|
||||
MARGIN = "margin",
|
||||
OUTSIDE_MARGIN = "outsideMargin",
|
||||
PAGE = "page",
|
||||
PARAGRAPH = "paragraph",
|
||||
TOP_MARGIN = "topMargin",
|
||||
}
|
||||
|
||||
export enum HorizontalPositionAlign {
|
||||
CENTER = "center",
|
||||
INSIDE = "inside",
|
||||
LEFT = "left",
|
||||
OUTSIDE = "outside",
|
||||
RIGHT = "right",
|
||||
}
|
||||
|
||||
export enum VerticalPositionAlign {
|
||||
BOTTOM = "bottom",
|
||||
CENTER = "center",
|
||||
INSIDE = "inside",
|
||||
OUTSIDE = "outside",
|
||||
TOP = "top",
|
||||
}
|
||||
```ts
|
||||
const image = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));
|
||||
doc.addImage(image);
|
||||
```
|
||||
|
||||
## Intro
|
||||
|
||||
Adding images can be done in two ways:
|
||||
|
||||
1. Call the `createImage` method to add the image directly into the `document`:
|
||||
|
||||
```js
|
||||
doc.createImage([IMAGE_BUFFER], [WIDTH], [HEIGHT], [POSITION_OPTIONS]);
|
||||
```
|
||||
|
||||
2. Create an `image` first, then add it into the `document`:
|
||||
|
||||
```ts
|
||||
const image = Media.addImage(doc, [IMAGE_BUFFER]);
|
||||
doc.addImage(image);
|
||||
```
|
||||
|
||||
`docx` supports `jpeg`, `jpg`, `bmp`, `gif` and `png`
|
||||
|
||||
## Positioning
|
||||
|
||||
> Positioning is the method on how to place the image on the document
|
||||
|
||||

|
||||
|
||||
Three types of image positioning is supported:
|
||||
|
||||
- Floating
|
||||
- Inline
|
||||
|
||||
By default, picture are exported as `Inline` elements.
|
||||
|
||||
### Usage
|
||||
|
||||
Pass `options` into the `[POSITION_OPTIONS]` metioned in the [Intro above](#Intro).
|
||||
|
||||
## Floating
|
||||
|
||||
To change the position the image to be on top of the text, simply add the `floating` property to the last argument. By default, the offsets are relative to the top left corner of the `page`. Offset units are in [emus](https://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/):
|
||||
|
||||
```ts
|
||||
const imageData = document.createImage(buffer, 903, 1149, {
|
||||
floating: {
|
||||
horizontalPosition: {
|
||||
offset: 1014400, // relative: HorizontalPositionRelativeFrom.PAGE by default
|
||||
},
|
||||
verticalPosition: {
|
||||
offset: 1014400, // relative: VerticalPositionRelativeFrom.PAGE by default
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts
|
||||
const imageData = document.createImage(buffer, 903, 1149, {
|
||||
floating: {
|
||||
horizontalPosition: {
|
||||
relative: HorizontalPositionRelativeFrom.RIGHT_MARGIN,
|
||||
offset: 1014400,
|
||||
},
|
||||
verticalPosition: {
|
||||
relative: VerticalPositionRelativeFrom.BOTTOM_MARGIN,
|
||||
offset: 1014400,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
Full options you can pass into `floating` are:
|
||||
|
||||
| Property | Type | Notes |
|
||||
| ------------------ | --------------------------- | -------- |
|
||||
| horizontalPosition | `HorizontalPositionOptions` | Required |
|
||||
| verticalPosition | `VerticalPositionOptions` | Required |
|
||||
| allowOverlap | `boolean` | Optional |
|
||||
| lockAnchor | `boolean` | Optional |
|
||||
| behindDocument | `boolean` | Optional |
|
||||
| layoutInCell | `boolean` | Optional |
|
||||
|
||||
`HorizontalPositionOptions` are:
|
||||
|
||||
| Property | Type | Notes | Possible Values |
|
||||
| -------- | -------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
||||
| relative | `HorizontalPositionRelativeFrom` | Required | `CHARACTER`, `COLUMN`, `INSIDE_MARGIN`, `LEFT_MARGIN`, `MARGIN`, `OUTSIDE_MARGIN`, `PAGE`, `RIGHT_MARGIN` |
|
||||
| align | `HorizontalPositionAlign` | You can either have `align` or `offset`, not both | `CENTER`, `INSIDE`, `LEFT`, `OUTSIDE`, `RIGHT` |
|
||||
| offset | `number` | You can either have `align` or `offset`, not both | `0` to `Infinity` |
|
||||
|
||||
`VerticalPositionOptions` are:
|
||||
|
||||
| Property | Type | Notes | Possible Values |
|
||||
| -------- | ------------------------------ | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| relative | `VerticalPositionRelativeFrom` | Required | `BOTTOM_MARGIN`, `INSIDE_MARGIN`, `LINE`, `MARGIN`, `OUTSIDE_MARGIN`, `PAGE`, `PARAGRAPH`, `TOP_MARGIN` |
|
||||
| align | `VerticalPositionAlign` | You can either have `align` or `offset`, not both | `BOTTOM`, `CENTER`, `INSIDE`, `OUTSIDE`, `TOP` |
|
||||
| offset | `number` | You can either have `align` or `offset`, not both | `0` to `Infinity` |
|
||||
|
||||
## Wrap text
|
||||
|
||||
Wrapping only works for floating elements. Text will "wrap" around the floating `image`.
|
||||
|
||||
Add `wrap` options inside the `floating` options:
|
||||
|
||||
```ts
|
||||
wrap: {
|
||||
type: [TextWrappingType],
|
||||
side: [TextWrappingSide],
|
||||
},
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
|
||||
floating: {
|
||||
horizontalPosition: {
|
||||
offset: 2014400,
|
||||
},
|
||||
verticalPosition: {
|
||||
offset: 2014400,
|
||||
},
|
||||
wrap: {
|
||||
type: TextWrappingType.SQUARE,
|
||||
side: TextWrappingSide.BOTH_SIDES,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Wrap options have the following properties are:
|
||||
|
||||
| Property | Type | Notes | Possible Values |
|
||||
| -------- | ------------------ | -------- | ------------------------------------------- |
|
||||
| type | `TextWrappingType` | Optional | `NONE`, `SQUARE`, `TIGHT`, `TOP_AND_BOTTOM` |
|
||||
| side | `TextWrappingSide` | Optional | `BOTH_SIDES`, `LEFT`, `RIGHT`, `LARGEST` |
|
||||
|
||||
## Margins
|
||||
|
||||
Margins give some space between the text and the image. Margins [only work for floating elements](http://officeopenxml.com/drwPicInline.php). Additionally, the image must also be in wrap mode (see above).
|
||||
|
||||
?> Be sure to also set `wrap` in your options!
|
||||
|
||||
To use, add the `margins` options inside the `floating` options:
|
||||
|
||||
```ts
|
||||
margins: {
|
||||
top: number,
|
||||
bottom: number,
|
||||
left: number,
|
||||
right: number
|
||||
},
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
|
||||
floating: {
|
||||
horizontalPosition: {
|
||||
offset: 2014400,
|
||||
},
|
||||
verticalPosition: {
|
||||
offset: 2014400,
|
||||
},
|
||||
wrap: {
|
||||
type: TextWrappingType.SQUARE,
|
||||
side: TextWrappingSide.BOTH_SIDES,
|
||||
},
|
||||
margins: {
|
||||
top: 201440,
|
||||
bottom: 201440,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### 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_
|
||||
|
||||
### Floating images
|
||||
|
||||
Example showing how to float images on top of text and optimally give a `margin`
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo38.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo38.ts_
|
||||
|
@ -26,7 +26,7 @@ _levels_ which form a sequence starting at 0 indicating the top-level
|
||||
list look and increasing from there to descibe the sublists, then
|
||||
sub-sublists, etc. Each level includes the following properties:
|
||||
|
||||
* **level**: This its 0-based index in the defintion stack
|
||||
* **level**: This is its 0-based index in the definition stack
|
||||
* **numberFormat**: This indicates how the bullet or number should be
|
||||
generated. Options include `bullet` (meaning don't count), `decimal`
|
||||
(arabic numerals), `upperRoman`, `lowerRoman`, `hex`, and many
|
||||
@ -64,8 +64,8 @@ styles or numbering of any kind, you'll have to use the
|
||||
`docx.Numbering` class.
|
||||
|
||||
First you need to create a new numbering container class and use it to
|
||||
create your abstract numbering style, define your levels, and creat
|
||||
your concreate numbering style:
|
||||
create your abstract numbering style, define your levels, and create
|
||||
your concrete numbering style:
|
||||
|
||||
```js
|
||||
const numbering = new docx.Numbering();
|
||||
@ -75,11 +75,11 @@ abstractNum.createLevel(0, "upperRoman", "%1", "start").addParagraphProperty(new
|
||||
abstractNum.createLevel(1, "decimal", "%2.", "start").addParagraphProperty(new Indent(1440, 980));
|
||||
abstractNum.createLevel(2, "lowerLetter", "%3)", "start").addParagraphProperty(new Indent(2160, 1700));
|
||||
|
||||
const concrete = numbering.createConcreteNumbering(numberedAbstract);
|
||||
const concrete = numbering.createConcreteNumbering(abstractNum);
|
||||
```
|
||||
|
||||
You can then apply your concrete style to paragraphs using their
|
||||
`#setNumbering` method:
|
||||
You can then apply your concrete style to paragraphs using the
|
||||
`setNumbering` method:
|
||||
|
||||
```js
|
||||
topLevelP.setNumbering(concrete, 0);
|
||||
|
66
docs/usage/page-numbers.md
Normal file
66
docs/usage/page-numbers.md
Normal file
@ -0,0 +1,66 @@
|
||||
# Page Numbers
|
||||
|
||||
> This feature allows you to set page numbers on each page
|
||||
|
||||
?> **Note:** This feature only works on Headers and Footers
|
||||
|
||||
```ts
|
||||
doc.Header.createParagraph().addRun(new TextRun("Page Number: ").pageNumber()).addRun(new TextRun("to ").numberOfTotalPages());
|
||||
```
|
||||
|
||||
## Current page number
|
||||
|
||||
To get the current page number, call the `.pageNumber()` method on a `TextRun`. Then add the newly created `TextRun` into a paragraph
|
||||
|
||||
```ts
|
||||
pageNumber();
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const currentPageRun = new TextRun("Current Page Number: ").pageNumber();
|
||||
paragraph.addRun(currentPageRun);
|
||||
```
|
||||
|
||||
## Total number of pages
|
||||
|
||||
```ts
|
||||
numberOfTotalPages();
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const lastPage = new TextRun("Total Page Number: ").numberOfTotalPages();
|
||||
paragraph.addRun(lastPage);
|
||||
```
|
||||
|
||||
|
||||
## Both
|
||||
|
||||
You can combine the two to get "Page 2 of 10" effect:
|
||||
|
||||
```ts
|
||||
const currentPageRun = new TextRun("Page ").pageNumber();
|
||||
const lastPage = new TextRun("of ").numberOfTotalPages();
|
||||
|
||||
paragraph.addRun(currentPageRun);
|
||||
paragraph.addRun(lastPage);
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
```ts
|
||||
doc.Header.createParagraph().addRun(new TextRun("Page ").pageNumber()).addRun(new TextRun("of ").numberOfTotalPages());
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple Example
|
||||
|
||||
Adding page numbers to Header and Footer
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo39.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo39.ts_
|
@ -76,6 +76,22 @@ paragraph.heading1().center();
|
||||
|
||||
The above will create a `heading 1` which is `centered`.
|
||||
|
||||
### Justified text with breaks
|
||||
|
||||
When a paragraph is justified, you may want to not justify the contents of incomplete lines, which end in a soft line break.
|
||||
|
||||

|
||||
|
||||
This is possible to achieve using:
|
||||
|
||||
```ts
|
||||
this.doc.Settings.addCompatibility().doNotExpandShiftReturn();
|
||||
```
|
||||
|
||||
The result is:
|
||||
|
||||

|
||||
|
||||
## Thematic Break
|
||||
|
||||
To add a break in the page, simply add `.thematicBreak()` on a paragraph:
|
||||
@ -106,7 +122,7 @@ var paragraph = new docx.Paragraph("Hello World on another page").pageBreakBefor
|
||||
|
||||

|
||||
|
||||
Example: https://github.com/dolanmiu/docx/blob/master/demo/demo15.js
|
||||
Example: https://github.com/dolanmiu/docx/blob/master/demo/demo15.ts
|
||||
|
||||
## Page break control
|
||||
|
||||
|
@ -14,7 +14,7 @@ const name = new TextRun("Name:")
|
||||
## Available methods
|
||||
|
||||
* For run formatting:
|
||||
* `.bold()`, `.italic()`, `.smallCaps()`, `.allCaps()`, `.strike()`, `.doubleStrike()`, `.subScript()`, `.superScript()`: Set the formatting property to true
|
||||
* `.bold()`, `.italics()`, `.smallCaps()`, `.allCaps()`, `.strike()`, `.doubleStrike()`, `.subScript()`, `.superScript()`: Set the formatting property to true
|
||||
* `.underline(style="single", color=null)`: Set the underline style and color
|
||||
* `.color(color)`: Set the text color, using 6 hex characters for RRGGBB (no leading `#`)
|
||||
* `.size(halfPts)`: Set the font size, measured in half-points
|
||||
|
@ -44,4 +44,4 @@ doc.addParagraph(paragraph);
|
||||
doc.createParagraph("Some normal text");
|
||||
```
|
||||
|
||||
Example: https://github.com/dolanmiu/docx/blob/master/demo/demo13.js
|
||||
Example: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
> Tab stops are useful, if you are unclear of what they are, [here is a link explaining](https://en.wikipedia.org/wiki/Tab_stop). It enables side by side text which is nicely laid out without the need for tables, or constantly pressing space bar.
|
||||
|
||||
**Note**: At the moment, the unit of measurement for a tab stop is counter intuitive for a human. It is using OpenXMLs own measuring system. For example, 2268 roughly translates to 3cm. Therefore in the future, I may consider changing it to percentages or even cm.
|
||||
!> **Note**: At the moment, the unit of measurement for a tab stop is counter intuitive for a human. It is using OpenXMLs own measuring system. For example, 2268 roughly translates to 3cm. Therefore in the future, I may consider changing it to percentages or even cm.
|
||||
|
||||

|
||||
|
||||
|
76
docs/usage/table-of-contents.md
Normal file
76
docs/usage/table-of-contents.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Table of Contents
|
||||
|
||||
You can generate table of contents with `docx`. More information can be found [here](http://officeopenxml.com/WPtableOfContents.php).
|
||||
|
||||
>Tables of Contents are fields and, by design, it's content is only generated or updated by Word. We can't do it programatically.
|
||||
>This is why, when you open a the file, Word you will prompt the message "This document contains fields that may refer to other files. Do you want to update the fields in this document?".
|
||||
>You have say yes to Word generate the content of all table of contents.
|
||||
|
||||
The complete documentation can be found [here](https://www.ecma-international.org/publications/standards/Ecma-376.htm) (at Part 1, Page 1251).
|
||||
|
||||
## How to
|
||||
|
||||
All you need to do is create a `TableOfContents` object and assign it to the document.
|
||||
|
||||
```js
|
||||
const toc = new TableOfContents("Summary", {
|
||||
hyperlink: true,
|
||||
headingStyleRange: "1-5",
|
||||
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)]
|
||||
});
|
||||
|
||||
doc.addTableOfContents(toc);
|
||||
```
|
||||
|
||||
## Table of Contents Options
|
||||
|
||||
Here is the list of all options that you can use to generate your tables of contents:
|
||||
|
||||
| Option | Type | TOC Field Switch | Description |
|
||||
| --- | --- | --- | --- |
|
||||
|captionLabel|string|`\a`|Includes captioned items, but omits caption labels and numbers. The identifier designated by `text` in this switch's field-argument corresponds to the caption label. Use ``\c`` to build a table of captions with labels and numbers.|
|
||||
|entriesFromBookmark|string|`\b`|Includes entries only from the portion of the document marked by the bookmark named by `text` in this switch's field-argument.|
|
||||
|captionLabelIncludingNumbers|string|`\c`|Includes figures, tables, charts, and other items that are numbered by a SEQ field (§17.16.5.56). The sequence identifier designated by `text` in this switch's field-argument, which corresponds to the caption label, shall match the identifier in the corresponding SEQ field.|
|
||||
|sequenceAndPageNumbersSeparator|string|`\d`|When used with `\s`, the `text` in this switch's field-argument defines the separator between sequence and page numbers. The default separator is a hyphen (-).|
|
||||
|tcFieldIdentifier|string|`\f`|Includes only those TC fields whose identifier exactly matches the `text` in this switch's field-argument (which is typically a letter).|
|
||||
|hyperlink|boolean|`\h`|Makes the table of contents entries hyperlinks.|
|
||||
|tcFieldLevelRange|string|`\l`|Includes TC fields that assign entries to one of the levels specified by `text` in this switch's field-argument as a range having the form startLevel-endLevel, where startLevel and endLevel are integers, and startLevel has a value equal-to or less-than endLevel. TC fields that assign entries to lower levels are skipped.|
|
||||
|pageNumbersEntryLevelsRange|string|`\n`|Without field-argument, omits page numbers from the table of contents. Page numbers are omitted from all levels unless a range of entry levels is specified by `text` in this switch's field-argument. A range is specified as for `\l`.|
|
||||
|headingStyleRange|string|`\o`|Uses paragraphs formatted with all or the specified range of builtin heading styles. Headings in a style range are specified by `text` in this switch's field-argument using the notation specified as for `\l`, where each integer corresponds to the style with a style ID of HeadingX (e.g. 1 corresponds to Heading1). If no heading range is specified, all heading levels used in the document are listed.|
|
||||
|entryAndPageNumberSeparator|string|`\p`|`text` in this switch's field-argument specifies a sequence of characters that separate an entry and its page number. The default is a tab with leader dots.|
|
||||
|seqFieldIdentifierForPrefix|string|`\s`|For entries numbered with a SEQ field (§17.16.5.56), adds a prefix to the page number. The prefix depends on the type of entry. `text` in this switch's field-argument shall match the identifier in the SEQ field.|
|
||||
|stylesWithLevels|StyleLevel[]|`\t`| Uses paragraphs formatted with styles other than the built-in heading styles. `text` in this switch's field-argument specifies those styles as a set of comma-separated doublets, with each doublet being a comma-separated set of style name and table of content level. `\t` can be combined with `\o`.|
|
||||
|useAppliedParagraphOutlineLevel|boolean|`\u`|Uses the applied paragraph outline level.|
|
||||
|preserveTabInEntries|boolean|`\w`|Preserves tab entries within table entries.|
|
||||
|preserveNewLineInEntries|boolean|`\x`|Preserves newline characters within table entries.|
|
||||
|hideTabAndPageNumbersInWebView|boolean|`\z`|Hides tab leader and page numbers in web page view (§17.18.102).|
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
// Let's define the options for generate a TOC for heading 1-5 and MySpectacularStyle,
|
||||
// making the entries be hyperlinks for the paragraph
|
||||
const toc = new TableOfContents("Summary", {
|
||||
hyperlink: true,
|
||||
headingStyleRange: "1-5",
|
||||
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)]
|
||||
});
|
||||
|
||||
doc.addTableOfContents(toc);
|
||||
|
||||
doc.addParagraph(new Paragraph("Header #1").heading1().pageBreakBefore());
|
||||
doc.addParagraph(new Paragraph("I'm a little text, very nicely written.'"));
|
||||
|
||||
doc.addParagraph(new Paragraph("Header #2").heading1().pageBreakBefore());
|
||||
doc.addParagraph(new Paragraph("I'm another text very nicely written.'"));
|
||||
doc.addParagraph(new Paragraph("Header #2.1").heading2());
|
||||
doc.addParagraph(new Paragraph("I'm another text very nicely written.'"));
|
||||
|
||||
doc.addParagraph(new Paragraph("My Spectacular Style #1").style("MySpectacularStyle").pageBreakBefore());
|
||||
```
|
||||
|
||||
### Complete example
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo28.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo28.ts_
|
305
docs/usage/tables.md
Normal file
305
docs/usage/tables.md
Normal file
@ -0,0 +1,305 @@
|
||||
# Tables
|
||||
|
||||
You can create tables with `docx`. More information can be found [here](http://officeopenxml.com/WPtable.php).
|
||||
|
||||
## Create Table
|
||||
|
||||
To create a table, simply use the `createTable()` method on a `document`.
|
||||
|
||||
```ts
|
||||
const table = doc.createTable([NUMBER OF ROWS], [NUMBER OF COLUMNS]);
|
||||
```
|
||||
|
||||
Alternatively, you can create a table object directly, and then add it in the `document`
|
||||
|
||||
```ts
|
||||
const table = new Table(4, 4);
|
||||
doc.addTable(table);
|
||||
```
|
||||
|
||||
The snippet below creates a table of 2 rows and 4 columns.
|
||||
|
||||
```ts
|
||||
const table = doc.createTable(2, 4);
|
||||
|
||||
// Or
|
||||
|
||||
const table = new Table(2, 4);
|
||||
doc.addTable(table);
|
||||
```
|
||||
|
||||
## Rows and Columns
|
||||
|
||||
You can get a row or a column from a table like so, where `index` is a number:
|
||||
|
||||
### Get Row
|
||||
|
||||
```ts
|
||||
const row = doc.getRow(index);
|
||||
```
|
||||
|
||||
With this, you can merge a row by using the `mergeCells()` method, where `startIndex` is the row number you want to merge from, and `endIndex` is where you want it to merge to:
|
||||
|
||||
```ts
|
||||
row.mergeCells(startIndex, endIndex);
|
||||
```
|
||||
|
||||
You can get a cell from a `row` by using the `getCell()` method, where `index` is the row index:
|
||||
|
||||
```ts
|
||||
row.getCell(index);
|
||||
```
|
||||
|
||||
### Get Column
|
||||
|
||||
```ts
|
||||
const column = doc.getColumn(index);
|
||||
```
|
||||
|
||||
Again, you can merge a row by using the `mergeCells()` method, where `startIndex` is the row number you want to merge from, and `endIndex` is where you want it to merge to:
|
||||
|
||||
```ts
|
||||
column.mergeCells(startIndex, endIndex);
|
||||
```
|
||||
|
||||
You can get a cell from a `column` by using the `getCell()` method, where `index` is the column index:
|
||||
|
||||
```ts
|
||||
column.getCell(index);
|
||||
```
|
||||
|
||||
## Cells
|
||||
|
||||
The `createTable()` method created a table with cells. To access the cell, use the `getCell()` method.
|
||||
|
||||
```ts
|
||||
const cell = table.getCell([ROW INDEX], [COLUMN INDEX]);
|
||||
```
|
||||
|
||||
You can also get a cell from a `column` or a `row` with `getCell()`, mentioned previously.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const cell = table.getCell(0, 2);
|
||||
|
||||
const cell = row.getCell(0);
|
||||
|
||||
const cell = column.getCell(2);
|
||||
```
|
||||
|
||||
### Add paragraph to a cell
|
||||
|
||||
Once you have got the cell, you can add data to it with the `addParagraph()` method.
|
||||
|
||||
```ts
|
||||
cell.addParagraph(new Paragraph("Hello"));
|
||||
```
|
||||
|
||||
### Set width of a cell
|
||||
|
||||
You can specify the width of a cell using:
|
||||
|
||||
`cell.Properties.setWidth(width, format)`
|
||||
|
||||
format can be:
|
||||
* WidthType.AUTO
|
||||
* WidthType.DXA: value is in twentieths of a point
|
||||
* WidthType.NIL: is considered as zero
|
||||
* WidthType.PCT: percent of table width
|
||||
|
||||
### Example
|
||||
|
||||
```ts
|
||||
cell.Properties.setWidth(100, WidthType.DXA);
|
||||
```
|
||||
|
||||
```ts
|
||||
cell.Properties.setWidth('50%', WidthType.PCT);
|
||||
```
|
||||
|
||||
## Borders
|
||||
|
||||
BorderStyle can be imported from `docx`. Size determines the thickness. HTML color can be a hex code or alias such as `red`.
|
||||
|
||||
```ts
|
||||
cell.Borders.addTopBorder([BorderStyle], [SIZE], [HTML COLOR]);
|
||||
```
|
||||
|
||||
```ts
|
||||
cell.Borders.addBottomBorder([BorderStyle], [SIZE], [HTML COLOR]);
|
||||
```
|
||||
|
||||
```ts
|
||||
cell.Borders.addStartBorder([[BorderStyle]], [SIZE], [HTML COLOR]);
|
||||
```
|
||||
|
||||
```ts
|
||||
cell.Borders.addEndBorder([BorderStyle], [SIZE], [HTML COLOR]);
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```ts
|
||||
import { BorderStyle } from "docx";
|
||||
|
||||
cell.Borders.addStartBorder(BorderStyle.DOT_DOT_DASH, 3, "green");
|
||||
cell.Borders.addEndBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000");
|
||||
```
|
||||
|
||||
### Google DOCS
|
||||
|
||||
Google DOCS does not support start and end borders, instead they use left and right borders. So to set left and right borders for Google DOCS you should use:
|
||||
|
||||
```ts
|
||||
import { BorderStyle } from "docx";
|
||||
|
||||
cell.Borders.addLeftBorder(BorderStyle.DOT_DOT_DASH, 3, "green");
|
||||
cell.Borders.addRightBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000");
|
||||
```
|
||||
|
||||
## Set Width
|
||||
|
||||
```ts
|
||||
import { WidthType } from "docx";
|
||||
|
||||
table.setWidth([WIDTH], [OPTIONAL WidthType. Defaults to DXA]);
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
table.setWidth(4535, WidthType.DXA);
|
||||
```
|
||||
|
||||
## Vertical Align
|
||||
|
||||
Sets the vertical alignment of the contents of the cell
|
||||
|
||||
```ts
|
||||
import { VerticalAlign } from "docx";
|
||||
|
||||
cell.setVerticalAlign([VerticalAlign TYPE]);
|
||||
```
|
||||
|
||||
For example, to center align a cell:
|
||||
|
||||
```ts
|
||||
cell.setVerticalAlign(VerticalAlign.CENTER);
|
||||
```
|
||||
|
||||
## Rows
|
||||
|
||||
To get a row, use the `getRow` method on a `table`. There are a handful of methods which you can apply to a row which will be explained below.
|
||||
|
||||
```ts
|
||||
table.getRow([ROW INDEX]);
|
||||
```
|
||||
|
||||
## Merge cells together
|
||||
|
||||
### Merging on a row
|
||||
|
||||
First obtain the row, and call `mergeCells()`. The first argument is where the merge should start. The second argument is where the merge should end.
|
||||
|
||||
```ts
|
||||
table.getRow(0).mergeCells([FROM INDEX], [TO INDEX]);
|
||||
```
|
||||
|
||||
#### Example
|
||||
|
||||
This will merge 3 cells together starting from index `0`:
|
||||
|
||||
```ts
|
||||
table.getRow(0).mergeCells(0, 2);
|
||||
```
|
||||
|
||||
### Merging on a column
|
||||
|
||||
It has not been implemented yet, but it will follow a similar structure as merging a row.
|
||||
|
||||
## Nested Tables
|
||||
|
||||
To have a table within a table
|
||||
|
||||
```ts
|
||||
cell.addTable(new Table(1, 1));
|
||||
```
|
||||
|
||||
## Pagination
|
||||
|
||||
###Prevent row pagination
|
||||
To prevent breaking contents of a row across multiple pages, call `cantSplit()`:
|
||||
|
||||
```ts
|
||||
table.getRow(0).setCantSplit();
|
||||
```
|
||||
|
||||
###Repeat row
|
||||
If a table is paginated on multiple pages, it is possible to repeat a row at the top of each new page calling `setTableHeader()`:
|
||||
|
||||
```ts
|
||||
table.getRow(0).setTableHeader();
|
||||
```
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
|
||||
|
||||
### Custom borders
|
||||
|
||||
Example showing how to add colourful borders to tables
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
|
||||
|
||||
### Adding images
|
||||
|
||||
Example showing how to add images to tables
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo36.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo36.ts_
|
||||
|
||||
### Alignment of text in a cell
|
||||
|
||||
Example showing how align text in a table cell
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo31.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo31.ts_
|
||||
|
||||
### Merging rows
|
||||
|
||||
Example showing merging of `rows`
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo32.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo32.ts_
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo41.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo41.ts_
|
||||
|
||||
### Merging columns
|
||||
|
||||
Example showing merging of `columns`
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo43.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo43.ts_
|
||||
|
||||
### Floating tables
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo34.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo34.ts_
|
||||
|
@ -22,7 +22,7 @@ text.bold();
|
||||
### Italics
|
||||
|
||||
```js
|
||||
text.italic();
|
||||
text.italics();
|
||||
```
|
||||
|
||||
### Underline
|
||||
@ -80,5 +80,5 @@ text.break();
|
||||
What if you want to create a paragraph which is **_bold_** and **_italic_**?
|
||||
|
||||
```js
|
||||
paragraph.bold().italic();
|
||||
paragraph.bold().italics();
|
||||
```
|
||||
|
32
package.json
32
package.json
@ -1,23 +1,25 @@
|
||||
{
|
||||
"name": "docx",
|
||||
"version": "4.1.0",
|
||||
"version": "5.0.0-rc4",
|
||||
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
"pretest": "rimraf ./build",
|
||||
"test": "mocha-webpack \"src/**/*.ts\"",
|
||||
"test-watch": "mocha-webpack \"src/**/*.ts\" --watch",
|
||||
"prepublishOnly": "npm run build",
|
||||
"test.coverage": "nyc npm test",
|
||||
"test.watch": "npm test -- --watch",
|
||||
"prepublishOnly": "npm run build --production",
|
||||
"lint": "tslint --project .",
|
||||
"build": "npm run webpack && npm run fix-types",
|
||||
"tsc": "rimraf ./build && tsc -p .",
|
||||
"webpack": "rimraf ./build && webpack",
|
||||
"build.web": "webpack --config webpack.web.config.js",
|
||||
"demo": "npm run build && npm run ts-node ./demo",
|
||||
"typedoc": "typedoc src/index.ts",
|
||||
"style": "prettier -l \"src/**/*.ts\"",
|
||||
"style.fix": "prettier \"src/**/*.ts\" --write",
|
||||
"fix-types": "node types-absolute-fixer.js",
|
||||
"style.fix": "npm run style -- --write",
|
||||
"fix-types": "ts-node scripts/types-absolute-fixer.ts",
|
||||
"e2e": "ts-node scripts/e2e.ts",
|
||||
"serve.docs": "cd docs && docsify serve",
|
||||
"ts-node": "ts-node"
|
||||
},
|
||||
"pre-commit": [
|
||||
@ -47,12 +49,10 @@
|
||||
],
|
||||
"types": "./build/index.d.ts",
|
||||
"dependencies": {
|
||||
"@types/image-size": "0.0.29",
|
||||
"@types/jszip": "^3.1.3",
|
||||
"fast-xml-parser": "^3.3.6",
|
||||
"image-size": "^0.6.2",
|
||||
"@types/jszip": "^3.1.4",
|
||||
"jszip": "^3.1.5",
|
||||
"xml": "^1.0.1"
|
||||
"xml": "^1.0.1",
|
||||
"xml-js": "^1.6.8"
|
||||
},
|
||||
"author": "Dolan Miu",
|
||||
"license": "MIT",
|
||||
@ -63,22 +63,30 @@
|
||||
"devDependencies": {
|
||||
"@types/chai": "^3.4.35",
|
||||
"@types/mocha": "^2.2.39",
|
||||
"@types/request-promise": "^4.1.42",
|
||||
"@types/sinon": "^4.3.1",
|
||||
"@types/webpack": "^4.4.24",
|
||||
"awesome-typescript-loader": "^3.4.1",
|
||||
"chai": "^3.5.0",
|
||||
"docsify": "^4.9.1",
|
||||
"glob": "^7.1.2",
|
||||
"istanbul-instrumenter-loader": "^3.0.1",
|
||||
"jszip": "^3.1.5",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha-webpack": "^1.0.1",
|
||||
"nyc": "^13.1.0",
|
||||
"pre-commit": "^1.2.2",
|
||||
"prettier": "^1.12.1",
|
||||
"prettier": "^1.15.2",
|
||||
"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",
|
||||
"tslint-immutable": "^4.9.0",
|
||||
"typedoc": "^0.11.1",
|
||||
"typescript": "2.9.2",
|
||||
"webpack": "^3.10.0"
|
||||
|
30
scripts/e2e.ts
Normal file
30
scripts/e2e.ts
Normal file
@ -0,0 +1,30 @@
|
||||
// tslint:disable:no-console
|
||||
import * as fs from "fs";
|
||||
import * as request from "request-promise";
|
||||
|
||||
async function e2e(filePath: string): Promise<void> {
|
||||
console.log(`Running e2e for: ${filePath}`);
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
console.error("File not found");
|
||||
throw Error("File not found");
|
||||
}
|
||||
|
||||
const result = await request.post({
|
||||
url: "https://wt-9017166451e5dc00461b648d19f5e8da-0.sandbox.auth0-extend.com/docx-validator",
|
||||
formData: {
|
||||
document: fs.createReadStream(filePath),
|
||||
},
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
e2e(process.argv[2])
|
||||
.then(() => {
|
||||
console.log("Success! Document is valid");
|
||||
})
|
||||
.catch(() => {
|
||||
console.log("Error! Validation failed");
|
||||
process.exit(1);
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
const glob = require("glob");
|
||||
const replace = require("replace-in-file");
|
||||
import * as glob from "glob";
|
||||
import * as replace from "replace-in-file";
|
||||
|
||||
const files = glob.sync("build/**/*.d.ts");
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { assert } from "chai";
|
||||
import { assert, expect } from "chai";
|
||||
|
||||
import { Formatter } from "../export/formatter";
|
||||
import * as file from "../file";
|
||||
import { CoreProperties } from "../file/core-properties";
|
||||
import { Attributes } from "../file/xml-components";
|
||||
import { Utility } from "../tests/utility";
|
||||
import { Formatter } from "export/formatter";
|
||||
import * as file from "file";
|
||||
import { CoreProperties } from "file/core-properties";
|
||||
import { Attributes } from "file/xml-components";
|
||||
|
||||
describe("Formatter", () => {
|
||||
let formatter: Formatter;
|
||||
@ -31,33 +30,31 @@ describe("Formatter", () => {
|
||||
const paragraph = new file.Paragraph();
|
||||
paragraph.addRun(new file.TextRun("test").bold());
|
||||
const newJson = formatter.format(paragraph);
|
||||
assert.isDefined(newJson["w:p"][1]["w:r"][0]["w:rPr"][0]["w:b"][0]._attr["w:val"]);
|
||||
assert.isDefined(newJson["w:p"][0]["w:r"][0]["w:rPr"][0]["w:b"]._attr["w:val"]);
|
||||
});
|
||||
|
||||
it("should format attributes (rsidSect)", () => {
|
||||
const attributes = new Attributes({
|
||||
rsidSect: "test2",
|
||||
});
|
||||
let newJson = formatter.format(attributes);
|
||||
newJson = Utility.jsonify(newJson);
|
||||
if (newJson._attr === undefined) {
|
||||
assert.fail();
|
||||
return;
|
||||
}
|
||||
assert.isDefined(newJson._attr["w:rsidSect"]);
|
||||
const tree = formatter.format(attributes);
|
||||
expect(tree).to.deep.equal({
|
||||
_attr: {
|
||||
"w:rsidSect": "test2",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should format attributes (val)", () => {
|
||||
const attributes = new Attributes({
|
||||
val: "test",
|
||||
});
|
||||
let newJson = formatter.format(attributes);
|
||||
newJson = Utility.jsonify(newJson);
|
||||
if (newJson._attr === undefined) {
|
||||
assert.fail();
|
||||
return;
|
||||
}
|
||||
assert.isDefined(newJson._attr["w:val"]);
|
||||
const tree = formatter.format(attributes);
|
||||
expect(tree).to.deep.equal({
|
||||
_attr: {
|
||||
"w:val": "test",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should should change 'p' tag into 'w:p' tag", () => {
|
||||
|
@ -2,6 +2,12 @@ import { BaseXmlComponent, IXmlableObject } from "file/xml-components";
|
||||
|
||||
export class Formatter {
|
||||
public format(input: BaseXmlComponent): IXmlableObject {
|
||||
return input.prepForXml();
|
||||
const output = input.prepForXml();
|
||||
|
||||
if (output) {
|
||||
return output;
|
||||
} else {
|
||||
throw Error("XMLComponent did not format correctly");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
src/export/packer/image-replacer.ts
Normal file
17
src/export/packer/image-replacer.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { IMediaData, Media } from "file/media";
|
||||
|
||||
export class ImageReplacer {
|
||||
public replace(xmlData: string, mediaData: IMediaData[], offset: number): string {
|
||||
let currentXmlData = xmlData;
|
||||
|
||||
mediaData.forEach((image, i) => {
|
||||
currentXmlData = currentXmlData.replace(`{${image.fileName}}`, (offset + i).toString());
|
||||
});
|
||||
|
||||
return currentXmlData;
|
||||
}
|
||||
|
||||
public getMediaData(xmlData: string, media: Media): IMediaData[] {
|
||||
return media.Array.filter((image) => xmlData.search(`{${image.fileName}}`) > 0);
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
/* tslint:disable:typedef space-before-function-paren */
|
||||
import { expect } from "chai";
|
||||
import { File } from "../../file";
|
||||
|
||||
import { File } from "file";
|
||||
|
||||
import { Compiler } from "./next-compiler";
|
||||
|
||||
describe("Compiler", () => {
|
||||
@ -15,11 +17,11 @@ describe("Compiler", () => {
|
||||
describe("#compile()", () => {
|
||||
it("should pack all the content", async function() {
|
||||
this.timeout(99999999);
|
||||
const zipFile = await compiler.compile(file);
|
||||
const zipFile = compiler.compile(file);
|
||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||
|
||||
expect(fileNames).is.an.instanceof(Array);
|
||||
expect(fileNames).has.length(17);
|
||||
expect(fileNames).has.length(18);
|
||||
expect(fileNames).to.include("word/document.xml");
|
||||
expect(fileNames).to.include("word/styles.xml");
|
||||
expect(fileNames).to.include("docProps/core.xml");
|
||||
@ -29,6 +31,7 @@ describe("Compiler", () => {
|
||||
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
||||
expect(fileNames).to.include("word/footer1.xml");
|
||||
expect(fileNames).to.include("word/footnotes.xml");
|
||||
expect(fileNames).to.include("word/settings.xml");
|
||||
expect(fileNames).to.include("word/_rels/footer1.xml.rels");
|
||||
expect(fileNames).to.include("word/_rels/document.xml.rels");
|
||||
expect(fileNames).to.include("[Content_Types].xml");
|
||||
@ -43,11 +46,11 @@ describe("Compiler", () => {
|
||||
|
||||
this.timeout(99999999);
|
||||
|
||||
const zipFile = await compiler.compile(file);
|
||||
const zipFile = compiler.compile(file);
|
||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||
|
||||
expect(fileNames).is.an.instanceof(Array);
|
||||
expect(fileNames).has.length(25);
|
||||
expect(fileNames).has.length(26);
|
||||
|
||||
expect(fileNames).to.include("word/header1.xml");
|
||||
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
||||
|
@ -3,38 +3,43 @@ import * as xml from "xml";
|
||||
|
||||
import { File } from "file";
|
||||
import { Formatter } from "../formatter";
|
||||
import { ImageReplacer } from "./image-replacer";
|
||||
|
||||
interface IXmlifyedFile {
|
||||
data: string;
|
||||
path: string;
|
||||
readonly data: string;
|
||||
readonly path: string;
|
||||
}
|
||||
|
||||
interface IXmlifyedFileMapping {
|
||||
Document: IXmlifyedFile;
|
||||
Styles: IXmlifyedFile;
|
||||
Properties: IXmlifyedFile;
|
||||
Numbering: IXmlifyedFile;
|
||||
Relationships: IXmlifyedFile;
|
||||
FileRelationships: IXmlifyedFile;
|
||||
Headers: IXmlifyedFile[];
|
||||
Footers: IXmlifyedFile[];
|
||||
HeaderRelationships: IXmlifyedFile[];
|
||||
FooterRelationships: IXmlifyedFile[];
|
||||
ContentTypes: IXmlifyedFile;
|
||||
AppProperties: IXmlifyedFile;
|
||||
FootNotes: IXmlifyedFile;
|
||||
readonly Document: IXmlifyedFile;
|
||||
readonly Styles: IXmlifyedFile;
|
||||
readonly Properties: IXmlifyedFile;
|
||||
readonly Numbering: IXmlifyedFile;
|
||||
readonly Relationships: IXmlifyedFile;
|
||||
readonly FileRelationships: IXmlifyedFile;
|
||||
readonly Headers: IXmlifyedFile[];
|
||||
readonly Footers: IXmlifyedFile[];
|
||||
readonly HeaderRelationships: IXmlifyedFile[];
|
||||
readonly FooterRelationships: IXmlifyedFile[];
|
||||
readonly ContentTypes: IXmlifyedFile;
|
||||
readonly AppProperties: IXmlifyedFile;
|
||||
readonly FootNotes: IXmlifyedFile;
|
||||
readonly Settings: IXmlifyedFile;
|
||||
}
|
||||
|
||||
export class Compiler {
|
||||
private readonly formatter: Formatter;
|
||||
private readonly imageReplacer: ImageReplacer;
|
||||
private readonly prettifyXml?: boolean;
|
||||
|
||||
constructor() {
|
||||
constructor(prettifyXml?: boolean) {
|
||||
this.formatter = new Formatter();
|
||||
this.imageReplacer = new ImageReplacer();
|
||||
this.prettifyXml = prettifyXml;
|
||||
}
|
||||
|
||||
public async compile(file: File): Promise<JSZip> {
|
||||
public compile(file: File): JSZip {
|
||||
const zip = new JSZip();
|
||||
|
||||
const xmlifiedFileMapping = this.xmlifyFile(file);
|
||||
|
||||
for (const key in xmlifiedFileMapping) {
|
||||
@ -62,13 +67,39 @@ export class Compiler {
|
||||
}
|
||||
|
||||
private xmlifyFile(file: File): IXmlifyedFileMapping {
|
||||
file.verifyUpdateFields();
|
||||
const documentRelationshipCount = file.DocumentRelationships.RelationshipCount + 1;
|
||||
|
||||
return {
|
||||
Relationships: {
|
||||
data: (() => {
|
||||
const xmlData = xml(this.formatter.format(file.Document), this.prettifyXml);
|
||||
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
|
||||
|
||||
mediaDatas.forEach((mediaData, i) => {
|
||||
file.DocumentRelationships.createRelationship(
|
||||
documentRelationshipCount + i,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
`media/${mediaData.fileName}`,
|
||||
);
|
||||
});
|
||||
|
||||
return xml(this.formatter.format(file.DocumentRelationships), this.prettifyXml);
|
||||
})(),
|
||||
path: "word/_rels/document.xml.rels",
|
||||
},
|
||||
Document: {
|
||||
data: xml(this.formatter.format(file.Document), true),
|
||||
data: (() => {
|
||||
const tempXmlData = xml(this.formatter.format(file.Document), this.prettifyXml);
|
||||
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
|
||||
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, documentRelationshipCount);
|
||||
|
||||
return xmlData;
|
||||
})(),
|
||||
path: "word/document.xml",
|
||||
},
|
||||
Styles: {
|
||||
data: xml(this.formatter.format(file.Styles)),
|
||||
data: xml(this.formatter.format(file.Styles), this.prettifyXml),
|
||||
path: "word/styles.xml",
|
||||
},
|
||||
Properties: {
|
||||
@ -81,45 +112,85 @@ export class Compiler {
|
||||
path: "docProps/core.xml",
|
||||
},
|
||||
Numbering: {
|
||||
data: xml(this.formatter.format(file.Numbering)),
|
||||
data: xml(this.formatter.format(file.Numbering), this.prettifyXml),
|
||||
path: "word/numbering.xml",
|
||||
},
|
||||
Relationships: {
|
||||
data: xml(this.formatter.format(file.DocumentRelationships)),
|
||||
path: "word/_rels/document.xml.rels",
|
||||
},
|
||||
FileRelationships: {
|
||||
data: xml(this.formatter.format(file.FileRelationships)),
|
||||
data: xml(this.formatter.format(file.FileRelationships), this.prettifyXml),
|
||||
path: "_rels/.rels",
|
||||
},
|
||||
Headers: file.Headers.map((headerWrapper, index) => ({
|
||||
data: xml(this.formatter.format(headerWrapper.Header)),
|
||||
path: `word/header${index + 1}.xml`,
|
||||
})),
|
||||
Footers: file.Footers.map((footerWrapper, index) => ({
|
||||
data: xml(this.formatter.format(footerWrapper.Footer)),
|
||||
path: `word/footer${index + 1}.xml`,
|
||||
})),
|
||||
HeaderRelationships: file.Headers.map((headerWrapper, index) => ({
|
||||
data: xml(this.formatter.format(headerWrapper.Relationships)),
|
||||
HeaderRelationships: file.Headers.map((headerWrapper, index) => {
|
||||
const xmlData = xml(this.formatter.format(headerWrapper.Header), this.prettifyXml);
|
||||
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
|
||||
|
||||
mediaDatas.forEach((mediaData, i) => {
|
||||
headerWrapper.Relationships.createRelationship(
|
||||
i,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
`media/${mediaData.fileName}`,
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
data: xml(this.formatter.format(headerWrapper.Relationships), this.prettifyXml),
|
||||
path: `word/_rels/header${index + 1}.xml.rels`,
|
||||
})),
|
||||
FooterRelationships: file.Footers.map((footerWrapper, index) => ({
|
||||
data: xml(this.formatter.format(footerWrapper.Relationships)),
|
||||
};
|
||||
}),
|
||||
FooterRelationships: file.Footers.map((footerWrapper, index) => {
|
||||
const xmlData = xml(this.formatter.format(footerWrapper.Footer), this.prettifyXml);
|
||||
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
|
||||
|
||||
mediaDatas.forEach((mediaData, i) => {
|
||||
footerWrapper.Relationships.createRelationship(
|
||||
i,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
`media/${mediaData.fileName}`,
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
data: xml(this.formatter.format(footerWrapper.Relationships), this.prettifyXml),
|
||||
path: `word/_rels/footer${index + 1}.xml.rels`,
|
||||
})),
|
||||
};
|
||||
}),
|
||||
Headers: file.Headers.map((headerWrapper, index) => {
|
||||
const tempXmlData = xml(this.formatter.format(headerWrapper.Header), this.prettifyXml);
|
||||
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
|
||||
// TODO: 0 needs to be changed when headers get relationships of their own
|
||||
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
|
||||
|
||||
return {
|
||||
data: xmlData,
|
||||
path: `word/header${index + 1}.xml`,
|
||||
};
|
||||
}),
|
||||
Footers: file.Footers.map((footerWrapper, index) => {
|
||||
const tempXmlData = xml(this.formatter.format(footerWrapper.Footer), this.prettifyXml);
|
||||
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
|
||||
// TODO: 0 needs to be changed when headers get relationships of their own
|
||||
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
|
||||
|
||||
return {
|
||||
data: xmlData,
|
||||
path: `word/footer${index + 1}.xml`,
|
||||
};
|
||||
}),
|
||||
ContentTypes: {
|
||||
data: xml(this.formatter.format(file.ContentTypes)),
|
||||
data: xml(this.formatter.format(file.ContentTypes), this.prettifyXml),
|
||||
path: "[Content_Types].xml",
|
||||
},
|
||||
AppProperties: {
|
||||
data: xml(this.formatter.format(file.AppProperties)),
|
||||
data: xml(this.formatter.format(file.AppProperties), this.prettifyXml),
|
||||
path: "docProps/app.xml",
|
||||
},
|
||||
FootNotes: {
|
||||
data: xml(this.formatter.format(file.FootNotes)),
|
||||
data: xml(this.formatter.format(file.FootNotes), this.prettifyXml),
|
||||
path: "word/footnotes.xml",
|
||||
},
|
||||
Settings: {
|
||||
data: xml(this.formatter.format(file.Settings), this.prettifyXml),
|
||||
path: "word/settings.xml",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
import { assert } from "chai";
|
||||
import { stub } from "sinon";
|
||||
|
||||
import { File, Paragraph } from "../../file";
|
||||
import { File, Paragraph } from "file";
|
||||
|
||||
import { Packer } from "./packer";
|
||||
|
||||
describe("Packer", () => {
|
||||
@ -45,4 +46,24 @@ describe("Packer", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#toBase64String()", () => {
|
||||
it("should create a standard docx file", async function() {
|
||||
this.timeout(99999999);
|
||||
const str = await packer.toBase64String(file);
|
||||
|
||||
assert.isDefined(str);
|
||||
assert.isTrue(str.length > 0);
|
||||
});
|
||||
|
||||
it("should handle exception if it throws any", () => {
|
||||
// tslint:disable-next-line:no-any
|
||||
const compiler = stub((packer as any).compiler, "compile");
|
||||
|
||||
compiler.throwsException();
|
||||
return packer.toBase64String(file).catch((error) => {
|
||||
assert.isDefined(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4,27 +4,36 @@ import { Compiler } from "./next-compiler";
|
||||
export class Packer {
|
||||
private readonly compiler: Compiler;
|
||||
|
||||
constructor() {
|
||||
this.compiler = new Compiler();
|
||||
constructor(prettifyXml?: boolean) {
|
||||
this.compiler = new Compiler(prettifyXml);
|
||||
}
|
||||
|
||||
public async toBuffer(file: File): Promise<Buffer> {
|
||||
const zip = await this.compiler.compile(file);
|
||||
const zipData = (await zip.generateAsync({ type: "nodebuffer" })) as Buffer;
|
||||
const zip = this.compiler.compile(file);
|
||||
const zipData = (await zip.generateAsync({
|
||||
type: "nodebuffer",
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
})) as Buffer;
|
||||
|
||||
return zipData;
|
||||
}
|
||||
|
||||
public async toBase64String(file: File): Promise<string> {
|
||||
const zip = await this.compiler.compile(file);
|
||||
const zipData = (await zip.generateAsync({ type: "base64" })) as string;
|
||||
const zip = this.compiler.compile(file);
|
||||
const zipData = (await zip.generateAsync({
|
||||
type: "base64",
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
})) as string;
|
||||
|
||||
return zipData;
|
||||
}
|
||||
|
||||
public async toBlob(file: File): Promise<Blob> {
|
||||
const zip = await this.compiler.compile(file);
|
||||
const zipData = (await zip.generateAsync({ type: "blob" })) as Blob;
|
||||
const zip = this.compiler.compile(file);
|
||||
const zipData = (await zip.generateAsync({
|
||||
type: "blob",
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
})) as Blob;
|
||||
|
||||
return zipData;
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IAppPropertiesAttributes {
|
||||
xmlns: string;
|
||||
vt: string;
|
||||
readonly xmlns: string;
|
||||
readonly vt: string;
|
||||
}
|
||||
|
||||
export class AppPropertiesAttributes extends XmlAttributeComponent<IAppPropertiesAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
xmlns: "xmlns",
|
||||
vt: "xmlns:vt",
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IContentTypeAttributes {
|
||||
xmlns?: string;
|
||||
readonly xmlns?: string;
|
||||
}
|
||||
|
||||
export class ContentTypeAttributes extends XmlAttributeComponent<IContentTypeAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
xmlns: "xmlns",
|
||||
};
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
// tslint:disable:no-string-literal
|
||||
|
||||
import { expect } from "chai";
|
||||
import { Formatter } from "../../export/formatter";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { ContentTypes } from "./content-types";
|
||||
|
||||
describe("ContentTypes", () => {
|
||||
@ -18,64 +20,70 @@ describe("ContentTypes", () => {
|
||||
expect(tree["Types"]).to.be.an.instanceof(Array);
|
||||
|
||||
expect(tree["Types"][0]).to.deep.equal({ _attr: { xmlns: "http://schemas.openxmlformats.org/package/2006/content-types" } });
|
||||
expect(tree["Types"][1]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/png", Extension: "png" } }] });
|
||||
expect(tree["Types"][2]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/jpeg", Extension: "jpeg" } }] });
|
||||
expect(tree["Types"][3]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/jpeg", Extension: "jpg" } }] });
|
||||
expect(tree["Types"][4]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/bmp", Extension: "bmp" } }] });
|
||||
expect(tree["Types"][5]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/gif", Extension: "gif" } }] });
|
||||
expect(tree["Types"][1]).to.deep.equal({ Default: { _attr: { ContentType: "image/png", Extension: "png" } } });
|
||||
expect(tree["Types"][2]).to.deep.equal({ Default: { _attr: { ContentType: "image/jpeg", Extension: "jpeg" } } });
|
||||
expect(tree["Types"][3]).to.deep.equal({ Default: { _attr: { ContentType: "image/jpeg", Extension: "jpg" } } });
|
||||
expect(tree["Types"][4]).to.deep.equal({ Default: { _attr: { ContentType: "image/bmp", Extension: "bmp" } } });
|
||||
expect(tree["Types"][5]).to.deep.equal({ Default: { _attr: { ContentType: "image/gif", Extension: "gif" } } });
|
||||
expect(tree["Types"][6]).to.deep.equal({
|
||||
Default: [{ _attr: { ContentType: "application/vnd.openxmlformats-package.relationships+xml", Extension: "rels" } }],
|
||||
Default: { _attr: { ContentType: "application/vnd.openxmlformats-package.relationships+xml", Extension: "rels" } },
|
||||
});
|
||||
expect(tree["Types"][7]).to.deep.equal({ Default: [{ _attr: { ContentType: "application/xml", Extension: "xml" } }] });
|
||||
expect(tree["Types"][7]).to.deep.equal({ Default: { _attr: { ContentType: "application/xml", Extension: "xml" } } });
|
||||
expect(tree["Types"][8]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
|
||||
PartName: "/word/document.xml",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(tree["Types"][9]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
|
||||
PartName: "/word/styles.xml",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(tree["Types"][10]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-package.core-properties+xml",
|
||||
PartName: "/docProps/core.xml",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(tree["Types"][11]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
PartName: "/docProps/app.xml",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(tree["Types"][12]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
|
||||
PartName: "/word/numbering.xml",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(tree["Types"][13]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml",
|
||||
PartName: "/word/footnotes.xml",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(tree["Types"][14]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
|
||||
PartName: "/word/settings.xml",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -86,26 +94,22 @@ describe("ContentTypes", () => {
|
||||
contentTypes.addFooter(102);
|
||||
const tree = new Formatter().format(contentTypes);
|
||||
|
||||
expect(tree["Types"][14]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
expect(tree["Types"][15]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||
PartName: "/word/footer101.xml",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(tree["Types"][15]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
expect(tree["Types"][16]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||
PartName: "/word/footer102.xml",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -116,26 +120,22 @@ describe("ContentTypes", () => {
|
||||
contentTypes.addHeader(202);
|
||||
const tree = new Formatter().format(contentTypes);
|
||||
|
||||
expect(tree["Types"][14]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
expect(tree["Types"][15]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||
PartName: "/word/header201.xml",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(tree["Types"][15]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
expect(tree["Types"][16]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||
PartName: "/word/header202.xml",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -30,6 +30,7 @@ export class ContentTypes extends XmlComponent {
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.extended-properties+xml", "/docProps/app.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", "/word/numbering.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", "/word/footnotes.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", "/word/settings.xml"));
|
||||
}
|
||||
|
||||
public addFooter(index: number): void {
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IDefaultAttributes {
|
||||
contentType: string;
|
||||
extension?: string;
|
||||
readonly contentType: string;
|
||||
readonly extension?: string;
|
||||
}
|
||||
|
||||
export class DefaultAttributes extends XmlAttributeComponent<IDefaultAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
contentType: "ContentType",
|
||||
extension: "Extension",
|
||||
};
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IOverrideAttributes {
|
||||
contentType: string;
|
||||
partName?: string;
|
||||
readonly contentType: string;
|
||||
readonly partName?: string;
|
||||
}
|
||||
|
||||
export class OverrideAttributes extends XmlAttributeComponent<IOverrideAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
contentType: "ContentType",
|
||||
partName: "PartName",
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../export/formatter";
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { CoreProperties } from "./properties";
|
||||
|
||||
describe("Properties", () => {
|
||||
|
@ -3,14 +3,14 @@ import { DocumentAttributes } from "../document/document-attributes";
|
||||
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
|
||||
|
||||
export interface IPropertiesOptions {
|
||||
title?: string;
|
||||
subject?: string;
|
||||
creator?: string;
|
||||
keywords?: string;
|
||||
description?: string;
|
||||
lastModifiedBy?: string;
|
||||
revision?: string;
|
||||
externalStyles?: string;
|
||||
readonly title?: string;
|
||||
readonly subject?: string;
|
||||
readonly creator?: string;
|
||||
readonly keywords?: string;
|
||||
readonly description?: string;
|
||||
readonly lastModifiedBy?: string;
|
||||
readonly revision?: string;
|
||||
readonly externalStyles?: string;
|
||||
}
|
||||
|
||||
export class CoreProperties extends XmlComponent {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../export/formatter";
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { Body } from "./body";
|
||||
|
||||
describe("Body", () => {
|
||||
@ -16,11 +17,11 @@ describe("Body", () => {
|
||||
expect(formatted)
|
||||
.to.have.property("w:sectPr")
|
||||
.and.to.be.an.instanceof(Array);
|
||||
expect(formatted["w:sectPr"]).to.have.length(7);
|
||||
expect(formatted["w:sectPr"]).to.have.length(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe("addSection", () => {
|
||||
describe("#addSection", () => {
|
||||
it("should add section with options", () => {
|
||||
body.addSection({
|
||||
width: 10000,
|
||||
@ -29,14 +30,95 @@ describe("Body", () => {
|
||||
|
||||
const formatted = new Formatter().format(body)["w:body"];
|
||||
expect(formatted).to.be.an.instanceof(Array);
|
||||
const defaultSectionPr = formatted[0]["w:p"][1]["w:pPr"][0]["w:sectPr"];
|
||||
const defaultSectionPr = formatted[0]["w:p"][0]["w:pPr"][0]["w:sectPr"];
|
||||
|
||||
// check that this is the default section and added first in paragraph
|
||||
expect(defaultSectionPr[0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(defaultSectionPr[0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
|
||||
|
||||
// check for new section (since it's the last one, it's direct child of body)
|
||||
const newSection = formatted[1]["w:sectPr"];
|
||||
expect(newSection[0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 10000, "w:w": 10000, "w:orient": "portrait" } }] });
|
||||
expect(newSection[0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 10000, "w:w": 10000, "w:orient": "portrait" } } });
|
||||
});
|
||||
|
||||
it("should add section with default parameters", () => {
|
||||
body.addSection({
|
||||
width: 10000,
|
||||
height: 10000,
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(body);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:body": [
|
||||
{
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [
|
||||
{
|
||||
"w:sectPr": [
|
||||
{ "w:pgSz": { _attr: { "w:w": 11906, "w:h": 16838, "w:orient": "portrait" } } },
|
||||
{
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:bottom": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:footer": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ "w:cols": { _attr: { "w:space": 708, "w:num": 1 } } },
|
||||
{ "w:docGrid": { _attr: { "w:linePitch": 360 } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:sectPr": [
|
||||
{ "w:pgSz": { _attr: { "w:w": 10000, "w:h": 10000, "w:orient": "portrait" } } },
|
||||
{
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:bottom": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:footer": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ "w:cols": { _attr: { "w:space": 708, "w:num": 1 } } },
|
||||
{ "w:docGrid": { _attr: { "w:linePitch": 360 } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getParagraphs", () => {
|
||||
it("should get no paragraphs", () => {
|
||||
const paragraphs = body.getParagraphs();
|
||||
|
||||
expect(paragraphs).to.be.an.instanceof(Array);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#DefaultSection", () => {
|
||||
it("should get section", () => {
|
||||
const section = body.DefaultSection;
|
||||
|
||||
const tree = new Formatter().format(section);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
||||
import { Paragraph, ParagraphProperties } from "../..";
|
||||
import { Paragraph, ParagraphProperties, TableOfContents } from "../..";
|
||||
import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties";
|
||||
|
||||
export class Body extends XmlComponent {
|
||||
@ -35,11 +35,9 @@ export class Body extends XmlComponent {
|
||||
this.sections.push(new SectionProperties(params));
|
||||
}
|
||||
}
|
||||
public prepForXml(): IXmlableObject {
|
||||
public prepForXml(): IXmlableObject | undefined {
|
||||
if (this.sections.length === 1) {
|
||||
this.root.push(this.sections[0]);
|
||||
} else if (this.sections.length > 1) {
|
||||
throw new Error("Invalid usage of sections. At the end of the body element there must be ONE section.");
|
||||
this.root.push(this.sections.pop() as SectionProperties);
|
||||
}
|
||||
|
||||
return super.prepForXml();
|
||||
@ -53,6 +51,14 @@ export class Body extends XmlComponent {
|
||||
return this.defaultSection;
|
||||
}
|
||||
|
||||
public getTablesOfContents(): TableOfContents[] {
|
||||
return this.root.filter((child) => child instanceof TableOfContents) as TableOfContents[];
|
||||
}
|
||||
|
||||
public getParagraphs(): Paragraph[] {
|
||||
return this.root.filter((child) => child instanceof Paragraph) as Paragraph[];
|
||||
}
|
||||
|
||||
private createSectionParagraph(section: SectionProperties): Paragraph {
|
||||
const paragraph = new Paragraph();
|
||||
const properties = new ParagraphProperties();
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IColumnsAttributes {
|
||||
space?: number;
|
||||
readonly space?: number;
|
||||
readonly num?: number;
|
||||
}
|
||||
|
||||
export class ColumnsAttributes extends XmlAttributeComponent<IColumnsAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
space: "w:space",
|
||||
num: "w:num",
|
||||
};
|
||||
}
|
||||
|
@ -2,11 +2,12 @@ import { XmlComponent } from "file/xml-components";
|
||||
import { ColumnsAttributes } from "./columns-attributes";
|
||||
|
||||
export class Columns extends XmlComponent {
|
||||
constructor(space: number) {
|
||||
constructor(space: number, num: number) {
|
||||
super("w:cols");
|
||||
this.root.push(
|
||||
new ColumnsAttributes({
|
||||
space: space,
|
||||
num: num,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IDocGridAttributesProperties {
|
||||
linePitch?: number;
|
||||
readonly linePitch?: number;
|
||||
}
|
||||
|
||||
export class DocGridAttributes extends XmlAttributeComponent<IDocGridAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
linePitch: "w:linePitch",
|
||||
};
|
||||
}
|
||||
|
@ -7,12 +7,12 @@ export enum FooterReferenceType {
|
||||
}
|
||||
|
||||
export interface IFooterReferenceAttributes {
|
||||
type: string;
|
||||
id: string;
|
||||
readonly type: string;
|
||||
readonly id: string;
|
||||
}
|
||||
|
||||
export class FooterReferenceAttributes extends XmlAttributeComponent<IFooterReferenceAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
type: "w:type",
|
||||
id: "r:id",
|
||||
};
|
||||
|
@ -2,8 +2,8 @@ import { XmlComponent } from "file/xml-components";
|
||||
import { FooterReferenceAttributes, FooterReferenceType } from "./footer-reference-attributes";
|
||||
|
||||
export interface IFooterOptions {
|
||||
footerType?: FooterReferenceType;
|
||||
footerId?: number;
|
||||
readonly footerType?: FooterReferenceType;
|
||||
readonly footerId?: number;
|
||||
}
|
||||
|
||||
export class FooterReference extends XmlComponent {
|
||||
|
@ -7,12 +7,12 @@ export enum HeaderReferenceType {
|
||||
}
|
||||
|
||||
export interface IHeaderReferenceAttributes {
|
||||
type: string;
|
||||
id: string;
|
||||
readonly type: string;
|
||||
readonly id: string;
|
||||
}
|
||||
|
||||
export class HeaderReferenceAttributes extends XmlAttributeComponent<IHeaderReferenceAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
type: "w:type",
|
||||
id: "r:id",
|
||||
};
|
||||
|
@ -2,8 +2,8 @@ import { XmlComponent } from "file/xml-components";
|
||||
import { HeaderReferenceAttributes, HeaderReferenceType } from "./header-reference-attributes";
|
||||
|
||||
export interface IHeaderOptions {
|
||||
headerType?: HeaderReferenceType;
|
||||
headerId?: number;
|
||||
readonly headerType?: HeaderReferenceType;
|
||||
readonly headerId?: number;
|
||||
}
|
||||
|
||||
export class HeaderReference extends XmlComponent {
|
||||
|
@ -4,3 +4,4 @@ export * from "./header-reference";
|
||||
export * from "./page-size";
|
||||
export * from "./page-number";
|
||||
export * from "./page-border";
|
||||
export * from "./line-number";
|
||||
|
@ -0,0 +1 @@
|
||||
export * from "./line-number";
|
@ -0,0 +1,38 @@
|
||||
// http://officeopenxml.com/WPsectionLineNumbering.php
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
export enum LineNumberRestartFormat {
|
||||
CONTINUOUS = "continuous",
|
||||
NEW_SECTION = "newSection",
|
||||
NEW_PAGE = "newPage",
|
||||
}
|
||||
|
||||
export interface ILineNumberAttributes {
|
||||
readonly lineNumberCountBy?: number;
|
||||
readonly lineNumberStart?: number;
|
||||
readonly lineNumberRestart?: LineNumberRestartFormat;
|
||||
readonly lineNumberDistance?: number;
|
||||
}
|
||||
|
||||
export class LineNumberAttributes extends XmlAttributeComponent<ILineNumberAttributes> {
|
||||
protected readonly xmlKeys = {
|
||||
lineNumberCountBy: "w:countBy",
|
||||
lineNumberStart: "w:start",
|
||||
lineNumberRestart: "w:restart",
|
||||
lineNumberDistance: "w:distance",
|
||||
};
|
||||
}
|
||||
|
||||
export class LineNumberType extends XmlComponent {
|
||||
constructor(countBy?: number, start?: number, restart?: LineNumberRestartFormat, dist?: number) {
|
||||
super("w:lnNumType");
|
||||
this.root.push(
|
||||
new LineNumberAttributes({
|
||||
lineNumberCountBy: countBy,
|
||||
lineNumberStart: start,
|
||||
lineNumberRestart: restart,
|
||||
lineNumberDistance: dist,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,16 +1,15 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../../../export/formatter";
|
||||
import { BorderStyle } from "../../../../styles";
|
||||
import { Formatter } from "export/formatter";
|
||||
import { BorderStyle } from "file/styles";
|
||||
|
||||
import { PageBorderDisplay, PageBorders, PageBorderZOrder } from "./page-borders";
|
||||
|
||||
describe("PageBorders", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("should create empty element when no options are passed", () => {
|
||||
const properties = new PageBorders();
|
||||
const tree = new Formatter().format(properties);
|
||||
|
||||
expect(tree).to.equal("");
|
||||
expect(() => new Formatter().format(properties)).to.throw();
|
||||
});
|
||||
|
||||
it("should create page borders with some configuration", () => {
|
||||
@ -22,8 +21,7 @@ describe("PageBorders", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:pgBorders"]);
|
||||
expect(tree["w:pgBorders"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:pgBorders"][0]).to.deep.equal({ _attr: { "w:display": "firstPage" } });
|
||||
expect(tree["w:pgBorders"]).to.deep.equal({ _attr: { "w:display": "firstPage" } });
|
||||
});
|
||||
|
||||
it("should create page borders with full configuration", () => {
|
||||
@ -59,32 +57,24 @@ describe("PageBorders", () => {
|
||||
expect(tree["w:pgBorders"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:pgBorders"][0]).to.deep.equal({ _attr: { "w:display": "firstPage", "w:zOrder": "back" } });
|
||||
expect(tree["w:pgBorders"][1]).to.deep.equal({
|
||||
"w:top": [
|
||||
{
|
||||
"w:top": {
|
||||
_attr: { "w:color": "001122", "w:size": 10, "w:val": "doubleWave" },
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(tree["w:pgBorders"][2]).to.deep.equal({
|
||||
"w:right": [
|
||||
{
|
||||
"w:right": {
|
||||
_attr: { "w:color": "223344", "w:size": 20, "w:val": "double" },
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(tree["w:pgBorders"][3]).to.deep.equal({
|
||||
"w:bottom": [
|
||||
{
|
||||
"w:bottom": {
|
||||
_attr: { "w:color": "556677", "w:size": 30, "w:val": "single" },
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(tree["w:pgBorders"][4]).to.deep.equal({
|
||||
"w:left": [
|
||||
{
|
||||
"w:left": {
|
||||
_attr: { "w:color": "889900", "w:size": 40, "w:val": "dotted" },
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
// http://officeopenxml.com/WPsectionBorders.php
|
||||
import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
import { BorderStyle } from "../../../../styles";
|
||||
import { BorderStyle } from "file/styles";
|
||||
import { IgnoreIfEmptyXmlComponent, XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
export enum PageBorderDisplay {
|
||||
ALL_PAGES = "allPages",
|
||||
@ -19,28 +19,28 @@ export enum PageBorderZOrder {
|
||||
}
|
||||
|
||||
export interface IPageBorderAttributes {
|
||||
display?: PageBorderDisplay;
|
||||
offsetFrom?: PageBorderOffsetFrom;
|
||||
zOrder?: PageBorderZOrder;
|
||||
readonly display?: PageBorderDisplay;
|
||||
readonly offsetFrom?: PageBorderOffsetFrom;
|
||||
readonly zOrder?: PageBorderZOrder;
|
||||
}
|
||||
|
||||
export interface IPageBorderConfiguration {
|
||||
style?: BorderStyle;
|
||||
size?: number;
|
||||
color?: string;
|
||||
space?: number;
|
||||
readonly style?: BorderStyle;
|
||||
readonly size?: number;
|
||||
readonly color?: string;
|
||||
readonly space?: number;
|
||||
}
|
||||
|
||||
export interface IPageBordersOptions {
|
||||
pageBorders?: IPageBorderAttributes;
|
||||
pageBorderTop?: IPageBorderConfiguration;
|
||||
pageBorderRight?: IPageBorderConfiguration;
|
||||
pageBorderBottom?: IPageBorderConfiguration;
|
||||
pageBorderLeft?: IPageBorderConfiguration;
|
||||
readonly pageBorders?: IPageBorderAttributes;
|
||||
readonly pageBorderTop?: IPageBorderConfiguration;
|
||||
readonly pageBorderRight?: IPageBorderConfiguration;
|
||||
readonly pageBorderBottom?: IPageBorderConfiguration;
|
||||
readonly pageBorderLeft?: IPageBorderConfiguration;
|
||||
}
|
||||
|
||||
class PageBordeAttributes extends XmlAttributeComponent<IPageBorderConfiguration> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
style: "w:val",
|
||||
size: "w:size",
|
||||
color: "w:color",
|
||||
@ -57,14 +57,14 @@ class PageBorder extends XmlComponent {
|
||||
}
|
||||
|
||||
class PageBordersAttributes extends XmlAttributeComponent<IPageBorderAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
display: "w:display",
|
||||
offsetFrom: "w:offsetFrom",
|
||||
zOrder: "w:zOrder",
|
||||
};
|
||||
}
|
||||
|
||||
export class PageBorders extends XmlComponent {
|
||||
export class PageBorders extends IgnoreIfEmptyXmlComponent {
|
||||
constructor(options?: IPageBordersOptions) {
|
||||
super("w:pgBorders");
|
||||
|
||||
@ -97,8 +97,4 @@ export class PageBorders extends XmlComponent {
|
||||
this.root.push(new PageBorder("w:left", options.pageBorderLeft));
|
||||
}
|
||||
}
|
||||
|
||||
public prepForXml(): IXmlableObject {
|
||||
return this.root.length > 0 ? super.prepForXml() : "";
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IPageMarginAttributes {
|
||||
top?: number;
|
||||
right?: number;
|
||||
bottom?: number;
|
||||
left?: number;
|
||||
header?: number;
|
||||
footer?: number;
|
||||
gutter?: number;
|
||||
mirror?: boolean;
|
||||
readonly top?: number;
|
||||
readonly right?: number;
|
||||
readonly bottom?: number;
|
||||
readonly left?: number;
|
||||
readonly header?: number;
|
||||
readonly footer?: number;
|
||||
readonly gutter?: number;
|
||||
readonly mirror?: boolean;
|
||||
}
|
||||
|
||||
export class PageMarginAttributes extends XmlAttributeComponent<IPageMarginAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
top: "w:top",
|
||||
right: "w:right",
|
||||
bottom: "w:bottom",
|
||||
|
@ -17,12 +17,12 @@ export enum PageNumberFormat {
|
||||
}
|
||||
|
||||
export interface IPageNumberTypeAttributes {
|
||||
pageNumberStart?: number;
|
||||
pageNumberFormatType?: PageNumberFormat;
|
||||
readonly pageNumberStart?: number;
|
||||
readonly pageNumberFormatType?: PageNumberFormat;
|
||||
}
|
||||
|
||||
export class PageNumberTypeAttributes extends XmlAttributeComponent<IPageNumberTypeAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
pageNumberStart: "w:start",
|
||||
pageNumberFormatType: "w:fmt",
|
||||
};
|
||||
|
@ -6,13 +6,13 @@ export enum PageOrientation {
|
||||
}
|
||||
|
||||
export interface IPageSizeAttributes {
|
||||
width?: number;
|
||||
height?: number;
|
||||
orientation?: PageOrientation;
|
||||
readonly width?: number;
|
||||
readonly height?: number;
|
||||
readonly orientation?: PageOrientation;
|
||||
}
|
||||
|
||||
export class PageSizeAttributes extends XmlAttributeComponent<IPageSizeAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
width: "w:w",
|
||||
height: "w:h",
|
||||
orientation: "w:orient",
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../../../export/formatter";
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { PageSize } from "./page-size";
|
||||
import { PageOrientation } from "./page-size-attributes";
|
||||
|
||||
@ -11,8 +12,7 @@ describe("PageSize", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
|
||||
expect(tree["w:pgSz"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:pgSz"][0]).to.deep.equal({ _attr: { "w:h": 200, "w:w": 100, "w:orient": "portrait" } });
|
||||
expect(tree["w:pgSz"]).to.deep.equal({ _attr: { "w:h": 200, "w:w": 100, "w:orient": "portrait" } });
|
||||
});
|
||||
|
||||
it("should create page size with horizontal and invert the lengths", () => {
|
||||
@ -20,8 +20,7 @@ describe("PageSize", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
|
||||
expect(tree["w:pgSz"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:pgSz"][0]).to.deep.equal({ _attr: { "w:h": 100, "w:w": 200, "w:orient": "landscape" } });
|
||||
expect(tree["w:pgSz"]).to.deep.equal({ _attr: { "w:h": 100, "w:w": 200, "w:orient": "landscape" } });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,12 +1,19 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../../export/formatter";
|
||||
import { FooterReferenceType, PageBorderOffsetFrom, PageNumberFormat } from "./";
|
||||
import { Formatter } from "export/formatter";
|
||||
import { FooterWrapper } from "file/footer-wrapper";
|
||||
import { HeaderWrapper } from "file/header-wrapper";
|
||||
import { Media } from "file/media";
|
||||
|
||||
import { PageBorderOffsetFrom } from "./page-border";
|
||||
import { PageNumberFormat } from "./page-number";
|
||||
import { SectionProperties } from "./section-properties";
|
||||
|
||||
describe("SectionProperties", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("should create section properties with options", () => {
|
||||
const media = new Media();
|
||||
|
||||
const properties = new SectionProperties({
|
||||
width: 11906,
|
||||
height: 16838,
|
||||
@ -18,21 +25,26 @@ describe("SectionProperties", () => {
|
||||
footer: 708,
|
||||
gutter: 0,
|
||||
mirror: false,
|
||||
column: {
|
||||
space: 708,
|
||||
count: 1,
|
||||
},
|
||||
linePitch: 360,
|
||||
headerId: 100,
|
||||
footerId: 200,
|
||||
footerType: FooterReferenceType.EVEN,
|
||||
headers: {
|
||||
default: new HeaderWrapper(media, 100),
|
||||
},
|
||||
footers: {
|
||||
even: new FooterWrapper(media, 200),
|
||||
},
|
||||
pageNumberStart: 10,
|
||||
pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT,
|
||||
});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
@ -44,14 +56,13 @@ describe("SectionProperties", () => {
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] });
|
||||
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] });
|
||||
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": [{ _attr: { "r:id": "rId100", "w:type": "default" } }] });
|
||||
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": [{ _attr: { "r:id": "rId200", "w:type": "even" } }] });
|
||||
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "cardinalText", "w:start": 10 } }] });
|
||||
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": { _attr: { "w:space": 708, "w:num": 1 } } });
|
||||
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": { _attr: { "w:linePitch": 360 } } });
|
||||
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": { _attr: { "r:id": "rId100", "w:type": "default" } } });
|
||||
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": { _attr: { "r:id": "rId200", "w:type": "even" } } });
|
||||
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": { _attr: { "w:fmt": "cardinalText", "w:start": 10 } } });
|
||||
});
|
||||
|
||||
it("should create section properties with no options", () => {
|
||||
@ -59,10 +70,9 @@ describe("SectionProperties", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
@ -74,13 +84,9 @@ describe("SectionProperties", () => {
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] });
|
||||
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] });
|
||||
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": [{ _attr: { "r:id": "rId0", "w:type": "default" } }] });
|
||||
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": [{ _attr: { "r:id": "rId0", "w:type": "default" } }] });
|
||||
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] });
|
||||
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": { _attr: { "w:space": 708, "w:num": 1 } } });
|
||||
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": { _attr: { "w:linePitch": 360 } } });
|
||||
});
|
||||
|
||||
it("should create section properties with changed options", () => {
|
||||
@ -90,10 +96,9 @@ describe("SectionProperties", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
@ -105,7 +110,6 @@ describe("SectionProperties", () => {
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
@ -116,10 +120,9 @@ describe("SectionProperties", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:bottom": 0,
|
||||
"w:footer": 708,
|
||||
@ -131,7 +134,6 @@ describe("SectionProperties", () => {
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
@ -143,10 +145,9 @@ describe("SectionProperties", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 0, "w:w": 0, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 0, "w:w": 0, "w:orient": "portrait" } } });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
@ -158,7 +159,6 @@ describe("SectionProperties", () => {
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
@ -170,9 +170,30 @@ describe("SectionProperties", () => {
|
||||
});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"][7]).to.deep.equal({
|
||||
"w:pgBorders": [{ _attr: { "w:offsetFrom": "page" } }],
|
||||
const pgBorders = tree["w:sectPr"].find((item) => item["w:pgBorders"] !== undefined);
|
||||
expect(pgBorders).to.deep.equal({
|
||||
"w:pgBorders": { _attr: { "w:offsetFrom": "page" } },
|
||||
});
|
||||
});
|
||||
|
||||
it("should create section properties with page number type, but without start attribute", () => {
|
||||
const properties = new SectionProperties({
|
||||
pageNumberFormatType: PageNumberFormat.UPPER_ROMAN,
|
||||
});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
const pgNumType = tree["w:sectPr"].find((item) => item["w:pgNumType"] !== undefined);
|
||||
expect(pgNumType).to.deep.equal({
|
||||
"w:pgNumType": { _attr: { "w:fmt": "upperRoman" } },
|
||||
});
|
||||
});
|
||||
|
||||
it("should create section properties without page number type", () => {
|
||||
const properties = new SectionProperties({});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
const pgNumType = tree["w:sectPr"].find((item) => item["w:pgNumType"] !== undefined);
|
||||
expect(pgNumType).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,115 +1,188 @@
|
||||
// http://officeopenxml.com/WPsection.php
|
||||
import { FooterWrapper } from "file/footer-wrapper";
|
||||
import { HeaderWrapper } from "file/header-wrapper";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { FooterReferenceType, IPageBordersOptions, IPageNumberTypeAttributes, PageBorders, PageNumberFormat, PageNumberType } from "./";
|
||||
|
||||
import { Columns } from "./columns/columns";
|
||||
import { IColumnsAttributes } from "./columns/columns-attributes";
|
||||
import { DocumentGrid } from "./doc-grid/doc-grid";
|
||||
import { IDocGridAttributesProperties } from "./doc-grid/doc-grid-attributes";
|
||||
import { FooterReference, IFooterOptions } from "./footer-reference/footer-reference";
|
||||
import { HeaderReference, IHeaderOptions } from "./header-reference/header-reference";
|
||||
import { HeaderReferenceType } from "./header-reference/header-reference-attributes";
|
||||
import { FooterReferenceType } from "./footer-reference";
|
||||
import { FooterReference } from "./footer-reference/footer-reference";
|
||||
import { HeaderReferenceType } from "./header-reference";
|
||||
import { HeaderReference } from "./header-reference/header-reference";
|
||||
import { ILineNumberAttributes, LineNumberType } from "./line-number";
|
||||
import { IPageBordersOptions, PageBorders } from "./page-border";
|
||||
import { PageMargin } from "./page-margin/page-margin";
|
||||
import { IPageMarginAttributes } from "./page-margin/page-margin-attributes";
|
||||
import { IPageNumberTypeAttributes, PageNumberType } from "./page-number";
|
||||
import { PageSize } from "./page-size/page-size";
|
||||
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
|
||||
import { TitlePage } from "./title-page/title-page";
|
||||
|
||||
export interface IHeaderFooterGroup<T> {
|
||||
readonly default?: T;
|
||||
readonly first?: T;
|
||||
readonly even?: T;
|
||||
}
|
||||
|
||||
interface IHeadersOptions {
|
||||
readonly headers?: IHeaderFooterGroup<HeaderWrapper>;
|
||||
}
|
||||
|
||||
interface IFootersOptions {
|
||||
readonly footers?: IHeaderFooterGroup<FooterWrapper>;
|
||||
}
|
||||
|
||||
interface ITitlePageOptions {
|
||||
readonly titlePage?: boolean;
|
||||
}
|
||||
|
||||
export type SectionPropertiesOptions = IPageSizeAttributes &
|
||||
IPageMarginAttributes &
|
||||
IColumnsAttributes &
|
||||
IDocGridAttributesProperties &
|
||||
IHeaderOptions &
|
||||
IFooterOptions &
|
||||
IHeadersOptions &
|
||||
IFootersOptions &
|
||||
IPageNumberTypeAttributes &
|
||||
IPageBordersOptions;
|
||||
ILineNumberAttributes &
|
||||
IPageBordersOptions &
|
||||
ITitlePageOptions & {
|
||||
readonly column?: {
|
||||
readonly space?: number;
|
||||
readonly count?: number;
|
||||
};
|
||||
};
|
||||
// Need to decouple this from the attributes
|
||||
|
||||
export class SectionProperties extends XmlComponent {
|
||||
private readonly options: SectionPropertiesOptions;
|
||||
|
||||
constructor(options?: SectionPropertiesOptions) {
|
||||
constructor(options: SectionPropertiesOptions = { column: {} }) {
|
||||
super("w:sectPr");
|
||||
|
||||
const defaultOptions = {
|
||||
width: 11906,
|
||||
height: 16838,
|
||||
top: 1440,
|
||||
right: 1440,
|
||||
bottom: 1440,
|
||||
left: 1440,
|
||||
header: 708,
|
||||
footer: 708,
|
||||
gutter: 0,
|
||||
mirror: false,
|
||||
space: 708,
|
||||
linePitch: 360,
|
||||
orientation: PageOrientation.PORTRAIT,
|
||||
headerType: HeaderReferenceType.DEFAULT,
|
||||
headerId: 0,
|
||||
footerType: FooterReferenceType.DEFAULT,
|
||||
footerId: 0,
|
||||
pageNumberStart: undefined,
|
||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||
pageBorders: undefined,
|
||||
pageBorderTop: undefined,
|
||||
pageBorderRight: undefined,
|
||||
pageBorderBottom: undefined,
|
||||
pageBorderLeft: undefined,
|
||||
};
|
||||
const {
|
||||
width = 11906,
|
||||
height = 16838,
|
||||
top = 1440,
|
||||
right = 1440,
|
||||
bottom = 1440,
|
||||
left = 1440,
|
||||
header = 708,
|
||||
footer = 708,
|
||||
gutter = 0,
|
||||
mirror = false,
|
||||
column = {},
|
||||
linePitch = 360,
|
||||
orientation = PageOrientation.PORTRAIT,
|
||||
headers,
|
||||
footers,
|
||||
pageNumberFormatType,
|
||||
pageNumberStart,
|
||||
lineNumberCountBy,
|
||||
lineNumberStart,
|
||||
lineNumberRestart,
|
||||
lineNumberDistance,
|
||||
pageBorders,
|
||||
pageBorderTop,
|
||||
pageBorderRight,
|
||||
pageBorderBottom,
|
||||
pageBorderLeft,
|
||||
titlePage = false,
|
||||
} = options;
|
||||
|
||||
const mergedOptions = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
};
|
||||
this.options = options;
|
||||
this.root.push(new PageSize(width, height, orientation));
|
||||
this.root.push(new PageMargin(top, right, bottom, left, header, footer, gutter, mirror));
|
||||
this.root.push(new Columns(column.space ? column.space : 708, column.count ? column.count : 1));
|
||||
this.root.push(new DocumentGrid(linePitch));
|
||||
|
||||
this.root.push(new PageSize(mergedOptions.width, mergedOptions.height, mergedOptions.orientation));
|
||||
this.root.push(
|
||||
new PageMargin(
|
||||
mergedOptions.top,
|
||||
mergedOptions.right,
|
||||
mergedOptions.bottom,
|
||||
mergedOptions.left,
|
||||
mergedOptions.header,
|
||||
mergedOptions.footer,
|
||||
mergedOptions.gutter,
|
||||
mergedOptions.mirror,
|
||||
),
|
||||
);
|
||||
this.root.push(new Columns(mergedOptions.space));
|
||||
this.root.push(new DocumentGrid(mergedOptions.linePitch));
|
||||
this.addHeaders(headers);
|
||||
this.addFooters(footers);
|
||||
|
||||
this.root.push(
|
||||
new HeaderReference({
|
||||
headerType: mergedOptions.headerType,
|
||||
headerId: mergedOptions.headerId,
|
||||
}),
|
||||
);
|
||||
this.root.push(
|
||||
new FooterReference({
|
||||
footerType: mergedOptions.footerType,
|
||||
footerId: mergedOptions.footerId,
|
||||
}),
|
||||
);
|
||||
if (pageNumberStart || pageNumberFormatType) {
|
||||
this.root.push(new PageNumberType(pageNumberStart, pageNumberFormatType));
|
||||
}
|
||||
|
||||
this.root.push(new PageNumberType(mergedOptions.pageNumberStart, mergedOptions.pageNumberFormatType));
|
||||
if (lineNumberCountBy || lineNumberStart || lineNumberRestart || lineNumberDistance) {
|
||||
this.root.push(new LineNumberType(lineNumberCountBy, lineNumberStart, lineNumberRestart, lineNumberDistance));
|
||||
}
|
||||
|
||||
if (
|
||||
mergedOptions.pageBorders ||
|
||||
mergedOptions.pageBorderTop ||
|
||||
mergedOptions.pageBorderRight ||
|
||||
mergedOptions.pageBorderBottom ||
|
||||
mergedOptions.pageBorderLeft
|
||||
) {
|
||||
if (pageBorders || pageBorderTop || pageBorderRight || pageBorderBottom || pageBorderLeft) {
|
||||
this.root.push(
|
||||
new PageBorders({
|
||||
pageBorders: mergedOptions.pageBorders,
|
||||
pageBorderTop: mergedOptions.pageBorderTop,
|
||||
pageBorderRight: mergedOptions.pageBorderRight,
|
||||
pageBorderBottom: mergedOptions.pageBorderBottom,
|
||||
pageBorderLeft: mergedOptions.pageBorderLeft,
|
||||
pageBorders: pageBorders,
|
||||
pageBorderTop: pageBorderTop,
|
||||
pageBorderRight: pageBorderRight,
|
||||
pageBorderBottom: pageBorderBottom,
|
||||
pageBorderLeft: pageBorderLeft,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
this.options = mergedOptions;
|
||||
if (titlePage) {
|
||||
this.root.push(new TitlePage());
|
||||
}
|
||||
}
|
||||
|
||||
private addHeaders(headers?: IHeaderFooterGroup<HeaderWrapper>): void {
|
||||
if (headers) {
|
||||
if (headers.default) {
|
||||
this.root.push(
|
||||
new HeaderReference({
|
||||
headerType: HeaderReferenceType.DEFAULT,
|
||||
headerId: headers.default.Header.ReferenceId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (headers.first) {
|
||||
this.root.push(
|
||||
new HeaderReference({
|
||||
headerType: HeaderReferenceType.FIRST,
|
||||
headerId: headers.first.Header.ReferenceId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (headers.even) {
|
||||
this.root.push(
|
||||
new HeaderReference({
|
||||
headerType: HeaderReferenceType.EVEN,
|
||||
headerId: headers.even.Header.ReferenceId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private addFooters(footers?: IHeaderFooterGroup<FooterWrapper>): void {
|
||||
if (footers) {
|
||||
if (footers.default) {
|
||||
this.root.push(
|
||||
new FooterReference({
|
||||
footerType: FooterReferenceType.DEFAULT,
|
||||
footerId: footers.default.Footer.ReferenceId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (footers.first) {
|
||||
this.root.push(
|
||||
new FooterReference({
|
||||
footerType: FooterReferenceType.FIRST,
|
||||
footerId: footers.first.Footer.ReferenceId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (footers.even) {
|
||||
this.root.push(
|
||||
new FooterReference({
|
||||
footerType: FooterReferenceType.EVEN,
|
||||
footerId: footers.even.Footer.ReferenceId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get Options(): SectionPropertiesOptions {
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IHeaderReferenceAttributes {
|
||||
value: string;
|
||||
readonly value: string;
|
||||
}
|
||||
|
||||
export class TitlePageAttributes extends XmlAttributeComponent<IHeaderReferenceAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
value: "w:val",
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../../../export/formatter";
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { TitlePage } from "./title-page";
|
||||
|
||||
describe("PageSize", () => {
|
||||
@ -10,8 +11,7 @@ describe("PageSize", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:titlePg"]);
|
||||
expect(tree["w:titlePg"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:titlePg"][0]).to.deep.equal({ _attr: { "w:val": "1" } });
|
||||
expect(tree["w:titlePg"]).to.deep.equal({ _attr: { "w:val": "1" } });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,33 +1,33 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IDocumentAttributesProperties {
|
||||
wpc?: string;
|
||||
mc?: string;
|
||||
o?: string;
|
||||
r?: string;
|
||||
m?: string;
|
||||
v?: string;
|
||||
wp14?: string;
|
||||
wp?: string;
|
||||
w10?: string;
|
||||
w?: string;
|
||||
w14?: string;
|
||||
w15?: string;
|
||||
wpg?: string;
|
||||
wpi?: string;
|
||||
wne?: string;
|
||||
wps?: string;
|
||||
Ignorable?: string;
|
||||
cp?: string;
|
||||
dc?: string;
|
||||
dcterms?: string;
|
||||
dcmitype?: string;
|
||||
xsi?: string;
|
||||
type?: string;
|
||||
readonly wpc?: string;
|
||||
readonly mc?: string;
|
||||
readonly o?: string;
|
||||
readonly r?: string;
|
||||
readonly m?: string;
|
||||
readonly v?: string;
|
||||
readonly wp14?: string;
|
||||
readonly wp?: string;
|
||||
readonly w10?: string;
|
||||
readonly w?: string;
|
||||
readonly w14?: string;
|
||||
readonly w15?: string;
|
||||
readonly wpg?: string;
|
||||
readonly wpi?: string;
|
||||
readonly wne?: string;
|
||||
readonly wps?: string;
|
||||
readonly Ignorable?: string;
|
||||
readonly cp?: string;
|
||||
readonly dc?: string;
|
||||
readonly dcterms?: string;
|
||||
readonly dcmitype?: string;
|
||||
readonly xsi?: string;
|
||||
readonly type?: string;
|
||||
}
|
||||
|
||||
export class DocumentAttributes extends XmlAttributeComponent<IDocumentAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
wpc: "xmlns:wpc",
|
||||
mc: "xmlns:mc",
|
||||
o: "xmlns:o",
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { assert, expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../export/formatter";
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { Paragraph } from "../paragraph";
|
||||
import { Table } from "../table";
|
||||
import { Document } from "./document";
|
||||
@ -51,14 +52,17 @@ describe("Document", () => {
|
||||
expect(body[0])
|
||||
.to.have.property("w:p")
|
||||
.which.includes({
|
||||
"w:r": [{ "w:rPr": [] }, { "w:t": [{ _attr: { "xml:space": "preserve" } }, "sample paragraph text"] }],
|
||||
"w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "sample paragraph text"] }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createTable", () => {
|
||||
it("should create a new table and append it to body", () => {
|
||||
const table = document.createTable(2, 3);
|
||||
const table = document.createTable({
|
||||
rows: 2,
|
||||
columns: 3,
|
||||
});
|
||||
expect(table).to.be.an.instanceof(Table);
|
||||
const body = new Formatter().format(document)["w:document"][1]["w:body"];
|
||||
expect(body)
|
||||
@ -68,7 +72,10 @@ describe("Document", () => {
|
||||
});
|
||||
|
||||
it("should create a table with the correct dimensions", () => {
|
||||
document.createTable(2, 3);
|
||||
document.createTable({
|
||||
rows: 2,
|
||||
columns: 3,
|
||||
});
|
||||
const body = new Formatter().format(document)["w:document"][1]["w:body"];
|
||||
expect(body)
|
||||
.to.be.an("array")
|
||||
@ -77,9 +84,9 @@ describe("Document", () => {
|
||||
.to.have.property("w:tbl")
|
||||
.which.includes({
|
||||
"w:tblGrid": [
|
||||
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
|
||||
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
|
||||
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
|
||||
{ "w:gridCol": { _attr: { "w:w": 100 } } },
|
||||
{ "w:gridCol": { _attr: { "w:w": 100 } } },
|
||||
{ "w:gridCol": { _attr: { "w:w": 100 } } },
|
||||
],
|
||||
});
|
||||
expect(body[0]["w:tbl"].filter((x) => x["w:tr"])).to.have.length(2);
|
||||
|
@ -1,7 +1,8 @@
|
||||
// http://officeopenxml.com/WPdocument.php
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { Paragraph } from "../paragraph";
|
||||
import { Table } from "../table";
|
||||
import { ITableOptions, Table } from "../table";
|
||||
import { TableOfContents } from "../table-of-contents";
|
||||
import { Body } from "./body";
|
||||
import { SectionPropertiesOptions } from "./body/section-properties";
|
||||
import { DocumentAttributes } from "./document-attributes";
|
||||
@ -41,18 +42,24 @@ export class Document extends XmlComponent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public addTableOfContents(toc: TableOfContents): Document {
|
||||
this.body.push(toc);
|
||||
return this;
|
||||
}
|
||||
|
||||
public createParagraph(text?: string): Paragraph {
|
||||
const para = new Paragraph(text);
|
||||
this.addParagraph(para);
|
||||
return para;
|
||||
}
|
||||
|
||||
public addTable(table: Table): void {
|
||||
public addTable(table: Table): Document {
|
||||
this.body.push(table);
|
||||
return this;
|
||||
}
|
||||
|
||||
public createTable(rows: number, cols: number): Table {
|
||||
const table = new Table(rows, cols);
|
||||
public createTable(options: ITableOptions): Table {
|
||||
const table = new Table(options);
|
||||
this.addTable(table);
|
||||
return table;
|
||||
}
|
||||
@ -60,4 +67,12 @@ export class Document extends XmlComponent {
|
||||
public get Body(): Body {
|
||||
return this.body;
|
||||
}
|
||||
|
||||
public getTablesOfContents(): TableOfContents[] {
|
||||
return this.body.getTablesOfContents();
|
||||
}
|
||||
|
||||
public getParagraphs(): Paragraph[] {
|
||||
return this.body.getParagraphs();
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,16 @@ import { XmlAttributeComponent } from "file/xml-components";
|
||||
import { IDistance } from "../drawing";
|
||||
|
||||
export interface IAnchorAttributes extends IDistance {
|
||||
allowOverlap?: "0" | "1";
|
||||
behindDoc?: "0" | "1";
|
||||
layoutInCell?: "0" | "1";
|
||||
locked?: "0" | "1";
|
||||
relativeHeight?: number;
|
||||
simplePos?: "0" | "1";
|
||||
readonly allowOverlap?: "0" | "1";
|
||||
readonly behindDoc?: "0" | "1";
|
||||
readonly layoutInCell?: "0" | "1";
|
||||
readonly locked?: "0" | "1";
|
||||
readonly relativeHeight?: number;
|
||||
readonly simplePos?: "0" | "1";
|
||||
}
|
||||
|
||||
export class AnchorAttributes extends XmlAttributeComponent<IAnchorAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
distT: "distT",
|
||||
distB: "distB",
|
||||
distL: "distL",
|
||||
|
@ -1,12 +1,27 @@
|
||||
import { assert } from "chai";
|
||||
|
||||
import { Utility } from "../../../tests/utility";
|
||||
import { IDrawingOptions, TextWrapStyle } from ".././";
|
||||
import { Anchor } from "./";
|
||||
import { Utility } from "tests/utility";
|
||||
|
||||
function createDrawing(drawingOptions: IDrawingOptions): Anchor {
|
||||
import { IDrawingOptions } from "../drawing";
|
||||
import { TextWrappingType } from "../text-wrap";
|
||||
import { Anchor } from "./anchor";
|
||||
|
||||
function createAnchor(drawingOptions: IDrawingOptions): Anchor {
|
||||
return new Anchor(
|
||||
1,
|
||||
{
|
||||
fileName: "test.png",
|
||||
stream: new Buffer(""),
|
||||
dimensions: {
|
||||
pixels: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
emus: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pixels: {
|
||||
x: 100,
|
||||
@ -26,14 +41,32 @@ describe("Anchor", () => {
|
||||
|
||||
describe("#constructor()", () => {
|
||||
it("should create a Drawing with correct root key", () => {
|
||||
anchor = createDrawing({});
|
||||
anchor = createAnchor({
|
||||
floating: {
|
||||
verticalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
horizontalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
const newJson = Utility.jsonify(anchor);
|
||||
assert.equal(newJson.rootKey, "wp:anchor");
|
||||
assert.equal(newJson.root.length, 10);
|
||||
});
|
||||
|
||||
it("should create a Drawing with all default options", () => {
|
||||
anchor = createDrawing({});
|
||||
anchor = createAnchor({
|
||||
floating: {
|
||||
verticalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
horizontalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
const newJson = Utility.jsonify(anchor);
|
||||
assert.equal(newJson.root.length, 10);
|
||||
|
||||
@ -58,7 +91,7 @@ describe("Anchor", () => {
|
||||
const horizontalPosition = newJson.root[2];
|
||||
assert.equal(horizontalPosition.rootKey, "wp:positionH");
|
||||
assert.include(horizontalPosition.root[0].root, {
|
||||
relativeFrom: "column",
|
||||
relativeFrom: "page",
|
||||
});
|
||||
assert.equal(horizontalPosition.root[1].rootKey, "wp:posOffset");
|
||||
assert.include(horizontalPosition.root[1].root[0], 0);
|
||||
@ -67,7 +100,7 @@ describe("Anchor", () => {
|
||||
const verticalPosition = newJson.root[3];
|
||||
assert.equal(verticalPosition.rootKey, "wp:positionV");
|
||||
assert.include(verticalPosition.root[0].root, {
|
||||
relativeFrom: "paragraph",
|
||||
relativeFrom: "page",
|
||||
});
|
||||
assert.equal(verticalPosition.root[1].rootKey, "wp:posOffset");
|
||||
assert.include(verticalPosition.root[1].root[0], 0);
|
||||
@ -101,10 +134,18 @@ describe("Anchor", () => {
|
||||
assert.equal(graphic.rootKey, "a:graphic");
|
||||
});
|
||||
|
||||
it("should create a Drawing with text wrapping", () => {
|
||||
anchor = createDrawing({
|
||||
textWrapping: {
|
||||
textWrapStyle: TextWrapStyle.SQUARE,
|
||||
it("should create a Drawing with square text wrapping", () => {
|
||||
anchor = createAnchor({
|
||||
floating: {
|
||||
verticalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
horizontalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
wrap: {
|
||||
type: TextWrappingType.SQUARE,
|
||||
},
|
||||
},
|
||||
});
|
||||
const newJson = Utility.jsonify(anchor);
|
||||
@ -114,5 +155,68 @@ describe("Anchor", () => {
|
||||
const textWrap = newJson.root[6];
|
||||
assert.equal(textWrap.rootKey, "wp:wrapSquare");
|
||||
});
|
||||
|
||||
it("should create a Drawing with no text wrapping", () => {
|
||||
anchor = createAnchor({
|
||||
floating: {
|
||||
verticalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
horizontalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
wrap: {
|
||||
type: TextWrappingType.NONE,
|
||||
},
|
||||
},
|
||||
});
|
||||
const newJson = Utility.jsonify(anchor);
|
||||
assert.equal(newJson.root.length, 10);
|
||||
|
||||
const textWrap = newJson.root[6];
|
||||
assert.equal(textWrap.rootKey, "wp:wrapNone");
|
||||
});
|
||||
|
||||
it("should create a Drawing with tight text wrapping", () => {
|
||||
anchor = createAnchor({
|
||||
floating: {
|
||||
horizontalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
verticalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
wrap: {
|
||||
type: TextWrappingType.TIGHT,
|
||||
},
|
||||
},
|
||||
});
|
||||
const newJson = Utility.jsonify(anchor);
|
||||
assert.equal(newJson.root.length, 10);
|
||||
|
||||
const textWrap = newJson.root[6];
|
||||
assert.equal(textWrap.rootKey, "wp:wrapTight");
|
||||
});
|
||||
|
||||
it("should create a Drawing with tight text wrapping", () => {
|
||||
anchor = createAnchor({
|
||||
floating: {
|
||||
verticalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
horizontalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
wrap: {
|
||||
type: TextWrappingType.TOP_AND_BOTTOM,
|
||||
},
|
||||
},
|
||||
});
|
||||
const newJson = Utility.jsonify(anchor);
|
||||
assert.equal(newJson.root.length, 10);
|
||||
|
||||
const textWrap = newJson.root[6];
|
||||
assert.equal(textWrap.rootKey, "wp:wrapTopAndBottom");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,17 +1,10 @@
|
||||
// http://officeopenxml.com/drwPicFloating.php
|
||||
import { IMediaDataDimensions } from "file/media";
|
||||
import { IMediaData, IMediaDataDimensions } from "file/media";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { IDrawingOptions } from "../drawing";
|
||||
import {
|
||||
HorizontalPosition,
|
||||
HorizontalPositionRelativeFrom,
|
||||
IFloating,
|
||||
SimplePos,
|
||||
VerticalPosition,
|
||||
VerticalPositionRelativeFrom,
|
||||
} from "../floating";
|
||||
import { HorizontalPosition, IFloating, SimplePos, VerticalPosition } from "../floating";
|
||||
import { Graphic } from "../inline/graphic";
|
||||
import { TextWrapStyle, WrapNone, WrapSquare, WrapTight, WrapTopAndBottom } from "../text-wrap";
|
||||
import { TextWrappingType, WrapNone, WrapSquare, WrapTight, WrapTopAndBottom } from "../text-wrap";
|
||||
import { DocProperties } from "./../doc-properties/doc-properties";
|
||||
import { EffectExtent } from "./../effect-extent/effect-extent";
|
||||
import { Extent } from "./../extent/extent";
|
||||
@ -23,30 +16,31 @@ const defaultOptions: IFloating = {
|
||||
behindDocument: false,
|
||||
lockAnchor: false,
|
||||
layoutInCell: true,
|
||||
verticalPosition: {
|
||||
relative: VerticalPositionRelativeFrom.PARAGRAPH,
|
||||
offset: 0,
|
||||
},
|
||||
horizontalPosition: {
|
||||
relative: HorizontalPositionRelativeFrom.COLUMN,
|
||||
offset: 0,
|
||||
},
|
||||
verticalPosition: {},
|
||||
horizontalPosition: {},
|
||||
};
|
||||
|
||||
export class Anchor extends XmlComponent {
|
||||
constructor(referenceId: number, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) {
|
||||
constructor(mediaData: IMediaData, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) {
|
||||
super("wp:anchor");
|
||||
|
||||
const floating = {
|
||||
margins: {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
...defaultOptions,
|
||||
...drawingOptions.floating,
|
||||
};
|
||||
|
||||
this.root.push(
|
||||
new AnchorAttributes({
|
||||
distT: 0,
|
||||
distB: 0,
|
||||
distL: 0,
|
||||
distR: 0,
|
||||
distT: floating.margins.top || 0,
|
||||
distB: floating.margins.bottom || 0,
|
||||
distL: floating.margins.left || 0,
|
||||
distR: floating.margins.right || 0,
|
||||
simplePos: "0", // note: word doesn't fully support - so we use 0
|
||||
allowOverlap: floating.allowOverlap === true ? "1" : "0",
|
||||
behindDoc: floating.behindDocument === true ? "1" : "0",
|
||||
@ -62,18 +56,18 @@ export class Anchor extends XmlComponent {
|
||||
this.root.push(new Extent(dimensions.emus.x, dimensions.emus.y));
|
||||
this.root.push(new EffectExtent());
|
||||
|
||||
if (drawingOptions.textWrapping !== undefined) {
|
||||
switch (drawingOptions.textWrapping.textWrapStyle) {
|
||||
case TextWrapStyle.SQUARE:
|
||||
this.root.push(new WrapSquare(drawingOptions.textWrapping));
|
||||
if (drawingOptions.floating !== undefined && drawingOptions.floating.wrap !== undefined) {
|
||||
switch (drawingOptions.floating.wrap.type) {
|
||||
case TextWrappingType.SQUARE:
|
||||
this.root.push(new WrapSquare(drawingOptions.floating.wrap, drawingOptions.floating.margins));
|
||||
break;
|
||||
case TextWrapStyle.TIGHT:
|
||||
this.root.push(new WrapTight(drawingOptions.textWrapping.distanceFromText));
|
||||
case TextWrappingType.TIGHT:
|
||||
this.root.push(new WrapTight(drawingOptions.floating.margins));
|
||||
break;
|
||||
case TextWrapStyle.TOP_AND_BOTTOM:
|
||||
this.root.push(new WrapTopAndBottom(drawingOptions.textWrapping.distanceFromText));
|
||||
case TextWrappingType.TOP_AND_BOTTOM:
|
||||
this.root.push(new WrapTopAndBottom(drawingOptions.floating.margins));
|
||||
break;
|
||||
case TextWrapStyle.NONE:
|
||||
case TextWrappingType.NONE:
|
||||
default:
|
||||
this.root.push(new WrapNone());
|
||||
}
|
||||
@ -83,6 +77,6 @@ export class Anchor extends XmlComponent {
|
||||
|
||||
this.root.push(new DocProperties());
|
||||
this.root.push(new GraphicFrameProperties());
|
||||
this.root.push(new Graphic(referenceId, dimensions.emus.x, dimensions.emus.y));
|
||||
this.root.push(new Graphic(mediaData, dimensions.emus.x, dimensions.emus.y));
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IDocPropertiesAttributes {
|
||||
id?: number;
|
||||
name?: string;
|
||||
descr?: string;
|
||||
readonly id?: number;
|
||||
readonly name?: string;
|
||||
readonly descr?: string;
|
||||
}
|
||||
|
||||
export class DocPropertiesAttributes extends XmlAttributeComponent<IDocPropertiesAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
id: "id",
|
||||
name: "name",
|
||||
descr: "descr",
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { assert } from "chai";
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Utility } from "../../tests/utility";
|
||||
import { Drawing, IDrawingOptions, PlacementPosition } from "./";
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { Drawing, IDrawingOptions } from "./drawing";
|
||||
|
||||
const imageBase64Data = `iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAACzVBMVEUAAAAAAAAAAAAAAAA/AD8zMzMqKiokJCQfHx8cHBwZGRkuFxcqFSonJyckJCQiIiIfHx8eHh4cHBwoGhomGSYkJCQhISEfHx8eHh4nHR0lHBwkGyQjIyMiIiIgICAfHx8mHh4lHh4kHR0jHCMiGyIhISEgICAfHx8lHx8kHh4jHR0hHCEhISEgICAlHx8kHx8jHh4jHh4iHSIhHCEhISElICAkHx8jHx8jHh4iHh4iHSIhHSElICAkICAjHx8jHx8iHh4iHh4hHiEhHSEkICAjHx8iHx8iHx8hHh4hHiEkHSEjHSAjHx8iHx8iHx8hHh4kHiEkHiEjHSAiHx8hHx8hHh4kHiEjHiAjHSAiHx8iHx8hHx8kHh4jHiEjHiAjHiAiICAiHx8kHx8jHh4jHiEjHiAiHiAiHSAiHx8jHx8jHx8jHiAiHiAiHiAiHSAiHx8jHx8jHx8iHiAiHiAiHiAjHx8jHx8jHx8jHx8iHiAiHiAiHiAjHx8jHx8jHx8iHx8iHSAiHiAjHiAjHx8jHx8hHx8iHx8iHyAiHiAjHiAjHiAjHh4hHx8iHx8iHx8iHyAjHSAjHiAjHiAjHh4hHx8iHx8iHx8jHyAjHiAhHh4iHx8iHx8jHyAjHSAjHSAhHiAhHh4iHx8iHx8jHx8jHyAjHSAjHSAiHh4iHh4jHx8jHx8jHyAjHyAhHSAhHSAiHh4iHh4jHx8jHx8jHyAhHyAhHSAiHSAiHh4jHh4jHx8jHx8jHyAhHyAhHSAiHSAjHR4jHh4jHx8jHx8hHyAhHyAiHSAjHSAjHR4jHh4jHx8hHx8hHyAhHyAiHyAjHSAjHR4jHR4hHh4hHx8hHyAiHyAjHyAjHSAjHR4jHR4hHh4hHx8hHyAjHyAjHyAjHSAjHR4hHR4hHR4hHx8iHyAjHyAjHyAjHSAhHR4hHR4hHR4hHx8jHyAjHyAjHyAjHyC9S2xeAAAA7nRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFxgZGhscHR4fICEiIyQlJicoKSorLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZISUpLTE1OUFFSU1RVVllaW1xdXmBhYmNkZWZnaGprbG1ub3Byc3R1dnd4eXp8fn+AgYKDhIWGiImKi4yNj5CRkpOUlZaXmJmam5ydnp+goaKjpKaoqqusra6vsLGys7S1tri5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+fkZpVQAABcBJREFUGBntwftjlQMcBvDnnLNL22qzJjWlKLHFVogyty3SiFq6EZliqZGyhnSxsLlMRahYoZKRFcul5dKFCatYqWZaNKvWtrPz/A2+7/b27qRzec/lPfvl/XxgMplMJpPJZDKZAtA9HJ3ppnIez0KnSdtC0RCNznHdJrbrh85wdSlVVRaEXuoGamYi5K5430HNiTiEWHKJg05eRWgNfKeV7RxbqUhGKPV/207VupQ8is0IoX5vtFC18SqEHaK4GyHTZ2kzVR8PBTCO4oANIZL4ShNVZcOhKKeYg9DoWdhI1ec3os2VFI0JCIUez5+i6st0qJZRrEAIJCw+QdW223BG/EmKwTBc/IJ/qfp2FDrkUnwFo8U9dZyqnaPhxLqfYjyM1S3vb6p+GGOBszsojoTDSDFz6qj66R4LzvYJxVMwUNRjf1H1ywQr/megg2RzLximy8waqvbda8M5iijegVEiHjlM1W/3h+FcXesphsMY4dMOUnUgOxyuPEzxPQwRNvV3qg5Nj4BreyimwADWe/dRVTMjEm6MoGLzGwtystL6RyOY3qSqdlYU3FpLZw1VW0sK5943MvUCKwJ1noNtjs6Ohge76Zq9ZkfpigU5WWkDYuCfbs1U5HWFR8/Qq4a9W0uK5k4ZmdrTCl8spGIePLPlbqqsc1Afe83O0hULc8alDYiBd7ZyitYMeBfR55rR2fOKP6ioPk2dGvZ+UVI0d8rtqT2tcCexlqK2F3wRn5Q+YVbBqrLKOupkr9lZujAOrmS0UpTb4JeIPkNHZ+cXr6uoPk2vyuBSPhWLEKj45PQJuQWryyqP0Z14uGLdROHIRNBEXDR09EP5r62rOHCazhrD4VKPwxTH+sIA3ZPTJ+YuWV22n+IruHFDC8X2CBjnPoolcGc2FYUwzmsUWXDHsoGKLBhmN0VvuBVfTVE/AAbpaid5CB4MbaLY1QXGuIViLTyZQcVyGGMuxWPwaA0Vk2GI9RRp8Ci2iuLkIBjhT5LNUfAspZFiTwyC72KK7+DNg1SsRvCNp3gZXq2k4iEEXSHFJHgVXUlxejCCbTvFAHiXdIJiXxyCK7KJ5FHoMZGK9xBcwyg2QpdlVMxEUM2iyIMuXXZQNF+HswxMsSAAJRQjoE//eoqDCXBSTO6f1xd+O0iyNRY6jaWi1ALNYCocZROj4JdEikroVkjFk9DcStXxpdfCD2MoXodu4RUU9ptxxmXssOfxnvDVcxRTod9FxyhqLoAqis5aPhwTDp9spRgEH2Q6KLbYoKqlaKTm6Isp0C/sJMnjFvhiERXPQvUNRe9p29lhR04CdBpC8Sl8YiuncIxEuzUUg4Dkgj+paVozygY9plPMh28SaymO9kabAopREGF3vt9MzeFFl8G7lRSZ8FFGK8XX4VA8QjEd7XrM3M0OXz8YCy+qKBLgq3wqnofiTorF0Ax56Rg1J1elW+BBAsVe+My6iYq7IK6keBdOIseV2qn5Pb8f3MqkWAXf9ThM8c8lAOIotuFsF875lRrH5klRcG0+xcPwQ1oLxfeRAP4heQTnGL78X2rqlw2DK59SXAV/zKaiGMAuko5InCt68mcOan5+ohf+z1pP8lQY/GHZQMV4YD3FpXDp4qerqbF/lBWBswyi+AL+ia+maLgcRRQj4IYlY/UpauqKBsPJAxQF8NM1TRQ/RudSPAD34rK3scOuR8/HGcspxsJfOVS8NZbiGXiUtPgINU3v3WFDmx8pEuG3EiqKKVbCC1vm2iZqap5LAtCtleQf8F9sFYWDohzeJczYyQ4V2bEZFGsQgJRGqqqhS2phHTWn9lDkIhBTqWqxQZ+IsRvtdHY9AvI2VX2hW68nfqGmuQsCEl3JdjfCF8OW1bPdtwhQ0gm2mQzfRE3a7KCYj0BNZJs8+Kxf/r6WtTEI2FIqlsMfFgRB5A6KUnSe/vUkX0AnuvUIt8SjM1m6wWQymUwmk8lkMgXRf5vi8rLQxtUhAAAAAElFTkSuQmCC`;
|
||||
|
||||
@ -10,7 +11,6 @@ function createDrawing(drawingOptions?: IDrawingOptions): Drawing {
|
||||
return new Drawing(
|
||||
{
|
||||
fileName: "test.jpg",
|
||||
referenceId: 1,
|
||||
stream: Buffer.from(imageBase64Data, "base64"),
|
||||
path: path,
|
||||
dimensions: {
|
||||
@ -32,24 +32,408 @@ describe("Drawing", () => {
|
||||
let currentBreak: Drawing;
|
||||
|
||||
describe("#constructor()", () => {
|
||||
it("should create a Drawing with correct root key", () => {
|
||||
currentBreak = createDrawing();
|
||||
const newJson = Utility.jsonify(currentBreak);
|
||||
assert.equal(newJson.rootKey, "w:drawing");
|
||||
});
|
||||
|
||||
it("should create a drawing with inline element when there are no options passed", () => {
|
||||
currentBreak = createDrawing();
|
||||
const newJson = Utility.jsonify(currentBreak);
|
||||
assert.equal(newJson.root[0].rootKey, "wp:inline");
|
||||
const tree = new Formatter().format(currentBreak);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:drawing": [
|
||||
{
|
||||
"wp:inline": [
|
||||
{
|
||||
_attr: {
|
||||
distB: 0,
|
||||
distL: 0,
|
||||
distR: 0,
|
||||
distT: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:extent": {
|
||||
_attr: {
|
||||
cx: 952500,
|
||||
cy: 952500,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:effectExtent": {
|
||||
_attr: {
|
||||
b: 0,
|
||||
l: 0,
|
||||
r: 0,
|
||||
t: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:docPr": {
|
||||
_attr: {
|
||||
descr: "",
|
||||
id: 0,
|
||||
name: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:cNvGraphicFramePr": [
|
||||
{
|
||||
"a:graphicFrameLocks": {
|
||||
_attr: {
|
||||
// tslint:disable-next-line:object-literal-key-quotes
|
||||
noChangeAspect: 1,
|
||||
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"a:graphic": [
|
||||
{
|
||||
_attr: {
|
||||
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:graphicData": [
|
||||
{
|
||||
_attr: {
|
||||
uri: "http://schemas.openxmlformats.org/drawingml/2006/picture",
|
||||
},
|
||||
},
|
||||
{
|
||||
"pic:pic": [
|
||||
{
|
||||
_attr: {
|
||||
"xmlns:pic": "http://schemas.openxmlformats.org/drawingml/2006/picture",
|
||||
},
|
||||
},
|
||||
{
|
||||
"pic:nvPicPr": [
|
||||
{
|
||||
"pic:cNvPr": {
|
||||
_attr: {
|
||||
desc: "",
|
||||
id: 0,
|
||||
name: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"pic:cNvPicPr": [
|
||||
{
|
||||
"a:picLocks": {
|
||||
_attr: {
|
||||
noChangeArrowheads: 1,
|
||||
noChangeAspect: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"pic:blipFill": [
|
||||
{
|
||||
"a:blip": {
|
||||
_attr: {
|
||||
// tslint:disable-next-line:object-literal-key-quotes
|
||||
cstate: "none",
|
||||
"r:embed": "rId{test.jpg}",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:srcRect": {},
|
||||
},
|
||||
{
|
||||
"a:stretch": [
|
||||
{
|
||||
"a:fillRect": {},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"pic:spPr": [
|
||||
{
|
||||
_attr: {
|
||||
bwMode: "auto",
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:xfrm": [
|
||||
{
|
||||
"a:ext": {
|
||||
_attr: {
|
||||
cx: 952500,
|
||||
cy: 952500,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:off": {
|
||||
_attr: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"a:prstGeom": [
|
||||
{
|
||||
_attr: {
|
||||
prst: "rect",
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:avLst": {},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create a drawing with anchor element when there options are passed", () => {
|
||||
currentBreak = createDrawing({
|
||||
position: PlacementPosition.FLOATING,
|
||||
floating: {
|
||||
horizontalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
verticalPosition: {
|
||||
offset: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(currentBreak);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:drawing": [
|
||||
{
|
||||
"wp:anchor": [
|
||||
{
|
||||
_attr: {
|
||||
allowOverlap: "1",
|
||||
behindDoc: "0",
|
||||
distB: 0,
|
||||
distL: 0,
|
||||
distR: 0,
|
||||
distT: 0,
|
||||
layoutInCell: "1",
|
||||
locked: "0",
|
||||
relativeHeight: 952500,
|
||||
simplePos: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:simplePos": {
|
||||
_attr: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:positionH": [
|
||||
{
|
||||
_attr: {
|
||||
relativeFrom: "page",
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:posOffset": ["0"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"wp:positionV": [
|
||||
{
|
||||
_attr: {
|
||||
relativeFrom: "page",
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:posOffset": ["0"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"wp:extent": {
|
||||
_attr: {
|
||||
cx: 952500,
|
||||
cy: 952500,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:effectExtent": {
|
||||
_attr: {
|
||||
b: 0,
|
||||
l: 0,
|
||||
r: 0,
|
||||
t: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:wrapNone": {},
|
||||
},
|
||||
{
|
||||
"wp:docPr": {
|
||||
_attr: {
|
||||
descr: "",
|
||||
id: 0,
|
||||
name: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:cNvGraphicFramePr": [
|
||||
{
|
||||
"a:graphicFrameLocks": {
|
||||
_attr: {
|
||||
// tslint:disable-next-line:object-literal-key-quotes
|
||||
noChangeAspect: 1,
|
||||
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"a:graphic": [
|
||||
{
|
||||
_attr: {
|
||||
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:graphicData": [
|
||||
{
|
||||
_attr: {
|
||||
uri: "http://schemas.openxmlformats.org/drawingml/2006/picture",
|
||||
},
|
||||
},
|
||||
{
|
||||
"pic:pic": [
|
||||
{
|
||||
_attr: {
|
||||
"xmlns:pic": "http://schemas.openxmlformats.org/drawingml/2006/picture",
|
||||
},
|
||||
},
|
||||
{
|
||||
"pic:nvPicPr": [
|
||||
{
|
||||
"pic:cNvPr": {
|
||||
_attr: {
|
||||
desc: "",
|
||||
id: 0,
|
||||
name: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"pic:cNvPicPr": [
|
||||
{
|
||||
"a:picLocks": {
|
||||
_attr: {
|
||||
noChangeArrowheads: 1,
|
||||
noChangeAspect: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"pic:blipFill": [
|
||||
{
|
||||
"a:blip": {
|
||||
_attr: {
|
||||
// tslint:disable-next-line:object-literal-key-quotes
|
||||
cstate: "none",
|
||||
"r:embed": "rId{test.jpg}",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:srcRect": {},
|
||||
},
|
||||
{
|
||||
"a:stretch": [
|
||||
{
|
||||
"a:fillRect": {},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"pic:spPr": [
|
||||
{
|
||||
_attr: {
|
||||
bwMode: "auto",
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:xfrm": [
|
||||
{
|
||||
"a:ext": {
|
||||
_attr: {
|
||||
cx: 952500,
|
||||
cy: 952500,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:off": {
|
||||
_attr: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"a:prstGeom": [
|
||||
{
|
||||
_attr: {
|
||||
prst: "rect",
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:avLst": {},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
const newJson = Utility.jsonify(currentBreak);
|
||||
assert.equal(newJson.root[0].rootKey, "wp:anchor");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -3,50 +3,29 @@ import { XmlComponent } from "file/xml-components";
|
||||
import { Anchor } from "./anchor";
|
||||
import { IFloating } from "./floating";
|
||||
import { Inline } from "./inline";
|
||||
import { ITextWrapping } from "./text-wrap";
|
||||
|
||||
export enum PlacementPosition {
|
||||
INLINE,
|
||||
FLOATING,
|
||||
}
|
||||
|
||||
export interface IDistance {
|
||||
distT?: number;
|
||||
distB?: number;
|
||||
distL?: number;
|
||||
distR?: number;
|
||||
readonly distT?: number;
|
||||
readonly distB?: number;
|
||||
readonly distL?: number;
|
||||
readonly distR?: number;
|
||||
}
|
||||
|
||||
export interface IDrawingOptions {
|
||||
position?: PlacementPosition;
|
||||
textWrapping?: ITextWrapping;
|
||||
floating?: IFloating;
|
||||
readonly floating?: IFloating;
|
||||
}
|
||||
|
||||
const defaultDrawingOptions: IDrawingOptions = {
|
||||
position: PlacementPosition.INLINE,
|
||||
};
|
||||
|
||||
export class Drawing extends XmlComponent {
|
||||
private readonly inline: Inline;
|
||||
|
||||
constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) {
|
||||
constructor(imageData: IMediaData, drawingOptions: IDrawingOptions = {}) {
|
||||
super("w:drawing");
|
||||
|
||||
if (imageData === undefined) {
|
||||
throw new Error("imageData cannot be undefined");
|
||||
}
|
||||
|
||||
const mergedOptions = {
|
||||
...defaultDrawingOptions,
|
||||
...drawingOptions,
|
||||
};
|
||||
|
||||
if (mergedOptions.position === PlacementPosition.INLINE) {
|
||||
this.inline = new Inline(imageData.referenceId, imageData.dimensions);
|
||||
if (!drawingOptions.floating) {
|
||||
this.inline = new Inline(imageData, imageData.dimensions);
|
||||
this.root.push(this.inline);
|
||||
} else if (mergedOptions.position === PlacementPosition.FLOATING) {
|
||||
this.root.push(new Anchor(imageData.referenceId, imageData.dimensions, mergedOptions));
|
||||
} else {
|
||||
this.root.push(new Anchor(imageData, imageData.dimensions, drawingOptions));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IEffectExtentAttributes {
|
||||
b?: number;
|
||||
l?: number;
|
||||
r?: number;
|
||||
t?: number;
|
||||
readonly b?: number;
|
||||
readonly l?: number;
|
||||
readonly r?: number;
|
||||
readonly t?: number;
|
||||
}
|
||||
|
||||
export class EffectExtentAttributes extends XmlAttributeComponent<IEffectExtentAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
b: "b",
|
||||
l: "l",
|
||||
r: "r",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user