Compare commits
627 Commits
Author | SHA1 | Date | |
---|---|---|---|
da0fa86345 | |||
78b310e1dd | |||
4541d7c977 | |||
a37ff90bd7 | |||
9998ddfcc9 | |||
12ed54c9fd | |||
3a1a21e498 | |||
e5bfa99b92 | |||
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 | |||
90f3da74cf | |||
28d8659cb5 | |||
5d1740a2ef | |||
7aa44568d8 | |||
bf1f702e5a | |||
78ad3e340a | |||
63a965beab | |||
815354e06f | |||
1cff104bae | |||
4805efad2e | |||
12e2ae9e91 | |||
c07b5cf709 | |||
0684738ec2 | |||
f2b50478bf | |||
fc71ebdfef | |||
10114bb12d | |||
6da3efdacc | |||
7cd8864fb9 | |||
a9c69664c7 | |||
50569224c3 | |||
e6d4741955 | |||
4d7387524c | |||
877aa325bb | |||
eb797d8986 | |||
fcb542471b | |||
5fe4405d76 | |||
baf0f17bd6 | |||
8e911698a5 | |||
0eb36be053 | |||
79dffc873a | |||
26ee12759c | |||
2e00634bc4 | |||
d63e6bf6b1 | |||
a95366e54e | |||
1c376abeb6 | |||
353d888abd | |||
b6bd532295 | |||
c5b004166d | |||
2f43600daf | |||
b39c7ce323 | |||
5b00279996 | |||
53b24965aa | |||
edce1bef92 | |||
e0d54d3af3 | |||
d1d1e01aff | |||
980bc597e2 | |||
a05c5edd49 | |||
985452f5f4 | |||
482674b3b3 | |||
fcbfed9068 | |||
a9167b4809 | |||
5b28eb0d00 | |||
f1b176670c | |||
11ce9a5206 | |||
8b667b8d4c | |||
a601a82a37 | |||
dcf3767a05 | |||
a9278009f8 | |||
5220f9e07c | |||
0b88064ca2 | |||
2233ccb968 | |||
311fde01a1 | |||
146d0ad9e5 | |||
385ad92331 | |||
571f8b526b | |||
7688aa99f6 | |||
4994bca34c | |||
1a3603dbfb | |||
aedfca377f | |||
7926f6c189 | |||
0303a9f553 | |||
e9a007d446 | |||
e382dbff84 | |||
e08be3d7a4 | |||
03c4190c2c | |||
12c1f82efe | |||
b1711ae293 | |||
a367951d07 | |||
c55f82c425 | |||
2dab11e8b3 | |||
d1044d262e | |||
010fde6258 | |||
8d83219bb6 | |||
a710483918 | |||
4f8ecf631b | |||
cc453dc24e | |||
60abe4857b | |||
dc942bc2e4 | |||
90497036a6 | |||
fe2032a3d4 | |||
877dcb462f | |||
4a3b7d71fe | |||
0e1bfb6524 | |||
e68bc50e6e | |||
a6a8012b39 | |||
e9e42011cd | |||
85f686dede | |||
0f6f79ebc6 | |||
9a6c92222c | |||
33f87523e0 | |||
a53818754a | |||
6c2f335a31 | |||
ff80f76855 | |||
6f0f3ccca7 | |||
eb9cc767c6 | |||
410080a8a3 | |||
d5b6225a90 | |||
a777842f9d | |||
62306133c8 | |||
a7f6224fb2 | |||
e97c432a64 | |||
0411153832 | |||
f9a0d1b388 | |||
bf5bcea607 | |||
f969c866a7 | |||
07261232ff | |||
2018f41368 | |||
bfa0edeb09 | |||
3d91c84966 | |||
f91fd5921f | |||
36157bb8db | |||
a38abeb4c2 | |||
675192b86f | |||
bc1132146f | |||
d850f37904 | |||
f15fdb921e | |||
7b43551148 | |||
ad356d2759 | |||
5f594a822f | |||
89df3c48e0 | |||
3436a1584c | |||
d44b0d50d8 | |||
1bd94aca7c | |||
f264e4d0f7 | |||
d8252141fe | |||
d9716a1cf7 | |||
e9bd27bc55 | |||
0a69851463 | |||
5e7c5aa620 | |||
f6996a1d98 | |||
e9ea4845be | |||
d961721b33 | |||
bb8c29f4a2 | |||
e10c20fa42 | |||
94274733f4 | |||
42f723813a | |||
4c75b91ce9 | |||
3dd6faa620 | |||
9a1313fe89 | |||
fe43525dad | |||
92da93a160 | |||
cf0c2c7691 | |||
e9aecfac1c | |||
12c8cb93f6 | |||
f9c97a673e | |||
ca8f331eec | |||
9e11774a1b | |||
257c8bde99 | |||
0bdbb1b24a | |||
bd1aa7b49e | |||
5de3dc802f | |||
f25ecd0743 | |||
cdf3a6fb40 | |||
f9c5db8df7 | |||
7d05d69707 | |||
08a3538d8e | |||
6b38a3637f | |||
7d116d1ce9 | |||
73267ac4e5 | |||
efe7d594b0 | |||
b7a4ca9c3b | |||
810fd7e7b8 | |||
22bd13a253 | |||
5f22950721 | |||
31fdf08c27 | |||
2d12f6e54c | |||
f7c02f4679 | |||
3fcfc29e3f | |||
817356f2a8 | |||
8fbaec3567 | |||
3f1eda60eb | |||
0d5e9ce9d8 | |||
019ae24c7d | |||
4dd84ded54 | |||
5232c055b1 | |||
06d3f3c34b | |||
2aca988c0b | |||
fc27e44471 | |||
60f312446e | |||
807605c0cc | |||
1aa592b1b9 | |||
763fa62eca | |||
40ac08836d | |||
6bdcee1d1b | |||
0ea79775c5 | |||
696b5daf5c | |||
e136f4e987 | |||
41308fea13 | |||
0e14b2d2e3 | |||
c9b66bf77b | |||
4168d1a296 | |||
51d172b8fc | |||
e02e54db2c | |||
305dd84769 | |||
985ea30d36 | |||
cc904ee211 | |||
8ceb38963e | |||
59ca9df663 | |||
0689489985 | |||
3015b05c77 | |||
f299e49966 | |||
0186450221 | |||
974eb510ef | |||
1fad9a666e | |||
b6cf1ab951 | |||
d979ef3b40 | |||
4a320d3031 | |||
fbfdc6a383 | |||
4dfce582c8 | |||
ee048968cc | |||
fe54e892cc | |||
5fab6e2714 | |||
8e80df30b9 | |||
b9e7b9e1e3 | |||
bb88053ae3 | |||
0611b94c01 | |||
3da0bb38df | |||
c57c7f2aa7 | |||
a05abd0eea | |||
c95e765456 | |||
9d034ab48d | |||
796e000826 | |||
60a92fb22b | |||
ddbae91840 | |||
ded155a6af | |||
59cc0d3211 | |||
4b06baab3f | |||
ef16dac4f4 | |||
b8934c68f2 | |||
e5fae3af64 | |||
34a92ab448 | |||
6c85ad3188 | |||
9f0e55b7b8 | |||
612a2f90ac | |||
c797ed9c25 | |||
5dc82d8176 | |||
802e461792 | |||
b9e702086a | |||
9f591c4d87 | |||
55220c147d | |||
2ea5b1c0bd | |||
ed72d60951 | |||
5f6d177df9 | |||
b3e15d6729 | |||
b26a9f8dcf | |||
f3a822b4b2 | |||
cee091fa58 | |||
84ebf8e6c3 | |||
2a0b45dc20 | |||
00955d2e4f | |||
044442b0d7 | |||
99290d646e | |||
52007785af | |||
9d3b32a841 | |||
0b939b1cd6 | |||
d63adc7a6b | |||
026a221e5e | |||
f1ac646518 | |||
8b3cf8c38c | |||
89e2129ca4 | |||
d9999f8dd9 | |||
0b963ec3b8 | |||
369ec9c30b | |||
c19a2e71b5 | |||
6085f69c22 | |||
8cb8d3514a | |||
55005b57c6 | |||
80cdaaeb1c | |||
deccfd7cc9 | |||
0f4c2e64f4 | |||
b8b5d18662 | |||
5ee5698347 | |||
97b254ee7b | |||
ac40a40ec0 | |||
b5b96506ae | |||
548fe3c864 | |||
97e976a184 | |||
5de6617d10 | |||
bcd6110578 | |||
ba0ca78696 | |||
22ba53ad4c | |||
81304f50d1 | |||
3c22372f49 | |||
7584671312 | |||
7296c9e744 | |||
c63a8982e4 | |||
8d35dc1bb0 | |||
0fedfcf709 | |||
6c2eb882bb | |||
c9eb27de11 | |||
dadd8c122f | |||
59560d96ba | |||
864c9afd93 | |||
b4f07f51ae | |||
e9b153095c | |||
7968c1efcf | |||
87648a742c | |||
cdb86b741c | |||
15e8d7c053 | |||
cbe9c3ac50 | |||
4d7bdc2ed9 | |||
d10c707f12 | |||
ac512b2eab | |||
fdf6a59c4c | |||
4b9a6a6735 | |||
49cc8a267c | |||
68cb57aea6 | |||
9d7fd55e4c | |||
195c62f80b | |||
1fd222abea | |||
ac40e13e33 | |||
53ab822dbc | |||
0c9c292291 | |||
573dd753a7 | |||
79b5b3a1f6 | |||
52e8fe576e | |||
0d34d2d92e | |||
b389ac6347 | |||
534c601068 | |||
424436579b | |||
a716360faa | |||
af485c678d | |||
84e298e7ee | |||
753287d9d1 | |||
21bb8f9016 | |||
dc136daeab | |||
e8bc7952db | |||
06418655c0 | |||
c99b2940c6 | |||
e67f5f80e1 | |||
3691d79a4a | |||
8108eca2fa | |||
4f48c8fb80 | |||
20ba081308 | |||
2119ae769b | |||
c618ca7539 | |||
8b11140be2 | |||
bebfec7755 | |||
124aac4888 | |||
b3bfd063d8 | |||
c92cab5e5b | |||
8c613195f3 | |||
41f941728e | |||
ecf1542d95 | |||
06b2bbba25 | |||
0494fdeabd | |||
226206b100 | |||
02f80bc616 | |||
3189c9251a | |||
ae43137906 | |||
50bee30799 | |||
38484a3063 | |||
919327ed08 | |||
c00c5fa02d | |||
80f09ac10b | |||
ee721ffbec | |||
323f91dd68 | |||
810ccb40d7 | |||
5242f7d55c | |||
d293ae516c | |||
3fb563f9c8 | |||
1dd1c65341 | |||
e593327fea | |||
64e0aeeb18 | |||
05816abc12 | |||
ce306aef07 | |||
373c850f35 | |||
a0e00b8eff | |||
a954c69458 | |||
a0e034bd55 | |||
1a1c1f26d9 | |||
114c429aed | |||
40251a76f6 | |||
a102b479e6 | |||
5b5f5ea203 | |||
40a8e581f1 | |||
1b988e7135 | |||
9433c7aedc | |||
479dfed987 | |||
1866033128 | |||
844b9ab2ec | |||
e6713d6ce2 | |||
069ce73c07 | |||
6dda6a5513 | |||
329def42ac | |||
c5137b5fad | |||
ab05ccb846 | |||
cb8e9fdd36 | |||
53ce3c1526 | |||
a78f06b41b | |||
ce7ef30806 | |||
fd93c0776a | |||
6a762c6c0e | |||
23c5aef276 | |||
69707a7207 | |||
cfd3505414 | |||
ee958dc351 | |||
680f2325a3 | |||
2f0ad3eeb2 | |||
c895a9c7d9 | |||
388a8404f5 | |||
320cb1c418 | |||
8a6b73915f | |||
774355d608 | |||
35dbce3a68 | |||
6d0a267ba4 | |||
df701be572 | |||
1edad47e6e | |||
c873abfe18 | |||
989446ef36 | |||
fc6daed620 | |||
5e921f1dfc | |||
45bbf1b693 | |||
8b8c664f0f | |||
079334f71b | |||
3fe0c76d54 | |||
1e55a3e6a8 | |||
950a2f8b53 | |||
bbe2e1e46e | |||
635c58c131 | |||
ba39d806b7 | |||
355e97cb5e | |||
4339f8cfc0 | |||
448572d7a1 | |||
2132c7b6da | |||
d0bd83d6c5 | |||
1037b7c23d | |||
df197f73ea | |||
ea08f603f5 | |||
fa2f1235f7 | |||
ff5d02c964 | |||
e93d6799fd | |||
f2027230a0 | |||
cb47d4f772 | |||
eebc9fbcfa | |||
0e698491f3 | |||
ae52e8fabb | |||
ccd655ef8b | |||
3dc6e71aaf | |||
e6d3577f74 | |||
ef05024f2f | |||
d3bc784248 | |||
979701331e | |||
76b1682296 | |||
7c31b72f99 | |||
f7c2072cff | |||
392db1cd11 | |||
ca244bcfe1 | |||
2d02f51f25 | |||
592fb5ca9f | |||
a3945bc7f1 | |||
2adde9830c | |||
0355afe11c | |||
998fe3f370 | |||
eb71fc20e6 | |||
ab348bd5f9 | |||
c518d1c6c7 | |||
d8d16b4a7d | |||
928e4b9bc2 | |||
4f900d6566 | |||
362a997187 | |||
89c658746d | |||
75d699f7ef | |||
f7412690b6 | |||
ece2b23407 | |||
f84af9a44b | |||
90dc1103f6 | |||
c469fb24db | |||
32be6e36da | |||
b34ad22ad6 | |||
2358139a6b | |||
43ebfe7a2f | |||
0b8094dea8 | |||
f1d1570b10 | |||
e9e68bc802 | |||
4c369510ce | |||
96413d6c47 | |||
df6c7cf19f | |||
49fc28d86c | |||
cc67a83ce8 | |||
742e2b5089 | |||
d19ff1e300 | |||
01950ed443 | |||
cf1689a3c2 | |||
66d0bab0a5 | |||
5889f20f1e | |||
ebec10e312 | |||
ae8a0c7fd0 | |||
bb49200fad | |||
a6bba0bc6c | |||
fb08f79344 | |||
741c74825e | |||
28539cd47b | |||
8ca7c5a343 | |||
32b56e7071 | |||
7dad717952 | |||
34e928755f | |||
51e0f311fe | |||
cc9dff6b94 | |||
101cc0fdea | |||
d408262fa8 | |||
ed53c30f42 | |||
ebbf6a99c1 | |||
b98c103e45 | |||
518fec0595 | |||
659936f3f0 | |||
b0febf5054 | |||
56b951a2b1 | |||
a3a9958a69 | |||
4f36bbf426 | |||
357bc7f377 | |||
da8405b5b9 | |||
492face7ab | |||
b6351f0260 | |||
3a7f9053b9 | |||
19b122684c | |||
72e89cfc3c |
13
.editorconfig
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Editor configuration, see http://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
29
.gitignore
vendored
@ -36,5 +36,30 @@ node_modules
|
|||||||
build
|
build
|
||||||
build-tests
|
build-tests
|
||||||
|
|
||||||
# vscode
|
# Documentation
|
||||||
.vscode
|
docs/api/
|
||||||
|
docs/.nojekyll
|
||||||
|
|
||||||
|
# VSCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.history
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Lock files
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Documents
|
||||||
|
My Document.docx
|
||||||
|
|
||||||
|
# Temporary folder
|
||||||
|
tmp
|
||||||
|
|
||||||
|
# nyc
|
||||||
|
.nyc_output
|
||||||
|
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
|
||||||
|
}
|
5
.prettierrc.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
trailingComma: all
|
||||||
|
printWidth: 140
|
||||||
|
tabWidth: 4
|
||||||
|
arrowParens: always
|
||||||
|
bracketSpacing: true
|
61
.travis.yml
@ -1,15 +1,60 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- "stable"
|
- 9
|
||||||
install:
|
install:
|
||||||
- npm install
|
- npm install
|
||||||
|
- npm install -g codecov
|
||||||
script:
|
script:
|
||||||
- npm test
|
- npm run lint
|
||||||
after_failure:
|
- npm run test.coverage
|
||||||
|
- npm run style
|
||||||
|
- npm run build
|
||||||
|
- npm run ts-node -- ./demo/demo1.ts
|
||||||
|
- npm run ts-node -- ./demo/demo2.ts
|
||||||
|
- npm run ts-node -- ./demo/demo3.ts
|
||||||
|
- npm run ts-node -- ./demo/demo4.ts
|
||||||
|
- npm run ts-node -- ./demo/demo5.ts
|
||||||
|
- npm run ts-node -- ./demo/demo6.ts
|
||||||
|
- npm run ts-node -- ./demo/demo7.ts
|
||||||
|
- npm run ts-node -- ./demo/demo8.ts
|
||||||
|
- npm run ts-node -- ./demo/demo9.ts
|
||||||
|
- npm run ts-node -- ./demo/demo10.ts
|
||||||
|
- npm run ts-node -- ./demo/demo11.ts
|
||||||
|
- npm run ts-node -- ./demo/demo12.ts
|
||||||
|
- npm run ts-node -- ./demo/demo13.ts
|
||||||
|
- npm run ts-node -- ./demo/demo14.ts
|
||||||
|
- npm run ts-node -- ./demo/demo15.ts
|
||||||
|
- npm run ts-node -- ./demo/demo16.ts
|
||||||
|
- npm run ts-node -- ./demo/demo17.ts
|
||||||
|
- npm run ts-node -- ./demo/demo18.ts
|
||||||
|
- npm run ts-node -- ./demo/demo19.ts
|
||||||
|
- npm run ts-node -- ./demo/demo20.ts
|
||||||
|
- npm run ts-node -- ./demo/demo21.ts
|
||||||
|
- 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 ts-node -- ./demo/demo33.ts
|
||||||
|
- npm run ts-node -- ./demo/demo34.ts
|
||||||
|
after_failure:
|
||||||
- "cat /home/travis/builds/dolanmiu/docx/npm-debug.log"
|
- "cat /home/travis/builds/dolanmiu/docx/npm-debug.log"
|
||||||
after_success:
|
after_success:
|
||||||
- bash ./deploy-docs.sh
|
- npm run typedoc
|
||||||
env:
|
- echo "docx.js.org" > docs/.nojekyll
|
||||||
global:
|
- echo "docx.js.org" > docs/CNAME
|
||||||
- ENCRYPTION_LABEL: "ad385fa3b525"
|
- codecov
|
||||||
|
deploy:
|
||||||
|
provider: pages
|
||||||
|
skip-cleanup: true
|
||||||
|
github-token: $GITHUB_TOKEN
|
||||||
|
keep-history: true
|
||||||
|
local-dir: docs
|
||||||
|
on:
|
||||||
|
branch: master
|
||||||
|
9
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": ["clippy", "docx", "dolan", "miu", "officegen", "typedoc"],
|
||||||
|
"prettier.trailingComma": "all",
|
||||||
|
"prettier.printWidth": 140,
|
||||||
|
"editor.formatOnSave": false,
|
||||||
|
"prettier.tabWidth": 4,
|
||||||
|
"prettier.arrowParens": "always",
|
||||||
|
"prettier.bracketSpacing": true
|
||||||
|
}
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
95
README.md
@ -1,79 +1,77 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="clippy the assistant" src="http://i60.tinypic.com/339pvtt.png">
|
<img alt="clippy the assistant" src="https://i.imgur.com/37uBGhO.gif">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
Generate .docx files with JS/TS very easily, written in TS.
|
Easily generate .docx files with JS/TS. Works for Node and on the Browser.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
-----
|
---
|
||||||
|
|
||||||
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][gemnasium-image]][gemnasium-url] [![Known Vulnerabilities][snky-image]][snky-url] [![Chat on Gitter][gitter-image]][gitter-url]
|
[![NPM version][npm-image]][npm-url]
|
||||||
|
[![Downloads per month][downloads-image]][downloads-url]
|
||||||
|
[![Build Status][travis-image]][travis-url]
|
||||||
|
[![Dependency Status][daviddm-image]][daviddm-url]
|
||||||
|
[![Known Vulnerabilities][snky-image]][snky-url]
|
||||||
|
[![Chat on Gitter][gitter-image]][gitter-url]
|
||||||
|
[![PRs Welcome][pr-image]][pr-url]
|
||||||
|
[![codecov][codecov-image]][codecov-url]
|
||||||
|
|
||||||
[](https://nodei.co/npm/docx/)
|
<p align="center">
|
||||||
|
<img src="https://i.imgur.com/H5FA1Qy.gif" alt="drawing" width="800"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
# docx
|
# Demo
|
||||||
|
|
||||||
## Install
|
## Browser
|
||||||
|
|
||||||
```sh
|
Here are examples of `docx` being used with basic `HTML/JS` in a browser environment.
|
||||||
$ npm install --save docx
|
|
||||||
```
|
|
||||||
|
|
||||||
## Demo
|
* https://codepen.io/anon/pen/dqoVgQ
|
||||||
|
* https://jsfiddle.net/3xhezb5w/2
|
||||||
|
|
||||||
```sh
|
Here is an example of `docx` working in `Angular`:
|
||||||
$ npm run demo
|
|
||||||
```
|
|
||||||
|
|
||||||
will run the demo selector app in the `demo` folder. It will prompt you to select a demo number, which will run a demo from that folder.
|
* https://stackblitz.com/edit/angular-afvxtz
|
||||||
|
|
||||||
## Guide
|
## Node
|
||||||
|
|
||||||
Please refer to [the Wiki](https://github.com/dolanmiu/docx/wiki) for details on how to use this library, examples and much more!
|
Press `endpoint` on the `RunKit` website:
|
||||||
|
|
||||||
Full documentation can be found here: [http://dolanmiu.github.io/docx](http://dolanmiu.github.io/docx)
|

|
||||||
|
|
||||||
## Simple Usage
|
* https://runkit.com/dolanmiu/docx-demo1 - Simple paragraph and text
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo2 - Advanced Paragraphs and text
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo3 - Bullet points
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo4 - Simple table
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo5 - Images
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo6 - Margins
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo7 - Landscape
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo8 - Header and Footer
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo10 - **My CV generated with docx**
|
||||||
|
|
||||||
```js
|
More [here](https://docx.js.org/#/examples) and [here](https://github.com/dolanmiu/docx/tree/master/demo)
|
||||||
// Used to create docx files
|
|
||||||
var docx = require('docx');
|
|
||||||
|
|
||||||
// Create document
|
# How to use & Documentation
|
||||||
var doc = new docx.Document();
|
|
||||||
|
|
||||||
// Add some content in the document
|
Please refer to the [documentation at https://docx.js.org/](https://docx.js.org/) for details on how to use this library, examples and much more!
|
||||||
var paragraph = new docx.Paragraph("Some cool text here.");
|
|
||||||
// Add more text into the paragraph if you wish
|
|
||||||
paragraph.addRun(new docx.TextRun('Lorem Ipsum Foo Bar'));
|
|
||||||
doc.addParagraph(paragraph);
|
|
||||||
|
|
||||||
// Used to export the file into a .docx file
|
# Examples
|
||||||
var exporter = new docx.LocalPacker(doc);
|
|
||||||
|
|
||||||
// Or use the express packer to make the file downloadable.
|
Check the `examples` section in the [documentation](https://docx.js.org/#/examples) and the [demo folder](https://github.com/dolanmiu/docx/tree/master/demo) for examples.
|
||||||
// res is express' Response object
|
|
||||||
var exporter = new docx.ExpressPacker(doc, res);
|
|
||||||
|
|
||||||
exporter.pack('My First Document');
|
# Contributing
|
||||||
|
|
||||||
// done! A file called 'My First Document.docx'
|
Read the contribution guidelines [here](https://docx.js.org/#/contribution-guidelines).
|
||||||
// will be in your file system if you used LocalPacker
|
|
||||||
// Or it will start downloading if you are using Express
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
---
|
||||||
Check [the Wiki](https://github.com/dolanmiu/docx/wiki/Examples) for examples.
|
|
||||||
|
|
||||||
-----
|
|
||||||
|
|
||||||
Made with 💖
|
Made with 💖
|
||||||
|
|
||||||
Huge thanks to [@felipeochoa](https://github.com/felipeochoa) for awesome contributions to this project
|
|
||||||
|
|
||||||
[npm-image]: https://badge.fury.io/js/docx.svg
|
[npm-image]: https://badge.fury.io/js/docx.svg
|
||||||
[npm-url]: https://npmjs.org/package/docx
|
[npm-url]: https://npmjs.org/package/docx
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/docx.svg
|
||||||
|
[downloads-url]: https://npmjs.org/package/docx
|
||||||
[travis-image]: https://travis-ci.org/dolanmiu/docx.svg?branch=master
|
[travis-image]: https://travis-ci.org/dolanmiu/docx.svg?branch=master
|
||||||
[travis-url]: https://travis-ci.org/dolanmiu/docx
|
[travis-url]: https://travis-ci.org/dolanmiu/docx
|
||||||
[daviddm-image]: https://david-dm.org/dolanmiu/docx.svg?theme=shields.io
|
[daviddm-image]: https://david-dm.org/dolanmiu/docx.svg?theme=shields.io
|
||||||
@ -82,6 +80,7 @@ Huge thanks to [@felipeochoa](https://github.com/felipeochoa) for awesome contri
|
|||||||
[snky-url]: https://snyk.io/test/github/dolanmiu/docx
|
[snky-url]: https://snyk.io/test/github/dolanmiu/docx
|
||||||
[gitter-image]: https://badges.gitter.im/dolanmiu/docx.svg
|
[gitter-image]: https://badges.gitter.im/dolanmiu/docx.svg
|
||||||
[gitter-url]: https://gitter.im/docx-lib/Lobby
|
[gitter-url]: https://gitter.im/docx-lib/Lobby
|
||||||
[gemnasium-image]: https://gemnasium.com/badges/github.com/dolanmiu/docx.svg
|
[pr-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
|
||||||
[gemnasium-url]: https://gemnasium.com/github.com/dolanmiu/docx
|
[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
|
||||||
|
2
demo/assets/custom-styles.xml
Normal file
39
demo/browser-demo.html
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<script src="../build/index.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>DOCX browser Word document generation</h1>
|
||||||
|
|
||||||
|
<button type="button" onclick="generate()">Click to generate document</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function generate() {
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const paragraph = new Paragraph("Hello World");
|
||||||
|
const institutionText = new TextRun("Foo Bar").bold();
|
||||||
|
const dateText = new TextRun("Github is the best").tab().bold();
|
||||||
|
paragraph.addRun(institutionText);
|
||||||
|
paragraph.addRun(dateText);
|
||||||
|
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBlob(doc).then(blob => {
|
||||||
|
console.log(blob);
|
||||||
|
saveAs(blob, "example.docx");
|
||||||
|
console.log("Document created successfully");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -1,22 +0,0 @@
|
|||||||
const docx = require('../build');
|
|
||||||
|
|
||||||
var doc = new docx.Document();
|
|
||||||
|
|
||||||
var paragraph = new docx.Paragraph("Hello World");
|
|
||||||
var institutionText = new docx.TextRun("University College London").bold();
|
|
||||||
var dateText = new docx.TextRun("5th Dec 2015").tab().bold();
|
|
||||||
paragraph.addRun(institutionText);
|
|
||||||
paragraph.addRun(dateText);
|
|
||||||
|
|
||||||
doc.addParagraph(paragraph);
|
|
||||||
|
|
||||||
// Feature coming soon
|
|
||||||
// var media = new docx.Media();
|
|
||||||
// media.addMedia("happy-penguins", "./demo/penguins.jpg");
|
|
||||||
// var pictureRun = new docx.PictureRun(media.getMedia("happy-penguins"));
|
|
||||||
|
|
||||||
// var exporter = new docx.LocalPacker(doc);
|
|
||||||
var exporter = new docx.LocalPacker(doc);
|
|
||||||
exporter.pack('My Document');
|
|
||||||
|
|
||||||
console.log('Document created succesfully at project root!');
|
|
20
demo/demo1.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Simple example to add text to a document
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Packer, Paragraph, TextRun } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const paragraph = new Paragraph("Hello World");
|
||||||
|
const institutionText = new TextRun("Foo Bar").bold();
|
||||||
|
const dateText = new TextRun("Github is the best").tab().bold();
|
||||||
|
paragraph.addRun(institutionText);
|
||||||
|
paragraph.addRun(dateText);
|
||||||
|
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
328
demo/demo10.ts
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
// Add images to header and footer
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Packer, Paragraph, TextRun } from "../build";
|
||||||
|
|
||||||
|
// tslint:disable:no-shadowed-variable
|
||||||
|
|
||||||
|
const PHONE_NUMBER = "07534563401";
|
||||||
|
const PROFILE_URL = "https://www.linkedin.com/in/dolan1";
|
||||||
|
const EMAIL = "docx@com";
|
||||||
|
|
||||||
|
const experiences = [
|
||||||
|
{
|
||||||
|
isCurrent: true,
|
||||||
|
summary: "Full-stack developer working with Angular and Java. Working for the iShares platform",
|
||||||
|
title: "Associate Software Developer",
|
||||||
|
startDate: {
|
||||||
|
month: 11,
|
||||||
|
year: 2017,
|
||||||
|
},
|
||||||
|
company: {
|
||||||
|
name: "BlackRock",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCurrent: false,
|
||||||
|
summary:
|
||||||
|
"Full-stack developer working with Angular, Node and TypeScript. Working for the iShares platform. Emphasis on Dev-ops and developing the continous integration pipeline.",
|
||||||
|
title: "Software Developer",
|
||||||
|
endDate: {
|
||||||
|
month: 11,
|
||||||
|
year: 2017,
|
||||||
|
},
|
||||||
|
startDate: {
|
||||||
|
month: 10,
|
||||||
|
year: 2016,
|
||||||
|
},
|
||||||
|
company: {
|
||||||
|
name: "Torch Markets",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCurrent: false,
|
||||||
|
summary:
|
||||||
|
"Used ASP.NET MVC 5 to produce a diversity data collection tool for the future of British television.\n\nUsed AngularJS and C# best practices. Technologies used include JavaScript, ASP.NET MVC 5, SQL, Oracle, SASS, Bootstrap, Grunt.",
|
||||||
|
title: "Software Developer",
|
||||||
|
endDate: {
|
||||||
|
month: 10,
|
||||||
|
year: 2016,
|
||||||
|
},
|
||||||
|
startDate: {
|
||||||
|
month: 3,
|
||||||
|
year: 2015,
|
||||||
|
},
|
||||||
|
company: {
|
||||||
|
name: "Soundmouse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCurrent: false,
|
||||||
|
summary:
|
||||||
|
"Develop web commerce platforms for various high profile clients.\n\nCreated a log analysis web application with the Play Framework in Java, incorporating Test Driven Development. It asynchronously uploads and processes large (2 GB) log files, and outputs meaningful results in context with the problem. \n\nAnalysis and development of the payment system infrastructure and user accounts section to be used by several clients of the company such as Waitrose, Tally Weijl, DJ Sports, Debenhams, Ann Summers, John Lewis and others.\n\nTechnologies used include WebSphere Commerce, Java, JavaScript and JSP.",
|
||||||
|
title: "Java Developer",
|
||||||
|
endDate: {
|
||||||
|
month: 10,
|
||||||
|
year: 2014,
|
||||||
|
},
|
||||||
|
startDate: {
|
||||||
|
month: 3,
|
||||||
|
year: 2013,
|
||||||
|
},
|
||||||
|
company: {
|
||||||
|
name: "Soundmouse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const education = [
|
||||||
|
{
|
||||||
|
degree: "Master of Science (MSc)",
|
||||||
|
fieldOfStudy: "Computer Science",
|
||||||
|
notes:
|
||||||
|
"Exam Results: 1st Class with Distinction, Dissertation: 1st Class with Distinction\n\nRelevant Courses: Java and C# Programming, Software Engineering, Artificial Intelligence, \nComputational Photography, Algorithmics, Architecture and Hardware.\n\nCreated a Windows 8 game in JavaScript for the dissertation. \n\nCreated an award-winning 3D stereoscopic game in C# using XNA.",
|
||||||
|
schoolName: "University College London",
|
||||||
|
startDate: {
|
||||||
|
year: 2012,
|
||||||
|
},
|
||||||
|
endDate: {
|
||||||
|
year: 2013,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
degree: "Bachelor of Engineering (BEng)",
|
||||||
|
fieldOfStudy: "Material Science and Engineering",
|
||||||
|
notes:
|
||||||
|
"Exam Results: 2:1, Dissertation: 1st Class with Distinction\n\nRelevant courses: C Programming, Mathematics and Business for Engineers.",
|
||||||
|
schoolName: "Imperial College London",
|
||||||
|
startDate: {
|
||||||
|
year: 2009,
|
||||||
|
},
|
||||||
|
endDate: {
|
||||||
|
year: 2012,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const skills = [
|
||||||
|
{
|
||||||
|
name: "Angular",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TypeScript",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "JavaScript",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NodeJS",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const achievements = [
|
||||||
|
{
|
||||||
|
issuer: "Oracle",
|
||||||
|
name: "Oracle Certified Expert",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
class DocumentCreator {
|
||||||
|
public create(data: object[]): Document {
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
const experiences = data[0] as any[];
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
const educations = data[1] as any[];
|
||||||
|
const skills = data[2] as object[];
|
||||||
|
const achivements = data[3] as object[];
|
||||||
|
const document = new Document();
|
||||||
|
document.addParagraph(new Paragraph("Dolan Miu").title());
|
||||||
|
|
||||||
|
document.addParagraph(this.createContactInfo(PHONE_NUMBER, PROFILE_URL, EMAIL));
|
||||||
|
document.addParagraph(this.createHeading("Education"));
|
||||||
|
|
||||||
|
for (const education of educations) {
|
||||||
|
document.addParagraph(
|
||||||
|
this.createInstitutionHeader(education.schoolName, `${education.startDate.year} - ${education.endDate.year}`),
|
||||||
|
);
|
||||||
|
document.addParagraph(this.createRoleText(`${education.fieldOfStudy} - ${education.degree}`));
|
||||||
|
|
||||||
|
const bulletPoints = this.splitParagraphIntoBullets(education.notes);
|
||||||
|
bulletPoints.forEach((bulletPoint) => {
|
||||||
|
document.addParagraph(this.createBullet(bulletPoint));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addParagraph(this.createHeading("Experience"));
|
||||||
|
|
||||||
|
for (const position of experiences) {
|
||||||
|
document.addParagraph(
|
||||||
|
this.createInstitutionHeader(
|
||||||
|
position.company.name,
|
||||||
|
this.createPositionDateText(position.startDate, position.endDate, position.isCurrent),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
document.addParagraph(this.createRoleText(position.title));
|
||||||
|
|
||||||
|
const bulletPoints = this.splitParagraphIntoBullets(position.summary);
|
||||||
|
|
||||||
|
bulletPoints.forEach((bulletPoint) => {
|
||||||
|
document.addParagraph(this.createBullet(bulletPoint));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addParagraph(this.createHeading("Skills, Achievements and Interests"));
|
||||||
|
|
||||||
|
document.addParagraph(this.createSubHeading("Skills"));
|
||||||
|
document.addParagraph(this.createSkillList(skills));
|
||||||
|
|
||||||
|
document.addParagraph(this.createSubHeading("Achievements"));
|
||||||
|
|
||||||
|
for (const achievementParagraph of this.createAchivementsList(achivements)) {
|
||||||
|
document.addParagraph(achievementParagraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addParagraph(this.createSubHeading("Interests"));
|
||||||
|
|
||||||
|
document.addParagraph(this.createInterests("Programming, Technology, Music Production, Web Design, 3D Modelling, Dancing."));
|
||||||
|
|
||||||
|
document.addParagraph(this.createHeading("References"));
|
||||||
|
|
||||||
|
document.addParagraph(
|
||||||
|
new Paragraph(
|
||||||
|
"Dr. Dean Mohamedally Director of Postgraduate Studies Department of Computer Science, University College London Malet Place, Bloomsbury, London WC1E d.mohamedally@ucl.ac.uk",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
document.addParagraph(new Paragraph("More references upon request"));
|
||||||
|
document.addParagraph(
|
||||||
|
new Paragraph(
|
||||||
|
"This CV was generated in real-time based on my Linked-In profile from my personal website www.dolan.bio.",
|
||||||
|
).center(),
|
||||||
|
);
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createContactInfo(phoneNumber: string, profileUrl: string, email: string): Paragraph {
|
||||||
|
const paragraph = new Paragraph().center();
|
||||||
|
const contactInfo = new TextRun(`Mobile: ${phoneNumber} | LinkedIn: ${profileUrl} | Email: ${email}`);
|
||||||
|
const address = new TextRun("Address: 58 Elm Avenue, Kent ME4 6ER, UK").break();
|
||||||
|
|
||||||
|
paragraph.addRun(contactInfo);
|
||||||
|
paragraph.addRun(address);
|
||||||
|
|
||||||
|
return paragraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createHeading(text: string): Paragraph {
|
||||||
|
return new Paragraph(text).heading1().thematicBreak();
|
||||||
|
}
|
||||||
|
|
||||||
|
public createSubHeading(text: string): Paragraph {
|
||||||
|
return new Paragraph(text).heading2();
|
||||||
|
}
|
||||||
|
|
||||||
|
public createInstitutionHeader(institutionName: string, dateText: string): Paragraph {
|
||||||
|
const paragraph = new Paragraph().maxRightTabStop();
|
||||||
|
const institution = new TextRun(institutionName).bold();
|
||||||
|
const date = new TextRun(dateText).tab().bold();
|
||||||
|
|
||||||
|
paragraph.addRun(institution);
|
||||||
|
paragraph.addRun(date);
|
||||||
|
|
||||||
|
return paragraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createRoleText(roleText: string): Paragraph {
|
||||||
|
const paragraph = new Paragraph();
|
||||||
|
const role = new TextRun(roleText).italic();
|
||||||
|
|
||||||
|
paragraph.addRun(role);
|
||||||
|
|
||||||
|
return paragraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createBullet(text: string): Paragraph {
|
||||||
|
return new Paragraph(text).bullet();
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
public createSkillList(skills: any[]): Paragraph {
|
||||||
|
const paragraph = new Paragraph();
|
||||||
|
const skillConcat = skills.map((skill) => skill.name).join(", ") + ".";
|
||||||
|
|
||||||
|
paragraph.addRun(new TextRun(skillConcat));
|
||||||
|
|
||||||
|
return paragraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
public createAchivementsList(achivements: any[]): Paragraph[] {
|
||||||
|
const arr: Paragraph[] = [];
|
||||||
|
|
||||||
|
for (const achievement of achivements) {
|
||||||
|
const paragraph = new Paragraph(achievement.name).bullet();
|
||||||
|
arr.push(paragraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createInterests(interests: string): Paragraph {
|
||||||
|
const paragraph = new Paragraph();
|
||||||
|
|
||||||
|
paragraph.addRun(new TextRun(interests));
|
||||||
|
return paragraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
public splitParagraphIntoBullets(text: string): string[] {
|
||||||
|
return text.split("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
public createPositionDateText(startDate: any, endDate: any, isCurrent: boolean): string {
|
||||||
|
const startDateText = this.getMonthFromInt(startDate.month) + ". " + startDate.year;
|
||||||
|
const endDateText = isCurrent ? "Present" : `${this.getMonthFromInt(endDate.month)}. ${endDate.year}`;
|
||||||
|
|
||||||
|
return `${startDateText} - ${endDateText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMonthFromInt(value: number): string {
|
||||||
|
switch (value) {
|
||||||
|
case 1:
|
||||||
|
return "Jan";
|
||||||
|
case 2:
|
||||||
|
return "Feb";
|
||||||
|
case 3:
|
||||||
|
return "Mar";
|
||||||
|
case 4:
|
||||||
|
return "Apr";
|
||||||
|
case 5:
|
||||||
|
return "May";
|
||||||
|
case 6:
|
||||||
|
return "Jun";
|
||||||
|
case 7:
|
||||||
|
return "Jul";
|
||||||
|
case 8:
|
||||||
|
return "Aug";
|
||||||
|
case 9:
|
||||||
|
return "Sept";
|
||||||
|
case 10:
|
||||||
|
return "Oct";
|
||||||
|
case 11:
|
||||||
|
return "Nov";
|
||||||
|
case 12:
|
||||||
|
return "Dec";
|
||||||
|
default:
|
||||||
|
return "N/A";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const documentCreator = new DocumentCreator();
|
||||||
|
|
||||||
|
const doc = documentCreator.create([experiences, education, skills, achievements]);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
141
demo/demo11.ts
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// Setting styles with JavaScript configuration
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Packer, Paragraph, Table } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document(undefined, {
|
||||||
|
top: 700,
|
||||||
|
right: 700,
|
||||||
|
bottom: 700,
|
||||||
|
left: 700,
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("Heading1", "Heading 1")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.quickFormat()
|
||||||
|
.font("Calibri")
|
||||||
|
.size(52)
|
||||||
|
.center()
|
||||||
|
.bold()
|
||||||
|
.color("000000")
|
||||||
|
.spacing({ line: 340 })
|
||||||
|
.underline("single", "000000");
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("Heading2", "Heading 2")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.font("Calibri")
|
||||||
|
.quickFormat()
|
||||||
|
.size(26)
|
||||||
|
.bold()
|
||||||
|
.spacing({ line: 340 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("Heading3", "Heading 3")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.font("Calibri")
|
||||||
|
.quickFormat()
|
||||||
|
.size(26)
|
||||||
|
.bold()
|
||||||
|
.spacing({ line: 276 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("Heading4", "Heading 4")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.justified()
|
||||||
|
.font("Calibri")
|
||||||
|
.size(26)
|
||||||
|
.bold();
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("normalPara", "Normal Para")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.font("Calibri")
|
||||||
|
.quickFormat()
|
||||||
|
.leftTabStop(453.543307087)
|
||||||
|
.maxRightTabStop()
|
||||||
|
.size(26)
|
||||||
|
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("normalPara2", "Normal Para2")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.quickFormat()
|
||||||
|
.font("Calibri")
|
||||||
|
.size(26)
|
||||||
|
.justified()
|
||||||
|
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("aside", "Aside")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.color("999999")
|
||||||
|
.italics()
|
||||||
|
.indent({ left: 720 })
|
||||||
|
.spacing({ line: 276 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("wellSpaced", "Well Spaced")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph")
|
||||||
|
.quickFormat()
|
||||||
|
.basedOn("Normal");
|
||||||
|
|
||||||
|
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||||
|
doc
|
||||||
|
.createParagraph("HEADING")
|
||||||
|
.heading1()
|
||||||
|
.center();
|
||||||
|
|
||||||
|
doc.Footer.createParagraph("1")
|
||||||
|
.style("normalPara")
|
||||||
|
.right();
|
||||||
|
|
||||||
|
doc.createParagraph("Ref. :").style("normalPara");
|
||||||
|
doc.createParagraph("Date :").style("normalPara");
|
||||||
|
|
||||||
|
doc.createParagraph("To,").style("normalPara");
|
||||||
|
doc.createParagraph("The Superindenting Engineer,(O &M)").style("normalPara");
|
||||||
|
|
||||||
|
doc.createParagraph("Sub : ").style("normalPara");
|
||||||
|
|
||||||
|
doc.createParagraph("Ref. : ").style("normalPara");
|
||||||
|
|
||||||
|
doc.createParagraph("Sir,").style("normalPara");
|
||||||
|
|
||||||
|
doc.createParagraph("BRIEF DESCRIPTION").style("normalPara");
|
||||||
|
|
||||||
|
const table = new Table(4, 4);
|
||||||
|
table
|
||||||
|
.getRow(0)
|
||||||
|
.getCell(0)
|
||||||
|
.addContent(new Paragraph("Pole No."));
|
||||||
|
// table.Properties.width = 10000;
|
||||||
|
doc.addTable(table);
|
||||||
|
|
||||||
|
const arrboth = [
|
||||||
|
{
|
||||||
|
image: "./demo/images/pizza.gif",
|
||||||
|
comment: "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: "./demo/images/pizza.gif",
|
||||||
|
comment: "Test 2",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
arrboth.forEach((item) => {
|
||||||
|
const para = doc.createParagraph();
|
||||||
|
para.addImage(doc.createImage(fs.readFileSync(item.image)));
|
||||||
|
// para.Properties.width = 60;
|
||||||
|
// para.Properties.height = 90;
|
||||||
|
doc.createParagraph(item.comment).style("normalPara2");
|
||||||
|
});
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
25
demo/demo12.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Scaling images
|
||||||
|
// 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("Hello World");
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
const image = doc.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||||
|
const image2 = doc.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||||
|
const image3 = doc.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||||
|
const image4 = doc.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||||
|
|
||||||
|
image.scale(0.5);
|
||||||
|
image2.scale(1);
|
||||||
|
image3.scale(2.5);
|
||||||
|
image4.scale(4);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
28
demo/demo13.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// This example shows 3 styles using XML styles
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Packer, Paragraph } from "../build";
|
||||||
|
|
||||||
|
const styles = fs.readFileSync("./demo/assets/custom-styles.xml", "utf-8");
|
||||||
|
const doc = new Document({
|
||||||
|
title: "Title",
|
||||||
|
externalStyles: styles,
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.createParagraph("Cool Heading Text").heading1();
|
||||||
|
|
||||||
|
const paragraph = new Paragraph('This is a custom named style from the template "MyFancyStyle"');
|
||||||
|
paragraph.style("MyFancyStyle");
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
doc.createParagraph("Some normal text");
|
||||||
|
|
||||||
|
doc.createParagraph("MyFancyStyle again").style("MyFancyStyle");
|
||||||
|
paragraph.style("MyFancyStyle");
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
28
demo/demo14.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Page numbers
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
doc.createParagraph("First Page").pageBreak();
|
||||||
|
doc.createParagraph("Second Page");
|
||||||
|
|
||||||
|
const pageNumber = new TextRun("Page ").pageNumber();
|
||||||
|
|
||||||
|
const pageoneheader = new Paragraph("First Page Header ").right();
|
||||||
|
|
||||||
|
pageoneheader.addRun(pageNumber);
|
||||||
|
const firstPageHeader = doc.createFirstPageHeader();
|
||||||
|
firstPageHeader.addParagraph(pageoneheader);
|
||||||
|
|
||||||
|
const pagetwoheader = new Paragraph("My Title ").right();
|
||||||
|
|
||||||
|
pagetwoheader.addRun(pageNumber);
|
||||||
|
doc.Header.addParagraph(pagetwoheader);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
18
demo/demo15.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Page break before example
|
||||||
|
// 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("Hello World");
|
||||||
|
const paragraph2 = new Paragraph("Hello World on another page").pageBreakBefore();
|
||||||
|
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
doc.addParagraph(paragraph2);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
48
demo/demo16.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const paragraph = new Paragraph("Hello World").pageBreak();
|
||||||
|
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
const header = doc.createHeader();
|
||||||
|
header.createParagraph("Header on another page");
|
||||||
|
const footer = doc.createFooter();
|
||||||
|
footer.createParagraph("Footer on another page");
|
||||||
|
|
||||||
|
doc.addSection({
|
||||||
|
headers: {
|
||||||
|
default: header,
|
||||||
|
},
|
||||||
|
footers: {
|
||||||
|
default: footer,
|
||||||
|
},
|
||||||
|
pageNumberStart: 1,
|
||||||
|
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.createParagraph("hello");
|
||||||
|
|
||||||
|
doc.addSection({
|
||||||
|
headers: {
|
||||||
|
default: header,
|
||||||
|
},
|
||||||
|
footers: {
|
||||||
|
default: footer,
|
||||||
|
},
|
||||||
|
pageNumberStart: 1,
|
||||||
|
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||||
|
orientation: PageOrientation.LANDSCAPE,
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.createParagraph("hello in landscape");
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
21
demo/demo17.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Footnotes
|
||||||
|
// 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("Hello World").referenceFootnote(1);
|
||||||
|
const paragraph2 = new Paragraph("Hello World").referenceFootnote(2);
|
||||||
|
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
doc.addParagraph(paragraph2);
|
||||||
|
|
||||||
|
doc.createFootnote(new Paragraph("Test"));
|
||||||
|
doc.createFootnote(new Paragraph("My amazing reference"));
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
17
demo/demo18.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Insert image from a buffer
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
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`;
|
||||||
|
|
||||||
|
// doc.createImage(Buffer.from(imageBase64Data, 'base64'));
|
||||||
|
doc.createImage(Buffer.from(imageBase64Data, "base64"), 100, 100);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
20
demo/demo19.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Export to base64 string - Useful in a browser environment.
|
||||||
|
// 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");
|
||||||
|
const institutionText = new TextRun("Foo").bold();
|
||||||
|
const dateText = new TextRun("Bar").tab().bold();
|
||||||
|
paragraph.addRun(institutionText);
|
||||||
|
paragraph.addRun(dateText);
|
||||||
|
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBase64String(doc).then((str) => {
|
||||||
|
fs.writeFileSync("My Document.docx", str);
|
||||||
|
});
|
@ -1,74 +0,0 @@
|
|||||||
const docx = require('../build');
|
|
||||||
|
|
||||||
const styles = new docx.Styles();
|
|
||||||
styles.createParagraphStyle('Heading1', 'Heading 1')
|
|
||||||
.basedOn("Normal")
|
|
||||||
.next("Normal")
|
|
||||||
.quickFormat()
|
|
||||||
.size(28)
|
|
||||||
.bold()
|
|
||||||
.italics()
|
|
||||||
.spacing({after: 120});
|
|
||||||
|
|
||||||
styles.createParagraphStyle('Heading2', 'Heading 2')
|
|
||||||
.basedOn("Normal")
|
|
||||||
.next("Normal")
|
|
||||||
.quickFormat()
|
|
||||||
.size(26)
|
|
||||||
.bold()
|
|
||||||
.underline('double', 'FF0000')
|
|
||||||
.spacing({before: 240, after: 120});
|
|
||||||
|
|
||||||
styles.createParagraphStyle('aside', 'Aside')
|
|
||||||
.basedOn('Normal')
|
|
||||||
.next('Normal')
|
|
||||||
.color('999999')
|
|
||||||
.italics()
|
|
||||||
.indent(720)
|
|
||||||
.spacing({line: 276});
|
|
||||||
|
|
||||||
styles.createParagraphStyle('wellSpaced', 'Well Spaced')
|
|
||||||
.basedOn('Normal')
|
|
||||||
.spacing({line: 276, before: 20 * 72 * .1, after: 20 * 72 * .05});
|
|
||||||
|
|
||||||
styles.createParagraphStyle('ListParagraph', 'List Paragraph')
|
|
||||||
.quickFormat()
|
|
||||||
.basedOn('Normal');
|
|
||||||
|
|
||||||
|
|
||||||
const numbering = new docx.Numbering();
|
|
||||||
const numberedAbstract = numbering.createAbstractNumbering();
|
|
||||||
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "left");
|
|
||||||
|
|
||||||
const doc = new docx.Document({
|
|
||||||
creator: 'Clippy',
|
|
||||||
title: 'Sample Document',
|
|
||||||
description: 'A brief example of using docx',
|
|
||||||
});
|
|
||||||
|
|
||||||
doc.createParagraph('Test heading1, bold and italicized').heading1();
|
|
||||||
doc.createParagraph('Some simple content');
|
|
||||||
doc.createParagraph('Test heading2 with double red underline').heading2();
|
|
||||||
|
|
||||||
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
|
|
||||||
const letterNumbering5 = numbering.createConcreteNumbering(numberedAbstract);
|
|
||||||
letterNumbering5.overrideLevel(0, 5);
|
|
||||||
|
|
||||||
doc.createParagraph('Option1').setNumbering(letterNumbering, 0);
|
|
||||||
doc.createParagraph('Option5 -- override 2 to 5').setNumbering(letterNumbering5, 0);
|
|
||||||
doc.createParagraph('Option3').setNumbering(letterNumbering, 0);
|
|
||||||
|
|
||||||
doc.createParagraph()
|
|
||||||
.createTextRun('Some monospaced content')
|
|
||||||
.font('Monospace');
|
|
||||||
|
|
||||||
doc.createParagraph('An aside, in light gray italics and indented').style('aside');
|
|
||||||
doc.createParagraph('This is normal, but well-spaced text').style('wellSpaced');
|
|
||||||
const para = doc.createParagraph();
|
|
||||||
para.createTextRun('This is a bold run,').bold();
|
|
||||||
para.createTextRun(' switching to normal ');
|
|
||||||
para.createTextRun('and then underlined ').underline();
|
|
||||||
para.createTextRun('and back to normal.');
|
|
||||||
|
|
||||||
const exporter = new docx.LocalPacker(doc, styles, undefined, numbering);
|
|
||||||
exporter.pack('test.docx');
|
|
78
demo/demo2.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// Example on how to customise the look at feel using Styles
|
||||||
|
// 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({
|
||||||
|
creator: "Clippy",
|
||||||
|
title: "Sample Document",
|
||||||
|
description: "A brief example of using docx",
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("Heading1", "Heading 1")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.quickFormat()
|
||||||
|
.size(28)
|
||||||
|
.bold()
|
||||||
|
.italics()
|
||||||
|
.spacing({ after: 120 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("Heading2", "Heading 2")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.quickFormat()
|
||||||
|
.size(26)
|
||||||
|
.bold()
|
||||||
|
.underline("double", "FF0000")
|
||||||
|
.spacing({ before: 240, after: 120 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("aside", "Aside")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.color("999999")
|
||||||
|
.italics()
|
||||||
|
.indent({ left: 720 })
|
||||||
|
.spacing({ line: 276 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("wellSpaced", "Well Spaced")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph")
|
||||||
|
.quickFormat()
|
||||||
|
.basedOn("Normal");
|
||||||
|
|
||||||
|
const numberedAbstract = doc.Numbering.createAbstractNumbering();
|
||||||
|
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "left");
|
||||||
|
|
||||||
|
doc.createParagraph("Test heading1, bold and italicized").heading1();
|
||||||
|
doc.createParagraph("Some simple content");
|
||||||
|
doc.createParagraph("Test heading2 with double red underline").heading2();
|
||||||
|
|
||||||
|
const letterNumbering = doc.Numbering.createConcreteNumbering(numberedAbstract);
|
||||||
|
const letterNumbering5 = doc.Numbering.createConcreteNumbering(numberedAbstract);
|
||||||
|
letterNumbering5.overrideLevel(0, 5);
|
||||||
|
|
||||||
|
doc.createParagraph("Option1").setNumbering(letterNumbering, 0);
|
||||||
|
doc.createParagraph("Option5 -- override 2 to 5").setNumbering(letterNumbering5, 0);
|
||||||
|
doc.createParagraph("Option3").setNumbering(letterNumbering, 0);
|
||||||
|
|
||||||
|
doc
|
||||||
|
.createParagraph()
|
||||||
|
.createTextRun("Some monospaced content")
|
||||||
|
.font("Monospace");
|
||||||
|
|
||||||
|
doc.createParagraph("An aside, in light gray italics and indented").style("aside");
|
||||||
|
doc.createParagraph("This is normal, but well-spaced text").style("wellSpaced");
|
||||||
|
const para = doc.createParagraph();
|
||||||
|
para.createTextRun("This is a bold run,").bold();
|
||||||
|
para.createTextRun(" switching to normal ");
|
||||||
|
para.createTextRun("and then underlined ").underline();
|
||||||
|
para.createTextRun("and back to normal.");
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
21
demo/demo20.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Add custom borders to table cell
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { BorderStyle, Document, Packer, Paragraph } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const table = doc.createTable(4, 4);
|
||||||
|
table
|
||||||
|
.getCell(2, 2)
|
||||||
|
.addContent(new Paragraph("Hello"))
|
||||||
|
.CellProperties.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");
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
37
demo/demo21.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// This demo shows how to create bookmarks then link to them with internal hyperlinks
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Packer } from "../build";
|
||||||
|
|
||||||
|
const loremIpsum =
|
||||||
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mi velit, convallis convallis scelerisque nec, faucibus nec leo. Phasellus at posuere mauris, tempus dignissim velit. Integer et tortor dolor. Duis auctor efficitur mattis. Vivamus ut metus accumsan tellus auctor sollicitudin venenatis et nibh. Cras quis massa ac metus fringilla venenatis. Proin rutrum mauris purus, ut suscipit magna consectetur id. Integer consectetur sollicitudin ante, vitae faucibus neque efficitur in. Praesent ultricies nibh lectus. Mauris pharetra id odio eget iaculis. Duis dictum, risus id pellentesque rutrum, lorem quam malesuada massa, quis ullamcorper turpis urna a diam. Cras vulputate metus vel massa porta ullamcorper. Etiam porta condimentum nulla nec tristique. Sed nulla urna, pharetra non tortor sed, sollicitudin molestie diam. Maecenas enim leo, feugiat eget vehicula id, sollicitudin vitae ante.";
|
||||||
|
|
||||||
|
const doc = new Document({
|
||||||
|
creator: "Clippy",
|
||||||
|
title: "Sample Document",
|
||||||
|
description: "A brief example of using docx with bookmarks and internal hyperlinks",
|
||||||
|
});
|
||||||
|
|
||||||
|
const anchorId = "anchorID";
|
||||||
|
|
||||||
|
// First create the bookmark
|
||||||
|
const bookmark = doc.createBookmark(anchorId, "Lorem Ipsum");
|
||||||
|
// That has header styling
|
||||||
|
doc
|
||||||
|
.createParagraph()
|
||||||
|
.addBookmark(bookmark)
|
||||||
|
.heading1();
|
||||||
|
doc.createParagraph("\n");
|
||||||
|
|
||||||
|
doc.createParagraph(loremIpsum);
|
||||||
|
doc.createParagraph().pageBreak();
|
||||||
|
|
||||||
|
// Now the link back up to the bookmark
|
||||||
|
const hyperlink = doc.createInternalHyperLink(anchorId, `Click me!`);
|
||||||
|
doc.createParagraph().addHyperLink(hyperlink);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
27
demo/demo22.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// This demo shows right to left for special languages
|
||||||
|
// 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 paragraph1 = new Paragraph().bidirectional();
|
||||||
|
const textRun1 = new TextRun("שלום עולם").rightToLeft();
|
||||||
|
paragraph1.addRun(textRun1);
|
||||||
|
doc.addParagraph(paragraph1);
|
||||||
|
|
||||||
|
const paragraph2 = new Paragraph().bidirectional();
|
||||||
|
const textRun2 = new TextRun("שלום עולם").bold().rightToLeft();
|
||||||
|
paragraph2.addRun(textRun2);
|
||||||
|
doc.addParagraph(paragraph2);
|
||||||
|
|
||||||
|
const paragraph3 = new Paragraph().bidirectional();
|
||||||
|
const textRun3 = new TextRun("שלום עולם").italic().rightToLeft();
|
||||||
|
paragraph3.addRun(textRun3);
|
||||||
|
doc.addParagraph(paragraph3);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
34
demo/demo23.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// This demo adds an image to the Media cache, and then insert to the document afterwards
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Media, Packer, Paragraph } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const paragraph = new Paragraph("Hello World");
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// I am adding an image to the paragraph rather than the document to make the image inline
|
||||||
|
paragraph.addImage(image5);
|
||||||
|
|
||||||
|
doc.addImage(image);
|
||||||
|
doc.addImage(image2);
|
||||||
|
doc.addImage(image3);
|
||||||
|
doc.addImage(image4);
|
||||||
|
doc.addImage(image5);
|
||||||
|
doc.addImage(image6);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
18
demo/demo24.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// 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, Paragraph } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const table = doc.createTable(4, 4);
|
||||||
|
table.getCell(2, 2).addContent(new Paragraph("Hello"));
|
||||||
|
|
||||||
|
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||||
|
table.getCell(1, 1).addContent(image.Paragraph);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
22
demo/demo26.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Creates two paragraphs, one with a border and one without
|
||||||
|
// 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("No border!");
|
||||||
|
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
const borderParagraph = new Paragraph("I have borders on my top and bottom sides!").createBorder();
|
||||||
|
borderParagraph.Borders.addTopBorder();
|
||||||
|
borderParagraph.Borders.addBottomBorder();
|
||||||
|
|
||||||
|
doc.addParagraph(borderParagraph);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
33
demo/demo27.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Packer } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
const myStyles = doc.Styles;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
myStyles.createParagraphStyle("myWonkyStyle", "My Wonky Style")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.color("990000")
|
||||||
|
.italics()
|
||||||
|
.indent({left: 720}) // 720 TWIP === 720 / 20 pt === .5 in
|
||||||
|
.spacing({line: 276}); // 276 / 240 = 1.15x line spacing
|
||||||
|
|
||||||
|
myStyles.createParagraphStyle("Heading2", "Heading 2")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.quickFormat()
|
||||||
|
.size(26) // 26 half-points === 13pt font
|
||||||
|
.bold()
|
||||||
|
.underline("double", "FF0000")
|
||||||
|
.spacing({before: 240, after: 120}); // TWIP for both
|
||||||
|
|
||||||
|
doc.createParagraph("Hello").style("myWonkyStyle");
|
||||||
|
doc.createParagraph("World").heading2(); // Uses the Heading2 style
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
43
demo/demo28.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Creates two paragraphs, one with a border and one without
|
||||||
|
// 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);
|
||||||
|
});
|
32
demo/demo29.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
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);
|
||||||
|
});
|
@ -1,35 +0,0 @@
|
|||||||
const docx = require('../build');
|
|
||||||
|
|
||||||
var doc = new docx.Document();
|
|
||||||
|
|
||||||
const numbering = new docx.Numbering();
|
|
||||||
|
|
||||||
const abstractNum = numbering.createAbstractNumbering();
|
|
||||||
abstractNum.createLevel(0, "upperRoman", "%1", "start")
|
|
||||||
.addParagraphProperty(new docx.Indent(720, 260));
|
|
||||||
abstractNum.createLevel(1, "decimal", "%2.", "start")
|
|
||||||
.addParagraphProperty(new docx.Indent(1440, 980));
|
|
||||||
abstractNum.createLevel(2, "lowerLetter", "%3)", "start")
|
|
||||||
.addParagraphProperty(new docx.Indent(2160, 1700));
|
|
||||||
|
|
||||||
const concrete = numbering.createConcreteNumbering(abstractNum);
|
|
||||||
|
|
||||||
var topLevelP = new docx.Paragraph("Hey you");
|
|
||||||
var subP = new docx.Paragraph("What's up fam");
|
|
||||||
var secondSubP = new docx.Paragraph("Hello World 2");
|
|
||||||
var subSubP = new docx.Paragraph("Yeah boi");
|
|
||||||
|
|
||||||
topLevelP.setNumbering(concrete, 0);
|
|
||||||
subP.setNumbering(concrete, 1);
|
|
||||||
secondSubP.setNumbering(concrete, 1);
|
|
||||||
subSubP.setNumbering(concrete, 2);
|
|
||||||
|
|
||||||
doc.addParagraph(topLevelP);
|
|
||||||
doc.addParagraph(subP);
|
|
||||||
doc.addParagraph(secondSubP);
|
|
||||||
doc.addParagraph(subSubP);
|
|
||||||
|
|
||||||
var exporter = new docx.LocalPacker(doc);
|
|
||||||
exporter.pack('My Document');
|
|
||||||
|
|
||||||
console.log('Document created succesfully at project root!');
|
|
46
demo/demo3.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Numbering and bullet points example
|
||||||
|
// 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 }));
|
||||||
|
abstractNum.createLevel(1, "decimal", "%2.", "start").addParagraphProperty(new Indent({ left: 1440, hanging: 980 }));
|
||||||
|
abstractNum.createLevel(2, "lowerLetter", "%3)", "start").addParagraphProperty(new Indent({ left: 14402160, hanging: 1700 }));
|
||||||
|
|
||||||
|
const concrete = numbering.createConcreteNumbering(abstractNum);
|
||||||
|
|
||||||
|
const topLevelP = new Paragraph("Hey you");
|
||||||
|
const subP = new Paragraph("What's up fam");
|
||||||
|
const secondSubP = new Paragraph("Hello World 2");
|
||||||
|
const subSubP = new Paragraph("Yeah boi");
|
||||||
|
|
||||||
|
topLevelP.setNumbering(concrete, 0);
|
||||||
|
subP.setNumbering(concrete, 1);
|
||||||
|
secondSubP.setNumbering(concrete, 1);
|
||||||
|
subSubP.setNumbering(concrete, 2);
|
||||||
|
|
||||||
|
doc.addParagraph(topLevelP);
|
||||||
|
doc.addParagraph(subP);
|
||||||
|
doc.addParagraph(secondSubP);
|
||||||
|
doc.addParagraph(subSubP);
|
||||||
|
|
||||||
|
const bullet1 = new Paragraph("Hey you").bullet();
|
||||||
|
const bullet2 = new Paragraph("What's up fam").bullet(1);
|
||||||
|
const bullet3 = new Paragraph("Hello World 2").bullet(2);
|
||||||
|
const bullet4 = new Paragraph("Yeah boi").bullet(3);
|
||||||
|
|
||||||
|
doc.addParagraph(bullet1);
|
||||||
|
doc.addParagraph(bullet2);
|
||||||
|
doc.addParagraph(bullet3);
|
||||||
|
doc.addParagraph(bullet4);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
29
demo/demo30.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
26
demo/demo31.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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(2, 2);
|
||||||
|
table
|
||||||
|
.getCell(1, 1)
|
||||||
|
.addContent(new Paragraph("This text should be in the middle of the cell"))
|
||||||
|
.CellProperties.setVerticalAlign(VerticalAlign.CENTER);
|
||||||
|
|
||||||
|
table
|
||||||
|
.getCell(1, 0)
|
||||||
|
.addContent(
|
||||||
|
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);
|
||||||
|
});
|
35
demo/demo32.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// 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 } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
let table = doc.createTable(2, 2);
|
||||||
|
|
||||||
|
table.getCell(0, 0).addContent(new Paragraph("Hello"));
|
||||||
|
table.getRow(0).mergeCells(0, 1);
|
||||||
|
|
||||||
|
doc.createParagraph("Another table").heading2();
|
||||||
|
|
||||||
|
table = doc.createTable(2, 3);
|
||||||
|
table.getCell(0, 0).addContent(new Paragraph("World"));
|
||||||
|
table.getRow(0).mergeCells(0, 2);
|
||||||
|
|
||||||
|
doc.createParagraph("Another table").heading2();
|
||||||
|
|
||||||
|
table = doc.createTable(2, 4);
|
||||||
|
table.getCell(0, 0).addContent(new Paragraph("Foo"));
|
||||||
|
|
||||||
|
table.getCell(1, 0).addContent(new Paragraph("Bar1"));
|
||||||
|
table.getCell(1, 1).addContent(new Paragraph("Bar2"));
|
||||||
|
table.getCell(1, 2).addContent(new Paragraph("Bar3"));
|
||||||
|
table.getCell(1, 3).addContent(new Paragraph("Bar4"));
|
||||||
|
|
||||||
|
table.getRow(0).mergeCells(0, 3);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
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);
|
||||||
|
});
|
32
demo/demo34.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// 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(2, 2).float({
|
||||||
|
horizontalAnchor: TableAnchorType.MARGIN,
|
||||||
|
verticalAnchor: TableAnchorType.MARGIN,
|
||||||
|
relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
|
||||||
|
relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
|
||||||
|
});
|
||||||
|
table.setFixedWidthLayout();
|
||||||
|
table.setWidth(WidthType.DXA, 4535);
|
||||||
|
|
||||||
|
table.getCell(0, 0).addContent(new Paragraph("Hello"));
|
||||||
|
table.getRow(0).mergeCells(0, 1);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
44
demo/demo38.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// 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, 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);
|
||||||
|
});
|
@ -1,12 +0,0 @@
|
|||||||
const docx = require('../build');
|
|
||||||
|
|
||||||
var doc = new docx.Document();
|
|
||||||
|
|
||||||
const table = doc.createTable(4, 4);
|
|
||||||
table.getCell(2, 2).addContent(new docx.Paragraph('Hello'));
|
|
||||||
|
|
||||||
|
|
||||||
var exporter = new docx.LocalPacker(doc);
|
|
||||||
exporter.pack('My Document');
|
|
||||||
|
|
||||||
console.log('Document created succesfully at project root!');
|
|
15
demo/demo4.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// 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 } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const table = doc.createTable(4, 4);
|
||||||
|
table.getCell(2, 2).addContent(new Paragraph("Hello"));
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
45
demo/demo5.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// 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, HorizontalPositionAlign, HorizontalPositionRelativeFrom, Packer, Paragraph, VerticalPositionAlign, VerticalPositionRelativeFrom} from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const paragraph = new Paragraph("Hello World");
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
doc.createImage(fs.readFileSync("./demo/images/image1.jpeg"));
|
||||||
|
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();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
29
demo/demo6.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Example of how to change page borders
|
||||||
|
// 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(undefined, {
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const paragraph = new Paragraph("Hello World");
|
||||||
|
const institutionText = new TextRun("Foo bar").bold();
|
||||||
|
const dateText = new TextRun("Github is the best").tab().bold();
|
||||||
|
paragraph.addRun(institutionText);
|
||||||
|
paragraph.addRun(dateText);
|
||||||
|
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
doc.createParagraph("Hello World").heading1();
|
||||||
|
doc.createParagraph("Foo bar");
|
||||||
|
doc.createParagraph("Github is the best");
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
18
demo/demo7.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Example of how to set the document to landscape
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Packer, PageOrientation, Paragraph } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document(undefined, {
|
||||||
|
orientation: PageOrientation.LANDSCAPE,
|
||||||
|
});
|
||||||
|
|
||||||
|
const paragraph = new Paragraph("Hello World");
|
||||||
|
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
17
demo/demo8.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Add text to header and footer
|
||||||
|
// 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.createParagraph("Hello World");
|
||||||
|
|
||||||
|
doc.Header.createParagraph("Header text");
|
||||||
|
doc.Footer.createParagraph("Footer text");
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
17
demo/demo9.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Add images to header and footer
|
||||||
|
// 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.createParagraph("Hello World");
|
||||||
|
|
||||||
|
doc.Header.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||||
|
doc.Footer.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
BIN
demo/dotx/template.dotx
Normal file
BIN
demo/images/cat.jpg
Normal file
After Width: | Height: | Size: 154 KiB |
BIN
demo/images/dog.png
Normal file
After Width: | Height: | Size: 190 KiB |
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
BIN
demo/images/parrots.bmp
Normal file
After Width: | Height: | Size: 818 KiB |
BIN
demo/images/pizza.gif
Normal file
After Width: | Height: | Size: 310 KiB |
@ -1,29 +0,0 @@
|
|||||||
var prompt = require('prompt');
|
|
||||||
var shelljs = require('shelljs');
|
|
||||||
var fs = require('fs');
|
|
||||||
|
|
||||||
console.log('What demo do you wish to run? (Enter a number)');
|
|
||||||
|
|
||||||
var schema = {
|
|
||||||
properties: {
|
|
||||||
number: {
|
|
||||||
pattern: /^[0-9]+$/,
|
|
||||||
message: 'Please enter a number.',
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
prompt.start();
|
|
||||||
|
|
||||||
prompt.get(schema, function (err, result) {
|
|
||||||
var demoNumber = result.number;
|
|
||||||
var filePath = `./demo/demo${demoNumber}.js`;
|
|
||||||
|
|
||||||
if (!fs.existsSync(filePath)) {
|
|
||||||
console.error(`demo${demoNumber} does not exist: ${filePath}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(`Running demo ${demoNumber}`);
|
|
||||||
shelljs.exec(`node ${filePath}`);
|
|
||||||
});
|
|
34
demo/index.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// tslint:disable:no-console
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as prompt from "prompt";
|
||||||
|
import * as shelljs from "shelljs";
|
||||||
|
|
||||||
|
console.log("What demo do you wish to run? (Enter a number)");
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
properties: {
|
||||||
|
number: {
|
||||||
|
pattern: /^[0-9]+$/,
|
||||||
|
message: "Please enter a number.",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
prompt.start();
|
||||||
|
|
||||||
|
prompt.get(schema, (_, result) => {
|
||||||
|
const demoNumber = result.number;
|
||||||
|
const filePath = `./demo/demo${demoNumber}.ts`;
|
||||||
|
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
console.error(`demo${demoNumber} does not exist: ${filePath}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(`Running demo ${demoNumber}`);
|
||||||
|
if (shelljs.exec(`npm run ts-node -- ${filePath}`).code === 0) {
|
||||||
|
console.log("Document created successfully");
|
||||||
|
} else {
|
||||||
|
console.error("Something went wrong with the demo");
|
||||||
|
}
|
||||||
|
});
|
@ -1,68 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e # Exit with nonzero exit code if anything fails
|
|
||||||
|
|
||||||
SOURCE_BRANCH="master"
|
|
||||||
TARGET_BRANCH="gh-pages"
|
|
||||||
|
|
||||||
function doCompile {
|
|
||||||
npm run typedoc
|
|
||||||
}
|
|
||||||
|
|
||||||
# Pull requests and commits to other branches shouldn't try to deploy, just build to verify
|
|
||||||
if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_BRANCH" != "$SOURCE_BRANCH" ]; then
|
|
||||||
echo "Skipping deploy; just doing a build."
|
|
||||||
doCompile
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Save some useful information
|
|
||||||
REPO=`git config remote.origin.url`
|
|
||||||
SSH_REPO=${REPO/https:\/\/github.com\//git@github.com:}
|
|
||||||
SHA=`git rev-parse --verify HEAD`
|
|
||||||
|
|
||||||
# Clone the existing gh-pages for this repo into docs/
|
|
||||||
# Create a new empty branch if gh-pages doesn't exist yet (should only happen on first deply)
|
|
||||||
git clone $REPO docs
|
|
||||||
cd docs
|
|
||||||
git checkout $TARGET_BRANCH || git checkout --orphan $TARGET_BRANCH
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
# Clean out existing contents
|
|
||||||
# echo "Cleaning out existing contents."
|
|
||||||
# rm -rf docs/*
|
|
||||||
|
|
||||||
# Run our compile script
|
|
||||||
doCompile
|
|
||||||
|
|
||||||
# Now let's go have some fun with the cloned repo
|
|
||||||
cd docs
|
|
||||||
git config user.name "Travis CI"
|
|
||||||
git config user.email "dolan_miu@hotmail.com"
|
|
||||||
ls
|
|
||||||
|
|
||||||
# add .nojekyll file
|
|
||||||
touch .nojekyll
|
|
||||||
|
|
||||||
# If there are no changes to the compiled out (e.g. this is a README update) then just bail.
|
|
||||||
if [ -z `git diff --exit-code` ]; then
|
|
||||||
echo "No changes to the output on this push; exiting."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Commit the "changes", i.e. the new version.
|
|
||||||
# The delta will show diffs between new and old versions.
|
|
||||||
git add .
|
|
||||||
git commit -m "Deploy to GitHub Pages: ${SHA}"
|
|
||||||
|
|
||||||
# Get the deploy key by using Travis's stored variables to decrypt deploy-key.enc
|
|
||||||
ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key"
|
|
||||||
ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv"
|
|
||||||
ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR}
|
|
||||||
ENCRYPTED_IV=${!ENCRYPTED_IV_VAR}
|
|
||||||
openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in deploy-key.enc -out deploy-key -d
|
|
||||||
chmod 600 deploy-key
|
|
||||||
eval `ssh-agent -s`
|
|
||||||
ssh-add deploy-key
|
|
||||||
|
|
||||||
# Now that we're all set up, we can push.
|
|
||||||
git push $SSH_REPO $TARGET_BRANCH
|
|
BIN
deploy-key.enc
63
docs/README.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<p align="center">
|
||||||
|
<img alt="clippy the assistant" src="https://i.imgur.com/pwCV6L8.png">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
Easily generate .docx files with JS/TS. Works for Node and on the Browser. :100:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Welcome
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install --save docx
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can `require` or `import` as usual:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let docx = require("docx");
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
import * as docx from "docx";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
var docx = require("docx");
|
||||||
|
|
||||||
|
// Create document
|
||||||
|
var doc = new docx.Document();
|
||||||
|
|
||||||
|
// Add some content in the document
|
||||||
|
var paragraph = new docx.Paragraph("Some cool text here.");
|
||||||
|
// Add more text into the paragraph if you wish
|
||||||
|
paragraph.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);
|
||||||
|
|
||||||
|
exporter.pack("My First Document");
|
||||||
|
|
||||||
|
// Done! A file called 'My First Document.docx' will be in your file system if you used LocalPacker
|
||||||
|
```
|
||||||
|
|
||||||
|
## Honoured Mentions
|
||||||
|
|
||||||
|
[@felipeochoa](https://github.com/felipeochoa)
|
||||||
|
|
||||||
|
[@h4buli](https://github.com/h4buli)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="clippy the assistant" src="http://i60.tinypic.com/339pvtt.png">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Made with 💖
|
28
docs/_sidebar.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
* [Getting Started](/)
|
||||||
|
|
||||||
|
* [Examples](examples.md)
|
||||||
|
|
||||||
|
* API
|
||||||
|
|
||||||
|
* [Documentation](https://docx.js.org/api/)
|
||||||
|
|
||||||
|
* Usage
|
||||||
|
|
||||||
|
* [Document](usage/document.md)
|
||||||
|
* [Paragraph](usage/paragraph.md)
|
||||||
|
* [Text](usage/text.md)
|
||||||
|
* [Image](usage/images.md)
|
||||||
|
* [Headers & Footers](usage/headers-and-footers.md)
|
||||||
|
* [Bullet Points](usage/bullet-points.md)
|
||||||
|
* [Numbering](usage/numbering.md)
|
||||||
|
* [Tab Stops](usage/tab-stops.md)
|
||||||
|
* [Table of Contents](usage/table-of-contents.md)
|
||||||
|
* Styling
|
||||||
|
* [Styling with JS](usage/styling-with-js.md)
|
||||||
|
* [Styling with XML](usage/styling-with-xml.md)
|
||||||
|
* Exporting
|
||||||
|
|
||||||
|
* [Packers](usage/packers.md)
|
||||||
|
|
||||||
|
* [Contribution Guidelines](contribution-guidelines.md)
|
||||||
|
|
213
docs/contribution-guidelines.md
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
# Contribution Guidelines
|
||||||
|
|
||||||
|
* Include documentation reference(s) at the top of each file:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// http://officeopenxml.com/WPdocument.php
|
||||||
|
```
|
||||||
|
|
||||||
|
* Follow Prettier standards, and consider using the [Prettier VSCode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) plugin.
|
||||||
|
|
||||||
|
* Follow the `TSLint` rules
|
||||||
|
|
||||||
|
## Always think about the user
|
||||||
|
|
||||||
|
The number one pillar for contribution is to **ALWAYS** think about how the user will use the library.
|
||||||
|
|
||||||
|
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 intuative?
|
||||||
|
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 accept primatives, 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, 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 construct the object, and inject it in
|
||||||
|
```js
|
||||||
|
public float(tableFloatProperties: TableFloatProperties): Table
|
||||||
|
```
|
||||||
|
|
||||||
|
**Do**
|
||||||
|
`ITableFloatOptions` is an interface for a JSON of primatives.
|
||||||
|
```js
|
||||||
|
public float(tableFloatOptions: ITableFloatOptions): Table
|
||||||
|
```
|
||||||
|
|
||||||
|
## Add vs Create
|
||||||
|
|
||||||
|
This is just a guideline, and the rules can sometimes be broken.
|
||||||
|
|
||||||
|
* Use `create` if the method `new`'s up an element inside:
|
||||||
|
|
||||||
|
```js
|
||||||
|
public createParagraph() {
|
||||||
|
const paragraph = new Paragraph();
|
||||||
|
this.root.push(paragraph);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Use `add` if you add the element into the method as a parameter:
|
||||||
|
|
||||||
|
```js
|
||||||
|
public addParagraph(paragraph: Paragraph) {
|
||||||
|
this.root.push(paragraph);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Getters and Setters
|
||||||
|
|
||||||
|
Getters and Setters are done with a capital letter like so:
|
||||||
|
|
||||||
|
```js
|
||||||
|
public get Level() {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
There is no performance advantage by doing this. It means we don't need to prefix all private variables with the ugly `_`:
|
||||||
|
|
||||||
|
**Do not:**
|
||||||
|
|
||||||
|
```js
|
||||||
|
private get _level: string;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Do**
|
||||||
|
|
||||||
|
```js
|
||||||
|
private get level: string;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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, 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`.
|
||||||
|
|
||||||
|
Here is a template of a test:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { assert } from "chai";
|
||||||
|
|
||||||
|
describe("ClassName", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#methodName()", () => {
|
||||||
|
it("should ", () => {
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Try not to use the `tests/utility.ts` file as this is being deprecated.
|
218
docs/examples.md
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
# Examples
|
||||||
|
|
||||||
|
> All examples can run independently and can be found in the `/demo` folder of the project
|
||||||
|
|
||||||
|
All the examples below can be ran locally, to do so, run the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run demo
|
||||||
|
```
|
||||||
|
|
||||||
|
This command will run the `demo selector app` in the `/demo` folder. It will prompt you to select a demo number, which will run a demo from that folder.
|
||||||
|
|
||||||
|
## Simple
|
||||||
|
|
||||||
|
A simple hello world of the `docx` library:
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo1.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo1.ts_
|
||||||
|
|
||||||
|
## Styles
|
||||||
|
|
||||||
|
### Styling with JS
|
||||||
|
|
||||||
|
This example shows how to customise the look and feel of a document using JS configuration
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo2.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo2.ts_
|
||||||
|
|
||||||
|
### Styling with XML
|
||||||
|
|
||||||
|
This example shows how to customise the look and feel of a document using XML configuration
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo13.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts_
|
||||||
|
|
||||||
|
## Numbering
|
||||||
|
|
||||||
|
This example shows many levels of numbering
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo3.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo3.ts_
|
||||||
|
|
||||||
|
## Table
|
||||||
|
|
||||||
|
Example of simple table
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
|
||||||
|
|
||||||
|
### Styling table borders
|
||||||
|
|
||||||
|
Styling the borders of a table
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
|
||||||
|
|
||||||
|
## Images
|
||||||
|
|
||||||
|
### Add image to the document
|
||||||
|
|
||||||
|
Importing Images from file system path
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
|
||||||
|
|
||||||
|
### Add images to header and footer
|
||||||
|
|
||||||
|
Example showing how to add image to headers and footers
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
|
||||||
|
|
||||||
|
### Scaling images
|
||||||
|
|
||||||
|
Example showing how to scale images
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo12.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo12.ts_
|
||||||
|
|
||||||
|
### Add Image to media before adding to document
|
||||||
|
|
||||||
|
This is the best way to add an image to a document because you can add the same image in two locations without increasing document size by re-using the same image
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo23.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo23.ts_
|
||||||
|
|
||||||
|
### Add image to table
|
||||||
|
|
||||||
|
As before, to add an image to a table, you would need to add it to the `Media` object first
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
|
||||||
|
|
||||||
|
### Images using Base64 URI
|
||||||
|
|
||||||
|
If you want to use a Base64 image instead
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo18.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo18.ts_
|
||||||
|
|
||||||
|
## Margins
|
||||||
|
|
||||||
|
Example showing how to set custom margains
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo6.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo6.ts_
|
||||||
|
|
||||||
|
## Orientation
|
||||||
|
|
||||||
|
Example showing how to set the document to `landscape` or `portrait`
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo7.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo7.ts_
|
||||||
|
|
||||||
|
## Headers & Footers
|
||||||
|
|
||||||
|
Example showing how to add headers and footers
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo8.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo8.ts_
|
||||||
|
|
||||||
|
## Multiple headers and footers
|
||||||
|
|
||||||
|
Check out `Sections` for this feature
|
||||||
|
|
||||||
|
## Page Breaks
|
||||||
|
|
||||||
|
### Normal page breaks
|
||||||
|
|
||||||
|
Example showing how to page break
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo14.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo14.ts_
|
||||||
|
|
||||||
|
### Page break before
|
||||||
|
|
||||||
|
Example showing how to page break before like in Word
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo15.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo15.ts_
|
||||||
|
|
||||||
|
## Sections
|
||||||
|
|
||||||
|
Example of how sections work. Sections allow multiple headers and footers, and `landscape`/`portrait` inside the same document
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo16.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo16.ts_
|
||||||
|
|
||||||
|
## Footnotes
|
||||||
|
|
||||||
|
Example of how to add footnotes. Good for references
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo17.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo17.ts_
|
||||||
|
|
||||||
|
## Packers
|
||||||
|
|
||||||
|
## Buffer output
|
||||||
|
|
||||||
|
Example showing how to use the Buffer packer and then write that buffer to the file system
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo19.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo19.ts_
|
||||||
|
|
||||||
|
|
||||||
|
## Bookmarks
|
||||||
|
|
||||||
|
Example showing how to make bookmarks to make internal hyperlinks within the document
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo21.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo21.ts_
|
||||||
|
|
||||||
|
## Bidirectional text
|
||||||
|
|
||||||
|
Example showing how to use bidirectional text for certain languages such as Hebrew
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo22.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo22.ts_
|
||||||
|
|
||||||
|
## Showcase
|
||||||
|
|
||||||
|
### My CV
|
||||||
|
|
||||||
|
Example showing how to add headers and footers
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo10.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo10.ts_
|
||||||
|
|
||||||
|
### Style and Images
|
||||||
|
|
||||||
|
This example shows how to customise the look and feel of a document and add images
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo11.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo11.ts_
|
30
docs/index.html
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>docx - Generate .docx documents with JavaScript</title>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
|
<meta name="description" content="Generate .docx documents with JavaScript">
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script>
|
||||||
|
window.$docsify = {
|
||||||
|
name: 'docx',
|
||||||
|
repo: 'https://github.com/dolanmiu/docx',
|
||||||
|
loadSidebar: true,
|
||||||
|
subMaxLevel: 2,
|
||||||
|
search: 'auto',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
|
||||||
|
<script src="//unpkg.com/docsify/lib/plugins/emoji.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/docsify-copy-code@2"></script>
|
||||||
|
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
21
docs/usage/bullet-points.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Bullet Points
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
To make a bullet point, simply make a paragraph into a bullet point:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var text = new docx.TextRun("Bullet points");
|
||||||
|
var paragraph = new docx.Paragraph(text).bullet();
|
||||||
|
|
||||||
|
var text2 = new docx.TextRun("Are awesome");
|
||||||
|
var paragraph2 = new docx.Paragraph(text2).bullet();
|
||||||
|
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
doc.addParagraph(paragraph2);
|
||||||
|
```
|
||||||
|
|
||||||
|
### This will produce:
|
||||||
|
|
||||||
|
* Bullet points
|
||||||
|
* Are awesome
|
35
docs/usage/document.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Document
|
||||||
|
|
||||||
|
> The `Document` object is the starting point of your `.docx` journey, this is the literal Word Document. You add all your content such as `Paragraphs` to this `Document`, and at the end export it however you like.
|
||||||
|
|
||||||
|
To create a new document, it is very easy:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var doc = new docx.Document();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Document properties
|
||||||
|
|
||||||
|
You can add properties to the Word document by specifying options, for example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var doc = new docx.Document({
|
||||||
|
creator: "Dolan Miu",
|
||||||
|
description: "My extremely interesting document",
|
||||||
|
title: "My Document",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Full list of options:
|
||||||
|
|
||||||
|
```
|
||||||
|
creator
|
||||||
|
description
|
||||||
|
title
|
||||||
|
subject
|
||||||
|
keywords
|
||||||
|
lastModifiedBy
|
||||||
|
revision
|
||||||
|
```
|
||||||
|
|
||||||
|
You can mix and match whatever properties you want, or provide no properties.
|
47
docs/usage/headers-and-footers.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Headers and Footers
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Creating Headers and footers is simple. Access the `Header` and `Footer` by doing so like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
doc.Header;
|
||||||
|
doc.Footer;
|
||||||
|
```
|
||||||
|
|
||||||
|
You can call the same methods as you would with a `File`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
doc.Header.createParagraph("Header text");
|
||||||
|
doc.Footer.createParagraph("Footer text");
|
||||||
|
```
|
||||||
|
|
||||||
|
Even add images:
|
||||||
|
|
||||||
|
```js
|
||||||
|
doc.Header.createImage([BUFFER_OF_YOUR_IMAGE]);
|
||||||
|
doc.Footer.createImage([BUFFER_OF_YOUR_IMAGE]);
|
||||||
|
```
|
||||||
|
|
||||||
|
Refer to `demo8.js` for more information
|
||||||
|
|
||||||
|
## Multiple Headers and Footers
|
||||||
|
|
||||||
|
Also all the supported section properties are implemented according to: http://officeopenxml.com/WPsection.php
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
const header = this.document.createHeader();
|
||||||
|
const footer = this.document.createFooter();
|
||||||
|
|
||||||
|
// Add new section with another header and footer
|
||||||
|
doc.addSection({
|
||||||
|
headerId: header.Header.ReferenceId,
|
||||||
|
footerId: footer.Footer.ReferenceId,
|
||||||
|
pageNumberStart: 1,
|
||||||
|
pageNumberFormatType: docx.PageNumberFormat.DECIMAL,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
231
docs/usage/images.md
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
# Images
|
||||||
|
|
||||||
|
To create a `floating` image on top of text:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
|
||||||
|
floating: {
|
||||||
|
horizontalPosition: {
|
||||||
|
offset: 1014400,
|
||||||
|
},
|
||||||
|
verticalPosition: {
|
||||||
|
offset: 1014400,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
By default with no arguments, its an `inline` image:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
doc.createImage(fs.readFileSync("./demo/images/parrots.bmp"));
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also create images manually and add them later:
|
||||||
|
|
||||||
|
```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_
|
96
docs/usage/numbering.md
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# Bullets and Numbering
|
||||||
|
|
||||||
|
`docx` is quite flexible in its bullets and numbering system, allowing
|
||||||
|
the user great freedom in how bullets and numbers are to be styled and
|
||||||
|
displayed. E.g., numbers can be shown using Arabic numerals, roman
|
||||||
|
numerals, or even ordinal words ("one", "two", "three", ...). The
|
||||||
|
format also supports re-using bullets/numbering styles throughout the
|
||||||
|
document, so that different lists using the same style need not
|
||||||
|
redefine them.
|
||||||
|
|
||||||
|
Because of this flexibility, bullets and numbering in DOCX involves a
|
||||||
|
couple of moving pieces:
|
||||||
|
|
||||||
|
1. Document-level bullets/numbering definitions (abstract)
|
||||||
|
2. Document-level bullets/numbering definitions (concrete)
|
||||||
|
3. Paragraph-level bullets/numbering selection
|
||||||
|
|
||||||
|
## Document-level bullets/numbering definitions (abstract)
|
||||||
|
|
||||||
|
Every document contains a set of abstract bullets/numbering
|
||||||
|
definitions which define the formatting and layout of paragraphs using
|
||||||
|
those bullets/numbering. An abstract numbering system defines how
|
||||||
|
bullets/numbers are to be shown for lists, including any sublists that
|
||||||
|
may be used. Thus each abstract definition includes a series of
|
||||||
|
_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
|
||||||
|
* **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
|
||||||
|
more.
|
||||||
|
* **levelText**: This is a format string using the output of the
|
||||||
|
`numberFormat` function and generating a string to insert before
|
||||||
|
every item in the list. You may use `%1`, `%2`, ... to reference the
|
||||||
|
numbers from each numbering level before this one. Thus a level
|
||||||
|
text of `%d)` with a number format of `lowerLetter` would result in
|
||||||
|
the sequence "a)", "b)", ...
|
||||||
|
* and a few others, which you can see in the OXML spec section 17.9.6
|
||||||
|
|
||||||
|
## Document-level bullets/numbering defintions (concrete)
|
||||||
|
|
||||||
|
Concrete definitions are sort of like concrete subclasses of the
|
||||||
|
abstract defintions. They indicate their parent and are allowed to
|
||||||
|
override certain level definitions. Thus two lists that differ only in
|
||||||
|
how sub-sub-lists are to be displayed can share the same abstract
|
||||||
|
numbering definition and have slightly different concrete definitions.
|
||||||
|
|
||||||
|
## Paragraph-level bullets/numbering selection
|
||||||
|
|
||||||
|
In order to use a bullets/numbering definition (which must be
|
||||||
|
concrete), paragraphs need to select it, similar to applying a CSS
|
||||||
|
class to an element, using both the concrete numbering definition ID
|
||||||
|
and the level number that the paragraph should be at. Additionally, MS
|
||||||
|
Word and LibreOffice typically apply a "ListParagraph" style to
|
||||||
|
paragraphs that are being numbered.
|
||||||
|
|
||||||
|
## Using bullets/numbering in `docx`
|
||||||
|
|
||||||
|
`docx` includes a pre-defined bullet style which you can add to your
|
||||||
|
paragraphs using `para.bullets()`. If you require different bullet
|
||||||
|
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:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const numbering = new docx.Numbering();
|
||||||
|
|
||||||
|
const abstractNum = numbering.createAbstractNumbering();
|
||||||
|
abstractNum.createLevel(0, "upperRoman", "%1", "start").addParagraphProperty(new Indent(720, 260));
|
||||||
|
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(abstractNum);
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then apply your concrete style to paragraphs using their
|
||||||
|
`#setNumbering` method:
|
||||||
|
|
||||||
|
```js
|
||||||
|
topLevelP.setNumbering(concrete, 0);
|
||||||
|
subP.setNumbering(concrete, 1);
|
||||||
|
subSubP.setNumbering(concrete, 2);
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, you need to let your exporter know about your numbering
|
||||||
|
styles when you're ready to render the document:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const packer = new Packer(doc, undefined, undefined, numbering);
|
||||||
|
packer.pack(myOutput);
|
||||||
|
```
|
117
docs/usage/packers.md
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
# Packers
|
||||||
|
|
||||||
|
> Packers are the way in which `docx` turns your code into `.docx` format. It is completely decoupled from the `docx.Document`.
|
||||||
|
|
||||||
|
## Version 4
|
||||||
|
|
||||||
|
Packers in `version 4` and above are now one single `Packer`. It works in both a node and browser environment (Angular etc). Now, the packer returns a `Buffer`, `Blob` or `base64 string`. It is up to you to take that and persist it with node's `fs`, send it down as a downloadable file, or anything else you wish. As of version 4, this library will not have options to export to PDF.
|
||||||
|
|
||||||
|
### Export as Buffer
|
||||||
|
|
||||||
|
This will return a NodeJS `Buffer`. If this is used in the browser, it will return a `UInt8Array` instead.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const packer = new docx.Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export as a `base64` string
|
||||||
|
|
||||||
|
```js
|
||||||
|
const packer = new docx.Packer();
|
||||||
|
|
||||||
|
packer.toBase64String(doc).then((string) => {
|
||||||
|
console.log(string);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export as Blob
|
||||||
|
|
||||||
|
This is useful if you want to send it as an downloadable in a browser environment.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const packer = new docx.Packer();
|
||||||
|
|
||||||
|
packer.toBlob(doc).then((blob) => {
|
||||||
|
// saveAs from FileSaver will download the file
|
||||||
|
saveAs(blob, "example.docx");
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Version 3 and below
|
||||||
|
|
||||||
|
### File System Packer
|
||||||
|
|
||||||
|
```js
|
||||||
|
const docx = require("docx");
|
||||||
|
|
||||||
|
const doc = new docx.Document();
|
||||||
|
const exporter = new docx.LocalPacker(doc);
|
||||||
|
exporter.pack("My Document");
|
||||||
|
// Word Document is in file system
|
||||||
|
```
|
||||||
|
|
||||||
|
### Buffer Packer
|
||||||
|
|
||||||
|
```js
|
||||||
|
const docx = require("docx");
|
||||||
|
|
||||||
|
const doc = new docx.Document();
|
||||||
|
const exporter = new docx.BufferPacker(doc);
|
||||||
|
const buffer = exporter.pack();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stream Packer
|
||||||
|
|
||||||
|
Creates a `node` `Readable` stream
|
||||||
|
|
||||||
|
```js
|
||||||
|
const docx = require("docx");
|
||||||
|
|
||||||
|
const doc = new docx.Document();
|
||||||
|
const exporter = new docx.StreamPacker(doc);
|
||||||
|
const stream = exporter.pack();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Express Packer
|
||||||
|
|
||||||
|
The old express packer is now deprecated and may disappear soon, so you should upgrade.
|
||||||
|
|
||||||
|
The reason for this is because it means this project needs to know about and use `express`, which for a Word document generator, does not sound right. Seperation of concerns.
|
||||||
|
|
||||||
|
It will still be usable (for now), but it is ill advised.
|
||||||
|
|
||||||
|
I used the express exporter in my [website](http://www.dolan.bio).
|
||||||
|
|
||||||
|
The recommended way is to use the `StreamPacker` and handle the `express` magic outside of the library:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const docx = require("docx");
|
||||||
|
|
||||||
|
const doc = new docx.Document();
|
||||||
|
const exporter = new docx.StreamPacker(doc);
|
||||||
|
|
||||||
|
const stream = exporter.pack();
|
||||||
|
|
||||||
|
// Express' response object
|
||||||
|
res.attachment("yourfile.xlsx");
|
||||||
|
stream.pipe(res);
|
||||||
|
```
|
||||||
|
|
||||||
|
where `res` is the response object obtained through the Express router. It is that simple. The file will begin downloading in the browser.
|
||||||
|
|
||||||
|
### PDF Exporting
|
||||||
|
|
||||||
|
You can export your word document as a PDF file like so:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const exporter = new docx.LocalPacker(doc);
|
||||||
|
exporter.packPdf("My Document");
|
||||||
|
|
||||||
|
// Express
|
||||||
|
const exporter = new docx.ExpressPacker(doc, res);
|
||||||
|
exporter.packPdf("My Document");
|
||||||
|
```
|
113
docs/usage/paragraph.md
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
# Paragraph
|
||||||
|
|
||||||
|
> Everything (text, images, graphs etc) in OpenXML is organised in paragraphs.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
You can add more text to the paragraph by doing this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var paragraph = new docx.Paragraph(),
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
var text = new docx.TextRun("Lorem Ipsum Foo Bar");
|
||||||
|
var paragraph = new docx.Paragraph();
|
||||||
|
paragraph.addRun(text);
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
var paragraph = new docx.Paragraph("Short hand notation for adding text.");
|
||||||
|
```
|
||||||
|
|
||||||
|
After you create the paragraph, you must add the paragraph into the `document`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Styles
|
||||||
|
|
||||||
|
To create styles, please refer to the styling Wiki: https://github.com/dolanmiu/docx/wiki/Styling
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Heading1 - Heading5
|
||||||
|
|
||||||
|
```js
|
||||||
|
paragraph.heading1();
|
||||||
|
paragraph.heading2();
|
||||||
|
paragraph.heading3();
|
||||||
|
paragraph.heading4();
|
||||||
|
paragraph.heading5();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Title
|
||||||
|
|
||||||
|
```js
|
||||||
|
paragraph.title();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Text Alignment
|
||||||
|
|
||||||
|
To change the text alignment of a paragraph, for center, left, right or justified:
|
||||||
|
|
||||||
|
```js
|
||||||
|
paragraph.center();
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
paragraph.left();
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
paragraph.right();
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
paragraph.justified();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
paragraph.heading1().center();
|
||||||
|
```
|
||||||
|
|
||||||
|
The above will create a `heading 1` which is `centered`.
|
||||||
|
|
||||||
|
## Thematic Break
|
||||||
|
|
||||||
|
To add a break in the page, simply add `.thematicBreak()` on a paragraph:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var paragraph = new docx.Paragraph("Amazing Heading").heading1().thematicBreak();
|
||||||
|
```
|
||||||
|
|
||||||
|
The above example will create a heading with a page break directly under it.
|
||||||
|
|
||||||
|
## Page Break
|
||||||
|
|
||||||
|
To move to a new page (insert a page break), simply add `.pageBreak()` on a paragraph:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var paragraph = new docx.Paragraph("Amazing Heading").heading1().pageBreak();
|
||||||
|
```
|
||||||
|
|
||||||
|
The above example will create a heading and start a new page immediately afterwards.
|
||||||
|
|
||||||
|
### Page break before:
|
||||||
|
|
||||||
|
This option (available in word) will make sure that the paragraph will start on a new page (if it's not already on a new page).
|
||||||
|
|
||||||
|
```js
|
||||||
|
var paragraph = new docx.Paragraph("Hello World on another page").pageBreakBefore();
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Example: https://github.com/dolanmiu/docx/blob/master/demo/demo15.js
|
||||||
|
|
||||||
|
## Page break control
|
||||||
|
|
||||||
|
Paragraphs have `.keepLines()` and `.keepNext()` methods that allow restricting page breaks within and between paragraphs. See [this Microsoft article](https://support.office.com/en-us/article/Keep-lines-and-paragraphs-together-d72af534-926f-4c4b-830a-abfc2daa3bfa) for more details)
|
163
docs/usage/styling-with-js.md
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
# Styling with JS
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
const para = new Paragraph("To whom it may concern:").heading2().center();
|
||||||
|
|
||||||
|
const name = new TextRun("Name:")
|
||||||
|
.bold()
|
||||||
|
.font("Calibri")
|
||||||
|
.allCaps();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available methods
|
||||||
|
|
||||||
|
* For run formatting:
|
||||||
|
* `.bold()`, `.italic()`, `.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
|
||||||
|
* `.font(name)`: Set the run's font
|
||||||
|
* `.style(name)`: Apply a named run style
|
||||||
|
* `.characterSpacing(value)`: Set the character spacing adjustment (in TWIPs)
|
||||||
|
* For paragraph formatting:
|
||||||
|
* `.heading1()`, `.heading2()`, `.heading3()`, `.heading4()`, `.heading5()`, `.title()`: apply the appropriate style to the paragraph
|
||||||
|
* `.left()`, `.center()`, `.right()`, `.justified()`: set the paragraph's alignment
|
||||||
|
* `.thematicBreak()`, `.pageBreak()`: Insert a thick rule or a page break beneath the paragraph
|
||||||
|
* `.leftTabStop(position)`: Add a left tab stop (measured in TWIPs from the left)
|
||||||
|
* `.maxRightTabStop()`: Add a right tab stop at the far right
|
||||||
|
* `.bullet()`: Use the default bullet style
|
||||||
|
* `.setNumbering(numbering, indentLevel)`: Use a custom numbering format for the paragraph
|
||||||
|
* `.style(name)`: Apply a named paragraph style
|
||||||
|
* `.indent(start, hanging=0)`: Set the paragraph's indent level (in TWIPs)
|
||||||
|
* `.spacing({before=0, after=0, line=0})`: Set the line and before/after on the paragraph. Before/after is measured in TWIPs, line is measured in 240ths of a line
|
||||||
|
|
||||||
|
Paragraph styles have all the run formatting methods, except `style()`, and `.left()`, `.center()`, `.right()`, `.justified()`, `.thematicBreak()`, `.leftTabStop(position)`, `.maxRightTabStop()`, `.indent(start, hanging=0)`, and `.spacing({before=0, after=0, line=0})` methods.
|
||||||
|
|
||||||
|
## Detailed guide
|
||||||
|
|
||||||
|
There are 4 items in DOCX that can be styled:
|
||||||
|
|
||||||
|
* Characters: Attributes that can change within a paragraph. e.g., bold, italics, etc.
|
||||||
|
* Paragraphs: Attributes like indent, text alignment, line spacing, etc.
|
||||||
|
* Tables: Border styles, table formats, etc.
|
||||||
|
* List items: These are the numbers and bullets that are automatically inserted
|
||||||
|
|
||||||
|
There are a few different ways of styling this content in DOCX, which somewhat resemble the HTML/CSS approach. In order of greatest to lowest priority:
|
||||||
|
|
||||||
|
1. Direct formatting (AKA inline formatting)
|
||||||
|
2. Centrally defined styles (similar to external CSS)
|
||||||
|
3. Document defaults (similar to a `*` rule in CSS)
|
||||||
|
|
||||||
|
Unlike CSS, less specific rules don't _necessarily_ override parent rules. The rules are a bit wonky, but if you're interested, see the [advanced formatting section](#Advanced formatting).
|
||||||
|
|
||||||
|
### Direct formatting (AKA inline formatting)
|
||||||
|
|
||||||
|
This is the type of formatting that your uncle uses when he types out documents: _N ... a ... m ... e ... :_ Then he grabs the mouse, highlights _Name:_ and moves over to the **B** for bold. This manner of formatting results in markup that is similar to writing `<span style="bold: true">Name:</span>` if you were typing out HTML. DOCX (the format) allows you to specify this for any of the four types of items. `docx` (the library) only supports this type of formatting for paragraphs and characters, using a _fluent_ api. Thus you could do:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const name = new TextRun("Name:")
|
||||||
|
.bold()
|
||||||
|
.font("Calibri")
|
||||||
|
.allCaps();
|
||||||
|
```
|
||||||
|
|
||||||
|
Or for paragraph formatting:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const para = new Paragraph("To whom it may concern:").heading2().center();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Centrally defined styles (similar to external CSS)
|
||||||
|
|
||||||
|
DOCX files contain a styles section separate from the main content, much like how HTML includes CSS files. Unlike CSS, DOCX distinguishes between styles meant for tables (which show up in the table formatting toolbar), styles for lists (which show up under bullets and numbering), and styles for runs and paragraphs, which show up as dropdowns offering standard styles, like "Heading 1", "Caption", or any custom styles defined in that document. <!-- TODO: add pictures of the panes -->. `docx` allows you to define these styles using a fluent interface as well.
|
||||||
|
|
||||||
|
There are three parts to using custom styles with `docx`:
|
||||||
|
|
||||||
|
1. Create a container object for the style definitions:
|
||||||
|
```js
|
||||||
|
const myStyles = new docx.Styles();
|
||||||
|
```
|
||||||
|
2. Define your custom styles, similar to the way you would format a paragraph or run
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 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
|
||||||
|
myStyles
|
||||||
|
.createParagraphStyle("myWonkyStyle", "My Wonky Style")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.color("999999")
|
||||||
|
.italics()
|
||||||
|
.indent(720) // 720 TWIP === 720 / 20 pt === .5 in
|
||||||
|
.spacing({ line: 276 }); // 276 / 240 = 1.15x line spacing
|
||||||
|
|
||||||
|
myStyles
|
||||||
|
.createParagraphStyle("Heading2", "Heading 2")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.quickFormat()
|
||||||
|
.size(26) // 26 half-points === 13pt font
|
||||||
|
.bold()
|
||||||
|
.underline("double", "FF0000")
|
||||||
|
.spacing({ before: 240, after: 120 }); // TWIP for both
|
||||||
|
```
|
||||||
|
|
||||||
|
3. When you generate your document, make sure to pass the `styles` container to the `Packer`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const packer = new Packer(doc, myStyles);
|
||||||
|
packer.pack(myOutStream);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: If you are using the `.headingX` or `.title` methods of paragraphs, you must make sure to define `HeadingX` or `Title` styles for these. Otherwise they'll show up unstyled :(. If you are using the `.bullet` or `.setNumbering` methods, you need to define a `ListParagraph` style or the numbers may not show up.
|
||||||
|
|
||||||
|
### Document defaults
|
||||||
|
|
||||||
|
Setting document defaults acts like a `*` rule in CSS: it applies to every paragraph and run in the document, but at a low priority level. Other styles affecting this property will override these defaults.
|
||||||
|
|
||||||
|
## Advanced formatting
|
||||||
|
|
||||||
|
### Style inheritance
|
||||||
|
|
||||||
|
Styles may define a `basedOn` attribute that references another style of the same type. In this case, any unspecified properties are inherited from the parent style.
|
||||||
|
|
||||||
|
### Interactions between the 4 items
|
||||||
|
|
||||||
|
In addition to the 3-layer hierarchy spelled above, there is some interaction between the 4 items that you can style.
|
||||||
|
For instance numbering styles may also specify some styling for paragraphs (typically indentation and tab stops); paragraphs may specify character formatting (e.g., heading font sizes); etc.
|
||||||
|
|
||||||
|
The elements that each style may affect are summarized in the table below. So, e.g., table styles may specify table formatting, paragraph formatting, and character formatting.
|
||||||
|
|
||||||
|
| Style type | Table | Paragraph | List item | Characters |
|
||||||
|
| :---------------- | :---: | :-------: | :-------: | :--------: |
|
||||||
|
| Document defaults | | X | | X |
|
||||||
|
| Table | X | X | | X |
|
||||||
|
| Paragraph | | X | X | X |
|
||||||
|
| Numbering | | X | X | |
|
||||||
|
| Character | | | | X |
|
||||||
|
| Direct formatting | X | X | X | X |
|
||||||
|
|
||||||
|
To determine the value of a styling property, you must first identify whether it's a table, paragraph, list item, or character property. E.g., numbering definition is a list item property. Then you need to find the last row in the table for which that property has an "X" and the document has formatting of that type. So if a particular run was in a paragraph whose style specified color as `FF0000`, but it also had a character style specifying color as `00DD00`, then the character style (lower down on the table) would trump, and the character would have color `00DD00`.
|
||||||
|
|
||||||
|
### Toggle properties
|
||||||
|
|
||||||
|
The following properties are treated in a special manner; they're called toggle properties:
|
||||||
|
|
||||||
|
* Bold
|
||||||
|
* All caps
|
||||||
|
* Small caps
|
||||||
|
* Italics
|
||||||
|
* Single strike-through
|
||||||
|
* Hidden
|
||||||
|
* Imprint
|
||||||
|
* Emboss
|
||||||
|
* Character outline
|
||||||
|
* Character shadow
|
||||||
|
|
||||||
|
For these properties, the rules state the following conflict resolution in case the property is specified at multiple points for the same item:
|
||||||
|
|
||||||
|
* Direct formatting trumps all if specified (either true or false)
|
||||||
|
* Otherwise, if the property is true in document defaults, the property is set to true
|
||||||
|
* Otherwise, the property's value is an XOR of its effective table, paragraph, and character values. (So specifying bold `true` on a table style and a paragraph style would result in non-bold text if a paragraph inside the table had that style)
|
47
docs/usage/styling-with-xml.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Styling with XML
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Create a new word document in Microsoft Word
|
||||||
|
2. Customise the styles on the Ribbon Bar.
|
||||||
|
For example, modify the `Normal`, `Heading 1`, `Heading 2` like so:
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
3. You can even create a totally new `Style`:
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
4. Save
|
||||||
|
5. Re-name the saved `.docx` file to `.zip` and un-zip
|
||||||
|
6. Find `styles.xml`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Read the styles using `fs`, and put it into the `Document` object in the constructor:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const styles = fs.readFileSync("./styles.xml", "utf-8");
|
||||||
|
const doc = new docx.Document({
|
||||||
|
title: "Title",
|
||||||
|
externalStyles: styles,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
You can use paragraphs, `heading1()`, `heading2()` etc and it will be styled according to your `styles.xml` created earlier. You can even use your new style you made by calling the `style` method:
|
||||||
|
|
||||||
|
```js
|
||||||
|
doc.createParagraph("Cool Heading Text").heading1();
|
||||||
|
|
||||||
|
let paragraph = new docx.Paragraph('This is a custom named style from the template "Cool New Style"');
|
||||||
|
paragraph.style("Cool New Style");
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
doc.createParagraph("Some normal text");
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: https://github.com/dolanmiu/docx/blob/master/demo/demo13.js
|
54
docs/usage/tab-stops.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Tab Stops
|
||||||
|
|
||||||
|
> 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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Simply call the relevant methods on the paragraph listed below. Then just add a `tab()` method call to a text object. Adding multiple `tabStops` will mean you would have to chain `tab()` until the desired `tabStop` is selected. Example is shown below.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
var paragraph = new docx.Paragraph().maxRightTabStop();
|
||||||
|
var leftText = new docx.TextRun("Hey everyone").bold();
|
||||||
|
var rightText = new docx.TextRun("11th November 2015").tab();
|
||||||
|
paragraph.addRun(leftText);
|
||||||
|
paragraph.addRun(rightText);
|
||||||
|
```
|
||||||
|
The example above will create a left aligned text, and a right aligned text on the same line. The laymans approach to this problem would be to either use text boxes or tables. YUK!
|
||||||
|
|
||||||
|
```js
|
||||||
|
var paragraph = new docx.Paragraph();
|
||||||
|
paragraph.maxRightTabStop();
|
||||||
|
paragraph.leftTabStop(1000);
|
||||||
|
var text = new docx.TextRun("Second tab stop here I come!").tab().tab();
|
||||||
|
paragraph.addRun(text);
|
||||||
|
```
|
||||||
|
|
||||||
|
The above shows the use of two tab stops, and how to select/use it.
|
||||||
|
|
||||||
|
## Left Tab Stop
|
||||||
|
```js
|
||||||
|
paragraph.leftTabStop(2268);
|
||||||
|
```
|
||||||
|
2268 is the distance from the left side.
|
||||||
|
|
||||||
|
## Center Tab Stop
|
||||||
|
```js
|
||||||
|
paragraph.centerTabStop(2268);
|
||||||
|
```
|
||||||
|
2268 is the distance from the left side.
|
||||||
|
|
||||||
|
## Right Tab Stop
|
||||||
|
```js
|
||||||
|
paragraph.rightTabStop(2268);
|
||||||
|
```
|
||||||
|
2268 is the distance from the left side.
|
||||||
|
|
||||||
|
## Max Right Tab Stop
|
||||||
|
```js
|
||||||
|
paragraph.maxRightTabStop();
|
||||||
|
```
|
||||||
|
This will create a tab stop on the very edge of the right hand side. Handy for right aligning and left aligning text on the same line.
|
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_
|
84
docs/usage/text.md
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# Text
|
||||||
|
|
||||||
|
Paragraphs need `text run` objects. To create text:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var text = new docx.TextRun("My awesome text here for my university dissertation");
|
||||||
|
paragraph.addRun(text);
|
||||||
|
```
|
||||||
|
|
||||||
|
Text objects have methods inside which changes the way the text is displayed.
|
||||||
|
|
||||||
|
## Typographical Emphasis
|
||||||
|
|
||||||
|
More info [here](https://english.stackexchange.com/questions/97081/what-is-the-typography-term-which-refers-to-the-usage-of-bold-italics-and-unde)
|
||||||
|
|
||||||
|
### Bold
|
||||||
|
|
||||||
|
```js
|
||||||
|
text.bold();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Italics
|
||||||
|
|
||||||
|
```js
|
||||||
|
text.italic();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Underline
|
||||||
|
|
||||||
|
```js
|
||||||
|
text.underline();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Strike through
|
||||||
|
|
||||||
|
```js
|
||||||
|
text.strike();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Double strike through
|
||||||
|
|
||||||
|
```js
|
||||||
|
text.doubleStrike();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Superscript
|
||||||
|
|
||||||
|
```js
|
||||||
|
text.superScript();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Subscript
|
||||||
|
|
||||||
|
```js
|
||||||
|
text.subScript();
|
||||||
|
```
|
||||||
|
|
||||||
|
### All Capitals
|
||||||
|
|
||||||
|
```js
|
||||||
|
text.allCaps();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Small Capitals
|
||||||
|
|
||||||
|
```js
|
||||||
|
text.smallCaps();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Break
|
||||||
|
|
||||||
|
Sometimes you would want to put text underneath another line of text but inside the same paragraph.
|
||||||
|
|
||||||
|
```js
|
||||||
|
text.break();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Chaining
|
||||||
|
|
||||||
|
What if you want to create a paragraph which is **_bold_** and **_italic_**?
|
||||||
|
|
||||||
|
```js
|
||||||
|
paragraph.bold().italic();
|
||||||
|
```
|
BIN
logo/logo-small.gif
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
logo/logo-small.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
logo/logo-small.psd
Normal file
BIN
logo/logo.psd
Normal file
64
package.json
@ -1,19 +1,31 @@
|
|||||||
{
|
{
|
||||||
"name": "docx",
|
"name": "docx",
|
||||||
"version": "2.1.3",
|
"version": "4.6.0",
|
||||||
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
|
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
|
||||||
"main": "build/index.js",
|
"main": "build/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"pretest": "rimraf ./build-tests && tsc -p ts/test-tsconfig.json",
|
"pretest": "rimraf ./build",
|
||||||
"test": "mocha ./build-tests --recursive",
|
"test": "mocha-webpack \"src/**/*.ts\"",
|
||||||
|
"test.coverage": "nyc npm test",
|
||||||
|
"test.watch": "npm test -- --watch",
|
||||||
"prepublishOnly": "npm run build",
|
"prepublishOnly": "npm run build",
|
||||||
"lint": "tslint --project ./ts",
|
"lint": "tslint --project .",
|
||||||
"build": "rimraf ./build && tsc -p ts",
|
"build": "npm run webpack && npm run fix-types",
|
||||||
"demo": "npm run build && node ./demo",
|
"tsc": "rimraf ./build && tsc -p .",
|
||||||
"typedoc": "typedoc --out docs/ ts/ --module commonjs --target ES6 --disableOutputCheck"
|
"webpack": "rimraf ./build && webpack",
|
||||||
|
"demo": "npm run build && npm run ts-node ./demo",
|
||||||
|
"typedoc": "typedoc src/index.ts",
|
||||||
|
"style": "prettier -l \"src/**/*.ts\"",
|
||||||
|
"style.fix": "npm run style -- --write",
|
||||||
|
"fix-types": "node types-absolute-fixer.js",
|
||||||
|
"ts-node": "ts-node"
|
||||||
},
|
},
|
||||||
|
"pre-commit": [
|
||||||
|
"style",
|
||||||
|
"lint"
|
||||||
|
],
|
||||||
"files": [
|
"files": [
|
||||||
"ts",
|
"src",
|
||||||
"build",
|
"build",
|
||||||
"template"
|
"template"
|
||||||
],
|
],
|
||||||
@ -22,6 +34,7 @@
|
|||||||
"url": "git+https://github.com/dolanmiu/docx.git"
|
"url": "git+https://github.com/dolanmiu/docx.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
"docx",
|
||||||
"office",
|
"office",
|
||||||
"word",
|
"word",
|
||||||
"generate",
|
"generate",
|
||||||
@ -34,10 +47,12 @@
|
|||||||
],
|
],
|
||||||
"types": "./build/index.d.ts",
|
"types": "./build/index.d.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/archiver": "^1.3.4",
|
"@types/image-size": "0.0.29",
|
||||||
"@types/express": "^4.0.35",
|
"@types/jszip": "^3.1.4",
|
||||||
"archiver": "^1.3.0",
|
"image-size": "^0.6.2",
|
||||||
"xml": "^1.0.1"
|
"jszip": "^3.1.5",
|
||||||
|
"xml": "^1.0.1",
|
||||||
|
"xml-js": "^1.6.8"
|
||||||
},
|
},
|
||||||
"author": "Dolan Miu",
|
"author": "Dolan Miu",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -48,13 +63,30 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^3.4.35",
|
"@types/chai": "^3.4.35",
|
||||||
"@types/mocha": "^2.2.39",
|
"@types/mocha": "^2.2.39",
|
||||||
|
"@types/sinon": "^4.3.1",
|
||||||
|
"awesome-typescript-loader": "^3.4.1",
|
||||||
"chai": "^3.5.0",
|
"chai": "^3.5.0",
|
||||||
"mocha": "^3.2.0",
|
"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",
|
||||||
"prompt": "^1.0.0",
|
"prompt": "^1.0.0",
|
||||||
|
"replace-in-file": "^3.1.0",
|
||||||
"rimraf": "^2.5.2",
|
"rimraf": "^2.5.2",
|
||||||
"shelljs": "^0.7.7",
|
"shelljs": "^0.7.7",
|
||||||
"tslint": "^5.1.0",
|
"sinon": "^5.0.7",
|
||||||
"typedoc": "^0.5.10",
|
"ts-node": "^7.0.1",
|
||||||
"typescript": "2.4.1"
|
"tslint": "^5.11.0",
|
||||||
|
"tslint-immutable": "^4.9.0",
|
||||||
|
"typedoc": "^0.11.1",
|
||||||
|
"typescript": "2.9.2",
|
||||||
|
"webpack": "^3.10.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { assert } from "chai";
|
import { assert } from "chai";
|
||||||
|
|
||||||
import * as docx from "../../docx";
|
import { Formatter } from "../export/formatter";
|
||||||
import { Attributes } from "../../docx/xml-components";
|
import * as file from "../file";
|
||||||
import { Formatter } from "../../export/formatter";
|
import { CoreProperties } from "../file/core-properties";
|
||||||
import { Properties } from "../../properties";
|
import { Attributes } from "../file/xml-components";
|
||||||
import { Utility } from "../utility";
|
import { Utility } from "../tests/utility";
|
||||||
|
|
||||||
describe("Formatter", () => {
|
describe("Formatter", () => {
|
||||||
let formatter: Formatter;
|
let formatter: Formatter;
|
||||||
@ -15,21 +15,21 @@ describe("Formatter", () => {
|
|||||||
|
|
||||||
describe("#format()", () => {
|
describe("#format()", () => {
|
||||||
it("should format simple paragraph", () => {
|
it("should format simple paragraph", () => {
|
||||||
const paragraph = new docx.Paragraph();
|
const paragraph = new file.Paragraph();
|
||||||
const newJson = formatter.format(paragraph);
|
const newJson = formatter.format(paragraph);
|
||||||
assert.isDefined(newJson["w:p"]);
|
assert.isDefined(newJson["w:p"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should remove xmlKeys", () => {
|
it("should remove xmlKeys", () => {
|
||||||
const paragraph = new docx.Paragraph();
|
const paragraph = new file.Paragraph();
|
||||||
const newJson = formatter.format(paragraph);
|
const newJson = formatter.format(paragraph);
|
||||||
const stringifiedJson = JSON.stringify(newJson);
|
const stringifiedJson = JSON.stringify(newJson);
|
||||||
assert(stringifiedJson.indexOf("xmlKeys") < 0);
|
assert(stringifiedJson.indexOf("xmlKeys") < 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should format simple paragraph with bold text", () => {
|
it("should format simple paragraph with bold text", () => {
|
||||||
const paragraph = new docx.Paragraph();
|
const paragraph = new file.Paragraph();
|
||||||
paragraph.addRun(new docx.TextRun("test").bold());
|
paragraph.addRun(new file.TextRun("test").bold());
|
||||||
const newJson = formatter.format(paragraph);
|
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"][1]["w:r"][0]["w:rPr"][0]["w:b"][0]._attr["w:val"]);
|
||||||
});
|
});
|
||||||
@ -61,13 +61,13 @@ describe("Formatter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should should change 'p' tag into 'w:p' tag", () => {
|
it("should should change 'p' tag into 'w:p' tag", () => {
|
||||||
const paragraph = new docx.Paragraph();
|
const paragraph = new file.Paragraph();
|
||||||
const newJson = formatter.format(paragraph);
|
const newJson = formatter.format(paragraph);
|
||||||
assert.isDefined(newJson["w:p"]);
|
assert.isDefined(newJson["w:p"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should format Properties object correctly", () => {
|
it("should format Properties object correctly", () => {
|
||||||
const properties = new Properties({
|
const properties = new CoreProperties({
|
||||||
title: "test document",
|
title: "test document",
|
||||||
creator: "Dolan",
|
creator: "Dolan",
|
||||||
});
|
});
|
13
src/export/formatter.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { BaseXmlComponent, IXmlableObject } from "file/xml-components";
|
||||||
|
|
||||||
|
export class Formatter {
|
||||||
|
public format(input: BaseXmlComponent): IXmlableObject {
|
||||||
|
const output = input.prepForXml();
|
||||||
|
|
||||||
|
if (output) {
|
||||||
|
return output;
|
||||||
|
} else {
|
||||||
|
throw Error("XMLComponent did not format correctly");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
src/export/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./packer/packer";
|
69
src/export/packer/next-compiler.spec.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/* tslint:disable:typedef space-before-function-paren */
|
||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { File } from "file";
|
||||||
|
|
||||||
|
import { Compiler } from "./next-compiler";
|
||||||
|
|
||||||
|
describe("Compiler", () => {
|
||||||
|
let compiler: Compiler;
|
||||||
|
let file: File;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
file = new File();
|
||||||
|
compiler = new Compiler();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#compile()", () => {
|
||||||
|
it("should pack all the content", async function() {
|
||||||
|
this.timeout(99999999);
|
||||||
|
const zipFile = await 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(18);
|
||||||
|
expect(fileNames).to.include("word/document.xml");
|
||||||
|
expect(fileNames).to.include("word/styles.xml");
|
||||||
|
expect(fileNames).to.include("docProps/core.xml");
|
||||||
|
expect(fileNames).to.include("docProps/app.xml");
|
||||||
|
expect(fileNames).to.include("word/numbering.xml");
|
||||||
|
expect(fileNames).to.include("word/header1.xml");
|
||||||
|
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");
|
||||||
|
expect(fileNames).to.include("_rels/.rels");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should pack all additional headers and footers", async function() {
|
||||||
|
file.createFooter();
|
||||||
|
file.createFooter();
|
||||||
|
file.createHeader();
|
||||||
|
file.createHeader();
|
||||||
|
|
||||||
|
this.timeout(99999999);
|
||||||
|
|
||||||
|
const zipFile = await 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(26);
|
||||||
|
|
||||||
|
expect(fileNames).to.include("word/header1.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
||||||
|
expect(fileNames).to.include("word/header2.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/header2.xml.rels");
|
||||||
|
expect(fileNames).to.include("word/header3.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/header3.xml.rels");
|
||||||
|
expect(fileNames).to.include("word/footer1.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/footer1.xml.rels");
|
||||||
|
expect(fileNames).to.include("word/footer2.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/footer2.xml.rels");
|
||||||
|
expect(fileNames).to.include("word/footer3.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/footer3.xml.rels");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
152
src/export/packer/next-compiler.ts
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import * as JSZip from "jszip";
|
||||||
|
import * as xml from "xml";
|
||||||
|
|
||||||
|
import { File } from "file";
|
||||||
|
import { Formatter } from "../formatter";
|
||||||
|
|
||||||
|
interface IXmlifyedFile {
|
||||||
|
readonly data: string;
|
||||||
|
readonly path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IXmlifyedFileMapping {
|
||||||
|
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;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.formatter = new Formatter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async compile(file: File): Promise<JSZip> {
|
||||||
|
const zip = new JSZip();
|
||||||
|
|
||||||
|
const xmlifiedFileMapping = this.xmlifyFile(file);
|
||||||
|
|
||||||
|
for (const key in xmlifiedFileMapping) {
|
||||||
|
if (!xmlifiedFileMapping[key]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = xmlifiedFileMapping[key] as IXmlifyedFile | IXmlifyedFile[];
|
||||||
|
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
for (const subFile of obj) {
|
||||||
|
zip.file(subFile.path, subFile.data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
zip.file(obj.path, obj.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const data of file.Media.Array) {
|
||||||
|
const mediaData = data.stream;
|
||||||
|
zip.file(`word/media/${data.fileName}`, mediaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const header of file.Headers) {
|
||||||
|
for (const data of header.Media.Array) {
|
||||||
|
zip.file(`word/media/${data.fileName}`, data.stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const footer of file.Footers) {
|
||||||
|
for (const data of footer.Media.Array) {
|
||||||
|
zip.file(`word/media/${data.fileName}`, data.stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return zip;
|
||||||
|
}
|
||||||
|
|
||||||
|
private xmlifyFile(file: File): IXmlifyedFileMapping {
|
||||||
|
file.verifyUpdateFields();
|
||||||
|
return {
|
||||||
|
Document: {
|
||||||
|
data: xml(this.formatter.format(file.Document), true),
|
||||||
|
path: "word/document.xml",
|
||||||
|
},
|
||||||
|
Styles: {
|
||||||
|
data: xml(this.formatter.format(file.Styles)),
|
||||||
|
path: "word/styles.xml",
|
||||||
|
},
|
||||||
|
Properties: {
|
||||||
|
data: xml(this.formatter.format(file.CoreProperties), {
|
||||||
|
declaration: {
|
||||||
|
standalone: "yes",
|
||||||
|
encoding: "UTF-8",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
path: "docProps/core.xml",
|
||||||
|
},
|
||||||
|
Numbering: {
|
||||||
|
data: xml(this.formatter.format(file.Numbering)),
|
||||||
|
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)),
|
||||||
|
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)),
|
||||||
|
path: `word/_rels/header${index + 1}.xml.rels`,
|
||||||
|
})),
|
||||||
|
FooterRelationships: file.Footers.map((footerWrapper, index) => ({
|
||||||
|
data: xml(this.formatter.format(footerWrapper.Relationships)),
|
||||||
|
path: `word/_rels/footer${index + 1}.xml.rels`,
|
||||||
|
})),
|
||||||
|
ContentTypes: {
|
||||||
|
data: xml(this.formatter.format(file.ContentTypes)),
|
||||||
|
path: "[Content_Types].xml",
|
||||||
|
},
|
||||||
|
AppProperties: {
|
||||||
|
data: xml(this.formatter.format(file.AppProperties)),
|
||||||
|
path: "docProps/app.xml",
|
||||||
|
},
|
||||||
|
FootNotes: {
|
||||||
|
data: xml(this.formatter.format(file.FootNotes)),
|
||||||
|
path: "word/footnotes.xml",
|
||||||
|
},
|
||||||
|
Settings: {
|
||||||
|
data: xml(this.formatter.format(file.Settings)),
|
||||||
|
path: "word/settings.xml",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* By default docx collapse empty tags. <a></a> -> <a/>. this function mimic it
|
||||||
|
so comparing (diff) original docx file and the library output is easier
|
||||||
|
Currently not used, so commenting out */
|
||||||
|
// private collapseEmptyTags(xmlData: string): string {
|
||||||
|
// const regEx = /<(([^ <>]+)[^<>]*)><\/\2>/g;
|
||||||
|
// const collapsed = xmlData.replace(regEx, "<$1/>");
|
||||||
|
// return collapsed;
|
||||||
|
// }
|
||||||
|
}
|
49
src/export/packer/packer.spec.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/* tslint:disable:typedef space-before-function-paren */
|
||||||
|
import { assert } from "chai";
|
||||||
|
import { stub } from "sinon";
|
||||||
|
|
||||||
|
import { File, Paragraph } from "file";
|
||||||
|
|
||||||
|
import { Packer } from "./packer";
|
||||||
|
|
||||||
|
describe("Packer", () => {
|
||||||
|
let packer: Packer;
|
||||||
|
let file: File;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
file = new File({
|
||||||
|
creator: "Dolan Miu",
|
||||||
|
revision: "1",
|
||||||
|
lastModifiedBy: "Dolan Miu",
|
||||||
|
});
|
||||||
|
const paragraph = new Paragraph("test text");
|
||||||
|
const heading = new Paragraph("Hello world").heading1();
|
||||||
|
|
||||||
|
file.addParagraph(new Paragraph("title").title());
|
||||||
|
file.addParagraph(heading);
|
||||||
|
file.addParagraph(new Paragraph("heading 2").heading2());
|
||||||
|
file.addParagraph(paragraph);
|
||||||
|
|
||||||
|
packer = new Packer();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#toBuffer()", () => {
|
||||||
|
it("should create a standard docx file", async function() {
|
||||||
|
this.timeout(99999999);
|
||||||
|
const buffer = await packer.toBuffer(file);
|
||||||
|
|
||||||
|
assert.isDefined(buffer);
|
||||||
|
assert.isTrue(buffer.byteLength > 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.toBuffer(file).catch((error) => {
|
||||||
|
assert.isDefined(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
40
src/export/packer/packer.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { File } from "file";
|
||||||
|
import { Compiler } from "./next-compiler";
|
||||||
|
|
||||||
|
export class Packer {
|
||||||
|
private readonly compiler: Compiler;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.compiler = new Compiler();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async toBuffer(file: File): Promise<Buffer> {
|
||||||
|
const zip = await 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",
|
||||||
|
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",
|
||||||
|
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
})) as Blob;
|
||||||
|
|
||||||
|
return zipData;
|
||||||
|
}
|
||||||
|
}
|
13
src/file/app-properties/app-properties-attributes.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export interface IAppPropertiesAttributes {
|
||||||
|
readonly xmlns: string;
|
||||||
|
readonly vt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AppPropertiesAttributes extends XmlAttributeComponent<IAppPropertiesAttributes> {
|
||||||
|
protected readonly xmlKeys = {
|
||||||
|
xmlns: "xmlns",
|
||||||
|
vt: "xmlns:vt",
|
||||||
|
};
|
||||||
|
}
|
15
src/file/app-properties/app-properties.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { AppPropertiesAttributes } from "./app-properties-attributes";
|
||||||
|
|
||||||
|
export class AppProperties extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("Properties");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new AppPropertiesAttributes({
|
||||||
|
xmlns: "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",
|
||||||
|
vt: "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
11
src/file/content-types/content-types-attributes.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export interface IContentTypeAttributes {
|
||||||
|
readonly xmlns?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ContentTypeAttributes extends XmlAttributeComponent<IContentTypeAttributes> {
|
||||||
|
protected readonly xmlKeys = {
|
||||||
|
xmlns: "xmlns",
|
||||||
|
};
|
||||||
|
}
|
144
src/file/content-types/content-types.spec.ts
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
// tslint:disable:no-string-literal
|
||||||
|
|
||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "export/formatter";
|
||||||
|
|
||||||
|
import { ContentTypes } from "./content-types";
|
||||||
|
|
||||||
|
describe("ContentTypes", () => {
|
||||||
|
let contentTypes: ContentTypes;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
contentTypes = new ContentTypes();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create default content types", () => {
|
||||||
|
const tree = new Formatter().format(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"][6]).to.deep.equal({
|
||||||
|
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"][8]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
|
||||||
|
PartName: "/word/document.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(tree["Types"][9]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
|
||||||
|
PartName: "/word/styles.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(tree["Types"][10]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-package.core-properties+xml",
|
||||||
|
PartName: "/docProps/core.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(tree["Types"][11]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||||
|
PartName: "/docProps/app.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(tree["Types"][12]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
|
||||||
|
PartName: "/word/numbering.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#addFooter()", () => {
|
||||||
|
it("should add footer", () => {
|
||||||
|
contentTypes.addFooter(101);
|
||||||
|
contentTypes.addFooter(102);
|
||||||
|
const tree = new Formatter().format(contentTypes);
|
||||||
|
|
||||||
|
expect(tree["Types"][14]).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: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||||
|
PartName: "/word/footer102.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#addHeader()", () => {
|
||||||
|
it("should add header", () => {
|
||||||
|
contentTypes.addHeader(201);
|
||||||
|
contentTypes.addHeader(202);
|
||||||
|
const tree = new Formatter().format(contentTypes);
|
||||||
|
|
||||||
|
expect(tree["Types"][14]).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: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||||
|
PartName: "/word/header202.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
46
src/file/content-types/content-types.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { ContentTypeAttributes } from "./content-types-attributes";
|
||||||
|
import { Default } from "./default/default";
|
||||||
|
import { Override } from "./override/override";
|
||||||
|
|
||||||
|
export class ContentTypes extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("Types");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new ContentTypeAttributes({
|
||||||
|
xmlns: "http://schemas.openxmlformats.org/package/2006/content-types",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.root.push(new Default("image/png", "png"));
|
||||||
|
this.root.push(new Default("image/jpeg", "jpeg"));
|
||||||
|
this.root.push(new Default("image/jpeg", "jpg"));
|
||||||
|
this.root.push(new Default("image/bmp", "bmp"));
|
||||||
|
this.root.push(new Default("image/gif", "gif"));
|
||||||
|
this.root.push(new Default("application/vnd.openxmlformats-package.relationships+xml", "rels"));
|
||||||
|
this.root.push(new Default("application/xml", "xml"));
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", "/word/document.xml"),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", "/word/styles.xml"));
|
||||||
|
this.root.push(new Override("application/vnd.openxmlformats-package.core-properties+xml", "/docProps/core.xml"));
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public addFooter(index: number): void {
|
||||||
|
this.root.push(
|
||||||
|
new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", `/word/footer${index}.xml`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addHeader(index: number): void {
|
||||||
|
this.root.push(
|
||||||
|
new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", `/word/header${index}.xml`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
13
src/file/content-types/default/default-attributes.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export interface IDefaultAttributes {
|
||||||
|
readonly contentType: string;
|
||||||
|
readonly extension?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DefaultAttributes extends XmlAttributeComponent<IDefaultAttributes> {
|
||||||
|
protected readonly xmlKeys = {
|
||||||
|
contentType: "ContentType",
|
||||||
|
extension: "Extension",
|
||||||
|
};
|
||||||
|
}
|
15
src/file/content-types/default/default.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { DefaultAttributes } from "./default-attributes";
|
||||||
|
|
||||||
|
export class Default extends XmlComponent {
|
||||||
|
constructor(contentType: string, extension?: string) {
|
||||||
|
super("Default");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new DefaultAttributes({
|
||||||
|
contentType: contentType,
|
||||||
|
extension: extension,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
13
src/file/content-types/override/override-attributes.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export interface IOverrideAttributes {
|
||||||
|
readonly contentType: string;
|
||||||
|
readonly partName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OverrideAttributes extends XmlAttributeComponent<IOverrideAttributes> {
|
||||||
|
protected readonly xmlKeys = {
|
||||||
|
contentType: "ContentType",
|
||||||
|
partName: "PartName",
|
||||||
|
};
|
||||||
|
}
|
15
src/file/content-types/override/override.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { OverrideAttributes } from "./override-attributes";
|
||||||
|
|
||||||
|
export class Override extends XmlComponent {
|
||||||
|
constructor(contentType: string, partName?: string) {
|
||||||
|
super("Override");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new OverrideAttributes({
|
||||||
|
contentType: contentType,
|
||||||
|
partName: partName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,7 @@
|
|||||||
import { DocumentAttributes } from "../docx/document/document-attributes";
|
import { XmlComponent } from "file/xml-components";
|
||||||
import { XmlComponent } from "../docx/xml-components";
|
import { DocumentAttributes } from "../document/document-attributes";
|
||||||
|
|
||||||
export class Title extends XmlComponent {
|
export class Title extends XmlComponent {
|
||||||
|
|
||||||
constructor(value: string) {
|
constructor(value: string) {
|
||||||
super("dc:title");
|
super("dc:title");
|
||||||
this.root.push(value);
|
this.root.push(value);
|
||||||
@ -10,7 +9,6 @@ export class Title extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Subject extends XmlComponent {
|
export class Subject extends XmlComponent {
|
||||||
|
|
||||||
constructor(value: string) {
|
constructor(value: string) {
|
||||||
super("dc:subject");
|
super("dc:subject");
|
||||||
this.root.push(value);
|
this.root.push(value);
|
||||||
@ -18,7 +16,6 @@ export class Subject extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Creator extends XmlComponent {
|
export class Creator extends XmlComponent {
|
||||||
|
|
||||||
constructor(value: string) {
|
constructor(value: string) {
|
||||||
super("dc:creator");
|
super("dc:creator");
|
||||||
this.root.push(value);
|
this.root.push(value);
|
||||||
@ -26,7 +23,6 @@ export class Creator extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Keywords extends XmlComponent {
|
export class Keywords extends XmlComponent {
|
||||||
|
|
||||||
constructor(value: string) {
|
constructor(value: string) {
|
||||||
super("cp:keywords");
|
super("cp:keywords");
|
||||||
this.root.push(value);
|
this.root.push(value);
|
||||||
@ -34,7 +30,6 @@ export class Keywords extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Description extends XmlComponent {
|
export class Description extends XmlComponent {
|
||||||
|
|
||||||
constructor(value: string) {
|
constructor(value: string) {
|
||||||
super("dc:description");
|
super("dc:description");
|
||||||
this.root.push(value);
|
this.root.push(value);
|
||||||
@ -42,7 +37,6 @@ export class Description extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class LastModifiedBy extends XmlComponent {
|
export class LastModifiedBy extends XmlComponent {
|
||||||
|
|
||||||
constructor(value: string) {
|
constructor(value: string) {
|
||||||
super("cp:lastModifiedBy");
|
super("cp:lastModifiedBy");
|
||||||
this.root.push(value);
|
this.root.push(value);
|
||||||
@ -50,7 +44,6 @@ export class LastModifiedBy extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Revision extends XmlComponent {
|
export class Revision extends XmlComponent {
|
||||||
|
|
||||||
constructor(value: string) {
|
constructor(value: string) {
|
||||||
super("cp:revision");
|
super("cp:revision");
|
||||||
this.root.push(value);
|
this.root.push(value);
|
||||||
@ -72,23 +65,25 @@ export abstract class DateComponent extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Created extends DateComponent {
|
export class Created extends DateComponent {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("dcterms:created");
|
super("dcterms:created");
|
||||||
this.root.push(new DocumentAttributes({
|
this.root.push(
|
||||||
type: "dcterms:W3CDTF",
|
new DocumentAttributes({
|
||||||
}));
|
type: "dcterms:W3CDTF",
|
||||||
|
}),
|
||||||
|
);
|
||||||
this.root.push(this.getCurrentDate());
|
this.root.push(this.getCurrentDate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Modified extends DateComponent {
|
export class Modified extends DateComponent {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("dcterms:modified");
|
super("dcterms:modified");
|
||||||
this.root.push(new DocumentAttributes({
|
this.root.push(
|
||||||
type: "dcterms:W3CDTF",
|
new DocumentAttributes({
|
||||||
}));
|
type: "dcterms:W3CDTF",
|
||||||
|
}),
|
||||||
|
);
|
||||||
this.root.push(this.getCurrentDate());
|
this.root.push(this.getCurrentDate());
|
||||||
}
|
}
|
||||||
}
|
}
|
1
src/file/core-properties/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./properties";
|
@ -1,13 +1,13 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
|
||||||
import { Formatter } from "../export/formatter";
|
import { Formatter } from "export/formatter";
|
||||||
import { Properties } from "../properties";
|
|
||||||
|
import { CoreProperties } from "./properties";
|
||||||
|
|
||||||
describe("Properties", () => {
|
describe("Properties", () => {
|
||||||
|
|
||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
it("sets the appropriate attributes on the top-level", () => {
|
it("sets the appropriate attributes on the top-level", () => {
|
||||||
const properties = new Properties({});
|
const properties = new CoreProperties({});
|
||||||
const tree = new Formatter().format(properties);
|
const tree = new Formatter().format(properties);
|
||||||
expect(Object.keys(tree)).to.deep.equal(["cp:coreProperties"]);
|
expect(Object.keys(tree)).to.deep.equal(["cp:coreProperties"]);
|
||||||
expect(tree["cp:coreProperties"]).to.be.an.instanceof(Array);
|
expect(tree["cp:coreProperties"]).to.be.an.instanceof(Array);
|
||||||
@ -23,18 +23,16 @@ describe("Properties", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should create properties with a title", () => {
|
it("should create properties with a title", () => {
|
||||||
const properties = new Properties({title: "test document"});
|
const properties = new CoreProperties({ title: "test document" });
|
||||||
const tree = new Formatter().format(properties);
|
const tree = new Formatter().format(properties);
|
||||||
expect(Object.keys(tree)).to.deep.equal(["cp:coreProperties"]);
|
expect(Object.keys(tree)).to.deep.equal(["cp:coreProperties"]);
|
||||||
expect(tree["cp:coreProperties"]).to.be.an.instanceof(Array);
|
expect(tree["cp:coreProperties"]).to.be.an.instanceof(Array);
|
||||||
expect(Object.keys(tree["cp:coreProperties"][0])).to.deep.equal(["_attr"]);
|
expect(Object.keys(tree["cp:coreProperties"][0])).to.deep.equal(["_attr"]);
|
||||||
expect(tree["cp:coreProperties"][1]).to.deep.equal(
|
expect(tree["cp:coreProperties"][1]).to.deep.equal({ "dc:title": ["test document"] });
|
||||||
{"dc:title": ["test document"]},
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create properties with all the attributes given", () => {
|
it("should create properties with all the attributes given", () => {
|
||||||
const properties = new Properties({
|
const properties = new CoreProperties({
|
||||||
title: "test document",
|
title: "test document",
|
||||||
subject: "test subject",
|
subject: "test subject",
|
||||||
creator: "me",
|
creator: "me",
|
||||||
@ -60,14 +58,14 @@ describe("Properties", () => {
|
|||||||
"dcterms:created",
|
"dcterms:created",
|
||||||
"dcterms:modified",
|
"dcterms:modified",
|
||||||
]);
|
]);
|
||||||
expect(tree["cp:coreProperties"].slice(1, -2).sort((a, b) => key(a) < key(b) ? -1 : 1)).to.deep.equal([
|
expect(tree["cp:coreProperties"].slice(1, -2).sort((a, b) => (key(a) < key(b) ? -1 : 1))).to.deep.equal([
|
||||||
{"cp:keywords": ["test docx"]},
|
{ "cp:keywords": ["test docx"] },
|
||||||
{"cp:lastModifiedBy": ["the author"]},
|
{ "cp:lastModifiedBy": ["the author"] },
|
||||||
{"cp:revision": ["123"]},
|
{ "cp:revision": ["123"] },
|
||||||
{"dc:creator": ["me"]},
|
{ "dc:creator": ["me"] },
|
||||||
{"dc:description": ["testing document"]},
|
{ "dc:description": ["testing document"] },
|
||||||
{"dc:subject": ["test subject"]},
|
{ "dc:subject": ["test subject"] },
|
||||||
{"dc:title": ["test document"]},
|
{ "dc:title": ["test document"] },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|