Compare commits
126 Commits
Author | SHA1 | Date | |
---|---|---|---|
2654799822 | |||
6245635b86 | |||
b4f1c4dd6a | |||
25a7ce3742 | |||
08bc069cbf | |||
0ebdcc30ed | |||
3eca81d3f5 | |||
faefbae3a1 | |||
370fb098ac | |||
c73019d84c | |||
250a1de71e | |||
012963e90a | |||
632f3cd19b | |||
1c8cd325d7 | |||
7bcdaab2f2 | |||
5b58e520f9 | |||
78f6ea6c44 | |||
b5172e73f9 | |||
5cf534ad26 | |||
d53cdb0558 | |||
212adbbffb | |||
99ab2f0ef5 | |||
a8201b2658 | |||
f13e676c3b | |||
61b01836bc | |||
2ee918b845 | |||
e9579d75c4 | |||
97824f1bb5 | |||
884c134b25 | |||
4f3cb49076 | |||
152285ed5a | |||
49b7ac212d | |||
83450e6277 | |||
6d6155c742 | |||
f8da2c311b | |||
f6bcaef5b5 | |||
1a9e71bfa1 | |||
3591e11637 | |||
47533cf4e2 | |||
de03f19b46 | |||
d1472368f6 | |||
b83d2c388f | |||
ee5425bef7 | |||
3fdbca939e | |||
c68dc8c52a | |||
96471ecb45 | |||
a13f898ad3 | |||
b4cce534a5 | |||
e0698554ad | |||
1649d2a0fa | |||
9683e89159 | |||
2bece0bb61 | |||
deb6c42d86 | |||
2b0953bb19 | |||
d25d22508c | |||
6db37eb4fb | |||
af461f8418 | |||
8bdbd1de39 | |||
1bdc93ef59 | |||
c12101fbc6 | |||
d5ba6578b3 | |||
b37aefdc8d | |||
0be7c26798 | |||
bf1d10e893 | |||
7dfb016faa | |||
b2aeb2d83c | |||
8f4c78e2a8 | |||
bd1f5898a8 | |||
05a4ef1702 | |||
da9e6d6664 | |||
e9d3853d93 | |||
5a9d6120a5 | |||
e8410ff692 | |||
3427d220c7 | |||
643e3c2f84 | |||
9b40b5e55e | |||
a622c210ef | |||
9495f30e2d | |||
617af87065 | |||
c1dd421b27 | |||
8eff6bd0cf | |||
8566c64eab | |||
afd468277e | |||
ca9c992237 | |||
c93b74661b | |||
08ff092638 | |||
2276572902 | |||
43ddeec6e1 | |||
9eaf04f4c7 | |||
c3c29bb119 | |||
ddb34e6a46 | |||
ef3055430b | |||
75cdae1473 | |||
860afe8f28 | |||
f685dbe0d1 | |||
50911fff57 | |||
a7064f4728 | |||
564e9600ea | |||
fcc4202598 | |||
bfbe59cb84 | |||
06abde2425 | |||
75c3c2f985 | |||
fefefdd473 | |||
db59474f1e | |||
1d5e806ff4 | |||
bf4885c7cf | |||
3b289be5ce | |||
2bb7e08ade | |||
b571a7550f | |||
721de30587 | |||
f16126e948 | |||
40d1a3a7c2 | |||
0d4c7a5fc0 | |||
5d401dfb27 | |||
a37c9d8f2f | |||
10ab3c70bf | |||
591b2f4e04 | |||
2536fbe752 | |||
33549d5ec3 | |||
720c6172e3 | |||
bbad2f5cde | |||
d2a0baa221 | |||
ad62f5459b | |||
dfb910defb | |||
04b6d8e54a | |||
d2dded860d |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: dolanmiu
|
||||||
|
patreon: dolanmiu
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -52,7 +52,6 @@ docs/.nojekyll
|
|||||||
.idea
|
.idea
|
||||||
|
|
||||||
# Lock files
|
# Lock files
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
|
||||||
# Documents
|
# Documents
|
||||||
|
8
.nycrc
8
.nycrc
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"check-coverage": true,
|
"check-coverage": true,
|
||||||
"lines": 92.34,
|
"lines": 93.53,
|
||||||
"functions": 88.27,
|
"functions": 89.63,
|
||||||
"branches": 84.64,
|
"branches": 88.57,
|
||||||
"statements": 92.15,
|
"statements": 93.34,
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*.ts"
|
"src/**/*.ts"
|
||||||
],
|
],
|
||||||
|
@ -10,7 +10,7 @@ script:
|
|||||||
- npm run style
|
- npm run style
|
||||||
- npm run build
|
- npm run build
|
||||||
- npm run ts-node -- ./demo/1-basic.ts
|
- npm run ts-node -- ./demo/1-basic.ts
|
||||||
- npm run e2e "My Document.docx"
|
# - npm run e2e "My Document.docx"
|
||||||
- npm run ts-node -- ./demo/2-declaritive-styles.ts
|
- npm run ts-node -- ./demo/2-declaritive-styles.ts
|
||||||
- npm run ts-node -- ./demo/3-numbering-and-bullet-points.ts
|
- npm run ts-node -- ./demo/3-numbering-and-bullet-points.ts
|
||||||
- npm run ts-node -- ./demo/4-basic-table.ts
|
- npm run ts-node -- ./demo/4-basic-table.ts
|
||||||
@ -20,7 +20,7 @@ script:
|
|||||||
- npm run ts-node -- ./demo/8-header-footer.ts
|
- npm run ts-node -- ./demo/8-header-footer.ts
|
||||||
- npm run ts-node -- ./demo/9-images-in-header-and-footer.ts
|
- npm run ts-node -- ./demo/9-images-in-header-and-footer.ts
|
||||||
- npm run ts-node -- ./demo/10-my-cv.ts
|
- npm run ts-node -- ./demo/10-my-cv.ts
|
||||||
- npm run e2e "My Document.docx"
|
# - npm run e2e "My Document.docx"
|
||||||
- npm run ts-node -- ./demo/11-declaritive-styles-2.ts
|
- npm run ts-node -- ./demo/11-declaritive-styles-2.ts
|
||||||
- npm run ts-node -- ./demo/12-scaling-images.ts
|
- npm run ts-node -- ./demo/12-scaling-images.ts
|
||||||
- npm run ts-node -- ./demo/13-xml-styles.ts
|
- npm run ts-node -- ./demo/13-xml-styles.ts
|
||||||
|
10
README.md
10
README.md
@ -18,7 +18,7 @@
|
|||||||
[![codecov][codecov-image]][codecov-url]
|
[![codecov][codecov-image]][codecov-url]
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://i.imgur.com/H5FA1Qy.gif" alt="drawing" width="800"/>
|
<img src="https://i.imgur.com/TCH0YzD.png" alt="drawing" width="800"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# Demo
|
# Demo
|
||||||
@ -27,8 +27,8 @@
|
|||||||
|
|
||||||
Here are examples of `docx` being used with basic `HTML/JS` in a browser environment:
|
Here are examples of `docx` being used with basic `HTML/JS` in a browser environment:
|
||||||
|
|
||||||
* https://codepen.io/anon/pen/dqoVgQ
|
* https://codepen.io/dolanmiu/pen/RwNeObg
|
||||||
* https://jsfiddle.net/3xhezb5w/2
|
* https://jsfiddle.net/dolanmiu/kqxrj35u/1/
|
||||||
|
|
||||||
Here is an example of `docx` working in `Angular`:
|
Here is an example of `docx` working in `Angular`:
|
||||||
|
|
||||||
@ -73,6 +73,10 @@ Read the contribution guidelines [here](https://docx.js.org/#/contribution-guide
|
|||||||
[<img src="https://i.imgur.com/suiH2zc.png" alt="drawing" height="50"/>](https://www.dabblewriter.com/)
|
[<img src="https://i.imgur.com/suiH2zc.png" alt="drawing" height="50"/>](https://www.dabblewriter.com/)
|
||||||
[<img src="https://i.imgur.com/1LjuK2M.png" alt="drawing" height="50"/>](https://turbopatent.com/)
|
[<img src="https://i.imgur.com/1LjuK2M.png" alt="drawing" height="50"/>](https://turbopatent.com/)
|
||||||
[<img src="https://i.imgur.com/dHMg0wF.gif" alt="drawing" height="50"/>](http://www.madisoncres.com/)
|
[<img src="https://i.imgur.com/dHMg0wF.gif" alt="drawing" height="50"/>](http://www.madisoncres.com/)
|
||||||
|
[<img src="https://i.imgur.com/QEZXU5b.png" alt="drawing" height="50"/>](https://www.beekast.com/)
|
||||||
|
[<img src="https://imgur.com/XVU6aoi.png" alt="drawing" height="50"/>](https://herraizsoto.com/)
|
||||||
|
[<img src="https://i.imgur.com/fn1xccG.png" alt="drawing" height="50"/>](http://www.ativer.com.br/)
|
||||||
|
|
||||||
|
|
||||||
...and many more!
|
...and many more!
|
||||||
|
|
||||||
|
@ -16,9 +16,9 @@ doc.addSection({
|
|||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: "Github is the best",
|
text: "\tGithub is the best",
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Generate a CV
|
// Generate a CV
|
||||||
// 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, TextRun } from "../build";
|
import { AlignmentType, Document, HeadingLevel, Packer, Paragraph, TabStopPosition, TabStopType, TextRun } from "../build";
|
||||||
|
|
||||||
// tslint:disable:no-shadowed-variable
|
// tslint:disable:no-shadowed-variable
|
||||||
|
|
||||||
@ -226,18 +226,21 @@ class DocumentCreator {
|
|||||||
|
|
||||||
public createInstitutionHeader(institutionName: string, dateText: string): Paragraph {
|
public createInstitutionHeader(institutionName: string, dateText: string): Paragraph {
|
||||||
return new Paragraph({
|
return new Paragraph({
|
||||||
tabStop: {
|
tabStops: [
|
||||||
maxRight: {},
|
{
|
||||||
},
|
type: TabStopType.RIGHT,
|
||||||
|
position: TabStopPosition.MAX,
|
||||||
|
},
|
||||||
|
],
|
||||||
children: [
|
children: [
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: institutionName,
|
text: institutionName,
|
||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: dateText,
|
text: `\t${dateText}`,
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,82 +1,153 @@
|
|||||||
// Setting styles with JavaScript configuration
|
// Setting styles with JavaScript configuration
|
||||||
// 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, Footer, HeadingLevel, Media, Packer, Paragraph, Table, TableCell, TableRow } from "../build";
|
import {
|
||||||
|
AlignmentType,
|
||||||
|
Document,
|
||||||
|
Footer,
|
||||||
|
HeadingLevel,
|
||||||
|
Media,
|
||||||
|
Packer,
|
||||||
|
Paragraph,
|
||||||
|
Table,
|
||||||
|
TableCell,
|
||||||
|
TableRow,
|
||||||
|
TabStopPosition,
|
||||||
|
UnderlineType,
|
||||||
|
} from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document({
|
||||||
|
styles: {
|
||||||
doc.Styles.createParagraphStyle("Heading1", "Heading 1")
|
paragraphStyles: [
|
||||||
.basedOn("Normal")
|
{
|
||||||
.next("Normal")
|
id: "Heading1",
|
||||||
.quickFormat()
|
name: "Heading 1",
|
||||||
.font("Calibri")
|
basedOn: "Normal",
|
||||||
.size(52)
|
next: "Normal",
|
||||||
.center()
|
quickFormat: true,
|
||||||
.bold()
|
run: {
|
||||||
.color("000000")
|
font: "Calibri",
|
||||||
.spacing({ line: 340 })
|
size: 52,
|
||||||
.underline("single", "000000");
|
bold: true,
|
||||||
|
color: "000000",
|
||||||
doc.Styles.createParagraphStyle("Heading2", "Heading 2")
|
underline: {
|
||||||
.basedOn("Normal")
|
type: UnderlineType.SINGLE,
|
||||||
.next("Normal")
|
color: "000000",
|
||||||
.font("Calibri")
|
},
|
||||||
.quickFormat()
|
},
|
||||||
.size(26)
|
paragraph: {
|
||||||
.bold()
|
alignment: AlignmentType.CENTER,
|
||||||
.spacing({ line: 340 });
|
spacing: { line: 340 },
|
||||||
|
},
|
||||||
doc.Styles.createParagraphStyle("Heading3", "Heading 3")
|
},
|
||||||
.basedOn("Normal")
|
{
|
||||||
.next("Normal")
|
id: "Heading2",
|
||||||
.font("Calibri")
|
name: "Heading 2",
|
||||||
.quickFormat()
|
basedOn: "Normal",
|
||||||
.size(26)
|
next: "Normal",
|
||||||
.bold()
|
quickFormat: true,
|
||||||
.spacing({ line: 276 });
|
run: {
|
||||||
|
font: "Calibri",
|
||||||
doc.Styles.createParagraphStyle("Heading4", "Heading 4")
|
size: 26,
|
||||||
.basedOn("Normal")
|
bold: true,
|
||||||
.next("Normal")
|
},
|
||||||
.justified()
|
paragraph: {
|
||||||
.font("Calibri")
|
spacing: { line: 340 },
|
||||||
.size(26)
|
},
|
||||||
.bold();
|
},
|
||||||
|
{
|
||||||
doc.Styles.createParagraphStyle("normalPara", "Normal Para")
|
id: "Heading3",
|
||||||
.basedOn("Normal")
|
name: "Heading 3",
|
||||||
.next("Normal")
|
basedOn: "Normal",
|
||||||
.font("Calibri")
|
next: "Normal",
|
||||||
.quickFormat()
|
quickFormat: true,
|
||||||
.leftTabStop(453.543307087)
|
run: {
|
||||||
.maxRightTabStop()
|
font: "Calibri",
|
||||||
.size(26)
|
size: 26,
|
||||||
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
|
bold: true,
|
||||||
|
},
|
||||||
doc.Styles.createParagraphStyle("normalPara2", "Normal Para2")
|
paragraph: {
|
||||||
.basedOn("Normal")
|
spacing: { line: 276 },
|
||||||
.next("Normal")
|
},
|
||||||
.quickFormat()
|
},
|
||||||
.font("Calibri")
|
{
|
||||||
.size(26)
|
id: "Heading4",
|
||||||
.justified()
|
name: "Heading 4",
|
||||||
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
|
basedOn: "Normal",
|
||||||
|
next: "Normal",
|
||||||
doc.Styles.createParagraphStyle("aside", "Aside")
|
quickFormat: true,
|
||||||
.basedOn("Normal")
|
run: {
|
||||||
.next("Normal")
|
font: "Calibri",
|
||||||
.color("999999")
|
size: 26,
|
||||||
.italics()
|
bold: true,
|
||||||
.indent({ left: 720 })
|
},
|
||||||
.spacing({ line: 276 });
|
paragraph: {
|
||||||
|
alignment: AlignmentType.JUSTIFIED,
|
||||||
doc.Styles.createParagraphStyle("wellSpaced", "Well Spaced")
|
},
|
||||||
.basedOn("Normal")
|
},
|
||||||
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
|
{
|
||||||
|
id: "normalPara",
|
||||||
doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph")
|
name: "Normal Para",
|
||||||
.quickFormat()
|
basedOn: "Normal",
|
||||||
.basedOn("Normal");
|
next: "Normal",
|
||||||
|
quickFormat: true,
|
||||||
|
run: {
|
||||||
|
font: "Calibri",
|
||||||
|
size: 26,
|
||||||
|
bold: true,
|
||||||
|
},
|
||||||
|
paragraph: {
|
||||||
|
spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 },
|
||||||
|
rightTabStop: TabStopPosition.MAX,
|
||||||
|
leftTabStop: 453.543307087,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "normalPara2",
|
||||||
|
name: "Normal Para2",
|
||||||
|
basedOn: "Normal",
|
||||||
|
next: "Normal",
|
||||||
|
quickFormat: true,
|
||||||
|
run: {
|
||||||
|
font: "Calibri",
|
||||||
|
size: 26,
|
||||||
|
},
|
||||||
|
paragraph: {
|
||||||
|
alignment: AlignmentType.JUSTIFIED,
|
||||||
|
spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "aside",
|
||||||
|
name: "Aside",
|
||||||
|
basedOn: "Normal",
|
||||||
|
next: "Normal",
|
||||||
|
run: {
|
||||||
|
color: "999999",
|
||||||
|
italics: true,
|
||||||
|
},
|
||||||
|
paragraph: {
|
||||||
|
spacing: { line: 276 },
|
||||||
|
indent: { left: 720 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "wellSpaced",
|
||||||
|
name: "Well Spaced",
|
||||||
|
basedOn: "Normal",
|
||||||
|
paragraph: {
|
||||||
|
spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "ListParagraph",
|
||||||
|
name: "List Paragraph",
|
||||||
|
basedOn: "Normal",
|
||||||
|
quickFormat: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const image = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));
|
const image = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Page numbers
|
// Page numbers
|
||||||
// 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, Header, Packer, Paragraph, TextRun } from "../build";
|
import { AlignmentType, Document, Header, Packer, PageBreak, PageNumber, Paragraph, TextRun } from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document();
|
||||||
|
|
||||||
@ -11,7 +11,12 @@ doc.addSection({
|
|||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
alignment: AlignmentType.RIGHT,
|
alignment: AlignmentType.RIGHT,
|
||||||
children: [new TextRun("My Title "), new TextRun("Page ").pageNumber()],
|
children: [
|
||||||
|
new TextRun("My Title "),
|
||||||
|
new TextRun({
|
||||||
|
children: ["Page ", PageNumber.CURRENT],
|
||||||
|
}),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@ -19,15 +24,20 @@ doc.addSection({
|
|||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
alignment: AlignmentType.RIGHT,
|
alignment: AlignmentType.RIGHT,
|
||||||
children: [new TextRun("First Page Header "), new TextRun("Page ").pageNumber()],
|
children: [
|
||||||
|
new TextRun("First Page Header "),
|
||||||
|
new TextRun({
|
||||||
|
children: ["Page ", PageNumber.CURRENT],
|
||||||
|
}),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "First Page",
|
children: [new TextRun("First Page"), new PageBreak()],
|
||||||
}).pageBreak(),
|
}),
|
||||||
new Paragraph("Second Page"),
|
new Paragraph("Second Page"),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Multiple sections and headers
|
// Multiple sections and headers
|
||||||
// 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, Footer, Header, Packer, PageNumberFormat, PageOrientation, Paragraph, TextRun } from "../build";
|
import { Document, Footer, Header, Packer, PageNumber, PageNumberFormat, PageOrientation, Paragraph, TextRun } from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document();
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
children: [new Paragraph("Hello World").pageBreak()],
|
children: [new Paragraph("Hello World")],
|
||||||
});
|
});
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
@ -53,7 +53,11 @@ doc.addSection({
|
|||||||
default: new Header({
|
default: new Header({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [new TextRun("Page number: ").pageNumber()],
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
children: ["Page number: ", PageNumber.CURRENT],
|
||||||
|
}),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@ -69,7 +73,11 @@ doc.addSection({
|
|||||||
default: new Header({
|
default: new Header({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [new TextRun("Page number: ").pageNumber()],
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
children: ["Page number: ", PageNumber.CURRENT],
|
||||||
|
}),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@ -90,7 +98,11 @@ doc.addSection({
|
|||||||
default: new Header({
|
default: new Header({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [new TextRun("Page number: ").pageNumber()],
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
children: ["Page number: ", PageNumber.CURRENT],
|
||||||
|
}),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
@ -1,16 +1,54 @@
|
|||||||
// Footnotes
|
// Footnotes
|
||||||
// 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, Packer, Paragraph } from "../build";
|
import { Document, FootnoteReferenceRun, Packer, Paragraph, TextRun } from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document({
|
||||||
|
footnotes: [
|
||||||
doc.addSection({
|
new Paragraph("Foo"),
|
||||||
children: [new Paragraph("Hello World").referenceFootnote(1), new Paragraph("Hello World").referenceFootnote(2)],
|
new Paragraph("Test"),
|
||||||
|
new Paragraph("My amazing reference"),
|
||||||
|
new Paragraph("Foo1"),
|
||||||
|
new Paragraph("Test1"),
|
||||||
|
new Paragraph("My amazing reference1"),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
doc.createFootnote(new Paragraph("Test"));
|
doc.addSection({
|
||||||
doc.createFootnote(new Paragraph("My amazing reference"));
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
children: ["Hello", new FootnoteReferenceRun(1)],
|
||||||
|
}),
|
||||||
|
new TextRun({
|
||||||
|
children: [" World!", new FootnoteReferenceRun(2)],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Hello World"), new FootnoteReferenceRun(3)],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.addSection({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
children: ["Hello", new FootnoteReferenceRun(4)],
|
||||||
|
}),
|
||||||
|
new TextRun({
|
||||||
|
children: [" World!", new FootnoteReferenceRun(5)],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Hello World"), new FootnoteReferenceRun(6)],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
Packer.toBuffer(doc).then((buffer) => {
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
fs.writeFileSync("My Document.docx", buffer);
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
@ -15,9 +15,9 @@ doc.addSection({
|
|||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: "Bar",
|
text: "\tBar",
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -1,55 +1,105 @@
|
|||||||
// Example on how to customise the look at feel using Styles
|
// Example on how to customise the look at feel using Styles
|
||||||
// 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, TextRun } from "../build";
|
import { AlignmentType, Document, HeadingLevel, Packer, Paragraph, TextRun, UnderlineType } from "../build";
|
||||||
|
|
||||||
const doc = new Document({
|
const doc = new Document({
|
||||||
creator: "Clippy",
|
creator: "Clippy",
|
||||||
title: "Sample Document",
|
title: "Sample Document",
|
||||||
description: "A brief example of using docx",
|
description: "A brief example of using docx",
|
||||||
|
styles: {
|
||||||
|
paragraphStyles: [
|
||||||
|
{
|
||||||
|
id: "Heading1",
|
||||||
|
name: "Heading 1",
|
||||||
|
basedOn: "Normal",
|
||||||
|
next: "Normal",
|
||||||
|
quickFormat: true,
|
||||||
|
run: {
|
||||||
|
size: 28,
|
||||||
|
bold: true,
|
||||||
|
italics: true,
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
paragraph: {
|
||||||
|
spacing: {
|
||||||
|
after: 120,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "Heading2",
|
||||||
|
name: "Heading 2",
|
||||||
|
basedOn: "Normal",
|
||||||
|
next: "Normal",
|
||||||
|
quickFormat: true,
|
||||||
|
run: {
|
||||||
|
size: 26,
|
||||||
|
bold: true,
|
||||||
|
underline: {
|
||||||
|
type: UnderlineType.DOUBLE,
|
||||||
|
color: "FF0000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
paragraph: {
|
||||||
|
spacing: {
|
||||||
|
before: 240,
|
||||||
|
after: 120,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "aside",
|
||||||
|
name: "Aside",
|
||||||
|
basedOn: "Normal",
|
||||||
|
next: "Normal",
|
||||||
|
run: {
|
||||||
|
color: "999999",
|
||||||
|
italics: true,
|
||||||
|
},
|
||||||
|
paragraph: {
|
||||||
|
indent: {
|
||||||
|
left: 720,
|
||||||
|
},
|
||||||
|
spacing: {
|
||||||
|
line: 276,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "wellSpaced",
|
||||||
|
name: "Well Spaced",
|
||||||
|
basedOn: "Normal",
|
||||||
|
quickFormat: true,
|
||||||
|
paragraph: {
|
||||||
|
spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "ListParagraph",
|
||||||
|
name: "List Paragraph",
|
||||||
|
basedOn: "Normal",
|
||||||
|
quickFormat: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
numbering: {
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
reference: "my-crazy-numbering",
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerLetter",
|
||||||
|
text: "%1)",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
doc.Styles.createParagraphStyle("Heading1", "Heading 1")
|
|
||||||
.basedOn("Normal")
|
|
||||||
.next("Normal")
|
|
||||||
.quickFormat()
|
|
||||||
.size(28)
|
|
||||||
.bold()
|
|
||||||
.italics()
|
|
||||||
.spacing({ after: 120 });
|
|
||||||
|
|
||||||
doc.Styles.createParagraphStyle("Heading2", "Heading 2")
|
|
||||||
.basedOn("Normal")
|
|
||||||
.next("Normal")
|
|
||||||
.quickFormat()
|
|
||||||
.size(26)
|
|
||||||
.bold()
|
|
||||||
.underline("double", "FF0000")
|
|
||||||
.spacing({ before: 240, after: 120 });
|
|
||||||
|
|
||||||
doc.Styles.createParagraphStyle("aside", "Aside")
|
|
||||||
.basedOn("Normal")
|
|
||||||
.next("Normal")
|
|
||||||
.color("999999")
|
|
||||||
.italics()
|
|
||||||
.indent({ left: 720 })
|
|
||||||
.spacing({ line: 276 });
|
|
||||||
|
|
||||||
doc.Styles.createParagraphStyle("wellSpaced", "Well Spaced")
|
|
||||||
.basedOn("Normal")
|
|
||||||
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
|
|
||||||
|
|
||||||
doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph")
|
|
||||||
.quickFormat()
|
|
||||||
.basedOn("Normal");
|
|
||||||
|
|
||||||
const numberedAbstract = doc.Numbering.createAbstractNumbering();
|
|
||||||
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "left");
|
|
||||||
|
|
||||||
const letterNumbering = doc.Numbering.createConcreteNumbering(numberedAbstract);
|
|
||||||
const letterNumbering5 = doc.Numbering.createConcreteNumbering(numberedAbstract);
|
|
||||||
letterNumbering5.overrideLevel(0, 5);
|
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
@ -64,32 +114,34 @@ doc.addSection({
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "Option1",
|
text: "Option1",
|
||||||
numbering: {
|
numbering: {
|
||||||
num: letterNumbering,
|
reference: "my-crazy-numbering",
|
||||||
level: 0,
|
level: 0,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "Option5 -- override 2 to 5",
|
text: "Option5 -- override 2 to 5",
|
||||||
numbering: {
|
numbering: {
|
||||||
num: letterNumbering,
|
reference: "my-crazy-numbering",
|
||||||
level: 0,
|
level: 0,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "Option3",
|
text: "Option3",
|
||||||
numbering: {
|
numbering: {
|
||||||
num: letterNumbering,
|
reference: "my-crazy-numbering",
|
||||||
level: 0,
|
level: 0,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
new Paragraph({}).addRun(
|
new Paragraph({
|
||||||
new TextRun({
|
children: [
|
||||||
text: "Some monospaced content",
|
new TextRun({
|
||||||
font: {
|
text: "Some monospaced content",
|
||||||
name: "Monospace",
|
font: {
|
||||||
},
|
name: "Monospace",
|
||||||
}),
|
},
|
||||||
),
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "An aside, in light gray italics and indented",
|
text: "An aside, in light gray italics and indented",
|
||||||
style: "aside",
|
style: "aside",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// This demo shows how to create bookmarks then link to them with internal hyperlinks
|
// This demo shows how to create bookmarks then link to them with internal hyperlinks
|
||||||
// Import from 'docx' rather than '../build' if you install from npm
|
// Import 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 } from "../build";
|
import { Bookmark, Document, HeadingLevel, HyperlinkRef, HyperlinkType, Packer, PageBreak, Paragraph } from "../build";
|
||||||
|
|
||||||
const LOREM_IPSUM =
|
const LOREM_IPSUM =
|
||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mi velit, convallis convallis scelerisque nec, faucibus nec leo. Phasellus at posuere mauris, tempus dignissim velit. Integer et tortor dolor. Duis auctor efficitur mattis. Vivamus ut metus accumsan tellus auctor sollicitudin venenatis et nibh. Cras quis massa ac metus fringilla venenatis. Proin rutrum mauris purus, ut suscipit magna consectetur id. Integer consectetur sollicitudin ante, vitae faucibus neque efficitur in. Praesent ultricies nibh lectus. Mauris pharetra id odio eget iaculis. Duis dictum, risus id pellentesque rutrum, lorem quam malesuada massa, quis ullamcorper turpis urna a diam. Cras vulputate metus vel massa porta ullamcorper. Etiam porta condimentum nulla nec tristique. Sed nulla urna, pharetra non tortor sed, sollicitudin molestie diam. Maecenas enim leo, feugiat eget vehicula id, sollicitudin vitae ante.";
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mi velit, convallis convallis scelerisque nec, faucibus nec leo. Phasellus at posuere mauris, tempus dignissim velit. Integer et tortor dolor. Duis auctor efficitur mattis. Vivamus ut metus accumsan tellus auctor sollicitudin venenatis et nibh. Cras quis massa ac metus fringilla venenatis. Proin rutrum mauris purus, ut suscipit magna consectetur id. Integer consectetur sollicitudin ante, vitae faucibus neque efficitur in. Praesent ultricies nibh lectus. Mauris pharetra id odio eget iaculis. Duis dictum, risus id pellentesque rutrum, lorem quam malesuada massa, quis ullamcorper turpis urna a diam. Cras vulputate metus vel massa porta ullamcorper. Etiam porta condimentum nulla nec tristique. Sed nulla urna, pharetra non tortor sed, sollicitudin molestie diam. Maecenas enim leo, feugiat eget vehicula id, sollicitudin vitae ante.";
|
||||||
@ -10,23 +10,28 @@ const doc = new Document({
|
|||||||
creator: "Clippy",
|
creator: "Clippy",
|
||||||
title: "Sample Document",
|
title: "Sample Document",
|
||||||
description: "A brief example of using docx with bookmarks and internal hyperlinks",
|
description: "A brief example of using docx with bookmarks and internal hyperlinks",
|
||||||
|
hyperlinks: {
|
||||||
|
myAnchorId: {
|
||||||
|
text: "Hyperlink",
|
||||||
|
type: HyperlinkType.INTERNAL,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const anchorId = "anchorID";
|
|
||||||
|
|
||||||
// First create the bookmark
|
|
||||||
const bookmark = doc.createBookmark(anchorId, "Lorem Ipsum");
|
|
||||||
const hyperlink = doc.createInternalHyperLink(anchorId, `Click me!`);
|
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
heading: HeadingLevel.HEADING_1,
|
heading: HeadingLevel.HEADING_1,
|
||||||
}).addBookmark(bookmark),
|
children: [new Bookmark("myAnchorId", "Lorem Ipsum")],
|
||||||
|
}),
|
||||||
new Paragraph("\n"),
|
new Paragraph("\n"),
|
||||||
new Paragraph(LOREM_IPSUM),
|
new Paragraph(LOREM_IPSUM),
|
||||||
new Paragraph({}).pageBreak(),
|
new Paragraph({
|
||||||
new Paragraph({}).addHyperLink(hyperlink),
|
children: [new PageBreak()],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [new HyperlinkRef("myAnchorId")],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,28 +1,53 @@
|
|||||||
// Custom styles using JavaScript configuration
|
// Custom styles using JavaScript configuration
|
||||||
// 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 } from "../build";
|
import { Document, HeadingLevel, Packer, Paragraph, UnderlineType } from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document({
|
||||||
|
styles: {
|
||||||
// The first argument is an ID you use to apply the style to paragraphs
|
paragraphStyles: [
|
||||||
// The second argument is a human-friendly name to show in the UI
|
{
|
||||||
doc.Styles.createParagraphStyle("myWonkyStyle", "My Wonky Style")
|
id: "myWonkyStyle",
|
||||||
.basedOn("Normal")
|
name: "My Wonky Style",
|
||||||
.next("Normal")
|
basedOn: "Normal",
|
||||||
.color("990000")
|
next: "Normal",
|
||||||
.italics()
|
run: {
|
||||||
.indent({ left: 720 }) // 720 TWIP === 720 / 20 pt === .5 in
|
color: "990000",
|
||||||
.spacing({ line: 276 }); // 276 / 240 = 1.15x line spacing
|
italics: true,
|
||||||
|
},
|
||||||
doc.Styles.createParagraphStyle("Heading2", "Heading 2")
|
paragraph: {
|
||||||
.basedOn("Normal")
|
indent: {
|
||||||
.next("Normal")
|
left: 720,
|
||||||
.quickFormat()
|
},
|
||||||
.size(26) // 26 half-points === 13pt font
|
spacing: {
|
||||||
.bold()
|
line: 276,
|
||||||
.underline("double", "FF0000")
|
},
|
||||||
.spacing({ before: 240, after: 120 }); // TWIP for both
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "Heading2",
|
||||||
|
name: "Heading 2",
|
||||||
|
basedOn: "Normal",
|
||||||
|
next: "Normal",
|
||||||
|
quickFormat: true,
|
||||||
|
run: {
|
||||||
|
bold: true,
|
||||||
|
size: 26,
|
||||||
|
underline: {
|
||||||
|
type: UnderlineType.DOUBLE,
|
||||||
|
color: "FF0000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
paragraph: {
|
||||||
|
spacing: {
|
||||||
|
before: 240,
|
||||||
|
after: 120,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
children: [
|
children: [
|
||||||
|
@ -3,15 +3,23 @@
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { File, HeadingLevel, Packer, Paragraph, StyleLevel, TableOfContents } from "../build";
|
import { File, HeadingLevel, Packer, Paragraph, StyleLevel, TableOfContents } from "../build";
|
||||||
|
|
||||||
const doc = new File();
|
const doc = new File({
|
||||||
|
styles: {
|
||||||
// The first argument is an ID you use to apply the style to paragraphs
|
paragraphStyles: [
|
||||||
// The second argument is a human-friendly name to show in the UI
|
{
|
||||||
doc.Styles.createParagraphStyle("MySpectacularStyle", "My Spectacular Style")
|
id: "MySpectacularStyle",
|
||||||
.basedOn("Heading1")
|
name: "My Spectacular Style",
|
||||||
.next("Heading1")
|
basedOn: "Heading1",
|
||||||
.color("990000")
|
next: "Heading1",
|
||||||
.italics();
|
quickFormat: true,
|
||||||
|
run: {
|
||||||
|
italics: true,
|
||||||
|
color: "990000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// WordprocessingML docs for TableOfContents can be found here:
|
// WordprocessingML docs for TableOfContents can be found here:
|
||||||
// http://officeopenxml.com/WPtableOfContents.php
|
// http://officeopenxml.com/WPtableOfContents.php
|
||||||
|
@ -1,23 +1,53 @@
|
|||||||
// Numbered lists
|
// Numbered lists
|
||||||
// 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, Numbering, Packer, Paragraph } from "../build";
|
import { AlignmentType, Document, Packer, Paragraph } from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document({
|
||||||
|
numbering: {
|
||||||
const numbering = new Numbering();
|
config: [
|
||||||
|
{
|
||||||
const abstractNum = numbering.createAbstractNumbering();
|
levels: [
|
||||||
abstractNum.createLevel(0, "upperRoman", "%1", "start").indent({ left: 720, hanging: 260 });
|
{
|
||||||
|
level: 0,
|
||||||
const concrete = numbering.createConcreteNumbering(abstractNum);
|
format: "upperRoman",
|
||||||
|
text: "%1",
|
||||||
|
alignment: AlignmentType.START,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 720, hanging: 260 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
reference: "my-crazy-reference",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "decimal",
|
||||||
|
text: "%1",
|
||||||
|
alignment: AlignmentType.START,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 720, hanging: 260 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
reference: "my-number-numbering-reference",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "line with contextual spacing",
|
text: "line with contextual spacing",
|
||||||
numbering: {
|
numbering: {
|
||||||
num: concrete,
|
reference: "my-crazy-reference",
|
||||||
level: 0,
|
level: 0,
|
||||||
},
|
},
|
||||||
contextualSpacing: true,
|
contextualSpacing: true,
|
||||||
@ -28,7 +58,7 @@ doc.addSection({
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "line with contextual spacing",
|
text: "line with contextual spacing",
|
||||||
numbering: {
|
numbering: {
|
||||||
num: concrete,
|
reference: "my-crazy-reference",
|
||||||
level: 0,
|
level: 0,
|
||||||
},
|
},
|
||||||
contextualSpacing: true,
|
contextualSpacing: true,
|
||||||
@ -39,7 +69,7 @@ doc.addSection({
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "line without contextual spacing",
|
text: "line without contextual spacing",
|
||||||
numbering: {
|
numbering: {
|
||||||
num: concrete,
|
reference: "my-crazy-reference",
|
||||||
level: 0,
|
level: 0,
|
||||||
},
|
},
|
||||||
contextualSpacing: false,
|
contextualSpacing: false,
|
||||||
@ -50,7 +80,7 @@ doc.addSection({
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "line without contextual spacing",
|
text: "line without contextual spacing",
|
||||||
numbering: {
|
numbering: {
|
||||||
num: concrete,
|
reference: "my-crazy-reference",
|
||||||
level: 0,
|
level: 0,
|
||||||
},
|
},
|
||||||
contextualSpacing: false,
|
contextualSpacing: false,
|
||||||
@ -58,6 +88,27 @@ doc.addSection({
|
|||||||
before: 200,
|
before: 200,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
text: "Step 1 - Add sugar",
|
||||||
|
numbering: {
|
||||||
|
reference: "my-number-numbering-reference",
|
||||||
|
level: 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
text: "Step 2 - Add wheat",
|
||||||
|
numbering: {
|
||||||
|
reference: "my-number-numbering-reference",
|
||||||
|
level: 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
text: "Step 3 - Put in oven",
|
||||||
|
numbering: {
|
||||||
|
reference: "my-number-numbering-reference",
|
||||||
|
level: 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,46 +1,91 @@
|
|||||||
// Numbering and bullet points example
|
// Numbering and bullet points example
|
||||||
// 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, Numbering, Packer, Paragraph } from "../build";
|
import { AlignmentType, Document, Packer, Paragraph } from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document({
|
||||||
|
numbering: {
|
||||||
const numbering = new Numbering();
|
config: [
|
||||||
|
{
|
||||||
const abstractNum = numbering.createAbstractNumbering();
|
reference: "my-crazy-numbering",
|
||||||
abstractNum.createLevel(0, "upperRoman", "%1", "start").indent({ left: 720, hanging: 260 });
|
levels: [
|
||||||
abstractNum.createLevel(1, "decimal", "%2.", "start").indent({ left: 1440, hanging: 980 });
|
{
|
||||||
abstractNum.createLevel(2, "lowerLetter", "%3)", "start").indent({ left: 2160, hanging: 1700 });
|
level: 0,
|
||||||
|
format: "upperRoman",
|
||||||
const concrete = numbering.createConcreteNumbering(abstractNum);
|
text: "%1",
|
||||||
|
alignment: AlignmentType.START,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 720, hanging: 260 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 1,
|
||||||
|
format: "decimal",
|
||||||
|
text: "%2.",
|
||||||
|
alignment: AlignmentType.START,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 1440, hanging: 980 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 2,
|
||||||
|
format: "lowerLetter",
|
||||||
|
text: "%3)",
|
||||||
|
alignment: AlignmentType.START,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 2160, hanging: 1700 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 3,
|
||||||
|
format: "upperLetter",
|
||||||
|
text: "%4)",
|
||||||
|
alignment: AlignmentType.START,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 2880, hanging: 2420 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "Hey you",
|
text: "Hey you",
|
||||||
numbering: {
|
numbering: {
|
||||||
num: concrete,
|
reference: "my-crazy-numbering",
|
||||||
level: 0,
|
level: 0,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "What's up fam",
|
text: "What's up fam",
|
||||||
numbering: {
|
numbering: {
|
||||||
num: concrete,
|
reference: "my-crazy-numbering",
|
||||||
level: 1,
|
level: 1,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "Hello World 2",
|
text: "Hello World 2",
|
||||||
numbering: {
|
numbering: {
|
||||||
num: concrete,
|
reference: "my-crazy-numbering",
|
||||||
level: 1,
|
level: 1,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "Yeah boi",
|
text: "Yeah boi",
|
||||||
numbering: {
|
numbering: {
|
||||||
num: concrete,
|
reference: "my-crazy-numbering",
|
||||||
level: 2,
|
level: 2,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@ -68,6 +113,27 @@ doc.addSection({
|
|||||||
level: 3,
|
level: 3,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
text: "101 MSXFM",
|
||||||
|
numbering: {
|
||||||
|
reference: "my-crazy-numbering",
|
||||||
|
level: 3,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
text: "back to level 1",
|
||||||
|
numbering: {
|
||||||
|
reference: "my-crazy-numbering",
|
||||||
|
level: 1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
text: "back to level 0",
|
||||||
|
numbering: {
|
||||||
|
reference: "my-crazy-numbering",
|
||||||
|
level: 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Example of how you would create a table and add data to it
|
// Example of how you would create a table and add data to it
|
||||||
// Import from 'docx' rather than '../build' if you install from npm
|
// Import 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, Table, TableCell, TableRow, VerticalAlign } from "../build";
|
import { Document, HeadingLevel, Packer, Paragraph, Table, TableCell, TableRow, VerticalAlign, TextDirection } from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document();
|
||||||
|
|
||||||
@ -17,6 +17,14 @@ const table = new Table({
|
|||||||
children: [new Paragraph({}), new Paragraph({})],
|
children: [new Paragraph({}), new Paragraph({})],
|
||||||
verticalAlign: VerticalAlign.CENTER,
|
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({
|
new TableRow({
|
||||||
@ -38,6 +46,22 @@ const table = new Table({
|
|||||||
],
|
],
|
||||||
verticalAlign: VerticalAlign.CENTER,
|
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,
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// Example of how you would merge cells together (Rows and Columns) and apply shading
|
// Example of how you would merge cells together (Rows and Columns) and apply shading
|
||||||
|
// 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 { Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build";
|
import { AlignmentType, Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document();
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ const table = new Table({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const table2 = new Table({
|
const table2 = new Table({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
rows: [
|
rows: [
|
||||||
new TableRow({
|
new TableRow({
|
||||||
children: [
|
children: [
|
||||||
@ -66,6 +68,7 @@ const table2 = new Table({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const table3 = new Table({
|
const table3 = new Table({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
rows: [
|
rows: [
|
||||||
new TableRow({
|
new TableRow({
|
||||||
children: [
|
children: [
|
||||||
|
@ -1,28 +1,44 @@
|
|||||||
// Sequential Captions
|
// Sequential Captions
|
||||||
// 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, Packer, Paragraph, TextRun } from "../build";
|
import { Document, Packer, Paragraph, SequentialIdentifier, TextRun } from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document();
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph("Hello World 1->")
|
new Paragraph({
|
||||||
.addSequentialIdentifier("Caption")
|
children: [
|
||||||
.addRun(new TextRun(" text after sequencial caption 2->"))
|
new TextRun("Hello World 1->"),
|
||||||
.addSequentialIdentifier("Caption"),
|
new SequentialIdentifier("Caption"),
|
||||||
new Paragraph("Hello World 1->")
|
new TextRun(" text after sequencial caption 2->"),
|
||||||
.addSequentialIdentifier("Label")
|
new SequentialIdentifier("Caption"),
|
||||||
.addRun(new TextRun(" text after sequencial caption 2->"))
|
],
|
||||||
.addSequentialIdentifier("Label"),
|
}),
|
||||||
new Paragraph("Hello World 1->")
|
new Paragraph({
|
||||||
.addSequentialIdentifier("Another")
|
children: [
|
||||||
.addRun(new TextRun(" text after sequencial caption 3->"))
|
new TextRun("Hello World 1->"),
|
||||||
.addSequentialIdentifier("Label"),
|
new SequentialIdentifier("Label"),
|
||||||
new Paragraph("Hello World 2->")
|
new TextRun(" text after sequencial caption 2->"),
|
||||||
.addSequentialIdentifier("Another")
|
new SequentialIdentifier("Label"),
|
||||||
.addRun(new TextRun(" text after sequencial caption 4->"))
|
],
|
||||||
.addSequentialIdentifier("Label"),
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun("Hello World 1->"),
|
||||||
|
new SequentialIdentifier("Another"),
|
||||||
|
new TextRun(" text after sequencial caption 3->"),
|
||||||
|
new SequentialIdentifier("Label"),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun("Hello World 2->"),
|
||||||
|
new SequentialIdentifier("Another"),
|
||||||
|
new TextRun(" text after sequencial caption 4->"),
|
||||||
|
new SequentialIdentifier("Label"),
|
||||||
|
],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import {
|
import {
|
||||||
Document,
|
Document,
|
||||||
|
OverlapType,
|
||||||
Packer,
|
Packer,
|
||||||
Paragraph,
|
Paragraph,
|
||||||
RelativeHorizontalPosition,
|
RelativeHorizontalPosition,
|
||||||
@ -43,6 +44,7 @@ const table = new Table({
|
|||||||
verticalAnchor: TableAnchorType.MARGIN,
|
verticalAnchor: TableAnchorType.MARGIN,
|
||||||
relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
|
relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
|
||||||
relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
|
relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
|
||||||
|
overlap: OverlapType.NEVER,
|
||||||
},
|
},
|
||||||
width: {
|
width: {
|
||||||
size: 4535,
|
size: 4535,
|
||||||
|
@ -1,15 +1,34 @@
|
|||||||
// Example on how to add hyperlinks to websites
|
// Example on how to add hyperlinks to websites
|
||||||
// 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, Packer, Paragraph } from "../build";
|
import { Document, HyperlinkRef, HyperlinkType, Packer, Paragraph, Media } from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document({
|
||||||
const paragraph = new Paragraph({});
|
hyperlinks: {
|
||||||
const link = doc.createHyperlink("http://www.example.com", "Hyperlink");
|
myCoolLink: {
|
||||||
|
link: "http://www.example.com",
|
||||||
|
text: "Hyperlink",
|
||||||
|
type: HyperlinkType.EXTERNAL,
|
||||||
|
},
|
||||||
|
myOtherLink: {
|
||||||
|
link: "http://www.google.com",
|
||||||
|
text: "Google Link",
|
||||||
|
type: HyperlinkType.EXTERNAL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const image1 = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||||
|
|
||||||
paragraph.addHyperLink(link);
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
children: [paragraph],
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [new HyperlinkRef("myCoolLink")],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [image1, new HyperlinkRef("myOtherLink")],
|
||||||
|
}),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
Packer.toBuffer(doc).then((buffer) => {
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Example how to display page numbers
|
// Example how to display page numbers
|
||||||
// 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, Footer, Header, Packer, PageNumberFormat, Paragraph, TextRun } from "../build";
|
import { AlignmentType, Document, Footer, Header, Packer, PageBreak, PageNumber, PageNumberFormat, Paragraph, TextRun } from "../build";
|
||||||
|
|
||||||
const doc = new Document({});
|
const doc = new Document({});
|
||||||
|
|
||||||
@ -9,9 +9,17 @@ doc.addSection({
|
|||||||
headers: {
|
headers: {
|
||||||
default: new Header({
|
default: new Header({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph("Foo Bar corp. ")
|
new Paragraph({
|
||||||
.addRun(new TextRun("Page Number ").pageNumber())
|
children: [
|
||||||
.addRun(new TextRun(" to ").numberOfTotalPages()),
|
new TextRun("Foo Bar corp. "),
|
||||||
|
new TextRun({
|
||||||
|
children: ["Page Number ", PageNumber.CURRENT],
|
||||||
|
}),
|
||||||
|
new TextRun({
|
||||||
|
children: [" to ", PageNumber.TOTAL_PAGES],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -19,11 +27,17 @@ doc.addSection({
|
|||||||
default: new Footer({
|
default: new Footer({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "Foo Bar corp. ",
|
|
||||||
alignment: AlignmentType.CENTER,
|
alignment: AlignmentType.CENTER,
|
||||||
})
|
children: [
|
||||||
.addRun(new TextRun("Page Number: ").pageNumber())
|
new TextRun("Foo Bar corp. "),
|
||||||
.addRun(new TextRun(" to ").numberOfTotalPages()),
|
new TextRun({
|
||||||
|
children: ["Page Number: ", PageNumber.CURRENT],
|
||||||
|
}),
|
||||||
|
new TextRun({
|
||||||
|
children: [" to ", PageNumber.TOTAL_PAGES],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -32,11 +46,21 @@ doc.addSection({
|
|||||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
new Paragraph("Hello World 1").pageBreak(),
|
new Paragraph({
|
||||||
new Paragraph("Hello World 2").pageBreak(),
|
children: [new TextRun("Hello World 1"), new PageBreak()],
|
||||||
new Paragraph("Hello World 3").pageBreak(),
|
}),
|
||||||
new Paragraph("Hello World 4").pageBreak(),
|
new Paragraph({
|
||||||
new Paragraph("Hello World 5").pageBreak(),
|
children: [new TextRun("Hello World 2"), new PageBreak()],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Hello World 3"), new PageBreak()],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Hello World 4"), new PageBreak()],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Hello World 5"), new PageBreak()],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,18 +1,30 @@
|
|||||||
// Multiple sections with total number of pages in each section
|
// Multiple sections with total number of pages in each section
|
||||||
// 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, Packer, PageNumberFormat, TextRun } from "../build";
|
import { AlignmentType, Document, Footer, Header, Packer, PageBreak, PageNumber, PageNumberFormat, Paragraph, TextRun } from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document();
|
||||||
|
|
||||||
|
const header = new Header({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun("Header on another page"),
|
||||||
|
new TextRun({
|
||||||
|
children: ["Page number: ", PageNumber.CURRENT],
|
||||||
|
}),
|
||||||
|
new TextRun({
|
||||||
|
children: [" to ", PageNumber.TOTAL_PAGES_IN_SECTION],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const header = doc.createHeader();
|
const footer = new Footer({
|
||||||
header.createParagraph("Header on another page");
|
children: [new Paragraph("Foo Bar corp. ")],
|
||||||
const footer = doc.createFooter();
|
});
|
||||||
footer.createParagraph("Foo Bar corp. ")
|
|
||||||
.center()
|
|
||||||
.addRun(new TextRun("Page Number: ").pageNumber())
|
|
||||||
.addRun(new TextRun(" to ").numberOfTotalPagesSection());
|
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
headers: {
|
headers: {
|
||||||
@ -21,13 +33,17 @@ doc.addSection({
|
|||||||
footers: {
|
footers: {
|
||||||
default: footer,
|
default: footer,
|
||||||
},
|
},
|
||||||
pageNumberStart: 1,
|
properties: {
|
||||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
pageNumberStart: 1,
|
||||||
|
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Section 1"), new PageBreak(), new TextRun("Section 1"), new PageBreak()],
|
||||||
|
}),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
doc.createParagraph("Section 1").pageBreak();
|
|
||||||
doc.createParagraph("Section 1").pageBreak();
|
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
headers: {
|
headers: {
|
||||||
default: header,
|
default: header,
|
||||||
@ -35,15 +51,17 @@ doc.addSection({
|
|||||||
footers: {
|
footers: {
|
||||||
default: footer,
|
default: footer,
|
||||||
},
|
},
|
||||||
pageNumberStart: 1,
|
properties: {
|
||||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
pageNumberStart: 1,
|
||||||
|
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Section 2"), new PageBreak(), new TextRun("Section 2"), new PageBreak()],
|
||||||
|
}),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
doc.createParagraph("Section 2").pageBreak();
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
doc.createParagraph("Section 2").pageBreak();
|
|
||||||
|
|
||||||
const packer = new Packer();
|
|
||||||
|
|
||||||
packer.toBuffer(doc).then((buffer) => {
|
|
||||||
fs.writeFileSync("My Document.docx", buffer);
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
});
|
});
|
||||||
|
31
demo/48-vertical-align.ts
Normal file
31
demo/48-vertical-align.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Example of making content of section vertically aligned
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Packer, Paragraph, SectionVerticalAlignValue, TextRun } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
doc.addSection({
|
||||||
|
properties: {
|
||||||
|
verticalAlign: SectionVerticalAlignValue.CENTER,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun("Hello World"),
|
||||||
|
new TextRun({
|
||||||
|
text: "Foo Bar",
|
||||||
|
bold: true,
|
||||||
|
}),
|
||||||
|
new TextRun({
|
||||||
|
text: "\tGithub is the best",
|
||||||
|
bold: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
37
demo/49-table-borders.ts
Normal file
37
demo/49-table-borders.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Add custom borders to the table itself
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { BorderStyle, Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const table = new Table({
|
||||||
|
rows: [
|
||||||
|
new TableRow({
|
||||||
|
children: [
|
||||||
|
new TableCell({
|
||||||
|
children: [new Paragraph("Hello")],
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
children: [],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new TableRow({
|
||||||
|
children: [
|
||||||
|
new TableCell({
|
||||||
|
children: [],
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
children: [new Paragraph("World")],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.addSection({ children: [table] });
|
||||||
|
|
||||||
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
61
demo/50-readme-demo.ts
Normal file
61
demo/50-readme-demo.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// 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, HeadingLevel, Media, Packer, Paragraph, Table, TableCell, TableRow, VerticalAlign } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const image1 = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||||
|
const image2 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));
|
||||||
|
|
||||||
|
const table = new Table({
|
||||||
|
rows: [
|
||||||
|
new TableRow({
|
||||||
|
children: [
|
||||||
|
new TableCell({
|
||||||
|
children: [new Paragraph(image1)],
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
text: "Hello",
|
||||||
|
heading: HeadingLevel.HEADING_1,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new TableRow({
|
||||||
|
children: [
|
||||||
|
new TableCell({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
text: "World",
|
||||||
|
heading: HeadingLevel.HEADING_1,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
children: [new Paragraph(image1)],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.addSection({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
text: "Hello World",
|
||||||
|
heading: HeadingLevel.HEADING_1,
|
||||||
|
}),
|
||||||
|
table,
|
||||||
|
new Paragraph(image2),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
37
demo/51-character-styles.ts
Normal file
37
demo/51-character-styles.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Custom character styles using JavaScript configuration
|
||||||
|
// 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({
|
||||||
|
styles: {
|
||||||
|
characterStyles: [
|
||||||
|
{
|
||||||
|
id: "myRedStyle",
|
||||||
|
name: "My Wonky Style",
|
||||||
|
basedOn: "Normal",
|
||||||
|
run: {
|
||||||
|
color: "990000",
|
||||||
|
italics: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.addSection({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Foo bar",
|
||||||
|
style: "myRedStyle",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
37
demo/52-japanese.ts
Normal file
37
demo/52-japanese.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Japanese text - Need to use a Japanese font
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, HeadingLevel, Packer, Paragraph } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document({
|
||||||
|
styles: {
|
||||||
|
paragraphStyles: [
|
||||||
|
{
|
||||||
|
id: "Normal",
|
||||||
|
name: "Normal",
|
||||||
|
basedOn: "Normal",
|
||||||
|
next: "Normal",
|
||||||
|
quickFormat: true,
|
||||||
|
run: {
|
||||||
|
font: "MS Gothic",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.addSection({
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
text: "KFCを食べるのが好き",
|
||||||
|
heading: HeadingLevel.HEADING_1,
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
text: "こんにちは",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
@ -21,9 +21,9 @@ doc.addSection({
|
|||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: "Github is the best",
|
text: "\tGithub is the best",
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
|
@ -12,21 +12,21 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
function generate() {
|
function generate() {
|
||||||
const doc = new Document();
|
const doc = new docx.Document();
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new docx.Paragraph({
|
||||||
children: [
|
children: [
|
||||||
new TextRun("Hello World"),
|
new docx.TextRun("Hello World"),
|
||||||
new TextRun({
|
new docx.TextRun({
|
||||||
text: "Foo Bar",
|
text: "Foo Bar",
|
||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new TextRun({
|
new docx.TextRun({
|
||||||
text: "Github is the best",
|
text: "\tGithub is the best",
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Packer.toBlob(doc).then((blob) => {
|
docx.Packer.toBlob(doc).then((blob) => {
|
||||||
console.log(blob);
|
console.log(blob);
|
||||||
saveAs(blob, "example.docx");
|
saveAs(blob, "example.docx");
|
||||||
console.log("Document created successfully");
|
console.log("Document created successfully");
|
||||||
|
@ -50,9 +50,9 @@ doc.addSection({
|
|||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: "Github is the best",
|
text: "\tGithub is the best",
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -7,7 +7,12 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
<meta name="description" content="Generate .docx documents with JavaScript">
|
<meta name="description" content="Generate .docx documents with JavaScript">
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css">
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/style.min.css"
|
||||||
|
title="docsify-darklight-theme"
|
||||||
|
type="text/css"
|
||||||
|
/>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -26,6 +31,10 @@
|
|||||||
<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="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js"
|
||||||
|
type="text/javascript">
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> Everything (text, images, graphs etc) in OpenXML is organised in paragraphs.
|
> Everything (text, images, graphs etc) in OpenXML is organised in paragraphs.
|
||||||
|
|
||||||
!> Paragraphs requires an understanding of [Sections](usage/sections.md).
|
!> Paragraphs requires an understanding of [Sections](sections.md).
|
||||||
|
|
||||||
You can create `Paragraphs` in the following ways:
|
You can create `Paragraphs` in the following ways:
|
||||||
|
|
||||||
@ -16,11 +16,11 @@ const paragraph = new Paragraph("Short hand Hello World");
|
|||||||
|
|
||||||
### Children Method
|
### Children Method
|
||||||
|
|
||||||
This method is useful for adding different `text` with different styles or adding `images` inline.
|
This method is useful for adding different [text](text.md) with different styles, [symbols](symbols.md), or adding [images](images.md) inline.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
children: [new TextRun("Lorem Ipsum Foo Bar"), new TextRun("Hello World")],
|
children: [new TextRun("Lorem Ipsum Foo Bar"), new TextRun("Hello World"), new SymbolRun("F071")],
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -56,27 +56,27 @@ doc.addSection({
|
|||||||
|
|
||||||
This is the list of options for a paragraph. A detailed explanation is below:
|
This is the list of options for a paragraph. A detailed explanation is below:
|
||||||
|
|
||||||
| Property | Type | Mandatory? | Possible Values |
|
| Property | Type | Mandatory? | Possible Values |
|
||||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------- |
|
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------- |
|
||||||
| [text](#text) | `string` | Optional | |
|
| [text](#text) | `string` | Optional | |
|
||||||
| [heading](#heading) | `HeadingLevel` | Optional | `HEADING_1`, `HEADING_2`, `HEADING_3`, `HEADING_4`, `HEADING_5`, `HEADING_6`, `TITLE` |
|
| [heading](#heading) | `HeadingLevel` | Optional | `HEADING_1`, `HEADING_2`, `HEADING_3`, `HEADING_4`, `HEADING_5`, `HEADING_6`, `TITLE` |
|
||||||
| [border](#border) | `IBorderOptions` | Optional | `top`, `bottom`, `left`, `right`. Each of these are of type IBorderPropertyOptions. Click here for Example |
|
| [border](#border) | `IBorderOptions` | Optional | `top`, `bottom`, `left`, `right`. Each of these are of type IBorderPropertyOptions. Click here for Example |
|
||||||
| [spacing](#spacing) | `ISpacingProperties` | Optional | See below for ISpacingProperties |
|
| [spacing](#spacing) | `ISpacingProperties` | Optional | See below for ISpacingProperties |
|
||||||
| [outlineLevel](#outline-level) | `number` | Optional | |
|
| [outlineLevel](#outline-level) | `number` | Optional | |
|
||||||
| alignment | `AlignmentType` | Optional | |
|
| alignment | `AlignmentType` | Optional | |
|
||||||
| heading | `HeadingLevel` | Optional | |
|
| heading | `HeadingLevel` | Optional | |
|
||||||
| bidirectional | `boolean` | Optional | |
|
| bidirectional | `boolean` | Optional | |
|
||||||
| thematicBreak | `boolean` | Optional | |
|
| thematicBreak | `boolean` | Optional | |
|
||||||
| pageBreakBefore | `boolean` | Optional | |
|
| pageBreakBefore | `boolean` | Optional | |
|
||||||
| contextualSpacing | `boolean` | Optional | |
|
| contextualSpacing | `boolean` | Optional | |
|
||||||
| indent | `IIndentAttributesProperties` | Optional | |
|
| indent | `IIndentAttributesProperties` | Optional | |
|
||||||
| keepLines | `boolean` | Optional | |
|
| keepLines | `boolean` | Optional | |
|
||||||
| keepNext | `boolean` | Optional | |
|
| keepNext | `boolean` | Optional | |
|
||||||
| children | `(TextRun or PictureRun or Hyperlink)[]` | Optional | |
|
| children | `(TextRun or PictureRun or Hyperlink)[]` | Optional | |
|
||||||
| style | `string` | Optional | |
|
| style | `string` | Optional | |
|
||||||
| tabStop | `{ left?: ITabStopOptions; right?: ITabStopOptions; maxRight?: { leader: LeaderType; }; center?: ITabStopOptions }` | Optional | |
|
| tabStop | `{ left?: ITabStopOptions; right?: ITabStopOptions; maxRight?: { leader: LeaderType; }; center?: ITabStopOptions }` | Optional | |
|
||||||
| bullet | `{ level: number }` | Optional | |
|
| bullet | `{ level: number }` | Optional | |
|
||||||
| numbering | `{ num: Num; level: number; custom?: boolean }` | Optional | |
|
| numbering | `{ num: ConcreteNumbering; level: number; custom?: boolean }` | Optional | |
|
||||||
|
|
||||||
## Text
|
## Text
|
||||||
|
|
||||||
@ -244,10 +244,12 @@ The above example will create a heading with a page break directly under it.
|
|||||||
|
|
||||||
## Page Break
|
## Page Break
|
||||||
|
|
||||||
To move to a new page (insert a page break), simply add `.pageBreak()` on a paragraph:
|
To move to a new page (insert a page break):
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
const paragraph = new docx.Paragraph("Amazing Heading").pageBreak();
|
const paragraph = new docx.Paragraph({
|
||||||
|
children: [new TextRun("Amazing Heading"), new PageBreak()],
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
The above example will create a heading and start a new page immediately afterwards.
|
The above example will create a heading and start a new page immediately afterwards.
|
||||||
|
@ -3,112 +3,147 @@
|
|||||||
## Example
|
## Example
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
const para = new Paragraph("To whom it may concern:").heading2().center();
|
const para = new Paragraph({
|
||||||
|
text: "To whom it may concern:",
|
||||||
|
heading: HeadingLevel.HEADING_2,
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
});
|
||||||
|
|
||||||
const name = new TextRun("Name:")
|
const name = new TextRun({
|
||||||
.bold()
|
text: "Name:",
|
||||||
.font("Calibri")
|
bold: true,
|
||||||
.allCaps();
|
font: "Calibri",
|
||||||
|
allCaps: true,
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Available methods
|
## Available Options
|
||||||
|
|
||||||
* For run formatting:
|
### Run formatting
|
||||||
* `.bold()`, `.italics()`, `.smallCaps()`, `.allCaps()`, `.strike()`, `.doubleStrike()`, `.subScript()`, `.superScript()`: Set the formatting property to true
|
|
||||||
* `.underline(style="single", color=null)`: Set the underline style and color
|
|
||||||
* `.color(color)`: Set the text color, using 6 hex characters for RRGGBB (no leading `#`)
|
|
||||||
* `.size(halfPts)`: Set the font size, measured in half-points
|
|
||||||
* `.font(name)`: Set the run's font
|
|
||||||
* `.style(name)`: Apply a named run style
|
|
||||||
* `.characterSpacing(value)`: Set the character spacing adjustment (in TWIPs)
|
|
||||||
* For paragraph formatting:
|
|
||||||
* `.heading1()`, `.heading2()`, `.heading3()`, `.heading4()`, `.heading5()`, `.title()`: apply the appropriate style to the paragraph
|
|
||||||
* `.left()`, `.center()`, `.right()`, `.justified()`: set the paragraph's alignment
|
|
||||||
* `.thematicBreak()`, `.pageBreak()`: Insert a thick rule or a page break beneath the paragraph
|
|
||||||
* `.leftTabStop(position)`: Add a left tab stop (measured in TWIPs from the left)
|
|
||||||
* `.maxRightTabStop()`: Add a right tab stop at the far right
|
|
||||||
* `.bullet()`: Use the default bullet style
|
|
||||||
* `.setNumbering(numbering, indentLevel)`: Use a custom numbering format for the paragraph
|
|
||||||
* `.style(name)`: Apply a named paragraph style
|
|
||||||
* `.indent(start, hanging=0)`: Set the paragraph's indent level (in TWIPs)
|
|
||||||
* `.spacing({before=0, after=0, line=0})`: Set the line and before/after on the paragraph. Before/after is measured in TWIPs, line is measured in 240ths of a line
|
|
||||||
|
|
||||||
Paragraph styles have all the run formatting methods, except `style()`, and `.left()`, `.center()`, `.right()`, `.justified()`, `.thematicBreak()`, `.leftTabStop(position)`, `.maxRightTabStop()`, `.indent(start, hanging=0)`, and `.spacing({before=0, after=0, line=0})` methods.
|
- `bold`, `italics`, `smallCaps`, `allCaps`, `strike`, `doubleStrike`, `subScript`, `superScript`: Set the formatting property to true
|
||||||
|
- `underline(style="single", color=null)`: Set the underline style and color
|
||||||
|
- `color(color)`: Set the text color, using 6 hex characters for RRGGBB (no leading `#`)
|
||||||
|
- `size(halfPts)`: Set the font size, measured in half-points
|
||||||
|
- `font(name)`: Set the run's font
|
||||||
|
- `style(name)`: Apply a named run style
|
||||||
|
- `characterSpacing(value)`: Set the character spacing adjustment (in TWIPs)
|
||||||
|
|
||||||
|
### Paragraph formatting
|
||||||
|
|
||||||
|
- `heading1`, `heading2`, `heading3`, `heading4`, `heading5`, `title`: apply the appropriate style to the paragraph
|
||||||
|
- `left`, `center`, `right`, `justified`: set the paragraph's alignment
|
||||||
|
- `thematicBreak`, `pageBreak`: Insert a thick rule or a page break beneath the paragraph
|
||||||
|
- `leftTabStop(position)`: Add a left tab stop (measured in TWIPs from the left)
|
||||||
|
- `maxRightTabStop`: Add a right tab stop at the far right
|
||||||
|
- `bullet`: Use the default bullet style
|
||||||
|
- `setNumbering(numbering, indentLevel)`: Use a custom numbering format for the paragraph
|
||||||
|
- `style(name)`: Apply a named paragraph style
|
||||||
|
- `indent(start, hanging=0)`: Set the paragraph's indent level (in TWIPs)
|
||||||
|
- `spacing({before=0, after=0, line=0})`: Set the line and before/after on the paragraph. Before/after is measured in TWIPs, line is measured in 240ths of a line
|
||||||
|
|
||||||
|
Paragraph styles have all the run formatting methods, except `style()`, and `left()`, `center()`, `right()`, `justified()`, `thematicBreak()`, `leftTabStop(position)`, `maxRightTabStop()`, `indent(start, hanging=0)`, and `spacing({before=0, after=0, line=0})` methods.
|
||||||
|
|
||||||
## Detailed guide
|
## Detailed guide
|
||||||
|
|
||||||
There are 4 items in DOCX that can be styled:
|
There are 4 items in `docx` that can be styled:
|
||||||
|
|
||||||
* Characters: Attributes that can change within a paragraph. e.g., bold, italics, etc.
|
- Characters: Attributes that can change within a paragraph. e.g., bold, italics, etc.
|
||||||
* Paragraphs: Attributes like indent, text alignment, line spacing, etc.
|
- Paragraphs: Attributes like indent, text alignment, line spacing, etc.
|
||||||
* Tables: Border styles, table formats, etc.
|
- Tables: Border styles, table formats, etc.
|
||||||
* List items: These are the numbers and bullets that are automatically inserted
|
- List items: These are the numbers and bullets that are automatically inserted
|
||||||
|
|
||||||
There are a few different ways of styling this content in DOCX, which somewhat resemble the HTML/CSS approach. In order of greatest to lowest priority:
|
There are a few different ways of styling this content in `docx`, which somewhat resemble the HTML/CSS approach. In order of greatest to lowest priority:
|
||||||
|
|
||||||
1. Direct formatting (AKA inline formatting)
|
1. Direct formatting (inline formatting)
|
||||||
2. Centrally defined styles (similar to external CSS)
|
2. Declaritive Styles (similar to external CSS)
|
||||||
3. Document defaults (similar to a `*` rule in CSS)
|
3. Document defaults (similar to a `*` rule in CSS)
|
||||||
|
|
||||||
Unlike CSS, less specific rules don't _necessarily_ override parent rules. The rules are a bit wonky, but if you're interested, see the [advanced formatting section](#Advanced formatting).
|
Unlike CSS, less specific rules don't _necessarily_ override parent rules. The rules are a bit wonky, but if you're interested, see the [advanced formatting section](#Advanced formatting).
|
||||||
|
|
||||||
### Direct formatting (AKA inline formatting)
|
### Direct formatting (inline formatting)
|
||||||
|
|
||||||
This is the type of formatting that your uncle uses when he types out documents: _N ... a ... m ... e ... :_ Then he grabs the mouse, highlights _Name:_ and moves over to the **B** for bold. This manner of formatting results in markup that is similar to writing `<span style="bold: true">Name:</span>` if you were typing out HTML. DOCX (the format) allows you to specify this for any of the four types of items. `docx` (the library) only supports this type of formatting for paragraphs and characters, using a _fluent_ api. Thus you could do:
|
This is the type of formatting that your uncle uses when he types out documents: _N ... a ... m ... e ... :_ Then he grabs the mouse, highlights _Name:_ and moves over to the **B** for bold. This manner of formatting results in markup that is similar to writing `<span style="bold: true">Name:</span>` if you were typing out HTML. `docx` (the format) allows you to specify this for any of the four types of items. `docx` (the library) only supports this type of formatting for paragraphs and characters, using a _fluent_ api. Thus you could do:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
const name = new TextRun("Name:")
|
const name = new TextRun({
|
||||||
.bold()
|
text: "Name:",
|
||||||
.font("Calibri")
|
bold: true,
|
||||||
.allCaps();
|
font: "Calibri",
|
||||||
|
allCaps: true,
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Or for paragraph formatting:
|
Or for paragraph formatting:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
const para = new Paragraph("To whom it may concern:").heading2().center();
|
const para = new Paragraph({
|
||||||
|
text: "To whom it may concern:",
|
||||||
|
heading: HeadingLevel.HEADING_2,
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Centrally defined styles (similar to external CSS)
|
### Declaritive Styles (similar to external CSS)
|
||||||
|
|
||||||
DOCX files contain a styles section separate from the main content, much like how HTML includes CSS files. Unlike CSS, DOCX distinguishes between styles meant for tables (which show up in the table formatting toolbar), styles for lists (which show up under bullets and numbering), and styles for runs and paragraphs, which show up as dropdowns offering standard styles, like "Heading 1", "Caption", or any custom styles defined in that document. <!-- TODO: add pictures of the panes -->. `docx` allows you to define these styles using a fluent interface as well.
|
`docx` files contain a styles section separate from the main content, much like how HTML includes CSS files. Unlike CSS, `docx` distinguishes between styles meant for tables (which show up in the table formatting toolbar), styles for lists (which show up under bullets and numbering), and styles for runs and paragraphs, which show up as dropdowns offering standard styles, like "Heading 1", "Caption", or any custom styles defined in that document. <!-- TODO: add pictures of the panes -->. `docx` allows you to define these styles using a fluent interface as well.
|
||||||
|
|
||||||
There are three parts to using custom styles with `docx`:
|
To add styles, define your custom styles in the `document`:
|
||||||
|
|
||||||
1. Create a container object for the style definitions:
|
```ts
|
||||||
```ts
|
// The first argument is an ID you use to apply the style to paragraphs
|
||||||
const myStyles = new docx.Styles();
|
// The second argument is a human-friendly name to show in the UI
|
||||||
```
|
const doc = new Document({
|
||||||
2. Define your custom styles, similar to the way you would format a paragraph or run
|
creator: "Clippy",
|
||||||
|
title: "Sample Document",
|
||||||
```ts
|
description: "A brief example of using docx",
|
||||||
// The first argument is an ID you use to apply the style to paragraphs
|
styles: {
|
||||||
// The second argument is a human-friendly name to show in the UI
|
paragraphStyles: [
|
||||||
myStyles
|
{
|
||||||
.createParagraphStyle("myWonkyStyle", "My Wonky Style")
|
id: "myWonkyStyle",
|
||||||
.basedOn("Normal")
|
name: "My Wonky Style",
|
||||||
.next("Normal")
|
basedOn: "Normal",
|
||||||
.color("999999")
|
next: "Normal",
|
||||||
.italics()
|
quickFormat: true,
|
||||||
.indent(720) // 720 TWIP === 720 / 20 pt === .5 in
|
run: {
|
||||||
.spacing({ line: 276 }); // 276 / 240 = 1.15x line spacing
|
italics: true,
|
||||||
|
color: "999999",
|
||||||
myStyles
|
},
|
||||||
.createParagraphStyle("Heading2", "Heading 2")
|
paragraph: {
|
||||||
.basedOn("Normal")
|
spacing: {
|
||||||
.next("Normal")
|
line: 276,
|
||||||
.quickFormat()
|
},
|
||||||
.size(26) // 26 half-points === 13pt font
|
indent: {
|
||||||
.bold()
|
left: 720,
|
||||||
.underline("double", "FF0000")
|
},
|
||||||
.spacing({ before: 240, after: 120 }); // TWIP for both
|
},
|
||||||
```
|
},
|
||||||
|
{
|
||||||
3. When you generate your document, make sure to pass the `styles` container to the `Packer`:
|
id: "Heading2",
|
||||||
|
name: "Heading 2",
|
||||||
```ts
|
basedOn: "Normal",
|
||||||
Packer.pack(myOutStream);
|
next: "Normal",
|
||||||
```
|
quickFormat: true,
|
||||||
|
run: {
|
||||||
|
size: 26
|
||||||
|
bold: true,
|
||||||
|
color: "999999",
|
||||||
|
{
|
||||||
|
type: UnderlineType.DOUBLE,
|
||||||
|
color: "FF0000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
paragraph: {
|
||||||
|
spacing: {
|
||||||
|
before: 240,
|
||||||
|
after: 120
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
**Note**: If you are using the `.headingX` or `.title` methods of paragraphs, you must make sure to define `HeadingX` or `Title` styles for these. Otherwise they'll show up unstyled :(. If you are using the `.bullet` or `.setNumbering` methods, you need to define a `ListParagraph` style or the numbers may not show up.
|
**Note**: If you are using the `.headingX` or `.title` methods of paragraphs, you must make sure to define `HeadingX` or `Title` styles for these. Otherwise they'll show up unstyled :(. If you are using the `.bullet` or `.setNumbering` methods, you need to define a `ListParagraph` style or the numbers may not show up.
|
||||||
|
|
||||||
@ -144,19 +179,29 @@ To determine the value of a styling property, you must first identify whether it
|
|||||||
|
|
||||||
The following properties are treated in a special manner; they're called toggle properties:
|
The following properties are treated in a special manner; they're called toggle properties:
|
||||||
|
|
||||||
* Bold
|
- Bold
|
||||||
* All caps
|
- All caps
|
||||||
* Small caps
|
- Small caps
|
||||||
* Italics
|
- Italics
|
||||||
* Single strike-through
|
- Single strike-through
|
||||||
* Hidden
|
- Hidden
|
||||||
* Imprint
|
- Imprint
|
||||||
* Emboss
|
- Emboss
|
||||||
* Character outline
|
- Character outline
|
||||||
* Character shadow
|
- Character shadow
|
||||||
|
|
||||||
For these properties, the rules state the following conflict resolution in case the property is specified at multiple points for the same item:
|
For these properties, the rules state the following conflict resolution in case the property is specified at multiple points for the same item:
|
||||||
|
|
||||||
* Direct formatting trumps all if specified (either true or false)
|
- Direct formatting trumps all if specified (either true or false)
|
||||||
* Otherwise, if the property is true in document defaults, the property is set to true
|
- Otherwise, if the property is true in document defaults, the property is set to true
|
||||||
* Otherwise, the property's value is an XOR of its effective table, paragraph, and character values. (So specifying bold `true` on a table style and a paragraph style would result in non-bold text if a paragraph inside the table had that style)
|
- Otherwise, the property's value is an XOR of its effective table, paragraph, and character values. (So specifying bold `true` on a table style and a paragraph style would result in non-bold text if a paragraph inside the table had that style)
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Declaritive styles
|
||||||
|
|
||||||
|
Importing Images from file system path
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/2-declaritive-styles.ts ':include')
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/2-declaritive-styles.ts_
|
||||||
|
53
docs/usage/symbols.md
Normal file
53
docs/usage/symbols.md
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Symbol Runs
|
||||||
|
|
||||||
|
!> SymbolRuns require an understanding of [Paragraphs](paragraph.md).
|
||||||
|
|
||||||
|
You can add multiple `symbol runs` in `Paragraphs` along with [text runs](text.md) using the Paragraph's `children` property.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { Paragraph, TextRun, SymbolRun } from "docx";
|
||||||
|
|
||||||
|
const paragraph = new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun("This is a checkbox: "),
|
||||||
|
new SymbolRun("F071")
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Specifying symbol font
|
||||||
|
|
||||||
|
By default symbol runs will use the `Wingdings` font. To switch fonts, pass an object instead of a string to the `SymbolRun` constructor and specify `char` and `symbolfont` properties:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const symbol = new SymbolRun({
|
||||||
|
char: "F071",
|
||||||
|
symbolfont: "Arial",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example symbols
|
||||||
|
|
||||||
|
Symbols are specified by their hexidecimal code. Ref http://officeopenxml.com/WPtextSpecialContent-symbol.php. Below are some examples.
|
||||||
|
|
||||||
|
- `F071`: empty checkbox
|
||||||
|
- `F043`: thumbs up
|
||||||
|
- `F04A`: smile
|
||||||
|
- `F04C`: frown
|
||||||
|
- `F022`: scissors
|
||||||
|
- `F0F0`: right arrow
|
||||||
|
- `F0FE`: checked box
|
||||||
|
|
||||||
|
## Typographical Emphasis
|
||||||
|
|
||||||
|
Symbol runs can have their display modified just like text runs. For example, they can be bolded and italicized:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const symbol = new SymbolRun({
|
||||||
|
char: "F071",
|
||||||
|
bold: true,
|
||||||
|
italics: true,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [text run](text.md) documentation for more info.
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> Tab stops are useful, if you are unclear of what they are, [here is a link explaining](https://en.wikipedia.org/wiki/Tab_stop). It enables side by side text which is nicely laid out without the need for tables, or constantly pressing space bar.
|
> Tab stops are useful, if you are unclear of what they are, [here is a link explaining](https://en.wikipedia.org/wiki/Tab_stop). It enables side by side text which is nicely laid out without the need for tables, or constantly pressing space bar.
|
||||||
|
|
||||||
!> **Note**: At the moment, the unit of measurement for a tab stop is counter intuitive for a human. It is using OpenXMLs own measuring system. For example, 2268 roughly translates to 3cm. Therefore in the future, I may consider changing it to percentages or even cm.
|
!> **Note**: 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)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@ -11,44 +11,111 @@ Simply call the relevant methods on the paragraph listed below. Then just add a
|
|||||||
## Example
|
## Example
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
const paragraph = new docx.Paragraph().maxRightTabStop();
|
const paragraph = new Paragraph({
|
||||||
const leftText = new docx.TextRun("Hey everyone").bold();
|
children: [new TextRun("Hey everyone").bold(), new TextRun(\t"11th November 1999")],
|
||||||
const rightText = new docx.TextRun("11th November 2015").tab();
|
tabStops: [
|
||||||
paragraph.addRun(leftText);
|
{
|
||||||
paragraph.addRun(rightText);
|
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. YUK!
|
|
||||||
|
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
|
```ts
|
||||||
const paragraph = new docx.Paragraph();
|
const paragraph = new Paragraph({
|
||||||
paragraph.maxRightTabStop();
|
children: [new TextRun("Second tab stop here I come!")],
|
||||||
paragraph.leftTabStop(1000);
|
tabStops: [
|
||||||
const text = new docx.TextRun("Second tab stop here I come!").tab().tab();
|
{
|
||||||
paragraph.addRun(text);
|
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.
|
The above shows the use of two tab stops, and how to select/use it.
|
||||||
|
|
||||||
## Left Tab Stop
|
You can add multiple tab stops of the same `type` too.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
paragraph.leftTabStop(2268);
|
const paragraph = new Paragraph({
|
||||||
|
children: [new TextRun("Multiple 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.
|
2268 is the distance from the left side.
|
||||||
|
|
||||||
## Center Tab Stop
|
## Center Tab Stop
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
paragraph.centerTabStop(2268);
|
const paragraph = new Paragraph({
|
||||||
|
tabStops: [
|
||||||
|
{
|
||||||
|
type: TabStopType.CENTER,
|
||||||
|
position: 2268,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
```
|
```
|
||||||
2268 is the distance from the left side.
|
|
||||||
|
2268 is the distance from the center.
|
||||||
|
|
||||||
## Right Tab Stop
|
## Right Tab Stop
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
paragraph.rightTabStop(2268);
|
const paragraph = new Paragraph({
|
||||||
|
tabStops: [
|
||||||
|
{
|
||||||
|
type: TabStopType.RIGHT,
|
||||||
|
position: 2268,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
```
|
```
|
||||||
2268 is the distance from the left side.
|
|
||||||
|
2268 is the distance fro0oum the left side.
|
||||||
|
|
||||||
## Max Right Tab Stop
|
## Max Right Tab Stop
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
paragraph.maxRightTabStop();
|
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.
|
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.
|
||||||
|
@ -47,30 +47,6 @@ Here is the list of all options that you can use to generate your tables of cont
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
```ts
|
|
||||||
// Let's define the options for generate a TOC for heading 1-5 and MySpectacularStyle,
|
|
||||||
// making the entries be hyperlinks for the paragraph
|
|
||||||
const toc = new TableOfContents("Summary", {
|
|
||||||
hyperlink: true,
|
|
||||||
headingStyleRange: "1-5",
|
|
||||||
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)],
|
|
||||||
});
|
|
||||||
|
|
||||||
doc.addTableOfContents(toc);
|
|
||||||
|
|
||||||
doc.add(new Paragraph("Header #1").heading1().pageBreakBefore());
|
|
||||||
doc.add(new Paragraph("I'm a little text, very nicely written.'"));
|
|
||||||
|
|
||||||
doc.add(new Paragraph("Header #2").heading1().pageBreakBefore());
|
|
||||||
doc.add(new Paragraph("I'm another text very nicely written.'"));
|
|
||||||
doc.add(new Paragraph("Header #2.1").heading2());
|
|
||||||
doc.add(new Paragraph("I'm another text very nicely written.'"));
|
|
||||||
|
|
||||||
doc.add(new Paragraph("My Spectacular Style #1").style("MySpectacularStyle").pageBreakBefore());
|
|
||||||
```
|
|
||||||
|
|
||||||
### Complete example
|
|
||||||
|
|
||||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/28-table-of-contents.ts ':include')
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/28-table-of-contents.ts ':include')
|
||||||
|
|
||||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/28-table-of-contents.ts_
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/28-table-of-contents.ts_
|
||||||
|
@ -328,7 +328,7 @@ const cell = new TableCell({
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/4-basic-table.ts ":include")
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/4-basic-table.ts ':include')
|
||||||
|
|
||||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/4-basic-table.ts_
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/4-basic-table.ts_
|
||||||
|
|
||||||
@ -336,7 +336,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/4-basic-table.ts_
|
|||||||
|
|
||||||
Example showing how to add colourful borders to tables
|
Example showing how to add colourful borders to tables
|
||||||
|
|
||||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/20-table-cell-borders.ts ":include")
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/20-table-cell-borders.ts ':include')
|
||||||
|
|
||||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/20-table-cell-borders.ts_
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/20-table-cell-borders.ts_
|
||||||
|
|
||||||
@ -344,11 +344,11 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/20-table-cell-borders
|
|||||||
|
|
||||||
Example showing how to add images to tables
|
Example showing how to add images to tables
|
||||||
|
|
||||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/24-images-to-table-cell.ts ":include")
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/24-images-to-table-cell.ts ':include')
|
||||||
|
|
||||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/24-images-to-table-cell.ts_
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/24-images-to-table-cell.ts_
|
||||||
|
|
||||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/36-image-to-table-cell.ts ":include")
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/36-image-to-table-cell.ts ':include')
|
||||||
|
|
||||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/36-image-to-table-cell.ts_
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/36-image-to-table-cell.ts_
|
||||||
|
|
||||||
@ -356,7 +356,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/36-image-to-table-cel
|
|||||||
|
|
||||||
Example showing how align text in a table cell
|
Example showing how align text in a table cell
|
||||||
|
|
||||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/31-tables.ts ":include")
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/31-tables.ts ':include')
|
||||||
|
|
||||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/31-tables.ts_
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/31-tables.ts_
|
||||||
|
|
||||||
@ -364,11 +364,11 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/31-tables.ts_
|
|||||||
|
|
||||||
Example showing merging of columns and rows and shading
|
Example showing merging of columns and rows and shading
|
||||||
|
|
||||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/32-merge-table-cells.ts ":include")
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/32-merge-and-shade-table-cells.ts ':include')
|
||||||
|
|
||||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/32-merge-table-cells.ts_
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/32-merge-and-shade-table-cells.ts_
|
||||||
|
|
||||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/41-merge-table-cells-2.ts ":include")
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/41-merge-table-cells-2.ts ':include')
|
||||||
|
|
||||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/41-merge-table-cells-2.ts_
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/41-merge-table-cells-2.ts_
|
||||||
|
|
||||||
@ -376,12 +376,12 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/41-merge-table-cells-
|
|||||||
|
|
||||||
Example showing merging of columns and rows
|
Example showing merging of columns and rows
|
||||||
|
|
||||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/43-images-to-table-cell-2.ts ":include")
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/43-images-to-table-cell-2.ts ':include')
|
||||||
|
|
||||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/43-images-to-table-cell-2.ts_
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/43-images-to-table-cell-2.ts_
|
||||||
|
|
||||||
### Floating tables
|
### Floating tables
|
||||||
|
|
||||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/34-floating-tables.ts ":include")
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/34-floating-tables.ts ':include')
|
||||||
|
|
||||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/34-floating-tables.ts_
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/34-floating-tables.ts_
|
||||||
|
8453
package-lock.json
generated
Normal file
8453
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "docx",
|
"name": "docx",
|
||||||
"version": "5.0.0-rc6",
|
"version": "5.1.0",
|
||||||
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
|
"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": {
|
||||||
"pretest": "rimraf ./build",
|
"pretest": "rimraf ./build",
|
||||||
@ -50,7 +50,9 @@
|
|||||||
"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",
|
||||||
"jszip": "^3.1.5",
|
"jszip": "^3.1.5",
|
||||||
|
"shortid": "^2.2.15",
|
||||||
"xml": "^1.0.1",
|
"xml": "^1.0.1",
|
||||||
"xml-js": "^1.6.8"
|
"xml-js": "^1.6.8"
|
||||||
},
|
},
|
||||||
@ -64,6 +66,7 @@
|
|||||||
"@types/chai": "^3.4.35",
|
"@types/chai": "^3.4.35",
|
||||||
"@types/mocha": "^2.2.39",
|
"@types/mocha": "^2.2.39",
|
||||||
"@types/request-promise": "^4.1.42",
|
"@types/request-promise": "^4.1.42",
|
||||||
|
"@types/shortid": "0.0.29",
|
||||||
"@types/sinon": "^4.3.1",
|
"@types/sinon": "^4.3.1",
|
||||||
"@types/webpack": "^4.4.24",
|
"@types/webpack": "^4.4.24",
|
||||||
"awesome-typescript-loader": "^3.4.1",
|
"awesome-typescript-loader": "^3.4.1",
|
||||||
@ -74,7 +77,7 @@
|
|||||||
"jszip": "^3.1.5",
|
"jszip": "^3.1.5",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
"mocha-webpack": "^1.0.1",
|
"mocha-webpack": "^1.0.1",
|
||||||
"nyc": "^13.1.0",
|
"nyc": "^14.1.1",
|
||||||
"pre-commit": "^1.2.2",
|
"pre-commit": "^1.2.2",
|
||||||
"prettier": "^1.15.2",
|
"prettier": "^1.15.2",
|
||||||
"prompt": "^1.0.0",
|
"prompt": "^1.0.0",
|
||||||
|
@ -28,15 +28,52 @@ describe("Formatter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should format simple paragraph with bold text", () => {
|
it("should format simple paragraph with bold text", () => {
|
||||||
const paragraph = new Paragraph("");
|
const paragraph = new Paragraph({
|
||||||
paragraph.addRun(
|
children: [
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: "test",
|
text: "test",
|
||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
);
|
],
|
||||||
const newJson = formatter.format(paragraph);
|
});
|
||||||
assert.isDefined(newJson["w:p"][1]["w:r"][0]["w:rPr"][0]["w:b"]._attr["w:val"]);
|
|
||||||
|
const tree = formatter.format(paragraph);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [
|
||||||
|
{
|
||||||
|
"w:b": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:bCs": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:t": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"xml:space": "preserve",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"test",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should format attributes (rsidSect)", () => {
|
it("should format attributes (rsidSect)", () => {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { BaseXmlComponent, IXmlableObject } from "file/xml-components";
|
import { BaseXmlComponent, IXmlableObject } from "file/xml-components";
|
||||||
|
import { File } from "../file";
|
||||||
|
|
||||||
export class Formatter {
|
export class Formatter {
|
||||||
public format(input: BaseXmlComponent): IXmlableObject {
|
public format(input: BaseXmlComponent, file?: File): IXmlableObject {
|
||||||
const output = input.prepForXml();
|
const output = input.prepForXml(file);
|
||||||
|
|
||||||
if (output) {
|
if (output) {
|
||||||
return output;
|
return output;
|
||||||
|
@ -5,7 +5,7 @@ export class ImageReplacer {
|
|||||||
let currentXmlData = xmlData;
|
let currentXmlData = xmlData;
|
||||||
|
|
||||||
mediaData.forEach((image, i) => {
|
mediaData.forEach((image, i) => {
|
||||||
currentXmlData = currentXmlData.replace(`{${image.fileName}}`, (offset + i).toString());
|
currentXmlData = currentXmlData.replace(new RegExp(`{${image.fileName}}`, "g"), (offset + i).toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
return currentXmlData;
|
return currentXmlData;
|
||||||
|
@ -4,6 +4,7 @@ import * as xml from "xml";
|
|||||||
import { File } from "file";
|
import { File } from "file";
|
||||||
import { Formatter } from "../formatter";
|
import { Formatter } from "../formatter";
|
||||||
import { ImageReplacer } from "./image-replacer";
|
import { ImageReplacer } from "./image-replacer";
|
||||||
|
import { NumberingReplacer } from "./numbering-replacer";
|
||||||
|
|
||||||
interface IXmlifyedFile {
|
interface IXmlifyedFile {
|
||||||
readonly data: string;
|
readonly data: string;
|
||||||
@ -30,10 +31,12 @@ interface IXmlifyedFileMapping {
|
|||||||
export class Compiler {
|
export class Compiler {
|
||||||
private readonly formatter: Formatter;
|
private readonly formatter: Formatter;
|
||||||
private readonly imageReplacer: ImageReplacer;
|
private readonly imageReplacer: ImageReplacer;
|
||||||
|
private readonly numberingReplacer: NumberingReplacer;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.formatter = new Formatter();
|
this.formatter = new Formatter();
|
||||||
this.imageReplacer = new ImageReplacer();
|
this.imageReplacer = new ImageReplacer();
|
||||||
|
this.numberingReplacer = new NumberingReplacer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public compile(file: File, prettifyXml?: boolean): JSZip {
|
public compile(file: File, prettifyXml?: boolean): JSZip {
|
||||||
@ -68,7 +71,7 @@ export class Compiler {
|
|||||||
file.verifyUpdateFields();
|
file.verifyUpdateFields();
|
||||||
const documentRelationshipCount = file.DocumentRelationships.RelationshipCount + 1;
|
const documentRelationshipCount = file.DocumentRelationships.RelationshipCount + 1;
|
||||||
|
|
||||||
const documentXmlData = xml(this.formatter.format(file.Document), prettify);
|
const documentXmlData = xml(this.formatter.format(file.Document, file), prettify);
|
||||||
const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media);
|
const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -82,24 +85,25 @@ export class Compiler {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return xml(this.formatter.format(file.DocumentRelationships), prettify);
|
return xml(this.formatter.format(file.DocumentRelationships, file), prettify);
|
||||||
})(),
|
})(),
|
||||||
path: "word/_rels/document.xml.rels",
|
path: "word/_rels/document.xml.rels",
|
||||||
},
|
},
|
||||||
Document: {
|
Document: {
|
||||||
data: (() => {
|
data: (() => {
|
||||||
const xmlData = this.imageReplacer.replace(documentXmlData, documentMediaDatas, documentRelationshipCount);
|
const xmlData = this.imageReplacer.replace(documentXmlData, documentMediaDatas, documentRelationshipCount);
|
||||||
|
const referenedXmlData = this.numberingReplacer.replace(xmlData, file.Numbering.ConcreteNumbering);
|
||||||
|
|
||||||
return xmlData;
|
return referenedXmlData;
|
||||||
})(),
|
})(),
|
||||||
path: "word/document.xml",
|
path: "word/document.xml",
|
||||||
},
|
},
|
||||||
Styles: {
|
Styles: {
|
||||||
data: xml(this.formatter.format(file.Styles), prettify),
|
data: xml(this.formatter.format(file.Styles, file), prettify),
|
||||||
path: "word/styles.xml",
|
path: "word/styles.xml",
|
||||||
},
|
},
|
||||||
Properties: {
|
Properties: {
|
||||||
data: xml(this.formatter.format(file.CoreProperties), {
|
data: xml(this.formatter.format(file.CoreProperties, file), {
|
||||||
declaration: {
|
declaration: {
|
||||||
standalone: "yes",
|
standalone: "yes",
|
||||||
encoding: "UTF-8",
|
encoding: "UTF-8",
|
||||||
@ -108,15 +112,15 @@ export class Compiler {
|
|||||||
path: "docProps/core.xml",
|
path: "docProps/core.xml",
|
||||||
},
|
},
|
||||||
Numbering: {
|
Numbering: {
|
||||||
data: xml(this.formatter.format(file.Numbering), prettify),
|
data: xml(this.formatter.format(file.Numbering, file), prettify),
|
||||||
path: "word/numbering.xml",
|
path: "word/numbering.xml",
|
||||||
},
|
},
|
||||||
FileRelationships: {
|
FileRelationships: {
|
||||||
data: xml(this.formatter.format(file.FileRelationships), prettify),
|
data: xml(this.formatter.format(file.FileRelationships, file), prettify),
|
||||||
path: "_rels/.rels",
|
path: "_rels/.rels",
|
||||||
},
|
},
|
||||||
HeaderRelationships: file.Headers.map((headerWrapper, index) => {
|
HeaderRelationships: file.Headers.map((headerWrapper, index) => {
|
||||||
const xmlData = xml(this.formatter.format(headerWrapper.Header), prettify);
|
const xmlData = xml(this.formatter.format(headerWrapper.Header, file), prettify);
|
||||||
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
|
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
|
||||||
|
|
||||||
mediaDatas.forEach((mediaData, i) => {
|
mediaDatas.forEach((mediaData, i) => {
|
||||||
@ -128,12 +132,12 @@ export class Compiler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: xml(this.formatter.format(headerWrapper.Relationships), prettify),
|
data: xml(this.formatter.format(headerWrapper.Relationships, file), prettify),
|
||||||
path: `word/_rels/header${index + 1}.xml.rels`,
|
path: `word/_rels/header${index + 1}.xml.rels`,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
FooterRelationships: file.Footers.map((footerWrapper, index) => {
|
FooterRelationships: file.Footers.map((footerWrapper, index) => {
|
||||||
const xmlData = xml(this.formatter.format(footerWrapper.Footer), prettify);
|
const xmlData = xml(this.formatter.format(footerWrapper.Footer, file), prettify);
|
||||||
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
|
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
|
||||||
|
|
||||||
mediaDatas.forEach((mediaData, i) => {
|
mediaDatas.forEach((mediaData, i) => {
|
||||||
@ -145,12 +149,12 @@ export class Compiler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: xml(this.formatter.format(footerWrapper.Relationships), prettify),
|
data: xml(this.formatter.format(footerWrapper.Relationships, file), prettify),
|
||||||
path: `word/_rels/footer${index + 1}.xml.rels`,
|
path: `word/_rels/footer${index + 1}.xml.rels`,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
Headers: file.Headers.map((headerWrapper, index) => {
|
Headers: file.Headers.map((headerWrapper, index) => {
|
||||||
const tempXmlData = xml(this.formatter.format(headerWrapper.Header), prettify);
|
const tempXmlData = xml(this.formatter.format(headerWrapper.Header, file), prettify);
|
||||||
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
|
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
|
||||||
// TODO: 0 needs to be changed when headers get relationships of their own
|
// TODO: 0 needs to be changed when headers get relationships of their own
|
||||||
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
|
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
|
||||||
@ -161,7 +165,7 @@ export class Compiler {
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
Footers: file.Footers.map((footerWrapper, index) => {
|
Footers: file.Footers.map((footerWrapper, index) => {
|
||||||
const tempXmlData = xml(this.formatter.format(footerWrapper.Footer), prettify);
|
const tempXmlData = xml(this.formatter.format(footerWrapper.Footer, file), prettify);
|
||||||
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
|
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
|
||||||
// TODO: 0 needs to be changed when headers get relationships of their own
|
// TODO: 0 needs to be changed when headers get relationships of their own
|
||||||
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
|
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
|
||||||
@ -172,19 +176,19 @@ export class Compiler {
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
ContentTypes: {
|
ContentTypes: {
|
||||||
data: xml(this.formatter.format(file.ContentTypes), prettify),
|
data: xml(this.formatter.format(file.ContentTypes, file), prettify),
|
||||||
path: "[Content_Types].xml",
|
path: "[Content_Types].xml",
|
||||||
},
|
},
|
||||||
AppProperties: {
|
AppProperties: {
|
||||||
data: xml(this.formatter.format(file.AppProperties), prettify),
|
data: xml(this.formatter.format(file.AppProperties, file), prettify),
|
||||||
path: "docProps/app.xml",
|
path: "docProps/app.xml",
|
||||||
},
|
},
|
||||||
FootNotes: {
|
FootNotes: {
|
||||||
data: xml(this.formatter.format(file.FootNotes), prettify),
|
data: xml(this.formatter.format(file.FootNotes, file), prettify),
|
||||||
path: "word/footnotes.xml",
|
path: "word/footnotes.xml",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
data: xml(this.formatter.format(file.Settings), prettify),
|
data: xml(this.formatter.format(file.Settings, file), prettify),
|
||||||
path: "word/settings.xml",
|
path: "word/settings.xml",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
17
src/export/packer/numbering-replacer.ts
Normal file
17
src/export/packer/numbering-replacer.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { ConcreteNumbering } from "file";
|
||||||
|
|
||||||
|
export class NumberingReplacer {
|
||||||
|
public replace(xmlData: string, concreteNumberings: ConcreteNumbering[]): string {
|
||||||
|
let currentXmlData = xmlData;
|
||||||
|
|
||||||
|
for (const concreteNumbering of concreteNumberings) {
|
||||||
|
if (!concreteNumbering.reference) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentXmlData = currentXmlData.replace(new RegExp(`{${concreteNumbering.reference}}`, "g"), concreteNumbering.id.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentXmlData;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,22 @@
|
|||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
import { DocumentAttributes } from "../document/document-attributes";
|
import { DocumentAttributes } from "../document/document-attributes";
|
||||||
|
import { INumberingOptions } from "../numbering";
|
||||||
|
import { HyperlinkType, Paragraph } from "../paragraph";
|
||||||
|
import { IStylesOptions } from "../styles";
|
||||||
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
|
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
|
||||||
|
|
||||||
|
export interface IInternalHyperlinkDefinition {
|
||||||
|
readonly text: string;
|
||||||
|
readonly type: HyperlinkType.INTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IExternalHyperlinkDefinition {
|
||||||
|
readonly link: string;
|
||||||
|
readonly text: string;
|
||||||
|
readonly type: HyperlinkType.EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IPropertiesOptions {
|
export interface IPropertiesOptions {
|
||||||
readonly title?: string;
|
readonly title?: string;
|
||||||
readonly subject?: string;
|
readonly subject?: string;
|
||||||
@ -11,6 +26,12 @@ export interface IPropertiesOptions {
|
|||||||
readonly lastModifiedBy?: string;
|
readonly lastModifiedBy?: string;
|
||||||
readonly revision?: string;
|
readonly revision?: string;
|
||||||
readonly externalStyles?: string;
|
readonly externalStyles?: string;
|
||||||
|
readonly styles?: IStylesOptions;
|
||||||
|
readonly numbering?: INumberingOptions;
|
||||||
|
readonly footnotes?: Paragraph[];
|
||||||
|
readonly hyperlinks?: {
|
||||||
|
readonly [key: string]: IInternalHyperlinkDefinition | IExternalHyperlinkDefinition;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CoreProperties extends XmlComponent {
|
export class CoreProperties extends XmlComponent {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
||||||
import { TableOfContents } from "../..";
|
import { Paragraph, ParagraphProperties, TableOfContents } from "../..";
|
||||||
|
import { File } from "../../../file";
|
||||||
import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties";
|
import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties";
|
||||||
|
|
||||||
export class Body extends XmlComponent {
|
export class Body extends XmlComponent {
|
||||||
@ -18,15 +19,19 @@ export class Body extends XmlComponent {
|
|||||||
* @param options new section options
|
* @param options new section options
|
||||||
*/
|
*/
|
||||||
public addSection(options: SectionPropertiesOptions): void {
|
public addSection(options: SectionPropertiesOptions): void {
|
||||||
|
const currentSection = this.sections.pop() as SectionProperties;
|
||||||
|
this.root.push(this.createSectionParagraph(currentSection));
|
||||||
|
|
||||||
this.sections.push(new SectionProperties(options));
|
this.sections.push(new SectionProperties(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
public prepForXml(): IXmlableObject | undefined {
|
public prepForXml(file?: File): IXmlableObject | undefined {
|
||||||
if (this.sections.length === 1) {
|
if (this.sections.length === 1) {
|
||||||
|
this.root.splice(0, 1);
|
||||||
this.root.push(this.sections.pop() as SectionProperties);
|
this.root.push(this.sections.pop() as SectionProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.prepForXml();
|
return super.prepForXml(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
public push(component: XmlComponent): void {
|
public push(component: XmlComponent): void {
|
||||||
@ -36,4 +41,12 @@ export class Body extends XmlComponent {
|
|||||||
public getTablesOfContents(): TableOfContents[] {
|
public getTablesOfContents(): TableOfContents[] {
|
||||||
return this.root.filter((child) => child instanceof TableOfContents) as TableOfContents[];
|
return this.root.filter((child) => child instanceof TableOfContents) as TableOfContents[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createSectionParagraph(section: SectionProperties): Paragraph {
|
||||||
|
const paragraph = new Paragraph({});
|
||||||
|
const properties = new ParagraphProperties({});
|
||||||
|
properties.addChildElement(section);
|
||||||
|
paragraph.addChildElement(properties);
|
||||||
|
return paragraph;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,3 +5,4 @@ export * from "./page-size";
|
|||||||
export * from "./page-number";
|
export * from "./page-number";
|
||||||
export * from "./page-border";
|
export * from "./page-border";
|
||||||
export * from "./line-number";
|
export * from "./line-number";
|
||||||
|
export * from "./vertical-align";
|
||||||
|
@ -8,6 +8,7 @@ import { Media } from "file/media";
|
|||||||
import { PageBorderOffsetFrom } from "./page-border";
|
import { PageBorderOffsetFrom } from "./page-border";
|
||||||
import { PageNumberFormat } from "./page-number";
|
import { PageNumberFormat } from "./page-number";
|
||||||
import { SectionProperties } from "./section-properties";
|
import { SectionProperties } from "./section-properties";
|
||||||
|
import { SectionVerticalAlignValue } from "./vertical-align";
|
||||||
|
|
||||||
describe("SectionProperties", () => {
|
describe("SectionProperties", () => {
|
||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
@ -39,6 +40,7 @@ describe("SectionProperties", () => {
|
|||||||
pageNumberStart: 10,
|
pageNumberStart: 10,
|
||||||
pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT,
|
pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT,
|
||||||
titlePage: true,
|
titlePage: true,
|
||||||
|
verticalAlign: SectionVerticalAlignValue.TOP,
|
||||||
});
|
});
|
||||||
const tree = new Formatter().format(properties);
|
const tree = new Formatter().format(properties);
|
||||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||||
|
@ -18,6 +18,7 @@ import { IPageNumberTypeAttributes, PageNumberType } from "./page-number";
|
|||||||
import { PageSize } from "./page-size/page-size";
|
import { PageSize } from "./page-size/page-size";
|
||||||
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
|
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
|
||||||
import { TitlePage } from "./title-page/title-page";
|
import { TitlePage } from "./title-page/title-page";
|
||||||
|
import { ISectionVerticalAlignAttributes, SectionVerticalAlign } from "./vertical-align";
|
||||||
|
|
||||||
export interface IHeaderFooterGroup<T> {
|
export interface IHeaderFooterGroup<T> {
|
||||||
readonly default?: T;
|
readonly default?: T;
|
||||||
@ -45,7 +46,8 @@ export type SectionPropertiesOptions = IPageSizeAttributes &
|
|||||||
IPageNumberTypeAttributes &
|
IPageNumberTypeAttributes &
|
||||||
ILineNumberAttributes &
|
ILineNumberAttributes &
|
||||||
IPageBordersOptions &
|
IPageBordersOptions &
|
||||||
ITitlePageOptions & {
|
ITitlePageOptions &
|
||||||
|
ISectionVerticalAlignAttributes & {
|
||||||
readonly column?: {
|
readonly column?: {
|
||||||
readonly space?: number;
|
readonly space?: number;
|
||||||
readonly count?: number;
|
readonly count?: number;
|
||||||
@ -87,6 +89,7 @@ export class SectionProperties extends XmlComponent {
|
|||||||
pageBorderBottom,
|
pageBorderBottom,
|
||||||
pageBorderLeft,
|
pageBorderLeft,
|
||||||
titlePage = false,
|
titlePage = false,
|
||||||
|
verticalAlign,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
@ -121,6 +124,10 @@ export class SectionProperties extends XmlComponent {
|
|||||||
if (titlePage) {
|
if (titlePage) {
|
||||||
this.root.push(new TitlePage());
|
this.root.push(new TitlePage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (verticalAlign) {
|
||||||
|
this.root.push(new SectionVerticalAlign(verticalAlign));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private addHeaders(headers?: IHeaderFooterGroup<HeaderWrapper>): void {
|
private addHeaders(headers?: IHeaderFooterGroup<HeaderWrapper>): void {
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./vertical-align";
|
||||||
|
export * from "./vertical-align-attributes";
|
@ -0,0 +1,12 @@
|
|||||||
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
import { SectionVerticalAlignValue } from "./vertical-align";
|
||||||
|
|
||||||
|
export interface ISectionVerticalAlignAttributes {
|
||||||
|
readonly verticalAlign?: SectionVerticalAlignValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SectionVerticalAlignAttributes extends XmlAttributeComponent<ISectionVerticalAlignAttributes> {
|
||||||
|
protected readonly xmlKeys = {
|
||||||
|
verticalAlign: "w:val",
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
// http://officeopenxml.com/WPsection.php
|
||||||
|
|
||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { SectionVerticalAlignAttributes } from "./vertical-align-attributes";
|
||||||
|
|
||||||
|
export enum SectionVerticalAlignValue {
|
||||||
|
BOTH = "both",
|
||||||
|
BOTTOM = "bottom",
|
||||||
|
CENTER = "center",
|
||||||
|
TOP = "top",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SectionVerticalAlign extends XmlComponent {
|
||||||
|
constructor(value: SectionVerticalAlignValue) {
|
||||||
|
super("w:vAlign");
|
||||||
|
this.root.push(new SectionVerticalAlignAttributes({ verticalAlign: value }));
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
// http://officeopenxml.com/WPdocument.php
|
// http://officeopenxml.com/WPdocument.php
|
||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
import { Paragraph } from "../paragraph";
|
import { Hyperlink, Paragraph } from "../paragraph";
|
||||||
import { Table } from "../table";
|
import { Table } from "../table";
|
||||||
import { TableOfContents } from "../table-of-contents";
|
import { TableOfContents } from "../table-of-contents";
|
||||||
import { Body } from "./body";
|
import { Body } from "./body";
|
||||||
@ -36,7 +36,7 @@ export class Document extends XmlComponent {
|
|||||||
this.root.push(this.body);
|
this.root.push(this.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public add(item: Paragraph | Table | TableOfContents): Document {
|
public add(item: Paragraph | Table | TableOfContents | Hyperlink): Document {
|
||||||
this.body.push(item);
|
this.body.push(item);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { Formatter } from "export/formatter";
|
|||||||
|
|
||||||
import { File } from "./file";
|
import { File } from "./file";
|
||||||
import { Footer, Header } from "./header";
|
import { Footer, Header } from "./header";
|
||||||
import { Paragraph } from "./paragraph";
|
import { HyperlinkRef, Paragraph } from "./paragraph";
|
||||||
import { Table, TableCell, TableRow } from "./table";
|
import { Table, TableCell, TableRow } from "./table";
|
||||||
import { TableOfContents } from "./table-of-contents";
|
import { TableOfContents } from "./table-of-contents";
|
||||||
|
|
||||||
@ -89,6 +89,91 @@ describe("File", () => {
|
|||||||
expect(tree["w:body"][0]["w:sectPr"][8]["w:footerReference"]._attr["w:type"]).to.equal("first");
|
expect(tree["w:body"][0]["w:sectPr"][8]["w:footerReference"]._attr["w:type"]).to.equal("first");
|
||||||
expect(tree["w:body"][0]["w:sectPr"][9]["w:footerReference"]._attr["w:type"]).to.equal("even");
|
expect(tree["w:body"][0]["w:sectPr"][9]["w:footerReference"]._attr["w:type"]).to.equal("even");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should add child", () => {
|
||||||
|
const doc = new File(undefined, undefined, [
|
||||||
|
{
|
||||||
|
children: [new Paragraph("test")],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const tree = new Formatter().format(doc.Document.Body);
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:body": [
|
||||||
|
{
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:t": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"xml:space": "preserve",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"test",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:sectPr": [
|
||||||
|
{
|
||||||
|
"w:pgSz": {
|
||||||
|
_attr: {
|
||||||
|
"w:h": 16838,
|
||||||
|
"w:orient": "portrait",
|
||||||
|
"w:w": 11906,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:pgMar": {
|
||||||
|
_attr: {
|
||||||
|
"w:bottom": 1440,
|
||||||
|
"w:footer": 708,
|
||||||
|
"w:gutter": 0,
|
||||||
|
"w:header": 708,
|
||||||
|
"w:left": 1440,
|
||||||
|
"w:mirrorMargins": false,
|
||||||
|
"w:right": 1440,
|
||||||
|
"w:top": 1440,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:cols": {
|
||||||
|
_attr: {
|
||||||
|
"w:num": 1,
|
||||||
|
"w:space": 708,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:docGrid": {
|
||||||
|
_attr: {
|
||||||
|
"w:linePitch": 360,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add hyperlink child", () => {
|
||||||
|
const doc = new File(undefined, undefined, [
|
||||||
|
{
|
||||||
|
children: [new HyperlinkRef("test")],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(doc.HyperlinkCache).to.deep.equal({});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#addSection", () => {
|
describe("#addSection", () => {
|
||||||
@ -102,6 +187,16 @@ describe("File", () => {
|
|||||||
expect(spy.called).to.equal(true);
|
expect(spy.called).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should add hyperlink child", () => {
|
||||||
|
const doc = new File();
|
||||||
|
|
||||||
|
doc.addSection({
|
||||||
|
children: [new HyperlinkRef("test")],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(doc.HyperlinkCache).to.deep.equal({});
|
||||||
|
});
|
||||||
|
|
||||||
it("should call the underlying document's add when adding a Table", () => {
|
it("should call the underlying document's add when adding a Table", () => {
|
||||||
const file = new File();
|
const file = new File();
|
||||||
const spy = sinon.spy(file.Document, "add");
|
const spy = sinon.spy(file.Document, "add");
|
||||||
@ -148,13 +243,196 @@ describe("File", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#createFootnote", () => {
|
describe("#HyperlinkCache", () => {
|
||||||
it("should call the underlying document's createFootnote", () => {
|
it("should initially have empty hyperlink cache", () => {
|
||||||
const wrapper = new File();
|
const file = new File();
|
||||||
const spy = sinon.spy(wrapper.FootNotes, "createFootNote");
|
|
||||||
wrapper.createFootnote(new Paragraph(""));
|
|
||||||
|
|
||||||
expect(spy.called).to.equal(true);
|
expect(file.HyperlinkCache).to.deep.equal({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#createFootnote", () => {
|
||||||
|
it("should create footnote", () => {
|
||||||
|
const wrapper = new File({
|
||||||
|
footnotes: [new Paragraph("hello")],
|
||||||
|
});
|
||||||
|
|
||||||
|
const tree = new Formatter().format(wrapper.FootNotes);
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:footnotes": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"mc:Ignorable": "w14 w15 wp14",
|
||||||
|
"xmlns:m": "http://schemas.openxmlformats.org/officeDocument/2006/math",
|
||||||
|
"xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
||||||
|
"xmlns:o": "urn:schemas-microsoft-com:office:office",
|
||||||
|
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||||
|
"xmlns:v": "urn:schemas-microsoft-com:vml",
|
||||||
|
"xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||||
|
"xmlns:w10": "urn:schemas-microsoft-com:office:word",
|
||||||
|
"xmlns:w14": "http://schemas.microsoft.com/office/word/2010/wordml",
|
||||||
|
"xmlns:w15": "http://schemas.microsoft.com/office/word/2012/wordml",
|
||||||
|
"xmlns:wne": "http://schemas.microsoft.com/office/word/2006/wordml",
|
||||||
|
"xmlns:wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||||
|
"xmlns:wp14": "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
|
||||||
|
"xmlns:wpc": "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
||||||
|
"xmlns:wpg": "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
|
||||||
|
"xmlns:wpi": "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
|
||||||
|
"xmlns:wps": "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:footnote": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:id": -1,
|
||||||
|
"w:type": "separator",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:pPr": [
|
||||||
|
{
|
||||||
|
"w:spacing": {
|
||||||
|
_attr: {
|
||||||
|
"w:after": 0,
|
||||||
|
"w:line": 240,
|
||||||
|
"w:lineRule": "auto",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [
|
||||||
|
{
|
||||||
|
"w:rStyle": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": "FootnoteReference",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:footnoteRef": {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:separator": {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:footnote": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:id": 0,
|
||||||
|
"w:type": "continuationSeparator",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:pPr": [
|
||||||
|
{
|
||||||
|
"w:spacing": {
|
||||||
|
_attr: {
|
||||||
|
"w:after": 0,
|
||||||
|
"w:line": 240,
|
||||||
|
"w:lineRule": "auto",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [
|
||||||
|
{
|
||||||
|
"w:rStyle": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": "FootnoteReference",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:footnoteRef": {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:continuationSeparator": {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:footnote": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:id": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [
|
||||||
|
{
|
||||||
|
"w:rStyle": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": "FootnoteReference",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:footnoteRef": {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:t": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"xml:space": "preserve",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"hello",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
113
src/file/file.ts
113
src/file/file.ts
@ -1,3 +1,4 @@
|
|||||||
|
import * as shortid from "shortid";
|
||||||
import { AppProperties } from "./app-properties/app-properties";
|
import { AppProperties } from "./app-properties/app-properties";
|
||||||
import { ContentTypes } from "./content-types/content-types";
|
import { ContentTypes } from "./content-types/content-types";
|
||||||
import { CoreProperties, IPropertiesOptions } from "./core-properties";
|
import { CoreProperties, IPropertiesOptions } from "./core-properties";
|
||||||
@ -16,7 +17,7 @@ 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 { Bookmark, Hyperlink, Paragraph } from "./paragraph";
|
import { Hyperlink, HyperlinkRef, HyperlinkType, Paragraph } from "./paragraph";
|
||||||
import { Relationships } from "./relationships";
|
import { Relationships } from "./relationships";
|
||||||
import { TargetModeType } from "./relationships/relationship/relationship";
|
import { TargetModeType } from "./relationships/relationship/relationship";
|
||||||
import { Settings } from "./settings";
|
import { Settings } from "./settings";
|
||||||
@ -40,14 +41,12 @@ export interface ISectionOptions {
|
|||||||
readonly size?: IPageSizeAttributes;
|
readonly size?: IPageSizeAttributes;
|
||||||
readonly margins?: IPageMarginAttributes;
|
readonly margins?: IPageMarginAttributes;
|
||||||
readonly properties?: SectionPropertiesOptions;
|
readonly properties?: SectionPropertiesOptions;
|
||||||
readonly children: Array<Paragraph | Table | TableOfContents>;
|
readonly children: Array<Paragraph | Table | TableOfContents | HyperlinkRef>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class File {
|
export class File {
|
||||||
// tslint:disable-next-line:readonly-keyword
|
// tslint:disable-next-line:readonly-keyword
|
||||||
private currentRelationshipId: number = 1;
|
private currentRelationshipId: number = 1;
|
||||||
// tslint:disable-next-line:readonly-keyword
|
|
||||||
private styles: Styles;
|
|
||||||
|
|
||||||
private readonly document: Document;
|
private readonly document: Document;
|
||||||
private readonly headers: IDocumentHeader[] = [];
|
private readonly headers: IDocumentHeader[] = [];
|
||||||
@ -61,6 +60,8 @@ export class File {
|
|||||||
private readonly settings: Settings;
|
private readonly settings: Settings;
|
||||||
private readonly contentTypes: ContentTypes;
|
private readonly contentTypes: ContentTypes;
|
||||||
private readonly appProperties: AppProperties;
|
private readonly appProperties: AppProperties;
|
||||||
|
private readonly styles: Styles;
|
||||||
|
private readonly hyperlinkCache: { readonly [key: string]: Hyperlink } = {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
options: IPropertiesOptions = {
|
options: IPropertiesOptions = {
|
||||||
@ -72,7 +73,13 @@ export class File {
|
|||||||
sections: ISectionOptions[] = [],
|
sections: ISectionOptions[] = [],
|
||||||
) {
|
) {
|
||||||
this.coreProperties = new CoreProperties(options);
|
this.coreProperties = new CoreProperties(options);
|
||||||
this.numbering = new Numbering();
|
this.numbering = new Numbering(
|
||||||
|
options.numbering
|
||||||
|
? options.numbering
|
||||||
|
: {
|
||||||
|
config: [],
|
||||||
|
},
|
||||||
|
);
|
||||||
this.docRelationships = new Relationships();
|
this.docRelationships = new Relationships();
|
||||||
this.fileRelationships = new Relationships();
|
this.fileRelationships = new Relationships();
|
||||||
this.appProperties = new AppProperties();
|
this.appProperties = new AppProperties();
|
||||||
@ -97,9 +104,16 @@ export class File {
|
|||||||
} else if (options.externalStyles) {
|
} 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) {
|
||||||
|
const stylesFactory = new DefaultStylesFactory();
|
||||||
|
const defaultStyles = stylesFactory.newInstance();
|
||||||
|
this.styles = new Styles({
|
||||||
|
...defaultStyles,
|
||||||
|
...options.styles,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const stylesFactory = new DefaultStylesFactory();
|
const stylesFactory = new DefaultStylesFactory();
|
||||||
this.styles = stylesFactory.newInstance();
|
this.styles = new Styles(stylesFactory.newInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addDefaultRelationships();
|
this.addDefaultRelationships();
|
||||||
@ -120,33 +134,42 @@ export class File {
|
|||||||
this.document.Body.addSection(section.properties ? section.properties : {});
|
this.document.Body.addSection(section.properties ? section.properties : {});
|
||||||
|
|
||||||
for (const child of section.children) {
|
for (const child of section.children) {
|
||||||
|
if (child instanceof HyperlinkRef) {
|
||||||
|
const hyperlink = this.hyperlinkCache[child.id];
|
||||||
|
this.document.add(hyperlink);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
this.document.add(child);
|
this.document.add(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public createHyperlink(link: string, text?: string): Hyperlink {
|
if (options.footnotes) {
|
||||||
const newText = text === undefined ? link : text;
|
for (const paragraph of options.footnotes) {
|
||||||
const hyperlink = new Hyperlink(newText, this.docRelationships.RelationshipCount);
|
this.footNotes.createFootNote(paragraph);
|
||||||
this.docRelationships.createRelationship(
|
}
|
||||||
hyperlink.linkId,
|
}
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
|
||||||
link,
|
|
||||||
TargetModeType.EXTERNAL,
|
|
||||||
);
|
|
||||||
return hyperlink;
|
|
||||||
}
|
|
||||||
|
|
||||||
public createInternalHyperLink(anchor: string, text?: string): Hyperlink {
|
if (options.hyperlinks) {
|
||||||
const newText = text === undefined ? anchor : text;
|
const cache = {};
|
||||||
const hyperlink = new Hyperlink(newText, this.docRelationships.RelationshipCount, anchor);
|
|
||||||
// NOTE: unlike File#createHyperlink(), since the link is to an internal bookmark
|
|
||||||
// we don't need to create a new relationship.
|
|
||||||
return hyperlink;
|
|
||||||
}
|
|
||||||
|
|
||||||
public createBookmark(name: string, text: string = name): Bookmark {
|
for (const key in options.hyperlinks) {
|
||||||
return new Bookmark(name, text, this.docRelationships.RelationshipCount);
|
if (!options.hyperlinks[key]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hyperlinkRef = options.hyperlinks[key];
|
||||||
|
|
||||||
|
const hyperlink =
|
||||||
|
hyperlinkRef.type === HyperlinkType.EXTERNAL
|
||||||
|
? this.createHyperlink(hyperlinkRef.link, hyperlinkRef.text)
|
||||||
|
: this.createInternalHyperLink(key, hyperlinkRef.text);
|
||||||
|
|
||||||
|
cache[key] = hyperlink;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hyperlinkCache = cache;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public addSection({
|
public addSection({
|
||||||
@ -174,20 +197,40 @@ export class File {
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
|
if (child instanceof HyperlinkRef) {
|
||||||
|
const hyperlink = this.hyperlinkCache[child.id];
|
||||||
|
this.document.add(hyperlink);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
this.document.add(child);
|
this.document.add(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public createFootnote(paragraph: Paragraph): void {
|
|
||||||
this.footNotes.createFootNote(paragraph);
|
|
||||||
}
|
|
||||||
|
|
||||||
public verifyUpdateFields(): void {
|
public verifyUpdateFields(): void {
|
||||||
if (this.document.getTablesOfContents().length) {
|
if (this.document.getTablesOfContents().length) {
|
||||||
this.settings.addUpdateFields();
|
this.settings.addUpdateFields();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createHyperlink(link: string, text: string = link): Hyperlink {
|
||||||
|
const hyperlink = new Hyperlink(text, shortid.generate().toLowerCase());
|
||||||
|
this.docRelationships.createRelationship(
|
||||||
|
hyperlink.linkId,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
||||||
|
link,
|
||||||
|
TargetModeType.EXTERNAL,
|
||||||
|
);
|
||||||
|
return hyperlink;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createInternalHyperLink(anchor: string, text: string = anchor): Hyperlink {
|
||||||
|
const hyperlink = new Hyperlink(text, shortid.generate().toLowerCase(), anchor);
|
||||||
|
// NOTE: unlike File#createHyperlink(), since the link is to an internal bookmark
|
||||||
|
// we don't need to create a new relationship.
|
||||||
|
return hyperlink;
|
||||||
|
}
|
||||||
|
|
||||||
private createHeader(header: Header): HeaderWrapper {
|
private createHeader(header: Header): HeaderWrapper {
|
||||||
const wrapper = new HeaderWrapper(this.media, this.currentRelationshipId++);
|
const wrapper = new HeaderWrapper(this.media, this.currentRelationshipId++);
|
||||||
|
|
||||||
@ -277,10 +320,6 @@ export class File {
|
|||||||
return this.styles;
|
return this.styles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set Styles(styles: Styles) {
|
|
||||||
this.styles = styles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get CoreProperties(): CoreProperties {
|
public get CoreProperties(): CoreProperties {
|
||||||
return this.coreProperties;
|
return this.coreProperties;
|
||||||
}
|
}
|
||||||
@ -324,4 +363,8 @@ export class File {
|
|||||||
public get Settings(): Settings {
|
public get Settings(): Settings {
|
||||||
return this.settings;
|
return this.settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get HyperlinkCache(): { readonly [key: string]: Hyperlink } {
|
||||||
|
return this.hyperlinkCache;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
1
src/file/footnotes/footnote/index.ts
Normal file
1
src/file/footnotes/footnote/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./run";
|
1
src/file/footnotes/footnote/run/index.ts
Normal file
1
src/file/footnotes/footnote/run/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./reference-run";
|
@ -44,7 +44,8 @@ export class FootNotes extends XmlComponent {
|
|||||||
line: 240,
|
line: 240,
|
||||||
lineRule: "auto",
|
lineRule: "auto",
|
||||||
},
|
},
|
||||||
}).addRun(new SeperatorRun()),
|
children: [new SeperatorRun()],
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
this.root.push(begin);
|
this.root.push(begin);
|
||||||
|
|
||||||
@ -56,7 +57,8 @@ export class FootNotes extends XmlComponent {
|
|||||||
line: 240,
|
line: 240,
|
||||||
lineRule: "auto",
|
lineRule: "auto",
|
||||||
},
|
},
|
||||||
}).addRun(new ContinuationSeperatorRun()),
|
children: [new ContinuationSeperatorRun()],
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
this.root.push(spacing);
|
this.root.push(spacing);
|
||||||
}
|
}
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from "./footnotes";
|
export * from "./footnotes";
|
||||||
|
export * from "./footnote";
|
||||||
|
@ -12,3 +12,4 @@ export * from "./xml-components";
|
|||||||
export * from "./header-wrapper";
|
export * from "./header-wrapper";
|
||||||
export * from "./footer-wrapper";
|
export * from "./footer-wrapper";
|
||||||
export * from "./header";
|
export * from "./header";
|
||||||
|
export * from "./footnotes";
|
||||||
|
605
src/file/numbering/abstract-numbering.spec.ts
Normal file
605
src/file/numbering/abstract-numbering.spec.ts
Normal file
@ -0,0 +1,605 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "export/formatter";
|
||||||
|
import { EMPTY_OBJECT } from "file/xml-components";
|
||||||
|
|
||||||
|
import { AlignmentType, TabStopPosition } from "../paragraph";
|
||||||
|
import { UnderlineType } from "../paragraph/run/underline";
|
||||||
|
import { ShadingType } from "../table";
|
||||||
|
import { AbstractNumbering } from "./abstract-numbering";
|
||||||
|
|
||||||
|
describe("AbstractNumbering", () => {
|
||||||
|
it("stores its ID at its .id property", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(5, []);
|
||||||
|
expect(abstractNumbering.id).to.equal(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#createLevel", () => {
|
||||||
|
it("creates a level with the given characteristics", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 3,
|
||||||
|
format: "lowerLetter",
|
||||||
|
text: "%1)",
|
||||||
|
alignment: AlignmentType.END,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } });
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } });
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "end" } } });
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": "lowerLetter" } } });
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses 'start' as the default alignment", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 3,
|
||||||
|
format: "lowerLetter",
|
||||||
|
text: "%1)",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } });
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } });
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "start" } } });
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": "lowerLetter" } } });
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("formatting methods: paragraph properties", () => {
|
||||||
|
it("#indent", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 720 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:ind": { _attr: { "w:left": 720 } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#spacing", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
spacing: { before: 50, after: 150 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:spacing": { _attr: { "w:before": 50, "w:after": 150 } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#center", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:jc": { _attr: { "w:val": "center" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#left", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:jc": { _attr: { "w:val": "left" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#right", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
alignment: AlignmentType.RIGHT,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:jc": { _attr: { "w:val": "right" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#justified", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
alignment: AlignmentType.JUSTIFIED,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:jc": { _attr: { "w:val": "both" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#thematicBreak", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
thematicBreak: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [
|
||||||
|
{
|
||||||
|
"w:pBdr": [
|
||||||
|
{
|
||||||
|
"w:bottom": {
|
||||||
|
_attr: {
|
||||||
|
"w:color": "auto",
|
||||||
|
"w:space": 1,
|
||||||
|
"w:val": "single",
|
||||||
|
"w:sz": 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#leftTabStop", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
leftTabStop: 1200,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [
|
||||||
|
{
|
||||||
|
"w:tabs": [{ "w:tab": { _attr: { "w:val": "left", "w:pos": 1200 } } }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#maxRightTabStop", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
rightTabStop: TabStopPosition.MAX,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [
|
||||||
|
{
|
||||||
|
"w:tabs": [{ "w:tab": { _attr: { "w:val": "right", "w:pos": 9026 } } }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#keepLines", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
keepLines: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:keepLines": EMPTY_OBJECT }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#keepNext", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
keepNext: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:pPr": [{ "w:keepNext": EMPTY_OBJECT }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("formatting methods: run properties", () => {
|
||||||
|
it("#size", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
size: 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#smallCaps", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
smallCaps: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:smallCaps": { _attr: { "w:val": true } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#allCaps", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
allCaps: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:caps": { _attr: { "w:val": true } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#strike", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
strike: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:strike": { _attr: { "w:val": true } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#doubleStrike", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
doubleStrike: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:dstrike": { _attr: { "w:val": true } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#subScript", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
subScript: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "subscript" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#superScript", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
superScript: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "superscript" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#font", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
font: "Times",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [
|
||||||
|
{ "w:rFonts": { _attr: { "w:ascii": "Times", "w:cs": "Times", "w:eastAsia": "Times", "w:hAnsi": "Times" } } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#bold", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
bold: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:b": { _attr: { "w:val": true } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#italics", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
italics: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:i": { _attr: { "w:val": true } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#highlight", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
highlight: "005599",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#shadow", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
shadow: {
|
||||||
|
type: ShadingType.PERCENT_10,
|
||||||
|
fill: "00FFFF",
|
||||||
|
color: "FF0000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#underline", () => {
|
||||||
|
it("should set underline to 'single' if no arguments are given", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
underline: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:u": { _attr: { "w:val": "single" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set the style if given", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
underline: {
|
||||||
|
type: UnderlineType.DOUBLE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:u": { _attr: { "w:val": "double" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set the style and color if given", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
underline: {
|
||||||
|
type: UnderlineType.DOUBLE,
|
||||||
|
color: "005599",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:u": { _attr: { "w:val": "double", "w:color": "005599" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#color", () => {
|
||||||
|
const abstractNumbering = new AbstractNumbering(1, [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: "lowerRoman",
|
||||||
|
text: "%0.",
|
||||||
|
style: {
|
||||||
|
run: {
|
||||||
|
color: "123456",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const tree = new Formatter().format(abstractNumbering);
|
||||||
|
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||||
|
"w:rPr": [{ "w:color": { _attr: { "w:val": "123456" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,5 +1,6 @@
|
|||||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
import { Level } from "./level";
|
|
||||||
|
import { ILevelsOptions, Level } from "./level";
|
||||||
import { MultiLevelType } from "./multi-level-type";
|
import { MultiLevelType } from "./multi-level-type";
|
||||||
|
|
||||||
interface IAbstractNumberingAttributesProperties {
|
interface IAbstractNumberingAttributesProperties {
|
||||||
@ -17,7 +18,7 @@ class AbstractNumberingAttributes extends XmlAttributeComponent<IAbstractNumberi
|
|||||||
export class AbstractNumbering extends XmlComponent {
|
export class AbstractNumbering extends XmlComponent {
|
||||||
public readonly id: number;
|
public readonly id: number;
|
||||||
|
|
||||||
constructor(id: number) {
|
constructor(id: number, levelOptions: ILevelsOptions[]) {
|
||||||
super("w:abstractNum");
|
super("w:abstractNum");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new AbstractNumberingAttributes({
|
new AbstractNumberingAttributes({
|
||||||
@ -27,15 +28,9 @@ export class AbstractNumbering extends XmlComponent {
|
|||||||
);
|
);
|
||||||
this.root.push(new MultiLevelType("hybridMultilevel"));
|
this.root.push(new MultiLevelType("hybridMultilevel"));
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
|
||||||
|
|
||||||
public addLevel(level: Level): void {
|
for (const option of levelOptions) {
|
||||||
this.root.push(level);
|
this.root.push(new Level(option));
|
||||||
}
|
}
|
||||||
|
|
||||||
public createLevel(num: number, format: string, text: string, align: string = "start"): Level {
|
|
||||||
const level = new Level(num, format, text, align);
|
|
||||||
this.addLevel(level);
|
|
||||||
return level;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
79
src/file/numbering/concrete-numbering.spec.ts
Normal file
79
src/file/numbering/concrete-numbering.spec.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "export/formatter";
|
||||||
|
|
||||||
|
import { LevelForOverride } from "./level";
|
||||||
|
import { ConcreteNumbering } from "./num";
|
||||||
|
|
||||||
|
describe("ConcreteNumbering", () => {
|
||||||
|
describe("#overrideLevel", () => {
|
||||||
|
let concreteNumbering: ConcreteNumbering;
|
||||||
|
beforeEach(() => {
|
||||||
|
concreteNumbering = new ConcreteNumbering(0, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets a new override level for the given level number", () => {
|
||||||
|
concreteNumbering.overrideLevel(3);
|
||||||
|
const tree = new Formatter().format(concreteNumbering);
|
||||||
|
expect(tree["w:num"]).to.include({
|
||||||
|
"w:lvlOverride": [
|
||||||
|
{ _attr: { "w:ilvl": 3 } },
|
||||||
|
{
|
||||||
|
"w:lvl": [
|
||||||
|
{ _attr: { "w:ilvl": 3, "w15:tentative": 1 } },
|
||||||
|
{ "w:start": { _attr: { "w:val": 1 } } },
|
||||||
|
{ "w:lvlJc": { _attr: { "w:val": "start" } } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets the startOverride element if start is given", () => {
|
||||||
|
concreteNumbering.overrideLevel(1, 9);
|
||||||
|
const tree = new Formatter().format(concreteNumbering);
|
||||||
|
expect(tree["w:num"]).to.include({
|
||||||
|
"w:lvlOverride": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:ilvl": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:startOverride": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": 9,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:lvl": [
|
||||||
|
{ _attr: { "w:ilvl": 1, "w15:tentative": 1 } },
|
||||||
|
{ "w:start": { _attr: { "w:val": 1 } } },
|
||||||
|
{ "w:lvlJc": { _attr: { "w:val": "start" } } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets the lvl element if overrideLevel.Level is accessed", () => {
|
||||||
|
const ol = concreteNumbering.overrideLevel(1);
|
||||||
|
expect(ol.Level).to.be.instanceof(LevelForOverride);
|
||||||
|
const tree = new Formatter().format(concreteNumbering);
|
||||||
|
|
||||||
|
expect(tree["w:num"]).to.include({
|
||||||
|
"w:lvlOverride": [
|
||||||
|
{ _attr: { "w:ilvl": 1 } },
|
||||||
|
{
|
||||||
|
"w:lvl": [
|
||||||
|
{ _attr: { "w:ilvl": 1, "w15:tentative": 1 } },
|
||||||
|
{ "w:start": { _attr: { "w:val": 1 } } },
|
||||||
|
{ "w:lvlJc": { _attr: { "w:val": "start" } } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -2,19 +2,18 @@ import { Attributes, XmlAttributeComponent, XmlComponent } from "file/xml-compon
|
|||||||
import {
|
import {
|
||||||
Alignment,
|
Alignment,
|
||||||
AlignmentType,
|
AlignmentType,
|
||||||
IIndentAttributesProperties,
|
|
||||||
Indent,
|
Indent,
|
||||||
ISpacingProperties,
|
|
||||||
KeepLines,
|
KeepLines,
|
||||||
KeepNext,
|
KeepNext,
|
||||||
LeftTabStop,
|
|
||||||
MaxRightTabStop,
|
|
||||||
Spacing,
|
Spacing,
|
||||||
|
TabStop,
|
||||||
|
TabStopType,
|
||||||
ThematicBreak,
|
ThematicBreak,
|
||||||
} from "../paragraph/formatting";
|
} from "../paragraph/formatting";
|
||||||
import { ParagraphProperties } from "../paragraph/properties";
|
import { ParagraphProperties } from "../paragraph/properties";
|
||||||
import * as formatting from "../paragraph/run/formatting";
|
import * as formatting from "../paragraph/run/formatting";
|
||||||
import { RunProperties } from "../paragraph/run/properties";
|
import { RunProperties } from "../paragraph/run/properties";
|
||||||
|
import { IParagraphStyleOptions2, IRunStyleOptions } from "../styles/style-options";
|
||||||
|
|
||||||
interface ILevelAttributesProperties {
|
interface ILevelAttributesProperties {
|
||||||
readonly ilvl?: number;
|
readonly ilvl?: number;
|
||||||
@ -62,7 +61,7 @@ class LevelText extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LevelJc extends XmlComponent {
|
class LevelJc extends XmlComponent {
|
||||||
constructor(value: string) {
|
constructor(value: AlignmentType) {
|
||||||
super("w:lvlJc");
|
super("w:lvlJc");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new Attributes({
|
new Attributes({
|
||||||
@ -78,6 +77,19 @@ export enum LevelSuffix {
|
|||||||
TAB = "tab",
|
TAB = "tab",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ILevelsOptions {
|
||||||
|
readonly level: number;
|
||||||
|
readonly format?: string;
|
||||||
|
readonly text?: string;
|
||||||
|
readonly alignment?: AlignmentType;
|
||||||
|
readonly start?: number;
|
||||||
|
readonly suffix?: LevelSuffix;
|
||||||
|
readonly style?: {
|
||||||
|
readonly run?: IRunStyleOptions;
|
||||||
|
readonly paragraph?: IParagraphStyleOptions2;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class Suffix extends XmlComponent {
|
class Suffix extends XmlComponent {
|
||||||
constructor(value: LevelSuffix) {
|
constructor(value: LevelSuffix) {
|
||||||
super("w:suff");
|
super("w:suff");
|
||||||
@ -93,7 +105,7 @@ export class LevelBase extends XmlComponent {
|
|||||||
private readonly paragraphProperties: ParagraphProperties;
|
private readonly paragraphProperties: ParagraphProperties;
|
||||||
private readonly runProperties: RunProperties;
|
private readonly runProperties: RunProperties;
|
||||||
|
|
||||||
constructor(level: number, start?: number, numberFormat?: string, levelText?: string, lvlJc?: string) {
|
constructor({ level, format, text, alignment = AlignmentType.START, start = 1, style, suffix }: ILevelsOptions) {
|
||||||
super("w:lvl");
|
super("w:lvl");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new LevelAttributes({
|
new LevelAttributes({
|
||||||
@ -102,17 +114,15 @@ export class LevelBase extends XmlComponent {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (start !== undefined) {
|
this.root.push(new Start(start));
|
||||||
this.root.push(new Start(start));
|
this.root.push(new LevelJc(alignment));
|
||||||
|
|
||||||
|
if (format) {
|
||||||
|
this.root.push(new NumberFormat(format));
|
||||||
}
|
}
|
||||||
if (numberFormat !== undefined) {
|
|
||||||
this.root.push(new NumberFormat(numberFormat));
|
if (text) {
|
||||||
}
|
this.root.push(new LevelText(text));
|
||||||
if (levelText !== undefined) {
|
|
||||||
this.root.push(new LevelText(levelText));
|
|
||||||
}
|
|
||||||
if (lvlJc !== undefined) {
|
|
||||||
this.root.push(new LevelJc(lvlJc));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.paragraphProperties = new ParagraphProperties({});
|
this.paragraphProperties = new ParagraphProperties({});
|
||||||
@ -120,157 +130,112 @@ export class LevelBase extends XmlComponent {
|
|||||||
|
|
||||||
this.root.push(this.paragraphProperties);
|
this.root.push(this.paragraphProperties);
|
||||||
this.root.push(this.runProperties);
|
this.root.push(this.runProperties);
|
||||||
}
|
|
||||||
|
|
||||||
public setSuffix(value: LevelSuffix): LevelBase {
|
if (suffix) {
|
||||||
this.root.push(new Suffix(value));
|
this.root.push(new Suffix(suffix));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public addParagraphProperty(property: XmlComponent): Level {
|
if (style) {
|
||||||
this.paragraphProperties.push(property);
|
if (style.run) {
|
||||||
return this;
|
if (style.run.size) {
|
||||||
}
|
this.runProperties.push(new formatting.Size(style.run.size));
|
||||||
|
}
|
||||||
|
|
||||||
public addRunProperty(property: XmlComponent): Level {
|
if (style.run.bold) {
|
||||||
this.runProperties.push(property);
|
this.runProperties.push(new formatting.Bold());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- Run formatting ---------------------- //
|
if (style.run.italics) {
|
||||||
|
this.runProperties.push(new formatting.Italics());
|
||||||
|
}
|
||||||
|
|
||||||
public size(twips: number): Level {
|
if (style.run.smallCaps) {
|
||||||
this.addRunProperty(new formatting.Size(twips));
|
this.runProperties.push(new formatting.SmallCaps());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public bold(): Level {
|
if (style.run.allCaps) {
|
||||||
this.addRunProperty(new formatting.Bold());
|
this.runProperties.push(new formatting.Caps());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public italics(): Level {
|
if (style.run.strike) {
|
||||||
this.addRunProperty(new formatting.Italics());
|
this.runProperties.push(new formatting.Strike());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public smallCaps(): Level {
|
if (style.run.doubleStrike) {
|
||||||
this.addRunProperty(new formatting.SmallCaps());
|
this.runProperties.push(new formatting.DoubleStrike());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public allCaps(): Level {
|
if (style.run.subScript) {
|
||||||
this.addRunProperty(new formatting.Caps());
|
this.runProperties.push(new formatting.SubScript());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public strike(): Level {
|
if (style.run.superScript) {
|
||||||
this.addRunProperty(new formatting.Strike());
|
this.runProperties.push(new formatting.SuperScript());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public doubleStrike(): Level {
|
if (style.run.underline) {
|
||||||
this.addRunProperty(new formatting.DoubleStrike());
|
this.runProperties.push(new formatting.Underline(style.run.underline.type, style.run.underline.color));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public subScript(): Level {
|
if (style.run.color) {
|
||||||
this.addRunProperty(new formatting.SubScript());
|
this.runProperties.push(new formatting.Color(style.run.color));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public superScript(): Level {
|
if (style.run.font) {
|
||||||
this.addRunProperty(new formatting.SuperScript());
|
this.runProperties.push(new formatting.RunFonts(style.run.font));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public underline(underlineType?: string, color?: string): Level {
|
if (style.run.highlight) {
|
||||||
this.addRunProperty(new formatting.Underline(underlineType, color));
|
this.runProperties.push(new formatting.Highlight(style.run.highlight));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public color(color: string): Level {
|
if (style.run.shadow) {
|
||||||
this.addRunProperty(new formatting.Color(color));
|
this.runProperties.push(new formatting.Shading(style.run.shadow.type, style.run.shadow.fill, style.run.shadow.color));
|
||||||
return this;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public font(fontName: string): Level {
|
if (style.paragraph) {
|
||||||
this.addRunProperty(new formatting.RunFonts(fontName));
|
if (style.paragraph.alignment) {
|
||||||
return this;
|
this.paragraphProperties.push(new Alignment(style.paragraph.alignment));
|
||||||
}
|
}
|
||||||
|
|
||||||
public highlight(color: string): Level {
|
if (style.paragraph.thematicBreak) {
|
||||||
this.addRunProperty(new formatting.Highlight(color));
|
this.paragraphProperties.push(new ThematicBreak());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public shadow(value: string, fill: string, color: string): Level {
|
if (style.paragraph.rightTabStop) {
|
||||||
this.addRunProperty(new formatting.Shading(value, fill, color));
|
this.paragraphProperties.push(new TabStop(TabStopType.RIGHT, style.paragraph.rightTabStop));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
// --------------------- Paragraph formatting ------------------------ //
|
|
||||||
|
|
||||||
public center(): Level {
|
if (style.paragraph.leftTabStop) {
|
||||||
this.addParagraphProperty(new Alignment(AlignmentType.CENTER));
|
this.paragraphProperties.push(new TabStop(TabStopType.LEFT, style.paragraph.leftTabStop));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public left(): Level {
|
if (style.paragraph.indent) {
|
||||||
this.addParagraphProperty(new Alignment(AlignmentType.LEFT));
|
this.paragraphProperties.push(new Indent(style.paragraph.indent));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public right(): Level {
|
if (style.paragraph.spacing) {
|
||||||
this.addParagraphProperty(new Alignment(AlignmentType.RIGHT));
|
this.paragraphProperties.push(new Spacing(style.paragraph.spacing));
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public justified(): Level {
|
if (style.paragraph.keepNext) {
|
||||||
this.addParagraphProperty(new Alignment(AlignmentType.BOTH));
|
this.paragraphProperties.push(new KeepNext());
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public thematicBreak(): Level {
|
if (style.paragraph.keepLines) {
|
||||||
this.addParagraphProperty(new ThematicBreak());
|
this.paragraphProperties.push(new KeepLines());
|
||||||
return this;
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public maxRightTabStop(): Level {
|
|
||||||
this.addParagraphProperty(new MaxRightTabStop());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public leftTabStop(position: number): Level {
|
|
||||||
this.addParagraphProperty(new LeftTabStop(position));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public indent(attrs: IIndentAttributesProperties): Level {
|
|
||||||
this.addParagraphProperty(new Indent(attrs));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public spacing(params: ISpacingProperties): Level {
|
|
||||||
this.addParagraphProperty(new Spacing(params));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public keepNext(): Level {
|
|
||||||
this.addParagraphProperty(new KeepNext());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public keepLines(): Level {
|
|
||||||
this.addParagraphProperty(new KeepLines());
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Level extends LevelBase {
|
export class Level extends LevelBase {
|
||||||
// This is the level that sits under abstractNum. We make a
|
// This is the level that sits under abstractNum. We make a
|
||||||
// handful of properties required
|
// handful of properties required
|
||||||
constructor(level: number, numberFormat: string, levelText: string, lvlJc: string) {
|
constructor(options: ILevelsOptions) {
|
||||||
super(level, 1, numberFormat, levelText, lvlJc);
|
super(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,10 +20,10 @@ class NumAttributes extends XmlAttributeComponent<INumAttributesProperties> {
|
|||||||
protected readonly xmlKeys = { numId: "w:numId" };
|
protected readonly xmlKeys = { numId: "w:numId" };
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Num extends XmlComponent {
|
export class ConcreteNumbering extends XmlComponent {
|
||||||
public readonly id: number;
|
public readonly id: number;
|
||||||
|
|
||||||
constructor(numId: number, abstractNumId: number) {
|
constructor(numId: number, abstractNumId: number, public readonly reference?: string) {
|
||||||
super("w:num");
|
super("w:num");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new NumAttributes({
|
new NumAttributes({
|
||||||
@ -55,7 +55,9 @@ export class LevelOverride extends XmlComponent {
|
|||||||
this.root.push(new StartOverride(start));
|
this.root.push(new StartOverride(start));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lvl = new LevelForOverride(this.levelNum);
|
this.lvl = new LevelForOverride({
|
||||||
|
level: this.levelNum,
|
||||||
|
});
|
||||||
this.root.push(this.lvl);
|
this.root.push(this.lvl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,22 +2,15 @@ import { expect } from "chai";
|
|||||||
|
|
||||||
import { Formatter } from "export/formatter";
|
import { Formatter } from "export/formatter";
|
||||||
|
|
||||||
import { AbstractNumbering } from "./abstract-numbering";
|
|
||||||
import { LevelForOverride } from "./level";
|
|
||||||
import { Num } from "./num";
|
|
||||||
import { Numbering } from "./numbering";
|
import { Numbering } from "./numbering";
|
||||||
|
|
||||||
import { EMPTY_OBJECT } from "file/xml-components";
|
|
||||||
|
|
||||||
describe("Numbering", () => {
|
describe("Numbering", () => {
|
||||||
let numbering: Numbering;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
numbering = new Numbering();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#constructor", () => {
|
describe("#constructor", () => {
|
||||||
it("creates a default numbering with one abstract and one concrete instance", () => {
|
it("creates a default numbering with one abstract and one concrete instance", () => {
|
||||||
|
const numbering = new Numbering({
|
||||||
|
config: [],
|
||||||
|
});
|
||||||
|
|
||||||
const tree = new Formatter().format(numbering);
|
const tree = new Formatter().format(numbering);
|
||||||
expect(Object.keys(tree)).to.deep.equal(["w:numbering"]);
|
expect(Object.keys(tree)).to.deep.equal(["w:numbering"]);
|
||||||
const abstractNums = tree["w:numbering"].filter((el) => el["w:abstractNum"]);
|
const abstractNums = tree["w:numbering"].filter((el) => el["w:abstractNum"]);
|
||||||
@ -46,418 +39,4 @@ describe("Numbering", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#createAbstractNumbering", () => {
|
|
||||||
it("returns a new AbstractNumbering instance", () => {
|
|
||||||
const a2 = numbering.createAbstractNumbering();
|
|
||||||
expect(a2).to.be.instanceof(AbstractNumbering);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("assigns a unique ID to each abstract numbering it creates", () => {
|
|
||||||
const a2 = numbering.createAbstractNumbering();
|
|
||||||
const a3 = numbering.createAbstractNumbering();
|
|
||||||
expect(a2.id).not.to.equal(a3.id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#createConcreteNumbering", () => {
|
|
||||||
it("returns a new Num instance with its abstract ID set to the AbstractNumbering's ID", () => {
|
|
||||||
const a2 = numbering.createAbstractNumbering();
|
|
||||||
const n = numbering.createConcreteNumbering(a2);
|
|
||||||
expect(n).to.be.instanceof(Num);
|
|
||||||
const tree = new Formatter().format(numbering);
|
|
||||||
const serializedN = tree["w:numbering"].find((obj) => obj["w:num"] && obj["w:num"][0]._attr["w:numId"] === n.id);
|
|
||||||
expect(serializedN["w:num"][1]["w:abstractNumId"]._attr["w:val"]).to.equal(a2.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("assigns a unique ID to each concrete numbering it creates", () => {
|
|
||||||
const a2 = numbering.createAbstractNumbering();
|
|
||||||
const n = numbering.createConcreteNumbering(a2);
|
|
||||||
const n2 = numbering.createConcreteNumbering(a2);
|
|
||||||
expect(n.id).not.to.equal(n2.id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("AbstractNumbering", () => {
|
|
||||||
it("stores its ID at its .id property", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(5);
|
|
||||||
expect(abstractNumbering.id).to.equal(5);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#createLevel", () => {
|
|
||||||
it("creates a level with the given characteristics", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(3, "lowerLetter", "%1)", "end");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } });
|
|
||||||
expect(tree["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } });
|
|
||||||
expect(tree["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "end" } } });
|
|
||||||
expect(tree["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": "lowerLetter" } } });
|
|
||||||
expect(tree["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("uses 'start' as the default alignment", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(3, "lowerLetter", "%1)");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } });
|
|
||||||
expect(tree["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } });
|
|
||||||
expect(tree["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "start" } } });
|
|
||||||
expect(tree["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": "lowerLetter" } } });
|
|
||||||
expect(tree["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } });
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("formatting methods: paragraph properties", () => {
|
|
||||||
it("#indent", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerLetter", "%0.").indent({ left: 720 });
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:ind": { _attr: { "w:left": 720 } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#spacing", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerLetter", "%0.").spacing({ before: 50, after: 150 });
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:spacing": { _attr: { "w:before": 50, "w:after": 150 } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#center", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerLetter", "%0.").center();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "center" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#left", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.", "left").left();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "left" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#right", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").right();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "right" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#justified", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").justified();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "both" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#thematicBreak", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").thematicBreak();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [
|
|
||||||
{
|
|
||||||
"w:pBdr": [
|
|
||||||
{
|
|
||||||
"w:bottom": {
|
|
||||||
_attr: {
|
|
||||||
"w:color": "auto",
|
|
||||||
"w:space": 1,
|
|
||||||
"w:val": "single",
|
|
||||||
"w:sz": 6,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#leftTabStop", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").leftTabStop(1200);
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [
|
|
||||||
{
|
|
||||||
"w:tabs": [{ "w:tab": { _attr: { "w:val": "left", "w:pos": 1200 } } }],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#maxRightTabStop", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").maxRightTabStop();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [
|
|
||||||
{
|
|
||||||
"w:tabs": [{ "w:tab": { _attr: { "w:val": "right", "w:pos": 9026 } } }],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#keepLines", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").keepLines();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:keepLines": EMPTY_OBJECT }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#keepNext", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").keepNext();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:pPr": [{ "w:keepNext": EMPTY_OBJECT }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("formatting methods: run properties", () => {
|
|
||||||
it("#size", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").size(24);
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#smallCaps", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").smallCaps();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:smallCaps": { _attr: { "w:val": true } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#allCaps", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").allCaps();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:caps": { _attr: { "w:val": true } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#strike", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").strike();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:strike": { _attr: { "w:val": true } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#doubleStrike", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").doubleStrike();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:dstrike": { _attr: { "w:val": true } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#subScript", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").subScript();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "subscript" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#superScript", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").superScript();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "superscript" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#font", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").font("Times");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [
|
|
||||||
{ "w:rFonts": { _attr: { "w:ascii": "Times", "w:cs": "Times", "w:eastAsia": "Times", "w:hAnsi": "Times" } } },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#bold", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").bold();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:b": { _attr: { "w:val": true } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#italics", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").italics();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:i": { _attr: { "w:val": true } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#highlight", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").highlight("005599");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#shadow", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").shadow("pct10", "00FFFF", "FF0000");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#underline", () => {
|
|
||||||
it("should set underline to 'single' if no arguments are given", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline();
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:u": { _attr: { "w:val": "single" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set the style if given", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline("double");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:u": { _attr: { "w:val": "double" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set the style and color if given", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline("double", "005599");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:u": { _attr: { "w:val": "double", "w:color": "005599" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#color", () => {
|
|
||||||
const abstractNumbering = new AbstractNumbering(1);
|
|
||||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").color("123456");
|
|
||||||
const tree = new Formatter().format(level);
|
|
||||||
expect(tree["w:lvl"]).to.include({
|
|
||||||
"w:rPr": [{ "w:color": { _attr: { "w:val": "123456" } } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("concrete numbering", () => {
|
|
||||||
describe("#overrideLevel", () => {
|
|
||||||
let numbering;
|
|
||||||
let abstractNumbering;
|
|
||||||
let concreteNumbering;
|
|
||||||
beforeEach(() => {
|
|
||||||
numbering = new Numbering();
|
|
||||||
abstractNumbering = numbering.createAbstractNumbering();
|
|
||||||
concreteNumbering = numbering.createConcreteNumbering(abstractNumbering);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets a new override level for the given level number", () => {
|
|
||||||
concreteNumbering.overrideLevel(3);
|
|
||||||
const tree = new Formatter().format(concreteNumbering);
|
|
||||||
expect(tree["w:num"]).to.include({
|
|
||||||
"w:lvlOverride": [
|
|
||||||
{
|
|
||||||
_attr: {
|
|
||||||
"w:ilvl": 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"w:lvl": {
|
|
||||||
_attr: {
|
|
||||||
"w:ilvl": 3,
|
|
||||||
"w15:tentative": 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets the startOverride element if start is given", () => {
|
|
||||||
concreteNumbering.overrideLevel(1, 9);
|
|
||||||
const tree = new Formatter().format(concreteNumbering);
|
|
||||||
expect(tree["w:num"]).to.include({
|
|
||||||
"w:lvlOverride": [
|
|
||||||
{
|
|
||||||
_attr: {
|
|
||||||
"w:ilvl": 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"w:startOverride": {
|
|
||||||
_attr: {
|
|
||||||
"w:val": 9,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"w:lvl": {
|
|
||||||
_attr: {
|
|
||||||
"w:ilvl": 1,
|
|
||||||
"w15:tentative": 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets the lvl element if overrideLevel.Level is accessed", () => {
|
|
||||||
const ol = concreteNumbering.overrideLevel(1);
|
|
||||||
expect(ol.Level).to.be.instanceof(LevelForOverride);
|
|
||||||
const tree = new Formatter().format(concreteNumbering);
|
|
||||||
expect(tree["w:num"]).to.include({
|
|
||||||
"w:lvlOverride": [
|
|
||||||
{ _attr: { "w:ilvl": 1 } },
|
|
||||||
{
|
|
||||||
"w:lvl": { _attr: { "w15:tentative": 1, "w:ilvl": 1 } },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,17 +1,27 @@
|
|||||||
import { Indent } from "file/paragraph";
|
// http://officeopenxml.com/WPnumbering.php
|
||||||
|
import { AlignmentType } from "file/paragraph";
|
||||||
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
import { DocumentAttributes } from "../document/document-attributes";
|
import { DocumentAttributes } from "../document/document-attributes";
|
||||||
import { AbstractNumbering } from "./abstract-numbering";
|
import { AbstractNumbering } from "./abstract-numbering";
|
||||||
import { Num } from "./num";
|
import { ILevelsOptions } from "./level";
|
||||||
|
import { ConcreteNumbering } from "./num";
|
||||||
|
|
||||||
|
export interface INumberingOptions {
|
||||||
|
readonly config: Array<{
|
||||||
|
readonly levels: ILevelsOptions[];
|
||||||
|
readonly reference: string;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
export class Numbering extends XmlComponent {
|
export class Numbering extends XmlComponent {
|
||||||
// tslint:disable-next-line:readonly-keyword
|
// tslint:disable-next-line:readonly-keyword
|
||||||
private nextId: number;
|
private nextId: number;
|
||||||
|
|
||||||
private readonly abstractNumbering: XmlComponent[] = [];
|
private readonly abstractNumbering: AbstractNumbering[] = [];
|
||||||
private readonly concreteNumbering: XmlComponent[] = [];
|
private readonly concreteNumbering: ConcreteNumbering[] = [];
|
||||||
|
|
||||||
constructor() {
|
constructor(options: INumberingOptions) {
|
||||||
super("w:numbering");
|
super("w:numbering");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new DocumentAttributes({
|
new DocumentAttributes({
|
||||||
@ -37,39 +47,125 @@ export class Numbering extends XmlComponent {
|
|||||||
|
|
||||||
this.nextId = 0;
|
this.nextId = 0;
|
||||||
|
|
||||||
const abstractNumbering = this.createAbstractNumbering();
|
const abstractNumbering = this.createAbstractNumbering([
|
||||||
|
{
|
||||||
abstractNumbering.createLevel(0, "bullet", "\u25CF", "left").addParagraphProperty(new Indent({ left: 720, hanging: 360 }));
|
level: 0,
|
||||||
|
format: "bullet",
|
||||||
abstractNumbering.createLevel(1, "bullet", "\u25CB", "left").addParagraphProperty(new Indent({ left: 1440, hanging: 360 }));
|
text: "\u25CF",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
abstractNumbering.createLevel(2, "bullet", "\u25A0", "left").addParagraphProperty(new Indent({ left: 2160, hanging: 360 }));
|
style: {
|
||||||
|
paragraph: {
|
||||||
abstractNumbering.createLevel(3, "bullet", "\u25CF", "left").addParagraphProperty(new Indent({ left: 2880, hanging: 360 }));
|
indent: { left: 720, hanging: 360 },
|
||||||
|
},
|
||||||
abstractNumbering.createLevel(4, "bullet", "\u25CB", "left").addParagraphProperty(new Indent({ left: 3600, hanging: 360 }));
|
},
|
||||||
|
},
|
||||||
abstractNumbering.createLevel(5, "bullet", "\u25A0", "left").addParagraphProperty(new Indent({ left: 4320, hanging: 360 }));
|
{
|
||||||
|
level: 1,
|
||||||
abstractNumbering.createLevel(6, "bullet", "\u25CF", "left").addParagraphProperty(new Indent({ left: 5040, hanging: 360 }));
|
format: "bullet",
|
||||||
|
text: "\u25CB",
|
||||||
abstractNumbering.createLevel(7, "bullet", "\u25CB", "left").addParagraphProperty(new Indent({ left: 5760, hanging: 360 }));
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
abstractNumbering.createLevel(8, "bullet", "\u25A0", "left").addParagraphProperty(new Indent({ left: 6480, hanging: 360 }));
|
paragraph: {
|
||||||
|
indent: { left: 1440, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 2,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25A0",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 2160, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 3,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25CF",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 2880, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 4,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25CB",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 3600, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 5,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25A0",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 4320, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 6,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25CF",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 5040, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 7,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25CF",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 5760, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 8,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u25CF",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 6480, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: 9,
|
||||||
|
format: "bullet",
|
||||||
|
text: "\u2726",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: 720, hanging: 360 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
this.createConcreteNumbering(abstractNumbering);
|
this.createConcreteNumbering(abstractNumbering);
|
||||||
}
|
|
||||||
|
|
||||||
public createAbstractNumbering(): AbstractNumbering {
|
for (const con of options.config) {
|
||||||
const num = new AbstractNumbering(this.nextId++);
|
const currentAbstractNumbering = this.createAbstractNumbering(con.levels);
|
||||||
this.abstractNumbering.push(num);
|
this.createConcreteNumbering(currentAbstractNumbering, con.reference);
|
||||||
return num;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public createConcreteNumbering(abstractNumbering: AbstractNumbering): Num {
|
|
||||||
const num = new Num(this.nextId++, abstractNumbering.id);
|
|
||||||
this.concreteNumbering.push(num);
|
|
||||||
return num;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public prepForXml(): IXmlableObject | undefined {
|
public prepForXml(): IXmlableObject | undefined {
|
||||||
@ -77,4 +173,20 @@ export class Numbering extends XmlComponent {
|
|||||||
this.concreteNumbering.forEach((x) => this.root.push(x));
|
this.concreteNumbering.forEach((x) => this.root.push(x));
|
||||||
return super.prepForXml();
|
return super.prepForXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createConcreteNumbering(abstractNumbering: AbstractNumbering, reference?: string): ConcreteNumbering {
|
||||||
|
const num = new ConcreteNumbering(this.nextId++, abstractNumbering.id, reference);
|
||||||
|
this.concreteNumbering.push(num);
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createAbstractNumbering(options: ILevelsOptions[]): AbstractNumbering {
|
||||||
|
const num = new AbstractNumbering(this.nextId++, options);
|
||||||
|
this.abstractNumbering.push(num);
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get ConcreteNumbering(): ConcreteNumbering[] {
|
||||||
|
return this.concreteNumbering;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// http://officeopenxml.com/WPalignment.php
|
// http://officeopenxml.com/WPalignment.php
|
||||||
|
// http://officeopenxml.com/WPtableAlignment.php
|
||||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
export enum AlignmentType {
|
export enum AlignmentType {
|
||||||
|
@ -7,6 +7,7 @@ export interface IIndentAttributesProperties {
|
|||||||
readonly firstLine?: number;
|
readonly firstLine?: number;
|
||||||
readonly start?: number;
|
readonly start?: number;
|
||||||
readonly end?: number;
|
readonly end?: number;
|
||||||
|
readonly right?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
class IndentAttributes extends XmlAttributeComponent<IIndentAttributesProperties> {
|
class IndentAttributes extends XmlAttributeComponent<IIndentAttributesProperties> {
|
||||||
@ -16,6 +17,7 @@ class IndentAttributes extends XmlAttributeComponent<IIndentAttributesProperties
|
|||||||
firstLine: "w:firstLine",
|
firstLine: "w:firstLine",
|
||||||
start: "w:start",
|
start: "w:start",
|
||||||
end: "w:end",
|
end: "w:end",
|
||||||
|
right: "w:end", // alias
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,8 @@ export enum HeadingLevel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Style extends XmlComponent {
|
export class Style extends XmlComponent {
|
||||||
public readonly styleId: string;
|
|
||||||
|
|
||||||
constructor(styleId: string) {
|
constructor(styleId: string) {
|
||||||
super("w:pStyle");
|
super("w:pStyle");
|
||||||
this.styleId = styleId;
|
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new Attributes({
|
new Attributes({
|
||||||
val: styleId,
|
val: styleId,
|
||||||
|
@ -2,13 +2,13 @@ import { assert } from "chai";
|
|||||||
|
|
||||||
import { Utility } from "tests/utility";
|
import { Utility } from "tests/utility";
|
||||||
|
|
||||||
import { LeaderType, LeftTabStop, MaxRightTabStop, RightTabStop } from "./tab-stop";
|
import { LeaderType, TabStop, TabStopType } from "./tab-stop";
|
||||||
|
|
||||||
describe("LeftTabStop", () => {
|
describe("LeftTabStop", () => {
|
||||||
let tabStop: LeftTabStop;
|
let tabStop: TabStop;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
tabStop = new LeftTabStop(100);
|
tabStop = new TabStop(TabStopType.LEFT, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
@ -29,10 +29,10 @@ describe("LeftTabStop", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("RightTabStop", () => {
|
describe("RightTabStop", () => {
|
||||||
let tabStop: RightTabStop;
|
let tabStop: TabStop;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
tabStop = new RightTabStop(100, LeaderType.DOT);
|
tabStop = new TabStop(TabStopType.RIGHT, 100, LeaderType.DOT);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
@ -52,28 +52,3 @@ describe("RightTabStop", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("MaxRightTabStop", () => {
|
|
||||||
let tabStop: MaxRightTabStop;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
tabStop = new MaxRightTabStop();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#constructor()", () => {
|
|
||||||
it("should create a Tab Stop with correct attributes", () => {
|
|
||||||
const newJson = Utility.jsonify(tabStop);
|
|
||||||
|
|
||||||
const attributes = {
|
|
||||||
val: "right",
|
|
||||||
pos: 9026,
|
|
||||||
};
|
|
||||||
assert.equal(JSON.stringify(newJson.root[0].root[0].root), JSON.stringify(attributes));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should create a Tab Stop with w:tab", () => {
|
|
||||||
const newJson = Utility.jsonify(tabStop);
|
|
||||||
assert.equal(newJson.root[0].rootKey, "w:tab");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
export class TabStop extends XmlComponent {
|
export class TabStop extends XmlComponent {
|
||||||
constructor(tab: TabStopItem) {
|
constructor(type: TabStopType, position: number, leader?: LeaderType) {
|
||||||
super("w:tabs");
|
super("w:tabs");
|
||||||
this.root.push(tab);
|
this.root.push(new TabStopItem(type, position, leader));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TabValue {
|
export enum TabStopType {
|
||||||
LEFT = "left",
|
LEFT = "left",
|
||||||
RIGHT = "right",
|
RIGHT = "right",
|
||||||
CENTER = "center",
|
CENTER = "center",
|
||||||
@ -28,8 +28,12 @@ export enum LeaderType {
|
|||||||
UNDERSCORE = "underscore",
|
UNDERSCORE = "underscore",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum TabStopPosition {
|
||||||
|
MAX = 9026,
|
||||||
|
}
|
||||||
|
|
||||||
export class TabAttributes extends XmlAttributeComponent<{
|
export class TabAttributes extends XmlAttributeComponent<{
|
||||||
readonly val: TabValue;
|
readonly val: TabStopType;
|
||||||
readonly pos: string | number;
|
readonly pos: string | number;
|
||||||
readonly leader?: LeaderType;
|
readonly leader?: LeaderType;
|
||||||
}> {
|
}> {
|
||||||
@ -37,7 +41,7 @@ export class TabAttributes extends XmlAttributeComponent<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TabStopItem extends XmlComponent {
|
export class TabStopItem extends XmlComponent {
|
||||||
constructor(value: TabValue, position: string | number, leader?: LeaderType) {
|
constructor(value: TabStopType, position: string | number, leader?: LeaderType) {
|
||||||
super("w:tab");
|
super("w:tab");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new TabAttributes({
|
new TabAttributes({
|
||||||
@ -48,27 +52,3 @@ export class TabStopItem extends XmlComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MaxRightTabStop extends TabStop {
|
|
||||||
constructor(leader?: LeaderType) {
|
|
||||||
super(new TabStopItem(TabValue.RIGHT, 9026, leader));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class LeftTabStop extends TabStop {
|
|
||||||
constructor(position: number, leader?: LeaderType) {
|
|
||||||
super(new TabStopItem(TabValue.LEFT, position, leader));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RightTabStop extends TabStop {
|
|
||||||
constructor(position: number, leader?: LeaderType) {
|
|
||||||
super(new TabStopItem(TabValue.RIGHT, position, leader));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CenterTabStop extends TabStop {
|
|
||||||
constructor(position: number, leader?: LeaderType) {
|
|
||||||
super(new TabStopItem(TabValue.CENTER, position, leader));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Attributes, XmlComponent } from "file/xml-components";
|
import { Attributes, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
export class NumberProperties extends XmlComponent {
|
export class NumberProperties extends XmlComponent {
|
||||||
constructor(numberId: number, indentLevel: number) {
|
constructor(numberId: number | string, indentLevel: number) {
|
||||||
super("w:numPr");
|
super("w:numPr");
|
||||||
this.root.push(new IndentLevel(indentLevel));
|
this.root.push(new IndentLevel(indentLevel));
|
||||||
this.root.push(new NumberId(numberId));
|
this.root.push(new NumberId(numberId));
|
||||||
@ -20,11 +20,11 @@ class IndentLevel extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NumberId extends XmlComponent {
|
class NumberId extends XmlComponent {
|
||||||
constructor(id: number) {
|
constructor(id: number | string) {
|
||||||
super("w:numId");
|
super("w:numId");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new Attributes({
|
new Attributes({
|
||||||
val: id,
|
val: typeof id === "string" ? `{${id}}` : id,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { assert } from "chai";
|
import { assert, expect } from "chai";
|
||||||
|
|
||||||
import { Utility } from "tests/utility";
|
import { Utility } from "tests/utility";
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ describe("Bookmark", () => {
|
|||||||
let bookmark: Bookmark;
|
let bookmark: Bookmark;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
bookmark = new Bookmark("anchor", "Internal Link", 0);
|
bookmark = new Bookmark("anchor", "Internal Link");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create a bookmark with three root elements", () => {
|
it("should create a bookmark with three root elements", () => {
|
||||||
@ -21,11 +21,8 @@ describe("Bookmark", () => {
|
|||||||
|
|
||||||
it("should create a bookmark with the correct attributes on the bookmark start element", () => {
|
it("should create a bookmark with the correct attributes on the bookmark start element", () => {
|
||||||
const newJson = Utility.jsonify(bookmark);
|
const newJson = Utility.jsonify(bookmark);
|
||||||
const attributes = {
|
|
||||||
name: "anchor",
|
assert.equal(newJson.start.root[0].root.name, "anchor");
|
||||||
id: "1",
|
|
||||||
};
|
|
||||||
assert.equal(JSON.stringify(newJson.start.root[0].root), JSON.stringify(attributes));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create a bookmark with the correct attributes on the text element", () => {
|
it("should create a bookmark with the correct attributes on the text element", () => {
|
||||||
@ -35,9 +32,6 @@ describe("Bookmark", () => {
|
|||||||
|
|
||||||
it("should create a bookmark with the correct attributes on the bookmark end element", () => {
|
it("should create a bookmark with the correct attributes on the bookmark end element", () => {
|
||||||
const newJson = Utility.jsonify(bookmark);
|
const newJson = Utility.jsonify(bookmark);
|
||||||
const attributes = {
|
expect(newJson.end.root[0].root.id).to.be.a("string");
|
||||||
id: "1",
|
|
||||||
};
|
|
||||||
assert.equal(JSON.stringify(newJson.end.root[0].root), JSON.stringify(attributes));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,49 +1,41 @@
|
|||||||
// http://officeopenxml.com/WPbookmark.php
|
// http://officeopenxml.com/WPbookmark.php
|
||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import * as shortid from "shortid";
|
||||||
import { TextRun } from "../run";
|
import { TextRun } from "../run";
|
||||||
import { BookmarkEndAttributes, BookmarkStartAttributes } from "./bookmark-attributes";
|
import { BookmarkEndAttributes, BookmarkStartAttributes } from "./bookmark-attributes";
|
||||||
|
|
||||||
export class Bookmark {
|
export class Bookmark {
|
||||||
public readonly linkId: number;
|
|
||||||
public readonly start: BookmarkStart;
|
public readonly start: BookmarkStart;
|
||||||
public readonly text: TextRun;
|
public readonly text: TextRun;
|
||||||
public readonly end: BookmarkEnd;
|
public readonly end: BookmarkEnd;
|
||||||
|
|
||||||
constructor(name: string, text: string, relationshipsCount: number) {
|
constructor(name: string, text: string) {
|
||||||
this.linkId = relationshipsCount + 1;
|
const linkId = shortid.generate().toLowerCase();
|
||||||
|
|
||||||
this.start = new BookmarkStart(name, this.linkId);
|
this.start = new BookmarkStart(name, linkId);
|
||||||
this.text = new TextRun(text);
|
this.text = new TextRun(text);
|
||||||
this.end = new BookmarkEnd(this.linkId);
|
this.end = new BookmarkEnd(linkId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BookmarkStart extends XmlComponent {
|
export class BookmarkStart extends XmlComponent {
|
||||||
public readonly linkId: number;
|
constructor(name: string, linkId: string) {
|
||||||
|
|
||||||
constructor(name: string, relationshipsCount: number) {
|
|
||||||
super("w:bookmarkStart");
|
super("w:bookmarkStart");
|
||||||
|
|
||||||
this.linkId = relationshipsCount;
|
|
||||||
const id = `${this.linkId}`;
|
|
||||||
const attributes = new BookmarkStartAttributes({
|
const attributes = new BookmarkStartAttributes({
|
||||||
name,
|
name,
|
||||||
id,
|
id: linkId,
|
||||||
});
|
});
|
||||||
this.root.push(attributes);
|
this.root.push(attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BookmarkEnd extends XmlComponent {
|
export class BookmarkEnd extends XmlComponent {
|
||||||
public readonly linkId: number;
|
constructor(linkId: string) {
|
||||||
|
|
||||||
constructor(relationshipsCount: number) {
|
|
||||||
super("w:bookmarkEnd");
|
super("w:bookmarkEnd");
|
||||||
|
|
||||||
this.linkId = relationshipsCount;
|
|
||||||
const id = `${this.linkId}`;
|
|
||||||
const attributes = new BookmarkEndAttributes({
|
const attributes = new BookmarkEndAttributes({
|
||||||
id,
|
id: linkId,
|
||||||
});
|
});
|
||||||
this.root.push(attributes);
|
this.root.push(attributes);
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,13 @@ import { expect } from "chai";
|
|||||||
import { Formatter } from "export/formatter";
|
import { Formatter } from "export/formatter";
|
||||||
|
|
||||||
import { Hyperlink } from "./";
|
import { Hyperlink } from "./";
|
||||||
|
import { HyperlinkRef } from "./hyperlink";
|
||||||
|
|
||||||
describe("Hyperlink", () => {
|
describe("Hyperlink", () => {
|
||||||
let hyperlink: Hyperlink;
|
let hyperlink: Hyperlink;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
hyperlink = new Hyperlink("https://example.com", 0);
|
hyperlink = new Hyperlink("https://example.com", "superid");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
@ -19,7 +20,7 @@ describe("Hyperlink", () => {
|
|||||||
{
|
{
|
||||||
_attr: {
|
_attr: {
|
||||||
"w:history": 1,
|
"w:history": 1,
|
||||||
"r:id": "rId1",
|
"r:id": "rIdsuperid",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -34,7 +35,7 @@ describe("Hyperlink", () => {
|
|||||||
|
|
||||||
describe("with optional anchor parameter", () => {
|
describe("with optional anchor parameter", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
hyperlink = new Hyperlink("Anchor Text", 0, "anchor");
|
hyperlink = new Hyperlink("Anchor Text", "superid2", "anchor");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create an internal link with anchor tag", () => {
|
it("should create an internal link with anchor tag", () => {
|
||||||
@ -59,3 +60,11 @@ describe("Hyperlink", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("HyperlinkRef", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
const hyperlinkRef = new HyperlinkRef("test-id");
|
||||||
|
|
||||||
|
expect(hyperlinkRef.id).to.equal("test-id");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -3,14 +3,23 @@ import { XmlComponent } from "file/xml-components";
|
|||||||
import { TextRun } from "../run";
|
import { TextRun } from "../run";
|
||||||
import { HyperlinkAttributes, IHyperlinkAttributesProperties } from "./hyperlink-attributes";
|
import { HyperlinkAttributes, IHyperlinkAttributesProperties } from "./hyperlink-attributes";
|
||||||
|
|
||||||
|
export enum HyperlinkType {
|
||||||
|
INTERNAL = "INTERNAL",
|
||||||
|
EXTERNAL = "EXTERNAL",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HyperlinkRef {
|
||||||
|
constructor(public readonly id: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
export class Hyperlink extends XmlComponent {
|
export class Hyperlink extends XmlComponent {
|
||||||
public readonly linkId: number;
|
public readonly linkId: string;
|
||||||
private readonly textRun: TextRun;
|
private readonly textRun: TextRun;
|
||||||
|
|
||||||
constructor(text: string, relationshipsCount: number, anchor?: string) {
|
constructor(text: string, relationshipId: string, anchor?: string) {
|
||||||
super("w:hyperlink");
|
super("w:hyperlink");
|
||||||
|
|
||||||
this.linkId = relationshipsCount + 1;
|
this.linkId = relationshipId;
|
||||||
|
|
||||||
const props: IHyperlinkAttributesProperties = {
|
const props: IHyperlinkAttributesProperties = {
|
||||||
history: 1,
|
history: 1,
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { assert, expect } from "chai";
|
import { assert, expect } from "chai";
|
||||||
|
import * as shortid from "shortid";
|
||||||
|
import { stub } from "sinon";
|
||||||
|
|
||||||
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 { Numbering } from "../numbering";
|
import { AlignmentType, HeadingLevel, LeaderType, PageBreak, TabStopPosition, TabStopType } from "./formatting";
|
||||||
import { AlignmentType, HeadingLevel, LeaderType } from "./formatting";
|
import { Bookmark } from "./links";
|
||||||
import { Paragraph } from "./paragraph";
|
import { Paragraph } from "./paragraph";
|
||||||
|
|
||||||
describe("Paragraph", () => {
|
describe("Paragraph", () => {
|
||||||
@ -254,11 +256,14 @@ describe("Paragraph", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("#maxRightTabStop()", () => {
|
describe("#maxRightTabStop()", () => {
|
||||||
it("should add maxRightTabStop to JSON", () => {
|
it("should add right tab stop to JSON", () => {
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
tabStop: {
|
tabStops: [
|
||||||
maxRight: {},
|
{
|
||||||
},
|
type: TabStopType.RIGHT,
|
||||||
|
position: TabStopPosition.MAX,
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
const tree = new Formatter().format(paragraph);
|
const tree = new Formatter().format(paragraph);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
@ -287,12 +292,13 @@ describe("Paragraph", () => {
|
|||||||
describe("#leftTabStop()", () => {
|
describe("#leftTabStop()", () => {
|
||||||
it("should add leftTabStop to JSON", () => {
|
it("should add leftTabStop to JSON", () => {
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
tabStop: {
|
tabStops: [
|
||||||
left: {
|
{
|
||||||
|
type: TabStopType.LEFT,
|
||||||
position: 100,
|
position: 100,
|
||||||
leader: LeaderType.HYPHEN,
|
leader: LeaderType.HYPHEN,
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
});
|
});
|
||||||
const tree = new Formatter().format(paragraph);
|
const tree = new Formatter().format(paragraph);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
@ -322,12 +328,13 @@ describe("Paragraph", () => {
|
|||||||
describe("#rightTabStop()", () => {
|
describe("#rightTabStop()", () => {
|
||||||
it("should add rightTabStop to JSON", () => {
|
it("should add rightTabStop to JSON", () => {
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
tabStop: {
|
tabStops: [
|
||||||
right: {
|
{
|
||||||
|
type: TabStopType.RIGHT,
|
||||||
position: 100,
|
position: 100,
|
||||||
leader: LeaderType.DOT,
|
leader: LeaderType.DOT,
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
});
|
});
|
||||||
const tree = new Formatter().format(paragraph);
|
const tree = new Formatter().format(paragraph);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
@ -357,12 +364,13 @@ describe("Paragraph", () => {
|
|||||||
describe("#centerTabStop()", () => {
|
describe("#centerTabStop()", () => {
|
||||||
it("should add centerTabStop to JSON", () => {
|
it("should add centerTabStop to JSON", () => {
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
tabStop: {
|
tabStops: [
|
||||||
center: {
|
{
|
||||||
|
type: TabStopType.CENTER,
|
||||||
position: 100,
|
position: 100,
|
||||||
leader: LeaderType.MIDDLE_DOT,
|
leader: LeaderType.MIDDLE_DOT,
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
});
|
});
|
||||||
const tree = new Formatter().format(paragraph);
|
const tree = new Formatter().format(paragraph);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
@ -492,8 +500,9 @@ describe("Paragraph", () => {
|
|||||||
|
|
||||||
describe("#pageBreak()", () => {
|
describe("#pageBreak()", () => {
|
||||||
it("should add page break to JSON", () => {
|
it("should add page break to JSON", () => {
|
||||||
const paragraph = new Paragraph({});
|
const paragraph = new Paragraph({
|
||||||
paragraph.pageBreak();
|
children: [new PageBreak()],
|
||||||
|
});
|
||||||
const tree = new Formatter().format(paragraph);
|
const tree = new Formatter().format(paragraph);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:p": [
|
"w:p": [
|
||||||
@ -589,14 +598,9 @@ describe("Paragraph", () => {
|
|||||||
|
|
||||||
describe("#setNumbering", () => {
|
describe("#setNumbering", () => {
|
||||||
it("should add list paragraph style to JSON", () => {
|
it("should add list paragraph style to JSON", () => {
|
||||||
const numbering = new Numbering();
|
|
||||||
const numberedAbstract = numbering.createAbstractNumbering();
|
|
||||||
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "start");
|
|
||||||
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
|
|
||||||
|
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
numbering: {
|
numbering: {
|
||||||
num: letterNumbering,
|
reference: "test id",
|
||||||
level: 0,
|
level: 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -615,14 +619,9 @@ describe("Paragraph", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("it should add numbered properties", () => {
|
it("it should add numbered properties", () => {
|
||||||
const numbering = new Numbering();
|
|
||||||
const numberedAbstract = numbering.createAbstractNumbering();
|
|
||||||
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "start");
|
|
||||||
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
|
|
||||||
|
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
numbering: {
|
numbering: {
|
||||||
num: letterNumbering,
|
reference: "test id",
|
||||||
level: 0,
|
level: 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -633,10 +632,7 @@ describe("Paragraph", () => {
|
|||||||
"w:pPr": [
|
"w:pPr": [
|
||||||
{ "w:pStyle": { _attr: { "w:val": "ListParagraph" } } },
|
{ "w:pStyle": { _attr: { "w:val": "ListParagraph" } } },
|
||||||
{
|
{
|
||||||
"w:numPr": [
|
"w:numPr": [{ "w:ilvl": { _attr: { "w:val": 0 } } }, { "w:numId": { _attr: { "w:val": "{test id}" } } }],
|
||||||
{ "w:ilvl": { _attr: { "w:val": 0 } } },
|
|
||||||
{ "w:numId": { _attr: { "w:val": letterNumbering.id } } },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -645,6 +641,49 @@ describe("Paragraph", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("it should add bookmark", () => {
|
||||||
|
stub(shortid, "generate").callsFake(() => {
|
||||||
|
return "test-unique-id";
|
||||||
|
});
|
||||||
|
const paragraph = new Paragraph({
|
||||||
|
children: [new Bookmark("test-id", "test")],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(paragraph);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:bookmarkStart": {
|
||||||
|
_attr: {
|
||||||
|
"w:id": "test-unique-id",
|
||||||
|
"w:name": "test-id",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:t": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"xml:space": "preserve",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"test",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:bookmarkEnd": {
|
||||||
|
_attr: {
|
||||||
|
"w:id": "test-unique-id",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("#style", () => {
|
describe("#style", () => {
|
||||||
it("should set the paragraph style to the given styleId", () => {
|
it("should set the paragraph style to the given styleId", () => {
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// http://officeopenxml.com/WPparagraph.php
|
// http://officeopenxml.com/WPparagraph.php
|
||||||
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
||||||
import { Num } from "file/numbering/num";
|
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
||||||
import { XmlComponent } from "file/xml-components";
|
|
||||||
|
|
||||||
|
import { File } from "../file";
|
||||||
import { Alignment, AlignmentType } from "./formatting/alignment";
|
import { Alignment, AlignmentType } from "./formatting/alignment";
|
||||||
import { Bidirectional } from "./formatting/bidirectional";
|
import { Bidirectional } from "./formatting/bidirectional";
|
||||||
import { IBorderOptions, ThematicBreak } from "./formatting/border";
|
import { IBorderOptions, ThematicBreak } from "./formatting/border";
|
||||||
@ -11,16 +11,11 @@ import { KeepLines, KeepNext } from "./formatting/keep";
|
|||||||
import { PageBreak, PageBreakBefore } from "./formatting/page-break";
|
import { PageBreak, PageBreakBefore } from "./formatting/page-break";
|
||||||
import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spacing";
|
import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spacing";
|
||||||
import { HeadingLevel, Style } from "./formatting/style";
|
import { HeadingLevel, Style } from "./formatting/style";
|
||||||
import { CenterTabStop, LeaderType, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop";
|
import { LeaderType, TabStop, TabStopPosition, TabStopType } from "./formatting/tab-stop";
|
||||||
import { NumberProperties } from "./formatting/unordered-list";
|
import { NumberProperties } from "./formatting/unordered-list";
|
||||||
import { Bookmark, Hyperlink, OutlineLevel } from "./links";
|
import { Bookmark, HyperlinkRef, OutlineLevel } from "./links";
|
||||||
import { ParagraphProperties } from "./properties";
|
import { ParagraphProperties } from "./properties";
|
||||||
import { PictureRun, Run, SequentialIdentifier, TextRun } from "./run";
|
import { PictureRun, Run, SequentialIdentifier, SymbolRun, TextRun } from "./run";
|
||||||
|
|
||||||
interface ITabStopOptions {
|
|
||||||
readonly position: number;
|
|
||||||
readonly leader?: LeaderType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IParagraphOptions {
|
export interface IParagraphOptions {
|
||||||
readonly text?: string;
|
readonly text?: string;
|
||||||
@ -36,24 +31,23 @@ export interface IParagraphOptions {
|
|||||||
readonly indent?: IIndentAttributesProperties;
|
readonly indent?: IIndentAttributesProperties;
|
||||||
readonly keepLines?: boolean;
|
readonly keepLines?: boolean;
|
||||||
readonly keepNext?: boolean;
|
readonly keepNext?: boolean;
|
||||||
readonly tabStop?: {
|
readonly tabStops?: Array<{
|
||||||
readonly left?: ITabStopOptions;
|
readonly position: number | TabStopPosition;
|
||||||
readonly right?: ITabStopOptions;
|
readonly type: TabStopType;
|
||||||
readonly maxRight?: {
|
readonly leader?: LeaderType;
|
||||||
readonly leader?: LeaderType;
|
}>;
|
||||||
};
|
|
||||||
readonly center?: ITabStopOptions;
|
|
||||||
};
|
|
||||||
readonly style?: string;
|
readonly style?: string;
|
||||||
readonly bullet?: {
|
readonly bullet?: {
|
||||||
readonly level: number;
|
readonly level: number;
|
||||||
};
|
};
|
||||||
readonly numbering?: {
|
readonly numbering?: {
|
||||||
readonly num: Num;
|
readonly reference: string;
|
||||||
readonly level: number;
|
readonly level: number;
|
||||||
readonly custom?: boolean;
|
readonly custom?: boolean;
|
||||||
};
|
};
|
||||||
readonly children?: Array<TextRun | PictureRun | Hyperlink>;
|
readonly children?: Array<
|
||||||
|
TextRun | PictureRun | SymbolRun | Bookmark | PageBreak | SequentialIdentifier | FootnoteReferenceRun | HyperlinkRef
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Paragraph extends XmlComponent {
|
export class Paragraph extends XmlComponent {
|
||||||
@ -130,21 +124,9 @@ export class Paragraph extends XmlComponent {
|
|||||||
this.properties.push(new KeepNext());
|
this.properties.push(new KeepNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.tabStop) {
|
if (options.tabStops) {
|
||||||
if (options.tabStop.left) {
|
for (const tabStop of options.tabStops) {
|
||||||
this.properties.push(new LeftTabStop(options.tabStop.left.position, options.tabStop.left.leader));
|
this.properties.push(new TabStop(tabStop.type, tabStop.position, tabStop.leader));
|
||||||
}
|
|
||||||
|
|
||||||
if (options.tabStop.right) {
|
|
||||||
this.properties.push(new RightTabStop(options.tabStop.right.position, options.tabStop.right.leader));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.tabStop.maxRight) {
|
|
||||||
this.properties.push(new MaxRightTabStop(options.tabStop.maxRight.leader));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.tabStop.center) {
|
|
||||||
this.properties.push(new CenterTabStop(options.tabStop.center.position, options.tabStop.center.leader));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,51 +143,36 @@ export class Paragraph extends XmlComponent {
|
|||||||
if (!options.numbering.custom) {
|
if (!options.numbering.custom) {
|
||||||
this.properties.push(new Style("ListParagraph"));
|
this.properties.push(new Style("ListParagraph"));
|
||||||
}
|
}
|
||||||
this.properties.push(new NumberProperties(options.numbering.num.id, options.numbering.level));
|
this.properties.push(new NumberProperties(options.numbering.reference, options.numbering.level));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.children) {
|
if (options.children) {
|
||||||
for (const child of options.children) {
|
for (const child of options.children) {
|
||||||
|
if (child instanceof Bookmark) {
|
||||||
|
this.root.push(child.start);
|
||||||
|
this.root.push(child.text);
|
||||||
|
this.root.push(child.end);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
this.root.push(child);
|
this.root.push(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public addRun(run: Run): Paragraph {
|
public prepForXml(file: File): IXmlableObject | undefined {
|
||||||
this.root.push(run);
|
for (const element of this.root) {
|
||||||
return this;
|
if (element instanceof HyperlinkRef) {
|
||||||
}
|
const index = this.root.indexOf(element);
|
||||||
|
this.root[index] = file.HyperlinkCache[element.id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public addHyperLink(hyperlink: Hyperlink): Paragraph {
|
return super.prepForXml();
|
||||||
this.root.push(hyperlink);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public addBookmark(bookmark: Bookmark): Paragraph {
|
|
||||||
// Bookmarks by spec have three components, a start, text, and end
|
|
||||||
this.root.push(bookmark.start);
|
|
||||||
this.root.push(bookmark.text);
|
|
||||||
this.root.push(bookmark.end);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public pageBreak(): Paragraph {
|
|
||||||
this.root.push(new PageBreak());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public referenceFootnote(id: number): Paragraph {
|
|
||||||
this.root.push(new FootnoteReferenceRun(id));
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public addRunToFront(run: Run): Paragraph {
|
public addRunToFront(run: Run): Paragraph {
|
||||||
this.root.splice(1, 0, run);
|
this.root.splice(1, 0, run);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public addSequentialIdentifier(identifier: string): Paragraph {
|
|
||||||
this.root.push(new SequentialIdentifier(identifier));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
export * from "./run";
|
export * from "./run";
|
||||||
export * from "./text-run";
|
export * from "./text-run";
|
||||||
|
export * from "./symbol-run";
|
||||||
export * from "./picture-run";
|
export * from "./picture-run";
|
||||||
|
export * from "./run-fonts";
|
||||||
export * from "./sequential-identifier";
|
export * from "./sequential-identifier";
|
||||||
|
export * from "./underline";
|
||||||
|
export * from "./tab";
|
||||||
|
@ -7,10 +7,6 @@ export class PictureRun extends Run {
|
|||||||
constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) {
|
constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) {
|
||||||
super({});
|
super({});
|
||||||
|
|
||||||
if (imageData === undefined) {
|
|
||||||
throw new Error("imageData cannot be undefined");
|
|
||||||
}
|
|
||||||
|
|
||||||
const drawing = new Drawing(imageData, drawingOptions);
|
const drawing = new Drawing(imageData, drawingOptions);
|
||||||
|
|
||||||
this.root.push(drawing);
|
this.root.push(drawing);
|
||||||
|
28
src/file/paragraph/run/run-components/symbol.spec.ts
Normal file
28
src/file/paragraph/run/run-components/symbol.spec.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "export/formatter";
|
||||||
|
|
||||||
|
import { Symbol } from "./symbol";
|
||||||
|
|
||||||
|
describe("Symbol", () => {
|
||||||
|
describe("#constructor", () => {
|
||||||
|
// Note: if no character is given, the output is a MS Windows logo
|
||||||
|
it("creates an empty symbol run if no character is given", () => {
|
||||||
|
const s = new Symbol();
|
||||||
|
const f = new Formatter().format(s);
|
||||||
|
expect(f).to.deep.equal({ "w:sym": { _attr: { "w:char": "", "w:font": "Wingdings" } } });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates the provided symbol with default font", () => {
|
||||||
|
const s = new Symbol("F071");
|
||||||
|
const f = new Formatter().format(s);
|
||||||
|
expect(f).to.deep.equal({ "w:sym": { _attr: { "w:char": "F071", "w:font": "Wingdings" } } });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates the provided symbol with the provided font", () => {
|
||||||
|
const s = new Symbol("F071", "Arial");
|
||||||
|
const f = new Formatter().format(s);
|
||||||
|
expect(f).to.deep.equal({ "w:sym": { _attr: { "w:char": "F071", "w:font": "Arial" } } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
20
src/file/paragraph/run/run-components/symbol.ts
Normal file
20
src/file/paragraph/run/run-components/symbol.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
interface ISymbolAttributesProperties {
|
||||||
|
readonly char: string;
|
||||||
|
readonly symbolfont?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SymbolAttributes extends XmlAttributeComponent<ISymbolAttributesProperties> {
|
||||||
|
protected readonly xmlKeys = {
|
||||||
|
char: "w:char",
|
||||||
|
symbolfont: "w:font",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Symbol extends XmlComponent {
|
||||||
|
constructor(char: string = "", symbolfont: string = "Wingdings") {
|
||||||
|
super("w:sym");
|
||||||
|
this.root.push(new SymbolAttributes({ char: char, symbolfont: symbolfont }));
|
||||||
|
}
|
||||||
|
}
|
@ -6,12 +6,6 @@ import { Text } from "./text";
|
|||||||
|
|
||||||
describe("Text", () => {
|
describe("Text", () => {
|
||||||
describe("#constructor", () => {
|
describe("#constructor", () => {
|
||||||
it("creates an empty text run if no text is given", () => {
|
|
||||||
const t = new Text("");
|
|
||||||
const f = new Formatter().format(t);
|
|
||||||
expect(f).to.deep.equal({ "w:t": { _attr: { "xml:space": "preserve" } } });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("adds the passed in text to the component", () => {
|
it("adds the passed in text to the component", () => {
|
||||||
const t = new Text(" this is\n text");
|
const t = new Text(" this is\n text");
|
||||||
const f = new Formatter().format(t);
|
const f = new Formatter().format(t);
|
||||||
|
@ -9,8 +9,7 @@ export class Text extends XmlComponent {
|
|||||||
constructor(text: string) {
|
constructor(text: string) {
|
||||||
super("w:t");
|
super("w:t");
|
||||||
this.root.push(new TextAttributes({ space: SpaceType.PRESERVE }));
|
this.root.push(new TextAttributes({ space: SpaceType.PRESERVE }));
|
||||||
if (text) {
|
|
||||||
this.root.push(text);
|
this.root.push(text);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
|
||||||
import { Formatter } from "export/formatter";
|
import { Formatter } from "export/formatter";
|
||||||
|
// import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
||||||
import { ShadingType } from "file/table";
|
import { ShadingType } from "file/table";
|
||||||
|
|
||||||
import { Run } from "./";
|
import { Run } from "./";
|
||||||
|
import { PageNumber } from "./run";
|
||||||
import { UnderlineType } from "./underline";
|
import { UnderlineType } from "./underline";
|
||||||
|
|
||||||
describe("Run", () => {
|
describe("Run", () => {
|
||||||
@ -130,6 +132,30 @@ describe("Run", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("#subScript()", () => {
|
||||||
|
it("it should add subScript to the properties", () => {
|
||||||
|
const run = new Run({
|
||||||
|
subScript: true,
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(run);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:r": [{ "w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "subscript" } } }] }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#superScript()", () => {
|
||||||
|
it("it should add superScript to the properties", () => {
|
||||||
|
const run = new Run({
|
||||||
|
superScript: true,
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(run);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:r": [{ "w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "superscript" } } }] }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("#highlight()", () => {
|
describe("#highlight()", () => {
|
||||||
it("it should add highlight to the properties", () => {
|
it("it should add highlight to the properties", () => {
|
||||||
const run = new Run({
|
const run = new Run({
|
||||||
@ -197,17 +223,6 @@ describe("Run", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#tab()", () => {
|
|
||||||
it("it should add break to the run", () => {
|
|
||||||
const run = new Run({});
|
|
||||||
run.tab();
|
|
||||||
const tree = new Formatter().format(run);
|
|
||||||
expect(tree).to.deep.equal({
|
|
||||||
"w:r": [{ "w:tab": {} }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#font()", () => {
|
describe("#font()", () => {
|
||||||
it("should set the font as named", () => {
|
it("should set the font as named", () => {
|
||||||
const run = new Run({
|
const run = new Run({
|
||||||
@ -270,8 +285,10 @@ describe("Run", () => {
|
|||||||
|
|
||||||
describe("#numberOfTotalPages", () => {
|
describe("#numberOfTotalPages", () => {
|
||||||
it("should set the run to the RTL mode", () => {
|
it("should set the run to the RTL mode", () => {
|
||||||
const run = new Run({});
|
const run = new Run({
|
||||||
run.numberOfTotalPages();
|
children: [PageNumber.TOTAL_PAGES],
|
||||||
|
});
|
||||||
|
|
||||||
const tree = new Formatter().format(run);
|
const tree = new Formatter().format(run);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:r": [
|
"w:r": [
|
||||||
@ -286,8 +303,10 @@ describe("Run", () => {
|
|||||||
|
|
||||||
describe("#numberOfTotalPagesSection", () => {
|
describe("#numberOfTotalPagesSection", () => {
|
||||||
it("should set the run to the RTL mode", () => {
|
it("should set the run to the RTL mode", () => {
|
||||||
const run = new Run({});
|
const run = new Run({
|
||||||
run.numberOfTotalPagesSection();
|
children: [PageNumber.TOTAL_PAGES_IN_SECTION],
|
||||||
|
});
|
||||||
|
|
||||||
const tree = new Formatter().format(run);
|
const tree = new Formatter().format(run);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:r": [
|
"w:r": [
|
||||||
@ -302,8 +321,9 @@ describe("Run", () => {
|
|||||||
|
|
||||||
describe("#pageNumber", () => {
|
describe("#pageNumber", () => {
|
||||||
it("should set the run to the RTL mode", () => {
|
it("should set the run to the RTL mode", () => {
|
||||||
const run = new Run({});
|
const run = new Run({
|
||||||
run.pageNumber();
|
children: [PageNumber.CURRENT],
|
||||||
|
});
|
||||||
const tree = new Formatter().format(run);
|
const tree = new Formatter().format(run);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:r": [
|
"w:r": [
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
import { ShadingType } from "file/table";
|
import { ShadingType } from "file/table";
|
||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
||||||
|
import { FieldInstruction } from "file/table-of-contents/field-instruction";
|
||||||
import { Break } from "./break";
|
import { Break } from "./break";
|
||||||
import { Caps, SmallCaps } from "./caps";
|
import { Caps, SmallCaps } from "./caps";
|
||||||
import { Begin, End, Separate } from "./field";
|
import { Begin, End, Separate } from "./field";
|
||||||
@ -23,10 +25,10 @@ import {
|
|||||||
} from "./formatting";
|
} from "./formatting";
|
||||||
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 { RunFonts } from "./run-fonts";
|
import { RunFonts } from "./run-fonts";
|
||||||
import { SubScript, SuperScript } from "./script";
|
import { SubScript, SuperScript } from "./script";
|
||||||
import { Style } from "./style";
|
import { Style } from "./style";
|
||||||
import { Tab } from "./tab";
|
|
||||||
import { Underline, UnderlineType } from "./underline";
|
import { Underline, UnderlineType } from "./underline";
|
||||||
|
|
||||||
export interface IRunOptions {
|
export interface IRunOptions {
|
||||||
@ -56,6 +58,14 @@ export interface IRunOptions {
|
|||||||
readonly fill: string;
|
readonly fill: string;
|
||||||
readonly color: string;
|
readonly color: string;
|
||||||
};
|
};
|
||||||
|
readonly children?: Array<Begin | FieldInstruction | Separate | End | PageNumber | FootnoteReferenceRun | string>;
|
||||||
|
readonly text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PageNumber {
|
||||||
|
CURRENT = "CURRENT",
|
||||||
|
TOTAL_PAGES = "TOTAL_PAGES",
|
||||||
|
TOTAL_PAGES_IN_SECTION = "TOTAL_PAGES_IN_SECTION",
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Run extends XmlComponent {
|
export class Run extends XmlComponent {
|
||||||
@ -134,39 +144,45 @@ export class Run extends XmlComponent {
|
|||||||
this.properties.push(new Shading(options.shading.type, options.shading.fill, options.shading.color));
|
this.properties.push(new Shading(options.shading.type, options.shading.fill, options.shading.color));
|
||||||
this.properties.push(new ShadowComplexScript(options.shading.type, options.shading.fill, options.shading.color));
|
this.properties.push(new ShadowComplexScript(options.shading.type, options.shading.fill, options.shading.color));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.children) {
|
||||||
|
for (const child of options.children) {
|
||||||
|
if (typeof child === "string") {
|
||||||
|
switch (child) {
|
||||||
|
case PageNumber.CURRENT:
|
||||||
|
this.root.push(new Begin());
|
||||||
|
this.root.push(new Page());
|
||||||
|
this.root.push(new Separate());
|
||||||
|
this.root.push(new End());
|
||||||
|
break;
|
||||||
|
case PageNumber.TOTAL_PAGES:
|
||||||
|
this.root.push(new Begin());
|
||||||
|
this.root.push(new NumberOfPages());
|
||||||
|
this.root.push(new Separate());
|
||||||
|
this.root.push(new End());
|
||||||
|
break;
|
||||||
|
case PageNumber.TOTAL_PAGES_IN_SECTION:
|
||||||
|
this.root.push(new Begin());
|
||||||
|
this.root.push(new NumberOfPagesSection());
|
||||||
|
this.root.push(new Separate());
|
||||||
|
this.root.push(new End());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.root.push(new Text(child));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.root.push(child);
|
||||||
|
}
|
||||||
|
} else if (options.text) {
|
||||||
|
this.root.push(new Text(options.text));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public break(): Run {
|
public break(): Run {
|
||||||
this.root.splice(1, 0, new Break());
|
this.root.splice(1, 0, new Break());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public tab(): Run {
|
|
||||||
this.root.splice(1, 0, new Tab());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public pageNumber(): Run {
|
|
||||||
this.root.push(new Begin());
|
|
||||||
this.root.push(new Page());
|
|
||||||
this.root.push(new Separate());
|
|
||||||
this.root.push(new End());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public numberOfTotalPages(): Run {
|
|
||||||
this.root.push(new Begin());
|
|
||||||
this.root.push(new NumberOfPages());
|
|
||||||
this.root.push(new Separate());
|
|
||||||
this.root.push(new End());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public numberOfTotalPagesSection(): Run {
|
|
||||||
this.root.push(new Begin());
|
|
||||||
this.root.push(new NumberOfPagesSection());
|
|
||||||
this.root.push(new Separate());
|
|
||||||
this.root.push(new End());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
76
src/file/paragraph/run/symbol-run.spec.ts
Normal file
76
src/file/paragraph/run/symbol-run.spec.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "export/formatter";
|
||||||
|
|
||||||
|
import { UnderlineType } from "./underline";
|
||||||
|
|
||||||
|
import { SymbolRun } from "./symbol-run";
|
||||||
|
|
||||||
|
describe("SymbolRun", () => {
|
||||||
|
let run: SymbolRun;
|
||||||
|
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create symbol run from text input", () => {
|
||||||
|
run = new SymbolRun("F071");
|
||||||
|
const f = new Formatter().format(run);
|
||||||
|
expect(f).to.deep.equal({
|
||||||
|
"w:r": [{ "w:sym": { _attr: { "w:char": "F071", "w:font": "Wingdings" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create symbol run from object input with just 'char' specified", () => {
|
||||||
|
run = new SymbolRun({ char: "F071" });
|
||||||
|
const f = new Formatter().format(run);
|
||||||
|
expect(f).to.deep.equal({
|
||||||
|
"w:r": [{ "w:sym": { _attr: { "w:char": "F071", "w:font": "Wingdings" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create symbol run from object input with just 'char' specified", () => {
|
||||||
|
run = new SymbolRun({ char: "F071", symbolfont: "Arial" });
|
||||||
|
const f = new Formatter().format(run);
|
||||||
|
expect(f).to.deep.equal({
|
||||||
|
"w:r": [{ "w:sym": { _attr: { "w:char": "F071", "w:font": "Arial" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add other standard run properties", () => {
|
||||||
|
run = new SymbolRun({
|
||||||
|
char: "F071",
|
||||||
|
symbolfont: "Arial",
|
||||||
|
italics: true,
|
||||||
|
bold: true,
|
||||||
|
underline: {
|
||||||
|
color: "red",
|
||||||
|
type: UnderlineType.DOUBLE,
|
||||||
|
},
|
||||||
|
color: "green",
|
||||||
|
size: 40,
|
||||||
|
highlight: "yellow",
|
||||||
|
});
|
||||||
|
|
||||||
|
const f = new Formatter().format(run);
|
||||||
|
expect(f).to.deep.equal({
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [
|
||||||
|
{ "w:b": { _attr: { "w:val": true } } },
|
||||||
|
{ "w:bCs": { _attr: { "w:val": true } } },
|
||||||
|
{ "w:i": { _attr: { "w:val": true } } },
|
||||||
|
{ "w:iCs": { _attr: { "w:val": true } } },
|
||||||
|
{ "w:u": { _attr: { "w:val": "double", "w:color": "red" } } },
|
||||||
|
{ "w:color": { _attr: { "w:val": "green" } } },
|
||||||
|
{ "w:sz": { _attr: { "w:val": 40 } } },
|
||||||
|
{ "w:szCs": { _attr: { "w:val": 40 } } },
|
||||||
|
{ "w:highlight": { _attr: { "w:val": "yellow" } } },
|
||||||
|
{ "w:highlightCs": { _attr: { "w:val": "yellow" } } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:sym": { _attr: { "w:char": "F071", "w:font": "Arial" } },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
20
src/file/paragraph/run/symbol-run.ts
Normal file
20
src/file/paragraph/run/symbol-run.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { IRunOptions, Run } from "./run";
|
||||||
|
import { Symbol } from "./run-components/symbol";
|
||||||
|
|
||||||
|
export interface ISymbolRunOptions extends IRunOptions {
|
||||||
|
readonly char: string;
|
||||||
|
readonly symbolfont?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SymbolRun extends Run {
|
||||||
|
constructor(options: ISymbolRunOptions | string) {
|
||||||
|
if (typeof options === "string") {
|
||||||
|
super({});
|
||||||
|
this.root.push(new Symbol(options));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super(options);
|
||||||
|
this.root.push(new Symbol(options.char, options.symbolfont));
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
|
||||||
import { Formatter } from "export/formatter";
|
import { Formatter } from "export/formatter";
|
||||||
|
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
||||||
|
|
||||||
import { TextRun } from "./text-run";
|
import { TextRun } from "./text-run";
|
||||||
|
|
||||||
@ -16,4 +17,25 @@ describe("TextRun", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("#referenceFootnote()", () => {
|
||||||
|
it("should add a valid footnote reference", () => {
|
||||||
|
run = new TextRun({
|
||||||
|
children: ["test", new FootnoteReferenceRun(1)],
|
||||||
|
});
|
||||||
|
const tree = new Formatter().format(run);
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:r": [
|
||||||
|
{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "test"] },
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{ "w:rPr": [{ "w:rStyle": { _attr: { "w:val": "FootnoteReference" } } }] },
|
||||||
|
{ "w:footnoteReference": { _attr: { "w:id": 1 } } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
import { IRunOptions, Run } from "./run";
|
import { IRunOptions, Run } from "./run";
|
||||||
import { Text } from "./run-components/text";
|
import { Text } from "./run-components/text";
|
||||||
|
|
||||||
export interface ITextRunOptions extends IRunOptions {
|
|
||||||
readonly text: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TextRun extends Run {
|
export class TextRun extends Run {
|
||||||
constructor(options: ITextRunOptions | string) {
|
constructor(options: IRunOptions | string) {
|
||||||
if (typeof options === "string") {
|
if (typeof options === "string") {
|
||||||
super({});
|
super({});
|
||||||
this.root.push(new Text(options));
|
this.root.push(new Text(options));
|
||||||
@ -14,6 +10,5 @@ export class TextRun extends Run {
|
|||||||
}
|
}
|
||||||
|
|
||||||
super(options);
|
super(options);
|
||||||
this.root.push(new Text(options.text));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ describe("Underline", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should use the given style type and color", () => {
|
it("should use the given style type and color", () => {
|
||||||
const underline = new u.Underline("double", "FF00CC");
|
const underline = new u.Underline(u.UnderlineType.DOUBLE, "FF00CC");
|
||||||
const tree = new Formatter().format(underline);
|
const tree = new Formatter().format(underline);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:u": { _attr: { "w:val": "double", "w:color": "FF00CC" } },
|
"w:u": { _attr: { "w:val": "double", "w:color": "FF00CC" } },
|
||||||
|
@ -33,7 +33,7 @@ export abstract class BaseUnderline extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Underline extends BaseUnderline {
|
export class Underline extends BaseUnderline {
|
||||||
constructor(underlineType: string = "single", color?: string) {
|
constructor(underlineType: UnderlineType = UnderlineType.SINGLE, color?: string) {
|
||||||
super(underlineType, color);
|
super(underlineType, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export class Relationships extends XmlComponent {
|
|||||||
this.root.push(relationship);
|
this.root.push(relationship);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createRelationship(id: number, 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.addRelationship(relationship);
|
||||||
|
|
||||||
|
@ -38,11 +38,13 @@ export class ExternalStylesFactory {
|
|||||||
throw new Error("can not find styles element");
|
throw new Error("can not find styles element");
|
||||||
}
|
}
|
||||||
|
|
||||||
const importedStyle = new Styles(new ImportedRootElementAttributes(stylesXmlElement.attributes));
|
|
||||||
const stylesElements = stylesXmlElement.elements || [];
|
const stylesElements = stylesXmlElement.elements || [];
|
||||||
for (const childElm of stylesElements) {
|
|
||||||
importedStyle.push(convertToXmlComponent(childElm) as ImportedXmlComponent);
|
const importedStyle = new Styles({
|
||||||
}
|
initialStyles: new ImportedRootElementAttributes(stylesXmlElement.attributes),
|
||||||
|
importedStyles: stylesElements.map((childElm) => convertToXmlComponent(childElm) as ImportedXmlComponent),
|
||||||
|
});
|
||||||
|
|
||||||
return importedStyle;
|
return importedStyle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { DocumentAttributes } from "../document/document-attributes";
|
import { DocumentAttributes } from "../document/document-attributes";
|
||||||
import { Color, Italics, Size } from "../paragraph/run/formatting";
|
import { IStylesOptions } from "./styles";
|
||||||
import { Styles } from "./";
|
|
||||||
|
|
||||||
|
import { DocumentDefaults } from "./defaults";
|
||||||
import {
|
import {
|
||||||
FootnoteReferenceStyle,
|
FootnoteReferenceStyle,
|
||||||
FootnoteText,
|
FootnoteText,
|
||||||
@ -18,7 +18,7 @@ import {
|
|||||||
} from "./style";
|
} from "./style";
|
||||||
|
|
||||||
export class DefaultStylesFactory {
|
export class DefaultStylesFactory {
|
||||||
public newInstance(): Styles {
|
public newInstance(): IStylesOptions {
|
||||||
const documentAttributes = new DocumentAttributes({
|
const documentAttributes = new DocumentAttributes({
|
||||||
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
||||||
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||||
@ -27,57 +27,55 @@ export class DefaultStylesFactory {
|
|||||||
w15: "http://schemas.microsoft.com/office/word/2012/wordml",
|
w15: "http://schemas.microsoft.com/office/word/2012/wordml",
|
||||||
Ignorable: "w14 w15",
|
Ignorable: "w14 w15",
|
||||||
});
|
});
|
||||||
const styles = new Styles(documentAttributes);
|
return {
|
||||||
styles.createDocumentDefaults();
|
initialStyles: documentAttributes,
|
||||||
|
importedStyles: [
|
||||||
const titleStyle = new TitleStyle();
|
new DocumentDefaults(),
|
||||||
titleStyle.addRunProperty(new Size(56));
|
new TitleStyle({
|
||||||
styles.push(titleStyle);
|
run: {
|
||||||
|
size: 56,
|
||||||
const heading1Style = new Heading1Style();
|
},
|
||||||
heading1Style.addRunProperty(new Color("2E74B5"));
|
}),
|
||||||
heading1Style.addRunProperty(new Size(32));
|
new Heading1Style({
|
||||||
styles.push(heading1Style);
|
run: {
|
||||||
|
color: "2E74B5",
|
||||||
const heading2Style = new Heading2Style();
|
size: 32,
|
||||||
heading2Style.addRunProperty(new Color("2E74B5"));
|
},
|
||||||
heading2Style.addRunProperty(new Size(26));
|
}),
|
||||||
styles.push(heading2Style);
|
new Heading2Style({
|
||||||
|
run: {
|
||||||
const heading3Style = new Heading3Style();
|
color: "2E74B5",
|
||||||
heading3Style.addRunProperty(new Color("1F4D78"));
|
size: 26,
|
||||||
heading3Style.addRunProperty(new Size(24));
|
},
|
||||||
styles.push(heading3Style);
|
}),
|
||||||
|
new Heading3Style({
|
||||||
const heading4Style = new Heading4Style();
|
run: {
|
||||||
heading4Style.addRunProperty(new Color("2E74B5"));
|
color: "1F4D78",
|
||||||
heading4Style.addRunProperty(new Italics());
|
size: 24,
|
||||||
styles.push(heading4Style);
|
},
|
||||||
|
}),
|
||||||
const heading5Style = new Heading5Style();
|
new Heading4Style({
|
||||||
heading5Style.addRunProperty(new Color("2E74B5"));
|
run: {
|
||||||
styles.push(heading5Style);
|
color: "2E74B5",
|
||||||
|
italics: true,
|
||||||
const heading6Style = new Heading6Style();
|
},
|
||||||
heading6Style.addRunProperty(new Color("1F4D78"));
|
}),
|
||||||
styles.push(heading6Style);
|
new Heading5Style({
|
||||||
|
run: {
|
||||||
const listParagraph = new ListParagraph();
|
color: "2E74B5",
|
||||||
// listParagraph.addParagraphProperty();
|
},
|
||||||
styles.push(listParagraph);
|
}),
|
||||||
|
new Heading6Style({
|
||||||
const hyperLinkStyle = new HyperlinkStyle();
|
run: {
|
||||||
styles.push(hyperLinkStyle);
|
color: "1F4D78",
|
||||||
|
},
|
||||||
const footnoteReferenceStyle = new FootnoteReferenceStyle();
|
}),
|
||||||
styles.push(footnoteReferenceStyle);
|
new ListParagraph({}),
|
||||||
|
new HyperlinkStyle({}),
|
||||||
const footnoteTextStyle = new FootnoteText();
|
new FootnoteReferenceStyle({}),
|
||||||
styles.push(footnoteTextStyle);
|
new FootnoteText({}),
|
||||||
|
new FootnoteTextChar({}),
|
||||||
const footnoteTextCharStyle = new FootnoteTextChar();
|
],
|
||||||
styles.push(footnoteTextCharStyle);
|
};
|
||||||
|
|
||||||
return styles;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
export * from "./styles";
|
export * from "./styles";
|
||||||
export * from "./style/character-style";
|
export * from "./style/character-style";
|
||||||
export * from "./style/paragraph-style";
|
export * from "./style/paragraph-style";
|
||||||
|
export * from "./style-options";
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user