Compare commits
351 Commits
Author | SHA1 | Date | |
---|---|---|---|
accb1d44d0 | |||
b61f798cd9 | |||
be2ec9d4cf | |||
514fbb1c09 | |||
8ce397f61e | |||
3b0dfb8489 | |||
f4933deaf2 | |||
add2273551 | |||
91b00466a0 | |||
d0612e115e | |||
e4a6cbe887 | |||
0386810714 | |||
d4585f60b3 | |||
18f015a959 | |||
10c866bdea | |||
680c938dcf | |||
c3a617fefd | |||
6362b498a6 | |||
2e30ab29be | |||
44568023cd | |||
c57aaa856d | |||
0f4f24c3f3 | |||
2fe7a2e3d3 | |||
20b62f5768 | |||
ab063eee08 | |||
66c7624b40 | |||
bfc302023f | |||
3505912f74 | |||
ec379f2e4d | |||
2d17954104 | |||
01c834b16b | |||
2aeb2c2e88 | |||
be88086434 | |||
28f0cc681d | |||
def29c535b | |||
168e5e6e11 | |||
ce71f19826 | |||
4fd3f3a6b8 | |||
39763d3869 | |||
36f90edc67 | |||
b0cbd6f609 | |||
d23d4a4557 | |||
07c58ee43c | |||
1037e8f1b9 | |||
f0ae8396f7 | |||
1ae5995f9c | |||
751ad9d304 | |||
0bd1514826 | |||
16bfc78d8d | |||
528008406a | |||
9e3010ac6a | |||
7c1fa6f4bf | |||
f58d31cc3d | |||
286c742ece | |||
3edc51d5a0 | |||
05d4c9688e | |||
d4401d1597 | |||
90358e73f6 | |||
a77ca02ea9 | |||
7785f0df02 | |||
0e6ed6e5ea | |||
70e84c8d38 | |||
11c3886659 | |||
6104e5bc35 | |||
c748b9a7fc | |||
714dbc0179 | |||
7f16cfc359 | |||
78757753c8 | |||
8343edcdf1 | |||
1ab9e08eb9 | |||
e1bc7f34c9 | |||
d5b495df5b | |||
d6256d2acb | |||
f1c7e448ae | |||
b600fd9324 | |||
3bf40ecb33 | |||
ffb650daa9 | |||
d2122e7806 | |||
6b6eb1d7a2 | |||
c80d881105 | |||
3c223c69e9 | |||
2420321008 | |||
29d5421cea | |||
71953cf45a | |||
325866d9c3 | |||
457e9c12e3 | |||
ff4a66466a | |||
cc142cc052 | |||
6e0dc69217 | |||
33332bcf66 | |||
b94f9ac25b | |||
0a7be48dcb | |||
3f3397620e | |||
154bc1df95 | |||
715e436c4c | |||
f5a9bcf839 | |||
31d2da86ff | |||
9dad3ca356 | |||
355e01edc6 | |||
c19bf1c404 | |||
970450074d | |||
91e8295648 | |||
3e40ae862b | |||
236cce604f | |||
0388a564b5 | |||
cd77ceba69 | |||
5ae8731abe | |||
7bb90c55d1 | |||
a07d6378e8 | |||
4fb8d277b4 | |||
73186ce920 | |||
d02f98956f | |||
d186d42bb6 | |||
338f7be967 | |||
b63a6e6e16 | |||
7e9884081e | |||
262f6323d0 | |||
811dd61562 | |||
9801879063 | |||
e30836c935 | |||
352511bb55 | |||
8ce057e25c | |||
0775ecb08b | |||
e909ab9886 | |||
e03bb155b9 | |||
91620d61d8 | |||
0fba450c9a | |||
ca604fb004 | |||
9e998d233c | |||
6c0fd3adec | |||
6ad18420e5 | |||
ef95a16f39 | |||
53861ec18d | |||
81b1569ad2 | |||
66a1992da0 | |||
100356c344 | |||
31905a27b1 | |||
08916176a2 | |||
ffb3d63c92 | |||
22005428ba | |||
a70acd6d63 | |||
af9e0b0d3f | |||
a0c2be8af2 | |||
df9d4b4219 | |||
cdf5015920 | |||
fe9b438a51 | |||
59cbf8c9cb | |||
7af8175034 | |||
bf0393b485 | |||
83a8822cf7 | |||
eada41b8a1 | |||
2c03377c47 | |||
484fc4aa2d | |||
2846014db0 | |||
c9d86619de | |||
ce485dbc29 | |||
4e875b4744 | |||
3f6c006716 | |||
f8f5d43b0c | |||
8c0fe00c6f | |||
c37d9ca5b3 | |||
f3dc1f0712 | |||
a4d96bbf6e | |||
90c40178aa | |||
2ee96b419a | |||
773900b620 | |||
ece440340f | |||
5233b4b5e6 | |||
86de252a52 | |||
c206d23480 | |||
5a53a138d9 | |||
5b5deb198e | |||
88a30e5af7 | |||
0b9ffa9a7b | |||
5052bcde4f | |||
d5409678ee | |||
72c39a9eb2 | |||
2ed7a28d1e | |||
7b2ff00c83 | |||
d5b0083d77 | |||
406183d104 | |||
c6d3e60314 | |||
be71037a13 | |||
c8c64b320b | |||
8514e8ad31 | |||
d505281a7e | |||
58575342e0 | |||
60ad6d6ab5 | |||
f9dec1cb3c | |||
ffd998cbf5 | |||
c713e6f371 | |||
3c55f9fc1f | |||
c9a8a99278 | |||
b309d327f0 | |||
58bad10bfe | |||
26ea6c518f | |||
d4594a97a2 | |||
68f951ef79 | |||
12e29c6a75 | |||
05eedb2ac1 | |||
8cbc6c15fe | |||
2c21f64b9f | |||
7ec9cff433 | |||
3f979b9981 | |||
3077ca96a7 | |||
46ded68369 | |||
b3a53d341d | |||
afcd5e70a1 | |||
ae48999e38 | |||
4c38e45455 | |||
ec847dccf9 | |||
ca47a1dc3f | |||
cc53ff417b | |||
fcba817a4a | |||
bcd8249d17 | |||
bc0a197ba1 | |||
2e16279d7b | |||
87590aedbd | |||
0fef7763d5 | |||
e55698d221 | |||
d410b5e96f | |||
8587601411 | |||
5b67cb21c6 | |||
42fd9d06de | |||
1e0b74fd27 | |||
21934ec85b | |||
b4f01fb6ec | |||
fbf6329500 | |||
41549443c3 | |||
92630af4e1 | |||
4db633da16 | |||
dac02f13ae | |||
1a52dec1c2 | |||
be1ca51162 | |||
768e20bb79 | |||
26b9b15de8 | |||
42713095b6 | |||
3acafeca38 | |||
7ca75c8c5a | |||
c00ad454a9 | |||
16d04eb4d3 | |||
816845f271 | |||
12eeda8d33 | |||
3d622c2ef1 | |||
21a6767c6f | |||
7a7326ec09 | |||
9b3af4e217 | |||
cee0114f81 | |||
03399aedf0 | |||
2739b94bd8 | |||
c720441d62 | |||
00b5898fe0 | |||
0d0940b7a5 | |||
f28c949ac6 | |||
63317d6ac8 | |||
6406ba831f | |||
7a7575964a | |||
d75c833717 | |||
40344b57d9 | |||
6fc26d1da3 | |||
a012a0e418 | |||
bf1a2f3059 | |||
ca316b4e6d | |||
1fb5c581c9 | |||
efa64b451a | |||
38059ea6b9 | |||
c2110622ca | |||
0452959b85 | |||
0eca8389b6 | |||
66e043137b | |||
d684654625 | |||
7749f6b20f | |||
15c9c0ae3e | |||
74fddc2ada | |||
cff866ecdc | |||
e952c02c83 | |||
546983c6ba | |||
c7134564a3 | |||
984a3573eb | |||
defe94a2b2 | |||
e23c8c4b46 | |||
7941ff487a | |||
7b7df33a0f | |||
68b04f2511 | |||
db3e017a7d | |||
c21d45a79e | |||
6defb686bb | |||
f68796dae1 | |||
b1872bcb95 | |||
aeb2487c70 | |||
e42e50dd39 | |||
f4572b9720 | |||
7646f3a845 | |||
e4e5b1e4f6 | |||
414fa70e5d | |||
f255d4c141 | |||
4513bb529b | |||
5907132062 | |||
69bf313f18 | |||
fe8b0a6b2b | |||
abcfd79f2f | |||
41e3b078ec | |||
4eb2f92c1f | |||
9bfb5bf681 | |||
473ad2947c | |||
ee28f6ad84 | |||
11bebd42ac | |||
49b4ca67e0 | |||
3282f762df | |||
d335106b46 | |||
cefa01f7ec | |||
b7ba2b3306 | |||
03d5152e7c | |||
af3b7f2613 | |||
1405841565 | |||
211d2dc0d6 | |||
82f431465f | |||
247afe56ce | |||
91e874eee5 | |||
ff67c3660b | |||
58c580b9ca | |||
a9da151026 | |||
83c72d0dbb | |||
2c31367851 | |||
3b618f3a2c | |||
4c5bc6232d | |||
694835bdac | |||
c8b75c19da | |||
c0181aa83a | |||
c5b615ae2a | |||
54cbd56f42 | |||
c8914a109e | |||
8509bccc4f | |||
4ce53a3d76 | |||
ec69f9ac07 | |||
5a4fe5d5da | |||
e6af9b4b73 | |||
361f9231d8 | |||
4215932550 | |||
29da523579 | |||
fbb4d7cfde | |||
323895f0ab | |||
32a5cca878 | |||
911c705b59 | |||
594c8d72b8 | |||
7ea5232b22 | |||
be12d61795 | |||
925340eb3d | |||
601a4038f4 | |||
e0576c889b | |||
4ff7f0a7bd |
65
.cspell.json
65
.cspell.json
@ -4,34 +4,40 @@
|
|||||||
"version": "0.2",
|
"version": "0.2",
|
||||||
// language - current active spelling language
|
// language - current active spelling language
|
||||||
"language": "en_US",
|
"language": "en_US",
|
||||||
"dictionaries": ["en_US", "typescript", "softwareTerms", "fonts", "npm"],
|
"dictionaries": [
|
||||||
|
"en_US",
|
||||||
|
"typescript",
|
||||||
|
"softwareTerms",
|
||||||
|
"fonts",
|
||||||
|
"npm"
|
||||||
|
],
|
||||||
// words - list of words to be always considered correct
|
// words - list of words to be always considered correct
|
||||||
"words": [
|
"words": [
|
||||||
"Xmlable",
|
"Abjad",
|
||||||
|
"aiueo",
|
||||||
|
"ATLEAST",
|
||||||
|
"chosung",
|
||||||
|
"clippy",
|
||||||
|
"datas",
|
||||||
|
"docsify",
|
||||||
|
"dolan",
|
||||||
|
"falsey",
|
||||||
|
"Initializable",
|
||||||
|
"iroha",
|
||||||
|
"jsonify",
|
||||||
|
"jszip",
|
||||||
|
"NUMPAGES",
|
||||||
|
"odttf",
|
||||||
|
"ooxml",
|
||||||
|
"panose",
|
||||||
|
"rels",
|
||||||
|
"rsid",
|
||||||
"twip",
|
"twip",
|
||||||
"twips",
|
"twips",
|
||||||
"jsonify",
|
"Xmlable",
|
||||||
"falsey",
|
|
||||||
"aiueo",
|
|
||||||
"iroha",
|
|
||||||
"aiueo",
|
|
||||||
"iroha",
|
|
||||||
"chosung",
|
|
||||||
"Abjad",
|
|
||||||
"Initializable",
|
|
||||||
"rels",
|
|
||||||
"dolan",
|
|
||||||
"xmlify",
|
|
||||||
"Xmlifyed",
|
|
||||||
"xmlified",
|
"xmlified",
|
||||||
"datas",
|
"xmlify",
|
||||||
"jszip",
|
"Xmlifyed"
|
||||||
"rsid",
|
|
||||||
"NUMPAGES",
|
|
||||||
"ATLEAST",
|
|
||||||
"ooxml",
|
|
||||||
"clippy",
|
|
||||||
"docsify"
|
|
||||||
],
|
],
|
||||||
"ignoreRegExpList": [
|
"ignoreRegExpList": [
|
||||||
"/\"w:.+\"/",
|
"/\"w:.+\"/",
|
||||||
@ -47,12 +53,19 @@
|
|||||||
"/xmlKeys = {[^}]+}/g",
|
"/xmlKeys = {[^}]+}/g",
|
||||||
"/\\.to\\.deep\\.equal\\({[^)]+}\\)/g",
|
"/\\.to\\.deep\\.equal\\({[^)]+}\\)/g",
|
||||||
"\\.to\\.include\\.members\\(\\[[^\\]]+]\\)",
|
"\\.to\\.include\\.members\\(\\[[^\\]]+]\\)",
|
||||||
"/new [a-zA-Z]+\\({[^£]+}\\)/g"
|
"/new [a-zA-Z]+\\({[^£]+}\\)/g",
|
||||||
|
"/<element name=\"[a-z]+\"/gi",
|
||||||
|
"/<attribute name=\"[a-z]+\"/gi"
|
||||||
|
],
|
||||||
|
"ignorePaths": [
|
||||||
|
"package.json",
|
||||||
|
"docs/api"
|
||||||
],
|
],
|
||||||
"ignorePaths": ["package.json", "docs/api"],
|
|
||||||
"allowCompoundWords": true,
|
"allowCompoundWords": true,
|
||||||
// flagWords - list of words to be always considered incorrect
|
// flagWords - list of words to be always considered incorrect
|
||||||
// This is useful for offensive words and common spelling errors.
|
// This is useful for offensive words and common spelling errors.
|
||||||
// For example "hte" should be "the"
|
// For example "hte" should be "the"
|
||||||
"flagWords": ["hte"]
|
"flagWords": [
|
||||||
|
"hte"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
14
.eslintrc.js
14
.eslintrc.js
@ -96,8 +96,8 @@ module.exports = {
|
|||||||
"@typescript-eslint/explicit-function-return-type": [
|
"@typescript-eslint/explicit-function-return-type": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
allowExpressions: false,
|
allowExpressions: true,
|
||||||
allowTypedFunctionExpressions: false,
|
allowTypedFunctionExpressions: true,
|
||||||
allowHigherOrderFunctions: false,
|
allowHigherOrderFunctions: false,
|
||||||
allowDirectConstAssertionInArrowFunctions: true,
|
allowDirectConstAssertionInArrowFunctions: true,
|
||||||
allowConciseArrowFunctionExpressionsStartingWithVoid: true,
|
allowConciseArrowFunctionExpressionsStartingWithVoid: true,
|
||||||
@ -214,7 +214,6 @@ module.exports = {
|
|||||||
"no-sequences": "error",
|
"no-sequences": "error",
|
||||||
"no-shadow": "off",
|
"no-shadow": "off",
|
||||||
"no-sparse-arrays": "error",
|
"no-sparse-arrays": "error",
|
||||||
"no-template-curly-in-string": "error",
|
|
||||||
"no-throw-literal": "error",
|
"no-throw-literal": "error",
|
||||||
"no-trailing-spaces": "error",
|
"no-trailing-spaces": "error",
|
||||||
"no-undef-init": "error",
|
"no-undef-init": "error",
|
||||||
@ -243,7 +242,6 @@ module.exports = {
|
|||||||
"unicorn/prefer-ternary": "error",
|
"unicorn/prefer-ternary": "error",
|
||||||
"use-isnan": "error",
|
"use-isnan": "error",
|
||||||
"valid-typeof": "off",
|
"valid-typeof": "off",
|
||||||
"@typescript-eslint/explicit-function-return-type": "off",
|
|
||||||
"functional/immutable-data": [
|
"functional/immutable-data": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
@ -252,9 +250,12 @@ module.exports = {
|
|||||||
ignoreAccessorPattern: ["**.root*", "**.numberingReferences*", "**.sections*", "**.properties*"],
|
ignoreAccessorPattern: ["**.root*", "**.numberingReferences*", "**.sections*", "**.properties*"],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"functional/no-method-signature": "error",
|
"functional/prefer-property-signatures": "error",
|
||||||
"functional/no-mixed-type": "error",
|
"functional/no-mixed-types": "error",
|
||||||
|
// TODO: Deprecated. Use prefer-immutable-types and type-declaration-immutability instead
|
||||||
"functional/prefer-readonly-type": "error",
|
"functional/prefer-readonly-type": "error",
|
||||||
|
// "functional/prefer-immutable-types": "error",
|
||||||
|
// "functional/type-declaration-immutability": "error",
|
||||||
"no-unused-vars": ["error", { argsIgnorePattern: "^[_]+$" }],
|
"no-unused-vars": ["error", { argsIgnorePattern: "^[_]+$" }],
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
@ -264,6 +265,7 @@ module.exports = {
|
|||||||
"@typescript-eslint/no-unused-expressions": "off",
|
"@typescript-eslint/no-unused-expressions": "off",
|
||||||
"@typescript-eslint/dot-notation": "off",
|
"@typescript-eslint/dot-notation": "off",
|
||||||
"prefer-destructuring": "off",
|
"prefer-destructuring": "off",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
2
.github/workflows/default.yml
vendored
2
.github/workflows/default.yml
vendored
@ -34,7 +34,7 @@ jobs:
|
|||||||
- name: Test
|
- name: Test
|
||||||
run: npm run test.coverage
|
run: npm run test.coverage
|
||||||
- name: Codecov
|
- name: Codecov
|
||||||
uses: codecov/codecov-action@v1
|
uses: codecov/codecov-action@v3
|
||||||
with:
|
with:
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
verbose: true
|
verbose: true
|
||||||
|
99
.github/workflows/demos.yml
vendored
99
.github/workflows/demos.yml
vendored
@ -301,15 +301,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
xml-file: build/extracted-doc/word/document.xml
|
xml-file: build/extracted-doc/word/document.xml
|
||||||
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
||||||
- name: Run Demo
|
|
||||||
run: npm run ts-node -- ./demo/30-template-document.ts
|
|
||||||
- name: Extract Word Document
|
|
||||||
run: npm run extract
|
|
||||||
- name: Validate XML
|
|
||||||
uses: ChristophWurst/xmllint-action@v1
|
|
||||||
with:
|
|
||||||
xml-file: build/extracted-doc/word/document.xml
|
|
||||||
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
|
||||||
- name: Run Demo
|
- name: Run Demo
|
||||||
run: npm run ts-node -- ./demo/31-tables.ts
|
run: npm run ts-node -- ./demo/31-tables.ts
|
||||||
- name: Extract Word Document
|
- name: Extract Word Document
|
||||||
@ -716,3 +707,93 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
xml-file: build/extracted-doc/word/document.xml
|
xml-file: build/extracted-doc/word/document.xml
|
||||||
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
||||||
|
# - name: Run Demo
|
||||||
|
# run: npm run ts-node -- ./demo/75-tab-stops.ts
|
||||||
|
# - name: Extract Word Document
|
||||||
|
# run: npm run extract
|
||||||
|
# - name: Validate XML
|
||||||
|
# uses: ChristophWurst/xmllint-action@v1
|
||||||
|
# with:
|
||||||
|
# xml-file: build/extracted-doc/word/document.xml
|
||||||
|
# xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
||||||
|
- name: Run Demo
|
||||||
|
run: npm run ts-node -- ./demo/76-compatibility.ts
|
||||||
|
- name: Extract Word Document
|
||||||
|
run: npm run extract
|
||||||
|
- name: Validate XML
|
||||||
|
uses: ChristophWurst/xmllint-action@v1
|
||||||
|
with:
|
||||||
|
xml-file: build/extracted-doc/word/document.xml
|
||||||
|
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
||||||
|
- name: Run Demo
|
||||||
|
run: npm run ts-node -- ./demo/77-side-by-side-tables.ts
|
||||||
|
- name: Extract Word Document
|
||||||
|
run: npm run extract
|
||||||
|
- name: Validate XML
|
||||||
|
uses: ChristophWurst/xmllint-action@v1
|
||||||
|
with:
|
||||||
|
xml-file: build/extracted-doc/word/document.xml
|
||||||
|
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
||||||
|
- name: Run Demo
|
||||||
|
run: npm run ts-node -- ./demo/78-thai-distributed.ts
|
||||||
|
- name: Extract Word Document
|
||||||
|
run: npm run extract
|
||||||
|
- name: Validate XML
|
||||||
|
uses: ChristophWurst/xmllint-action@v1
|
||||||
|
with:
|
||||||
|
xml-file: build/extracted-doc/word/document.xml
|
||||||
|
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
||||||
|
- name: Run Demo
|
||||||
|
run: npm run ts-node -- ./demo/79-table-from-data-source.ts
|
||||||
|
- name: Extract Word Document
|
||||||
|
run: npm run extract
|
||||||
|
- name: Validate XML
|
||||||
|
uses: ChristophWurst/xmllint-action@v1
|
||||||
|
with:
|
||||||
|
xml-file: build/extracted-doc/word/document.xml
|
||||||
|
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
||||||
|
- name: Run Demo
|
||||||
|
run: npm run ts-node -- ./demo/80-thai-distributed.ts
|
||||||
|
- name: Extract Word Document
|
||||||
|
run: npm run extract
|
||||||
|
- name: Validate XML
|
||||||
|
uses: ChristophWurst/xmllint-action@v1
|
||||||
|
with:
|
||||||
|
xml-file: build/extracted-doc/word/document.xml
|
||||||
|
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
||||||
|
- name: Run Demo
|
||||||
|
run: npm run ts-node -- ./demo/81-continuous-header.ts
|
||||||
|
- name: Extract Word Document
|
||||||
|
run: npm run extract
|
||||||
|
- name: Validate XML
|
||||||
|
uses: ChristophWurst/xmllint-action@v1
|
||||||
|
with:
|
||||||
|
xml-file: build/extracted-doc/word/document.xml
|
||||||
|
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
||||||
|
- name: Run Demo
|
||||||
|
run: npm run ts-node -- ./demo/82-new-headers-new-section.ts
|
||||||
|
- name: Extract Word Document
|
||||||
|
run: npm run extract
|
||||||
|
- name: Validate XML
|
||||||
|
uses: ChristophWurst/xmllint-action@v1
|
||||||
|
with:
|
||||||
|
xml-file: build/extracted-doc/word/document.xml
|
||||||
|
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
||||||
|
- name: Run Demo
|
||||||
|
run: npm run ts-node -- ./demo/83-setting-languages.ts
|
||||||
|
- name: Extract Word Document
|
||||||
|
run: npm run extract
|
||||||
|
- name: Validate XML
|
||||||
|
uses: ChristophWurst/xmllint-action@v1
|
||||||
|
with:
|
||||||
|
xml-file: build/extracted-doc/word/document.xml
|
||||||
|
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
||||||
|
- name: Run Demo
|
||||||
|
run: npm run ts-node -- ./demo/84-positional-tabs.ts
|
||||||
|
- name: Extract Word Document
|
||||||
|
run: npm run extract
|
||||||
|
- name: Validate XML
|
||||||
|
uses: ChristophWurst/xmllint-action@v1
|
||||||
|
with:
|
||||||
|
xml-file: build/extracted-doc/word/document.xml
|
||||||
|
xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd
|
||||||
|
@ -40,4 +40,7 @@ build-tests
|
|||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
# docs
|
# docs
|
||||||
docs
|
docs
|
||||||
|
|
||||||
|
# src
|
||||||
|
src
|
||||||
|
6
.nycrc
6
.nycrc
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"check-coverage": true,
|
"check-coverage": true,
|
||||||
"statements": 99.79,
|
"statements": 99.87,
|
||||||
"branches": 98.41,
|
"branches": 98.21,
|
||||||
"functions": 100,
|
"functions": 100,
|
||||||
"lines": 99.73,
|
"lines": 99.86,
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*.ts"
|
"src/**/*.ts"
|
||||||
],
|
],
|
||||||
|
18
.vscode/launch.json
vendored
Normal file
18
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Run Demo",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"-r",
|
||||||
|
"${workspaceFolder}/node_modules/ts-node/register",
|
||||||
|
"-r",
|
||||||
|
"${workspaceFolder}/node_modules/tsconfig-paths/register"
|
||||||
|
],
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"program": "${workspaceFolder}/demo/85-template-document.ts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
Easily generate .docx files with JS/TS. Works for Node and on the Browser.
|
Easily generate and modify .docx files with JS/TS. Works for Node and on the Browser.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -88,6 +88,7 @@ Read the contribution guidelines [here](https://docx.js.org/#/contribution-guide
|
|||||||
[<img src="https://i.imgur.com/cmykN7c.png" alt="drawing"/>](https://www.arity.co/)
|
[<img src="https://i.imgur.com/cmykN7c.png" alt="drawing"/>](https://www.arity.co/)
|
||||||
[<img src="https://i.imgur.com/PXo25um.png" alt="drawing" height="50"/>](https://www.circadianrisk.com/)
|
[<img src="https://i.imgur.com/PXo25um.png" alt="drawing" height="50"/>](https://www.circadianrisk.com/)
|
||||||
[<img src="https://i.imgur.com/AKGhtlh.png" alt="drawing"/>](https://lexense.com/)
|
[<img src="https://i.imgur.com/AKGhtlh.png" alt="drawing"/>](https://lexense.com/)
|
||||||
|
[<img src="https://i.imgur.com/9tqJaHw.png" alt="drawing" height="50"/>](https://novelpad.co/)
|
||||||
|
|
||||||
...and many more!
|
...and many more!
|
||||||
|
|
||||||
|
@ -25,6 +25,17 @@ const doc = new Document({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
text: "",
|
||||||
|
border: {
|
||||||
|
top: {
|
||||||
|
color: "auto",
|
||||||
|
space: 1,
|
||||||
|
style: BorderStyle.SINGLE,
|
||||||
|
size: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [
|
children: [
|
||||||
new TextRun({
|
new TextRun({
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
// Example on how to use a template document
|
|
||||||
// Import from 'docx' rather than '../build' if you install from npm
|
|
||||||
import * as fs from "fs";
|
|
||||||
import { Document, ImportDotx, Packer, Paragraph } from "../build";
|
|
||||||
|
|
||||||
const importDotx = new ImportDotx();
|
|
||||||
const filePath = "./demo/dotx/template.dotx";
|
|
||||||
|
|
||||||
fs.readFile(filePath, (err, data) => {
|
|
||||||
if (err) {
|
|
||||||
throw new Error(`Failed to read file ${filePath}.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
importDotx.extract(data).then((templateDocument) => {
|
|
||||||
const doc = new Document(
|
|
||||||
{
|
|
||||||
sections: [
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
titlePage: templateDocument.titlePageIsDefined,
|
|
||||||
},
|
|
||||||
children: [new Paragraph("Hello World")],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
template: templateDocument,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Packer.toBuffer(doc).then((buffer) => {
|
|
||||||
fs.writeFileSync("My Document.docx", buffer);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -5,7 +5,83 @@ import { Document, Packer, Paragraph, TextRun, CommentRangeStart, CommentRangeEn
|
|||||||
|
|
||||||
const doc = new Document({
|
const doc = new Document({
|
||||||
comments: {
|
comments: {
|
||||||
children: [{ id: 0, author: "Ray Chen", date: new Date(), text: "comment text content" }],
|
children: [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
author: "Ray Chen",
|
||||||
|
date: new Date(),
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "some initial text content",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "comment text content",
|
||||||
|
}),
|
||||||
|
new TextRun({ text: "", break: 1 }),
|
||||||
|
new TextRun({
|
||||||
|
text: "More text here",
|
||||||
|
bold: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
author: "Bob Ross",
|
||||||
|
date: new Date(),
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Some initial text content",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "comment text content",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
author: "John Doe",
|
||||||
|
date: new Date(),
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Hello World",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
author: "Beatriz",
|
||||||
|
date: new Date(),
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Another reply",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
sections: [
|
sections: [
|
||||||
{
|
{
|
||||||
@ -26,6 +102,32 @@ const doc = new Document({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new CommentRangeStart(1),
|
||||||
|
new CommentRangeStart(2),
|
||||||
|
new CommentRangeStart(3),
|
||||||
|
new TextRun({
|
||||||
|
text: "Some text which need commenting",
|
||||||
|
bold: true,
|
||||||
|
}),
|
||||||
|
new CommentRangeEnd(1),
|
||||||
|
new TextRun({
|
||||||
|
children: [new CommentReference(1)],
|
||||||
|
bold: true,
|
||||||
|
}),
|
||||||
|
new CommentRangeEnd(2),
|
||||||
|
new TextRun({
|
||||||
|
children: [new CommentReference(2)],
|
||||||
|
bold: true,
|
||||||
|
}),
|
||||||
|
new CommentRangeEnd(3),
|
||||||
|
new TextRun({
|
||||||
|
children: [new CommentReference(3)],
|
||||||
|
bold: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Exporting the document as a stream
|
// Example of using tab stops
|
||||||
// Import from 'docx' rather than '../build' if you install from npm
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { Document, HeadingLevel, Packer, Paragraph, TabStopPosition, TabStopType, TextRun } from "../build";
|
import { Document, HeadingLevel, Packer, Paragraph, TabStopPosition, TabStopType, TextRun } from "../build";
|
||||||
|
148
demo/81-continuous-header.ts
Normal file
148
demo/81-continuous-header.ts
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// Example of a continuous header
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Footer, Header, Packer, Paragraph, SectionType, Tab, TextRun } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document({
|
||||||
|
creator: "Creator",
|
||||||
|
title: "Title",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
properties: { titlePage: true },
|
||||||
|
headers: {
|
||||||
|
first: new Header({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "HEADER PAGE ONE",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
default: new Header({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "HEADER PAGE TWO AND FOLLOWING PAGES",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
footers: {
|
||||||
|
first: new Footer({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "FOOTER PAGE ONE",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
default: new Footer({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "FOOTER PAGE TWO AND FOLLOWING PAGES",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer ac suscipit orci, in lobortis risus. Nulla vehicula rutrum finibus. Nullam consequat, magna in vehicula commodo, enim massa consectetur nisl, sit amet rutrum nunc ante vel lorem. Sed sit amet scelerisque velit. Proin non quam eget mauris aliquet posuere a sed orci. Proin posuere ante suscipit neque dignissim hendrerit. Pellentesque eget dapibus metus. Donec at mollis mauris. Vestibulum sit amet scelerisque nulla. Vivamus ipsum erat, tempor sed volutpat non, molestie at odio. Vivamus lectus ligula, finibus at mattis vitae, euismod sed tellus. Etiam neque massa, faucibus a fringilla nec, mollis at ex. Aliquam eget nibh tortor. Sed ut viverra libero. Nulla facilisis bibendum quam eget porttitor.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Sed eget nunc ac turpis facilisis volutpat. Duis eget arcu vitae neque porta hendrerit. Proin vel ante nulla. Duis congue efficitur dui. Suspendisse potenti. Aliquam aliquam nibh eu ipsum sagittis efficitur. Quisque sagittis metus dui, vitae suscipit tortor sollicitudin at. Suspendisse convallis, sem ac ornare condimentum, odio ipsum dapibus justo, a aliquam risus massa ut enim. Mauris vel placerat nibh. Ut iaculis vitae nibh at elementum. Quisque hendrerit et magna vitae mollis. Duis dictum euismod leo, at cursus risus sodales sed.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Sed gravida commodo felis, at aliquet risus volutpat ut. Nam nec ex eleifend tellus sodales volutpat nec ac nibh. Vestibulum pretium, leo vitae lobortis accumsan, urna libero euismod ante, consequat aliquam enim risus id nisl. Donec sagittis, justo eu luctus posuere, leo purus pellentesque turpis, eget volutpat mi leo vitae lacus. Etiam ante ante, posuere at augue non, lacinia ornare purus. Praesent vitae velit in enim congue maximus. Vivamus tincidunt fringilla neque. Curabitur fermentum justo nec sapien porttitor, ac ullamcorper nisi imperdiet. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed non orci vel eros egestas eleifend sit amet a diam. Duis mattis at ligula quis faucibus. Donec elementum lacus velit, a vehicula nunc gravida a. Phasellus eget nunc vehicula, varius velit a, maximus velit. Sed a suscipit nisi, non hendrerit felis. Proin mattis facilisis massa, quis elementum neque fringilla non.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Sed gravida commodo felis, at aliquet risus volutpat ut. Nam nec ex eleifend tellus sodales volutpat nec ac nibh. Vestibulum pretium, leo vitae lobortis accumsan, urna libero euismod ante, consequat aliquam enim risus id nisl. Donec sagittis, justo eu luctus posuere, leo purus pellentesque turpis, eget volutpat mi leo vitae lacus. Etiam ante ante, posuere at augue non, lacinia ornare purus. Praesent vitae velit in enim congue maximus. Vivamus tincidunt fringilla neque. Curabitur fermentum justo nec sapien porttitor, ac ullamcorper nisi imperdiet. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed non orci vel eros egestas eleifend sit amet a diam. Duis mattis at ligula quis faucibus. Donec elementum lacus velit, a vehicula nunc gravida a. Phasellus eget nunc vehicula, varius velit a, maximus velit. Sed a suscipit nisi, non hendrerit felis. Proin mattis facilisis massa, quis elementum neque fringilla non.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: {
|
||||||
|
after: 500,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "The first section ends after this paragraph.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
type: SectionType.CONTINUOUS,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer ac suscipit orci, in lobortis risus. Nulla vehicula rutrum finibus. Nullam consequat, magna in vehicula commodo, enim massa consectetur nisl, sit amet rutrum nunc ante vel lorem. Sed sit amet scelerisque velit. Proin non quam eget mauris aliquet posuere a sed orci. Proin posuere ante suscipit neque dignissim hendrerit. Pellentesque eget dapibus metus. Donec at mollis mauris. Vestibulum sit amet scelerisque nulla. Vivamus ipsum erat, tempor sed volutpat non, molestie at odio. Vivamus lectus ligula, finibus at mattis vitae, euismod sed tellus. Etiam neque massa, faucibus a fringilla nec, mollis at ex. Aliquam eget nibh tortor. Sed ut viverra libero. Nulla facilisis bibendum quam eget porttitor.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Sed eget nunc ac turpis facilisis volutpat. Duis eget arcu vitae neque porta hendrerit. Proin vel ante nulla. Duis congue efficitur dui. Suspendisse potenti. Aliquam aliquam nibh eu ipsum sagittis efficitur. Quisque sagittis metus dui, vitae suscipit tortor sollicitudin at. Suspendisse convallis, sem ac ornare condimentum, odio ipsum dapibus justo, a aliquam risus massa ut enim. Mauris vel placerat nibh. Ut iaculis vitae nibh at elementum. Quisque hendrerit et magna vitae mollis. Duis dictum euismod leo, at cursus risus sodales sed.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Sed gravida commodo felis, at aliquet risus volutpat ut. Nam nec ex eleifend tellus sodales volutpat nec ac nibh. Vestibulum pretium, leo vitae lobortis accumsan, urna libero euismod ante, consequat aliquam enim risus id nisl. Donec sagittis, justo eu luctus posuere, leo purus pellentesque turpis, eget volutpat mi leo vitae lacus. Etiam ante ante, posuere at augue non, lacinia ornare purus. Praesent vitae velit in enim congue maximus. Vivamus tincidunt fringilla neque. Curabitur fermentum justo nec sapien porttitor, ac ullamcorper nisi imperdiet. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed non orci vel eros egestas eleifend sit amet a diam. Duis mattis at ligula quis faucibus. Donec elementum lacus velit, a vehicula nunc gravida a. Phasellus eget nunc vehicula, varius velit a, maximus velit. Sed a suscipit nisi, non hendrerit felis. Proin mattis facilisis massa, quis elementum neque fringilla non.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Sed gravida commodo felis, at aliquet risus volutpat ut. Nam nec ex eleifend tellus sodales volutpat nec ac nibh. Vestibulum pretium, leo vitae lobortis accumsan, urna libero euismod ante, consequat aliquam enim risus id nisl. Donec sagittis, justo eu luctus posuere, leo purus pellentesque turpis, eget volutpat mi leo vitae lacus. Etiam ante ante, posuere at augue non, lacinia ornare purus. Praesent vitae velit in enim congue maximus. Vivamus tincidunt fringilla neque. Curabitur fermentum justo nec sapien porttitor, ac ullamcorper nisi imperdiet. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed non orci vel eros egestas eleifend sit amet a diam. Duis mattis at ligula quis faucibus. Donec elementum lacus velit, a vehicula nunc gravida a. Phasellus eget nunc vehicula, varius velit a, maximus velit. Sed a suscipit nisi, non hendrerit felis. Proin mattis facilisis massa, quis elementum neque fringilla non.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "The second section starts with the headline above. Move cursor to the end of this text and press enter until next page is generated in continuous section break mode.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
148
demo/82-new-headers-new-section.ts
Normal file
148
demo/82-new-headers-new-section.ts
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// Example of using headers and footers in a new section
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Footer, Header, Packer, Paragraph, SectionType, Tab, TextRun } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document({
|
||||||
|
creator: "Creator",
|
||||||
|
title: "Title",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
default: new Header({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "HEADER PAGE ONE",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
footers: {
|
||||||
|
default: new Footer({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "FOOTER PAGE ONE",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer ac suscipit orci, in lobortis risus. Nulla vehicula rutrum finibus. Nullam consequat, magna in vehicula commodo, enim massa consectetur nisl, sit amet rutrum nunc ante vel lorem. Sed sit amet scelerisque velit. Proin non quam eget mauris aliquet posuere a sed orci. Proin posuere ante suscipit neque dignissim hendrerit. Pellentesque eget dapibus metus. Donec at mollis mauris. Vestibulum sit amet scelerisque nulla. Vivamus ipsum erat, tempor sed volutpat non, molestie at odio. Vivamus lectus ligula, finibus at mattis vitae, euismod sed tellus. Etiam neque massa, faucibus a fringilla nec, mollis at ex. Aliquam eget nibh tortor. Sed ut viverra libero. Nulla facilisis bibendum quam eget porttitor.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Sed eget nunc ac turpis facilisis volutpat. Duis eget arcu vitae neque porta hendrerit. Proin vel ante nulla. Duis congue efficitur dui. Suspendisse potenti. Aliquam aliquam nibh eu ipsum sagittis efficitur. Quisque sagittis metus dui, vitae suscipit tortor sollicitudin at. Suspendisse convallis, sem ac ornare condimentum, odio ipsum dapibus justo, a aliquam risus massa ut enim. Mauris vel placerat nibh. Ut iaculis vitae nibh at elementum. Quisque hendrerit et magna vitae mollis. Duis dictum euismod leo, at cursus risus sodales sed.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Sed gravida commodo felis, at aliquet risus volutpat ut. Nam nec ex eleifend tellus sodales volutpat nec ac nibh. Vestibulum pretium, leo vitae lobortis accumsan, urna libero euismod ante, consequat aliquam enim risus id nisl. Donec sagittis, justo eu luctus posuere, leo purus pellentesque turpis, eget volutpat mi leo vitae lacus. Etiam ante ante, posuere at augue non, lacinia ornare purus. Praesent vitae velit in enim congue maximus. Vivamus tincidunt fringilla neque. Curabitur fermentum justo nec sapien porttitor, ac ullamcorper nisi imperdiet. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed non orci vel eros egestas eleifend sit amet a diam. Duis mattis at ligula quis faucibus. Donec elementum lacus velit, a vehicula nunc gravida a. Phasellus eget nunc vehicula, varius velit a, maximus velit. Sed a suscipit nisi, non hendrerit felis. Proin mattis facilisis massa, quis elementum neque fringilla non.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Sed gravida commodo felis, at aliquet risus volutpat ut. Nam nec ex eleifend tellus sodales volutpat nec ac nibh. Vestibulum pretium, leo vitae lobortis accumsan, urna libero euismod ante, consequat aliquam enim risus id nisl. Donec sagittis, justo eu luctus posuere, leo purus pellentesque turpis, eget volutpat mi leo vitae lacus. Etiam ante ante, posuere at augue non, lacinia ornare purus. Praesent vitae velit in enim congue maximus. Vivamus tincidunt fringilla neque. Curabitur fermentum justo nec sapien porttitor, ac ullamcorper nisi imperdiet. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed non orci vel eros egestas eleifend sit amet a diam. Duis mattis at ligula quis faucibus. Donec elementum lacus velit, a vehicula nunc gravida a. Phasellus eget nunc vehicula, varius velit a, maximus velit. Sed a suscipit nisi, non hendrerit felis. Proin mattis facilisis massa, quis elementum neque fringilla non.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: {
|
||||||
|
after: 500,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "The first section ends after this paragraph.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
default: new Header({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "HEADER PAGE TWO AND FOLLOWING PAGES",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
footers: {
|
||||||
|
default: new Footer({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "FOOTER PAGE TWO AND FOLLOWING PAGES",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer ac suscipit orci, in lobortis risus. Nulla vehicula rutrum finibus. Nullam consequat, magna in vehicula commodo, enim massa consectetur nisl, sit amet rutrum nunc ante vel lorem. Sed sit amet scelerisque velit. Proin non quam eget mauris aliquet posuere a sed orci. Proin posuere ante suscipit neque dignissim hendrerit. Pellentesque eget dapibus metus. Donec at mollis mauris. Vestibulum sit amet scelerisque nulla. Vivamus ipsum erat, tempor sed volutpat non, molestie at odio. Vivamus lectus ligula, finibus at mattis vitae, euismod sed tellus. Etiam neque massa, faucibus a fringilla nec, mollis at ex. Aliquam eget nibh tortor. Sed ut viverra libero. Nulla facilisis bibendum quam eget porttitor.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Sed eget nunc ac turpis facilisis volutpat. Duis eget arcu vitae neque porta hendrerit. Proin vel ante nulla. Duis congue efficitur dui. Suspendisse potenti. Aliquam aliquam nibh eu ipsum sagittis efficitur. Quisque sagittis metus dui, vitae suscipit tortor sollicitudin at. Suspendisse convallis, sem ac ornare condimentum, odio ipsum dapibus justo, a aliquam risus massa ut enim. Mauris vel placerat nibh. Ut iaculis vitae nibh at elementum. Quisque hendrerit et magna vitae mollis. Duis dictum euismod leo, at cursus risus sodales sed.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Sed gravida commodo felis, at aliquet risus volutpat ut. Nam nec ex eleifend tellus sodales volutpat nec ac nibh. Vestibulum pretium, leo vitae lobortis accumsan, urna libero euismod ante, consequat aliquam enim risus id nisl. Donec sagittis, justo eu luctus posuere, leo purus pellentesque turpis, eget volutpat mi leo vitae lacus. Etiam ante ante, posuere at augue non, lacinia ornare purus. Praesent vitae velit in enim congue maximus. Vivamus tincidunt fringilla neque. Curabitur fermentum justo nec sapien porttitor, ac ullamcorper nisi imperdiet. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed non orci vel eros egestas eleifend sit amet a diam. Duis mattis at ligula quis faucibus. Donec elementum lacus velit, a vehicula nunc gravida a. Phasellus eget nunc vehicula, varius velit a, maximus velit. Sed a suscipit nisi, non hendrerit felis. Proin mattis facilisis massa, quis elementum neque fringilla non.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Sed gravida commodo felis, at aliquet risus volutpat ut. Nam nec ex eleifend tellus sodales volutpat nec ac nibh. Vestibulum pretium, leo vitae lobortis accumsan, urna libero euismod ante, consequat aliquam enim risus id nisl. Donec sagittis, justo eu luctus posuere, leo purus pellentesque turpis, eget volutpat mi leo vitae lacus. Etiam ante ante, posuere at augue non, lacinia ornare purus. Praesent vitae velit in enim congue maximus. Vivamus tincidunt fringilla neque. Curabitur fermentum justo nec sapien porttitor, ac ullamcorper nisi imperdiet. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed non orci vel eros egestas eleifend sit amet a diam. Duis mattis at ligula quis faucibus. Donec elementum lacus velit, a vehicula nunc gravida a. Phasellus eget nunc vehicula, varius velit a, maximus velit. Sed a suscipit nisi, non hendrerit felis. Proin mattis facilisis massa, quis elementum neque fringilla non.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "The second section starts with the headline above. Move cursor to the end of this text and press enter until next page is generated in continuous section break mode.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
69
demo/83-setting-languages.ts
Normal file
69
demo/83-setting-languages.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// 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 } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document({
|
||||||
|
styles: {
|
||||||
|
default: {
|
||||||
|
document: {
|
||||||
|
run: {
|
||||||
|
color: "ff0000",
|
||||||
|
language: {
|
||||||
|
value: "es-ES",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
paragraphStyles: [
|
||||||
|
{
|
||||||
|
id: "frenchNormal",
|
||||||
|
name: "French Normal",
|
||||||
|
basedOn: "Normal",
|
||||||
|
next: "Normal",
|
||||||
|
run: {
|
||||||
|
color: "999999",
|
||||||
|
italics: true,
|
||||||
|
language: {
|
||||||
|
value: "fr-FR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "koreanNormal",
|
||||||
|
name: "Korean Normal",
|
||||||
|
basedOn: "Normal",
|
||||||
|
next: "Normal",
|
||||||
|
run: {
|
||||||
|
color: "0000ff",
|
||||||
|
bold: true,
|
||||||
|
language: {
|
||||||
|
value: "ko-KR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
properties: {},
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
text: "Yo vivo en Granada, una ciudad pequeña que tiene monumentos muy importantes como la Alhambra. Aquí la comida es deliciosa y son famosos el gazpacho, el rebujito y el salmorejo.",
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
text: "Toute personne a droit à l'éducation. L'éducation doit être gratuite, au moins en ce qui concerne l'enseignement élémentaire et fondamental. L'enseignement élémentaire est obligatoire. L'enseignement technique et professionnel doit être généralisé; l'accès aux études supérieures doit être ouvert en pleine égalité à tous en fonction de leur mérite.",
|
||||||
|
style: "frenchNormal",
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
text: "대법관은 대법원장의 제청으로 국회의 동의를 얻어 대통령이 임명한다. 강화조약. 국가는 국민 모두의 생산 및 생활의 기반이 되는 국토의 효율적이고 균형있는 이용·개발과 보전을 위하여 법률이 정하는 바에 의하여 그에 관한 필요한 제한과 의무를 과할 수 있다, 국가는 청원에 대하여 심사할 의무를 진다.",
|
||||||
|
style: "koreanNormal",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
60
demo/84-positional-tabs.ts
Normal file
60
demo/84-positional-tabs.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// Simple example apply positional tabs to a document
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import {
|
||||||
|
Document,
|
||||||
|
Packer,
|
||||||
|
Paragraph,
|
||||||
|
PositionalTab,
|
||||||
|
Tab,
|
||||||
|
TextRun,
|
||||||
|
PositionalTabAlignment,
|
||||||
|
PositionalTabRelativeTo,
|
||||||
|
PositionalTabLeader,
|
||||||
|
} from "../build";
|
||||||
|
|
||||||
|
const doc = new Document({
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
properties: {},
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun("Full name"),
|
||||||
|
new TextRun({
|
||||||
|
children: [
|
||||||
|
new PositionalTab({
|
||||||
|
alignment: PositionalTabAlignment.RIGHT,
|
||||||
|
relativeTo: PositionalTabRelativeTo.MARGIN,
|
||||||
|
leader: PositionalTabLeader.DOT,
|
||||||
|
}),
|
||||||
|
"John Doe",
|
||||||
|
],
|
||||||
|
bold: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun("Hello World"),
|
||||||
|
new TextRun({
|
||||||
|
children: [
|
||||||
|
new PositionalTab({
|
||||||
|
alignment: PositionalTabAlignment.CENTER,
|
||||||
|
relativeTo: PositionalTabRelativeTo.INDENT,
|
||||||
|
leader: PositionalTabLeader.HYPHEN,
|
||||||
|
}),
|
||||||
|
"Foo bar",
|
||||||
|
],
|
||||||
|
bold: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
155
demo/85-template-document.ts
Normal file
155
demo/85-template-document.ts
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
// Patch a document with patches
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import {
|
||||||
|
ExternalHyperlink,
|
||||||
|
HeadingLevel,
|
||||||
|
ImageRun,
|
||||||
|
Paragraph,
|
||||||
|
patchDocument,
|
||||||
|
PatchType,
|
||||||
|
Table,
|
||||||
|
TableCell,
|
||||||
|
TableRow,
|
||||||
|
TextDirection,
|
||||||
|
TextRun,
|
||||||
|
VerticalAlign,
|
||||||
|
} from "../build";
|
||||||
|
|
||||||
|
patchDocument(fs.readFileSync("demo/assets/simple-template.docx"), {
|
||||||
|
patches: {
|
||||||
|
name: {
|
||||||
|
type: PatchType.PARAGRAPH,
|
||||||
|
children: [new TextRun("Sir. "), new TextRun("John Doe"), new TextRun("(The Conqueror)")],
|
||||||
|
},
|
||||||
|
table_heading_1: {
|
||||||
|
type: PatchType.PARAGRAPH,
|
||||||
|
children: [new TextRun("Heading wow!")],
|
||||||
|
},
|
||||||
|
item_1: {
|
||||||
|
type: PatchType.PARAGRAPH,
|
||||||
|
children: [
|
||||||
|
new TextRun("#657"),
|
||||||
|
new ExternalHyperlink({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "BBC News Link",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
link: "https://www.bbc.co.uk/news",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
paragraph_replace: {
|
||||||
|
type: PatchType.DOCUMENT,
|
||||||
|
children: [
|
||||||
|
new Paragraph("Lorem ipsum paragraph"),
|
||||||
|
new Paragraph("Another paragraph"),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun("This is a "),
|
||||||
|
new ExternalHyperlink({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Google Link",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
link: "https://www.google.co.uk",
|
||||||
|
}),
|
||||||
|
new ImageRun({ data: fs.readFileSync("./demo/images/dog.png"), transformation: { width: 100, height: 100 } }),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
header_adjective: {
|
||||||
|
type: PatchType.PARAGRAPH,
|
||||||
|
children: [new TextRun("Delightful Header")],
|
||||||
|
},
|
||||||
|
footer_text: {
|
||||||
|
type: PatchType.PARAGRAPH,
|
||||||
|
children: [
|
||||||
|
new TextRun("replaced just as"),
|
||||||
|
new TextRun(" well"),
|
||||||
|
new ExternalHyperlink({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "BBC News Link",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
link: "https://www.bbc.co.uk/news",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
image_test: {
|
||||||
|
type: PatchType.PARAGRAPH,
|
||||||
|
children: [new ImageRun({ data: fs.readFileSync("./demo/images/image1.jpeg"), transformation: { width: 100, height: 100 } })],
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
type: PatchType.DOCUMENT,
|
||||||
|
children: [
|
||||||
|
new Table({
|
||||||
|
rows: [
|
||||||
|
new TableRow({
|
||||||
|
children: [
|
||||||
|
new TableCell({
|
||||||
|
children: [new Paragraph({}), new Paragraph({})],
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
children: [new Paragraph({}), new Paragraph({})],
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
children: [new Paragraph({ text: "bottom to top" }), new Paragraph({})],
|
||||||
|
textDirection: TextDirection.BOTTOM_TO_TOP_LEFT_TO_RIGHT,
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
children: [new Paragraph({ text: "top to bottom" }), new Paragraph({})],
|
||||||
|
textDirection: TextDirection.TOP_TO_BOTTOM_RIGHT_TO_LEFT,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new TableRow({
|
||||||
|
children: [
|
||||||
|
new TableCell({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
text: "Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah",
|
||||||
|
heading: HeadingLevel.HEADING_1,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
text: "This text should be in the middle of the cell",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
text: "Text above should be vertical from bottom to top",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
text: "Text above should be vertical from top to bottom",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).then((doc) => {
|
||||||
|
fs.writeFileSync("My Document.docx", doc);
|
||||||
|
});
|
20
demo/86-generate-template.ts
Normal file
20
demo/86-generate-template.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Generate a template 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({
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("{{template}}")],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
15
demo/87-template-document.ts
Normal file
15
demo/87-template-document.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Patch a document with patches
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { patchDocument, PatchType, TextRun } from "../build";
|
||||||
|
|
||||||
|
patchDocument(fs.readFileSync("demo/assets/simple-template-2.docx"), {
|
||||||
|
patches: {
|
||||||
|
name: {
|
||||||
|
type: PatchType.PARAGRAPH,
|
||||||
|
children: [new TextRun("Max")],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).then((doc) => {
|
||||||
|
fs.writeFileSync("My Document.docx", doc);
|
||||||
|
});
|
31
demo/88-template-document.ts
Normal file
31
demo/88-template-document.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Patch a document with patches
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { IPatch, patchDocument, PatchType, TextRun } from "../build";
|
||||||
|
|
||||||
|
export const font = "Trebuchet MS";
|
||||||
|
export const getPatches = (fields: { [key: string]: string }) => {
|
||||||
|
const patches: { [key: string]: IPatch } = {};
|
||||||
|
|
||||||
|
for (const field in fields) {
|
||||||
|
patches[field] = {
|
||||||
|
type: PatchType.PARAGRAPH,
|
||||||
|
children: [new TextRun({ text: fields[field], font })],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return patches;
|
||||||
|
};
|
||||||
|
|
||||||
|
const patches = getPatches({
|
||||||
|
name: "Mr",
|
||||||
|
table_heading_1: "John",
|
||||||
|
item_1: "Doe",
|
||||||
|
paragraph_replace: "Lorem ipsum paragraph",
|
||||||
|
});
|
||||||
|
|
||||||
|
patchDocument(fs.readFileSync("demo/assets/simple-template.docx"), {
|
||||||
|
patches,
|
||||||
|
}).then((doc) => {
|
||||||
|
fs.writeFileSync("My Document.docx", doc);
|
||||||
|
});
|
29
demo/89-template-document.ts
Normal file
29
demo/89-template-document.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Patch a document with patches
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { IPatch, patchDocument, PatchType, TextRun } from "../build";
|
||||||
|
|
||||||
|
export const font = "Trebuchet MS";
|
||||||
|
export const getPatches = (fields: { [key: string]: string }) => {
|
||||||
|
const patches: { [key: string]: IPatch } = {};
|
||||||
|
|
||||||
|
for (const field in fields) {
|
||||||
|
patches[field] = {
|
||||||
|
type: PatchType.PARAGRAPH,
|
||||||
|
children: [new TextRun({ text: fields[field], font })],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return patches;
|
||||||
|
};
|
||||||
|
|
||||||
|
const patches = getPatches({
|
||||||
|
salutation: "Mr.",
|
||||||
|
"first-name": "John",
|
||||||
|
});
|
||||||
|
|
||||||
|
patchDocument(fs.readFileSync("demo/assets/simple-template-3.docx"), {
|
||||||
|
patches,
|
||||||
|
}).then((doc) => {
|
||||||
|
fs.writeFileSync("My Document.docx", doc);
|
||||||
|
});
|
BIN
demo/assets/generated-template.docx
Normal file
BIN
demo/assets/generated-template.docx
Normal file
Binary file not shown.
BIN
demo/assets/simple-template-2.docx
Normal file
BIN
demo/assets/simple-template-2.docx
Normal file
Binary file not shown.
BIN
demo/assets/simple-template-3.docx
Normal file
BIN
demo/assets/simple-template-3.docx
Normal file
Binary file not shown.
BIN
demo/assets/simple-template.docx
Normal file
BIN
demo/assets/simple-template.docx
Normal file
Binary file not shown.
@ -1,10 +1,10 @@
|
|||||||
<img src="https://i.imgur.com/37uBGhO.gif" alt="drawing" style="width:200px;"/>
|
<img src="https://i.imgur.com/37uBGhO.gif" alt="drawing" style="width:200px;"/>
|
||||||
|
|
||||||
> Easily generate .docx files with JS/TS. Works for Node and on the Browser. :100:
|
> Easily generate and modify .docx files with JS/TS. Works for Node and on the Browser. :100:
|
||||||
|
|
||||||
- Simple, declarative API
|
- Simple, declarative API
|
||||||
- 60+ usage examples
|
- 80+ usage examples
|
||||||
- Battle tested, mature, 99%+ coverage
|
- Battle tested, mature, 99.9%+ coverage
|
||||||
|
|
||||||
[GitHub](https://github.com/dolanmiu/docx)
|
[GitHub](https://github.com/dolanmiu/docx)
|
||||||
[Get Started](#Welcome)
|
[Get Started](#Welcome)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
- [Getting Started](/)
|
- [Getting Started](/)
|
||||||
|
|
||||||
- [Examples](https://github.com/dolanmiu/docx/tree/master/demo)
|
- Examples
|
||||||
|
|
||||||
|
- [Demos](https://github.com/dolanmiu/docx/tree/master/demo)
|
||||||
|
|
||||||
- API
|
- API
|
||||||
|
|
||||||
@ -20,7 +22,7 @@
|
|||||||
- [Hyperlinks](usage/hyperlinks.md)
|
- [Hyperlinks](usage/hyperlinks.md)
|
||||||
- [Numbering](usage/numbering.md)
|
- [Numbering](usage/numbering.md)
|
||||||
- [Tables](usage/tables.md)
|
- [Tables](usage/tables.md)
|
||||||
- [Tab Stops](usage/tab-stops.md)
|
- [Tabs](usage/tabs.md)
|
||||||
- [Table of Contents](usage/table-of-contents.md)
|
- [Table of Contents](usage/table-of-contents.md)
|
||||||
- [Page Numbers](usage/page-numbers.md)
|
- [Page Numbers](usage/page-numbers.md)
|
||||||
- [Change Tracking](usage/change-tracking.md)
|
- [Change Tracking](usage/change-tracking.md)
|
||||||
@ -36,6 +38,10 @@
|
|||||||
|
|
||||||
- [Packers](usage/packers.md)
|
- [Packers](usage/packers.md)
|
||||||
|
|
||||||
|
- Modifying Existing Documents
|
||||||
|
|
||||||
|
- [Patcher](usage/patcher.md)
|
||||||
|
|
||||||
- Utility
|
- Utility
|
||||||
|
|
||||||
- [Convenience functions](usage/convenience-functions.md)
|
- [Convenience functions](usage/convenience-functions.md)
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
# Contribution Guidelines
|
# Contribution Guidelines
|
||||||
|
|
||||||
- Include documentation reference(s) at the top of each file:
|
- Include documentation reference(s) at the top of each file as a comment. For example:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// http://officeopenxml.com/WPdocument.php
|
// http://officeopenxml.com/WPdocument.php
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<!-- cSpell:ignore datypic -->
|
||||||
|
It can be a link to `officeopenxml.com` or `datypic.com` etc.
|
||||||
|
It could also be a reference to the official ECMA-376 standard: https://www.ecma-international.org/publications-and-standards/standards/ecma-376/
|
||||||
|
|
||||||
|
- Include a portion of the schema as a comment for cross reference. For example:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// <xsd:element name="tbl" type="CT_Tbl" minOccurs="0" maxOccurs="1"/>
|
||||||
|
```
|
||||||
|
|
||||||
- Follow Prettier standards, and consider using the [Prettier VSCode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) plugin.
|
- Follow Prettier standards, and consider using the [Prettier VSCode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) plugin.
|
||||||
|
|
||||||
- Follow the `ESLint` rules
|
- Follow the `ESLint` rules
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
<script src="https://unpkg.com/docsify-copy-code@2"></script>
|
<script src="https://unpkg.com/docsify-copy-code@2"></script>
|
||||||
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
|
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
|
||||||
<script src="//unpkg.com/prismjs/components/prism-typescript.min.js"></script>
|
<script src="//unpkg.com/prismjs/components/prism-typescript.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/docsify-sign-off-sheet@1.0.0/dist/index.iife.js"></script>
|
||||||
<script src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js" type="text/javascript"></script>
|
<script src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js" type="text/javascript"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
42
docs/usage/footnotes.md
Normal file
42
docs/usage/footnotes.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Footnotes
|
||||||
|
|
||||||
|
!> Footnotes requires an understanding of [Sections](usage/sections.md).
|
||||||
|
|
||||||
|
Use footnotes and endnotes to explain, comment on, or provide references to something in a document. Usually, footnotes appear at the bottom of the page.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const doc = new Document({
|
||||||
|
footnotes: {
|
||||||
|
1: { children: [new Paragraph("Foo"), new Paragraph("Bar")] },
|
||||||
|
2: { children: [new Paragraph("Test")] },
|
||||||
|
},
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
children: ["Hello"],
|
||||||
|
}),
|
||||||
|
new FootnoteReferenceRun(1),
|
||||||
|
new TextRun({
|
||||||
|
children: [" World!"],
|
||||||
|
}),
|
||||||
|
new FootnoteReferenceRun(2),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Footnotes requires an entry into the `footnotes` array in the `Document` constructor, and a `FootnoteReferenceRun` in the `Paragraph` constructor.
|
||||||
|
|
||||||
|
`footnotes` is an object of number to `Footnote` objects. The number is the reference number, and the `Footnote` object is the content of the footnote. The `Footnote` object has a `children` property, which is an array of `Paragraph` objects.
|
||||||
|
|
||||||
|
`FootnoteReferenceRun` is a `Run` object, which are added to `Paragraph`s. It takes a number as a parameter, which is the reference number of the footnote.
|
@ -305,4 +305,12 @@ Example: https://github.com/dolanmiu/docx/blob/master/demo/15-page-break-before.
|
|||||||
|
|
||||||
## Page break control
|
## 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)
|
Paragraphs have `keepLines` and `keepNext` properties 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.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const paragraph = new Paragraph({
|
||||||
|
text: "Stay on the same page",
|
||||||
|
keepLines: true,
|
||||||
|
keepNext: true,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
94
docs/usage/patcher.md
Normal file
94
docs/usage/patcher.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# Patcher
|
||||||
|
|
||||||
|
The patcher allows you to modify existing documents, and add new content to them.
|
||||||
|
|
||||||
|
!> The Patcher requires an understanding of [Paragraphs](usage/paragraph.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { patchDocument } from "docx";
|
||||||
|
|
||||||
|
patchDocument(fs.readFileSync("My Document.docx"), {
|
||||||
|
patches: {
|
||||||
|
// Patches here
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Patches
|
||||||
|
|
||||||
|
The patcher takes in a `patches` object, which is a map of `string` to `Patch`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface Patch {
|
||||||
|
type: PatchType;
|
||||||
|
children: FileChild[] | ParagraphChild[];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Property | Type | Notes | Possible Values |
|
||||||
|
| -------- | --------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| type | `PatchType` | Required | `DOCUMENT`, `PARAGRAPH` |
|
||||||
|
| children | `FileChild[] or ParagraphChild[]` | Required | The contents to replace with. A `FileChild` is a `Paragraph` or `Table`, whereas a `ParagraphChild` is typical `Paragraph` children. |
|
||||||
|
|
||||||
|
### How to patch existing document
|
||||||
|
|
||||||
|
1. Open your existing word document in your favorite Word Processor
|
||||||
|
2. Write tags in the document where you want to patch in a mustache style notation. For example, `{{my_patch}}` and `{{my_second_patch}}`.
|
||||||
|
3. Run the patcher with the patches as a key value pair.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
### Word Document
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Patcher
|
||||||
|
|
||||||
|
?> Notice how there is no handlebar notation in the key.
|
||||||
|
|
||||||
|
The patch can be as simple as a string, or as complex as a table. Images, hyperlinks, and other complex elements within the `docx` library are also supported.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
patchDocument(fs.readFileSync("My Document.docx"), {
|
||||||
|
patches: {
|
||||||
|
my_patch: {
|
||||||
|
type: PatchType.PARAGRAPH,
|
||||||
|
children: [new TextRun("Sir. "), new TextRun("John Doe"), new TextRun("(The Conqueror)")],
|
||||||
|
},
|
||||||
|
my_second_patch: {
|
||||||
|
type: PatchType.DOCUMENT,
|
||||||
|
children: [
|
||||||
|
new Paragraph("Lorem ipsum paragraph"),
|
||||||
|
new Paragraph("Another paragraph"),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun("This is a "),
|
||||||
|
new ExternalHyperlink({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Google Link",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
link: "https://www.google.co.uk",
|
||||||
|
}),
|
||||||
|
new ImageRun({ data: fs.readFileSync("./demo/images/dog.png"), transformation: { width: 100, height: 100 } }),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/85-template-document.ts_
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/85-template-document.ts ":include :type=code typescript")
|
@ -1,121 +0,0 @@
|
|||||||
# 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**: The unit of measurement for a tab stop is in [DXA](https://stackoverflow.com/questions/14360183/default-wordml-unit-measurement-pixel-or-point-or-inches)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Simply declare the tab stops on the paragraph, as shown below. Use the tab character `\t` to indicate the tab position within the `text` property of a `TextRun`. Adding multiple `tabStops` will mean you can add additional `\t` characters until the desired `tabStop` is selected. Example is shown below.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const paragraph = new Paragraph({
|
|
||||||
children: [new TextRun({ text: "Hey everyone", bold: true}), new TextRun("\t11th November 1999")],
|
|
||||||
tabStops: [
|
|
||||||
{
|
|
||||||
type: TabStopType.RIGHT,
|
|
||||||
position: TabStopPosition.MAX,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
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. Not ideal!
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const paragraph = new Paragraph({
|
|
||||||
children: [new TextRun("\t\tSecond tab stop here I come!")],
|
|
||||||
tabStops: [
|
|
||||||
{
|
|
||||||
type: TabStopType.RIGHT,
|
|
||||||
position: TabStopPosition.MAX,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: TabStopType.LEFT,
|
|
||||||
position: 1000,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
The above shows the use of two tab stops, and how to select/use it.
|
|
||||||
|
|
||||||
You can add multiple tab stops of the same `type` too.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const paragraph = new Paragraph({
|
|
||||||
children: [new TextRun("Multiple \ttab \tstops!")],
|
|
||||||
tabStops: [
|
|
||||||
{
|
|
||||||
type: TabStopType.RIGHT,
|
|
||||||
position: TabStopPosition.MAX,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: TabStopType.RIGHT,
|
|
||||||
position: 1000,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Left Tab Stop
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const paragraph = new Paragraph({
|
|
||||||
tabStops: [
|
|
||||||
{
|
|
||||||
type: TabStopType.LEFT,
|
|
||||||
position: 2268,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
2268 is the distance from the left side.
|
|
||||||
|
|
||||||
## Center Tab Stop
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const paragraph = new Paragraph({
|
|
||||||
tabStops: [
|
|
||||||
{
|
|
||||||
type: TabStopType.CENTER,
|
|
||||||
position: 2268,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
2268 is the distance from the center.
|
|
||||||
|
|
||||||
## Right Tab Stop
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const paragraph = new Paragraph({
|
|
||||||
tabStops: [
|
|
||||||
{
|
|
||||||
type: TabStopType.RIGHT,
|
|
||||||
position: 2268,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
2268 is the distance from the left side.
|
|
||||||
|
|
||||||
## Max Right Tab Stop
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const paragraph = new Paragraph({
|
|
||||||
tabStops: [
|
|
||||||
{
|
|
||||||
type: TabStopType.RIGHT,
|
|
||||||
position: TabStopPosition.MAX,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
184
docs/usage/tabs.md
Normal file
184
docs/usage/tabs.md
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
# Tabs and Tab Stops
|
||||||
|
|
||||||
|
## 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**: The unit of measurement for a tab stop is in [DXA](https://stackoverflow.com/questions/14360183/default-wordml-unit-measurement-pixel-or-point-or-inches)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Simply declare the tab stops on the paragraph, as shown below. Use the tab character `\t` or add the `new Tab()` child to indicate the tab position within the `text` property of a `TextRun`. Adding multiple `tabStops` will mean you can add additional `\t` characters until the desired `tabStop` is selected. Example is shown below.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const paragraph = new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({ text: "Hey everyone", bold: true }),
|
||||||
|
new TextRun("\t11th November 1999"),
|
||||||
|
new TextRun({
|
||||||
|
children: [new Tab(), "11th November 1999"],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
tabStops: [
|
||||||
|
{
|
||||||
|
type: TabStopType.RIGHT,
|
||||||
|
position: TabStopPosition.MAX,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
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. Not ideal!
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const paragraph = new Paragraph({
|
||||||
|
children: [new TextRun("\t\tSecond tab stop here I come!")],
|
||||||
|
tabStops: [
|
||||||
|
{
|
||||||
|
type: TabStopType.RIGHT,
|
||||||
|
position: TabStopPosition.MAX,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: TabStopType.LEFT,
|
||||||
|
position: 1000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The above shows the use of two tab stops, and how to select/use it.
|
||||||
|
|
||||||
|
You can add multiple tab stops of the same `type` too.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const paragraph = new Paragraph({
|
||||||
|
children: [new TextRun("Multiple \ttab \tstops!")],
|
||||||
|
tabStops: [
|
||||||
|
{
|
||||||
|
type: TabStopType.RIGHT,
|
||||||
|
position: TabStopPosition.MAX,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: TabStopType.RIGHT,
|
||||||
|
position: 1000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const paragraph = new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
children: ["Multiple ", new Tab(), "tab ", new Tab(), "stops!"],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
tabStops: [
|
||||||
|
{
|
||||||
|
type: TabStopType.RIGHT,
|
||||||
|
position: TabStopPosition.MAX,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: TabStopType.RIGHT,
|
||||||
|
position: 1000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Left Tab Stop
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const paragraph = new Paragraph({
|
||||||
|
tabStops: [
|
||||||
|
{
|
||||||
|
type: TabStopType.LEFT,
|
||||||
|
position: 2268,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
2268 is the distance from the left side.
|
||||||
|
|
||||||
|
### Center Tab Stop
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const paragraph = new Paragraph({
|
||||||
|
tabStops: [
|
||||||
|
{
|
||||||
|
type: TabStopType.CENTER,
|
||||||
|
position: 2268,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
2268 is the distance from the center.
|
||||||
|
|
||||||
|
### Right Tab Stop
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const paragraph = new Paragraph({
|
||||||
|
tabStops: [
|
||||||
|
{
|
||||||
|
type: TabStopType.RIGHT,
|
||||||
|
position: 2268,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
2268 is the distance from the left side.
|
||||||
|
|
||||||
|
### Max Right Tab Stop
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const paragraph = new Paragraph({
|
||||||
|
tabStops: [
|
||||||
|
{
|
||||||
|
type: TabStopType.RIGHT,
|
||||||
|
position: TabStopPosition.MAX,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Positional Tabs
|
||||||
|
|
||||||
|
> Positional tab allow you to create a tab stop that is relative to the margin, or the page. This is useful if you want to create a table of contents, or a table of figures.
|
||||||
|
|
||||||
|
They are easier to use than the normal tab stops, as you can use the `PositionalTab` class to create a tab stop, and then add the text to the `TextRun` children. Useful for most cases.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun("Full name"),
|
||||||
|
new TextRun({
|
||||||
|
children: [
|
||||||
|
new PositionalTab({
|
||||||
|
alignment: PositionalTabAlignment.RIGHT,
|
||||||
|
relativeTo: PositionalTabRelativeTo.MARGIN,
|
||||||
|
leader: PositionalTabLeader.DOT,
|
||||||
|
}),
|
||||||
|
"John Doe",
|
||||||
|
],
|
||||||
|
bold: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Option | Type | Description | Possible Values |
|
||||||
|
| ---------- | ------------------------- | ------------------------------------- | ------------------------------------------------------------- |
|
||||||
|
| alignment | `PositionalTabAlignment` | The alignment of the tab stop | `LEFT`, `RIGHT`, `CENTER` |
|
||||||
|
| relativeTo | `PositionalTabRelativeTo` | The relative position of the tab stop | `MARGIN`, `INDENT` |
|
||||||
|
| leader | `PositionalTabLeader` | The leader of the tab stop | `NONE`, `DOT`, `HYPHEN`, `UNDERSCORE`, `MIDDLE_DOT`, `EQUALS` |
|
3669
package-lock.json
generated
3669
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
27
package.json
27
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "docx",
|
"name": "docx",
|
||||||
"version": "7.8.2",
|
"version": "8.0.2",
|
||||||
"description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
|
"description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
|
||||||
"main": "build/index.js",
|
"main": "build/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -28,9 +28,7 @@
|
|||||||
"lint"
|
"lint"
|
||||||
],
|
],
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"build"
|
||||||
"build",
|
|
||||||
"template"
|
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -52,7 +50,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.0.0",
|
||||||
"jszip": "^3.1.5",
|
"jszip": "^3.1.5",
|
||||||
"nanoid": "^3.1.20",
|
"nanoid": "^3.3.4",
|
||||||
"xml": "^1.0.1",
|
"xml": "^1.0.1",
|
||||||
"xml-js": "^1.6.8"
|
"xml-js": "^1.6.8"
|
||||||
},
|
},
|
||||||
@ -61,14 +59,14 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/dolanmiu/docx/issues"
|
"url": "https://github.com/dolanmiu/docx/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/dolanmiu/docx#readme",
|
"homepage": "https://docx.js.org",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.2.15",
|
"@types/chai": "^4.2.15",
|
||||||
"@types/glob": "^8.0.0",
|
"@types/chai-as-promised": "^7.1.5",
|
||||||
"@types/mocha": "^10.0.0",
|
"@types/mocha": "^10.0.0",
|
||||||
"@types/prompt": "^1.1.1",
|
"@types/prompt": "^1.1.1",
|
||||||
"@types/request-promise": "^4.1.42",
|
"@types/request-promise": "^4.1.42",
|
||||||
"@types/shelljs": "^0.8.9",
|
"@types/shelljs": "^0.8.11",
|
||||||
"@types/sinon": "^10.0.0",
|
"@types/sinon": "^10.0.0",
|
||||||
"@types/unzipper": "^0.10.4",
|
"@types/unzipper": "^0.10.4",
|
||||||
"@types/webpack": "^5.0.0",
|
"@types/webpack": "^5.0.0",
|
||||||
@ -76,16 +74,17 @@
|
|||||||
"@typescript-eslint/parser": "^5.36.1",
|
"@typescript-eslint/parser": "^5.36.1",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"chai": "^4.3.6",
|
"chai": "^4.3.6",
|
||||||
|
"chai-as-promised": "^7.1.1",
|
||||||
"cspell": "^6.2.2",
|
"cspell": "^6.2.2",
|
||||||
"docsify-cli": "^4.3.0",
|
"docsify-cli": "^4.3.0",
|
||||||
"eslint": "^8.23.0",
|
"eslint": "^8.23.0",
|
||||||
"eslint-plugin-functional": "^4.3.1",
|
"eslint-plugin-functional": "^5.0.1",
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-jsdoc": "^39.3.6",
|
"eslint-plugin-jsdoc": "^40.0.0",
|
||||||
"eslint-plugin-no-null": "^1.0.2",
|
"eslint-plugin-no-null": "^1.0.2",
|
||||||
"eslint-plugin-prefer-arrow": "^1.2.3",
|
"eslint-plugin-prefer-arrow": "^1.2.3",
|
||||||
"eslint-plugin-unicorn": "^45.0.0",
|
"eslint-plugin-unicorn": "^46.0.0",
|
||||||
"glob": "^8.0.1",
|
"glob": "^9.3.0",
|
||||||
"jszip": "^3.1.5",
|
"jszip": "^3.1.5",
|
||||||
"mocha": "^10.0.0",
|
"mocha": "^10.0.0",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
@ -96,7 +95,7 @@
|
|||||||
"replace-in-file": "^6.2.0",
|
"replace-in-file": "^6.2.0",
|
||||||
"request": "^2.88.0",
|
"request": "^2.88.0",
|
||||||
"request-promise": "^4.2.2",
|
"request-promise": "^4.2.2",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^4.0.4",
|
||||||
"shelljs": "^0.8.4",
|
"shelljs": "^0.8.4",
|
||||||
"sinon": "^15.0.0",
|
"sinon": "^15.0.0",
|
||||||
"stream-browserify": "^3.0.0",
|
"stream-browserify": "^3.0.0",
|
||||||
@ -105,7 +104,7 @@
|
|||||||
"tsconfig-paths": "^4.0.0",
|
"tsconfig-paths": "^4.0.0",
|
||||||
"tsconfig-paths-webpack-plugin": "^4.0.0",
|
"tsconfig-paths-webpack-plugin": "^4.0.0",
|
||||||
"typedoc": "^0.23.2",
|
"typedoc": "^0.23.2",
|
||||||
"typescript": "4.9.4",
|
"typescript": "5.0.3",
|
||||||
"unzipper": "^0.10.11",
|
"unzipper": "^0.10.11",
|
||||||
"webpack": "^5.28.0",
|
"webpack": "^5.28.0",
|
||||||
"webpack-cli": "^5.0.0"
|
"webpack-cli": "^5.0.0"
|
||||||
|
@ -9,7 +9,7 @@ for (const file of files) {
|
|||||||
from: /"@[a-z/-]*"/gi,
|
from: /"@[a-z/-]*"/gi,
|
||||||
to: (match) => {
|
to: (match) => {
|
||||||
const matchSlug = match.replace(/['"]+/g, "").replace(/[@]+/g, "").trim();
|
const matchSlug = match.replace(/['"]+/g, "").replace(/[@]+/g, "").trim();
|
||||||
const levelCount = file.split("/").length - 2;
|
const levelCount = file.split(/[\/\\]/).length - 2;
|
||||||
const backLevels = Array(levelCount).fill("../").join("");
|
const backLevels = Array(levelCount).fill("../").join("");
|
||||||
|
|
||||||
return `"${backLevels}${matchSlug}"`;
|
return `"${backLevels}${matchSlug}"`;
|
||||||
|
@ -2,7 +2,7 @@ import { BaseXmlComponent, IContext, IXmlableObject } from "@file/xml-components
|
|||||||
|
|
||||||
export class Formatter {
|
export class Formatter {
|
||||||
// tslint:disable-next-line: no-object-literal-type-assertion
|
// tslint:disable-next-line: no-object-literal-type-assertion
|
||||||
public format(input: BaseXmlComponent, context: IContext = {} as IContext): IXmlableObject {
|
public format(input: BaseXmlComponent, context: IContext = { stack: [] } as unknown as IContext): IXmlableObject {
|
||||||
const output = input.prepForXml(context);
|
const output = input.prepForXml(context);
|
||||||
|
|
||||||
if (output) {
|
if (output) {
|
||||||
|
@ -59,9 +59,8 @@ export class Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const data of file.Media.Array) {
|
for (const { stream, fileName } of file.Media.Array) {
|
||||||
const mediaData = data.stream;
|
zip.file(`word/media/${fileName}`, stream);
|
||||||
zip.file(`word/media/${data.fileName}`, mediaData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return zip;
|
return zip;
|
||||||
@ -74,6 +73,7 @@ export class Compiler {
|
|||||||
this.formatter.format(file.Document.View, {
|
this.formatter.format(file.Document.View, {
|
||||||
viewWrapper: file.Document,
|
viewWrapper: file.Document,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -100,6 +100,7 @@ export class Compiler {
|
|||||||
this.formatter.format(file.Document.Relationships, {
|
this.formatter.format(file.Document.Relationships, {
|
||||||
viewWrapper: file.Document,
|
viewWrapper: file.Document,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -125,6 +126,7 @@ export class Compiler {
|
|||||||
this.formatter.format(file.Styles, {
|
this.formatter.format(file.Styles, {
|
||||||
viewWrapper: file.Document,
|
viewWrapper: file.Document,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -144,6 +146,7 @@ export class Compiler {
|
|||||||
this.formatter.format(file.CoreProperties, {
|
this.formatter.format(file.CoreProperties, {
|
||||||
viewWrapper: file.Document,
|
viewWrapper: file.Document,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -160,6 +163,7 @@ export class Compiler {
|
|||||||
this.formatter.format(file.Numbering, {
|
this.formatter.format(file.Numbering, {
|
||||||
viewWrapper: file.Document,
|
viewWrapper: file.Document,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -176,6 +180,7 @@ export class Compiler {
|
|||||||
this.formatter.format(file.FileRelationships, {
|
this.formatter.format(file.FileRelationships, {
|
||||||
viewWrapper: file.Document,
|
viewWrapper: file.Document,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -191,6 +196,7 @@ export class Compiler {
|
|||||||
this.formatter.format(headerWrapper.View, {
|
this.formatter.format(headerWrapper.View, {
|
||||||
viewWrapper: headerWrapper,
|
viewWrapper: headerWrapper,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -214,6 +220,7 @@ export class Compiler {
|
|||||||
this.formatter.format(headerWrapper.Relationships, {
|
this.formatter.format(headerWrapper.Relationships, {
|
||||||
viewWrapper: headerWrapper,
|
viewWrapper: headerWrapper,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -230,6 +237,7 @@ export class Compiler {
|
|||||||
this.formatter.format(footerWrapper.View, {
|
this.formatter.format(footerWrapper.View, {
|
||||||
viewWrapper: footerWrapper,
|
viewWrapper: footerWrapper,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -253,6 +261,7 @@ export class Compiler {
|
|||||||
this.formatter.format(footerWrapper.Relationships, {
|
this.formatter.format(footerWrapper.Relationships, {
|
||||||
viewWrapper: footerWrapper,
|
viewWrapper: footerWrapper,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -269,6 +278,7 @@ export class Compiler {
|
|||||||
this.formatter.format(headerWrapper.View, {
|
this.formatter.format(headerWrapper.View, {
|
||||||
viewWrapper: headerWrapper,
|
viewWrapper: headerWrapper,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -293,6 +303,7 @@ export class Compiler {
|
|||||||
this.formatter.format(footerWrapper.View, {
|
this.formatter.format(footerWrapper.View, {
|
||||||
viewWrapper: footerWrapper,
|
viewWrapper: footerWrapper,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -317,6 +328,7 @@ export class Compiler {
|
|||||||
this.formatter.format(file.ContentTypes, {
|
this.formatter.format(file.ContentTypes, {
|
||||||
viewWrapper: file.Document,
|
viewWrapper: file.Document,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -332,6 +344,7 @@ export class Compiler {
|
|||||||
this.formatter.format(file.CustomProperties, {
|
this.formatter.format(file.CustomProperties, {
|
||||||
viewWrapper: file.Document,
|
viewWrapper: file.Document,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -348,6 +361,7 @@ export class Compiler {
|
|||||||
this.formatter.format(file.AppProperties, {
|
this.formatter.format(file.AppProperties, {
|
||||||
viewWrapper: file.Document,
|
viewWrapper: file.Document,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -363,7 +377,8 @@ export class Compiler {
|
|||||||
data: xml(
|
data: xml(
|
||||||
this.formatter.format(file.FootNotes.View, {
|
this.formatter.format(file.FootNotes.View, {
|
||||||
viewWrapper: file.FootNotes,
|
viewWrapper: file.FootNotes,
|
||||||
file: file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -378,7 +393,8 @@ export class Compiler {
|
|||||||
data: xml(
|
data: xml(
|
||||||
this.formatter.format(file.FootNotes.Relationships, {
|
this.formatter.format(file.FootNotes.Relationships, {
|
||||||
viewWrapper: file.FootNotes,
|
viewWrapper: file.FootNotes,
|
||||||
file: file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -394,6 +410,7 @@ export class Compiler {
|
|||||||
this.formatter.format(file.Settings, {
|
this.formatter.format(file.Settings, {
|
||||||
viewWrapper: file.Document,
|
viewWrapper: file.Document,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
@ -410,6 +427,7 @@ export class Compiler {
|
|||||||
this.formatter.format(file.Comments, {
|
this.formatter.format(file.Comments, {
|
||||||
viewWrapper: file.Document,
|
viewWrapper: file.Document,
|
||||||
file,
|
file,
|
||||||
|
stack: [],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
indent: prettify,
|
indent: prettify,
|
||||||
|
@ -1,25 +1,23 @@
|
|||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
import { twipsMeasureValue } from "@util/values";
|
import { PositiveUniversalMeasure, twipsMeasureValue } from "@util/values";
|
||||||
|
|
||||||
export interface IColumnAttributes {
|
// <xsd:complexType name="CT_Column">
|
||||||
readonly width: number | string;
|
// <xsd:attribute name="w" type="s:ST_TwipsMeasure" use="optional" />
|
||||||
readonly space?: number | string;
|
// <xsd:attribute name="space" type="s:ST_TwipsMeasure" use="optional" default="0" />
|
||||||
}
|
// </xsd:complexType>
|
||||||
|
|
||||||
export class ColumnAttributes extends XmlAttributeComponent<IColumnAttributes> {
|
type IColumnAttributes = {
|
||||||
protected readonly xmlKeys = {
|
readonly width: number | PositiveUniversalMeasure;
|
||||||
width: "w:w",
|
readonly space?: number | PositiveUniversalMeasure;
|
||||||
space: "w:space",
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Column extends XmlComponent {
|
export class Column extends XmlComponent {
|
||||||
public constructor({ width, space }: IColumnAttributes) {
|
public constructor({ width, space }: IColumnAttributes) {
|
||||||
super("w:col");
|
super("w:col");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new ColumnAttributes({
|
new NextAttributeComponent<IColumnAttributes>({
|
||||||
width: twipsMeasureValue(width),
|
width: { key: "w:w", value: twipsMeasureValue(width) },
|
||||||
space: space === undefined ? undefined : twipsMeasureValue(space),
|
space: { key: "w:space", value: space === undefined ? undefined : twipsMeasureValue(space) },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
import { decimalNumber, twipsMeasureValue } from "@util/values";
|
import { decimalNumber, PositiveUniversalMeasure, twipsMeasureValue } from "@util/values";
|
||||||
|
|
||||||
import { Column } from "./column";
|
import { Column } from "./column";
|
||||||
|
|
||||||
@ -12,32 +12,23 @@ import { Column } from "./column";
|
|||||||
// <xsd:attribute name="num" type="ST_DecimalNumber" use="optional" default="1"/>
|
// <xsd:attribute name="num" type="ST_DecimalNumber" use="optional" default="1"/>
|
||||||
// <xsd:attribute name="sep" type="s:ST_OnOff" use="optional"/>
|
// <xsd:attribute name="sep" type="s:ST_OnOff" use="optional"/>
|
||||||
// </xsd:complexType>
|
// </xsd:complexType>
|
||||||
export interface IColumnsAttributes {
|
export type IColumnsAttributes = {
|
||||||
readonly space?: number | string;
|
readonly space?: number | PositiveUniversalMeasure;
|
||||||
readonly count?: number;
|
readonly count?: number;
|
||||||
readonly separate?: boolean;
|
readonly separate?: boolean;
|
||||||
readonly equalWidth?: boolean;
|
readonly equalWidth?: boolean;
|
||||||
readonly children?: readonly Column[];
|
readonly children?: readonly Column[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export class ColumnsAttributes extends XmlAttributeComponent<IColumnsAttributes> {
|
|
||||||
protected readonly xmlKeys = {
|
|
||||||
space: "w:space",
|
|
||||||
count: "w:num",
|
|
||||||
separate: "w:sep",
|
|
||||||
equalWidth: "w:equalWidth",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Columns extends XmlComponent {
|
export class Columns extends XmlComponent {
|
||||||
public constructor({ space, count, separate, equalWidth, children }: IColumnsAttributes) {
|
public constructor({ space, count, separate, equalWidth, children }: IColumnsAttributes) {
|
||||||
super("w:cols");
|
super("w:cols");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new ColumnsAttributes({
|
new NextAttributeComponent<Omit<IColumnsAttributes, "children">>({
|
||||||
space: space === undefined ? undefined : twipsMeasureValue(space),
|
space: { key: "w:space", value: space === undefined ? undefined : twipsMeasureValue(space) },
|
||||||
count: count === undefined ? undefined : decimalNumber(count),
|
count: { key: "w:num", value: count === undefined ? undefined : decimalNumber(count) },
|
||||||
separate,
|
separate: { key: "w:sep", value: separate },
|
||||||
equalWidth,
|
equalWidth: { key: "w:equalWidth", value: equalWidth },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// http://officeopenxml.com/WPsectionLineNumbering.php
|
// http://officeopenxml.com/WPsectionLineNumbering.php
|
||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
import { decimalNumber, twipsMeasureValue } from "@util/values";
|
import { decimalNumber, PositiveUniversalMeasure, twipsMeasureValue } from "@util/values";
|
||||||
|
|
||||||
// <xsd:simpleType name="ST_LineNumberRestart">
|
// <xsd:simpleType name="ST_LineNumberRestart">
|
||||||
// <xsd:restriction base="xsd:string">
|
// <xsd:restriction base="xsd:string">
|
||||||
@ -26,27 +26,23 @@ export interface ILineNumberAttributes {
|
|||||||
readonly countBy?: number;
|
readonly countBy?: number;
|
||||||
readonly start?: number;
|
readonly start?: number;
|
||||||
readonly restart?: LineNumberRestartFormat;
|
readonly restart?: LineNumberRestartFormat;
|
||||||
readonly distance?: number | string;
|
readonly distance?: number | PositiveUniversalMeasure;
|
||||||
}
|
|
||||||
|
|
||||||
export class LineNumberAttributes extends XmlAttributeComponent<ILineNumberAttributes> {
|
|
||||||
protected readonly xmlKeys = {
|
|
||||||
countBy: "w:countBy",
|
|
||||||
start: "w:start",
|
|
||||||
restart: "w:restart",
|
|
||||||
distance: "w:distance",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LineNumberType extends XmlComponent {
|
export class LineNumberType extends XmlComponent {
|
||||||
public constructor({ countBy, start, restart, distance }: ILineNumberAttributes) {
|
public constructor({ countBy, start, restart, distance }: ILineNumberAttributes) {
|
||||||
super("w:lnNumType");
|
super("w:lnNumType");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new LineNumberAttributes({
|
new NextAttributeComponent<{
|
||||||
countBy: countBy === undefined ? undefined : decimalNumber(countBy),
|
readonly countBy?: number;
|
||||||
start: start === undefined ? undefined : decimalNumber(start),
|
readonly start?: number;
|
||||||
restart,
|
readonly restart?: LineNumberRestartFormat;
|
||||||
distance: distance === undefined ? undefined : twipsMeasureValue(distance),
|
readonly distance?: number | PositiveUniversalMeasure;
|
||||||
|
}>({
|
||||||
|
countBy: { key: "w:countBy", value: countBy === undefined ? undefined : decimalNumber(countBy) },
|
||||||
|
start: { key: "w:start", value: start === undefined ? undefined : decimalNumber(start) },
|
||||||
|
restart: { key: "w:restart", value: restart },
|
||||||
|
distance: { key: "w:distance", value: distance === undefined ? undefined : twipsMeasureValue(distance) },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
import { signedTwipsMeasureValue, twipsMeasureValue } from "@util/values";
|
import { PositiveUniversalMeasure, signedTwipsMeasureValue, twipsMeasureValue, UniversalMeasure } from "@util/values";
|
||||||
|
|
||||||
// <xsd:complexType name="CT_PageMar">
|
// <xsd:complexType name="CT_PageMar">
|
||||||
// <xsd:attribute name="top" type="ST_SignedTwipsMeasure" use="required"/>
|
// <xsd:attribute name="top" type="ST_SignedTwipsMeasure" use="required"/>
|
||||||
@ -10,48 +10,36 @@ import { signedTwipsMeasureValue, twipsMeasureValue } from "@util/values";
|
|||||||
// <xsd:attribute name="footer" type="s:ST_TwipsMeasure" use="required"/>
|
// <xsd:attribute name="footer" type="s:ST_TwipsMeasure" use="required"/>
|
||||||
// <xsd:attribute name="gutter" type="s:ST_TwipsMeasure" use="required"/>
|
// <xsd:attribute name="gutter" type="s:ST_TwipsMeasure" use="required"/>
|
||||||
// </xsd:complexType>
|
// </xsd:complexType>
|
||||||
export interface IPageMarginAttributes {
|
export type IPageMarginAttributes = {
|
||||||
readonly top?: number | string;
|
readonly top?: number | UniversalMeasure;
|
||||||
readonly right?: number | string;
|
readonly right?: number | PositiveUniversalMeasure;
|
||||||
readonly bottom?: number | string;
|
readonly bottom?: number | UniversalMeasure;
|
||||||
readonly left?: number | string;
|
readonly left?: number | PositiveUniversalMeasure;
|
||||||
readonly header?: number | string;
|
readonly header?: number | PositiveUniversalMeasure;
|
||||||
readonly footer?: number | string;
|
readonly footer?: number | PositiveUniversalMeasure;
|
||||||
readonly gutter?: number | string;
|
readonly gutter?: number | PositiveUniversalMeasure;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class PageMarginAttributes extends XmlAttributeComponent<IPageMarginAttributes> {
|
|
||||||
protected readonly xmlKeys = {
|
|
||||||
top: "w:top",
|
|
||||||
right: "w:right",
|
|
||||||
bottom: "w:bottom",
|
|
||||||
left: "w:left",
|
|
||||||
header: "w:header",
|
|
||||||
footer: "w:footer",
|
|
||||||
gutter: "w:gutter",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PageMargin extends XmlComponent {
|
export class PageMargin extends XmlComponent {
|
||||||
public constructor(
|
public constructor(
|
||||||
top: number | string,
|
top: number | UniversalMeasure,
|
||||||
right: number | string,
|
right: number | PositiveUniversalMeasure,
|
||||||
bottom: number | string,
|
bottom: number | UniversalMeasure,
|
||||||
left: number | string,
|
left: number | PositiveUniversalMeasure,
|
||||||
header: number | string,
|
header: number | PositiveUniversalMeasure,
|
||||||
footer: number | string,
|
footer: number | PositiveUniversalMeasure,
|
||||||
gutter: number | string,
|
gutter: number | PositiveUniversalMeasure,
|
||||||
) {
|
) {
|
||||||
super("w:pgMar");
|
super("w:pgMar");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new PageMarginAttributes({
|
new NextAttributeComponent<IPageMarginAttributes>({
|
||||||
top: signedTwipsMeasureValue(top),
|
top: { key: "w:top", value: signedTwipsMeasureValue(top) },
|
||||||
right: twipsMeasureValue(right),
|
right: { key: "w:right", value: twipsMeasureValue(right) },
|
||||||
bottom: signedTwipsMeasureValue(bottom),
|
bottom: { key: "w:bottom", value: signedTwipsMeasureValue(bottom) },
|
||||||
left: twipsMeasureValue(left),
|
left: { key: "w:left", value: twipsMeasureValue(left) },
|
||||||
header: twipsMeasureValue(header),
|
header: { key: "w:header", value: twipsMeasureValue(header) },
|
||||||
footer: twipsMeasureValue(footer),
|
footer: { key: "w:footer", value: twipsMeasureValue(footer) },
|
||||||
gutter: twipsMeasureValue(gutter),
|
gutter: { key: "w:gutter", value: twipsMeasureValue(gutter) },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
import { twipsMeasureValue } from "@util/values";
|
import { PositiveUniversalMeasure, twipsMeasureValue } from "@util/values";
|
||||||
|
|
||||||
// <xsd:simpleType name="ST_PageOrientation">
|
// <xsd:simpleType name="ST_PageOrientation">
|
||||||
// <xsd:restriction base="xsd:string">
|
// <xsd:restriction base="xsd:string">
|
||||||
@ -18,22 +18,14 @@ export enum PageOrientation {
|
|||||||
// <xsd:attribute name="orient" type="ST_PageOrientation" use="optional"/>
|
// <xsd:attribute name="orient" type="ST_PageOrientation" use="optional"/>
|
||||||
// <xsd:attribute name="code" type="ST_DecimalNumber" use="optional"/>
|
// <xsd:attribute name="code" type="ST_DecimalNumber" use="optional"/>
|
||||||
// </xsd:complexType>
|
// </xsd:complexType>
|
||||||
export interface IPageSizeAttributes {
|
export type IPageSizeAttributes = {
|
||||||
readonly width?: number | string;
|
readonly width?: number | PositiveUniversalMeasure;
|
||||||
readonly height?: number | string;
|
readonly height?: number | PositiveUniversalMeasure;
|
||||||
readonly orientation?: PageOrientation;
|
readonly orientation?: PageOrientation;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class PageSizeAttributes extends XmlAttributeComponent<IPageSizeAttributes> {
|
|
||||||
protected readonly xmlKeys = {
|
|
||||||
width: "w:w",
|
|
||||||
height: "w:h",
|
|
||||||
orientation: "w:orient",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PageSize extends XmlComponent {
|
export class PageSize extends XmlComponent {
|
||||||
public constructor(width: number | string, height: number | string, orientation: PageOrientation) {
|
public constructor(width: number | PositiveUniversalMeasure, height: number | PositiveUniversalMeasure, orientation: PageOrientation) {
|
||||||
super("w:pgSz");
|
super("w:pgSz");
|
||||||
|
|
||||||
const flip = orientation === PageOrientation.LANDSCAPE;
|
const flip = orientation === PageOrientation.LANDSCAPE;
|
||||||
@ -42,10 +34,10 @@ export class PageSize extends XmlComponent {
|
|||||||
const heightTwips = twipsMeasureValue(height);
|
const heightTwips = twipsMeasureValue(height);
|
||||||
|
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new PageSizeAttributes({
|
new NextAttributeComponent<IPageSizeAttributes>({
|
||||||
width: flip ? heightTwips : widthTwips,
|
width: { key: "w:w", value: flip ? heightTwips : widthTwips },
|
||||||
height: flip ? widthTwips : heightTwips,
|
height: { key: "w:h", value: flip ? widthTwips : heightTwips },
|
||||||
orientation: orientation,
|
orientation: { key: "w:orient", value: orientation },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,9 @@ import { FooterWrapper } from "@file/footer-wrapper";
|
|||||||
import { HeaderWrapper } from "@file/header-wrapper";
|
import { HeaderWrapper } from "@file/header-wrapper";
|
||||||
import { VerticalAlign, VerticalAlignElement } from "@file/vertical-align";
|
import { VerticalAlign, VerticalAlignElement } from "@file/vertical-align";
|
||||||
import { OnOffElement, XmlComponent } from "@file/xml-components";
|
import { OnOffElement, XmlComponent } from "@file/xml-components";
|
||||||
|
import { PositiveUniversalMeasure, UniversalMeasure } from "@util/values";
|
||||||
|
|
||||||
import { HeaderFooterReference, HeaderFooterReferenceType, HeaderFooterType } from "./properties/header-footer-reference";
|
import { HeaderFooterReference, HeaderFooterReferenceType, HeaderFooterType } from "./properties/header-footer-reference";
|
||||||
|
|
||||||
import { Columns, IColumnsAttributes } from "./properties/columns";
|
import { Columns, IColumnsAttributes } from "./properties/columns";
|
||||||
import { DocumentGrid, IDocGridAttributesProperties } from "./properties/doc-grid";
|
import { DocumentGrid, IDocGridAttributesProperties } from "./properties/doc-grid";
|
||||||
import { ILineNumberAttributes, LineNumberType } from "./properties/line-number";
|
import { ILineNumberAttributes, LineNumberType } from "./properties/line-number";
|
||||||
@ -76,10 +76,10 @@ export interface ISectionPropertiesOptions {
|
|||||||
// </xsd:group>
|
// </xsd:group>
|
||||||
|
|
||||||
export const sectionMarginDefaults = {
|
export const sectionMarginDefaults = {
|
||||||
TOP: "1in",
|
TOP: "1in" as UniversalMeasure,
|
||||||
RIGHT: "1in",
|
RIGHT: "1in" as PositiveUniversalMeasure,
|
||||||
BOTTOM: "1in",
|
BOTTOM: "1in" as UniversalMeasure,
|
||||||
LEFT: "1in",
|
LEFT: "1in" as PositiveUniversalMeasure,
|
||||||
HEADER: 708,
|
HEADER: 708,
|
||||||
FOOTER: 708,
|
FOOTER: 708,
|
||||||
GUTTER: 0,
|
GUTTER: 0,
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
|
||||||
|
import { createHyperlinkClick, createHyperlinkHover } from "./doc-properties-children";
|
||||||
|
|
||||||
|
describe("Document Properties Children", () => {
|
||||||
|
describe("#createHyperlinkClick", () => {
|
||||||
|
it("should create a Hyperlink Click component", () => {
|
||||||
|
const tree = new Formatter().format(createHyperlinkClick("1", false));
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"a:hlinkClick": {
|
||||||
|
_attr: {
|
||||||
|
"r:id": "rId1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a Hyperlink Click component with xmlns:a", () => {
|
||||||
|
const tree = new Formatter().format(createHyperlinkClick("1", true));
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"a:hlinkClick": {
|
||||||
|
_attr: {
|
||||||
|
"r:id": "rId1",
|
||||||
|
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#createHyperlinkHover", () => {
|
||||||
|
it("should create a Hyperlink Hover component", () => {
|
||||||
|
const tree = new Formatter().format(createHyperlinkHover("1", false));
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"a:hlinkHover": {
|
||||||
|
_attr: {
|
||||||
|
"r:id": "rId1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a Hyperlink Hover component with xmlns:a", () => {
|
||||||
|
const tree = new Formatter().format(createHyperlinkHover("1", true));
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"a:hlinkHover": {
|
||||||
|
_attr: {
|
||||||
|
"r:id": "rId1",
|
||||||
|
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
57
src/file/drawing/doc-properties/doc-properties-children.ts
Normal file
57
src/file/drawing/doc-properties/doc-properties-children.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// <sequence>
|
||||||
|
// <element name="hlinkClick" type="CT_Hyperlink" minOccurs="0" maxOccurs="1" />
|
||||||
|
// <element name="hlinkHover" type="CT_Hyperlink" minOccurs="0" maxOccurs="1" />
|
||||||
|
// <element name="extLst" type="CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1" />
|
||||||
|
// </sequence>
|
||||||
|
|
||||||
|
import { BuilderElement, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
|
// <xsd:complexType name="CT_Hyperlink">
|
||||||
|
// <xsd:group ref="EG_PContent" minOccurs="0" maxOccurs="unbounded" />
|
||||||
|
// <xsd:attribute name="tgtFrame" type="s:ST_String" use="optional" />
|
||||||
|
// <xsd:attribute name="tooltip" type="s:ST_String" use="optional" />
|
||||||
|
// <xsd:attribute name="docLocation" type="s:ST_String" use="optional" />
|
||||||
|
// <xsd:attribute name="history" type="s:ST_OnOff" use="optional" />
|
||||||
|
// <xsd:attribute name="anchor" type="s:ST_String" use="optional" />
|
||||||
|
// <xsd:attribute ref="r:id" />
|
||||||
|
// </xsd:complexType>
|
||||||
|
|
||||||
|
// TODO: Implement the rest of the attributes
|
||||||
|
|
||||||
|
export const createHyperlinkClick = (linkId: string, hasXmlNs: boolean): XmlComponent =>
|
||||||
|
new BuilderElement({
|
||||||
|
name: "a:hlinkClick",
|
||||||
|
attributes: {
|
||||||
|
...(hasXmlNs
|
||||||
|
? {
|
||||||
|
xmlns: {
|
||||||
|
key: "xmlns:a",
|
||||||
|
value: "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
id: {
|
||||||
|
key: "r:id",
|
||||||
|
value: `rId${linkId}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const createHyperlinkHover = (linkId: string, hasXmlNs: boolean): XmlComponent =>
|
||||||
|
new BuilderElement({
|
||||||
|
name: "a:hlinkHover",
|
||||||
|
attributes: {
|
||||||
|
...(hasXmlNs
|
||||||
|
? {
|
||||||
|
xmlns: {
|
||||||
|
key: "xmlns:a",
|
||||||
|
value: "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
id: {
|
||||||
|
key: "r:id",
|
||||||
|
value: `rId${linkId}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -1,19 +1,22 @@
|
|||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_docPr_topic_ID0ES32OB.html
|
||||||
|
import { IContext, IXmlableObject, NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
|
import { ConcreteHyperlink } from "@file/paragraph";
|
||||||
|
|
||||||
import { uniqueNumericId } from "@util/convenience-functions";
|
import { uniqueNumericId } from "@util/convenience-functions";
|
||||||
|
|
||||||
class DocPropertiesAttributes extends XmlAttributeComponent<{
|
import { createHyperlinkClick } from "./doc-properties-children";
|
||||||
readonly id?: number;
|
|
||||||
readonly name?: string;
|
// <complexType name="CT_NonVisualDrawingProps">
|
||||||
readonly description?: string;
|
// <sequence>
|
||||||
readonly title?: string;
|
// <element name="hlinkClick" type="CT_Hyperlink" minOccurs="0" maxOccurs="1" />
|
||||||
}> {
|
// <element name="hlinkHover" type="CT_Hyperlink" minOccurs="0" maxOccurs="1" />
|
||||||
protected readonly xmlKeys = {
|
// <element name="extLst" type="CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1" />
|
||||||
id: "id",
|
// </sequence>
|
||||||
name: "name",
|
// <attribute name="id" type="ST_DrawingElementId" use="required" />
|
||||||
description: "descr",
|
// <attribute name="name" type="xsd:string" use="required" />
|
||||||
title: "title",
|
// <attribute name="descr" type="xsd:string" use="optional" default="" />
|
||||||
};
|
// <attribute name="hidden" type="xsd:boolean" use="optional" default="false" />
|
||||||
}
|
// </complexType>
|
||||||
|
|
||||||
export interface DocPropertiesOptions {
|
export interface DocPropertiesOptions {
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
@ -26,12 +29,39 @@ export class DocProperties extends XmlComponent {
|
|||||||
super("wp:docPr");
|
super("wp:docPr");
|
||||||
|
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new DocPropertiesAttributes({
|
new NextAttributeComponent({
|
||||||
id: uniqueNumericId(),
|
id: {
|
||||||
name,
|
key: "id",
|
||||||
description,
|
value: uniqueNumericId(),
|
||||||
title,
|
},
|
||||||
|
name: {
|
||||||
|
key: "name",
|
||||||
|
value: name,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
key: "descr",
|
||||||
|
value: description,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
key: "title",
|
||||||
|
value: title,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public prepForXml(context: IContext): IXmlableObject | undefined {
|
||||||
|
for (let i = context.stack.length - 1; i >= 0; i--) {
|
||||||
|
const element = context.stack[i];
|
||||||
|
if (!(element instanceof ConcreteHyperlink)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.root.push(createHyperlinkClick(element.linkId, true));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.prepForXml(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { SinonStub, stub } from "sinon";
|
import { SinonStub, stub } from "sinon";
|
||||||
|
|
||||||
|
import { IContext } from "@file/xml-components";
|
||||||
import { Formatter } from "@export/formatter";
|
import { Formatter } from "@export/formatter";
|
||||||
import * as convenienceFunctions from "@util/convenience-functions";
|
import * as convenienceFunctions from "@util/convenience-functions";
|
||||||
|
|
||||||
|
import { ConcreteHyperlink, TextRun } from "../";
|
||||||
import { Drawing, IDrawingOptions } from "./drawing";
|
import { Drawing, IDrawingOptions } from "./drawing";
|
||||||
|
|
||||||
const imageBase64Data = `iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAACzVBMVEUAAAAAAAAAAAAAAAA/AD8zMzMqKiokJCQfHx8cHBwZGRkuFxcqFSonJyckJCQiIiIfHx8eHh4cHBwoGhomGSYkJCQhISEfHx8eHh4nHR0lHBwkGyQjIyMiIiIgICAfHx8mHh4lHh4kHR0jHCMiGyIhISEgICAfHx8lHx8kHh4jHR0hHCEhISEgICAlHx8kHx8jHh4jHh4iHSIhHCEhISElICAkHx8jHx8jHh4iHh4iHSIhHSElICAkICAjHx8jHx8iHh4iHh4hHiEhHSEkICAjHx8iHx8iHx8hHh4hHiEkHSEjHSAjHx8iHx8iHx8hHh4kHiEkHiEjHSAiHx8hHx8hHh4kHiEjHiAjHSAiHx8iHx8hHx8kHh4jHiEjHiAjHiAiICAiHx8kHx8jHh4jHiEjHiAiHiAiHSAiHx8jHx8jHx8jHiAiHiAiHiAiHSAiHx8jHx8jHx8iHiAiHiAiHiAjHx8jHx8jHx8jHx8iHiAiHiAiHiAjHx8jHx8jHx8iHx8iHSAiHiAjHiAjHx8jHx8hHx8iHx8iHyAiHiAjHiAjHiAjHh4hHx8iHx8iHx8iHyAjHSAjHiAjHiAjHh4hHx8iHx8iHx8jHyAjHiAhHh4iHx8iHx8jHyAjHSAjHSAhHiAhHh4iHx8iHx8jHx8jHyAjHSAjHSAiHh4iHh4jHx8jHx8jHyAjHyAhHSAhHSAiHh4iHh4jHx8jHx8jHyAhHyAhHSAiHSAiHh4jHh4jHx8jHx8jHyAhHyAhHSAiHSAjHR4jHh4jHx8jHx8hHyAhHyAiHSAjHSAjHR4jHh4jHx8hHx8hHyAhHyAiHyAjHSAjHR4jHR4hHh4hHx8hHyAiHyAjHyAjHSAjHR4jHR4hHh4hHx8hHyAjHyAjHyAjHSAjHR4hHR4hHR4hHx8iHyAjHyAjHyAjHSAhHR4hHR4hHR4hHx8jHyAjHyAjHyAjHyC9S2xeAAAA7nRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFxgZGhscHR4fICEiIyQlJicoKSorLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZISUpLTE1OUFFSU1RVVllaW1xdXmBhYmNkZWZnaGprbG1ub3Byc3R1dnd4eXp8fn+AgYKDhIWGiImKi4yNj5CRkpOUlZaXmJmam5ydnp+goaKjpKaoqqusra6vsLGys7S1tri5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+fkZpVQAABcBJREFUGBntwftjlQMcBvDnnLNL22qzJjWlKLHFVogyty3SiFq6EZliqZGyhnSxsLlMRahYoZKRFcul5dKFCatYqWZaNKvWtrPz/A2+7/b27qRzec/lPfvl/XxgMplMJpPJZDKZAtA9HJ3ppnIez0KnSdtC0RCNznHdJrbrh85wdSlVVRaEXuoGamYi5K5430HNiTiEWHKJg05eRWgNfKeV7RxbqUhGKPV/207VupQ8is0IoX5vtFC18SqEHaK4GyHTZ2kzVR8PBTCO4oANIZL4ShNVZcOhKKeYg9DoWdhI1ec3os2VFI0JCIUez5+i6st0qJZRrEAIJCw+QdW223BG/EmKwTBc/IJ/qfp2FDrkUnwFo8U9dZyqnaPhxLqfYjyM1S3vb6p+GGOBszsojoTDSDFz6qj66R4LzvYJxVMwUNRjf1H1ywQr/megg2RzLximy8waqvbda8M5iijegVEiHjlM1W/3h+FcXesphsMY4dMOUnUgOxyuPEzxPQwRNvV3qg5Nj4BreyimwADWe/dRVTMjEm6MoGLzGwtystL6RyOY3qSqdlYU3FpLZw1VW0sK5943MvUCKwJ1noNtjs6Ohge76Zq9ZkfpigU5WWkDYuCfbs1U5HWFR8/Qq4a9W0uK5k4ZmdrTCl8spGIePLPlbqqsc1Afe83O0hULc8alDYiBd7ZyitYMeBfR55rR2fOKP6ioPk2dGvZ+UVI0d8rtqT2tcCexlqK2F3wRn5Q+YVbBqrLKOupkr9lZujAOrmS0UpTb4JeIPkNHZ+cXr6uoPk2vyuBSPhWLEKj45PQJuQWryyqP0Z14uGLdROHIRNBEXDR09EP5r62rOHCazhrD4VKPwxTH+sIA3ZPTJ+YuWV22n+IruHFDC8X2CBjnPoolcGc2FYUwzmsUWXDHsoGKLBhmN0VvuBVfTVE/AAbpaid5CB4MbaLY1QXGuIViLTyZQcVyGGMuxWPwaA0Vk2GI9RRp8Ci2iuLkIBjhT5LNUfAspZFiTwyC72KK7+DNg1SsRvCNp3gZXq2k4iEEXSHFJHgVXUlxejCCbTvFAHiXdIJiXxyCK7KJ5FHoMZGK9xBcwyg2QpdlVMxEUM2iyIMuXXZQNF+HswxMsSAAJRQjoE//eoqDCXBSTO6f1xd+O0iyNRY6jaWi1ALNYCocZROj4JdEikroVkjFk9DcStXxpdfCD2MoXodu4RUU9ptxxmXssOfxnvDVcxRTod9FxyhqLoAqis5aPhwTDp9spRgEH2Q6KLbYoKqlaKTm6Isp0C/sJMnjFvhiERXPQvUNRe9p29lhR04CdBpC8Sl8YiuncIxEuzUUg4Dkgj+paVozygY9plPMh28SaymO9kabAopREGF3vt9MzeFFl8G7lRSZ8FFGK8XX4VA8QjEd7XrM3M0OXz8YCy+qKBLgq3wqnofiTorF0Ax56Rg1J1elW+BBAsVe+My6iYq7IK6keBdOIseV2qn5Pb8f3MqkWAXf9ThM8c8lAOIotuFsF875lRrH5klRcG0+xcPwQ1oLxfeRAP4heQTnGL78X2rqlw2DK59SXAV/zKaiGMAuko5InCt68mcOan5+ohf+z1pP8lQY/GHZQMV4YD3FpXDp4qerqbF/lBWBswyi+AL+ia+maLgcRRQj4IYlY/UpauqKBsPJAxQF8NM1TRQ/RudSPAD34rK3scOuR8/HGcspxsJfOVS8NZbiGXiUtPgINU3v3WFDmx8pEuG3EiqKKVbCC1vm2iZqap5LAtCtleQf8F9sFYWDohzeJczYyQ4V2bEZFGsQgJRGqqqhS2phHTWn9lDkIhBTqWqxQZ+IsRvtdHY9AvI2VX2hW68nfqGmuQsCEl3JdjfCF8OW1bPdtwhQ0gm2mQzfRE3a7KCYj0BNZJs8+Kxf/r6WtTEI2FIqlsMfFgRB5A6KUnSe/vUkX0AnuvUIt8SjM1m6wWQymUwmk8lkMgXRf5vi8rLQxtUhAAAAAElFTkSuQmCC`;
|
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`;
|
||||||
@ -450,5 +452,257 @@ describe("Drawing", () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should create a drawing with a hyperlink", () => {
|
||||||
|
currentBreak = createDrawing({
|
||||||
|
floating: {
|
||||||
|
horizontalPosition: {
|
||||||
|
offset: 0,
|
||||||
|
},
|
||||||
|
verticalPosition: {
|
||||||
|
offset: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(currentBreak, {
|
||||||
|
stack: [new ConcreteHyperlink([new TextRun("Test")], "1")],
|
||||||
|
} as unknown as IContext);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:drawing": [
|
||||||
|
{
|
||||||
|
"wp:anchor": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
allowOverlap: "1",
|
||||||
|
behindDoc: "0",
|
||||||
|
distB: 0,
|
||||||
|
distL: 0,
|
||||||
|
distR: 0,
|
||||||
|
distT: 0,
|
||||||
|
layoutInCell: "1",
|
||||||
|
locked: "0",
|
||||||
|
relativeHeight: 952500,
|
||||||
|
simplePos: "0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wp:simplePos": {
|
||||||
|
_attr: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wp:positionH": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
relativeFrom: "page",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wp:posOffset": ["0"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wp:positionV": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
relativeFrom: "page",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wp:posOffset": ["0"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wp:extent": {
|
||||||
|
_attr: {
|
||||||
|
cx: 952500,
|
||||||
|
cy: 952500,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wp:effectExtent": {
|
||||||
|
_attr: {
|
||||||
|
b: 0,
|
||||||
|
l: 0,
|
||||||
|
r: 0,
|
||||||
|
t: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wp:wrapNone": {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wp:docPr": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
descr: "",
|
||||||
|
id: 0,
|
||||||
|
name: "",
|
||||||
|
title: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a:hlinkClick": {
|
||||||
|
_attr: {
|
||||||
|
"r:id": "rId1",
|
||||||
|
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wp:cNvGraphicFramePr": [
|
||||||
|
{
|
||||||
|
"a:graphicFrameLocks": {
|
||||||
|
_attr: {
|
||||||
|
// tslint:disable-next-line:object-literal-key-quotes
|
||||||
|
noChangeAspect: 1,
|
||||||
|
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a:graphic": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a:graphicData": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
uri: "http://schemas.openxmlformats.org/drawingml/2006/picture",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pic:pic": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"xmlns:pic": "http://schemas.openxmlformats.org/drawingml/2006/picture",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pic:nvPicPr": [
|
||||||
|
{
|
||||||
|
"pic:cNvPr": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
descr: "",
|
||||||
|
id: 0,
|
||||||
|
name: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a:hlinkClick": {
|
||||||
|
_attr: {
|
||||||
|
"r:id": "rId1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pic:cNvPicPr": [
|
||||||
|
{
|
||||||
|
"a:picLocks": {
|
||||||
|
_attr: {
|
||||||
|
noChangeArrowheads: 1,
|
||||||
|
noChangeAspect: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pic:blipFill": [
|
||||||
|
{
|
||||||
|
"a:blip": {
|
||||||
|
_attr: {
|
||||||
|
// tslint:disable-next-line:object-literal-key-quotes
|
||||||
|
cstate: "none",
|
||||||
|
"r:embed": "rId{test.jpg}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a:srcRect": {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a:stretch": [
|
||||||
|
{
|
||||||
|
"a:fillRect": {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pic:spPr": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
bwMode: "auto",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a:xfrm": [
|
||||||
|
{
|
||||||
|
_attr: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a:off": {
|
||||||
|
_attr: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a:ext": {
|
||||||
|
_attr: {
|
||||||
|
cx: 952500,
|
||||||
|
cy: 952500,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a:prstGeom": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
prst: "rect",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a:avLst": {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,21 @@
|
|||||||
import { XmlComponent } from "@file/xml-components";
|
import { IContext, IXmlableObject, XmlComponent } from "@file/xml-components";
|
||||||
|
import { createHyperlinkClick } from "@file/drawing/doc-properties/doc-properties-children";
|
||||||
|
import { ConcreteHyperlink } from "@file/paragraph";
|
||||||
|
|
||||||
import { NonVisualPropertiesAttributes } from "./non-visual-properties-attributes";
|
import { NonVisualPropertiesAttributes } from "./non-visual-properties-attributes";
|
||||||
|
|
||||||
|
// <complexType name="CT_NonVisualDrawingProps">
|
||||||
|
// <sequence>
|
||||||
|
// <element name="hlinkClick" type="CT_Hyperlink" minOccurs="0" maxOccurs="1" />
|
||||||
|
// <element name="hlinkHover" type="CT_Hyperlink" minOccurs="0" maxOccurs="1" />
|
||||||
|
// <element name="extLst" type="CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1" />
|
||||||
|
// </sequence>
|
||||||
|
// <attribute name="id" type="ST_DrawingElementId" use="required" />
|
||||||
|
// <attribute name="name" type="xsd:string" use="required" />
|
||||||
|
// <attribute name="descr" type="xsd:string" use="optional" default="" />
|
||||||
|
// <attribute name="hidden" type="xsd:boolean" use="optional" default="false" />
|
||||||
|
// </complexType>
|
||||||
|
|
||||||
export class NonVisualProperties extends XmlComponent {
|
export class NonVisualProperties extends XmlComponent {
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super("pic:cNvPr");
|
super("pic:cNvPr");
|
||||||
@ -13,4 +28,19 @@ export class NonVisualProperties extends XmlComponent {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public prepForXml(context: IContext): IXmlableObject | undefined {
|
||||||
|
for (let i = context.stack.length - 1; i >= 0; i--) {
|
||||||
|
const element = context.stack[i];
|
||||||
|
if (!(element instanceof ConcreteHyperlink)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.root.push(createHyperlinkClick(element.linkId, false));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.prepForXml(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
5
src/file/file-child.ts
Normal file
5
src/file/file-child.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
|
export class FileChild extends XmlComponent {
|
||||||
|
public readonly fileChild = Symbol();
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
import { IDocumentTemplate } from "../import-dotx";
|
|
||||||
|
|
||||||
export interface IFileProperties {
|
|
||||||
readonly template?: IDocumentTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432
|
|
||||||
/**
|
|
||||||
* @ignore
|
|
||||||
*/
|
|
||||||
export const WORKAROUND = "";
|
|
@ -1,12 +1,11 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
|
||||||
import { Formatter } from "@export/formatter";
|
import { Formatter } from "@export/formatter";
|
||||||
import { sectionMarginDefaults, sectionPageSizeDefaults } from "./document";
|
|
||||||
|
|
||||||
|
import { sectionMarginDefaults, sectionPageSizeDefaults } from "./document";
|
||||||
import { File } from "./file";
|
import { File } from "./file";
|
||||||
import { Footer, Header } from "./header";
|
import { Footer, Header } from "./header";
|
||||||
import { Paragraph } from "./paragraph";
|
import { Paragraph } from "./paragraph";
|
||||||
import { Media } from "./media";
|
|
||||||
|
|
||||||
const PAGE_SIZE_DEFAULTS = {
|
const PAGE_SIZE_DEFAULTS = {
|
||||||
"w:h": sectionPageSizeDefaults.HEIGHT,
|
"w:h": sectionPageSizeDefaults.HEIGHT,
|
||||||
@ -433,29 +432,6 @@ describe("File", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#templates", () => {
|
|
||||||
// Test will be deprecated when import-dotx and templates are deprecated
|
|
||||||
it("should work with template", () => {
|
|
||||||
const doc = new File(
|
|
||||||
{
|
|
||||||
sections: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
template: {
|
|
||||||
currentRelationshipId: 1,
|
|
||||||
headers: [],
|
|
||||||
footers: [],
|
|
||||||
styles: "",
|
|
||||||
titlePageIsDefined: true,
|
|
||||||
media: new Media(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(doc).to.not.be.undefined;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#externalStyles", () => {
|
describe("#externalStyles", () => {
|
||||||
it("should work with external styles", () => {
|
it("should work with external styles", () => {
|
||||||
const doc = new File({
|
const doc = new File({
|
||||||
|
@ -4,22 +4,19 @@ import { CoreProperties, IPropertiesOptions } from "./core-properties";
|
|||||||
import { CustomProperties } from "./custom-properties";
|
import { CustomProperties } from "./custom-properties";
|
||||||
import { DocumentWrapper } from "./document-wrapper";
|
import { DocumentWrapper } from "./document-wrapper";
|
||||||
import { HeaderFooterReferenceType, ISectionPropertiesOptions } from "./document/body/section-properties";
|
import { HeaderFooterReferenceType, ISectionPropertiesOptions } from "./document/body/section-properties";
|
||||||
import { IFileProperties } from "./file-properties";
|
|
||||||
import { FooterWrapper, IDocumentFooter } from "./footer-wrapper";
|
import { FooterWrapper, IDocumentFooter } from "./footer-wrapper";
|
||||||
import { FootnotesWrapper } from "./footnotes-wrapper";
|
import { FootnotesWrapper } from "./footnotes-wrapper";
|
||||||
import { Footer, Header } from "./header";
|
import { Footer, Header } from "./header";
|
||||||
import { HeaderWrapper, IDocumentHeader } from "./header-wrapper";
|
import { HeaderWrapper, IDocumentHeader } from "./header-wrapper";
|
||||||
import { Media } from "./media";
|
import { Media } from "./media";
|
||||||
import { Numbering } from "./numbering";
|
import { Numbering } from "./numbering";
|
||||||
import { Paragraph } from "./paragraph";
|
|
||||||
import { Comments } from "./paragraph/run/comment-run";
|
import { Comments } from "./paragraph/run/comment-run";
|
||||||
import { Relationships } from "./relationships";
|
import { Relationships } from "./relationships";
|
||||||
import { Settings } from "./settings";
|
import { Settings } from "./settings";
|
||||||
import { Styles } from "./styles";
|
import { Styles } from "./styles";
|
||||||
import { ExternalStylesFactory } from "./styles/external-styles-factory";
|
import { ExternalStylesFactory } from "./styles/external-styles-factory";
|
||||||
import { DefaultStylesFactory } from "./styles/factory";
|
import { DefaultStylesFactory } from "./styles/factory";
|
||||||
import { Table } from "./table";
|
import { FileChild } from "./file-child";
|
||||||
import { TableOfContents } from "./table-of-contents";
|
|
||||||
|
|
||||||
export interface ISectionOptions {
|
export interface ISectionOptions {
|
||||||
readonly headers?: {
|
readonly headers?: {
|
||||||
@ -33,7 +30,7 @@ export interface ISectionOptions {
|
|||||||
readonly even?: Footer;
|
readonly even?: Footer;
|
||||||
};
|
};
|
||||||
readonly properties?: ISectionPropertiesOptions;
|
readonly properties?: ISectionPropertiesOptions;
|
||||||
readonly children: readonly (Paragraph | Table | TableOfContents)[];
|
readonly children: readonly FileChild[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class File {
|
export class File {
|
||||||
@ -57,7 +54,7 @@ export class File {
|
|||||||
private readonly styles: Styles;
|
private readonly styles: Styles;
|
||||||
private readonly comments: Comments;
|
private readonly comments: Comments;
|
||||||
|
|
||||||
public constructor(options: IPropertiesOptions, fileProperties: IFileProperties = {}) {
|
public constructor(options: IPropertiesOptions) {
|
||||||
this.coreProperties = new CoreProperties({
|
this.coreProperties = new CoreProperties({
|
||||||
...options,
|
...options,
|
||||||
creator: options.creator ?? "Un-named",
|
creator: options.creator ?? "Un-named",
|
||||||
@ -82,20 +79,9 @@ export class File {
|
|||||||
updateFields: options.features?.updateFields,
|
updateFields: options.features?.updateFields,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.media = fileProperties.template && fileProperties.template.media ? fileProperties.template.media : new Media();
|
this.media = new Media();
|
||||||
|
|
||||||
if (fileProperties.template) {
|
if (options.externalStyles) {
|
||||||
this.currentRelationshipId = fileProperties.template.currentRelationshipId + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up styles
|
|
||||||
if (fileProperties.template && options.externalStyles) {
|
|
||||||
throw Error("can not use both template and external styles");
|
|
||||||
}
|
|
||||||
if (fileProperties.template && fileProperties.template.styles) {
|
|
||||||
const stylesFactory = new ExternalStylesFactory();
|
|
||||||
this.styles = stylesFactory.newInstance(fileProperties.template.styles);
|
|
||||||
} else if (options.externalStyles) {
|
|
||||||
const stylesFactory = new ExternalStylesFactory();
|
const stylesFactory = new ExternalStylesFactory();
|
||||||
this.styles = stylesFactory.newInstance(options.externalStyles);
|
this.styles = stylesFactory.newInstance(options.externalStyles);
|
||||||
} else if (options.styles) {
|
} else if (options.styles) {
|
||||||
@ -112,18 +98,6 @@ export class File {
|
|||||||
|
|
||||||
this.addDefaultRelationships();
|
this.addDefaultRelationships();
|
||||||
|
|
||||||
if (fileProperties.template && fileProperties.template.headers) {
|
|
||||||
for (const templateHeader of fileProperties.template.headers) {
|
|
||||||
this.addHeaderToDocument(templateHeader.header, templateHeader.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileProperties.template && fileProperties.template.footers) {
|
|
||||||
for (const templateFooter of fileProperties.template.footers) {
|
|
||||||
this.addFooterToDocument(templateFooter.footer, templateFooter.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const section of options.sections) {
|
for (const section of options.sections) {
|
||||||
this.addSection(section);
|
this.addSection(section);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
export * from "./paragraph";
|
export * from "./paragraph";
|
||||||
export * from "./table";
|
export * from "./table";
|
||||||
export * from "./file";
|
export * from "./file";
|
||||||
export * from "./file-properties";
|
|
||||||
export * from "./numbering";
|
export * from "./numbering";
|
||||||
export * from "./media";
|
export * from "./media";
|
||||||
export * from "./drawing";
|
export * from "./drawing";
|
||||||
|
@ -15,96 +15,30 @@ describe("Media", () => {
|
|||||||
(convenienceFunctions.uniqueId as SinonStub).restore();
|
(convenienceFunctions.uniqueId as SinonStub).restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#addMedia", () => {
|
describe("#Array", () => {
|
||||||
it("should add media", () => {
|
it("Get images as array", () => {
|
||||||
const image = new Media().addMedia("", {
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
});
|
|
||||||
expect(image.fileName).to.equal("test.png");
|
|
||||||
expect(image.transformation).to.deep.equal({
|
|
||||||
pixels: {
|
|
||||||
x: 100,
|
|
||||||
y: 100,
|
|
||||||
},
|
|
||||||
flip: undefined,
|
|
||||||
emus: {
|
|
||||||
x: 952500,
|
|
||||||
y: 952500,
|
|
||||||
},
|
|
||||||
rotation: undefined,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return UInt8Array if atob is present", () => {
|
|
||||||
// eslint-disable-next-line functional/immutable-data
|
|
||||||
global.atob = () => "atob result";
|
|
||||||
|
|
||||||
const image = new Media().addMedia("", {
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
});
|
|
||||||
expect(image.stream).to.be.an.instanceof(Uint8Array);
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, functional/immutable-data
|
|
||||||
(global as any).atob = undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should use data as is if its not a string", () => {
|
|
||||||
// eslint-disable-next-line functional/immutable-data
|
|
||||||
global.atob = () => "atob result";
|
|
||||||
|
|
||||||
const image = new Media().addMedia(Buffer.from(""), {
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
});
|
|
||||||
expect(image.stream).to.be.an.instanceof(Uint8Array);
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, functional/immutable-data
|
|
||||||
(global as any).atob = undefined;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#addImage", () => {
|
|
||||||
it("should add media", () => {
|
|
||||||
const media = new Media();
|
const media = new Media();
|
||||||
media.addMedia("", {
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
});
|
|
||||||
|
|
||||||
media.addImage("test2.png", {
|
media.addImage("test2.png", {
|
||||||
stream: Buffer.from(""),
|
stream: Buffer.from(""),
|
||||||
fileName: "",
|
fileName: "test.png",
|
||||||
transformation: {
|
transformation: {
|
||||||
pixels: {
|
pixels: {
|
||||||
x: Math.round(1),
|
x: Math.round(100),
|
||||||
y: Math.round(1),
|
y: Math.round(100),
|
||||||
|
},
|
||||||
|
flip: {
|
||||||
|
vertical: true,
|
||||||
|
horizontal: true,
|
||||||
},
|
},
|
||||||
emus: {
|
emus: {
|
||||||
x: Math.round(1 * 9525),
|
x: Math.round(1 * 9525),
|
||||||
y: Math.round(1 * 9525),
|
y: Math.round(1 * 9525),
|
||||||
},
|
},
|
||||||
|
rotation: 90,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(media.Array).to.be.lengthOf(2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#Array", () => {
|
|
||||||
it("Get images as array", () => {
|
|
||||||
const media = new Media();
|
|
||||||
media.addMedia("", {
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
flip: {
|
|
||||||
vertical: true,
|
|
||||||
horizontal: true,
|
|
||||||
},
|
|
||||||
rotation: 90,
|
|
||||||
});
|
|
||||||
|
|
||||||
const array = media.Array;
|
const array = media.Array;
|
||||||
expect(array).to.be.an.instanceof(Array);
|
expect(array).to.be.an.instanceof(Array);
|
||||||
expect(array.length).to.equal(1);
|
expect(array.length).to.equal(1);
|
||||||
@ -121,10 +55,10 @@ describe("Media", () => {
|
|||||||
horizontal: true,
|
horizontal: true,
|
||||||
},
|
},
|
||||||
emus: {
|
emus: {
|
||||||
x: 952500,
|
x: 9525,
|
||||||
y: 952500,
|
y: 9525,
|
||||||
},
|
},
|
||||||
rotation: 5400000,
|
rotation: 90,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { uniqueId } from "@util/convenience-functions";
|
|
||||||
|
|
||||||
import { IMediaData } from "./data";
|
import { IMediaData } from "./data";
|
||||||
|
|
||||||
export interface IMediaTransformation {
|
export interface IMediaTransformation {
|
||||||
@ -20,33 +18,6 @@ export class Media {
|
|||||||
this.map = new Map<string, IMediaData>();
|
this.map = new Map<string, IMediaData>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public addMedia(data: Buffer | string | Uint8Array | ArrayBuffer, transformation: IMediaTransformation): IMediaData {
|
|
||||||
const key = `${uniqueId()}.png`;
|
|
||||||
|
|
||||||
const newData = typeof data === "string" ? this.convertDataURIToBinary(data) : data;
|
|
||||||
|
|
||||||
const imageData: IMediaData = {
|
|
||||||
stream: newData,
|
|
||||||
fileName: key,
|
|
||||||
transformation: {
|
|
||||||
pixels: {
|
|
||||||
x: Math.round(transformation.width),
|
|
||||||
y: Math.round(transformation.height),
|
|
||||||
},
|
|
||||||
emus: {
|
|
||||||
x: Math.round(transformation.width * 9525),
|
|
||||||
y: Math.round(transformation.height * 9525),
|
|
||||||
},
|
|
||||||
flip: transformation.flip,
|
|
||||||
rotation: transformation.rotation ? transformation.rotation * 60000 : undefined,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
this.map.set(key, imageData);
|
|
||||||
|
|
||||||
return imageData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public addImage(key: string, mediaData: IMediaData): void {
|
public addImage(key: string, mediaData: IMediaData): void {
|
||||||
this.map.set(key, mediaData);
|
this.map.set(key, mediaData);
|
||||||
}
|
}
|
||||||
@ -54,24 +25,4 @@ export class Media {
|
|||||||
public get Array(): readonly IMediaData[] {
|
public get Array(): readonly IMediaData[] {
|
||||||
return Array.from(this.map.values());
|
return Array.from(this.map.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertDataURIToBinary(dataURI: string): Uint8Array {
|
|
||||||
// https://gist.github.com/borismus/1032746
|
|
||||||
// https://github.com/mafintosh/base64-to-uint8array
|
|
||||||
const BASE64_MARKER = ";base64,";
|
|
||||||
|
|
||||||
const base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
|
|
||||||
|
|
||||||
if (typeof atob === "function") {
|
|
||||||
return new Uint8Array(
|
|
||||||
atob(dataURI.substring(base64Index))
|
|
||||||
.split("")
|
|
||||||
.map((c) => c.charCodeAt(0)),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
||||||
const b = require("buf" + "fer");
|
|
||||||
return new b.Buffer(dataURI, "base64");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,11 @@ describe("ConcreteNumbering", () => {
|
|||||||
abstractNumId: 1,
|
abstractNumId: 1,
|
||||||
reference: "1",
|
reference: "1",
|
||||||
instance: 0,
|
instance: 0,
|
||||||
overrideLevel: {
|
overrideLevels: [
|
||||||
num: 3,
|
{
|
||||||
},
|
num: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const tree = new Formatter().format(concreteNumbering);
|
const tree = new Formatter().format(concreteNumbering);
|
||||||
@ -44,16 +46,61 @@ describe("ConcreteNumbering", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("sets a new override level for two different level numbers", () => {
|
||||||
|
const concreteNumbering = new ConcreteNumbering({
|
||||||
|
numId: 0,
|
||||||
|
abstractNumId: 1,
|
||||||
|
reference: "1",
|
||||||
|
instance: 0,
|
||||||
|
overrideLevels: [{ num: 3 }, { num: 5 }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const tree = new Formatter().format(concreteNumbering);
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:num": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:numId": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:abstractNumId": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:lvlOverride": {
|
||||||
|
_attr: {
|
||||||
|
"w:ilvl": 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:lvlOverride": {
|
||||||
|
_attr: {
|
||||||
|
"w:ilvl": 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("sets the startOverride element if start is given", () => {
|
it("sets the startOverride element if start is given", () => {
|
||||||
const concreteNumbering = new ConcreteNumbering({
|
const concreteNumbering = new ConcreteNumbering({
|
||||||
numId: 0,
|
numId: 0,
|
||||||
abstractNumId: 1,
|
abstractNumId: 1,
|
||||||
reference: "1",
|
reference: "1",
|
||||||
instance: 0,
|
instance: 0,
|
||||||
overrideLevel: {
|
overrideLevels: [
|
||||||
num: 1,
|
{
|
||||||
start: 9,
|
num: 1,
|
||||||
},
|
start: 9,
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
const tree = new Formatter().format(concreteNumbering);
|
const tree = new Formatter().format(concreteNumbering);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
@ -90,15 +137,143 @@ describe("ConcreteNumbering", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("sets the startOverride element for several levels if start is given", () => {
|
||||||
|
const concreteNumbering = new ConcreteNumbering({
|
||||||
|
numId: 0,
|
||||||
|
abstractNumId: 1,
|
||||||
|
reference: "1",
|
||||||
|
instance: 0,
|
||||||
|
overrideLevels: [
|
||||||
|
{
|
||||||
|
num: 1,
|
||||||
|
start: 9,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 3,
|
||||||
|
start: 10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(concreteNumbering);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:num": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:numId": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:abstractNumId": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:lvlOverride": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:ilvl": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:startOverride": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": 9,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:lvlOverride": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:ilvl": 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:startOverride": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Mix of overrideLevels with start and without", () => {
|
||||||
|
const concreteNumbering = new ConcreteNumbering({
|
||||||
|
numId: 0,
|
||||||
|
abstractNumId: 1,
|
||||||
|
reference: "1",
|
||||||
|
instance: 0,
|
||||||
|
overrideLevels: [
|
||||||
|
{
|
||||||
|
num: 1,
|
||||||
|
start: 9,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(concreteNumbering);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:num": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:numId": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:abstractNumId": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:lvlOverride": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:ilvl": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:startOverride": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": 9,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:lvlOverride": {
|
||||||
|
_attr: {
|
||||||
|
"w:ilvl": 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("sets the lvl element if overrideLevel.Level is accessed", () => {
|
it("sets the lvl element if overrideLevel.Level is accessed", () => {
|
||||||
const concreteNumbering = new ConcreteNumbering({
|
const concreteNumbering = new ConcreteNumbering({
|
||||||
numId: 0,
|
numId: 0,
|
||||||
abstractNumId: 1,
|
abstractNumId: 1,
|
||||||
reference: "1",
|
reference: "1",
|
||||||
instance: 0,
|
instance: 0,
|
||||||
overrideLevel: {
|
overrideLevels: [
|
||||||
num: 1,
|
{
|
||||||
},
|
num: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
const tree = new Formatter().format(concreteNumbering);
|
const tree = new Formatter().format(concreteNumbering);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
|
@ -18,15 +18,17 @@ class NumAttributes extends XmlAttributeComponent<{
|
|||||||
protected readonly xmlKeys = { numId: "w:numId" };
|
protected readonly xmlKeys = { numId: "w:numId" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IOverrideLevel {
|
||||||
|
readonly num: number;
|
||||||
|
readonly start?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IConcreteNumberingOptions {
|
export interface IConcreteNumberingOptions {
|
||||||
readonly numId: number;
|
readonly numId: number;
|
||||||
readonly abstractNumId: number;
|
readonly abstractNumId: number;
|
||||||
readonly reference: string;
|
readonly reference: string;
|
||||||
readonly instance: number;
|
readonly instance: number;
|
||||||
readonly overrideLevel?: {
|
readonly overrideLevels?: readonly IOverrideLevel[];
|
||||||
readonly num: number;
|
|
||||||
readonly start?: number;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// <xsd:complexType name="CT_Numbering">
|
// <xsd:complexType name="CT_Numbering">
|
||||||
@ -60,8 +62,10 @@ export class ConcreteNumbering extends XmlComponent {
|
|||||||
|
|
||||||
this.root.push(new AbstractNumId(decimalNumber(options.abstractNumId)));
|
this.root.push(new AbstractNumId(decimalNumber(options.abstractNumId)));
|
||||||
|
|
||||||
if (options.overrideLevel) {
|
if (options.overrideLevels && options.overrideLevels.length) {
|
||||||
this.root.push(new LevelOverride(options.overrideLevel.num, options.overrideLevel.start));
|
for (const level of options.overrideLevels) {
|
||||||
|
this.root.push(new LevelOverride(level.num, level.start));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,10 +164,12 @@ export class Numbering extends XmlComponent {
|
|||||||
abstractNumId: abstractNumbering.id,
|
abstractNumId: abstractNumbering.id,
|
||||||
reference: "default-bullet-numbering",
|
reference: "default-bullet-numbering",
|
||||||
instance: 0,
|
instance: 0,
|
||||||
overrideLevel: {
|
overrideLevels: [
|
||||||
num: 0,
|
{
|
||||||
start: 1,
|
num: 0,
|
||||||
},
|
start: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,39 +1,14 @@
|
|||||||
// http://officeopenxml.com/WPindentation.php
|
// http://officeopenxml.com/WPindentation.php
|
||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
import { signedTwipsMeasureValue, twipsMeasureValue } from "@util/values";
|
import { PositiveUniversalMeasure, signedTwipsMeasureValue, twipsMeasureValue, UniversalMeasure } from "@util/values";
|
||||||
|
|
||||||
export interface IIndentAttributesProperties {
|
export interface IIndentAttributesProperties {
|
||||||
readonly start?: number | string;
|
readonly start?: number | UniversalMeasure;
|
||||||
readonly end?: number | string;
|
readonly end?: number | UniversalMeasure;
|
||||||
readonly left?: number | string;
|
readonly left?: number | UniversalMeasure;
|
||||||
readonly right?: number | string;
|
readonly right?: number | UniversalMeasure;
|
||||||
readonly hanging?: number | string;
|
readonly hanging?: number | PositiveUniversalMeasure;
|
||||||
readonly firstLine?: number | string;
|
readonly firstLine?: number | PositiveUniversalMeasure;
|
||||||
}
|
|
||||||
|
|
||||||
// <xsd:complexType name="CT_Ind">
|
|
||||||
// <xsd:attribute name="start" type="ST_SignedTwipsMeasure" use="optional"/>
|
|
||||||
// <xsd:attribute name="startChars" type="ST_DecimalNumber" use="optional"/>
|
|
||||||
// <xsd:attribute name="end" type="ST_SignedTwipsMeasure" use="optional"/>
|
|
||||||
// <xsd:attribute name="endChars" type="ST_DecimalNumber" use="optional"/>
|
|
||||||
// <xsd:attribute name="left" type="ST_SignedTwipsMeasure" use="optional"/>
|
|
||||||
// <xsd:attribute name="leftChars" type="ST_DecimalNumber" use="optional"/>
|
|
||||||
// <xsd:attribute name="right" type="ST_SignedTwipsMeasure" use="optional"/>
|
|
||||||
// <xsd:attribute name="rightChars" type="ST_DecimalNumber" use="optional"/>
|
|
||||||
// <xsd:attribute name="hanging" type="s:ST_TwipsMeasure" use="optional"/>
|
|
||||||
// <xsd:attribute name="hangingChars" type="ST_DecimalNumber" use="optional"/>
|
|
||||||
// <xsd:attribute name="firstLine" type="s:ST_TwipsMeasure" use="optional"/>
|
|
||||||
// <xsd:attribute name="firstLineChars" type="ST_DecimalNumber" use="optional"/>
|
|
||||||
// </xsd:complexType>
|
|
||||||
class IndentAttributes extends XmlAttributeComponent<IIndentAttributesProperties> {
|
|
||||||
protected readonly xmlKeys = {
|
|
||||||
start: "w:start",
|
|
||||||
end: "w:end",
|
|
||||||
left: "w:left",
|
|
||||||
right: "w:right",
|
|
||||||
hanging: "w:hanging",
|
|
||||||
firstLine: "w:firstLine",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// <xsd:complexType name="CT_PPrBase">
|
// <xsd:complexType name="CT_PPrBase">
|
||||||
@ -43,14 +18,53 @@ class IndentAttributes extends XmlAttributeComponent<IIndentAttributesProperties
|
|||||||
export class Indent extends XmlComponent {
|
export class Indent extends XmlComponent {
|
||||||
public constructor({ start, end, left, right, hanging, firstLine }: IIndentAttributesProperties) {
|
public constructor({ start, end, left, right, hanging, firstLine }: IIndentAttributesProperties) {
|
||||||
super("w:ind");
|
super("w:ind");
|
||||||
|
// <xsd:complexType name="CT_Ind">
|
||||||
|
// <xsd:attribute name="start" type="ST_SignedTwipsMeasure" use="optional"/>
|
||||||
|
// <xsd:attribute name="startChars" type="ST_DecimalNumber" use="optional"/>
|
||||||
|
// <xsd:attribute name="end" type="ST_SignedTwipsMeasure" use="optional"/>
|
||||||
|
// <xsd:attribute name="endChars" type="ST_DecimalNumber" use="optional"/>
|
||||||
|
// <xsd:attribute name="left" type="ST_SignedTwipsMeasure" use="optional"/>
|
||||||
|
// <xsd:attribute name="leftChars" type="ST_DecimalNumber" use="optional"/>
|
||||||
|
// <xsd:attribute name="right" type="ST_SignedTwipsMeasure" use="optional"/>
|
||||||
|
// <xsd:attribute name="rightChars" type="ST_DecimalNumber" use="optional"/>
|
||||||
|
// <xsd:attribute name="hanging" type="s:ST_TwipsMeasure" use="optional"/>
|
||||||
|
// <xsd:attribute name="hangingChars" type="ST_DecimalNumber" use="optional"/>
|
||||||
|
// <xsd:attribute name="firstLine" type="s:ST_TwipsMeasure" use="optional"/>
|
||||||
|
// <xsd:attribute name="firstLineChars" type="ST_DecimalNumber" use="optional"/>
|
||||||
|
// </xsd:complexType>
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new IndentAttributes({
|
new NextAttributeComponent<{
|
||||||
start: start === undefined ? undefined : signedTwipsMeasureValue(start),
|
readonly start?: number | UniversalMeasure;
|
||||||
end: end === undefined ? undefined : signedTwipsMeasureValue(end),
|
readonly end?: number | UniversalMeasure;
|
||||||
left: left === undefined ? undefined : signedTwipsMeasureValue(left),
|
readonly left?: number | UniversalMeasure;
|
||||||
right: right === undefined ? undefined : signedTwipsMeasureValue(right),
|
readonly right?: number | UniversalMeasure;
|
||||||
hanging: hanging === undefined ? undefined : twipsMeasureValue(hanging),
|
readonly hanging?: number | PositiveUniversalMeasure;
|
||||||
firstLine: firstLine === undefined ? undefined : twipsMeasureValue(firstLine),
|
readonly firstLine?: number | PositiveUniversalMeasure;
|
||||||
|
}>({
|
||||||
|
start: {
|
||||||
|
key: "w:start",
|
||||||
|
value: start === undefined ? undefined : signedTwipsMeasureValue(start),
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
key: "w:end",
|
||||||
|
value: end === undefined ? undefined : signedTwipsMeasureValue(end),
|
||||||
|
},
|
||||||
|
left: {
|
||||||
|
key: "w:left",
|
||||||
|
value: left === undefined ? undefined : signedTwipsMeasureValue(left),
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
key: "w:right",
|
||||||
|
value: right === undefined ? undefined : signedTwipsMeasureValue(right),
|
||||||
|
},
|
||||||
|
hanging: {
|
||||||
|
key: "w:hanging",
|
||||||
|
value: hanging === undefined ? undefined : twipsMeasureValue(hanging),
|
||||||
|
},
|
||||||
|
firstLine: {
|
||||||
|
key: "w:firstLine",
|
||||||
|
value: firstLine === undefined ? undefined : twipsMeasureValue(firstLine),
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -954,6 +954,7 @@ describe("Paragraph", () => {
|
|||||||
paragraph.prepForXml({
|
paragraph.prepForXml({
|
||||||
viewWrapper: viewWrapperMock,
|
viewWrapper: viewWrapperMock,
|
||||||
file: file,
|
file: file,
|
||||||
|
stack: [],
|
||||||
});
|
});
|
||||||
const tree = new Formatter().format(paragraph);
|
const tree = new Formatter().format(paragraph);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// http://officeopenxml.com/WPparagraph.php
|
// http://officeopenxml.com/WPparagraph.php
|
||||||
import { FootnoteReferenceRun } from "@file/footnotes";
|
import { FootnoteReferenceRun } from "@file/footnotes";
|
||||||
import { IContext, IXmlableObject, XmlComponent } from "@file/xml-components";
|
import { IContext, IXmlableObject } from "@file/xml-components";
|
||||||
import { uniqueId } from "@util/convenience-functions";
|
import { uniqueId } from "@util/convenience-functions";
|
||||||
|
import { FileChild } from "@file/file-child";
|
||||||
|
|
||||||
import { TargetModeType } from "../relationships/relationship/relationship";
|
import { TargetModeType } from "../relationships/relationship/relationship";
|
||||||
import { DeletedTextRun, InsertedTextRun } from "../track-revision";
|
import { DeletedTextRun, InsertedTextRun } from "../track-revision";
|
||||||
@ -39,7 +40,7 @@ export interface IParagraphOptions extends IParagraphPropertiesOptions {
|
|||||||
readonly children?: readonly ParagraphChild[];
|
readonly children?: readonly ParagraphChild[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Paragraph extends XmlComponent {
|
export class Paragraph extends FileChild {
|
||||||
private readonly properties: ParagraphProperties;
|
private readonly properties: ParagraphProperties;
|
||||||
|
|
||||||
public constructor(options: string | IParagraphOptions) {
|
public constructor(options: string | IParagraphOptions) {
|
||||||
|
@ -31,6 +31,7 @@ describe("ParagraphProperties", () => {
|
|||||||
} as File,
|
} as File,
|
||||||
// tslint:disable-next-line: no-object-literal-type-assertion
|
// tslint:disable-next-line: no-object-literal-type-assertion
|
||||||
viewWrapper: new DocumentWrapper({ background: {} }),
|
viewWrapper: new DocumentWrapper({ background: {} }),
|
||||||
|
stack: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
@ -124,6 +125,21 @@ describe("ParagraphProperties", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should create with the autoSpaceEastAsianText property", () => {
|
||||||
|
const properties = new ParagraphProperties({
|
||||||
|
autoSpaceEastAsianText: true,
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(properties);
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:pPr": [
|
||||||
|
{
|
||||||
|
"w:autoSpaceDN": {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("should create with the wordWrap property", () => {
|
it("should create with the wordWrap property", () => {
|
||||||
const properties = new ParagraphProperties({
|
const properties = new ParagraphProperties({
|
||||||
wordWrap: true,
|
wordWrap: true,
|
||||||
|
@ -23,7 +23,13 @@ export interface ILevelParagraphStylePropertiesOptions {
|
|||||||
readonly leftTabStop?: number;
|
readonly leftTabStop?: number;
|
||||||
readonly indent?: IIndentAttributesProperties;
|
readonly indent?: IIndentAttributesProperties;
|
||||||
readonly spacing?: ISpacingProperties;
|
readonly spacing?: ISpacingProperties;
|
||||||
|
/**
|
||||||
|
* Specifies that the paragraph (or at least part of it) should be rendered on the same page as the next paragraph when possible. If multiple paragraphs are to be kept together but they exceed a page, then the set of paragraphs begin on a new page and page breaks are used thereafter as needed.
|
||||||
|
*/
|
||||||
readonly keepNext?: boolean;
|
readonly keepNext?: boolean;
|
||||||
|
/**
|
||||||
|
* Specifies that all lines of the paragraph are to be kept on a single page when possible.
|
||||||
|
*/
|
||||||
readonly keepLines?: boolean;
|
readonly keepLines?: boolean;
|
||||||
readonly outlineLevel?: number;
|
readonly outlineLevel?: number;
|
||||||
}
|
}
|
||||||
@ -53,6 +59,11 @@ export interface IParagraphPropertiesOptions extends IParagraphStylePropertiesOp
|
|||||||
readonly suppressLineNumbers?: boolean;
|
readonly suppressLineNumbers?: boolean;
|
||||||
readonly wordWrap?: boolean;
|
readonly wordWrap?: boolean;
|
||||||
readonly scale?: number;
|
readonly scale?: number;
|
||||||
|
/**
|
||||||
|
* This element specifies whether inter-character spacing shall automatically be adjusted between regions of numbers and regions of East Asian text in the current paragraph. These regions shall be determined by the Unicode character values of the text content within the paragraph.
|
||||||
|
* This only works in Microsoft Word. It is not part of the ECMA-376 OOXML standard.
|
||||||
|
*/
|
||||||
|
readonly autoSpaceEastAsianText?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ParagraphProperties extends IgnoreIfEmptyXmlComponent {
|
export class ParagraphProperties extends IgnoreIfEmptyXmlComponent {
|
||||||
@ -179,6 +190,10 @@ export class ParagraphProperties extends IgnoreIfEmptyXmlComponent {
|
|||||||
if (options.suppressLineNumbers !== undefined) {
|
if (options.suppressLineNumbers !== undefined) {
|
||||||
this.push(new OnOffElement("w:suppressLineNumbers", options.suppressLineNumbers));
|
this.push(new OnOffElement("w:suppressLineNumbers", options.suppressLineNumbers));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.autoSpaceEastAsianText !== undefined) {
|
||||||
|
this.push(new OnOffElement("w:autoSpaceDN", options.autoSpaceEastAsianText));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public push(item: XmlComponent): void {
|
public push(item: XmlComponent): void {
|
||||||
|
@ -2,6 +2,8 @@ import { expect } from "chai";
|
|||||||
import * as sinon from "sinon";
|
import * as sinon from "sinon";
|
||||||
|
|
||||||
import { Formatter } from "@export/formatter";
|
import { Formatter } from "@export/formatter";
|
||||||
|
|
||||||
|
import { Paragraph } from "../paragraph";
|
||||||
import { Comment, CommentRangeEnd, CommentRangeStart, CommentReference, Comments } from "./comment-run";
|
import { Comment, CommentRangeEnd, CommentRangeStart, CommentReference, Comments } from "./comment-run";
|
||||||
|
|
||||||
describe("CommentRangeStart", () => {
|
describe("CommentRangeStart", () => {
|
||||||
@ -44,7 +46,7 @@ describe("Comment", () => {
|
|||||||
let clock: sinon.SinonFakeTimers;
|
let clock: sinon.SinonFakeTimers;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const now = new Date(1999, 0, 1);
|
const now = new Date("1999-01-01T00:00:00.000Z");
|
||||||
clock = sinon.useFakeTimers(now.getTime());
|
clock = sinon.useFakeTimers(now.getTime());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -56,8 +58,8 @@ describe("Comment", () => {
|
|||||||
it("should create", () => {
|
it("should create", () => {
|
||||||
const component = new Comment({
|
const component = new Comment({
|
||||||
id: 0,
|
id: 0,
|
||||||
text: "test-comment",
|
children: [new Paragraph("test-comment")],
|
||||||
date: new Date(1999, 0, 1),
|
date: new Date("1999-01-01T00:00:00.000Z"),
|
||||||
});
|
});
|
||||||
const tree = new Formatter().format(component);
|
const tree = new Formatter().format(component);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
@ -88,7 +90,7 @@ describe("Comment", () => {
|
|||||||
it("should create by using default date", () => {
|
it("should create by using default date", () => {
|
||||||
const component = new Comment({
|
const component = new Comment({
|
||||||
id: 0,
|
id: 0,
|
||||||
text: "test-comment",
|
children: [new Paragraph("test-comment")],
|
||||||
});
|
});
|
||||||
const tree = new Formatter().format(component);
|
const tree = new Formatter().format(component);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
@ -125,13 +127,13 @@ describe("Comments", () => {
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
text: "test-comment",
|
children: [new Paragraph("test-comment")],
|
||||||
date: new Date(1999, 0, 1),
|
date: new Date("1999-01-01T00:00:00.000Z"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
text: "test-comment-2",
|
children: [new Paragraph("test-comment-2")],
|
||||||
date: new Date(1999, 0, 1),
|
date: new Date("1999-01-01T00:00:00.000Z"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { Paragraph } from "@file/paragraph";
|
import { FileChild } from "@file/file-child";
|
||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
import { TextRun } from "./text-run";
|
|
||||||
|
|
||||||
export interface ICommentOptions {
|
export interface ICommentOptions {
|
||||||
readonly id: number;
|
readonly id: number;
|
||||||
readonly text: string;
|
readonly children: readonly FileChild[];
|
||||||
readonly initials?: string;
|
readonly initials?: string;
|
||||||
readonly author?: string;
|
readonly author?: string;
|
||||||
readonly date?: Date;
|
readonly date?: Date;
|
||||||
@ -120,7 +118,7 @@ export class CommentReference extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Comment extends XmlComponent {
|
export class Comment extends XmlComponent {
|
||||||
public constructor({ id, initials, author, date = new Date(), text }: ICommentOptions) {
|
public constructor({ id, initials, author, date = new Date(), children }: ICommentOptions) {
|
||||||
super("w:comment");
|
super("w:comment");
|
||||||
|
|
||||||
this.root.push(
|
this.root.push(
|
||||||
@ -132,7 +130,9 @@ export class Comment extends XmlComponent {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.root.push(new Paragraph({ children: [new TextRun(text)] }));
|
for (const child of children) {
|
||||||
|
this.root.push(child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export class Comments extends XmlComponent {
|
export class Comments extends XmlComponent {
|
||||||
|
229
src/file/paragraph/run/empty-children.spec.ts
Normal file
229
src/file/paragraph/run/empty-children.spec.ts
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
import {
|
||||||
|
AnnotationReference,
|
||||||
|
CarriageReturn,
|
||||||
|
ContinuationSeparator,
|
||||||
|
DayLong,
|
||||||
|
DayShort,
|
||||||
|
EndnoteReference,
|
||||||
|
FootnoteReferenceElement,
|
||||||
|
LastRenderedPageBreak,
|
||||||
|
MonthLong,
|
||||||
|
MonthShort,
|
||||||
|
NoBreakHyphen,
|
||||||
|
PageNumberElement,
|
||||||
|
Separator,
|
||||||
|
SoftHyphen,
|
||||||
|
Tab,
|
||||||
|
YearLong,
|
||||||
|
YearShort,
|
||||||
|
} from "./empty-children";
|
||||||
|
|
||||||
|
// <xsd:element name="noBreakHyphen" type="CT_Empty"/>
|
||||||
|
// <xsd:element name="softHyphen" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="dayShort" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="monthShort" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="yearShort" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="dayLong" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="monthLong" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="yearLong" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="annotationRef" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="footnoteRef" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="endnoteRef" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="separator" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="continuationSeparator" type="CT_Empty" minOccurs="0" />
|
||||||
|
// ...
|
||||||
|
// <xsd:element name="pgNum" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="cr" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="tab" type="CT_Empty" minOccurs="0" />
|
||||||
|
// ...
|
||||||
|
// <xsd:element name="lastRenderedPageBreak" type="CT_Empty" minOccurs="0" maxOccurs="1" />
|
||||||
|
|
||||||
|
describe("NoBreakHyphen", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a NoBreakHyphen with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new NoBreakHyphen());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:noBreakHyphen": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("SoftHyphen", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a SoftHyphen with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new SoftHyphen());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:softHyphen": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("DayShort", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a DayShort with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new DayShort());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:dayShort": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("MonthShort", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a MonthShort with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new MonthShort());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:monthShort": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("YearShort", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a YearShort with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new YearShort());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:yearShort": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("DayLong", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a DayLong with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new DayLong());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:dayLong": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("MonthLong", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a MonthLong with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new MonthLong());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:monthLong": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("YearLong", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a YearLong with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new YearLong());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:yearLong": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("AnnotationReference", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a AnnotationReference with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new AnnotationReference());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:annotationRef": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("FootnoteReferenceElement", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a FootnoteReferenceElement with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new FootnoteReferenceElement());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:footnoteRef": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("EndnoteReference", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a EndnoteReference with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new EndnoteReference());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:endnoteRef": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Separator", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a Separator with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new Separator());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:separator": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("ContinuationSeparator", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a ContinuationSeparator with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new ContinuationSeparator());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:continuationSeparator": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("PageNumberElement", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a PageNumberElement with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new PageNumberElement());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:pgNum": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("CarriageReturn", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a CarriageReturn with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new CarriageReturn());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:cr": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Tab", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a Tab with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new Tab());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:tab": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("LastRenderedPageBreak", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a LastRenderedPageBreak with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(new LastRenderedPageBreak());
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:lastRenderedPageBreak": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
127
src/file/paragraph/run/empty-children.ts
Normal file
127
src/file/paragraph/run/empty-children.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import { EmptyElement } from "@file/xml-components";
|
||||||
|
|
||||||
|
// <xsd:group name="EG_RunInnerContent">
|
||||||
|
// ...
|
||||||
|
// <xsd:element name="noBreakHyphen" type="CT_Empty"/>
|
||||||
|
// <xsd:element name="softHyphen" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="dayShort" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="monthShort" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="yearShort" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="dayLong" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="monthLong" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="yearLong" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="annotationRef" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="footnoteRef" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="endnoteRef" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="separator" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="continuationSeparator" type="CT_Empty" minOccurs="0" />
|
||||||
|
// ...
|
||||||
|
// <xsd:element name="pgNum" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="cr" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="tab" type="CT_Empty" minOccurs="0" />
|
||||||
|
// ...
|
||||||
|
// <xsd:element name="lastRenderedPageBreak" type="CT_Empty" minOccurs="0" maxOccurs="1" />
|
||||||
|
// </xsd:choice>
|
||||||
|
// </xsd:group>
|
||||||
|
|
||||||
|
export class NoBreakHyphen extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:noBreakHyphen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SoftHyphen extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:softHyphen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DayShort extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:dayShort");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MonthShort extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:monthShort");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class YearShort extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:yearShort");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DayLong extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:dayLong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MonthLong extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:monthLong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class YearLong extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:yearLong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AnnotationReference extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:annotationRef");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FootnoteReferenceElement extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:footnoteRef");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EndnoteReference extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:endnoteRef");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Separator extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:separator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ContinuationSeparator extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:continuationSeparator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PageNumberElement extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:pgNum");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CarriageReturn extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:cr");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Tab extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:tab");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LastRenderedPageBreak extends EmptyElement {
|
||||||
|
public constructor() {
|
||||||
|
super("w:lastRenderedPageBreak");
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import { Attributes, XmlComponent } from "@file/xml-components";
|
import { Attributes, XmlComponent } from "@file/xml-components";
|
||||||
import { hexColorValue, signedTwipsMeasureValue } from "@util/values";
|
import { hexColorValue, signedTwipsMeasureValue, UniversalMeasure } from "@util/values";
|
||||||
|
|
||||||
export class CharacterSpacing extends XmlComponent {
|
export class CharacterSpacing extends XmlComponent {
|
||||||
public constructor(value: number | string) {
|
public constructor(value: number | UniversalMeasure) {
|
||||||
super("w:spacing");
|
super("w:spacing");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new Attributes({
|
new Attributes({
|
||||||
|
@ -47,6 +47,7 @@ describe("ImageRun", () => {
|
|||||||
},
|
},
|
||||||
} as unknown as File,
|
} as unknown as File,
|
||||||
viewWrapper: {} as unknown as IViewWrapper,
|
viewWrapper: {} as unknown as IViewWrapper,
|
||||||
|
stack: [],
|
||||||
});
|
});
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:r": [
|
"w:r": [
|
||||||
@ -298,6 +299,7 @@ describe("ImageRun", () => {
|
|||||||
},
|
},
|
||||||
} as unknown as File,
|
} as unknown as File,
|
||||||
viewWrapper: {} as unknown as IViewWrapper,
|
viewWrapper: {} as unknown as IViewWrapper,
|
||||||
|
stack: [],
|
||||||
});
|
});
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:r": [
|
"w:r": [
|
||||||
@ -552,6 +554,7 @@ describe("ImageRun", () => {
|
|||||||
},
|
},
|
||||||
} as unknown as File,
|
} as unknown as File,
|
||||||
viewWrapper: {} as unknown as IViewWrapper,
|
viewWrapper: {} as unknown as IViewWrapper,
|
||||||
|
stack: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
@ -810,6 +813,7 @@ describe("ImageRun", () => {
|
|||||||
},
|
},
|
||||||
} as unknown as File,
|
} as unknown as File,
|
||||||
viewWrapper: {} as unknown as IViewWrapper,
|
viewWrapper: {} as unknown as IViewWrapper,
|
||||||
|
stack: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
|
@ -7,6 +7,7 @@ export * from "./run-fonts";
|
|||||||
export * from "./sequential-identifier";
|
export * from "./sequential-identifier";
|
||||||
export * from "./underline";
|
export * from "./underline";
|
||||||
export * from "./emphasis-mark";
|
export * from "./emphasis-mark";
|
||||||
export * from "./tab";
|
|
||||||
export * from "./simple-field";
|
export * from "./simple-field";
|
||||||
export * from "./comment-run";
|
export * from "./comment-run";
|
||||||
|
export * from "./empty-children";
|
||||||
|
export * from "./positional-tab";
|
||||||
|
29
src/file/paragraph/run/language.spec.ts
Normal file
29
src/file/paragraph/run/language.spec.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
|
||||||
|
import { createLanguageComponent } from "./language";
|
||||||
|
|
||||||
|
describe("Language", () => {
|
||||||
|
describe("#createLanguageComponent", () => {
|
||||||
|
it("should create a language component", () => {
|
||||||
|
const tree = new Formatter().format(
|
||||||
|
createLanguageComponent({
|
||||||
|
value: "en-US",
|
||||||
|
eastAsia: "zh-CN",
|
||||||
|
bidirectional: "ar-SA",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:lang": {
|
||||||
|
_attr: {
|
||||||
|
"w:bidi": "ar-SA",
|
||||||
|
"w:eastAsia": "zh-CN",
|
||||||
|
"w:val": "en-US",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
35
src/file/paragraph/run/language.ts
Normal file
35
src/file/paragraph/run/language.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { BuilderElement, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
|
// <xsd:complexType name="CT_Language">
|
||||||
|
// <xsd:attribute name="val" type="s:ST_Lang" use="optional"/>
|
||||||
|
// <xsd:attribute name="eastAsia" type="s:ST_Lang" use="optional"/>
|
||||||
|
// <xsd:attribute name="bidi" type="s:ST_Lang" use="optional"/>
|
||||||
|
// </xsd:complexType>
|
||||||
|
export interface ILanguageOptions {
|
||||||
|
readonly value?: string;
|
||||||
|
readonly eastAsia?: string;
|
||||||
|
readonly bidirectional?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createLanguageComponent = (options: ILanguageOptions): XmlComponent =>
|
||||||
|
new BuilderElement<{
|
||||||
|
readonly value?: string;
|
||||||
|
readonly eastAsia?: string;
|
||||||
|
readonly bidirectional?: string;
|
||||||
|
}>({
|
||||||
|
name: "w:lang",
|
||||||
|
attributes: {
|
||||||
|
value: {
|
||||||
|
key: "w:val",
|
||||||
|
value: options.value,
|
||||||
|
},
|
||||||
|
eastAsia: {
|
||||||
|
key: "w:eastAsia",
|
||||||
|
value: options.eastAsia,
|
||||||
|
},
|
||||||
|
bidirectional: {
|
||||||
|
key: "w:bidi",
|
||||||
|
value: options.bidirectional,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
27
src/file/paragraph/run/positional-tab.spec.ts
Normal file
27
src/file/paragraph/run/positional-tab.spec.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
|
||||||
|
import { PositionalTab, PositionalTabAlignment, PositionalTabLeader, PositionalTabRelativeTo } from "./positional-tab";
|
||||||
|
|
||||||
|
describe("PositionalTab", () => {
|
||||||
|
it("should create a PositionalTab with correct root key", () => {
|
||||||
|
const tree = new Formatter().format(
|
||||||
|
new PositionalTab({
|
||||||
|
alignment: PositionalTabAlignment.CENTER,
|
||||||
|
relativeTo: PositionalTabRelativeTo.MARGIN,
|
||||||
|
leader: PositionalTabLeader.DOT,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:ptab": {
|
||||||
|
_attr: {
|
||||||
|
"w:alignment": "center",
|
||||||
|
"w:relativeTo": "margin",
|
||||||
|
"w:leader": "dot",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
80
src/file/paragraph/run/positional-tab.ts
Normal file
80
src/file/paragraph/run/positional-tab.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
|
// <xsd:simpleType name="ST_PTabAlignment">
|
||||||
|
// <xsd:restriction base="xsd:string">
|
||||||
|
// <xsd:enumeration value="left" />
|
||||||
|
// <xsd:enumeration value="center" />
|
||||||
|
// <xsd:enumeration value="right" />
|
||||||
|
// </xsd:restriction>
|
||||||
|
// </xsd:simpleType>
|
||||||
|
export enum PositionalTabAlignment {
|
||||||
|
LEFT = "left",
|
||||||
|
CENTER = "center",
|
||||||
|
RIGHT = "right",
|
||||||
|
}
|
||||||
|
|
||||||
|
// <xsd:simpleType name="ST_PTabRelativeTo">
|
||||||
|
// <xsd:restriction base="xsd:string">
|
||||||
|
// <xsd:enumeration value="margin" />
|
||||||
|
// <xsd:enumeration value="indent" />
|
||||||
|
// </xsd:restriction>
|
||||||
|
// </xsd:simpleType>
|
||||||
|
export enum PositionalTabRelativeTo {
|
||||||
|
MARGIN = "margin",
|
||||||
|
INDENT = "indent",
|
||||||
|
}
|
||||||
|
|
||||||
|
// <xsd:simpleType name="ST_PTabLeader">
|
||||||
|
// <xsd:restriction base="xsd:string">
|
||||||
|
// <xsd:enumeration value="none" />
|
||||||
|
// <xsd:enumeration value="dot" />
|
||||||
|
// <xsd:enumeration value="hyphen" />
|
||||||
|
// <xsd:enumeration value="underscore" />
|
||||||
|
// <xsd:enumeration value="middleDot" />
|
||||||
|
// </xsd:restriction>
|
||||||
|
// </xsd:simpleType>
|
||||||
|
export enum PositionalTabLeader {
|
||||||
|
NONE = "none",
|
||||||
|
DOT = "dot",
|
||||||
|
HYPHEN = "hyphen",
|
||||||
|
UNDERSCORE = "underscore",
|
||||||
|
MIDDLE_DOT = "middleDot",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PositionalTabOptions {
|
||||||
|
readonly alignment: PositionalTabAlignment;
|
||||||
|
readonly relativeTo: PositionalTabRelativeTo;
|
||||||
|
readonly leader: PositionalTabLeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <xsd:complexType name="CT_PTab">
|
||||||
|
// <xsd:attribute name="alignment" type="ST_PTabAlignment" use="required" />
|
||||||
|
// <xsd:attribute name="relativeTo" type="ST_PTabRelativeTo" use="required" />
|
||||||
|
// <xsd:attribute name="leader" type="ST_PTabLeader" use="required" />
|
||||||
|
// </xsd:complexType>
|
||||||
|
export class PositionalTab extends XmlComponent {
|
||||||
|
public constructor(options: PositionalTabOptions) {
|
||||||
|
super("w:ptab");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new NextAttributeComponent<{
|
||||||
|
readonly alignment: PositionalTabAlignment;
|
||||||
|
readonly relativeTo: PositionalTabRelativeTo;
|
||||||
|
readonly leader: PositionalTabLeader;
|
||||||
|
}>({
|
||||||
|
alignment: {
|
||||||
|
key: "w:alignment",
|
||||||
|
value: options.alignment,
|
||||||
|
},
|
||||||
|
relativeTo: {
|
||||||
|
key: "w:relativeTo",
|
||||||
|
value: options.relativeTo,
|
||||||
|
},
|
||||||
|
leader: {
|
||||||
|
key: "w:leader",
|
||||||
|
value: options.leader,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -9,9 +9,11 @@ import {
|
|||||||
StringValueElement,
|
StringValueElement,
|
||||||
XmlComponent,
|
XmlComponent,
|
||||||
} from "@file/xml-components";
|
} from "@file/xml-components";
|
||||||
|
import { PositiveUniversalMeasure, UniversalMeasure } from "@util/values";
|
||||||
|
|
||||||
import { EmphasisMark, EmphasisMarkType } from "./emphasis-mark";
|
import { EmphasisMark, EmphasisMarkType } from "./emphasis-mark";
|
||||||
import { CharacterSpacing, Color, Highlight, HighlightComplexScript } from "./formatting";
|
import { CharacterSpacing, Color, Highlight, HighlightComplexScript } from "./formatting";
|
||||||
|
import { createLanguageComponent, ILanguageOptions } from "./language";
|
||||||
import { IFontAttributesProperties, RunFonts } from "./run-fonts";
|
import { IFontAttributesProperties, RunFonts } from "./run-fonts";
|
||||||
import { SubScript, SuperScript } from "./script";
|
import { SubScript, SuperScript } from "./script";
|
||||||
import { Underline, UnderlineType } from "./underline";
|
import { Underline, UnderlineType } from "./underline";
|
||||||
@ -21,6 +23,16 @@ interface IFontOptions {
|
|||||||
readonly hint?: string;
|
readonly hint?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum TextEffect {
|
||||||
|
BLINK_BACKGROUND = "blinkBackground",
|
||||||
|
LIGHTS = "lights",
|
||||||
|
ANTS_BLACK = "antsBlack",
|
||||||
|
ANTS_RED = "antsRed",
|
||||||
|
SHIMMER = "shimmer",
|
||||||
|
SPARKLE = "sparkle",
|
||||||
|
NONE = "none",
|
||||||
|
}
|
||||||
|
|
||||||
export interface IRunStylePropertiesOptions {
|
export interface IRunStylePropertiesOptions {
|
||||||
readonly bold?: boolean;
|
readonly bold?: boolean;
|
||||||
readonly boldComplexScript?: boolean;
|
readonly boldComplexScript?: boolean;
|
||||||
@ -30,12 +42,15 @@ export interface IRunStylePropertiesOptions {
|
|||||||
readonly color?: string;
|
readonly color?: string;
|
||||||
readonly type?: UnderlineType;
|
readonly type?: UnderlineType;
|
||||||
};
|
};
|
||||||
|
readonly effect?: TextEffect;
|
||||||
readonly emphasisMark?: {
|
readonly emphasisMark?: {
|
||||||
readonly type?: EmphasisMarkType;
|
readonly type?: EmphasisMarkType;
|
||||||
};
|
};
|
||||||
readonly color?: string;
|
readonly color?: string;
|
||||||
readonly size?: number | string;
|
readonly kern?: number | PositiveUniversalMeasure;
|
||||||
readonly sizeComplexScript?: boolean | number | string;
|
readonly position?: UniversalMeasure;
|
||||||
|
readonly size?: number | PositiveUniversalMeasure;
|
||||||
|
readonly sizeComplexScript?: boolean | number | PositiveUniversalMeasure;
|
||||||
readonly rightToLeft?: boolean;
|
readonly rightToLeft?: boolean;
|
||||||
readonly smallCaps?: boolean;
|
readonly smallCaps?: boolean;
|
||||||
readonly allCaps?: boolean;
|
readonly allCaps?: boolean;
|
||||||
@ -51,10 +66,13 @@ export interface IRunStylePropertiesOptions {
|
|||||||
readonly emboss?: boolean;
|
readonly emboss?: boolean;
|
||||||
readonly imprint?: boolean;
|
readonly imprint?: boolean;
|
||||||
readonly revision?: IRunPropertiesChangeOptions;
|
readonly revision?: IRunPropertiesChangeOptions;
|
||||||
|
readonly language?: ILanguageOptions;
|
||||||
readonly border?: IBorderOptions;
|
readonly border?: IBorderOptions;
|
||||||
|
readonly snapToGrid?: boolean;
|
||||||
readonly vanish?: boolean;
|
readonly vanish?: boolean;
|
||||||
readonly specVanish?: boolean;
|
readonly specVanish?: boolean;
|
||||||
readonly scale?: number;
|
readonly scale?: number;
|
||||||
|
readonly math?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRunPropertiesOptions extends IRunStylePropertiesOptions {
|
export interface IRunPropertiesOptions extends IRunStylePropertiesOptions {
|
||||||
@ -133,6 +151,10 @@ export class RunProperties extends IgnoreIfEmptyXmlComponent {
|
|||||||
this.push(new Underline(options.underline.type, options.underline.color));
|
this.push(new Underline(options.underline.type, options.underline.color));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.effect) {
|
||||||
|
this.push(new StringValueElement("w:effect", options.effect));
|
||||||
|
}
|
||||||
|
|
||||||
if (options.emphasisMark) {
|
if (options.emphasisMark) {
|
||||||
this.push(new EmphasisMark(options.emphasisMark.type));
|
this.push(new EmphasisMark(options.emphasisMark.type));
|
||||||
}
|
}
|
||||||
@ -141,6 +163,14 @@ export class RunProperties extends IgnoreIfEmptyXmlComponent {
|
|||||||
this.push(new Color(options.color));
|
this.push(new Color(options.color));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.kern) {
|
||||||
|
this.push(new HpsMeasureElement("w:kern", options.kern));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.position) {
|
||||||
|
this.push(new StringValueElement("w:position", options.position));
|
||||||
|
}
|
||||||
|
|
||||||
if (options.size !== undefined) {
|
if (options.size !== undefined) {
|
||||||
this.push(new HpsMeasureElement("w:sz", options.size));
|
this.push(new HpsMeasureElement("w:sz", options.size));
|
||||||
}
|
}
|
||||||
@ -226,6 +256,10 @@ export class RunProperties extends IgnoreIfEmptyXmlComponent {
|
|||||||
this.push(new BorderElement("w:bdr", options.border));
|
this.push(new BorderElement("w:bdr", options.border));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.snapToGrid) {
|
||||||
|
this.push(new OnOffElement("w:snapToGrid", options.snapToGrid));
|
||||||
|
}
|
||||||
|
|
||||||
if (options.vanish) {
|
if (options.vanish) {
|
||||||
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_vanish_topic_ID0E6W3O.html
|
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_vanish_topic_ID0E6W3O.html
|
||||||
// http://www.datypic.com/sc/ooxml/e-w_vanish-1.html
|
// http://www.datypic.com/sc/ooxml/e-w_vanish-1.html
|
||||||
@ -240,6 +274,14 @@ export class RunProperties extends IgnoreIfEmptyXmlComponent {
|
|||||||
if (options.scale !== undefined) {
|
if (options.scale !== undefined) {
|
||||||
this.push(new NumberValueElement("w:w", options.scale));
|
this.push(new NumberValueElement("w:w", options.scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.language) {
|
||||||
|
this.push(createLanguageComponent(options.language));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.math) {
|
||||||
|
this.push(new OnOffElement("w:oMath", options.math));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public push(item: XmlComponent): void {
|
public push(item: XmlComponent): void {
|
||||||
|
@ -7,6 +7,7 @@ import { ShadingType } from "@file/shading";
|
|||||||
import { EmphasisMarkType } from "./emphasis-mark";
|
import { EmphasisMarkType } from "./emphasis-mark";
|
||||||
import { PageNumber, Run } from "./run";
|
import { PageNumber, Run } from "./run";
|
||||||
import { UnderlineType } from "./underline";
|
import { UnderlineType } from "./underline";
|
||||||
|
import { TextEffect } from "./properties";
|
||||||
|
|
||||||
describe("Run", () => {
|
describe("Run", () => {
|
||||||
describe("#bold()", () => {
|
describe("#bold()", () => {
|
||||||
@ -580,5 +581,147 @@ describe("Run", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("#language", () => {
|
||||||
|
it("should correctly set the language", () => {
|
||||||
|
const run = new Run({
|
||||||
|
language: {
|
||||||
|
value: "en-US",
|
||||||
|
eastAsia: "zh-CN",
|
||||||
|
bidirectional: "ar-SA",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(run);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [
|
||||||
|
{
|
||||||
|
"w:lang": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": "en-US",
|
||||||
|
"w:eastAsia": "zh-CN",
|
||||||
|
"w:bidi": "ar-SA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#position", () => {
|
||||||
|
it("should correctly set the position", () => {
|
||||||
|
const run = new Run({
|
||||||
|
position: "2mm",
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(run);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [
|
||||||
|
{
|
||||||
|
"w:position": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": "2mm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#effect", () => {
|
||||||
|
it("should correctly set the effect", () => {
|
||||||
|
const run = new Run({
|
||||||
|
effect: TextEffect.ANTS_BLACK,
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(run);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [
|
||||||
|
{
|
||||||
|
"w:effect": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": "antsBlack",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#math", () => {
|
||||||
|
it("should correctly set the math", () => {
|
||||||
|
const run = new Run({
|
||||||
|
math: true,
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(run);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [
|
||||||
|
{
|
||||||
|
"w:oMath": {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#kern", () => {
|
||||||
|
it("should correctly set the kern", () => {
|
||||||
|
const run = new Run({
|
||||||
|
kern: "2mm",
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(run);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [
|
||||||
|
{
|
||||||
|
"w:kern": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": "2mm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#snapToGrid", () => {
|
||||||
|
it("should correctly set the snapToGrid", () => {
|
||||||
|
const run = new Run({
|
||||||
|
snapToGrid: true,
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(run);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [
|
||||||
|
{
|
||||||
|
"w:snapToGrid": {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -9,10 +9,91 @@ import { Begin, End, Separate } from "./field";
|
|||||||
import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number";
|
import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number";
|
||||||
import { IRunPropertiesOptions, RunProperties } from "./properties";
|
import { IRunPropertiesOptions, RunProperties } from "./properties";
|
||||||
import { Text } from "./run-components/text";
|
import { Text } from "./run-components/text";
|
||||||
import { Tab } from "./tab";
|
import {
|
||||||
|
AnnotationReference,
|
||||||
|
CarriageReturn,
|
||||||
|
ContinuationSeparator,
|
||||||
|
DayLong,
|
||||||
|
DayShort,
|
||||||
|
EndnoteReference,
|
||||||
|
FootnoteReferenceElement,
|
||||||
|
LastRenderedPageBreak,
|
||||||
|
MonthLong,
|
||||||
|
MonthShort,
|
||||||
|
NoBreakHyphen,
|
||||||
|
PageNumberElement,
|
||||||
|
Separator,
|
||||||
|
SoftHyphen,
|
||||||
|
Tab,
|
||||||
|
YearLong,
|
||||||
|
YearShort,
|
||||||
|
} from "./empty-children";
|
||||||
|
import { PositionalTab } from "./positional-tab";
|
||||||
|
|
||||||
export interface IRunOptions extends IRunPropertiesOptions {
|
export interface IRunOptions extends IRunPropertiesOptions {
|
||||||
readonly children?: readonly (Begin | FieldInstruction | Separate | End | PageNumber | FootnoteReferenceRun | Tab | string)[];
|
// <xsd:choice>
|
||||||
|
// <xsd:element name="br" type="CT_Br" />
|
||||||
|
// <xsd:element name="t" type="CT_Text" />
|
||||||
|
// <xsd:element name="contentPart" type="CT_Rel" />
|
||||||
|
// <xsd:element name="delText" type="CT_Text" />
|
||||||
|
// <xsd:element name="instrText" type="CT_Text" />
|
||||||
|
// <xsd:element name="delInstrText" type="CT_Text" />
|
||||||
|
// <xsd:element name="noBreakHyphen" type="CT_Empty" />
|
||||||
|
// <xsd:element name="softHyphen" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="dayShort" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="monthShort" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="yearShort" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="dayLong" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="monthLong" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="yearLong" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="annotationRef" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="footnoteRef" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="endnoteRef" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="separator" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="continuationSeparator" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="sym" type="CT_Sym" minOccurs="0" />
|
||||||
|
// <xsd:element name="pgNum" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="cr" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="tab" type="CT_Empty" minOccurs="0" />
|
||||||
|
// <xsd:element name="object" type="CT_Object" />
|
||||||
|
// <xsd:element name="pict" type="CT_Picture" />
|
||||||
|
// <xsd:element name="fldChar" type="CT_FldChar" />
|
||||||
|
// <xsd:element name="ruby" type="CT_Ruby" />
|
||||||
|
// <xsd:element name="footnoteReference" type="CT_FtnEdnRef" />
|
||||||
|
// <xsd:element name="endnoteReference" type="CT_FtnEdnRef" />
|
||||||
|
// <xsd:element name="commentReference" type="CT_Markup" />
|
||||||
|
// <xsd:element name="drawing" type="CT_Drawing" />
|
||||||
|
// <xsd:element name="ptab" type="CT_PTab" minOccurs="0" />
|
||||||
|
// <xsd:element name="lastRenderedPageBreak" type="CT_Empty" minOccurs="0" maxOccurs="1" />
|
||||||
|
// </xsd:choice>
|
||||||
|
readonly children?: readonly (
|
||||||
|
| Begin
|
||||||
|
| FieldInstruction
|
||||||
|
| Separate
|
||||||
|
| End
|
||||||
|
| PageNumber
|
||||||
|
| FootnoteReferenceRun
|
||||||
|
| Break
|
||||||
|
| AnnotationReference
|
||||||
|
| CarriageReturn
|
||||||
|
| ContinuationSeparator
|
||||||
|
| DayLong
|
||||||
|
| DayShort
|
||||||
|
| EndnoteReference
|
||||||
|
| FootnoteReferenceElement
|
||||||
|
| LastRenderedPageBreak
|
||||||
|
| MonthLong
|
||||||
|
| MonthShort
|
||||||
|
| NoBreakHyphen
|
||||||
|
| PageNumberElement
|
||||||
|
| Separator
|
||||||
|
| SoftHyphen
|
||||||
|
| Tab
|
||||||
|
| YearLong
|
||||||
|
| YearShort
|
||||||
|
| PositionalTab
|
||||||
|
| string
|
||||||
|
)[];
|
||||||
readonly break?: number;
|
readonly break?: number;
|
||||||
readonly text?: string;
|
readonly text?: string;
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
import { expect } from "chai";
|
|
||||||
|
|
||||||
import { Formatter } from "@export/formatter";
|
|
||||||
|
|
||||||
import { Tab } from "./tab";
|
|
||||||
|
|
||||||
describe("Tab", () => {
|
|
||||||
let tab: Tab;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
tab = new Tab();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#constructor()", () => {
|
|
||||||
it("should create a Tab with correct root key", () => {
|
|
||||||
const tree = new Formatter().format(tab);
|
|
||||||
expect(tree).to.deep.equal({
|
|
||||||
"w:tab": {},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,14 +0,0 @@
|
|||||||
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_tab_topic_ID0EM6AO.html
|
|
||||||
import { XmlComponent } from "@file/xml-components";
|
|
||||||
|
|
||||||
// <xsd:group name="EG_RunInnerContent">
|
|
||||||
// ...
|
|
||||||
// <xsd:element name="tab" type="CT_Empty" minOccurs="0"/>
|
|
||||||
//
|
|
||||||
// TODO: this is unused and undocumented currently.
|
|
||||||
// I think the intended use was for users to import, and insert as a child of `Run`.
|
|
||||||
export class Tab extends XmlComponent {
|
|
||||||
public constructor() {
|
|
||||||
super("w:tab");
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,13 +12,9 @@ export class Relationships extends XmlComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addRelationship(relationship: Relationship): void {
|
|
||||||
this.root.push(relationship);
|
|
||||||
}
|
|
||||||
|
|
||||||
public createRelationship(id: number | string, type: RelationshipType, target: string, targetMode?: TargetModeType): Relationship {
|
public createRelationship(id: number | string, type: RelationshipType, target: string, targetMode?: TargetModeType): Relationship {
|
||||||
const relationship = new Relationship(`rId${id}`, type, target, targetMode);
|
const relationship = new Relationship(`rId${id}`, type, target, targetMode);
|
||||||
this.addRelationship(relationship);
|
this.root.push(relationship);
|
||||||
|
|
||||||
return relationship;
|
return relationship;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
const createLsdException = (name: string, uiPriority?: number, qFormat?: number, semiHidden?: number, unhideWhenUsed?: number) => {
|
const createLsdException = (
|
||||||
|
name: string,
|
||||||
|
uiPriority?: number,
|
||||||
|
qFormat?: number,
|
||||||
|
semiHidden?: number,
|
||||||
|
unhideWhenUsed?: number,
|
||||||
|
): readonly object[] => {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
@ -8,7 +8,6 @@ export interface IBaseCharacterStyleOptions extends IStyleOptions {
|
|||||||
|
|
||||||
export interface ICharacterStyleOptions extends IBaseCharacterStyleOptions {
|
export interface ICharacterStyleOptions extends IBaseCharacterStyleOptions {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly name?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StyleForCharacter extends Style {
|
export class StyleForCharacter extends Style {
|
||||||
|
@ -9,7 +9,6 @@ export interface IBaseParagraphStyleOptions extends IStyleOptions {
|
|||||||
|
|
||||||
export interface IParagraphStyleOptions extends IBaseParagraphStyleOptions {
|
export interface IParagraphStyleOptions extends IBaseParagraphStyleOptions {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly name?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StyleForParagraph extends Style {
|
export class StyleForParagraph extends Style {
|
||||||
|
@ -41,6 +41,13 @@ export interface IStyleAttributes {
|
|||||||
|
|
||||||
export interface IStyleOptions {
|
export interface IStyleOptions {
|
||||||
readonly name?: string;
|
readonly name?: string;
|
||||||
|
/**
|
||||||
|
* Specifies the style upon which the current style is based-that is, the style from which the current style inherits. It is the mechanism for implementing style inheritance.
|
||||||
|
* Note that if the type of the current style must match the type of the style upon which it is based or the basedOn element will be ignored.
|
||||||
|
* However, if the current style is a numbering style, then the `basedOn` element is ignored.
|
||||||
|
*
|
||||||
|
* **WARNING**: You cannot set `basedOn` to be the same as `name`. This is akin to inheriting from itself. This creates a cyclic dependency and cause undesirable behavior.
|
||||||
|
*/
|
||||||
readonly basedOn?: string;
|
readonly basedOn?: string;
|
||||||
readonly next?: string;
|
readonly next?: string;
|
||||||
readonly link?: string;
|
readonly link?: string;
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
import { Paragraph } from "@file/paragraph";
|
import { Paragraph } from "@file/paragraph";
|
||||||
import { Run } from "@file/paragraph/run";
|
import { Run } from "@file/paragraph/run";
|
||||||
import { Begin, End, Separate } from "@file/paragraph/run/field";
|
import { Begin, End, Separate } from "@file/paragraph/run/field";
|
||||||
import { XmlComponent } from "@file/xml-components";
|
import { FileChild } from "@file/file-child";
|
||||||
import { FieldInstruction } from "./field-instruction";
|
import { FieldInstruction } from "./field-instruction";
|
||||||
import { StructuredDocumentTagContent } from "./sdt-content";
|
import { StructuredDocumentTagContent } from "./sdt-content";
|
||||||
import { StructuredDocumentTagProperties } from "./sdt-properties";
|
import { StructuredDocumentTagProperties } from "./sdt-properties";
|
||||||
import { ITableOfContentsOptions } from "./table-of-contents-properties";
|
import { ITableOfContentsOptions } from "./table-of-contents-properties";
|
||||||
|
|
||||||
export class TableOfContents extends XmlComponent {
|
export class TableOfContents extends FileChild {
|
||||||
public constructor(alias: string = "Table of Contents", properties?: ITableOfContentsOptions) {
|
public constructor(alias: string = "Table of Contents", properties?: ITableOfContentsOptions) {
|
||||||
super("w:sdt");
|
super("w:sdt");
|
||||||
this.root.push(new StructuredDocumentTagProperties(alias));
|
this.root.push(new StructuredDocumentTagProperties(alias));
|
||||||
|
@ -9,11 +9,11 @@
|
|||||||
// </xsd:sequence>
|
// </xsd:sequence>
|
||||||
// </xsd:complexType>
|
// </xsd:complexType>
|
||||||
|
|
||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
import { twipsMeasureValue } from "@util/values";
|
import { PositiveUniversalMeasure, twipsMeasureValue } from "@util/values";
|
||||||
|
|
||||||
export class TableGrid extends XmlComponent {
|
export class TableGrid extends XmlComponent {
|
||||||
public constructor(widths: readonly number[] | readonly string[]) {
|
public constructor(widths: readonly number[] | readonly PositiveUniversalMeasure[]) {
|
||||||
super("w:tblGrid");
|
super("w:tblGrid");
|
||||||
for (const width of widths) {
|
for (const width of widths) {
|
||||||
this.root.push(new GridCol(width));
|
this.root.push(new GridCol(width));
|
||||||
@ -21,15 +21,15 @@ export class TableGrid extends XmlComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GridColAttributes extends XmlAttributeComponent<{ readonly w: number | string }> {
|
|
||||||
protected readonly xmlKeys = { w: "w:w" };
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GridCol extends XmlComponent {
|
export class GridCol extends XmlComponent {
|
||||||
public constructor(width?: number | string) {
|
public constructor(width?: number | PositiveUniversalMeasure) {
|
||||||
super("w:gridCol");
|
super("w:gridCol");
|
||||||
if (width !== undefined) {
|
if (width !== undefined) {
|
||||||
this.root.push(new GridColAttributes({ w: twipsMeasureValue(width) }));
|
this.root.push(
|
||||||
|
new NextAttributeComponent<{ readonly width: number | PositiveUniversalMeasure }>({
|
||||||
|
width: { key: "w:w", value: twipsMeasureValue(width) },
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,7 @@ describe("Table Float Properties", () => {
|
|||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:tblpPr": [
|
"w:tblpPr": [
|
||||||
{
|
{
|
||||||
_attr: {
|
_attr: {},
|
||||||
overlap: "never",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"w:tblOverlap": {
|
"w:tblOverlap": {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { StringEnumValueElement, XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { NextAttributeComponent, StringEnumValueElement, XmlComponent } from "@file/xml-components";
|
||||||
import { signedTwipsMeasureValue, twipsMeasureValue } from "@util/values";
|
import { PositiveUniversalMeasure, signedTwipsMeasureValue, twipsMeasureValue, UniversalMeasure } from "@util/values";
|
||||||
|
|
||||||
export enum TableAnchorType {
|
export enum TableAnchorType {
|
||||||
MARGIN = "margin",
|
MARGIN = "margin",
|
||||||
@ -35,7 +35,7 @@ export enum OverlapType {
|
|||||||
OVERLAP = "overlap",
|
OVERLAP = "overlap",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITableFloatOptions {
|
export type ITableFloatOptions = {
|
||||||
/* cSpell:disable */
|
/* cSpell:disable */
|
||||||
/**
|
/**
|
||||||
* Specifies the horizontal anchor or the base object from which the horizontal positioning in the
|
* Specifies the horizontal anchor or the base object from which the horizontal positioning in the
|
||||||
@ -55,7 +55,7 @@ export interface ITableFloatOptions {
|
|||||||
* If relativeHorizontalPosition is also specified, then the absoluteHorizontalPosition attribute is ignored.
|
* If relativeHorizontalPosition is also specified, then the absoluteHorizontalPosition attribute is ignored.
|
||||||
* If the attribute is omitted, the value is assumed to be zero.
|
* If the attribute is omitted, the value is assumed to be zero.
|
||||||
*/
|
*/
|
||||||
readonly absoluteHorizontalPosition?: number | string;
|
readonly absoluteHorizontalPosition?: number | UniversalMeasure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies a relative horizontal position for the table, relative to the horizontalAnchor attribute.
|
* Specifies a relative horizontal position for the table, relative to the horizontalAnchor attribute.
|
||||||
@ -86,7 +86,7 @@ export interface ITableFloatOptions {
|
|||||||
* If relativeVerticalPosition is also specified, then the absoluteVerticalPosition attribute is ignored.
|
* If relativeVerticalPosition is also specified, then the absoluteVerticalPosition attribute is ignored.
|
||||||
* If the attribute is omitted, the value is assumed to be zero.
|
* If the attribute is omitted, the value is assumed to be zero.
|
||||||
*/
|
*/
|
||||||
readonly absoluteVerticalPosition?: number | string;
|
readonly absoluteVerticalPosition?: number | UniversalMeasure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies a relative vertical position for the table, relative to the verticalAnchor attribute.
|
* Specifies a relative vertical position for the table, relative to the verticalAnchor attribute.
|
||||||
@ -104,27 +104,27 @@ export interface ITableFloatOptions {
|
|||||||
* Specifies the minimum distance to be maintained between the table and the top of text in the paragraph
|
* Specifies the minimum distance to be maintained between the table and the top of text in the paragraph
|
||||||
* below the table. The value is in twentieths of a point. If omitted, the value is assumed to be zero.
|
* below the table. The value is in twentieths of a point. If omitted, the value is assumed to be zero.
|
||||||
*/
|
*/
|
||||||
readonly bottomFromText?: number | string;
|
readonly bottomFromText?: number | PositiveUniversalMeasure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the minimum distance to be maintained between the table and the bottom edge of text in the paragraph
|
* Specifies the minimum distance to be maintained between the table and the bottom edge of text in the paragraph
|
||||||
* above the table. The value is in twentieths of a point. If omitted, the value is assumed to be zero.
|
* above the table. The value is in twentieths of a point. If omitted, the value is assumed to be zero.
|
||||||
*/
|
*/
|
||||||
readonly topFromText?: number | string;
|
readonly topFromText?: number | PositiveUniversalMeasure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the minimum distance to be maintained between the table and the edge of text in the paragraph
|
* Specifies the minimum distance to be maintained between the table and the edge of text in the paragraph
|
||||||
* to the left of the table. The value is in twentieths of a point. If omitted, the value is assumed to be zero.
|
* to the left of the table. The value is in twentieths of a point. If omitted, the value is assumed to be zero.
|
||||||
*/
|
*/
|
||||||
readonly leftFromText?: number | string;
|
readonly leftFromText?: number | PositiveUniversalMeasure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the minimum distance to be maintained between the table and the edge of text in the paragraph
|
* Specifies the minimum distance to be maintained between the table and the edge of text in the paragraph
|
||||||
* to the right of the table. The value is in twentieths of a point. If omitted, the value is assumed to be zero.
|
* to the right of the table. The value is in twentieths of a point. If omitted, the value is assumed to be zero.
|
||||||
*/
|
*/
|
||||||
readonly rightFromText?: number | string;
|
readonly rightFromText?: number | PositiveUniversalMeasure;
|
||||||
readonly overlap?: OverlapType;
|
readonly overlap?: OverlapType;
|
||||||
}
|
};
|
||||||
|
|
||||||
// <xsd:complexType name="CT_TblPPr">
|
// <xsd:complexType name="CT_TblPPr">
|
||||||
// <xsd:attribute name="leftFromText" type="s:ST_TwipsMeasure"/>
|
// <xsd:attribute name="leftFromText" type="s:ST_TwipsMeasure"/>
|
||||||
@ -139,51 +139,65 @@ export interface ITableFloatOptions {
|
|||||||
// <xsd:attribute name="tblpY" type="ST_SignedTwipsMeasure"/>
|
// <xsd:attribute name="tblpY" type="ST_SignedTwipsMeasure"/>
|
||||||
// </xsd:complexType>
|
// </xsd:complexType>
|
||||||
|
|
||||||
export class TableFloatOptionsAttributes extends XmlAttributeComponent<ITableFloatOptions> {
|
|
||||||
protected readonly xmlKeys = {
|
|
||||||
horizontalAnchor: "w:horzAnchor",
|
|
||||||
verticalAnchor: "w:vertAnchor",
|
|
||||||
absoluteHorizontalPosition: "w:tblpX",
|
|
||||||
relativeHorizontalPosition: "w:tblpXSpec",
|
|
||||||
absoluteVerticalPosition: "w:tblpY",
|
|
||||||
relativeVerticalPosition: "w:tblpYSpec",
|
|
||||||
bottomFromText: "w:bottomFromText",
|
|
||||||
topFromText: "w:topFromText",
|
|
||||||
leftFromText: "w:leftFromText",
|
|
||||||
rightFromText: "w:rightFromText",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TableFloatProperties extends XmlComponent {
|
export class TableFloatProperties extends XmlComponent {
|
||||||
public constructor({
|
public constructor({
|
||||||
|
horizontalAnchor,
|
||||||
|
verticalAnchor,
|
||||||
|
absoluteHorizontalPosition,
|
||||||
|
relativeHorizontalPosition,
|
||||||
|
absoluteVerticalPosition,
|
||||||
|
relativeVerticalPosition,
|
||||||
|
bottomFromText,
|
||||||
|
topFromText,
|
||||||
leftFromText,
|
leftFromText,
|
||||||
rightFromText,
|
rightFromText,
|
||||||
topFromText,
|
overlap,
|
||||||
bottomFromText,
|
|
||||||
absoluteHorizontalPosition,
|
|
||||||
absoluteVerticalPosition,
|
|
||||||
...options
|
|
||||||
}: ITableFloatOptions) {
|
}: ITableFloatOptions) {
|
||||||
super("w:tblpPr");
|
super("w:tblpPr");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new TableFloatOptionsAttributes({
|
new NextAttributeComponent<Omit<ITableFloatOptions, "overlap">>({
|
||||||
leftFromText: leftFromText === undefined ? undefined : twipsMeasureValue(leftFromText),
|
leftFromText: { key: "w:leftFromText", value: leftFromText === undefined ? undefined : twipsMeasureValue(leftFromText) },
|
||||||
rightFromText: rightFromText === undefined ? undefined : twipsMeasureValue(rightFromText),
|
rightFromText: {
|
||||||
topFromText: topFromText === undefined ? undefined : twipsMeasureValue(topFromText),
|
key: "w:rightFromText",
|
||||||
bottomFromText: bottomFromText === undefined ? undefined : twipsMeasureValue(bottomFromText),
|
value: rightFromText === undefined ? undefined : twipsMeasureValue(rightFromText),
|
||||||
absoluteHorizontalPosition:
|
},
|
||||||
absoluteHorizontalPosition === undefined ? undefined : signedTwipsMeasureValue(absoluteHorizontalPosition),
|
topFromText: { key: "w:topFromText", value: topFromText === undefined ? undefined : twipsMeasureValue(topFromText) },
|
||||||
absoluteVerticalPosition:
|
bottomFromText: {
|
||||||
absoluteVerticalPosition === undefined ? undefined : signedTwipsMeasureValue(absoluteVerticalPosition),
|
key: "w:bottomFromText",
|
||||||
...options,
|
value: bottomFromText === undefined ? undefined : twipsMeasureValue(bottomFromText),
|
||||||
|
},
|
||||||
|
absoluteHorizontalPosition: {
|
||||||
|
key: "w:tblpX",
|
||||||
|
value: absoluteHorizontalPosition === undefined ? undefined : signedTwipsMeasureValue(absoluteHorizontalPosition),
|
||||||
|
},
|
||||||
|
absoluteVerticalPosition: {
|
||||||
|
key: "w:tblpY",
|
||||||
|
value: absoluteVerticalPosition === undefined ? undefined : signedTwipsMeasureValue(absoluteVerticalPosition),
|
||||||
|
},
|
||||||
|
horizontalAnchor: {
|
||||||
|
key: "w:horzAnchor",
|
||||||
|
value: horizontalAnchor === undefined ? undefined : horizontalAnchor,
|
||||||
|
},
|
||||||
|
relativeHorizontalPosition: {
|
||||||
|
key: "w:tblpXSpec",
|
||||||
|
value: relativeHorizontalPosition,
|
||||||
|
},
|
||||||
|
relativeVerticalPosition: {
|
||||||
|
key: "w:tblpYSpec",
|
||||||
|
value: relativeVerticalPosition,
|
||||||
|
},
|
||||||
|
verticalAnchor: {
|
||||||
|
key: "w:vertAnchor",
|
||||||
|
value: verticalAnchor,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (options.overlap) {
|
if (overlap) {
|
||||||
// <xsd:complexType name="CT_TblOverlap">
|
// <xsd:complexType name="CT_TblOverlap">
|
||||||
// <xsd:attribute name="val" type="ST_TblOverlap" use="required"/>
|
// <xsd:attribute name="val" type="ST_TblOverlap" use="required"/>
|
||||||
// </xsd:complexType>
|
// </xsd:complexType>
|
||||||
this.root.push(new StringEnumValueElement<OverlapType>("w:tblOverlap", options.overlap));
|
this.root.push(new StringEnumValueElement<OverlapType>("w:tblOverlap", overlap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
import { twipsMeasureValue } from "@util/values";
|
import { PositiveUniversalMeasure, twipsMeasureValue } from "@util/values";
|
||||||
|
|
||||||
// <xsd:complexType name="CT_Height">
|
// <xsd:complexType name="CT_Height">
|
||||||
// <xsd:attribute name="val" type="s:ST_TwipsMeasure"/>
|
// <xsd:attribute name="val" type="s:ST_TwipsMeasure"/>
|
||||||
@ -30,7 +30,7 @@ export class TableRowHeightAttributes extends XmlAttributeComponent<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TableRowHeight extends XmlComponent {
|
export class TableRowHeight extends XmlComponent {
|
||||||
public constructor(value: number | string, rule: HeightRule) {
|
public constructor(value: number | PositiveUniversalMeasure, rule: HeightRule) {
|
||||||
super("w:trHeight");
|
super("w:trHeight");
|
||||||
|
|
||||||
this.root.push(
|
this.root.push(
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
// </xsd:complexContent>
|
// </xsd:complexContent>
|
||||||
// </xsd:complexType>
|
// </xsd:complexType>
|
||||||
import { IgnoreIfEmptyXmlComponent, OnOffElement } from "@file/xml-components";
|
import { IgnoreIfEmptyXmlComponent, OnOffElement } from "@file/xml-components";
|
||||||
|
import { PositiveUniversalMeasure } from "@util/values";
|
||||||
|
|
||||||
import { HeightRule, TableRowHeight } from "./table-row-height";
|
import { HeightRule, TableRowHeight } from "./table-row-height";
|
||||||
|
|
||||||
@ -35,7 +36,7 @@ export interface ITableRowPropertiesOptions {
|
|||||||
readonly cantSplit?: boolean;
|
readonly cantSplit?: boolean;
|
||||||
readonly tableHeader?: boolean;
|
readonly tableHeader?: boolean;
|
||||||
readonly height?: {
|
readonly height?: {
|
||||||
readonly value: number | string;
|
readonly value: number | PositiveUniversalMeasure;
|
||||||
readonly rule: HeightRule;
|
readonly rule: HeightRule;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// http://officeopenxml.com/WPtableWidth.php
|
// http://officeopenxml.com/WPtableWidth.php
|
||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
import { measurementOrPercentValue } from "@util/values";
|
import { measurementOrPercentValue, Percentage, UniversalMeasure } from "@util/values";
|
||||||
|
|
||||||
// <xsd:simpleType name="ST_TblWidth">
|
// <xsd:simpleType name="ST_TblWidth">
|
||||||
// <xsd:restriction base="xsd:string">
|
// <xsd:restriction base="xsd:string">
|
||||||
@ -25,14 +25,10 @@ export enum WidthType {
|
|||||||
// <xsd:attribute name="w" type="ST_MeasurementOrPercent"/>
|
// <xsd:attribute name="w" type="ST_MeasurementOrPercent"/>
|
||||||
// <xsd:attribute name="type" type="ST_TblWidth"/>
|
// <xsd:attribute name="type" type="ST_TblWidth"/>
|
||||||
// </xsd:complexType>
|
// </xsd:complexType>
|
||||||
export interface ITableWidthProperties {
|
export type ITableWidthProperties = {
|
||||||
readonly size: string | number;
|
readonly size: number | Percentage | UniversalMeasure;
|
||||||
readonly type?: WidthType;
|
readonly type?: WidthType;
|
||||||
}
|
};
|
||||||
|
|
||||||
class TableWidthAttributes extends XmlAttributeComponent<ITableWidthProperties> {
|
|
||||||
protected readonly xmlKeys = { type: "w:type", size: "w:w" };
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TableWidthElement extends XmlComponent {
|
export class TableWidthElement extends XmlComponent {
|
||||||
public constructor(name: string, { type = WidthType.AUTO, size }: ITableWidthProperties) {
|
public constructor(name: string, { type = WidthType.AUTO, size }: ITableWidthProperties) {
|
||||||
@ -42,6 +38,12 @@ export class TableWidthElement extends XmlComponent {
|
|||||||
if (type === WidthType.PERCENTAGE && typeof size === "number") {
|
if (type === WidthType.PERCENTAGE && typeof size === "number") {
|
||||||
tableWidthValue = `${size}%`;
|
tableWidthValue = `${size}%`;
|
||||||
}
|
}
|
||||||
this.root.push(new TableWidthAttributes({ type: type, size: measurementOrPercentValue(tableWidthValue) }));
|
|
||||||
|
this.root.push(
|
||||||
|
new NextAttributeComponent<ITableWidthProperties>({
|
||||||
|
type: { key: "w:type", value: type },
|
||||||
|
size: { key: "w:w", value: measurementOrPercentValue(tableWidthValue) },
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// http://officeopenxml.com/WPtableGrid.php
|
// http://officeopenxml.com/WPtableGrid.php
|
||||||
import { XmlComponent } from "@file/xml-components";
|
import { FileChild } from "@file/file-child";
|
||||||
|
|
||||||
import { AlignmentType } from "../paragraph";
|
import { AlignmentType } from "../paragraph";
|
||||||
import { TableGrid } from "./grid";
|
import { TableGrid } from "./grid";
|
||||||
@ -34,7 +34,7 @@ export interface ITableOptions {
|
|||||||
readonly visuallyRightToLeft?: boolean;
|
readonly visuallyRightToLeft?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Table extends XmlComponent {
|
export class Table extends FileChild {
|
||||||
public constructor({
|
public constructor({
|
||||||
rows,
|
rows,
|
||||||
width,
|
width,
|
||||||
|
@ -5,6 +5,8 @@ import { IXmlableObject } from "./xmlable-object";
|
|||||||
export interface IContext {
|
export interface IContext {
|
||||||
readonly file: File;
|
readonly file: File;
|
||||||
readonly viewWrapper: IViewWrapper;
|
readonly viewWrapper: IViewWrapper;
|
||||||
|
// eslint-disable-next-line functional/prefer-readonly-type
|
||||||
|
readonly stack: IXmlableObject[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class BaseXmlComponent {
|
export abstract class BaseXmlComponent {
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { BaseXmlComponent, IContext } from "./base";
|
import { BaseXmlComponent, IContext } from "./base";
|
||||||
import { IXmlableObject } from "./xmlable-object";
|
import { IXmlableObject, IXmlAttribute } from "./xmlable-object";
|
||||||
|
|
||||||
export type AttributeMap<T> = { readonly [P in keyof T]: string };
|
export type AttributeMap<T> = { readonly [P in keyof T]: string };
|
||||||
|
|
||||||
|
export type AttributeData = { readonly [key: string]: boolean | number | string };
|
||||||
|
export type AttributePayload<T> = { readonly [P in keyof T]: { readonly key: string; readonly value: T[P] } };
|
||||||
|
|
||||||
export abstract class XmlAttributeComponent<T extends object> extends BaseXmlComponent {
|
export abstract class XmlAttributeComponent<T extends object> extends BaseXmlComponent {
|
||||||
// tslint:disable-next-line:readonly-keyword
|
|
||||||
protected readonly root: T;
|
|
||||||
protected readonly xmlKeys?: AttributeMap<T>;
|
protected readonly xmlKeys?: AttributeMap<T>;
|
||||||
|
|
||||||
public constructor(properties: T) {
|
public constructor(private readonly root: T) {
|
||||||
super("_attr");
|
super("_attr");
|
||||||
this.root = properties;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public prepForXml(_: IContext): IXmlableObject {
|
public prepForXml(_: IContext): IXmlableObject {
|
||||||
@ -26,3 +26,16 @@ export abstract class XmlAttributeComponent<T extends object> extends BaseXmlCom
|
|||||||
return { _attr: attrs };
|
return { _attr: attrs };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class NextAttributeComponent<T extends AttributeData> extends BaseXmlComponent {
|
||||||
|
public constructor(private readonly root: AttributePayload<T>) {
|
||||||
|
super("_attr");
|
||||||
|
}
|
||||||
|
|
||||||
|
public prepForXml(_: IContext): IXmlableObject {
|
||||||
|
const attrs = Object.values<{ readonly key: string; readonly value: string | boolean | number }>(this.root)
|
||||||
|
.filter(({ value }) => value !== undefined)
|
||||||
|
.reduce((acc, { key, value }) => ({ ...acc, [key]: value }), {} as IXmlAttribute);
|
||||||
|
return { _attr: attrs };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -62,7 +62,7 @@ describe("ImportedXmlComponent", () => {
|
|||||||
describe("#prepForXml()", () => {
|
describe("#prepForXml()", () => {
|
||||||
it("should transform for xml", () => {
|
it("should transform for xml", () => {
|
||||||
// tslint:disable-next-line: no-object-literal-type-assertion
|
// tslint:disable-next-line: no-object-literal-type-assertion
|
||||||
const converted = importedXmlComponent.prepForXml({} as IContext);
|
const converted = importedXmlComponent.prepForXml({ stack: [] } as unknown as IContext);
|
||||||
expect(converted).to.deep.equal({
|
expect(converted).to.deep.equal({
|
||||||
"w:test": [
|
"w:test": [
|
||||||
{
|
{
|
||||||
|
41
src/file/xml-components/simple-elements.spec.ts
Normal file
41
src/file/xml-components/simple-elements.spec.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
|
||||||
|
import { BuilderElement } from "./simple-elements";
|
||||||
|
|
||||||
|
describe("BuilderElement", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a simple BuilderElement", () => {
|
||||||
|
const element = new BuilderElement({
|
||||||
|
name: "test",
|
||||||
|
});
|
||||||
|
|
||||||
|
const tree = new Formatter().format(element);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
test: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a simple BuilderElement with attributes", () => {
|
||||||
|
const element = new BuilderElement<{ readonly testAttr: string }>({
|
||||||
|
name: "test",
|
||||||
|
attributes: {
|
||||||
|
testAttr: {
|
||||||
|
key: "w:testAttr",
|
||||||
|
value: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const tree = new Formatter().format(element);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
test: {
|
||||||
|
_attr: {
|
||||||
|
"w:testAttr": "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,6 +1,6 @@
|
|||||||
import { Attributes, XmlComponent } from "@file/xml-components";
|
import { AttributeData, AttributePayload, Attributes, NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
import { hpsMeasureValue } from "@util/values";
|
import { hpsMeasureValue, PositiveUniversalMeasure } from "@util/values";
|
||||||
|
|
||||||
// This represents element type CT_OnOff, which indicate a boolean value.
|
// This represents element type CT_OnOff, which indicate a boolean value.
|
||||||
//
|
//
|
||||||
@ -26,8 +26,13 @@ export class OnOffElement extends XmlComponent {
|
|||||||
// <xsd:complexType name="CT_HpsMeasure">
|
// <xsd:complexType name="CT_HpsMeasure">
|
||||||
// <xsd:attribute name="val" type="ST_HpsMeasure" use="required"/>
|
// <xsd:attribute name="val" type="ST_HpsMeasure" use="required"/>
|
||||||
// </xsd:complexType>
|
// </xsd:complexType>
|
||||||
|
|
||||||
|
// <xsd:simpleType name="ST_HpsMeasure">
|
||||||
|
// <xsd:union memberTypes="s:ST_UnsignedDecimalNumber s:ST_PositiveUniversalMeasure" />
|
||||||
|
// </xsd:simpleType>
|
||||||
|
|
||||||
export class HpsMeasureElement extends XmlComponent {
|
export class HpsMeasureElement extends XmlComponent {
|
||||||
public constructor(name: string, val: number | string) {
|
public constructor(name: string, val: number | PositiveUniversalMeasure) {
|
||||||
super(name);
|
super(name);
|
||||||
this.root.push(new Attributes({ val: hpsMeasureValue(val) }));
|
this.root.push(new Attributes({ val: hpsMeasureValue(val) }));
|
||||||
}
|
}
|
||||||
@ -35,6 +40,11 @@ export class HpsMeasureElement extends XmlComponent {
|
|||||||
|
|
||||||
// This represents element type CT_String, which indicate a string value.
|
// This represents element type CT_String, which indicate a string value.
|
||||||
//
|
//
|
||||||
|
// <xsd:complexType name="CT_Empty"/>
|
||||||
|
export class EmptyElement extends XmlComponent {}
|
||||||
|
|
||||||
|
// This represents element type CT_Empty, which indicate aan empty element.
|
||||||
|
//
|
||||||
// <xsd:complexType name="CT_String">
|
// <xsd:complexType name="CT_String">
|
||||||
// <xsd:attribute name="val" type="s:ST_String" use="required"/>
|
// <xsd:attribute name="val" type="s:ST_String" use="required"/>
|
||||||
// </xsd:complexType>
|
// </xsd:complexType>
|
||||||
@ -70,3 +80,19 @@ export class StringContainer extends XmlComponent {
|
|||||||
this.root.push(val);
|
this.root.push(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class BuilderElement<T extends AttributeData> extends XmlComponent {
|
||||||
|
public constructor(options: {
|
||||||
|
readonly name: string;
|
||||||
|
readonly attributes?: AttributePayload<T>;
|
||||||
|
readonly children?: readonly XmlComponent[];
|
||||||
|
}) {
|
||||||
|
super(options.name);
|
||||||
|
|
||||||
|
if (options.attributes) {
|
||||||
|
this.root.push(new NextAttributeComponent(options.attributes));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user