Compare commits

...

30 Commits
5.1.0 ... 5.2.2

Author SHA1 Message Date
b8232f7a02 Version bump 2020-07-11 19:34:29 +01:00
49eadb0efc Merge pull request #559 from wangfengming/master
:fix: `rowSpan` can't work when column index out of range
2020-07-09 01:29:06 +01:00
40dc90e585 :fix: insert the continue cell properly 2020-07-08 12:32:01 +08:00
0de302d192 :typo: update comments 2020-07-08 11:26:24 +08:00
80bab95f6c :fix: rowSpan can't work when column index out of range 2020-07-08 10:55:15 +08:00
ba3d551c9f Version bump 2020-06-29 01:17:13 +01:00
d14fe31f97 Merge pull request #553 from wangfengming/master
Fix: `rowSpan` does not work correctly
2020-06-24 16:39:57 +01:00
057f41e355 :test: more test cases 2020-06-22 12:34:08 +08:00
8c9b61b37a :fix: handle rowSpan by convert between the virtual column index and the root index 2020-06-22 12:25:51 +08:00
11e54b3e2c :fix: handle cell that has both columnSpan and rowSpan 2020-06-20 21:36:35 +08:00
fa7cb0bef1 :test: add test case for columnSpan and rowSpan 2020-06-20 21:01:23 +08:00
3977c8ab3b :fix: rowSpan continue cell should has the same border to the first row cell 2020-06-20 20:20:22 +08:00
e8f92efe05 Merge pull request #2 from dolanmiu/master
merge from origin
2020-06-20 20:05:20 +08:00
994df8531b :fix: rowSpan does not work correctly 2020-06-20 19:47:46 +08:00
3cdf96ee0c Version bump 2020-06-17 14:34:27 +01:00
e2f55d52e9 Merge pull request #550 from wangfengming/feature/enhance-font
Feature/enhance font
2020-06-13 20:38:22 +01:00
d6fa33035a :doc: update demo 53-chinese.ts 2020-06-07 14:58:59 +08:00
f11bca728f :typo: update comment for the demo 2020-06-07 12:46:21 +08:00
596761d78d :doc: doc and demo for "Font for eastAsia" 2020-06-07 12:39:17 +08:00
8a3c8d4664 :test: Font for eastAsia 2020-06-07 12:38:31 +08:00
fdfce79e87 :feat: Font for eastAsia 2020-06-07 12:38:03 +08:00
88340aa336 Merge pull request #1 from dolanmiu/master
merge from origin
2020-06-03 19:04:55 +08:00
d0f53fdd4b Merge branch 'master' of github.com:dolanmiu/docx
# Conflicts:
#	package-lock.json
2020-05-28 18:40:44 +01:00
d657f61e11 Update node types 2020-05-28 18:38:55 +01:00
20e6b98770 Version bump 2020-05-26 21:07:30 +01:00
5ae824358c Revert "Update numbering.ts"
This reverts commit 0ebdcc30ed.
2020-05-26 21:06:43 +01:00
3ea106bd22 Merge pull request #546 from wangfengming/feature/emphasis-mark
Feature/emphasis mark
2020-05-22 13:29:25 +01:00
538264dec5 :demo: support emphasis mark 2020-05-22 12:56:02 +08:00
6bcd1c2c24 :doc: support emphasis mark 2020-05-22 12:32:40 +08:00
120c3a7bbe :feat: support emphasis mark 2020-05-22 12:22:45 +08:00
30 changed files with 1067 additions and 124 deletions

View File

@ -161,6 +161,10 @@ doc.addSection({
text: "and then underlined ", text: "and then underlined ",
underline: {}, underline: {},
}), }),
new TextRun({
text: "and then emphasis-mark ",
emphasisMark: {},
}),
new TextRun({ new TextRun({
text: "and back to normal.", text: "and back to normal.",
}), }),

View File

@ -2,7 +2,7 @@
// Also includes an example on how to center tables // Also includes an example on how to center tables
// Import from 'docx' rather than '../build' if you install from npm // Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { AlignmentType, Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build"; import { AlignmentType, BorderStyle, Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build";
const doc = new Document(); const doc = new Document();
@ -184,7 +184,7 @@ const table5 = new Table({
new TableRow({ new TableRow({
children: [ children: [
new TableCell({ new TableCell({
children: [], children: [new Paragraph("1,0")],
}), }),
new TableCell({ new TableCell({
children: [new Paragraph("1,2")], children: [new Paragraph("1,2")],
@ -195,10 +195,10 @@ const table5 = new Table({
new TableRow({ new TableRow({
children: [ children: [
new TableCell({ new TableCell({
children: [], children: [new Paragraph("2,0")],
}), }),
new TableCell({ new TableCell({
children: [], children: [new Paragraph("2,1")],
}), }),
], ],
}), }),
@ -209,6 +209,163 @@ const table5 = new Table({
}, },
}); });
const borders = {
top: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
},
bottom: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
},
left: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
},
right: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
},
};
const table6 = new Table({
rows: [
new TableRow({
children: [
new TableCell({
borders,
children: [new Paragraph("0,0")],
rowSpan: 2,
}),
new TableCell({
borders,
children: [new Paragraph("0,1")],
}),
],
}),
new TableRow({
children: [
new TableCell({
borders,
children: [new Paragraph("1,1")],
rowSpan: 2,
}),
],
}),
new TableRow({
children: [
new TableCell({
borders,
children: [new Paragraph("2,0")],
}),
],
}),
],
width: {
size: 100,
type: WidthType.PERCENTAGE,
},
});
const table7 = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("0,0")],
}),
new TableCell({
children: [new Paragraph("0,1")],
}),
new TableCell({
children: [new Paragraph("0,2")],
rowSpan: 2,
}),
new TableCell({
children: [new Paragraph("0,3")],
rowSpan: 3,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("1,0")],
columnSpan: 2,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("2,0")],
columnSpan: 2,
}),
new TableCell({
children: [new Paragraph("2,2")],
rowSpan: 2,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("3,0")],
}),
new TableCell({
children: [new Paragraph("3,1")],
}),
new TableCell({
children: [new Paragraph("3,3")],
}),
],
}),
],
width: {
size: 100,
type: WidthType.PERCENTAGE,
},
});
const table8 = new Table({
rows: [
new TableRow({
children: [
new TableCell({ children: [new Paragraph("1,1")] }),
new TableCell({ children: [new Paragraph("1,2")] }),
new TableCell({ children: [new Paragraph("1,3")] }),
new TableCell({ children: [new Paragraph("1,4")], rowSpan: 4, borders }),
],
}),
new TableRow({
children: [
new TableCell({ children: [new Paragraph("2,1")] }),
new TableCell({ children: [new Paragraph("2,2")] }),
new TableCell({ children: [new Paragraph("2,3")], rowSpan: 3 }),
],
}),
new TableRow({
children: [
new TableCell({ children: [new Paragraph("3,1")] }),
new TableCell({ children: [new Paragraph("3,2")], rowSpan: 2 }),
],
}),
new TableRow({
children: [
new TableCell({ children: [new Paragraph("4,1")] }),
],
}),
],
width: {
size: 100,
type: WidthType.PERCENTAGE,
},
});
doc.addSection({ doc.addSection({
children: [ children: [
table, table,
@ -222,10 +379,16 @@ doc.addSection({
heading: HeadingLevel.HEADING_2, heading: HeadingLevel.HEADING_2,
}), }),
table3, table3,
new Paragraph("Merging columns"), new Paragraph("Merging columns 1"),
table4, table4,
new Paragraph("More Merging columns"), new Paragraph("Merging columns 2"),
table5, table5,
new Paragraph("Merging columns 3"),
table6,
new Paragraph("Merging columns 4"),
table7,
new Paragraph("Merging columns 5"),
table8,
], ],
}); });

55
demo/53-chinese.ts Normal file
View File

@ -0,0 +1,55 @@
// Chinese text - Chinese text need to use a Chinese font. And ascii text need to use a ascii font.
// Different from the `52-japanese.ts`.
// `52-japanese.ts` will set all characters to use Japanese font.
// `53-chinese.ts` will set Chinese characters to use Chinese font, and set ascii characters to use ascii font.
// Note that if the OS have not install `KaiTi` font, this demo doesn't work.
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, HeadingLevel, Packer, Paragraph, TextRun } from "../build";
const doc = new Document({
styles: {
paragraphStyles: [
{
id: "Normal",
name: "Normal",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
font: {
ascii: "Times",
eastAsia: "KaiTi",
},
},
},
],
},
});
doc.addSection({
children: [
new Paragraph({
text: "中文和英文 Chinese and English",
heading: HeadingLevel.HEADING_1,
}),
new Paragraph({
text: "中文和英文 Chinese and English",
}),
new Paragraph({
children: [
new TextRun({
text: "中文和英文 Chinese and English",
font: { eastAsia: "SimSun" }, // set eastAsia to "SimSun".
// The ascii characters will use the default font ("Times") specified in paragraphStyles
}),
],
}),
],
});
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

View File

@ -22,10 +22,11 @@ const name = new TextRun({
### Run formatting ### Run formatting
- `bold`, `italics`, `smallCaps`, `allCaps`, `strike`, `doubleStrike`, `subScript`, `superScript`: Set the formatting property to true - `bold`, `italics`, `smallCaps`, `allCaps`, `strike`, `doubleStrike`, `subScript`, `superScript`: Set the formatting property to true
- `underline(style="single", color=null)`: Set the underline style and color - `underline({type="single", color=null})`: Set the underline style and color
- `emphasisMark({type="dot"})`: Set the emphasis mark style
- `color(color)`: Set the text color, using 6 hex characters for RRGGBB (no leading `#`) - `color(color)`: Set the text color, using 6 hex characters for RRGGBB (no leading `#`)
- `size(halfPts)`: Set the font size, measured in half-points - `size(halfPts)`: Set the font size, measured in half-points
- `font(name)`: Set the run's font - `font(name)` or `font({ascii, cs, eastAsia, hAnsi, hint})`: Set the run's font
- `style(name)`: Apply a named run style - `style(name)`: Apply a named run style
- `characterSpacing(value)`: Set the character spacing adjustment (in TWIPs) - `characterSpacing(value)`: Set the character spacing adjustment (in TWIPs)

View File

@ -68,6 +68,15 @@ const text = new TextRun({
}); });
``` ```
### Emphasis Mark
```ts
const text = new TextRun({
text: "and then emphasis mark",
emphasisMark: {},
});
```
### Strike through ### Strike through
```ts ```ts

76
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "docx", "name": "docx",
"version": "5.0.2", "version": "5.1.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -191,7 +191,7 @@
}, },
"@sinonjs/formatio": { "@sinonjs/formatio": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz",
"integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==",
"dev": true, "dev": true,
"requires": { "requires": {
@ -256,7 +256,7 @@
}, },
"@types/fs-extra": { "@types/fs-extra": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.1.tgz", "resolved": "http://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.1.tgz",
"integrity": "sha512-h3wnflb+jMTipvbbZnClgA2BexrT4w0GcfoCz5qyxd0IRsbqhLSyesM6mqZTAnhbVmhyTm5tuxfRu9R+8l+lGw==", "integrity": "sha512-h3wnflb+jMTipvbbZnClgA2BexrT4w0GcfoCz5qyxd0IRsbqhLSyesM6mqZTAnhbVmhyTm5tuxfRu9R+8l+lGw==",
"dev": true, "dev": true,
"requires": { "requires": {
@ -296,7 +296,7 @@
}, },
"@types/lodash": { "@types/lodash": {
"version": "4.14.104", "version": "4.14.104",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.104.tgz", "resolved": "http://registry.npmjs.org/@types/lodash/-/lodash-4.14.104.tgz",
"integrity": "sha512-ufQcVg4daO8xQ5kopxRHanqFdL4AI7ondQkV+2f+7mz3gvp0LkBx2zBRC6hfs3T87mzQFmf5Fck7Fi145Ul6NQ==", "integrity": "sha512-ufQcVg4daO8xQ5kopxRHanqFdL4AI7ondQkV+2f+7mz3gvp0LkBx2zBRC6hfs3T87mzQFmf5Fck7Fi145Ul6NQ==",
"dev": true "dev": true
}, },
@ -319,9 +319,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "13.1.6", "version": "14.0.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.1.6.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.5.tgz",
"integrity": "sha512-Jg1F+bmxcpENHP23sVKkNuU3uaxPnsBMW0cLjleiikFKomJQbsn0Cqk2yDvQArqzZN6ABfBkZ0To7pQ8sLdWDg==" "integrity": "sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA=="
}, },
"@types/request": { "@types/request": {
"version": "2.48.1", "version": "2.48.1",
@ -702,7 +702,7 @@
}, },
"util": { "util": {
"version": "0.10.3", "version": "0.10.3",
"resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz",
"integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -731,7 +731,7 @@
}, },
"async": { "async": {
"version": "0.9.2", "version": "0.9.2",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz",
"integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=",
"dev": true "dev": true
}, },
@ -755,7 +755,7 @@
}, },
"awesome-typescript-loader": { "awesome-typescript-loader": {
"version": "3.5.0", "version": "3.5.0",
"resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-3.5.0.tgz", "resolved": "http://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-3.5.0.tgz",
"integrity": "sha512-qzgm9SEvodVkSi9QY7Me1/rujg+YBNMjayNSAyzNghwTEez++gXoPCwMvpbHRG7wrOkDCiF6dquvv9ESmUBAuw==", "integrity": "sha512-qzgm9SEvodVkSi9QY7Me1/rujg+YBNMjayNSAyzNghwTEez++gXoPCwMvpbHRG7wrOkDCiF6dquvv9ESmUBAuw==",
"dev": true, "dev": true,
"requires": { "requires": {
@ -799,7 +799,7 @@
}, },
"chalk": { "chalk": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -1285,7 +1285,7 @@
}, },
"chai": { "chai": {
"version": "3.5.0", "version": "3.5.0",
"resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", "resolved": "http://registry.npmjs.org/chai/-/chai-3.5.0.tgz",
"integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -1507,7 +1507,7 @@
}, },
"commander": { "commander": {
"version": "2.15.1", "version": "2.15.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
"dev": true "dev": true
}, },
@ -1763,7 +1763,7 @@
}, },
"deep-eql": { "deep-eql": {
"version": "0.1.3", "version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", "resolved": "http://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
"integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -2586,7 +2586,7 @@
}, },
"fast-deep-equal": { "fast-deep-equal": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
"dev": true "dev": true
}, },
@ -3927,7 +3927,7 @@
"is-buffer": { "is-buffer": {
"version": "1.1.6", "version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true "dev": true
}, },
"is-ci": { "is-ci": {
@ -4351,7 +4351,7 @@
}, },
"jsesc": { "jsesc": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
"integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
"dev": true "dev": true
}, },
@ -4483,7 +4483,7 @@
}, },
"load-json-file": { "load-json-file": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -4777,7 +4777,7 @@
"minimatch": { "minimatch": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true, "dev": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
@ -4812,7 +4812,7 @@
}, },
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -4967,7 +4967,7 @@
}, },
"ncp": { "ncp": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", "resolved": "http://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz",
"integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=", "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=",
"dev": true "dev": true
}, },
@ -4985,7 +4985,7 @@
}, },
"next-tick": { "next-tick": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
"dev": true "dev": true
}, },
@ -5518,7 +5518,7 @@
}, },
"os-locale": { "os-locale": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -5695,7 +5695,7 @@
}, },
"path-is-absolute": { "path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true "dev": true
}, },
@ -5766,7 +5766,7 @@
}, },
"pify": { "pify": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true "dev": true
}, },
@ -6467,7 +6467,7 @@
}, },
"safe-regex": { "safe-regex": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
"integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -7034,7 +7034,7 @@
}, },
"strip-ansi": { "strip-ansi": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -7052,7 +7052,7 @@
}, },
"strip-eof": { "strip-eof": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
"dev": true "dev": true
}, },
@ -7440,7 +7440,7 @@
}, },
"typedoc": { "typedoc": {
"version": "0.11.1", "version": "0.11.1",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.11.1.tgz", "resolved": "http://registry.npmjs.org/typedoc/-/typedoc-0.11.1.tgz",
"integrity": "sha512-jdNIoHm5wkZqxQTe/g9AQ3LKnZyrzHXqu6A/c9GUOeJyBWLxNr7/Dm3rwFvLksuxRNwTvY/0HRDU9sJTa9WQSg==", "integrity": "sha512-jdNIoHm5wkZqxQTe/g9AQ3LKnZyrzHXqu6A/c9GUOeJyBWLxNr7/Dm3rwFvLksuxRNwTvY/0HRDU9sJTa9WQSg==",
"dev": true, "dev": true,
"requires": { "requires": {
@ -7482,7 +7482,7 @@
}, },
"typescript": { "typescript": {
"version": "2.7.2", "version": "2.7.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz", "resolved": "http://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz",
"integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==", "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==",
"dev": true "dev": true
} }
@ -7586,7 +7586,7 @@
}, },
"yargs": { "yargs": {
"version": "3.10.0", "version": "3.10.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
"integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -8015,7 +8015,7 @@
}, },
"load-json-file": { "load-json-file": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -8216,7 +8216,7 @@
}, },
"winston": { "winston": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", "resolved": "http://registry.npmjs.org/winston/-/winston-2.1.1.tgz",
"integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -8231,13 +8231,13 @@
"dependencies": { "dependencies": {
"async": { "async": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz",
"integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=",
"dev": true "dev": true
}, },
"colors": { "colors": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
"integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=",
"dev": true "dev": true
}, },
@ -8257,7 +8257,7 @@
}, },
"wrap-ansi": { "wrap-ansi": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -8391,7 +8391,7 @@
}, },
"yargs": { "yargs": {
"version": "4.8.1", "version": "4.8.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", "resolved": "http://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz",
"integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -8435,7 +8435,7 @@
}, },
"yargs-parser": { "yargs-parser": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", "resolved": "http://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz",
"integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=",
"dev": true, "dev": true,
"requires": { "requires": {

View File

@ -1,6 +1,6 @@
{ {
"name": "docx", "name": "docx",
"version": "5.1.0", "version": "5.2.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": {
@ -50,7 +50,7 @@
"types": "./build/index.d.ts", "types": "./build/index.d.ts",
"dependencies": { "dependencies": {
"@types/jszip": "^3.1.4", "@types/jszip": "^3.1.4",
"@types/node": "^13.1.6", "@types/node": "^14.0.5",
"jszip": "^3.1.5", "jszip": "^3.1.5",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"xml": "^1.0.1", "xml": "^1.0.1",

View File

@ -3,7 +3,7 @@ import { expect } from "chai";
import { Formatter } from "export/formatter"; import { Formatter } from "export/formatter";
import { EMPTY_OBJECT } from "file/xml-components"; import { EMPTY_OBJECT } from "file/xml-components";
import { AlignmentType, TabStopPosition } from "../paragraph"; import { AlignmentType, EmphasisMarkType, TabStopPosition } from "../paragraph";
import { UnderlineType } from "../paragraph/run/underline"; import { UnderlineType } from "../paragraph/run/underline";
import { ShadingType } from "../table"; import { ShadingType } from "../table";
import { AbstractNumbering } from "./abstract-numbering"; import { AbstractNumbering } from "./abstract-numbering";
@ -417,7 +417,7 @@ describe("AbstractNumbering", () => {
}); });
}); });
it("#font", () => { it("#font by name", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {
level: 0, level: 0,
@ -433,7 +433,47 @@ describe("AbstractNumbering", () => {
const tree = new Formatter().format(abstractNumbering); const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [ "w:rPr": [
{ "w:rFonts": { _attr: { "w:ascii": "Times", "w:cs": "Times", "w:eastAsia": "Times", "w:hAnsi": "Times" } } }, {
"w:rFonts": {
_attr: {
"w:ascii": "Times",
"w:cs": "Times",
"w:eastAsia": "Times",
"w:hAnsi": "Times",
},
},
},
],
});
});
it("#font for ascii and eastAsia", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
text: "%0.",
style: {
run: {
font: {
ascii: "Times",
eastAsia: "KaiTi",
},
},
},
},
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [
{
"w:rFonts": {
_attr: {
"w:ascii": "Times",
"w:eastAsia": "KaiTi",
},
},
},
], ],
}); });
}); });
@ -582,6 +622,48 @@ describe("AbstractNumbering", () => {
}); });
}); });
describe("#emphasisMark", () => {
it("should set emphasisMark to 'dot' if no arguments are given", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
text: "%0.",
style: {
run: {
emphasisMark: {},
},
},
},
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [{ "w:em": { _attr: { "w:val": "dot" } } }],
});
});
it("should set the style if given", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
text: "%0.",
style: {
run: {
emphasisMark: {
type: EmphasisMarkType.DOT,
},
},
},
},
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [{ "w:em": { _attr: { "w:val": "dot" } } }],
});
});
});
it("#color", () => { it("#color", () => {
const abstractNumbering = new AbstractNumbering(1, [ const abstractNumbering = new AbstractNumbering(1, [
{ {

View File

@ -177,6 +177,10 @@ export class LevelBase extends XmlComponent {
this.runProperties.push(new formatting.Underline(style.run.underline.type, style.run.underline.color)); this.runProperties.push(new formatting.Underline(style.run.underline.type, style.run.underline.color));
} }
if (style.run.emphasisMark) {
this.runProperties.push(new formatting.EmphasisMark(style.run.emphasisMark.type));
}
if (style.run.color) { if (style.run.color) {
this.runProperties.push(new formatting.Color(style.run.color)); this.runProperties.push(new formatting.Color(style.run.color));
} }

View File

@ -147,17 +147,6 @@ export class Numbering extends XmlComponent {
}, },
}, },
}, },
{
level: 9,
format: "bullet",
text: "\u2726",
alignment: AlignmentType.LEFT,
style: {
paragraph: {
indent: { left: 720, hanging: 360 },
},
},
},
]); ]);
this.createConcreteNumbering(abstractNumbering); this.createConcreteNumbering(abstractNumbering);

View File

@ -0,0 +1,29 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import * as em from "./emphasis-mark";
describe("EmphasisMark", () => {
describe("#constructor()", () => {
it("should create a new EmphasisMark object with w:em as the rootKey", () => {
const emphasisMark = new em.EmphasisMark();
const tree = new Formatter().format(emphasisMark);
expect(tree).to.deep.equal({
"w:em": { _attr: { "w:val": "dot" } },
});
});
});
});
describe("DotEmphasisMark", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const emphasisMark = new em.DotEmphasisMark();
const tree = new Formatter().format(emphasisMark);
expect(tree).to.deep.equal({
"w:em": { _attr: { "w:val": "dot" } },
});
});
});
});

View File

@ -0,0 +1,28 @@
import { Attributes, XmlComponent } from "file/xml-components";
export enum EmphasisMarkType {
DOT = "dot",
}
export abstract class BaseEmphasisMark extends XmlComponent {
protected constructor(emphasisMarkType: EmphasisMarkType) {
super("w:em");
this.root.push(
new Attributes({
val: emphasisMarkType,
}),
);
}
}
export class EmphasisMark extends BaseEmphasisMark {
constructor(emphasisMarkType: EmphasisMarkType = EmphasisMarkType.DOT) {
super(emphasisMarkType);
}
}
export class DotEmphasisMark extends BaseEmphasisMark {
constructor() {
super(EmphasisMarkType.DOT);
}
}

View File

@ -1,7 +1,9 @@
import { Attributes, XmlComponent } from "file/xml-components"; import { Attributes, XmlComponent } from "file/xml-components";
export { Underline } from "./underline"; export { Underline } from "./underline";
export { EmphasisMark } from "./emphasis-mark";
export { SubScript, SuperScript } from "./script"; export { SubScript, SuperScript } from "./script";
export { RunFonts } from "./run-fonts"; export { RunFonts, IFontAttributesProperties } from "./run-fonts";
export class Bold extends XmlComponent { export class Bold extends XmlComponent {
constructor() { constructor() {

View File

@ -5,4 +5,5 @@ export * from "./picture-run";
export * from "./run-fonts"; export * from "./run-fonts";
export * from "./sequential-identifier"; export * from "./sequential-identifier";
export * from "./underline"; export * from "./underline";
export * from "./emphasis-mark";
export * from "./tab"; export * from "./tab";

View File

@ -21,5 +21,12 @@ describe("RunFonts", () => {
}, },
}); });
}); });
it("uses the font attrs for ascii and eastAsia", () => {
const tree = new Formatter().format(new RunFonts({ ascii: "Times", eastAsia: "KaiTi" }));
expect(tree).to.deep.equal({
"w:rFonts": { _attr: { "w:ascii": "Times", "w:eastAsia": "KaiTi" } },
});
});
}); });
}); });

View File

@ -1,14 +1,14 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
interface IRunFontAttributesProperties { export interface IFontAttributesProperties {
readonly ascii: string; readonly ascii?: string;
readonly cs: string; readonly cs?: string;
readonly eastAsia: string; readonly eastAsia?: string;
readonly hAnsi: string; readonly hAnsi?: string;
readonly hint?: string; readonly hint?: string;
} }
class RunFontAttributes extends XmlAttributeComponent<IRunFontAttributesProperties> { class RunFontAttributes extends XmlAttributeComponent<IFontAttributesProperties> {
protected readonly xmlKeys = { protected readonly xmlKeys = {
ascii: "w:ascii", ascii: "w:ascii",
cs: "w:cs", cs: "w:cs",
@ -19,16 +19,26 @@ class RunFontAttributes extends XmlAttributeComponent<IRunFontAttributesProperti
} }
export class RunFonts extends XmlComponent { export class RunFonts extends XmlComponent {
constructor(ascii: string, hint?: string) { constructor(name: string, hint?: string);
constructor(attrs: string | IFontAttributesProperties);
constructor(nameOrAttrs: string | IFontAttributesProperties, hint?: string) {
super("w:rFonts"); super("w:rFonts");
this.root.push( if (typeof nameOrAttrs === "string") {
new RunFontAttributes({ // use constructor(name: string, hint?: string);
ascii: ascii, const name = nameOrAttrs;
cs: ascii, this.root.push(
eastAsia: ascii, new RunFontAttributes({
hAnsi: ascii, ascii: name,
hint: hint, cs: name,
}), eastAsia: name,
); hAnsi: name,
hint: hint,
}),
);
} else {
// use constructor(attrs: IRunFontAttributesProperties);
const attrs = nameOrAttrs;
this.root.push(new RunFontAttributes(attrs));
}
} }
} }

View File

@ -5,6 +5,7 @@ import { Formatter } from "export/formatter";
import { ShadingType } from "file/table"; import { ShadingType } from "file/table";
import { Run } from "./"; import { Run } from "./";
import { EmphasisMarkType } from "./emphasis-mark";
import { PageNumber } from "./run"; import { PageNumber } from "./run";
import { UnderlineType } from "./underline"; import { UnderlineType } from "./underline";
@ -84,6 +85,30 @@ describe("Run", () => {
}); });
}); });
describe("#emphasisMark()", () => {
it("should default to 'dot'", () => {
const run = new Run({
emphasisMark: {},
});
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [{ "w:rPr": [{ "w:em": { _attr: { "w:val": "dot" } } }] }],
});
});
it("should set the style type if given", () => {
const run = new Run({
emphasisMark: {
type: EmphasisMarkType.DOT,
},
});
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [{ "w:rPr": [{ "w:em": { _attr: { "w:val": "dot" } } }] }],
});
});
});
describe("#smallCaps()", () => { describe("#smallCaps()", () => {
it("it should add smallCaps to the properties", () => { it("it should add smallCaps to the properties", () => {
const run = new Run({ const run = new Run({
@ -235,7 +260,42 @@ describe("Run", () => {
"w:r": [ "w:r": [
{ {
"w:rPr": [ "w:rPr": [
{ "w:rFonts": { _attr: { "w:ascii": "Times", "w:cs": "Times", "w:eastAsia": "Times", "w:hAnsi": "Times" } } }, {
"w:rFonts": {
_attr: {
"w:ascii": "Times",
"w:cs": "Times",
"w:eastAsia": "Times",
"w:hAnsi": "Times",
},
},
},
],
},
],
});
});
it("should set the font for ascii and eastAsia", () => {
const run = new Run({
font: {
ascii: "Times",
eastAsia: "KaiTi",
},
});
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{
"w:rPr": [
{
"w:rFonts": {
_attr: {
"w:ascii": "Times",
"w:eastAsia": "KaiTi",
},
},
},
], ],
}, },
], ],

View File

@ -6,6 +6,7 @@ import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run"
import { FieldInstruction } from "file/table-of-contents/field-instruction"; import { FieldInstruction } from "file/table-of-contents/field-instruction";
import { Break } from "./break"; import { Break } from "./break";
import { Caps, SmallCaps } from "./caps"; import { Caps, SmallCaps } from "./caps";
import { EmphasisMark, EmphasisMarkType } from "./emphasis-mark";
import { Begin, End, Separate } from "./field"; import { Begin, End, Separate } from "./field";
import { import {
Bold, Bold,
@ -26,11 +27,16 @@ import {
import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number"; import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number";
import { RunProperties } from "./properties"; import { RunProperties } from "./properties";
import { Text } from "./run-components/text"; import { Text } from "./run-components/text";
import { RunFonts } from "./run-fonts"; import { IFontAttributesProperties, RunFonts } from "./run-fonts";
import { SubScript, SuperScript } from "./script"; import { SubScript, SuperScript } from "./script";
import { Style } from "./style"; import { Style } from "./style";
import { Underline, UnderlineType } from "./underline"; import { Underline, UnderlineType } from "./underline";
interface IFontOptions {
readonly name: string;
readonly hint?: string;
}
export interface IRunOptions { export interface IRunOptions {
readonly bold?: true; readonly bold?: true;
readonly italics?: true; readonly italics?: true;
@ -38,6 +44,9 @@ export interface IRunOptions {
readonly color?: string; readonly color?: string;
readonly type?: UnderlineType; readonly type?: UnderlineType;
}; };
readonly emphasisMark?: {
readonly type?: EmphasisMarkType;
};
readonly color?: string; readonly color?: string;
readonly size?: number; readonly size?: number;
readonly rightToLeft?: boolean; readonly rightToLeft?: boolean;
@ -48,10 +57,7 @@ export interface IRunOptions {
readonly subScript?: boolean; readonly subScript?: boolean;
readonly superScript?: boolean; readonly superScript?: boolean;
readonly style?: string; readonly style?: string;
readonly font?: { readonly font?: IFontOptions | IFontAttributesProperties;
readonly name: string;
readonly hint?: string;
};
readonly highlight?: string; readonly highlight?: string;
readonly shading?: { readonly shading?: {
readonly type: ShadingType; readonly type: ShadingType;
@ -90,6 +96,10 @@ export class Run extends XmlComponent {
this.properties.push(new Underline(options.underline.type, options.underline.color)); this.properties.push(new Underline(options.underline.type, options.underline.color));
} }
if (options.emphasisMark) {
this.properties.push(new EmphasisMark(options.emphasisMark.type));
}
if (options.color) { if (options.color) {
this.properties.push(new Color(options.color)); this.properties.push(new Color(options.color));
} }
@ -132,7 +142,11 @@ export class Run extends XmlComponent {
} }
if (options.font) { if (options.font) {
this.properties.push(new RunFonts(options.font.name, options.font.hint)); if ("name" in options.font) {
this.properties.push(new RunFonts(options.font.name, options.font.hint));
} else {
this.properties.push(new RunFonts(options.font));
}
} }
if (options.highlight) { if (options.highlight) {

View File

@ -1,5 +1,7 @@
import { expect } from "chai"; import { expect } from "chai";
import { EmphasisMarkType } from "./emphasis-mark";
import { Formatter } from "export/formatter"; import { Formatter } from "export/formatter";
import { UnderlineType } from "./underline"; import { UnderlineType } from "./underline";
@ -44,6 +46,9 @@ describe("SymbolRun", () => {
color: "red", color: "red",
type: UnderlineType.DOUBLE, type: UnderlineType.DOUBLE,
}, },
emphasisMark: {
type: EmphasisMarkType.DOT,
},
color: "green", color: "green",
size: 40, size: 40,
highlight: "yellow", highlight: "yellow",
@ -59,6 +64,7 @@ describe("SymbolRun", () => {
{ "w:i": { _attr: { "w:val": true } } }, { "w:i": { _attr: { "w:val": true } } },
{ "w:iCs": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } },
{ "w:u": { _attr: { "w:val": "double", "w:color": "red" } } }, { "w:u": { _attr: { "w:val": "double", "w:color": "red" } } },
{ "w:em": { _attr: { "w:val": "dot" } } },
{ "w:color": { _attr: { "w:val": "green" } } }, { "w:color": { _attr: { "w:val": "green" } } },
{ "w:sz": { _attr: { "w:val": 40 } } }, { "w:sz": { _attr: { "w:val": 40 } } },
{ "w:szCs": { _attr: { "w:val": 40 } } }, { "w:szCs": { _attr: { "w:val": 40 } } },

View File

@ -1,6 +1,6 @@
import { Size, SizeComplexScript } from "file/paragraph/run/formatting"; import { Size, SizeComplexScript } from "file/paragraph/run/formatting";
import { RunProperties } from "file/paragraph/run/properties"; import { RunProperties } from "file/paragraph/run/properties";
import { RunFonts } from "file/paragraph/run/run-fonts"; import { IFontAttributesProperties, RunFonts } from "file/paragraph/run/run-fonts";
import { XmlComponent } from "file/xml-components"; import { XmlComponent } from "file/xml-components";
export class RunPropertiesDefaults extends XmlComponent { export class RunPropertiesDefaults extends XmlComponent {
@ -18,8 +18,8 @@ export class RunPropertiesDefaults extends XmlComponent {
return this; return this;
} }
public font(fontName: string): RunPropertiesDefaults { public font(font: string | IFontAttributesProperties): RunPropertiesDefaults {
this.properties.push(new RunFonts(fontName)); this.properties.push(new RunFonts(font));
return this; return this;
} }
} }

View File

@ -1,4 +1,11 @@
import { AlignmentType, IIndentAttributesProperties, ISpacingProperties, UnderlineType } from "../paragraph"; import {
AlignmentType,
EmphasisMarkType,
IFontAttributesProperties,
IIndentAttributesProperties,
ISpacingProperties,
UnderlineType,
} from "../paragraph";
import { ShadingType } from "../table"; import { ShadingType } from "../table";
export interface IRunStyleOptions { export interface IRunStyleOptions {
@ -15,8 +22,11 @@ export interface IRunStyleOptions {
readonly type?: UnderlineType; readonly type?: UnderlineType;
readonly color?: string; readonly color?: string;
}; };
readonly emphasisMark?: {
readonly type?: EmphasisMarkType;
};
readonly color?: string; readonly color?: string;
readonly font?: string; readonly font?: string | IFontAttributesProperties;
readonly characterSpacing?: number; readonly characterSpacing?: number;
readonly highlight?: string; readonly highlight?: string;
readonly shadow?: { readonly shadow?: {

View File

@ -1,6 +1,7 @@
import { expect } from "chai"; import { expect } from "chai";
import { Formatter } from "export/formatter"; import { Formatter } from "export/formatter";
import { EmphasisMarkType } from "file/paragraph/run/emphasis-mark";
import { UnderlineType } from "file/paragraph/run/underline"; import { UnderlineType } from "file/paragraph/run/underline";
import { ShadingType } from "file/table"; import { ShadingType } from "file/table";
import { EMPTY_OBJECT } from "file/xml-components"; import { EMPTY_OBJECT } from "file/xml-components";
@ -201,7 +202,7 @@ describe("CharacterStyle", () => {
}); });
}); });
it("should add font", () => { it("should add font by name", () => {
const style = new CharacterStyle({ const style = new CharacterStyle({
id: "myStyleId", id: "myStyleId",
run: { run: {
@ -240,6 +241,46 @@ describe("CharacterStyle", () => {
}); });
}); });
it("should add font for ascii and eastAsia", () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
font: {
ascii: "test font ascii",
eastAsia: "test font eastAsia",
},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [
{
"w:rFonts": {
_attr: {
"w:ascii": "test font ascii",
"w:eastAsia": "test font eastAsia",
},
},
},
],
},
{
"w:uiPriority": {
_attr: {
"w:val": 99,
},
},
},
{
"w:unhideWhenUsed": EMPTY_OBJECT,
},
],
});
});
it("should add character spacing", () => { it("should add character spacing", () => {
const style = new CharacterStyle({ const style = new CharacterStyle({
id: "myStyleId", id: "myStyleId",
@ -412,6 +453,66 @@ describe("CharacterStyle", () => {
}); });
}); });
describe("#emphasisMark", () => {
it("should set emphasisMark to 'dot' if no arguments are given", () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
emphasisMark: {},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:em": { _attr: { "w:val": "dot" } } }],
},
{
"w:uiPriority": {
_attr: {
"w:val": 99,
},
},
},
{
"w:unhideWhenUsed": EMPTY_OBJECT,
},
],
});
});
it("should set the style if given", () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
emphasisMark: {
type: EmphasisMarkType.DOT,
},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:em": { _attr: { "w:val": "dot" } } }],
},
{
"w:uiPriority": {
_attr: {
"w:val": 99,
},
},
},
{
"w:unhideWhenUsed": EMPTY_OBJECT,
},
],
});
});
});
it("#superScript", () => { it("#superScript", () => {
const style = new CharacterStyle({ const style = new CharacterStyle({
id: "myStyleId", id: "myStyleId",
@ -616,7 +717,17 @@ describe("CharacterStyle", () => {
"w:style": [ "w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } }, { _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{ {
"w:rPr": [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }], "w:rPr": [
{
"w:shd": {
_attr: {
"w:val": "pct10",
"w:fill": "00FFFF",
"w:color": "FF0000",
},
},
},
],
}, },
{ {
"w:uiPriority": { "w:uiPriority": {

View File

@ -1,5 +1,7 @@
import { EmphasisMarkType } from "file/paragraph/run/emphasis-mark";
import * as formatting from "file/paragraph/run/formatting"; import * as formatting from "file/paragraph/run/formatting";
import { RunProperties } from "file/paragraph/run/properties"; import { RunProperties } from "file/paragraph/run/properties";
import { IFontAttributesProperties } from "file/paragraph/run/run-fonts";
import { UnderlineType } from "file/paragraph/run/underline"; import { UnderlineType } from "file/paragraph/run/underline";
import { BasedOn, Link, SemiHidden, UiPriority, UnhideWhenUsed } from "./components"; import { BasedOn, Link, SemiHidden, UiPriority, UnhideWhenUsed } from "./components";
@ -23,8 +25,11 @@ export interface IBaseCharacterStyleOptions {
readonly type?: UnderlineType; readonly type?: UnderlineType;
readonly color?: string; readonly color?: string;
}; };
readonly emphasisMark?: {
readonly type?: EmphasisMarkType;
};
readonly color?: string; readonly color?: string;
readonly font?: string; readonly font?: string | IFontAttributesProperties;
readonly characterSpacing?: number; readonly characterSpacing?: number;
readonly highlight?: string; readonly highlight?: string;
readonly shadow?: { readonly shadow?: {
@ -104,6 +109,10 @@ export class CharacterStyle extends Style {
this.runProperties.push(new formatting.Underline(options.run.underline.type, options.run.underline.color)); this.runProperties.push(new formatting.Underline(options.run.underline.type, options.run.underline.color));
} }
if (options.run.emphasisMark) {
this.runProperties.push(new formatting.EmphasisMark(options.run.emphasisMark.type));
}
if (options.run.color) { if (options.run.color) {
this.runProperties.push(new formatting.Color(options.run.color)); this.runProperties.push(new formatting.Color(options.run.color));
} }

View File

@ -1,7 +1,7 @@
import { expect } from "chai"; import { expect } from "chai";
import { Formatter } from "export/formatter"; import { Formatter } from "export/formatter";
import { AlignmentType, TabStopPosition } from "file/paragraph"; import { AlignmentType, EmphasisMarkType, TabStopPosition } from "file/paragraph";
import { UnderlineType } from "file/paragraph/run/underline"; import { UnderlineType } from "file/paragraph/run/underline";
import { ShadingType } from "file/table"; import { ShadingType } from "file/table";
import { EMPTY_OBJECT } from "file/xml-components"; import { EMPTY_OBJECT } from "file/xml-components";
@ -49,7 +49,15 @@ describe("ParagraphStyle", () => {
const style = new ParagraphStyle({ id: "myStyleId", quickFormat: true }); const style = new ParagraphStyle({ id: "myStyleId", quickFormat: true });
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:qFormat": EMPTY_OBJECT }], "w:style": [
{
_attr: {
"w:type": "paragraph",
"w:styleId": "myStyleId",
},
},
{ "w:qFormat": EMPTY_OBJECT },
],
}); });
}); });
@ -299,7 +307,15 @@ describe("ParagraphStyle", () => {
}); });
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:pPr": [{ "w:keepLines": EMPTY_OBJECT }] }], "w:style": [
{
_attr: {
"w:type": "paragraph",
"w:styleId": "myStyleId",
},
},
{ "w:pPr": [{ "w:keepLines": EMPTY_OBJECT }] },
],
}); });
}); });
@ -312,7 +328,15 @@ describe("ParagraphStyle", () => {
}); });
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:pPr": [{ "w:keepNext": EMPTY_OBJECT }] }], "w:style": [
{
_attr: {
"w:type": "paragraph",
"w:styleId": "myStyleId",
},
},
{ "w:pPr": [{ "w:keepNext": EMPTY_OBJECT }] },
],
}); });
}); });
@ -460,7 +484,7 @@ describe("ParagraphStyle", () => {
}); });
}); });
it("#font", () => { it("#font by name", () => {
const style = new ParagraphStyle({ const style = new ParagraphStyle({
id: "myStyleId", id: "myStyleId",
run: { run: {
@ -473,7 +497,46 @@ describe("ParagraphStyle", () => {
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
{ {
"w:rPr": [ "w:rPr": [
{ "w:rFonts": { _attr: { "w:ascii": "Times", "w:cs": "Times", "w:eastAsia": "Times", "w:hAnsi": "Times" } } }, {
"w:rFonts": {
_attr: {
"w:ascii": "Times",
"w:cs": "Times",
"w:eastAsia": "Times",
"w:hAnsi": "Times",
},
},
},
],
},
],
});
});
it("#font for ascii and eastAsia", () => {
const style = new ParagraphStyle({
id: "myStyleId",
run: {
font: {
ascii: "Times",
eastAsia: "KaiTi",
},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
{
"w:rPr": [
{
"w:rFonts": {
_attr: {
"w:ascii": "Times",
"w:eastAsia": "KaiTi",
},
},
},
], ],
}, },
], ],
@ -550,7 +613,17 @@ describe("ParagraphStyle", () => {
"w:style": [ "w:style": [
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
{ {
"w:rPr": [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }], "w:rPr": [
{
"w:shd": {
_attr: {
"w:val": "pct10",
"w:fill": "00FFFF",
"w:color": "FF0000",
},
},
},
],
}, },
], ],
}); });
@ -617,6 +690,46 @@ describe("ParagraphStyle", () => {
}); });
}); });
describe("#emphasisMark", () => {
it("should set emphasisMark to 'dot' if no arguments are given", () => {
const style = new ParagraphStyle({
id: "myStyleId",
run: {
emphasisMark: {},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:em": { _attr: { "w:val": "dot" } } }],
},
],
});
});
it("should set the style if given", () => {
const style = new ParagraphStyle({
id: "myStyleId",
run: {
emphasisMark: {
type: EmphasisMarkType.DOT,
},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:em": { _attr: { "w:val": "dot" } } }],
},
],
});
});
});
it("#color", () => { it("#color", () => {
const style = new ParagraphStyle({ const style = new ParagraphStyle({
id: "myStyleId", id: "myStyleId",
@ -639,7 +752,15 @@ describe("ParagraphStyle", () => {
const style = new ParagraphStyle({ id: "myStyleId", link: "MyLink" }); const style = new ParagraphStyle({ id: "myStyleId", link: "MyLink" });
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:link": { _attr: { "w:val": "MyLink" } } }], "w:style": [
{
_attr: {
"w:type": "paragraph",
"w:styleId": "myStyleId",
},
},
{ "w:link": { _attr: { "w:val": "MyLink" } } },
],
}); });
}); });
@ -647,7 +768,15 @@ describe("ParagraphStyle", () => {
const style = new ParagraphStyle({ id: "myStyleId", semiHidden: true }); const style = new ParagraphStyle({ id: "myStyleId", semiHidden: true });
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:semiHidden": EMPTY_OBJECT }], "w:style": [
{
_attr: {
"w:type": "paragraph",
"w:styleId": "myStyleId",
},
},
{ "w:semiHidden": EMPTY_OBJECT },
],
}); });
}); });
@ -672,7 +801,15 @@ describe("ParagraphStyle", () => {
const style = new ParagraphStyle({ id: "myStyleId", unhideWhenUsed: true }); const style = new ParagraphStyle({ id: "myStyleId", unhideWhenUsed: true });
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:unhideWhenUsed": EMPTY_OBJECT }], "w:style": [
{
_attr: {
"w:type": "paragraph",
"w:styleId": "myStyleId",
},
},
{ "w:unhideWhenUsed": EMPTY_OBJECT },
],
}); });
}); });
}); });

View File

@ -114,6 +114,10 @@ export class ParagraphStyle extends Style {
this.runProperties.push(new formatting.Underline(options.run.underline.type, options.run.underline.color)); this.runProperties.push(new formatting.Underline(options.run.underline.type, options.run.underline.color));
} }
if (options.run.emphasisMark) {
this.runProperties.push(new formatting.EmphasisMark(options.run.emphasisMark.type));
}
if (options.run.color) { if (options.run.color) {
this.runProperties.push(new formatting.Color(options.run.color)); this.runProperties.push(new formatting.Color(options.run.color));
} }

View File

@ -70,6 +70,9 @@ export class TableCell extends XmlComponent {
if (options.verticalMerge) { if (options.verticalMerge) {
this.properties.addVerticalMerge(options.verticalMerge); this.properties.addVerticalMerge(options.verticalMerge);
} else if (options.rowSpan && options.rowSpan > 1) {
// if cell already have a `verticalMerge`, don't handle `rowSpan`
this.properties.addVerticalMerge(VerticalMergeType.RESTART);
} }
if (options.margins) { if (options.margins) {
@ -84,10 +87,6 @@ export class TableCell extends XmlComponent {
this.properties.addGridSpan(options.columnSpan); this.properties.addGridSpan(options.columnSpan);
} }
if (options.rowSpan && options.rowSpan > 1) {
this.properties.addVerticalMerge(VerticalMergeType.RESTART);
}
if (options.width) { if (options.width) {
this.properties.setWidth(options.width.size, options.width.type); this.properties.setWidth(options.width.size, options.width.type);
} }

View File

@ -182,4 +182,97 @@ describe("TableRow", () => {
}); });
}); });
}); });
describe("#rootIndexToColumnIndex", () => {
it("should get the correct virtual column index by root index", () => {
const tableRow = new TableRow({
children: [
new TableCell({
children: [new Paragraph("test")],
columnSpan: 3,
}),
new TableCell({
children: [new Paragraph("test")],
}),
new TableCell({
children: [new Paragraph("test")],
}),
new TableCell({
children: [new Paragraph("test")],
columnSpan: 3,
}),
],
});
expect(tableRow.rootIndexToColumnIndex(1)).to.equal(0);
expect(tableRow.rootIndexToColumnIndex(2)).to.equal(3);
expect(tableRow.rootIndexToColumnIndex(3)).to.equal(4);
expect(tableRow.rootIndexToColumnIndex(4)).to.equal(5);
expect(() => tableRow.rootIndexToColumnIndex(0)).to.throw(`cell 'rootIndex' should between 1 to 4`);
expect(() => tableRow.rootIndexToColumnIndex(5)).to.throw(`cell 'rootIndex' should between 1 to 4`);
});
});
describe("#columnIndexToRootIndex", () => {
it("should get the correct root index by virtual column index", () => {
const tableRow = new TableRow({
children: [
new TableCell({
children: [new Paragraph("test")],
columnSpan: 3,
}),
new TableCell({
children: [new Paragraph("test")],
}),
new TableCell({
children: [new Paragraph("test")],
}),
new TableCell({
children: [new Paragraph("test")],
columnSpan: 3,
}),
],
});
expect(tableRow.columnIndexToRootIndex(0)).to.equal(1);
expect(tableRow.columnIndexToRootIndex(1)).to.equal(1);
expect(tableRow.columnIndexToRootIndex(2)).to.equal(1);
expect(tableRow.columnIndexToRootIndex(3)).to.equal(2);
expect(tableRow.columnIndexToRootIndex(4)).to.equal(3);
expect(tableRow.columnIndexToRootIndex(5)).to.equal(4);
expect(tableRow.columnIndexToRootIndex(6)).to.equal(4);
expect(tableRow.columnIndexToRootIndex(7)).to.equal(4);
expect(() => tableRow.columnIndexToRootIndex(-1)).to.throw(`cell 'columnIndex' should not less than zero`);
expect(() => tableRow.columnIndexToRootIndex(8)).to.throw(`cell 'columnIndex' should not great than 7`);
});
it("should allow end new cell index", () => {
const tableRow = new TableRow({
children: [
new TableCell({
children: [new Paragraph("test")],
columnSpan: 3,
}),
new TableCell({
children: [new Paragraph("test")],
}),
new TableCell({
children: [new Paragraph("test")],
}),
new TableCell({
children: [new Paragraph("test")],
columnSpan: 3,
}),
],
});
expect(tableRow.columnIndexToRootIndex(8, true)).to.equal(5);
// for column 10, just place the new cell at the end of row
expect(tableRow.columnIndexToRootIndex(10, true)).to.equal(5);
});
});
}); });

View File

@ -46,8 +46,56 @@ export class TableRow extends XmlComponent {
return this.options.children; return this.options.children;
} }
public get cells(): TableCell[] {
return this.root.filter((xmlComponent) => xmlComponent instanceof TableCell);
}
public addCellToIndex(cell: TableCell, index: number): void { public addCellToIndex(cell: TableCell, index: number): void {
// Offset because properties is also in root. // Offset because properties is also in root.
this.root.splice(index + 1, 0, cell); this.root.splice(index + 1, 0, cell);
} }
public addCellToColumnIndex(cell: TableCell, columnIndex: number): void {
const rootIndex = this.columnIndexToRootIndex(columnIndex, true);
this.addCellToIndex(cell, rootIndex - 1);
}
public rootIndexToColumnIndex(rootIndex: number): number {
// convert the root index to the virtual column index
if (rootIndex < 1 || rootIndex >= this.root.length) {
throw new Error(`cell 'rootIndex' should between 1 to ${this.root.length - 1}`);
}
let colIdx = 0;
// Offset because properties is also in root.
for (let rootIdx = 1; rootIdx < rootIndex; rootIdx++) {
const cell = this.root[rootIdx] as TableCell;
colIdx += cell.options.columnSpan || 1;
}
return colIdx;
}
public columnIndexToRootIndex(columnIndex: number, allowEndNewCell: boolean = false): number {
// convert the virtual column index to the root index
// `allowEndNewCell` for get index to inert new cell
if (columnIndex < 0) {
throw new Error(`cell 'columnIndex' should not less than zero`);
}
let colIdx = 0;
// Offset because properties is also in root.
let rootIdx = 1;
while (colIdx <= columnIndex) {
if (rootIdx >= this.root.length) {
if (allowEndNewCell) {
// for inserting verticalMerge CONTINUE cell at end of row
return this.root.length;
} else {
throw new Error(`cell 'columnIndex' should not great than ${colIdx - 1}`);
}
}
const cell = this.root[rootIdx] as TableCell;
rootIdx += 1;
colIdx += (cell && cell.options.columnSpan) || 1;
}
return rootIdx - 1;
}
} }

View File

@ -188,6 +188,72 @@ describe("Table", () => {
}); });
}); });
it("creates a table with the correct columnSpan and rowSpan", () => {
const table = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("hello")],
columnSpan: 2,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("hello")],
rowSpan: 2,
}),
new TableCell({
children: [new Paragraph("hello")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("hello")],
}),
],
}),
],
});
const tree = new Formatter().format(table);
const cellP = { "w:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "hello"] }] }] };
expect(tree).to.deep.equal({
"w:tbl": [
{ "w:tblPr": [DEFAULT_TABLE_PROPERTIES, BORDERS, WIDTHS] },
{
"w:tblGrid": [{ "w:gridCol": { _attr: { "w:w": 100 } } }, { "w:gridCol": { _attr: { "w:w": 100 } } }],
},
{
"w:tr": [
{
"w:tc": [{ "w:tcPr": [{ "w:gridSpan": { _attr: { "w:val": 2 } } }] }, cellP],
},
],
},
{
"w:tr": [
{
"w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "restart" } } }] }, cellP],
},
{ "w:tc": [cellP] },
],
},
{
"w:tr": [
{
"w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }, { "w:p": {} }],
},
{ "w:tc": [cellP] },
],
},
],
});
});
it("sets the table to fixed width layout", () => { it("sets the table to fixed width layout", () => {
const table = new Table({ const table = new Table({
rows: [ rows: [

View File

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