Compare commits
163 Commits
Author | SHA1 | Date | |
---|---|---|---|
34c3285426 | |||
a5afce458d | |||
2f687125d1 | |||
5c1a731314 | |||
622331ad24 | |||
3a593a53d3 | |||
31a9111667 | |||
373c890354 | |||
723d76d06f | |||
a24d745d75 | |||
90891cfafd | |||
29f890918c | |||
b0f8f8ddbd | |||
9c89c1ab59 | |||
7a9cb92955 | |||
77edf8862b | |||
bb1604cd8f | |||
816cb54b14 | |||
920bd3c175 | |||
a2a01edc24 | |||
193d0c4239 | |||
d8cc11c5ab | |||
b874051f32 | |||
272e2496f4 | |||
43c199c725 | |||
0e975b3d66 | |||
c6ab47e838 | |||
48c17d51bb | |||
df2315ae4a | |||
e67fd9cb2b | |||
639842332f | |||
2cb7d44a77 | |||
6cd62418a5 | |||
f3ba11b21c | |||
51b1e08a35 | |||
b0d0041ff9 | |||
55c5dd818a | |||
3bebe0ad59 | |||
c5c67fd92a | |||
1486d3de56 | |||
6d8eea40be | |||
0922d49cd3 | |||
f76471f553 | |||
973177676e | |||
5aa878b901 | |||
b99d1fc129 | |||
1590f9ac70 | |||
6c772b956c | |||
9d09ff3518 | |||
d9ad69b7cd | |||
e23d230acb | |||
5ca872cb07 | |||
b179facca2 | |||
db60270e1f | |||
3e2130bc80 | |||
1b336785b4 | |||
6eed0fe0f5 | |||
2f8f69b0c4 | |||
4580a65a84 | |||
661c3e21e4 | |||
32646f8806 | |||
231428852e | |||
46ddf77342 | |||
441afe8c97 | |||
ca9ce01f56 | |||
2b9ce0febd | |||
18760387db | |||
28dabb06cb | |||
50fc9b6274 | |||
367518d504 | |||
efd89f24e6 | |||
4fd2b6f1d3 | |||
5ae541a40d | |||
c9fb9a827d | |||
918faf59c3 | |||
3a9fa49fbb | |||
218a08d793 | |||
416a239708 | |||
d1bdbd397a | |||
a6077b8f16 | |||
8f133ff93a | |||
1f12e159ef | |||
83a7f4664d | |||
728aefc4a7 | |||
40730548bb | |||
d6c2c96757 | |||
b22f565dd0 | |||
41eeac1b05 | |||
18a5f22f4c | |||
2a1161d857 | |||
4b6d3c3e3c | |||
14a1d62148 | |||
1f98d9461b | |||
8dc590746b | |||
de5f5c9a77 | |||
788205b14d | |||
ca5f6a56a5 | |||
bf8dfe6604 | |||
b0ee0305fb | |||
8f6984580a | |||
383c6d769f | |||
9f38a4f48a | |||
475a132ce0 | |||
4111413074 | |||
50209bb435 | |||
b37d2c141d | |||
d19cdcae0c | |||
3f652764e9 | |||
7b5cf690e2 | |||
d674523139 | |||
db7f27a88c | |||
677300e34f | |||
9271b2d11a | |||
942c6d028c | |||
f717126145 | |||
abd5ace85c | |||
612c024b1f | |||
12ad545fe8 | |||
6d0f6a61d7 | |||
e3bcad6d3c | |||
f7c372a85c | |||
bb277a4a76 | |||
52b78a583e | |||
20ab36191b | |||
dbfb80e660 | |||
421f4471de | |||
04ea2b2dc9 | |||
93a2404343 | |||
149cda9a3b | |||
b9465b2a20 | |||
ab07f2ecbe | |||
c70c147afe | |||
8db52212ab | |||
d021a3995e | |||
1ea347ed21 | |||
0725e54271 | |||
3b6aca2451 | |||
249e44532f | |||
d3af3663ec | |||
bf18154fcf | |||
5efc9e383b | |||
6505119d3b | |||
600f97de29 | |||
a1c21d2143 | |||
39066fb5f2 | |||
8fd99052fb | |||
4e671eb512 | |||
2677c4a4ec | |||
e19890e27a | |||
28556f277b | |||
50e08f198c | |||
7639b60b15 | |||
765a9686d8 | |||
ff443aa7c4 | |||
28fc328cb3 | |||
083f121683 | |||
2f57d3be84 | |||
7b47dadede | |||
8ad5485347 | |||
8df78e45d9 | |||
39cef5e61d | |||
5552f9d834 | |||
d13795696d |
@ -10,6 +10,7 @@ script:
|
||||
- npm run style
|
||||
- npm run build
|
||||
- npm run ts-node -- ./demo/demo1.ts
|
||||
- npm run e2e "My Document.docx"
|
||||
- npm run ts-node -- ./demo/demo2.ts
|
||||
- npm run ts-node -- ./demo/demo3.ts
|
||||
- npm run ts-node -- ./demo/demo4.ts
|
||||
@ -19,6 +20,7 @@ script:
|
||||
- npm run ts-node -- ./demo/demo8.ts
|
||||
- npm run ts-node -- ./demo/demo9.ts
|
||||
- npm run ts-node -- ./demo/demo10.ts
|
||||
- npm run e2e "My Document.docx"
|
||||
- npm run ts-node -- ./demo/demo11.ts
|
||||
- npm run ts-node -- ./demo/demo12.ts
|
||||
- npm run ts-node -- ./demo/demo13.ts
|
||||
@ -41,6 +43,7 @@ script:
|
||||
- npm run ts-node -- ./demo/demo30.ts
|
||||
- npm run ts-node -- ./demo/demo31.ts
|
||||
- npm run ts-node -- ./demo/demo32.ts
|
||||
# - npm run e2e "My Document.docx" // Need to fix
|
||||
- npm run ts-node -- ./demo/demo33.ts
|
||||
- npm run ts-node -- ./demo/demo34.ts
|
||||
after_failure:
|
||||
|
15
README.md
15
README.md
@ -25,7 +25,7 @@
|
||||
|
||||
## Browser
|
||||
|
||||
Here are examples of `docx` being used with basic `HTML/JS` in a browser environment.
|
||||
Here are examples of `docx` being used with basic `HTML/JS` in a browser environment:
|
||||
|
||||
* https://codepen.io/anon/pen/dqoVgQ
|
||||
* https://jsfiddle.net/3xhezb5w/2
|
||||
@ -64,8 +64,17 @@ Check the `examples` section in the [documentation](https://docx.js.org/#/exampl
|
||||
|
||||
Read the contribution guidelines [here](https://docx.js.org/#/contribution-guidelines).
|
||||
|
||||
# Used by
|
||||
|
||||
[<img src="https://i.imgur.com/zy5qWmI.png" alt="drawing" height="200"/>](https://hfour.com/)
|
||||
[<img src="https://i.imgur.com/OYP5tgS.png" alt="drawing" height="200"/>](https://fuzzproductions.com/)
|
||||
[<img src="https://i.imgur.com/zUDMfZ3.png" alt="drawing" height="200"/>](https://www.mettzer.com/)
|
||||
|
||||
---
|
||||
|
||||
[![patreon][patreon-image]][patreon-url]
|
||||
[![browserstack][browserstack-image]][browserstack-url]
|
||||
|
||||
Made with 💖
|
||||
|
||||
[npm-image]: https://badge.fury.io/js/docx.svg
|
||||
@ -84,3 +93,7 @@ Made with 💖
|
||||
[pr-url]: http://makeapullrequest.com
|
||||
[codecov-image]: https://codecov.io/gh/dolanmiu/docx/branch/master/graph/badge.svg
|
||||
[codecov-url]: https://codecov.io/gh/dolanmiu/docx
|
||||
[patreon-image]: https://user-images.githubusercontent.com/2917613/51251459-4e880480-1991-11e9-92bf-38b96675a9e2.png
|
||||
[patreon-url]: https://www.patreon.com/dolanmiu
|
||||
[browserstack-image]: https://user-images.githubusercontent.com/2917613/54233552-128e9d00-4505-11e9-88fb-025a4e04007c.png
|
||||
[browserstack-url]: https://www.browserstack.com
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Add images to header and footer
|
||||
// Generate a CV
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph, TextRun } from "../build";
|
||||
@ -233,7 +233,7 @@ class DocumentCreator {
|
||||
|
||||
public createRoleText(roleText: string): Paragraph {
|
||||
const paragraph = new Paragraph();
|
||||
const role = new TextRun(roleText).italic();
|
||||
const role = new TextRun(roleText).italics();
|
||||
|
||||
paragraph.addRun(role);
|
||||
|
||||
|
@ -84,8 +84,7 @@ doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph")
|
||||
.basedOn("Normal");
|
||||
|
||||
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||
doc
|
||||
.createParagraph("HEADING")
|
||||
doc.createParagraph("HEADING")
|
||||
.heading1()
|
||||
.center();
|
||||
|
||||
@ -107,12 +106,15 @@ doc.createParagraph("Sir,").style("normalPara");
|
||||
|
||||
doc.createParagraph("BRIEF DESCRIPTION").style("normalPara");
|
||||
|
||||
const table = new Table(4, 4);
|
||||
const table = new Table({
|
||||
rows: 4,
|
||||
columns: 4,
|
||||
});
|
||||
table
|
||||
.getRow(0)
|
||||
.getCell(0)
|
||||
.addContent(new Paragraph("Pole No."));
|
||||
// table.Properties.width = 10000;
|
||||
.addParagraph(new Paragraph("Pole No."));
|
||||
|
||||
doc.addTable(table);
|
||||
|
||||
const arrboth = [
|
||||
@ -127,10 +129,7 @@ const arrboth = [
|
||||
];
|
||||
|
||||
arrboth.forEach((item) => {
|
||||
const para = doc.createParagraph();
|
||||
para.addImage(doc.createImage(fs.readFileSync(item.image)));
|
||||
// para.Properties.width = 60;
|
||||
// para.Properties.height = 90;
|
||||
doc.createImage(fs.readFileSync(item.image));
|
||||
doc.createParagraph(item.comment).style("normalPara2");
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Multiple sections and headers
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, PageNumberFormat, PageOrientation, Paragraph } from "../build";
|
||||
import { Document, Packer, PageNumberFormat, PageOrientation, Paragraph, TextRun } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
@ -41,6 +41,42 @@ doc.addSection({
|
||||
|
||||
doc.createParagraph("hello in landscape");
|
||||
|
||||
const header2 = doc.createHeader();
|
||||
const pageNumber = new TextRun("Page number: ").pageNumber();
|
||||
header2.createParagraph().addRun(pageNumber);
|
||||
|
||||
doc.addSection({
|
||||
headers: {
|
||||
default: header2,
|
||||
},
|
||||
orientation: PageOrientation.PORTRAIT,
|
||||
});
|
||||
|
||||
doc.createParagraph("Page number in the header must be 2, because it continues from the previous section.");
|
||||
|
||||
doc.addSection({
|
||||
headers: {
|
||||
default: header2,
|
||||
},
|
||||
pageNumberFormatType: PageNumberFormat.UPPER_ROMAN,
|
||||
orientation: PageOrientation.PORTRAIT,
|
||||
});
|
||||
|
||||
doc.createParagraph(
|
||||
"Page number in the header must be III, because it continues from the previous section, but is defined as upper roman.",
|
||||
);
|
||||
|
||||
doc.addSection({
|
||||
headers: {
|
||||
default: header2,
|
||||
},
|
||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||
pageNumberStart: 25,
|
||||
orientation: PageOrientation.PORTRAIT,
|
||||
});
|
||||
|
||||
doc.createParagraph("Page number in the header must be 25, because it is defined to start at 25 and to be decimal in this section.");
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
|
@ -5,11 +5,14 @@ import { BorderStyle, Document, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable(4, 4);
|
||||
const table = doc.createTable({
|
||||
rows: 4,
|
||||
columns: 4,
|
||||
});
|
||||
table
|
||||
.getCell(2, 2)
|
||||
.addContent(new Paragraph("Hello"))
|
||||
.CellProperties.Borders.addTopBorder(BorderStyle.DASH_DOT_STROKED, 3, "red")
|
||||
.addParagraph(new Paragraph("Hello"))
|
||||
.Borders.addTopBorder(BorderStyle.DASH_DOT_STROKED, 3, "red")
|
||||
.addBottomBorder(BorderStyle.DOUBLE, 3, "blue")
|
||||
.addStartBorder(BorderStyle.DOT_DOT_DASH, 3, "green")
|
||||
.addEndBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000");
|
||||
|
@ -16,7 +16,7 @@ paragraph2.addRun(textRun2);
|
||||
doc.addParagraph(paragraph2);
|
||||
|
||||
const paragraph3 = new Paragraph().bidirectional();
|
||||
const textRun3 = new TextRun("שלום עולם").italic().rightToLeft();
|
||||
const textRun3 = new TextRun("שלום עולם").italics().rightToLeft();
|
||||
paragraph3.addRun(textRun3);
|
||||
doc.addParagraph(paragraph3);
|
||||
|
||||
|
@ -5,11 +5,14 @@ import { Document, Media, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable(4, 4);
|
||||
table.getCell(2, 2).addContent(new Paragraph("Hello"));
|
||||
const table = doc.createTable({
|
||||
rows: 4,
|
||||
columns: 4,
|
||||
});
|
||||
table.getCell(2, 2).addParagraph(new Paragraph("Hello"));
|
||||
|
||||
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||
table.getCell(1, 1).addContent(image.Paragraph);
|
||||
table.getCell(1, 1).addParagraph(image.Paragraph);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
// Custom styles using JavaScript configuration
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer } from "../build";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Creates two paragraphs, one with a border and one without
|
||||
// Table of contents
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { File, Packer, Paragraph, StyleLevel, TableOfContents } from "../build";
|
||||
|
@ -1,3 +1,5 @@
|
||||
// Numbered lists
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Indent, Numbering, Packer, Paragraph } from "../build";
|
||||
|
||||
@ -15,10 +17,22 @@ const item2 = new Paragraph("line with contextual spacing");
|
||||
const item3 = new Paragraph("line without contextual spacing");
|
||||
const item4 = new Paragraph("line without contextual spacing");
|
||||
|
||||
item1.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(true);
|
||||
item2.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(true);
|
||||
item3.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(false);
|
||||
item4.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(false);
|
||||
item1
|
||||
.setNumbering(concrete, 0)
|
||||
.spacing({ before: 200 })
|
||||
.contextualSpacing(true);
|
||||
item2
|
||||
.setNumbering(concrete, 0)
|
||||
.spacing({ before: 200 })
|
||||
.contextualSpacing(true);
|
||||
item3
|
||||
.setNumbering(concrete, 0)
|
||||
.spacing({ before: 200 })
|
||||
.contextualSpacing(false);
|
||||
item4
|
||||
.setNumbering(concrete, 0)
|
||||
.spacing({ before: 200 })
|
||||
.contextualSpacing(false);
|
||||
|
||||
doc.addParagraph(item1);
|
||||
doc.addParagraph(item2);
|
||||
@ -29,4 +43,4 @@ const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
// Example on how to use a template document
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, ImportDotx, Packer, Paragraph } from "../build";
|
||||
|
||||
|
@ -5,15 +5,18 @@ import { Document, Packer, Paragraph, VerticalAlign } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable(2, 2);
|
||||
const table = doc.createTable({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
});
|
||||
table
|
||||
.getCell(1, 1)
|
||||
.addContent(new Paragraph("This text should be in the middle of the cell"))
|
||||
.CellProperties.setVerticalAlign(VerticalAlign.CENTER);
|
||||
.addParagraph(new Paragraph("This text should be in the middle of the cell"))
|
||||
.setVerticalAlign(VerticalAlign.CENTER);
|
||||
|
||||
table
|
||||
.getCell(1, 0)
|
||||
.addContent(
|
||||
.addParagraph(
|
||||
new Paragraph(
|
||||
"Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah",
|
||||
).heading1(),
|
||||
|
@ -1,33 +1,99 @@
|
||||
// Example of how you would create a table and add data to it
|
||||
// Example of how you would merge cells together
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph } from "../build";
|
||||
import { Document, Packer, Paragraph, ShadingType, WidthType } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
let table = doc.createTable(2, 2);
|
||||
let table = doc.createTable({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
});
|
||||
|
||||
table.getCell(0, 0).addContent(new Paragraph("Hello"));
|
||||
table.getCell(0, 0).addParagraph(new Paragraph("Hello"));
|
||||
table.getRow(0).mergeCells(0, 1);
|
||||
|
||||
doc.createParagraph("Another table").heading2();
|
||||
|
||||
table = doc.createTable(2, 3);
|
||||
table.getCell(0, 0).addContent(new Paragraph("World"));
|
||||
table = doc.createTable({
|
||||
rows: 2,
|
||||
columns: 3,
|
||||
width: 100,
|
||||
widthUnitType: WidthType.AUTO,
|
||||
columnWidths: [1000, 1000, 1000],
|
||||
});
|
||||
table
|
||||
.getCell(0, 0)
|
||||
.addParagraph(new Paragraph("World"))
|
||||
.setMargins({
|
||||
top: 1000,
|
||||
bottom: 1000,
|
||||
left: 1000,
|
||||
right: 1000,
|
||||
});
|
||||
table.getRow(0).mergeCells(0, 2);
|
||||
|
||||
doc.createParagraph("Another table").heading2();
|
||||
|
||||
table = doc.createTable(2, 4);
|
||||
table.getCell(0, 0).addContent(new Paragraph("Foo"));
|
||||
table = doc.createTable({
|
||||
rows: 2,
|
||||
columns: 4,
|
||||
width: 7000,
|
||||
widthUnitType: WidthType.DXA,
|
||||
margins: {
|
||||
top: 400,
|
||||
bottom: 400,
|
||||
right: 400,
|
||||
left: 400,
|
||||
},
|
||||
});
|
||||
table.getCell(0, 0).addParagraph(new Paragraph("Foo"));
|
||||
table.getCell(0, 1).addParagraph(new Paragraph("v"));
|
||||
|
||||
table.getCell(1, 0).addContent(new Paragraph("Bar1"));
|
||||
table.getCell(1, 1).addContent(new Paragraph("Bar2"));
|
||||
table.getCell(1, 2).addContent(new Paragraph("Bar3"));
|
||||
table.getCell(1, 3).addContent(new Paragraph("Bar4"));
|
||||
table
|
||||
.getCell(1, 0)
|
||||
.addParagraph(new Paragraph("Bar1"))
|
||||
.setShading({
|
||||
fill: "b79c2f",
|
||||
val: ShadingType.REVERSE_DIAGONAL_STRIPE,
|
||||
color: "auto",
|
||||
});
|
||||
table
|
||||
.getCell(1, 1)
|
||||
.addParagraph(new Paragraph("Bar2"))
|
||||
.setShading({
|
||||
fill: "42c5f4",
|
||||
val: ShadingType.PERCENT_95,
|
||||
color: "auto",
|
||||
});
|
||||
table
|
||||
.getCell(1, 2)
|
||||
.addParagraph(new Paragraph("Bar3"))
|
||||
.setShading({
|
||||
fill: "880aa8",
|
||||
val: ShadingType.PERCENT_10,
|
||||
color: "e2df0b",
|
||||
});
|
||||
table
|
||||
.getCell(1, 3)
|
||||
.addParagraph(new Paragraph("Bar4"))
|
||||
.setShading({
|
||||
fill: "FF0000",
|
||||
val: ShadingType.CLEAR,
|
||||
color: "auto",
|
||||
});
|
||||
|
||||
table.getRow(0).mergeCells(0, 3);
|
||||
|
||||
doc.createParagraph("hi");
|
||||
|
||||
doc.createTable({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
width: 100,
|
||||
widthUnitType: WidthType.PERCENTAGE,
|
||||
});
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
|
@ -1,28 +1,25 @@
|
||||
// Example of how you would create a table with float positions
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import {
|
||||
Document,
|
||||
Packer,
|
||||
Paragraph,
|
||||
RelativeHorizontalPosition,
|
||||
RelativeVerticalPosition,
|
||||
TableAnchorType,
|
||||
WidthType,
|
||||
} from "../build";
|
||||
import { Document, Packer, Paragraph, RelativeHorizontalPosition, RelativeVerticalPosition, TableAnchorType, WidthType } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable(2, 2).float({
|
||||
horizontalAnchor: TableAnchorType.MARGIN,
|
||||
verticalAnchor: TableAnchorType.MARGIN,
|
||||
relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
|
||||
relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
|
||||
const table = doc.createTable({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
float: {
|
||||
horizontalAnchor: TableAnchorType.MARGIN,
|
||||
verticalAnchor: TableAnchorType.MARGIN,
|
||||
relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
|
||||
relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
|
||||
},
|
||||
width: 4535,
|
||||
widthUnitType: WidthType.DXA,
|
||||
});
|
||||
table.setFixedWidthLayout();
|
||||
table.setWidth(WidthType.DXA, 4535);
|
||||
|
||||
table.getCell(0, 0).addContent(new Paragraph("Hello"));
|
||||
table.getCell(0, 0).addParagraph(new Paragraph("Hello"));
|
||||
table.getRow(0).mergeCells(0, 1);
|
||||
|
||||
const packer = new Packer();
|
||||
|
18
demo/demo35.ts
Normal file
18
demo/demo35.ts
Normal file
@ -0,0 +1,18 @@
|
||||
// Example on how to add hyperlinks to websites
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
const paragraph = new Paragraph();
|
||||
const link = doc.createHyperlink("http://www.example.com", "Hyperlink");
|
||||
|
||||
link.bold();
|
||||
paragraph.addHyperLink(link);
|
||||
doc.addParagraph(paragraph);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
26
demo/demo36.ts
Normal file
26
demo/demo36.ts
Normal file
@ -0,0 +1,26 @@
|
||||
// Add image to table cell
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Media, Packer, Table } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||
|
||||
const table = new Table({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
});
|
||||
table.getCell(1, 1).addParagraph(image.Paragraph);
|
||||
|
||||
// doc.createParagraph("Hello World");
|
||||
doc.addTable(table);
|
||||
|
||||
// doc.Header.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||
doc.Header.addTable(table);
|
||||
// doc.Footer.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
18
demo/demo37.ts
Normal file
18
demo/demo37.ts
Normal file
@ -0,0 +1,18 @@
|
||||
// Add images to header and footer
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Media, Packer } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||
doc.createParagraph("Hello World");
|
||||
|
||||
doc.Header.addImage(image);
|
||||
doc.Header.createImage(fs.readFileSync("./demo/images/pizza.gif"));
|
||||
doc.Header.createImage(fs.readFileSync("./demo/images/image1.jpeg"));
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
// Example of how to add images to the document - You can use Buffers, UInt8Arrays or Base64 strings
|
||||
// Example of how to "wrap" text around an image
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
// import { Document, Packer, Paragraph } from "../build";
|
||||
|
33
demo/demo39.ts
Normal file
33
demo/demo39.ts
Normal file
@ -0,0 +1,33 @@
|
||||
// Example how to display page numbers
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, PageNumberFormat, TextRun } from "../build";
|
||||
|
||||
const doc = new Document(
|
||||
{},
|
||||
{
|
||||
pageNumberStart: 1,
|
||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||
},
|
||||
);
|
||||
|
||||
doc.Header.createParagraph("Foo Bar corp. ")
|
||||
.addRun(new TextRun("Page Number ").pageNumber())
|
||||
.addRun(new TextRun(" to ").numberOfTotalPages());
|
||||
|
||||
doc.Footer.createParagraph("Foo Bar corp. ")
|
||||
.center()
|
||||
.addRun(new TextRun("Page Number: ").pageNumber())
|
||||
.addRun(new TextRun(" to ").numberOfTotalPages());
|
||||
|
||||
doc.createParagraph("Hello World 1").pageBreak();
|
||||
doc.createParagraph("Hello World 2").pageBreak();
|
||||
doc.createParagraph("Hello World 3").pageBreak();
|
||||
doc.createParagraph("Hello World 4").pageBreak();
|
||||
doc.createParagraph("Hello World 5").pageBreak();
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
@ -5,8 +5,11 @@ import { Document, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable(4, 4);
|
||||
table.getCell(2, 2).addContent(new Paragraph("Hello"));
|
||||
const table = doc.createTable({
|
||||
rows: 4,
|
||||
columns: 4,
|
||||
});
|
||||
table.getCell(2, 2).addParagraph(new Paragraph("Hello"));
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
|
25
demo/demo40.ts
Normal file
25
demo/demo40.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// Example demonstrating line numbers.
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Paragraph, Packer, LineNumberRestartFormat } from "../build";
|
||||
|
||||
const doc = new Document(
|
||||
{},
|
||||
{
|
||||
lineNumberCountBy: 1,
|
||||
lineNumberRestart: LineNumberRestartFormat.CONTINUOUS,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
|
||||
doc.addParagraph(new Paragraph('Hello').heading1());
|
||||
doc.createParagraph("Himenaeos duis luctus nullam fermentum lobortis potenti vivamus non dis, sed facilisis ultricies scelerisque aenean risus hac senectus. Adipiscing id venenatis justo ante gravida placerat, ac curabitur dis pellentesque proin bibendum risus, aliquam porta taciti vulputate primis. Tortor ipsum fermentum quam vel convallis primis nisl praesent tincidunt, lobortis quisque felis vitae condimentum class ut sem nam, aenean potenti pretium ac amet lacinia himenaeos mi. Aliquam nisl turpis hendrerit est morbi malesuada, augue interdum mus inceptos curabitur tristique, parturient feugiat sodales nulla facilisi. Aliquam non pulvinar purus nulla ex integer, velit faucibus vitae at bibendum quam, risus elit aenean adipiscing posuere.");
|
||||
doc.createParagraph("Sed laoreet id mattis egestas nam mollis elit lacinia convallis dui tincidunt ultricies habitant, pharetra per maximus interdum neque tempor risus efficitur morbi imperdiet senectus. Lectus laoreet senectus finibus inceptos donec potenti fermentum, ultrices eleifend odio suscipit magnis tellus maximus nibh, ac sit nullam eget felis himenaeos. Diam class sem magnis aenean commodo faucibus id proin mi, nullam sodales nec mus parturient ornare ad inceptos velit hendrerit, bibendum placerat eleifend integer facilisis urna dictumst suspendisse.");
|
||||
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
51
demo/demo41.ts
Normal file
51
demo/demo41.ts
Normal file
@ -0,0 +1,51 @@
|
||||
// Multiple cells merging in the same table
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable({
|
||||
rows: 13,
|
||||
columns: 6,
|
||||
});
|
||||
let row = 0;
|
||||
table.getCell(row, 0).addContent(new Paragraph("0,0"));
|
||||
table.getCell(row, 1).addContent(new Paragraph("0,1"));
|
||||
table.getCell(row, 3).addContent(new Paragraph("0,3"));
|
||||
table.getCell(row, 4).addContent(new Paragraph("0,4"));
|
||||
table.getRow(row).mergeCells(4, 5);
|
||||
table.getRow(row).mergeCells(1, 2);
|
||||
row = 1;
|
||||
table.getCell(row, 0).addContent(new Paragraph("1,0"));
|
||||
table.getCell(row, 2).addContent(new Paragraph("1,2"));
|
||||
table.getCell(row, 4).addContent(new Paragraph("1,4"));
|
||||
table.getRow(row).mergeCells(4, 5);
|
||||
table.getRow(row).mergeCells(2, 3);
|
||||
table.getRow(row).mergeCells(0, 1);
|
||||
|
||||
row = 2;
|
||||
table.getCell(row, 0).addContent(new Paragraph("2,0"));
|
||||
table.getCell(row, 1).addContent(new Paragraph("2,1"));
|
||||
table.getCell(row, 2).addContent(new Paragraph("2,2"));
|
||||
table.getCell(row, 3).addContent(new Paragraph("2,3"));
|
||||
table.getCell(row, 4).addContent(new Paragraph("2,4"));
|
||||
table.getRow(row).mergeCells(4, 5);
|
||||
table.getRow(row).mergeCells(1, 2);
|
||||
row = 3;
|
||||
table.getCell(row, 0).addContent(new Paragraph("3,0"));
|
||||
table.getCell(row, 1).addContent(new Paragraph("3,1"));
|
||||
table.getCell(row, 2).addContent(new Paragraph("3,2"));
|
||||
table.getCell(row, 3).addContent(new Paragraph("3,3"));
|
||||
table.getCell(row, 4).addContent(new Paragraph("3,4"));
|
||||
table.getCell(row, 5).addContent(new Paragraph("3,5"));
|
||||
row = 4;
|
||||
table.getCell(row, 0).addContent(new Paragraph("4,0"));
|
||||
table.getCell(row, 5).addContent(new Paragraph("4,5"));
|
||||
table.getRow(row).mergeCells(0, 4);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
20
demo/demo43.ts
Normal file
20
demo/demo43.ts
Normal file
@ -0,0 +1,20 @@
|
||||
// Add image to table cell
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable({
|
||||
rows: 4,
|
||||
columns: 4,
|
||||
});
|
||||
table.getCell(2, 2).addParagraph(new Paragraph("Hello"));
|
||||
table.getColumn(3).mergeCells(1, 2);
|
||||
// table.getCell(3, 2).addParagraph(new Paragraph("Hello"));
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
@ -29,6 +29,7 @@ import * as docx from "docx";
|
||||
## Basic Usage
|
||||
|
||||
```js
|
||||
var fs = require("fs");
|
||||
var docx = require("docx");
|
||||
|
||||
// Create document
|
||||
@ -41,11 +42,12 @@ paragraph.addRun(new docx.TextRun("Lorem Ipsum Foo Bar"));
|
||||
doc.addParagraph(paragraph);
|
||||
|
||||
// Used to export the file into a .docx file
|
||||
var exporter = new docx.LocalPacker(doc);
|
||||
var packer = new docx.Packer();
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My First Document.docx", buffer);
|
||||
});
|
||||
|
||||
exporter.pack("My First Document");
|
||||
|
||||
// Done! A file called 'My First Document.docx' will be in your file system if you used LocalPacker
|
||||
// Done! A file called 'My First Document.docx' will be in your file system.
|
||||
```
|
||||
|
||||
## Honoured Mentions
|
||||
|
@ -15,8 +15,10 @@
|
||||
* [Headers & Footers](usage/headers-and-footers.md)
|
||||
* [Bullet Points](usage/bullet-points.md)
|
||||
* [Numbering](usage/numbering.md)
|
||||
* [Tables](usage/tables.md)
|
||||
* [Tab Stops](usage/tab-stops.md)
|
||||
* [Table of Contents](usage/table-of-contents.md)
|
||||
* [Page Numbers](usage/page-numbers.md)
|
||||
* Styling
|
||||
* [Styling with JS](usage/styling-with-js.md)
|
||||
* [Styling with XML](usage/styling-with-xml.md)
|
||||
|
@ -12,13 +12,13 @@
|
||||
|
||||
## Always think about the user
|
||||
|
||||
The number one pillar for contribution is to **ALWAYS** think about how the user will use the library.
|
||||
The number one pillar for contribution to `docx` is to **ALWAYS** think about how the user will use `docx`.
|
||||
|
||||
Put yourself in their position, and imagine how they would feel about your feature you wrote.
|
||||
|
||||
1. Is it easy to use?
|
||||
1. Is it easy to use?
|
||||
2. Has it been documented well?
|
||||
3. Is it intuative?
|
||||
3. Is it intuitive?
|
||||
4. Is it consistent with the rest of the API?
|
||||
5. Is it fun to use?
|
||||
|
||||
@ -37,32 +37,44 @@ Unesesary coment removed // Make sure to use correct spelling
|
||||
|
||||
## No leaky components in API interface
|
||||
|
||||
This mainly applies to the API the end user will consume.
|
||||
> This mainly applies to the API the end user will consume.
|
||||
|
||||
Try to make method parameters accept primatives, or `json` objects, so that child components are created **inside** the component, rather than being **injected** in.
|
||||
Try to make method parameters of the outside API accept primitives, or `json` objects, so that child components are created **inside** the component, rather than being **injected** in.
|
||||
|
||||
This is so that:
|
||||
|
||||
1. Imports are much cleaner, no need for:
|
||||
1. Imports are much cleaner for the end user, no need for:
|
||||
```js
|
||||
import { ChildComponent } from "./my-feature/sub-component/deeper/.../my-deep.component";
|
||||
```
|
||||
|
||||
|
||||
2. This is what I consider "leakage". The code is aware of the underlying implementation of the component.
|
||||
3. It means the end user does not need to import and create the child component to be injected.
|
||||
|
||||
**Do not**
|
||||
`TableFloatProperties` is a class. The outside world would have to construct the object, and inject it in
|
||||
|
||||
`TableFloatProperties` is a class. The outside world would have to `new` up the object, and inject it in like so:
|
||||
|
||||
```js
|
||||
public float(tableFloatProperties: TableFloatProperties): Table
|
||||
```
|
||||
|
||||
```ts
|
||||
table.float(new TableFloatProperties(...));
|
||||
```
|
||||
|
||||
**Do**
|
||||
`ITableFloatOptions` is an interface for a JSON of primatives.
|
||||
|
||||
`ITableFloatOptions` is an interface for a JSON of primitives. The end user would need to pass in a json object and not need to worry about the internals:
|
||||
|
||||
```js
|
||||
public float(tableFloatOptions: ITableFloatOptions): Table
|
||||
```
|
||||
|
||||
```ts
|
||||
table.float({...});
|
||||
```
|
||||
|
||||
## Add vs Create
|
||||
|
||||
This is just a guideline, and the rules can sometimes be broken.
|
||||
@ -76,7 +88,8 @@ This is just a guideline, and the rules can sometimes be broken.
|
||||
}
|
||||
```
|
||||
|
||||
* Use `add` if you add the element into the method as a parameter:
|
||||
* Use `add` if you add the element into the method as a parameter.
|
||||
*Note:* This may look like its breaking the previous guideline, but it has semantically different meanings. The previous one is using data to construct an object, whereas this one is simply adding elements into the document:
|
||||
|
||||
```js
|
||||
public addParagraph(paragraph: Paragraph) {
|
||||
@ -108,6 +121,30 @@ private get _level: string;
|
||||
private get level: string;
|
||||
```
|
||||
|
||||
## Temporal Methods
|
||||
|
||||
Some methods are `non-temporal`, which means regardless of when you call the method, it will have the same affect on the document. For example, setting the width of a table at the end of the document will have the same effect as setting the width at the start:
|
||||
|
||||
```ts
|
||||
table.setWidth(1000); // now removed as of version 5.0.0
|
||||
```
|
||||
|
||||
Whereas some methods are `temporal`, which means depending on the time-frame they are called, it would produce a difference result. For example, moving `createParagraph()` around your code will physically alter the document.
|
||||
|
||||
```ts
|
||||
doc.createParagraph("hello");
|
||||
```
|
||||
|
||||
If a method is `non-temporal`, put it in the objects `constructor`. For example:
|
||||
|
||||
```ts
|
||||
const table = new Table(width: number);
|
||||
```
|
||||
|
||||
`Non-temporal` methods are usually methods which can only be used one time and one time only. For example, `.float()`. It does not make sense to call `.float()` again if its already floating.
|
||||
|
||||
I am not sure what the real term is, but this will do.
|
||||
|
||||
## Interfaces over type alias
|
||||
|
||||
Do not use `type`, but rather use `Interfaces`. `type` cannot be extended, and a class cannot implement it.
|
||||
@ -154,7 +191,7 @@ enum WeaponType = {
|
||||
}
|
||||
```
|
||||
|
||||
## Spell correctly, full and in American English
|
||||
## Spell correctly, in full and in American English
|
||||
|
||||
I am not sure where these habits in software development come from, but I do not believe it is beneficial:
|
||||
|
||||
|
@ -14,7 +14,7 @@ This command will run the `demo selector app` in the `/demo` folder. It will pro
|
||||
|
||||
A simple hello world of the `docx` library:
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo1.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo1.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo1.ts_
|
||||
|
||||
@ -24,7 +24,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo1.ts_
|
||||
|
||||
This example shows how to customise the look and feel of a document using JS configuration
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo2.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo2.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo2.ts_
|
||||
|
||||
@ -32,7 +32,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo2.ts_
|
||||
|
||||
This example shows how to customise the look and feel of a document using XML configuration
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo13.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo13.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts_
|
||||
|
||||
@ -40,7 +40,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts_
|
||||
|
||||
This example shows many levels of numbering
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo3.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo3.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo3.ts_
|
||||
|
||||
@ -48,7 +48,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo3.ts_
|
||||
|
||||
Example of simple table
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
|
||||
|
||||
@ -56,7 +56,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
|
||||
|
||||
Styling the borders of a table
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
|
||||
|
||||
@ -66,7 +66,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
|
||||
|
||||
Importing Images from file system path
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
|
||||
|
||||
@ -74,7 +74,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
|
||||
|
||||
Example showing how to add image to headers and footers
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
|
||||
|
||||
@ -82,7 +82,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
|
||||
|
||||
Example showing how to scale images
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo12.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo12.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo12.ts_
|
||||
|
||||
@ -90,7 +90,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo12.ts_
|
||||
|
||||
This is the best way to add an image to a document because you can add the same image in two locations without increasing document size by re-using the same image
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo23.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo23.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo23.ts_
|
||||
|
||||
@ -98,7 +98,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo23.ts_
|
||||
|
||||
As before, to add an image to a table, you would need to add it to the `Media` object first
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
|
||||
|
||||
@ -106,15 +106,15 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
|
||||
|
||||
If you want to use a Base64 image instead
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo18.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo18.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo18.ts_
|
||||
|
||||
## Margins
|
||||
|
||||
Example showing how to set custom margains
|
||||
Example showing how to set custom margins
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo6.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo6.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo6.ts_
|
||||
|
||||
@ -122,7 +122,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo6.ts_
|
||||
|
||||
Example showing how to set the document to `landscape` or `portrait`
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo7.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo7.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo7.ts_
|
||||
|
||||
@ -130,7 +130,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo7.ts_
|
||||
|
||||
Example showing how to add headers and footers
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo8.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo8.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo8.ts_
|
||||
|
||||
@ -144,7 +144,7 @@ Check out `Sections` for this feature
|
||||
|
||||
Example showing how to page break
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo14.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo14.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo14.ts_
|
||||
|
||||
@ -152,15 +152,16 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo14.ts_
|
||||
|
||||
Example showing how to page break before like in Word
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo15.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo15.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo15.ts_
|
||||
|
||||
## Sections
|
||||
|
||||
Example of how sections work. Sections allow multiple headers and footers, and `landscape`/`portrait` inside the same document
|
||||
Example of how sections work. Sections allow multiple headers and footers, and `landscape`/`portrait` inside the same document.
|
||||
Also you can have different page number formats and starts for different sections.
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo16.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo16.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo16.ts_
|
||||
|
||||
@ -168,7 +169,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo16.ts_
|
||||
|
||||
Example of how to add footnotes. Good for references
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo17.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo17.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo17.ts_
|
||||
|
||||
@ -178,7 +179,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo17.ts_
|
||||
|
||||
Example showing how to use the Buffer packer and then write that buffer to the file system
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo19.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo19.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo19.ts_
|
||||
|
||||
@ -187,7 +188,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo19.ts_
|
||||
|
||||
Example showing how to make bookmarks to make internal hyperlinks within the document
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo21.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo21.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo21.ts_
|
||||
|
||||
@ -195,7 +196,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo21.ts_
|
||||
|
||||
Example showing how to use bidirectional text for certain languages such as Hebrew
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo22.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo22.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo22.ts_
|
||||
|
||||
@ -205,7 +206,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo22.ts_
|
||||
|
||||
Example showing how to add headers and footers
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo10.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo10.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo10.ts_
|
||||
|
||||
@ -213,6 +214,6 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo10.ts_
|
||||
|
||||
This example shows how to customise the look and feel of a document and add images
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo11.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo11.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo11.ts_
|
||||
|
@ -25,6 +25,7 @@
|
||||
<script src="//unpkg.com/docsify/lib/plugins/emoji.min.js"></script>
|
||||
<script src="https://unpkg.com/docsify-copy-code@2"></script>
|
||||
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
|
||||
<script src="//unpkg.com/prismjs/components/prism-typescript.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -5,7 +5,7 @@
|
||||
To create a new document, it is very easy:
|
||||
|
||||
```js
|
||||
var doc = new docx.Document();
|
||||
const doc = new docx.Document();
|
||||
```
|
||||
|
||||
## Document properties
|
||||
@ -13,7 +13,7 @@ var doc = new docx.Document();
|
||||
You can add properties to the Word document by specifying options, for example:
|
||||
|
||||
```js
|
||||
var doc = new docx.Document({
|
||||
const doc = new docx.Document({
|
||||
creator: "Dolan Miu",
|
||||
description: "My extremely interesting document",
|
||||
title: "My Document",
|
||||
@ -22,14 +22,18 @@ var doc = new docx.Document({
|
||||
|
||||
### Full list of options:
|
||||
|
||||
```
|
||||
creator
|
||||
description
|
||||
title
|
||||
subject
|
||||
keywords
|
||||
lastModifiedBy
|
||||
revision
|
||||
```
|
||||
|
||||
* creator
|
||||
* description
|
||||
* title
|
||||
* subject
|
||||
* keywords
|
||||
* lastModifiedBy
|
||||
* revision
|
||||
|
||||
You can mix and match whatever properties you want, or provide no properties.
|
||||
|
||||
### units for positioning
|
||||
|
||||
Various parts of the API require positioning arguments. The units are "20ths of a point" from the [OOXML](http://officeopenxml.com/index.php) specification.
|
||||
See [Lars Corneliussen's blog post](https://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/) for more information and how to convert units.
|
@ -23,7 +23,7 @@ doc.Header.createImage([BUFFER_OF_YOUR_IMAGE]);
|
||||
doc.Footer.createImage([BUFFER_OF_YOUR_IMAGE]);
|
||||
```
|
||||
|
||||
Refer to `demo8.js` for more information
|
||||
Refer to [`demo8.ts`](https://github.com/dolanmiu/docx/blob/master/demo/demo8.ts) for more information.
|
||||
|
||||
## Multiple Headers and Footers
|
||||
|
||||
@ -37,8 +37,12 @@ Also all the supported section properties are implemented according to: http://o
|
||||
|
||||
// Add new section with another header and footer
|
||||
doc.addSection({
|
||||
headerId: header.Header.ReferenceId,
|
||||
footerId: footer.Footer.ReferenceId,
|
||||
headers: {
|
||||
default: header
|
||||
},
|
||||
footers: {
|
||||
default: footer
|
||||
},
|
||||
pageNumberStart: 1,
|
||||
pageNumberFormatType: docx.PageNumberFormat.DECIMAL,
|
||||
});
|
||||
|
47
docs/usage/hyperlinks.md
Normal file
47
docs/usage/hyperlinks.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Hyperlinks
|
||||
|
||||
There are two types of hyperlinks: internal (pointing to a bookmark inside the document) and external (pointing to an external url).
|
||||
|
||||
## Internal
|
||||
|
||||
To create an internal hyperlink you need first to create a bookmark (the paragraph that will be the destination of the hyperlink) with `doc.createBookmark(anchor, text)`.
|
||||
|
||||
A bookmark is composed of an anchor (an identifier) and the text displayed. After creating a bookmark just add it to a paragraph with `paragraph.addBookmark(bookmark)`
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const paragraph = this.doc.createParagraph();
|
||||
const bookmark = this.doc.createBookmark('anchorForChapter1', 'This is chapter1');
|
||||
paragraph.addBookmark(bookmark);
|
||||
```
|
||||
|
||||
Then you can create an hyperlink pointing to that bookmark with `doc.createInternalHyperLink(anchor,text)`:
|
||||
|
||||
```ts
|
||||
const paragraph = this.doc.createParagraph();
|
||||
const link = this.doc.createInternalHyperLink('anchorForChapter1', 'This is a link to chapter1');
|
||||
paragraph.addHyperLink(link);
|
||||
```
|
||||
|
||||
## External
|
||||
|
||||
To create an external hyperlink you just need to specify the url and the text of the link, then add it to a paragraph with `doc.createHyperlink(url, text)`:
|
||||
|
||||
```ts
|
||||
const paragraph = this.doc.createParagraph();
|
||||
const link = this.doc.createHyperlink('https://docx.js.org', 'This is an external link');
|
||||
paragraph.addHyperLink(link);
|
||||
```
|
||||
|
||||
|
||||
## Styling an hyperlink
|
||||
|
||||
It is possible to set the style of the text of an hyperlink. This can be done applying run formatting on `TextRun` property of the hyperlink.
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
const link = this.doc.createHyperlink('https://docx.js.org', 'This is an external link');
|
||||
link.TextRun.bold().italics()
|
||||
```
|
@ -210,7 +210,7 @@ doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
|
||||
|
||||
Importing Images from file system path
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
|
||||
|
||||
@ -218,7 +218,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
|
||||
|
||||
Example showing how to add image to headers and footers
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
|
||||
|
||||
@ -226,6 +226,6 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
|
||||
|
||||
Example showing how to float images on top of text and optimally give a `margin`
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo38.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo38.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo38.ts_
|
||||
|
@ -26,7 +26,7 @@ _levels_ which form a sequence starting at 0 indicating the top-level
|
||||
list look and increasing from there to descibe the sublists, then
|
||||
sub-sublists, etc. Each level includes the following properties:
|
||||
|
||||
* **level**: This its 0-based index in the defintion stack
|
||||
* **level**: This is its 0-based index in the definition stack
|
||||
* **numberFormat**: This indicates how the bullet or number should be
|
||||
generated. Options include `bullet` (meaning don't count), `decimal`
|
||||
(arabic numerals), `upperRoman`, `lowerRoman`, `hex`, and many
|
||||
@ -64,8 +64,8 @@ styles or numbering of any kind, you'll have to use the
|
||||
`docx.Numbering` class.
|
||||
|
||||
First you need to create a new numbering container class and use it to
|
||||
create your abstract numbering style, define your levels, and creat
|
||||
your concreate numbering style:
|
||||
create your abstract numbering style, define your levels, and create
|
||||
your concrete numbering style:
|
||||
|
||||
```js
|
||||
const numbering = new docx.Numbering();
|
||||
@ -78,8 +78,8 @@ abstractNum.createLevel(2, "lowerLetter", "%3)", "start").addParagraphProperty(n
|
||||
const concrete = numbering.createConcreteNumbering(abstractNum);
|
||||
```
|
||||
|
||||
You can then apply your concrete style to paragraphs using their
|
||||
`#setNumbering` method:
|
||||
You can then apply your concrete style to paragraphs using the
|
||||
`setNumbering` method:
|
||||
|
||||
```js
|
||||
topLevelP.setNumbering(concrete, 0);
|
||||
|
66
docs/usage/page-numbers.md
Normal file
66
docs/usage/page-numbers.md
Normal file
@ -0,0 +1,66 @@
|
||||
# Page Numbers
|
||||
|
||||
> This feature allows you to set page numbers on each page
|
||||
|
||||
?> **Note:** This feature only works on Headers and Footers
|
||||
|
||||
```ts
|
||||
doc.Header.createParagraph().addRun(new TextRun("Page Number: ").pageNumber()).addRun(new TextRun("to ").numberOfTotalPages());
|
||||
```
|
||||
|
||||
## Current page number
|
||||
|
||||
To get the current page number, call the `.pageNumber()` method on a `TextRun`. Then add the newly created `TextRun` into a paragraph
|
||||
|
||||
```ts
|
||||
pageNumber();
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const currentPageRun = new TextRun("Current Page Number: ").pageNumber();
|
||||
paragraph.addRun(currentPageRun);
|
||||
```
|
||||
|
||||
## Total number of pages
|
||||
|
||||
```ts
|
||||
numberOfTotalPages();
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const lastPage = new TextRun("Total Page Number: ").numberOfTotalPages();
|
||||
paragraph.addRun(lastPage);
|
||||
```
|
||||
|
||||
|
||||
## Both
|
||||
|
||||
You can combine the two to get "Page 2 of 10" effect:
|
||||
|
||||
```ts
|
||||
const currentPageRun = new TextRun("Page ").pageNumber();
|
||||
const lastPage = new TextRun("of ").numberOfTotalPages();
|
||||
|
||||
paragraph.addRun(currentPageRun);
|
||||
paragraph.addRun(lastPage);
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
```ts
|
||||
doc.Header.createParagraph().addRun(new TextRun("Page ").pageNumber()).addRun(new TextRun("of ").numberOfTotalPages());
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple Example
|
||||
|
||||
Adding page numbers to Header and Footer
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo39.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo39.ts_
|
@ -76,6 +76,22 @@ paragraph.heading1().center();
|
||||
|
||||
The above will create a `heading 1` which is `centered`.
|
||||
|
||||
### Justified text with breaks
|
||||
|
||||
When a paragraph is justified, you may want to not justify the contents of incomplete lines, which end in a soft line break.
|
||||
|
||||

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

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

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

|
||||
|
||||
|
@ -71,6 +71,6 @@ doc.addParagraph(new Paragraph("My Spectacular Style #1").style("MySpectacularSt
|
||||
|
||||
### Complete example
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo28.ts ":include")
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo28.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo28.ts_
|
||||
|
305
docs/usage/tables.md
Normal file
305
docs/usage/tables.md
Normal file
@ -0,0 +1,305 @@
|
||||
# Tables
|
||||
|
||||
You can create tables with `docx`. More information can be found [here](http://officeopenxml.com/WPtable.php).
|
||||
|
||||
## Create Table
|
||||
|
||||
To create a table, simply use the `createTable()` method on a `document`.
|
||||
|
||||
```ts
|
||||
const table = doc.createTable([NUMBER OF ROWS], [NUMBER OF COLUMNS]);
|
||||
```
|
||||
|
||||
Alternatively, you can create a table object directly, and then add it in the `document`
|
||||
|
||||
```ts
|
||||
const table = new Table(4, 4);
|
||||
doc.addTable(table);
|
||||
```
|
||||
|
||||
The snippet below creates a table of 2 rows and 4 columns.
|
||||
|
||||
```ts
|
||||
const table = doc.createTable(2, 4);
|
||||
|
||||
// Or
|
||||
|
||||
const table = new Table(2, 4);
|
||||
doc.addTable(table);
|
||||
```
|
||||
|
||||
## Rows and Columns
|
||||
|
||||
You can get a row or a column from a table like so, where `index` is a number:
|
||||
|
||||
### Get Row
|
||||
|
||||
```ts
|
||||
const row = doc.getRow(index);
|
||||
```
|
||||
|
||||
With this, you can merge a row by using the `mergeCells()` method, where `startIndex` is the row number you want to merge from, and `endIndex` is where you want it to merge to:
|
||||
|
||||
```ts
|
||||
row.mergeCells(startIndex, endIndex);
|
||||
```
|
||||
|
||||
You can get a cell from a `row` by using the `getCell()` method, where `index` is the row index:
|
||||
|
||||
```ts
|
||||
row.getCell(index);
|
||||
```
|
||||
|
||||
### Get Column
|
||||
|
||||
```ts
|
||||
const column = doc.getColumn(index);
|
||||
```
|
||||
|
||||
Again, you can merge a row by using the `mergeCells()` method, where `startIndex` is the row number you want to merge from, and `endIndex` is where you want it to merge to:
|
||||
|
||||
```ts
|
||||
column.mergeCells(startIndex, endIndex);
|
||||
```
|
||||
|
||||
You can get a cell from a `column` by using the `getCell()` method, where `index` is the column index:
|
||||
|
||||
```ts
|
||||
column.getCell(index);
|
||||
```
|
||||
|
||||
## Cells
|
||||
|
||||
The `createTable()` method created a table with cells. To access the cell, use the `getCell()` method.
|
||||
|
||||
```ts
|
||||
const cell = table.getCell([ROW INDEX], [COLUMN INDEX]);
|
||||
```
|
||||
|
||||
You can also get a cell from a `column` or a `row` with `getCell()`, mentioned previously.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const cell = table.getCell(0, 2);
|
||||
|
||||
const cell = row.getCell(0);
|
||||
|
||||
const cell = column.getCell(2);
|
||||
```
|
||||
|
||||
### Add paragraph to a cell
|
||||
|
||||
Once you have got the cell, you can add data to it with the `addParagraph()` method.
|
||||
|
||||
```ts
|
||||
cell.addParagraph(new Paragraph("Hello"));
|
||||
```
|
||||
|
||||
### Set width of a cell
|
||||
|
||||
You can specify the width of a cell using:
|
||||
|
||||
`cell.Properties.setWidth(width, format)`
|
||||
|
||||
format can be:
|
||||
* WidthType.AUTO
|
||||
* WidthType.DXA: value is in twentieths of a point
|
||||
* WidthType.NIL: is considered as zero
|
||||
* WidthType.PCT: percent of table width
|
||||
|
||||
### Example
|
||||
|
||||
```ts
|
||||
cell.Properties.setWidth(100, WidthType.DXA);
|
||||
```
|
||||
|
||||
```ts
|
||||
cell.Properties.setWidth('50%', WidthType.PCT);
|
||||
```
|
||||
|
||||
## Borders
|
||||
|
||||
BorderStyle can be imported from `docx`. Size determines the thickness. HTML color can be a hex code or alias such as `red`.
|
||||
|
||||
```ts
|
||||
cell.Borders.addTopBorder([BorderStyle], [SIZE], [HTML COLOR]);
|
||||
```
|
||||
|
||||
```ts
|
||||
cell.Borders.addBottomBorder([BorderStyle], [SIZE], [HTML COLOR]);
|
||||
```
|
||||
|
||||
```ts
|
||||
cell.Borders.addStartBorder([[BorderStyle]], [SIZE], [HTML COLOR]);
|
||||
```
|
||||
|
||||
```ts
|
||||
cell.Borders.addEndBorder([BorderStyle], [SIZE], [HTML COLOR]);
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```ts
|
||||
import { BorderStyle } from "docx";
|
||||
|
||||
cell.Borders.addStartBorder(BorderStyle.DOT_DOT_DASH, 3, "green");
|
||||
cell.Borders.addEndBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000");
|
||||
```
|
||||
|
||||
### Google DOCS
|
||||
|
||||
Google DOCS does not support start and end borders, instead they use left and right borders. So to set left and right borders for Google DOCS you should use:
|
||||
|
||||
```ts
|
||||
import { BorderStyle } from "docx";
|
||||
|
||||
cell.Borders.addLeftBorder(BorderStyle.DOT_DOT_DASH, 3, "green");
|
||||
cell.Borders.addRightBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000");
|
||||
```
|
||||
|
||||
## Set Width
|
||||
|
||||
```ts
|
||||
import { WidthType } from "docx";
|
||||
|
||||
table.setWidth([WIDTH], [OPTIONAL WidthType. Defaults to DXA]);
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
table.setWidth(4535, WidthType.DXA);
|
||||
```
|
||||
|
||||
## Vertical Align
|
||||
|
||||
Sets the vertical alignment of the contents of the cell
|
||||
|
||||
```ts
|
||||
import { VerticalAlign } from "docx";
|
||||
|
||||
cell.setVerticalAlign([VerticalAlign TYPE]);
|
||||
```
|
||||
|
||||
For example, to center align a cell:
|
||||
|
||||
```ts
|
||||
cell.setVerticalAlign(VerticalAlign.CENTER);
|
||||
```
|
||||
|
||||
## Rows
|
||||
|
||||
To get a row, use the `getRow` method on a `table`. There are a handful of methods which you can apply to a row which will be explained below.
|
||||
|
||||
```ts
|
||||
table.getRow([ROW INDEX]);
|
||||
```
|
||||
|
||||
## Merge cells together
|
||||
|
||||
### Merging on a row
|
||||
|
||||
First obtain the row, and call `mergeCells()`. The first argument is where the merge should start. The second argument is where the merge should end.
|
||||
|
||||
```ts
|
||||
table.getRow(0).mergeCells([FROM INDEX], [TO INDEX]);
|
||||
```
|
||||
|
||||
#### Example
|
||||
|
||||
This will merge 3 cells together starting from index `0`:
|
||||
|
||||
```ts
|
||||
table.getRow(0).mergeCells(0, 2);
|
||||
```
|
||||
|
||||
### Merging on a column
|
||||
|
||||
It has not been implemented yet, but it will follow a similar structure as merging a row.
|
||||
|
||||
## Nested Tables
|
||||
|
||||
To have a table within a table
|
||||
|
||||
```ts
|
||||
cell.addTable(new Table(1, 1));
|
||||
```
|
||||
|
||||
## Pagination
|
||||
|
||||
###Prevent row pagination
|
||||
To prevent breaking contents of a row across multiple pages, call `cantSplit()`:
|
||||
|
||||
```ts
|
||||
table.getRow(0).setCantSplit();
|
||||
```
|
||||
|
||||
###Repeat row
|
||||
If a table is paginated on multiple pages, it is possible to repeat a row at the top of each new page calling `setTableHeader()`:
|
||||
|
||||
```ts
|
||||
table.getRow(0).setTableHeader();
|
||||
```
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
|
||||
|
||||
### Custom borders
|
||||
|
||||
Example showing how to add colourful borders to tables
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
|
||||
|
||||
### Adding images
|
||||
|
||||
Example showing how to add images to tables
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo36.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo36.ts_
|
||||
|
||||
### Alignment of text in a cell
|
||||
|
||||
Example showing how align text in a table cell
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo31.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo31.ts_
|
||||
|
||||
### Merging rows
|
||||
|
||||
Example showing merging of `rows`
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo32.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo32.ts_
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo41.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo41.ts_
|
||||
|
||||
### Merging columns
|
||||
|
||||
Example showing merging of `columns`
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo43.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo43.ts_
|
||||
|
||||
### Floating tables
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo34.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo34.ts_
|
||||
|
@ -22,7 +22,7 @@ text.bold();
|
||||
### Italics
|
||||
|
||||
```js
|
||||
text.italic();
|
||||
text.italics();
|
||||
```
|
||||
|
||||
### Underline
|
||||
@ -80,5 +80,5 @@ text.break();
|
||||
What if you want to create a paragraph which is **_bold_** and **_italic_**?
|
||||
|
||||
```js
|
||||
paragraph.bold().italic();
|
||||
paragraph.bold().italics();
|
||||
```
|
||||
|
17
package.json
17
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docx",
|
||||
"version": "4.6.0",
|
||||
"version": "5.0.0-rc3",
|
||||
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
@ -8,7 +8,7 @@
|
||||
"test": "mocha-webpack \"src/**/*.ts\"",
|
||||
"test.coverage": "nyc npm test",
|
||||
"test.watch": "npm test -- --watch",
|
||||
"prepublishOnly": "npm run build",
|
||||
"prepublishOnly": "npm run build --production",
|
||||
"lint": "tslint --project .",
|
||||
"build": "npm run webpack && npm run fix-types",
|
||||
"tsc": "rimraf ./build && tsc -p .",
|
||||
@ -17,7 +17,9 @@
|
||||
"typedoc": "typedoc src/index.ts",
|
||||
"style": "prettier -l \"src/**/*.ts\"",
|
||||
"style.fix": "npm run style -- --write",
|
||||
"fix-types": "node types-absolute-fixer.js",
|
||||
"fix-types": "ts-node scripts/types-absolute-fixer.ts",
|
||||
"e2e": "ts-node scripts/e2e.ts",
|
||||
"serve.docs": "cd docs && docsify serve",
|
||||
"ts-node": "ts-node"
|
||||
},
|
||||
"pre-commit": [
|
||||
@ -47,9 +49,7 @@
|
||||
],
|
||||
"types": "./build/index.d.ts",
|
||||
"dependencies": {
|
||||
"@types/image-size": "0.0.29",
|
||||
"@types/jszip": "^3.1.4",
|
||||
"image-size": "^0.6.2",
|
||||
"jszip": "^3.1.5",
|
||||
"xml": "^1.0.1",
|
||||
"xml-js": "^1.6.8"
|
||||
@ -63,9 +63,12 @@
|
||||
"devDependencies": {
|
||||
"@types/chai": "^3.4.35",
|
||||
"@types/mocha": "^2.2.39",
|
||||
"@types/request-promise": "^4.1.42",
|
||||
"@types/sinon": "^4.3.1",
|
||||
"@types/webpack": "^4.4.24",
|
||||
"awesome-typescript-loader": "^3.4.1",
|
||||
"chai": "^3.5.0",
|
||||
"docsify": "^4.9.1",
|
||||
"glob": "^7.1.2",
|
||||
"istanbul-instrumenter-loader": "^3.0.1",
|
||||
"jszip": "^3.1.5",
|
||||
@ -73,9 +76,11 @@
|
||||
"mocha-webpack": "^1.0.1",
|
||||
"nyc": "^13.1.0",
|
||||
"pre-commit": "^1.2.2",
|
||||
"prettier": "^1.12.1",
|
||||
"prettier": "^1.15.2",
|
||||
"prompt": "^1.0.0",
|
||||
"replace-in-file": "^3.1.0",
|
||||
"request": "^2.88.0",
|
||||
"request-promise": "^4.2.2",
|
||||
"rimraf": "^2.5.2",
|
||||
"shelljs": "^0.7.7",
|
||||
"sinon": "^5.0.7",
|
||||
|
30
scripts/e2e.ts
Normal file
30
scripts/e2e.ts
Normal file
@ -0,0 +1,30 @@
|
||||
// tslint:disable:no-console
|
||||
import * as fs from "fs";
|
||||
import * as request from "request-promise";
|
||||
|
||||
async function e2e(filePath: string): Promise<void> {
|
||||
console.log(`Running e2e for: ${filePath}`);
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
console.error("File not found");
|
||||
throw Error("File not found");
|
||||
}
|
||||
|
||||
const result = await request.post({
|
||||
url: "https://wt-9017166451e5dc00461b648d19f5e8da-0.sandbox.auth0-extend.com/docx-validator",
|
||||
formData: {
|
||||
document: fs.createReadStream(filePath),
|
||||
},
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
e2e(process.argv[2])
|
||||
.then(() => {
|
||||
console.log("Success! Document is valid");
|
||||
})
|
||||
.catch(() => {
|
||||
console.log("Error! Validation failed");
|
||||
process.exit(1);
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
const glob = require("glob");
|
||||
const replace = require("replace-in-file");
|
||||
import * as glob from "glob";
|
||||
import * as replace from "replace-in-file";
|
||||
|
||||
const files = glob.sync("build/**/*.d.ts");
|
||||
|
||||
@ -14,7 +14,7 @@ for (const file of files) {
|
||||
.fill("../")
|
||||
.join("");
|
||||
|
||||
return `"${backLevels}${matchSlug}"`;
|
||||
return `"${backLevels}${matchSlug}"`;
|
||||
},
|
||||
});
|
||||
}
|
@ -31,7 +31,7 @@ describe("Formatter", () => {
|
||||
const paragraph = new file.Paragraph();
|
||||
paragraph.addRun(new file.TextRun("test").bold());
|
||||
const newJson = formatter.format(paragraph);
|
||||
assert.isDefined(newJson["w:p"][1]["w:r"][0]["w:rPr"][0]["w:b"][0]._attr["w:val"]);
|
||||
assert.isDefined(newJson["w:p"][0]["w:r"][0]["w:rPr"][0]["w:b"]._attr["w:val"]);
|
||||
});
|
||||
|
||||
it("should format attributes (rsidSect)", () => {
|
||||
|
17
src/export/packer/image-replacer.ts
Normal file
17
src/export/packer/image-replacer.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { IMediaData, Media } from "file/media";
|
||||
|
||||
export class ImageReplacer {
|
||||
public replace(xmlData: string, mediaData: IMediaData[], offset: number): string {
|
||||
let currentXmlData = xmlData;
|
||||
|
||||
mediaData.forEach((image, i) => {
|
||||
currentXmlData = currentXmlData.replace(`{${image.fileName}}`, (offset + i).toString());
|
||||
});
|
||||
|
||||
return currentXmlData;
|
||||
}
|
||||
|
||||
public getMediaData(xmlData: string, media: Media): IMediaData[] {
|
||||
return media.Array.filter((image) => xmlData.search(`{${image.fileName}}`) > 0);
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ describe("Compiler", () => {
|
||||
describe("#compile()", () => {
|
||||
it("should pack all the content", async function() {
|
||||
this.timeout(99999999);
|
||||
const zipFile = await compiler.compile(file);
|
||||
const zipFile = compiler.compile(file);
|
||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||
|
||||
expect(fileNames).is.an.instanceof(Array);
|
||||
@ -46,7 +46,7 @@ describe("Compiler", () => {
|
||||
|
||||
this.timeout(99999999);
|
||||
|
||||
const zipFile = await compiler.compile(file);
|
||||
const zipFile = compiler.compile(file);
|
||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||
|
||||
expect(fileNames).is.an.instanceof(Array);
|
||||
|
@ -3,6 +3,7 @@ import * as xml from "xml";
|
||||
|
||||
import { File } from "file";
|
||||
import { Formatter } from "../formatter";
|
||||
import { ImageReplacer } from "./image-replacer";
|
||||
|
||||
interface IXmlifyedFile {
|
||||
readonly data: string;
|
||||
@ -28,14 +29,15 @@ interface IXmlifyedFileMapping {
|
||||
|
||||
export class Compiler {
|
||||
private readonly formatter: Formatter;
|
||||
private readonly imageReplacer: ImageReplacer;
|
||||
|
||||
constructor() {
|
||||
this.formatter = new Formatter();
|
||||
this.imageReplacer = new ImageReplacer();
|
||||
}
|
||||
|
||||
public async compile(file: File): Promise<JSZip> {
|
||||
public compile(file: File): JSZip {
|
||||
const zip = new JSZip();
|
||||
|
||||
const xmlifiedFileMapping = this.xmlifyFile(file);
|
||||
|
||||
for (const key in xmlifiedFileMapping) {
|
||||
@ -59,26 +61,39 @@ export class Compiler {
|
||||
zip.file(`word/media/${data.fileName}`, mediaData);
|
||||
}
|
||||
|
||||
for (const header of file.Headers) {
|
||||
for (const data of header.Media.Array) {
|
||||
zip.file(`word/media/${data.fileName}`, data.stream);
|
||||
}
|
||||
}
|
||||
|
||||
for (const footer of file.Footers) {
|
||||
for (const data of footer.Media.Array) {
|
||||
zip.file(`word/media/${data.fileName}`, data.stream);
|
||||
}
|
||||
}
|
||||
|
||||
return zip;
|
||||
}
|
||||
|
||||
private xmlifyFile(file: File): IXmlifyedFileMapping {
|
||||
file.verifyUpdateFields();
|
||||
const documentRelationshipCount = file.DocumentRelationships.RelationshipCount + 1;
|
||||
|
||||
return {
|
||||
Relationships: {
|
||||
data: (() => {
|
||||
const xmlData = xml(this.formatter.format(file.Document));
|
||||
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
|
||||
|
||||
mediaDatas.forEach((mediaData, i) => {
|
||||
file.DocumentRelationships.createRelationship(
|
||||
documentRelationshipCount + i,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
`media/${mediaData.fileName}`,
|
||||
);
|
||||
});
|
||||
|
||||
return xml(this.formatter.format(file.DocumentRelationships));
|
||||
})(),
|
||||
path: "word/_rels/document.xml.rels",
|
||||
},
|
||||
Document: {
|
||||
data: xml(this.formatter.format(file.Document), true),
|
||||
data: (() => {
|
||||
const tempXmlData = xml(this.formatter.format(file.Document), true);
|
||||
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
|
||||
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, documentRelationshipCount);
|
||||
|
||||
return xmlData;
|
||||
})(),
|
||||
path: "word/document.xml",
|
||||
},
|
||||
Styles: {
|
||||
@ -98,30 +113,66 @@ export class Compiler {
|
||||
data: xml(this.formatter.format(file.Numbering)),
|
||||
path: "word/numbering.xml",
|
||||
},
|
||||
Relationships: {
|
||||
data: xml(this.formatter.format(file.DocumentRelationships)),
|
||||
path: "word/_rels/document.xml.rels",
|
||||
},
|
||||
FileRelationships: {
|
||||
data: xml(this.formatter.format(file.FileRelationships)),
|
||||
path: "_rels/.rels",
|
||||
},
|
||||
Headers: file.Headers.map((headerWrapper, index) => ({
|
||||
data: xml(this.formatter.format(headerWrapper.Header)),
|
||||
path: `word/header${index + 1}.xml`,
|
||||
})),
|
||||
Footers: file.Footers.map((footerWrapper, index) => ({
|
||||
data: xml(this.formatter.format(footerWrapper.Footer)),
|
||||
path: `word/footer${index + 1}.xml`,
|
||||
})),
|
||||
HeaderRelationships: file.Headers.map((headerWrapper, index) => ({
|
||||
data: xml(this.formatter.format(headerWrapper.Relationships)),
|
||||
path: `word/_rels/header${index + 1}.xml.rels`,
|
||||
})),
|
||||
FooterRelationships: file.Footers.map((footerWrapper, index) => ({
|
||||
data: xml(this.formatter.format(footerWrapper.Relationships)),
|
||||
path: `word/_rels/footer${index + 1}.xml.rels`,
|
||||
})),
|
||||
HeaderRelationships: file.Headers.map((headerWrapper, index) => {
|
||||
const xmlData = xml(this.formatter.format(headerWrapper.Header));
|
||||
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
|
||||
|
||||
mediaDatas.forEach((mediaData, i) => {
|
||||
headerWrapper.Relationships.createRelationship(
|
||||
i,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
`media/${mediaData.fileName}`,
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
data: xml(this.formatter.format(headerWrapper.Relationships)),
|
||||
path: `word/_rels/header${index + 1}.xml.rels`,
|
||||
};
|
||||
}),
|
||||
FooterRelationships: file.Footers.map((footerWrapper, index) => {
|
||||
const xmlData = xml(this.formatter.format(footerWrapper.Footer));
|
||||
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
|
||||
|
||||
mediaDatas.forEach((mediaData, i) => {
|
||||
footerWrapper.Relationships.createRelationship(
|
||||
i,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
`media/${mediaData.fileName}`,
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
data: xml(this.formatter.format(footerWrapper.Relationships)),
|
||||
path: `word/_rels/footer${index + 1}.xml.rels`,
|
||||
};
|
||||
}),
|
||||
Headers: file.Headers.map((headerWrapper, index) => {
|
||||
const tempXmlData = xml(this.formatter.format(headerWrapper.Header));
|
||||
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
|
||||
// TODO: 0 needs to be changed when headers get relationships of their own
|
||||
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
|
||||
|
||||
return {
|
||||
data: xmlData,
|
||||
path: `word/header${index + 1}.xml`,
|
||||
};
|
||||
}),
|
||||
Footers: file.Footers.map((footerWrapper, index) => {
|
||||
const tempXmlData = xml(this.formatter.format(footerWrapper.Footer));
|
||||
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
|
||||
// TODO: 0 needs to be changed when headers get relationships of their own
|
||||
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
|
||||
|
||||
return {
|
||||
data: xmlData,
|
||||
path: `word/footer${index + 1}.xml`,
|
||||
};
|
||||
}),
|
||||
ContentTypes: {
|
||||
data: xml(this.formatter.format(file.ContentTypes)),
|
||||
path: "[Content_Types].xml",
|
||||
@ -140,13 +191,4 @@ export class Compiler {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/* By default docx collapse empty tags. <a></a> -> <a/>. this function mimic it
|
||||
so comparing (diff) original docx file and the library output is easier
|
||||
Currently not used, so commenting out */
|
||||
// private collapseEmptyTags(xmlData: string): string {
|
||||
// const regEx = /<(([^ <>]+)[^<>]*)><\/\2>/g;
|
||||
// const collapsed = xmlData.replace(regEx, "<$1/>");
|
||||
// return collapsed;
|
||||
// }
|
||||
}
|
||||
|
@ -46,4 +46,24 @@ describe("Packer", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#toBase64String()", () => {
|
||||
it("should create a standard docx file", async function() {
|
||||
this.timeout(99999999);
|
||||
const str = await packer.toBase64String(file);
|
||||
|
||||
assert.isDefined(str);
|
||||
assert.isTrue(str.length > 0);
|
||||
});
|
||||
|
||||
it("should handle exception if it throws any", () => {
|
||||
// tslint:disable-next-line:no-any
|
||||
const compiler = stub((packer as any).compiler, "compile");
|
||||
|
||||
compiler.throwsException();
|
||||
return packer.toBase64String(file).catch((error) => {
|
||||
assert.isDefined(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ export class Packer {
|
||||
}
|
||||
|
||||
public async toBuffer(file: File): Promise<Buffer> {
|
||||
const zip = await this.compiler.compile(file);
|
||||
const zip = this.compiler.compile(file);
|
||||
const zipData = (await zip.generateAsync({
|
||||
type: "nodebuffer",
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
@ -19,7 +19,7 @@ export class Packer {
|
||||
}
|
||||
|
||||
public async toBase64String(file: File): Promise<string> {
|
||||
const zip = await this.compiler.compile(file);
|
||||
const zip = this.compiler.compile(file);
|
||||
const zipData = (await zip.generateAsync({
|
||||
type: "base64",
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
@ -29,7 +29,7 @@ export class Packer {
|
||||
}
|
||||
|
||||
public async toBlob(file: File): Promise<Blob> {
|
||||
const zip = await this.compiler.compile(file);
|
||||
const zip = this.compiler.compile(file);
|
||||
const zipData = (await zip.generateAsync({
|
||||
type: "blob",
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
|
@ -20,64 +20,70 @@ describe("ContentTypes", () => {
|
||||
expect(tree["Types"]).to.be.an.instanceof(Array);
|
||||
|
||||
expect(tree["Types"][0]).to.deep.equal({ _attr: { xmlns: "http://schemas.openxmlformats.org/package/2006/content-types" } });
|
||||
expect(tree["Types"][1]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/png", Extension: "png" } }] });
|
||||
expect(tree["Types"][2]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/jpeg", Extension: "jpeg" } }] });
|
||||
expect(tree["Types"][3]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/jpeg", Extension: "jpg" } }] });
|
||||
expect(tree["Types"][4]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/bmp", Extension: "bmp" } }] });
|
||||
expect(tree["Types"][5]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/gif", Extension: "gif" } }] });
|
||||
expect(tree["Types"][1]).to.deep.equal({ Default: { _attr: { ContentType: "image/png", Extension: "png" } } });
|
||||
expect(tree["Types"][2]).to.deep.equal({ Default: { _attr: { ContentType: "image/jpeg", Extension: "jpeg" } } });
|
||||
expect(tree["Types"][3]).to.deep.equal({ Default: { _attr: { ContentType: "image/jpeg", Extension: "jpg" } } });
|
||||
expect(tree["Types"][4]).to.deep.equal({ Default: { _attr: { ContentType: "image/bmp", Extension: "bmp" } } });
|
||||
expect(tree["Types"][5]).to.deep.equal({ Default: { _attr: { ContentType: "image/gif", Extension: "gif" } } });
|
||||
expect(tree["Types"][6]).to.deep.equal({
|
||||
Default: [{ _attr: { ContentType: "application/vnd.openxmlformats-package.relationships+xml", Extension: "rels" } }],
|
||||
Default: { _attr: { ContentType: "application/vnd.openxmlformats-package.relationships+xml", Extension: "rels" } },
|
||||
});
|
||||
expect(tree["Types"][7]).to.deep.equal({ Default: [{ _attr: { ContentType: "application/xml", Extension: "xml" } }] });
|
||||
expect(tree["Types"][7]).to.deep.equal({ Default: { _attr: { ContentType: "application/xml", Extension: "xml" } } });
|
||||
expect(tree["Types"][8]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
|
||||
PartName: "/word/document.xml",
|
||||
},
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
|
||||
PartName: "/word/document.xml",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(tree["Types"][9]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
|
||||
PartName: "/word/styles.xml",
|
||||
},
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
|
||||
PartName: "/word/styles.xml",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(tree["Types"][10]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-package.core-properties+xml",
|
||||
PartName: "/docProps/core.xml",
|
||||
},
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-package.core-properties+xml",
|
||||
PartName: "/docProps/core.xml",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(tree["Types"][11]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
PartName: "/docProps/app.xml",
|
||||
},
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
PartName: "/docProps/app.xml",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(tree["Types"][12]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
|
||||
PartName: "/word/numbering.xml",
|
||||
},
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
|
||||
PartName: "/word/numbering.xml",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(tree["Types"][13]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml",
|
||||
PartName: "/word/footnotes.xml",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(tree["Types"][14]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
|
||||
PartName: "/word/settings.xml",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -88,26 +94,22 @@ describe("ContentTypes", () => {
|
||||
contentTypes.addFooter(102);
|
||||
const tree = new Formatter().format(contentTypes);
|
||||
|
||||
expect(tree["Types"][14]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||
PartName: "/word/footer101.xml",
|
||||
},
|
||||
expect(tree["Types"][15]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||
PartName: "/word/footer101.xml",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(tree["Types"][15]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||
PartName: "/word/footer102.xml",
|
||||
},
|
||||
expect(tree["Types"][16]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||
PartName: "/word/footer102.xml",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -118,26 +120,22 @@ describe("ContentTypes", () => {
|
||||
contentTypes.addHeader(202);
|
||||
const tree = new Formatter().format(contentTypes);
|
||||
|
||||
expect(tree["Types"][14]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||
PartName: "/word/header201.xml",
|
||||
},
|
||||
expect(tree["Types"][15]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||
PartName: "/word/header201.xml",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(tree["Types"][15]).to.deep.equal({
|
||||
Override: [
|
||||
{
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||
PartName: "/word/header202.xml",
|
||||
},
|
||||
expect(tree["Types"][16]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||
PartName: "/word/header202.xml",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -30,6 +30,7 @@ export class ContentTypes extends XmlComponent {
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.extended-properties+xml", "/docProps/app.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", "/word/numbering.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", "/word/footnotes.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", "/word/settings.xml"));
|
||||
}
|
||||
|
||||
public addFooter(index: number): void {
|
||||
|
@ -17,7 +17,7 @@ describe("Body", () => {
|
||||
expect(formatted)
|
||||
.to.have.property("w:sectPr")
|
||||
.and.to.be.an.instanceof(Array);
|
||||
expect(formatted["w:sectPr"]).to.have.length(5);
|
||||
expect(formatted["w:sectPr"]).to.have.length(4);
|
||||
});
|
||||
});
|
||||
|
||||
@ -30,14 +30,14 @@ describe("Body", () => {
|
||||
|
||||
const formatted = new Formatter().format(body)["w:body"];
|
||||
expect(formatted).to.be.an.instanceof(Array);
|
||||
const defaultSectionPr = formatted[0]["w:p"][1]["w:pPr"][0]["w:sectPr"];
|
||||
const defaultSectionPr = formatted[0]["w:p"][0]["w:pPr"][0]["w:sectPr"];
|
||||
|
||||
// check that this is the default section and added first in paragraph
|
||||
expect(defaultSectionPr[0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(defaultSectionPr[0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
|
||||
|
||||
// check for new section (since it's the last one, it's direct child of body)
|
||||
const newSection = formatted[1]["w:sectPr"];
|
||||
expect(newSection[0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 10000, "w:w": 10000, "w:orient": "portrait" } }] });
|
||||
expect(newSection[0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 10000, "w:w": 10000, "w:orient": "portrait" } } });
|
||||
});
|
||||
|
||||
it("should add section with default parameters", () => {
|
||||
@ -52,31 +52,27 @@ describe("Body", () => {
|
||||
"w:body": [
|
||||
{
|
||||
"w:p": [
|
||||
{ "w:pPr": [] },
|
||||
{
|
||||
"w:pPr": [
|
||||
{
|
||||
"w:sectPr": [
|
||||
{ "w:pgSz": [{ _attr: { "w:w": 11906, "w:h": 16838, "w:orient": "portrait" } }] },
|
||||
{ "w:pgSz": { _attr: { "w:w": 11906, "w:h": 16838, "w:orient": "portrait" } } },
|
||||
{
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:bottom": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:footer": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:bottom": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:footer": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{ "w:cols": [{ _attr: { "w:space": 708 } }] },
|
||||
{ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] },
|
||||
{ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] },
|
||||
{ "w:cols": { _attr: { "w:space": 708 } } },
|
||||
{ "w:docGrid": { _attr: { "w:linePitch": 360 } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@ -85,26 +81,23 @@ describe("Body", () => {
|
||||
},
|
||||
{
|
||||
"w:sectPr": [
|
||||
{ "w:pgSz": [{ _attr: { "w:w": 10000, "w:h": 10000, "w:orient": "portrait" } }] },
|
||||
{ "w:pgSz": { _attr: { "w:w": 10000, "w:h": 10000, "w:orient": "portrait" } } },
|
||||
{
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:bottom": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:footer": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:bottom": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:footer": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{ "w:cols": [{ _attr: { "w:space": 708 } }] },
|
||||
{ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] },
|
||||
{ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] },
|
||||
{ "w:cols": { _attr: { "w:space": 708 } } },
|
||||
{ "w:docGrid": { _attr: { "w:linePitch": 360 } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -37,7 +37,7 @@ export class Body extends XmlComponent {
|
||||
}
|
||||
public prepForXml(): IXmlableObject | undefined {
|
||||
if (this.sections.length === 1) {
|
||||
this.root.push(this.sections[0]);
|
||||
this.root.push(this.sections.pop() as SectionProperties);
|
||||
}
|
||||
|
||||
return super.prepForXml();
|
||||
|
@ -4,3 +4,4 @@ export * from "./header-reference";
|
||||
export * from "./page-size";
|
||||
export * from "./page-number";
|
||||
export * from "./page-border";
|
||||
export * from "./line-number";
|
||||
|
@ -0,0 +1 @@
|
||||
export * from "./line-number";
|
@ -0,0 +1,38 @@
|
||||
// http://officeopenxml.com/WPsectionLineNumbering.php
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
export enum LineNumberRestartFormat {
|
||||
CONTINUOUS = "continuous",
|
||||
NEW_SECTION = "newSection",
|
||||
NEW_PAGE = "newPage",
|
||||
}
|
||||
|
||||
export interface ILineNumberAttributes {
|
||||
readonly lineNumberCountBy?: number;
|
||||
readonly lineNumberStart?: number;
|
||||
readonly lineNumberRestart?: LineNumberRestartFormat;
|
||||
readonly lineNumberDistance?: number;
|
||||
}
|
||||
|
||||
export class LineNumberAttributes extends XmlAttributeComponent<ILineNumberAttributes> {
|
||||
protected readonly xmlKeys = {
|
||||
lineNumberCountBy: "w:countBy",
|
||||
lineNumberStart: "w:start",
|
||||
lineNumberRestart: "w:restart",
|
||||
lineNumberDistance: "w:distance",
|
||||
};
|
||||
}
|
||||
|
||||
export class LineNumberType extends XmlComponent {
|
||||
constructor(countBy?: number, start?: number, restart?: LineNumberRestartFormat, dist?: number) {
|
||||
super("w:lnNumType");
|
||||
this.root.push(
|
||||
new LineNumberAttributes({
|
||||
lineNumberCountBy: countBy,
|
||||
lineNumberStart: start,
|
||||
lineNumberRestart: restart,
|
||||
lineNumberDistance: dist,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -21,8 +21,7 @@ describe("PageBorders", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:pgBorders"]);
|
||||
expect(tree["w:pgBorders"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:pgBorders"][0]).to.deep.equal({ _attr: { "w:display": "firstPage" } });
|
||||
expect(tree["w:pgBorders"]).to.deep.equal({ _attr: { "w:display": "firstPage" } });
|
||||
});
|
||||
|
||||
it("should create page borders with full configuration", () => {
|
||||
@ -58,32 +57,24 @@ describe("PageBorders", () => {
|
||||
expect(tree["w:pgBorders"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:pgBorders"][0]).to.deep.equal({ _attr: { "w:display": "firstPage", "w:zOrder": "back" } });
|
||||
expect(tree["w:pgBorders"][1]).to.deep.equal({
|
||||
"w:top": [
|
||||
{
|
||||
_attr: { "w:color": "001122", "w:size": 10, "w:val": "doubleWave" },
|
||||
},
|
||||
],
|
||||
"w:top": {
|
||||
_attr: { "w:color": "001122", "w:size": 10, "w:val": "doubleWave" },
|
||||
},
|
||||
});
|
||||
expect(tree["w:pgBorders"][2]).to.deep.equal({
|
||||
"w:right": [
|
||||
{
|
||||
_attr: { "w:color": "223344", "w:size": 20, "w:val": "double" },
|
||||
},
|
||||
],
|
||||
"w:right": {
|
||||
_attr: { "w:color": "223344", "w:size": 20, "w:val": "double" },
|
||||
},
|
||||
});
|
||||
expect(tree["w:pgBorders"][3]).to.deep.equal({
|
||||
"w:bottom": [
|
||||
{
|
||||
_attr: { "w:color": "556677", "w:size": 30, "w:val": "single" },
|
||||
},
|
||||
],
|
||||
"w:bottom": {
|
||||
_attr: { "w:color": "556677", "w:size": 30, "w:val": "single" },
|
||||
},
|
||||
});
|
||||
expect(tree["w:pgBorders"][4]).to.deep.equal({
|
||||
"w:left": [
|
||||
{
|
||||
_attr: { "w:color": "889900", "w:size": 40, "w:val": "dotted" },
|
||||
},
|
||||
],
|
||||
"w:left": {
|
||||
_attr: { "w:color": "889900", "w:size": 40, "w:val": "dotted" },
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
// http://officeopenxml.com/WPsectionBorders.php
|
||||
import { BorderStyle } from "file/styles";
|
||||
import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
import { IgnoreIfEmptyXmlComponent, XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
export enum PageBorderDisplay {
|
||||
ALL_PAGES = "allPages",
|
||||
@ -64,7 +64,7 @@ class PageBordersAttributes extends XmlAttributeComponent<IPageBorderAttributes>
|
||||
};
|
||||
}
|
||||
|
||||
export class PageBorders extends XmlComponent {
|
||||
export class PageBorders extends IgnoreIfEmptyXmlComponent {
|
||||
constructor(options?: IPageBordersOptions) {
|
||||
super("w:pgBorders");
|
||||
|
||||
@ -97,10 +97,4 @@ export class PageBorders extends XmlComponent {
|
||||
this.root.push(new PageBorder("w:left", options.pageBorderLeft));
|
||||
}
|
||||
}
|
||||
|
||||
public prepForXml(): IXmlableObject | undefined {
|
||||
if (this.root.length > 0) {
|
||||
return super.prepForXml();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,7 @@ describe("PageSize", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
|
||||
expect(tree["w:pgSz"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:pgSz"][0]).to.deep.equal({ _attr: { "w:h": 200, "w:w": 100, "w:orient": "portrait" } });
|
||||
expect(tree["w:pgSz"]).to.deep.equal({ _attr: { "w:h": 200, "w:w": 100, "w:orient": "portrait" } });
|
||||
});
|
||||
|
||||
it("should create page size with horizontal and invert the lengths", () => {
|
||||
@ -21,8 +20,7 @@ describe("PageSize", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
|
||||
expect(tree["w:pgSz"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:pgSz"][0]).to.deep.equal({ _attr: { "w:h": 100, "w:w": 200, "w:orient": "landscape" } });
|
||||
expect(tree["w:pgSz"]).to.deep.equal({ _attr: { "w:h": 100, "w:w": 200, "w:orient": "landscape" } });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -39,29 +39,27 @@ describe("SectionProperties", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] });
|
||||
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] });
|
||||
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": [{ _attr: { "r:id": "rId100", "w:type": "default" } }] });
|
||||
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": [{ _attr: { "r:id": "rId200", "w:type": "even" } }] });
|
||||
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "cardinalText", "w:start": 10 } }] });
|
||||
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": { _attr: { "w:space": 708 } } });
|
||||
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": { _attr: { "w:linePitch": 360 } } });
|
||||
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": { _attr: { "r:id": "rId100", "w:type": "default" } } });
|
||||
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": { _attr: { "r:id": "rId200", "w:type": "even" } } });
|
||||
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": { _attr: { "w:fmt": "cardinalText", "w:start": 10 } } });
|
||||
});
|
||||
|
||||
it("should create section properties with no options", () => {
|
||||
@ -69,26 +67,23 @@ describe("SectionProperties", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] });
|
||||
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] });
|
||||
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] });
|
||||
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": { _attr: { "w:space": 708 } } });
|
||||
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": { _attr: { "w:linePitch": 360 } } });
|
||||
});
|
||||
|
||||
it("should create section properties with changed options", () => {
|
||||
@ -98,22 +93,20 @@ describe("SectionProperties", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
"w:top": 0,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
"w:top": 0,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -124,22 +117,20 @@ describe("SectionProperties", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:bottom": 0,
|
||||
"w:footer": 708,
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:bottom": 0,
|
||||
"w:footer": 708,
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -151,22 +142,20 @@ describe("SectionProperties", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 0, "w:w": 0, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 0, "w:w": 0, "w:orient": "portrait" } } });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
"w:pgMar": {
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -180,8 +169,28 @@ describe("SectionProperties", () => {
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
const pgBorders = tree["w:sectPr"].find((item) => item["w:pgBorders"] !== undefined);
|
||||
expect(pgBorders).to.deep.equal({
|
||||
"w:pgBorders": [{ _attr: { "w:offsetFrom": "page" } }],
|
||||
"w:pgBorders": { _attr: { "w:offsetFrom": "page" } },
|
||||
});
|
||||
});
|
||||
|
||||
it("should create section properties with page number type, but without start attribute", () => {
|
||||
const properties = new SectionProperties({
|
||||
pageNumberFormatType: PageNumberFormat.UPPER_ROMAN,
|
||||
});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
const pgNumType = tree["w:sectPr"].find((item) => item["w:pgNumType"] !== undefined);
|
||||
expect(pgNumType).to.deep.equal({
|
||||
"w:pgNumType": { _attr: { "w:fmt": "upperRoman" } },
|
||||
});
|
||||
});
|
||||
|
||||
it("should create section properties without page number type", () => {
|
||||
const properties = new SectionProperties({});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
const pgNumType = tree["w:sectPr"].find((item) => item["w:pgNumType"] !== undefined);
|
||||
expect(pgNumType).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -11,10 +11,11 @@ import { FooterReferenceType } from "./footer-reference";
|
||||
import { FooterReference } from "./footer-reference/footer-reference";
|
||||
import { HeaderReferenceType } from "./header-reference";
|
||||
import { HeaderReference } from "./header-reference/header-reference";
|
||||
import { ILineNumberAttributes, LineNumberType } from "./line-number";
|
||||
import { IPageBordersOptions, PageBorders } from "./page-border";
|
||||
import { PageMargin } from "./page-margin/page-margin";
|
||||
import { IPageMarginAttributes } from "./page-margin/page-margin-attributes";
|
||||
import { IPageNumberTypeAttributes, PageNumberFormat, PageNumberType } from "./page-number";
|
||||
import { IPageNumberTypeAttributes, PageNumberType } from "./page-number";
|
||||
import { PageSize } from "./page-size/page-size";
|
||||
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
|
||||
import { TitlePage } from "./title-page/title-page";
|
||||
@ -44,6 +45,7 @@ export type SectionPropertiesOptions = IPageSizeAttributes &
|
||||
IHeadersOptions &
|
||||
IFootersOptions &
|
||||
IPageNumberTypeAttributes &
|
||||
ILineNumberAttributes &
|
||||
IPageBordersOptions &
|
||||
ITitlePageOptions;
|
||||
|
||||
@ -69,8 +71,12 @@ export class SectionProperties extends XmlComponent {
|
||||
orientation = PageOrientation.PORTRAIT,
|
||||
headers,
|
||||
footers,
|
||||
pageNumberFormatType = PageNumberFormat.DECIMAL,
|
||||
pageNumberFormatType,
|
||||
pageNumberStart,
|
||||
lineNumberCountBy,
|
||||
lineNumberStart,
|
||||
lineNumberRestart,
|
||||
lineNumberDistance,
|
||||
pageBorders,
|
||||
pageBorderTop,
|
||||
pageBorderRight,
|
||||
@ -88,7 +94,13 @@ export class SectionProperties extends XmlComponent {
|
||||
this.addHeaders(headers);
|
||||
this.addFooters(footers);
|
||||
|
||||
this.root.push(new PageNumberType(pageNumberStart, pageNumberFormatType));
|
||||
if (pageNumberStart || pageNumberFormatType) {
|
||||
this.root.push(new PageNumberType(pageNumberStart, pageNumberFormatType));
|
||||
}
|
||||
|
||||
if (lineNumberCountBy || lineNumberStart || lineNumberRestart || lineNumberDistance) {
|
||||
this.root.push(new LineNumberType(lineNumberCountBy, lineNumberStart, lineNumberRestart, lineNumberDistance));
|
||||
}
|
||||
|
||||
if (pageBorders || pageBorderTop || pageBorderRight || pageBorderBottom || pageBorderLeft) {
|
||||
this.root.push(
|
||||
|
@ -11,8 +11,7 @@ describe("PageSize", () => {
|
||||
const tree = new Formatter().format(properties);
|
||||
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:titlePg"]);
|
||||
expect(tree["w:titlePg"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:titlePg"][0]).to.deep.equal({ _attr: { "w:val": "1" } });
|
||||
expect(tree["w:titlePg"]).to.deep.equal({ _attr: { "w:val": "1" } });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -52,14 +52,17 @@ describe("Document", () => {
|
||||
expect(body[0])
|
||||
.to.have.property("w:p")
|
||||
.which.includes({
|
||||
"w:r": [{ "w:rPr": [] }, { "w:t": [{ _attr: { "xml:space": "preserve" } }, "sample paragraph text"] }],
|
||||
"w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "sample paragraph text"] }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createTable", () => {
|
||||
it("should create a new table and append it to body", () => {
|
||||
const table = document.createTable(2, 3);
|
||||
const table = document.createTable({
|
||||
rows: 2,
|
||||
columns: 3,
|
||||
});
|
||||
expect(table).to.be.an.instanceof(Table);
|
||||
const body = new Formatter().format(document)["w:document"][1]["w:body"];
|
||||
expect(body)
|
||||
@ -69,7 +72,10 @@ describe("Document", () => {
|
||||
});
|
||||
|
||||
it("should create a table with the correct dimensions", () => {
|
||||
document.createTable(2, 3);
|
||||
document.createTable({
|
||||
rows: 2,
|
||||
columns: 3,
|
||||
});
|
||||
const body = new Formatter().format(document)["w:document"][1]["w:body"];
|
||||
expect(body)
|
||||
.to.be.an("array")
|
||||
@ -78,9 +84,9 @@ describe("Document", () => {
|
||||
.to.have.property("w:tbl")
|
||||
.which.includes({
|
||||
"w:tblGrid": [
|
||||
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
|
||||
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
|
||||
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
|
||||
{ "w:gridCol": { _attr: { "w:w": 100 } } },
|
||||
{ "w:gridCol": { _attr: { "w:w": 100 } } },
|
||||
{ "w:gridCol": { _attr: { "w:w": 100 } } },
|
||||
],
|
||||
});
|
||||
expect(body[0]["w:tbl"].filter((x) => x["w:tr"])).to.have.length(2);
|
||||
|
@ -1,7 +1,7 @@
|
||||
// http://officeopenxml.com/WPdocument.php
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { Paragraph } from "../paragraph";
|
||||
import { Table } from "../table";
|
||||
import { ITableOptions, Table } from "../table";
|
||||
import { TableOfContents } from "../table-of-contents";
|
||||
import { Body } from "./body";
|
||||
import { SectionPropertiesOptions } from "./body/section-properties";
|
||||
@ -53,12 +53,13 @@ export class Document extends XmlComponent {
|
||||
return para;
|
||||
}
|
||||
|
||||
public addTable(table: Table): void {
|
||||
public addTable(table: Table): Document {
|
||||
this.body.push(table);
|
||||
return this;
|
||||
}
|
||||
|
||||
public createTable(rows: number, cols: number): Table {
|
||||
const table = new Table(rows, cols);
|
||||
public createTable(options: ITableOptions): Table {
|
||||
const table = new Table(options);
|
||||
this.addTable(table);
|
||||
return table;
|
||||
}
|
||||
|
@ -8,7 +8,20 @@ import { Anchor } from "./anchor";
|
||||
|
||||
function createAnchor(drawingOptions: IDrawingOptions): Anchor {
|
||||
return new Anchor(
|
||||
1,
|
||||
{
|
||||
fileName: "test.png",
|
||||
stream: new Buffer(""),
|
||||
dimensions: {
|
||||
pixels: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
emus: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pixels: {
|
||||
x: 100,
|
||||
|
@ -1,5 +1,5 @@
|
||||
// http://officeopenxml.com/drwPicFloating.php
|
||||
import { IMediaDataDimensions } from "file/media";
|
||||
import { IMediaData, IMediaDataDimensions } from "file/media";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { IDrawingOptions } from "../drawing";
|
||||
import { HorizontalPosition, IFloating, SimplePos, VerticalPosition } from "../floating";
|
||||
@ -21,7 +21,7 @@ const defaultOptions: IFloating = {
|
||||
};
|
||||
|
||||
export class Anchor extends XmlComponent {
|
||||
constructor(referenceId: number, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) {
|
||||
constructor(mediaData: IMediaData, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) {
|
||||
super("wp:anchor");
|
||||
|
||||
const floating = {
|
||||
@ -77,6 +77,6 @@ export class Anchor extends XmlComponent {
|
||||
|
||||
this.root.push(new DocProperties());
|
||||
this.root.push(new GraphicFrameProperties());
|
||||
this.root.push(new Graphic(referenceId, dimensions.emus.x, dimensions.emus.y));
|
||||
this.root.push(new Graphic(mediaData, dimensions.emus.x, dimensions.emus.y));
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ function createDrawing(drawingOptions?: IDrawingOptions): Drawing {
|
||||
return new Drawing(
|
||||
{
|
||||
fileName: "test.jpg",
|
||||
referenceId: 1,
|
||||
stream: Buffer.from(imageBase64Data, "base64"),
|
||||
path: path,
|
||||
dimensions: {
|
||||
|
@ -21,15 +21,11 @@ export class Drawing extends XmlComponent {
|
||||
constructor(imageData: IMediaData, drawingOptions: IDrawingOptions = {}) {
|
||||
super("w:drawing");
|
||||
|
||||
if (imageData === undefined) {
|
||||
throw new Error("imageData cannot be undefined");
|
||||
}
|
||||
|
||||
if (!drawingOptions.floating) {
|
||||
this.inline = new Inline(imageData.referenceId, imageData.dimensions);
|
||||
this.inline = new Inline(imageData, imageData.dimensions);
|
||||
this.root.push(this.inline);
|
||||
} else {
|
||||
this.root.push(new Anchor(imageData.referenceId, imageData.dimensions, drawingOptions));
|
||||
this.root.push(new Anchor(imageData, imageData.dimensions, drawingOptions));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { IMediaData } from "file/media";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { GraphicDataAttributes } from "./graphic-data-attribute";
|
||||
import { Pic } from "./pic";
|
||||
|
||||
export class GraphicData extends XmlComponent {
|
||||
private readonly pic: Pic;
|
||||
|
||||
constructor(referenceId: number, x: number, y: number) {
|
||||
constructor(mediaData: IMediaData, x: number, y: number) {
|
||||
super("a:graphicData");
|
||||
|
||||
this.root.push(
|
||||
@ -14,7 +16,7 @@ export class GraphicData extends XmlComponent {
|
||||
}),
|
||||
);
|
||||
|
||||
this.pic = new Pic(referenceId, x, y);
|
||||
this.pic = new Pic(mediaData, x, y);
|
||||
|
||||
this.root.push(this.pic);
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { IMediaData } from "file/media";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { Blip } from "./blip";
|
||||
import { SourceRectangle } from "./source-rectangle";
|
||||
import { Stretch } from "./stretch";
|
||||
|
||||
export class BlipFill extends XmlComponent {
|
||||
constructor(referenceId: number) {
|
||||
constructor(mediaData: IMediaData) {
|
||||
super("pic:blipFill");
|
||||
this.root.push(new Blip(referenceId));
|
||||
|
||||
this.root.push(new Blip(mediaData));
|
||||
this.root.push(new SourceRectangle());
|
||||
this.root.push(new Stretch());
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { IMediaData } from "file/media";
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
interface IBlipProperties {
|
||||
@ -13,11 +14,11 @@ class BlipAttributes extends XmlAttributeComponent<IBlipProperties> {
|
||||
}
|
||||
|
||||
export class Blip extends XmlComponent {
|
||||
constructor(referenceId: number) {
|
||||
constructor(mediaData: IMediaData) {
|
||||
super("a:blip");
|
||||
this.root.push(
|
||||
new BlipAttributes({
|
||||
embed: `rId${referenceId}`,
|
||||
embed: `rId{${mediaData.fileName}}`,
|
||||
cstate: "none",
|
||||
}),
|
||||
);
|
||||
|
@ -1,5 +1,7 @@
|
||||
// http://officeopenxml.com/drwPic.php
|
||||
import { IMediaData } from "file/media";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { BlipFill } from "./blip/blip-fill";
|
||||
import { NonVisualPicProperties } from "./non-visual-pic-properties/non-visual-pic-properties";
|
||||
import { PicAttributes } from "./pic-attributes";
|
||||
@ -8,7 +10,7 @@ import { ShapeProperties } from "./shape-properties/shape-properties";
|
||||
export class Pic extends XmlComponent {
|
||||
private readonly shapeProperties: ShapeProperties;
|
||||
|
||||
constructor(referenceId: number, x: number, y: number) {
|
||||
constructor(mediaData: IMediaData, x: number, y: number) {
|
||||
super("pic:pic");
|
||||
|
||||
this.root.push(
|
||||
@ -20,7 +22,7 @@ export class Pic extends XmlComponent {
|
||||
this.shapeProperties = new ShapeProperties(x, y);
|
||||
|
||||
this.root.push(new NonVisualPicProperties());
|
||||
this.root.push(new BlipFill(referenceId));
|
||||
this.root.push(new BlipFill(mediaData));
|
||||
this.root.push(new ShapeProperties(x, y));
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { IMediaData } from "file/media";
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
import { GraphicData } from "./graphic-data";
|
||||
|
||||
interface IGraphicProperties {
|
||||
@ -14,7 +16,7 @@ class GraphicAttributes extends XmlAttributeComponent<IGraphicProperties> {
|
||||
export class Graphic extends XmlComponent {
|
||||
private readonly data: GraphicData;
|
||||
|
||||
constructor(referenceId: number, x: number, y: number) {
|
||||
constructor(mediaData: IMediaData, x: number, y: number) {
|
||||
super("a:graphic");
|
||||
this.root.push(
|
||||
new GraphicAttributes({
|
||||
@ -22,7 +24,7 @@ export class Graphic extends XmlComponent {
|
||||
}),
|
||||
);
|
||||
|
||||
this.data = new GraphicData(referenceId, x, y);
|
||||
this.data = new GraphicData(mediaData, x, y);
|
||||
|
||||
this.root.push(this.data);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// http://officeopenxml.com/drwPicInline.php
|
||||
import { IMediaDataDimensions } from "file/media";
|
||||
import { IMediaData, IMediaDataDimensions } from "file/media";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { DocProperties } from "./../doc-properties/doc-properties";
|
||||
import { EffectExtent } from "./../effect-extent/effect-extent";
|
||||
@ -12,7 +12,7 @@ export class Inline extends XmlComponent {
|
||||
private readonly extent: Extent;
|
||||
private readonly graphic: Graphic;
|
||||
|
||||
constructor(referenceId: number, private readonly dimensions: IMediaDataDimensions) {
|
||||
constructor(readonly mediaData: IMediaData, private readonly dimensions: IMediaDataDimensions) {
|
||||
super("wp:inline");
|
||||
|
||||
this.root.push(
|
||||
@ -25,7 +25,7 @@ export class Inline extends XmlComponent {
|
||||
);
|
||||
|
||||
this.extent = new Extent(dimensions.emus.x, dimensions.emus.y);
|
||||
this.graphic = new Graphic(referenceId, dimensions.emus.x, dimensions.emus.y);
|
||||
this.graphic = new Graphic(mediaData, dimensions.emus.x, dimensions.emus.y);
|
||||
|
||||
this.root.push(this.extent);
|
||||
this.root.push(new EffectExtent());
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { expect } from "chai";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { File } from "./file";
|
||||
import { Paragraph } from "./paragraph";
|
||||
import { Table } from "./table";
|
||||
|
||||
describe("File", () => {
|
||||
describe("#constructor", () => {
|
||||
it("should create with correct headers", () => {
|
||||
it("should create with correct headers and footers", () => {
|
||||
const doc = new File();
|
||||
const header = doc.createHeader();
|
||||
const footer = doc.createFooter();
|
||||
@ -22,8 +25,28 @@ describe("File", () => {
|
||||
|
||||
const tree = new Formatter().format(doc.Document.Body);
|
||||
|
||||
expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"][0]._attr["w:type"]).to.equal("default");
|
||||
expect(tree["w:body"][1]["w:sectPr"][5]["w:footerReference"][0]._attr["w:type"]).to.equal("default");
|
||||
expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
|
||||
expect(tree["w:body"][1]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default");
|
||||
});
|
||||
|
||||
it("should create with first headers and footers", () => {
|
||||
const doc = new File();
|
||||
const header = doc.createHeader();
|
||||
const footer = doc.createFooter();
|
||||
|
||||
doc.addSection({
|
||||
headers: {
|
||||
first: header,
|
||||
},
|
||||
footers: {
|
||||
first: footer,
|
||||
},
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(doc.Document.Body);
|
||||
|
||||
expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("first");
|
||||
expect(tree["w:body"][1]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("first");
|
||||
});
|
||||
|
||||
it("should create with correct headers", () => {
|
||||
@ -46,13 +69,105 @@ describe("File", () => {
|
||||
|
||||
const tree = new Formatter().format(doc.Document.Body);
|
||||
|
||||
expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"][0]._attr["w:type"]).to.equal("default");
|
||||
expect(tree["w:body"][1]["w:sectPr"][5]["w:headerReference"][0]._attr["w:type"]).to.equal("first");
|
||||
expect(tree["w:body"][1]["w:sectPr"][6]["w:headerReference"][0]._attr["w:type"]).to.equal("even");
|
||||
expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
|
||||
expect(tree["w:body"][1]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first");
|
||||
expect(tree["w:body"][1]["w:sectPr"][6]["w:headerReference"]._attr["w:type"]).to.equal("even");
|
||||
|
||||
expect(tree["w:body"][1]["w:sectPr"][7]["w:footerReference"][0]._attr["w:type"]).to.equal("default");
|
||||
expect(tree["w:body"][1]["w:sectPr"][8]["w:footerReference"][0]._attr["w:type"]).to.equal("first");
|
||||
expect(tree["w:body"][1]["w:sectPr"][9]["w:footerReference"][0]._attr["w:type"]).to.equal("even");
|
||||
expect(tree["w:body"][1]["w:sectPr"][7]["w:footerReference"]._attr["w:type"]).to.equal("default");
|
||||
expect(tree["w:body"][1]["w:sectPr"][8]["w:footerReference"]._attr["w:type"]).to.equal("first");
|
||||
expect(tree["w:body"][1]["w:sectPr"][9]["w:footerReference"]._attr["w:type"]).to.equal("even");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addParagraph", () => {
|
||||
it("should call the underlying document's addParagraph", () => {
|
||||
const file = new File();
|
||||
const spy = sinon.spy(file.Document, "addParagraph");
|
||||
file.addParagraph(new Paragraph());
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addTable", () => {
|
||||
it("should call the underlying document's addTable", () => {
|
||||
const wrapper = new File();
|
||||
const spy = sinon.spy(wrapper.Document, "addTable");
|
||||
wrapper.addTable(
|
||||
new Table({
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createTable", () => {
|
||||
it("should call the underlying document's createTable", () => {
|
||||
const wrapper = new File();
|
||||
const spy = sinon.spy(wrapper.Document, "createTable");
|
||||
wrapper.createTable({
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
});
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addTableOfContents", () => {
|
||||
it("should call the underlying document's addTableOfContents", () => {
|
||||
const wrapper = new File();
|
||||
const spy = sinon.spy(wrapper.Document, "addTableOfContents");
|
||||
// tslint:disable-next-line:no-any
|
||||
wrapper.addTableOfContents({} as any);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createParagraph", () => {
|
||||
it("should call the underlying document's createParagraph", () => {
|
||||
const wrapper = new File();
|
||||
const spy = sinon.spy(wrapper.Document, "createParagraph");
|
||||
wrapper.createParagraph("test");
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addImage", () => {
|
||||
it("should call the underlying document's addImage", () => {
|
||||
const wrapper = new File();
|
||||
const spy = sinon.spy(wrapper.Document, "addParagraph");
|
||||
// tslint:disable-next-line:no-any
|
||||
wrapper.addImage({} as any);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createImage", () => {
|
||||
it("should call the underlying document's createImage", () => {
|
||||
const wrapper = new File();
|
||||
const spy = sinon.spy(wrapper.Media, "addMedia");
|
||||
const wrapperSpy = sinon.spy(wrapper.Document, "addParagraph");
|
||||
wrapper.createImage("");
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
expect(wrapperSpy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createFootnote", () => {
|
||||
it("should call the underlying document's createFootnote", () => {
|
||||
const wrapper = new File();
|
||||
const spy = sinon.spy(wrapper.FootNotes, "createFootNote");
|
||||
wrapper.createFootnote(new Paragraph(""));
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -19,11 +19,12 @@ import { Image, Media } from "./media";
|
||||
import { Numbering } from "./numbering";
|
||||
import { Bookmark, Hyperlink, Paragraph } from "./paragraph";
|
||||
import { Relationships } from "./relationships";
|
||||
import { TargetModeType } from "./relationships/relationship/relationship";
|
||||
import { Settings } from "./settings";
|
||||
import { Styles } from "./styles";
|
||||
import { ExternalStylesFactory } from "./styles/external-styles-factory";
|
||||
import { DefaultStylesFactory } from "./styles/factory";
|
||||
import { Table } from "./table";
|
||||
import { ITableOptions, Table } from "./table";
|
||||
import { TableOfContents } from "./table-of-contents";
|
||||
|
||||
export class File {
|
||||
@ -57,12 +58,13 @@ export class File {
|
||||
this.coreProperties = new CoreProperties(options);
|
||||
this.numbering = new Numbering();
|
||||
this.docRelationships = new Relationships();
|
||||
this.media = new Media();
|
||||
this.fileRelationships = new Relationships();
|
||||
this.appProperties = new AppProperties();
|
||||
this.footNotes = new FootNotes();
|
||||
this.contentTypes = new ContentTypes();
|
||||
|
||||
this.media = fileProperties.template && fileProperties.template.media ? fileProperties.template.media : new Media();
|
||||
|
||||
if (fileProperties.template) {
|
||||
this.currentRelationshipId = fileProperties.template.currentRelationshipId + 1;
|
||||
}
|
||||
@ -72,7 +74,8 @@ export class File {
|
||||
throw Error("can not use both template and external styles");
|
||||
}
|
||||
if (fileProperties.template) {
|
||||
this.styles = fileProperties.template.styles;
|
||||
const stylesFactory = new ExternalStylesFactory();
|
||||
this.styles = stylesFactory.newInstance(fileProperties.template.styles);
|
||||
} else if (options.externalStyles) {
|
||||
const stylesFactory = new ExternalStylesFactory();
|
||||
this.styles = stylesFactory.newInstance(options.externalStyles);
|
||||
@ -109,8 +112,9 @@ export class File {
|
||||
this.settings = new Settings();
|
||||
}
|
||||
|
||||
public addTableOfContents(toc: TableOfContents): void {
|
||||
public addTableOfContents(toc: TableOfContents): File {
|
||||
this.document.addTableOfContents(toc);
|
||||
return this;
|
||||
}
|
||||
|
||||
public addParagraph(paragraph: Paragraph): File {
|
||||
@ -122,12 +126,13 @@ export class File {
|
||||
return this.document.createParagraph(text);
|
||||
}
|
||||
|
||||
public addTable(table: Table): void {
|
||||
return this.document.addTable(table);
|
||||
public addTable(table: Table): File {
|
||||
this.document.addTable(table);
|
||||
return this;
|
||||
}
|
||||
|
||||
public createTable(rows: number, cols: number): Table {
|
||||
return this.document.createTable(rows, cols);
|
||||
public createTable(options: ITableOptions): Table {
|
||||
return this.document.createTable(options);
|
||||
}
|
||||
|
||||
public addImage(image: Image): File {
|
||||
@ -154,7 +159,7 @@ export class File {
|
||||
hyperlink.linkId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
||||
link,
|
||||
"External",
|
||||
TargetModeType.EXTERNAL,
|
||||
);
|
||||
return hyperlink;
|
||||
}
|
||||
@ -319,6 +324,11 @@ export class File {
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
|
||||
"footnotes.xml",
|
||||
);
|
||||
this.docRelationships.createRelationship(
|
||||
this.currentRelationshipId++,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings",
|
||||
"settings.xml",
|
||||
);
|
||||
}
|
||||
|
||||
private groupHeaders(headers: IDocumentHeader[], group: IHeaderFooterGroup<HeaderWrapper> = {}): IHeaderFooterGroup<HeaderWrapper> {
|
||||
@ -326,12 +336,6 @@ export class File {
|
||||
|
||||
for (const header of headers) {
|
||||
switch (header.type) {
|
||||
case HeaderReferenceType.DEFAULT:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
default: header.header,
|
||||
};
|
||||
break;
|
||||
case HeaderReferenceType.FIRST:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
@ -344,6 +348,7 @@ export class File {
|
||||
even: header.header,
|
||||
};
|
||||
break;
|
||||
case HeaderReferenceType.DEFAULT:
|
||||
default:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
@ -361,12 +366,6 @@ export class File {
|
||||
|
||||
for (const footer of footers) {
|
||||
switch (footer.type) {
|
||||
case FooterReferenceType.DEFAULT:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
default: footer.footer,
|
||||
};
|
||||
break;
|
||||
case FooterReferenceType.FIRST:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
@ -379,6 +378,7 @@ export class File {
|
||||
even: footer.footer,
|
||||
};
|
||||
break;
|
||||
case FooterReferenceType.DEFAULT:
|
||||
default:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
|
88
src/file/footer-wrapper.spec.ts
Normal file
88
src/file/footer-wrapper.spec.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import { expect } from "chai";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import { FooterWrapper } from "./footer-wrapper";
|
||||
import { Media } from "./media";
|
||||
import { Paragraph } from "./paragraph";
|
||||
import { Table } from "./table";
|
||||
|
||||
describe("FooterWrapper", () => {
|
||||
describe("#addParagraph", () => {
|
||||
it("should call the underlying footer's addParagraph", () => {
|
||||
const file = new FooterWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Footer, "addParagraph");
|
||||
file.addParagraph(new Paragraph());
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addTable", () => {
|
||||
it("should call the underlying footer's addParagraph", () => {
|
||||
const file = new FooterWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Footer, "addTable");
|
||||
file.addTable(
|
||||
new Table({
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createTable", () => {
|
||||
it("should call the underlying footer's createTable", () => {
|
||||
const wrapper = new FooterWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(wrapper.Footer, "createTable");
|
||||
wrapper.createTable(1, 1);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createParagraph", () => {
|
||||
it("should call the underlying footer's createParagraph", () => {
|
||||
const file = new FooterWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Footer, "addParagraph");
|
||||
file.createParagraph();
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addImage", () => {
|
||||
it("should call the underlying footer's addImage", () => {
|
||||
const file = new FooterWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Footer, "addParagraph");
|
||||
// tslint:disable-next-line:no-any
|
||||
file.addImage({} as any);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createImage", () => {
|
||||
it("should call the underlying footer's createImage", () => {
|
||||
const file = new FooterWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Media, "addMedia");
|
||||
const fileSpy = sinon.spy(file, "addImage");
|
||||
file.createImage("");
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
expect(fileSpy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addChildElement", () => {
|
||||
it("should call the underlying footer's addChildElement", () => {
|
||||
const file = new FooterWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Footer, "addChildElement");
|
||||
// tslint:disable-next-line:no-any
|
||||
file.addChildElement({} as any);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { FooterReferenceType } from "./document";
|
||||
import { Footer } from "./footer/footer";
|
||||
import { Image, IMediaData, Media } from "./media";
|
||||
import { Image, Media } from "./media";
|
||||
import { ImageParagraph, Paragraph } from "./paragraph";
|
||||
import { Relationships } from "./relationships";
|
||||
import { Table } from "./table";
|
||||
@ -43,29 +43,8 @@ export class FooterWrapper {
|
||||
this.footer.addChildElement(childElement);
|
||||
}
|
||||
|
||||
public addImageRelationship(image: Buffer, refId: number, width?: number, height?: number): IMediaData {
|
||||
const mediaData = this.media.addMedia(image, refId, width, height);
|
||||
this.relationships.createRelationship(
|
||||
mediaData.referenceId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
`media/${mediaData.fileName}`,
|
||||
);
|
||||
return mediaData;
|
||||
}
|
||||
|
||||
public addHyperlinkRelationship(target: string, refId: number, targetMode?: "External" | undefined): void {
|
||||
this.relationships.createRelationship(
|
||||
refId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
||||
target,
|
||||
targetMode,
|
||||
);
|
||||
}
|
||||
|
||||
public createImage(image: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): void {
|
||||
// TODO
|
||||
// tslint:disable-next-line:no-any
|
||||
const mediaData = this.addImageRelationship(image as any, this.relationships.RelationshipCount, width, height);
|
||||
const mediaData = this.media.addMedia(image, width, height);
|
||||
this.addImage(new Image(new ImageParagraph(mediaData)));
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,10 @@ export class Footer extends InitializableXmlComponent {
|
||||
}
|
||||
|
||||
public createTable(rows: number, cols: number): Table {
|
||||
const table = new Table(rows, cols);
|
||||
const table = new Table({
|
||||
rows: rows,
|
||||
columns: cols,
|
||||
});
|
||||
this.addTable(table);
|
||||
return table;
|
||||
}
|
||||
|
@ -11,8 +11,7 @@ describe("Footnote", () => {
|
||||
const tree = new Formatter().format(footnote);
|
||||
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:footnote"]);
|
||||
expect(tree["w:footnote"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:footnote"][0]).to.deep.equal({ _attr: { "w:type": "separator", "w:id": 1 } });
|
||||
expect(tree["w:footnote"]).to.deep.equal({ _attr: { "w:type": "separator", "w:id": 1 } });
|
||||
});
|
||||
|
||||
it("should create a footnote without a footnote type", () => {
|
||||
@ -20,8 +19,7 @@ describe("Footnote", () => {
|
||||
const tree = new Formatter().format(footnote);
|
||||
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:footnote"]);
|
||||
expect(tree["w:footnote"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:footnote"][0]).to.deep.equal({ _attr: { "w:id": 1 } });
|
||||
expect(tree["w:footnote"]).to.deep.equal({ _attr: { "w:id": 1 } });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -9,19 +9,78 @@ import { Table } from "./table";
|
||||
describe("HeaderWrapper", () => {
|
||||
describe("#addParagraph", () => {
|
||||
it("should call the underlying header's addParagraph", () => {
|
||||
const file = new HeaderWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Header, "addParagraph");
|
||||
file.addParagraph(new Paragraph());
|
||||
const wrapper = new HeaderWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(wrapper.Header, "addParagraph");
|
||||
wrapper.addParagraph(new Paragraph());
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addTable", () => {
|
||||
it("should call the underlying header's addParagraph", () => {
|
||||
it("should call the underlying header's addTable", () => {
|
||||
const wrapper = new HeaderWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(wrapper.Header, "addTable");
|
||||
wrapper.addTable(
|
||||
new Table({
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createTable", () => {
|
||||
it("should call the underlying header's createTable", () => {
|
||||
const wrapper = new HeaderWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(wrapper.Header, "createTable");
|
||||
wrapper.createTable(1, 1);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createParagraph", () => {
|
||||
it("should call the underlying header's createParagraph", () => {
|
||||
const file = new HeaderWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Header, "addTable");
|
||||
file.addTable(new Table(1, 1));
|
||||
const spy = sinon.spy(file.Header, "addParagraph");
|
||||
file.createParagraph();
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addImage", () => {
|
||||
it("should call the underlying header's addImage", () => {
|
||||
const file = new HeaderWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Header, "addParagraph");
|
||||
// tslint:disable-next-line:no-any
|
||||
file.addImage({} as any);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createImage", () => {
|
||||
it("should call the underlying header's createImage", () => {
|
||||
const file = new HeaderWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Media, "addMedia");
|
||||
const fileSpy = sinon.spy(file, "addImage");
|
||||
file.createImage("");
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
expect(fileSpy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addChildElement", () => {
|
||||
it("should call the underlying header's addChildElement", () => {
|
||||
const file = new HeaderWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Header, "addChildElement");
|
||||
// tslint:disable-next-line:no-any
|
||||
file.addChildElement({} as any);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
|
@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { HeaderReferenceType } from "./document";
|
||||
import { Header } from "./header/header";
|
||||
import { Image, IMediaData, Media } from "./media";
|
||||
import { Image, Media } from "./media";
|
||||
import { ImageParagraph, Paragraph } from "./paragraph";
|
||||
import { Relationships } from "./relationships";
|
||||
import { Table } from "./table";
|
||||
@ -43,29 +43,8 @@ export class HeaderWrapper {
|
||||
this.header.addChildElement(childElement);
|
||||
}
|
||||
|
||||
public addImageRelationship(image: Buffer, refId: number, width?: number, height?: number): IMediaData {
|
||||
const mediaData = this.media.addMedia(image, refId, width, height);
|
||||
this.relationships.createRelationship(
|
||||
mediaData.referenceId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
`media/${mediaData.fileName}`,
|
||||
);
|
||||
return mediaData;
|
||||
}
|
||||
|
||||
public addHyperlinkRelationship(target: string, refId: number, targetMode?: "External" | undefined): void {
|
||||
this.relationships.createRelationship(
|
||||
refId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
||||
target,
|
||||
targetMode,
|
||||
);
|
||||
}
|
||||
|
||||
public createImage(image: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): void {
|
||||
// TODO
|
||||
// tslint:disable-next-line:no-any
|
||||
const mediaData = this.addImageRelationship(image as any, this.relationships.RelationshipCount, width, height);
|
||||
const mediaData = this.media.addMedia(image, width, height);
|
||||
this.addImage(new Image(new ImageParagraph(mediaData)));
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,10 @@ export class Header extends InitializableXmlComponent {
|
||||
}
|
||||
|
||||
public createTable(rows: number, cols: number): Table {
|
||||
const table = new Table(rows, cols);
|
||||
const table = new Table({
|
||||
rows: rows,
|
||||
columns: cols,
|
||||
});
|
||||
this.addTable(table);
|
||||
return table;
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ export interface IMediaDataDimensions {
|
||||
}
|
||||
|
||||
export interface IMediaData {
|
||||
readonly referenceId: number;
|
||||
readonly stream: Buffer | Uint8Array | ArrayBuffer;
|
||||
readonly path?: string;
|
||||
readonly fileName: string;
|
||||
|
@ -1,5 +1,6 @@
|
||||
// tslint:disable:object-literal-key-quotes
|
||||
import { expect } from "chai";
|
||||
import { stub } from "sinon";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
@ -20,28 +21,30 @@ describe("Media", () => {
|
||||
});
|
||||
|
||||
it("should ensure the correct relationship id is used when adding image", () => {
|
||||
// tslint:disable-next-line:no-any
|
||||
stub(Media as any, "generateId").callsFake(() => "testId");
|
||||
|
||||
const file = new File();
|
||||
const image1 = Media.addImage(file, "test");
|
||||
|
||||
const tree = new Formatter().format(image1.Paragraph);
|
||||
const inlineElements = tree["w:p"][1]["w:r"][1]["w:drawing"][0]["wp:inline"];
|
||||
const inlineElements = tree["w:p"][0]["w:r"][0]["w:drawing"][0]["wp:inline"];
|
||||
const graphicData = inlineElements.find((x) => x["a:graphic"]);
|
||||
|
||||
expect(graphicData["a:graphic"][1]["a:graphicData"][1]["pic:pic"][2]["pic:blipFill"][0]["a:blip"][0]).to.deep.equal({
|
||||
expect(graphicData["a:graphic"][1]["a:graphicData"][1]["pic:pic"][2]["pic:blipFill"][0]["a:blip"]).to.deep.equal({
|
||||
_attr: {
|
||||
"r:embed": `rId${file.DocumentRelationships.RelationshipCount}`,
|
||||
"r:embed": `rId{testId.png}`,
|
||||
cstate: "none",
|
||||
},
|
||||
});
|
||||
|
||||
const image2 = Media.addImage(file, "test");
|
||||
const tree2 = new Formatter().format(image2.Paragraph);
|
||||
const inlineElements2 = tree2["w:p"][1]["w:r"][1]["w:drawing"][0]["wp:inline"];
|
||||
const inlineElements2 = tree2["w:p"][0]["w:r"][0]["w:drawing"][0]["wp:inline"];
|
||||
const graphicData2 = inlineElements2.find((x) => x["a:graphic"]);
|
||||
|
||||
expect(graphicData2["a:graphic"][1]["a:graphicData"][1]["pic:pic"][2]["pic:blipFill"][0]["a:blip"][0]).to.deep.equal({
|
||||
expect(graphicData2["a:graphic"][1]["a:graphicData"][1]["pic:pic"][2]["pic:blipFill"][0]["a:blip"]).to.deep.equal({
|
||||
_attr: {
|
||||
"r:embed": `rId${file.DocumentRelationships.RelationshipCount}`,
|
||||
"r:embed": `rId{testId.png}`,
|
||||
cstate: "none",
|
||||
},
|
||||
});
|
||||
@ -53,9 +56,8 @@ describe("Media", () => {
|
||||
// tslint:disable-next-line:no-any
|
||||
(Media as any).generateId = () => "test";
|
||||
|
||||
const image = new Media().addMedia("", 1);
|
||||
const image = new Media().addMedia("");
|
||||
expect(image.fileName).to.equal("test.png");
|
||||
expect(image.referenceId).to.equal(1);
|
||||
expect(image.dimensions).to.deep.equal({
|
||||
pixels: {
|
||||
x: 100,
|
||||
@ -74,7 +76,7 @@ describe("Media", () => {
|
||||
// tslint:disable-next-line:no-any
|
||||
(Media as any).generateId = () => "test";
|
||||
|
||||
const image = new Media().addMedia("", 1);
|
||||
const image = new Media().addMedia("");
|
||||
expect(image.stream).to.be.an.instanceof(Uint8Array);
|
||||
});
|
||||
});
|
||||
@ -85,12 +87,11 @@ describe("Media", () => {
|
||||
(Media as any).generateId = () => "test";
|
||||
|
||||
const media = new Media();
|
||||
media.addMedia("", 1);
|
||||
media.addMedia("");
|
||||
|
||||
const image = media.getMedia("test.png");
|
||||
|
||||
expect(image.fileName).to.equal("test.png");
|
||||
expect(image.referenceId).to.equal(1);
|
||||
expect(image.dimensions).to.deep.equal({
|
||||
pixels: {
|
||||
x: 100,
|
||||
@ -116,7 +117,7 @@ describe("Media", () => {
|
||||
(Media as any).generateId = () => "test";
|
||||
|
||||
const media = new Media();
|
||||
media.addMedia("", 1);
|
||||
media.addMedia("");
|
||||
|
||||
const array = media.Array;
|
||||
expect(array).to.be.an.instanceof(Array);
|
||||
@ -124,7 +125,6 @@ describe("Media", () => {
|
||||
|
||||
const image = array[0];
|
||||
expect(image.fileName).to.equal("test.png");
|
||||
expect(image.referenceId).to.equal(1);
|
||||
expect(image.dimensions).to.deep.equal({
|
||||
pixels: {
|
||||
x: 100,
|
||||
|
@ -4,11 +4,6 @@ import { ImageParagraph } from "../paragraph";
|
||||
import { IMediaData } from "./data";
|
||||
import { Image } from "./image";
|
||||
|
||||
interface IHackedFile {
|
||||
// tslint:disable-next-line:readonly-keyword
|
||||
currentRelationshipId: number;
|
||||
}
|
||||
|
||||
export class Media {
|
||||
public static addImage(
|
||||
file: File,
|
||||
@ -18,14 +13,7 @@ export class Media {
|
||||
drawingOptions?: IDrawingOptions,
|
||||
): Image {
|
||||
// Workaround to expose id without exposing to API
|
||||
const exposedFile = (file as {}) as IHackedFile;
|
||||
const mediaData = file.Media.addMedia(buffer, exposedFile.currentRelationshipId++, width, height);
|
||||
file.DocumentRelationships.createRelationship(
|
||||
mediaData.referenceId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
`media/${mediaData.fileName}`,
|
||||
);
|
||||
|
||||
const mediaData = file.Media.addMedia(buffer, width, height);
|
||||
return new Image(new ImageParagraph(mediaData, drawingOptions));
|
||||
}
|
||||
|
||||
@ -57,17 +45,11 @@ export class Media {
|
||||
return data;
|
||||
}
|
||||
|
||||
public addMedia(
|
||||
buffer: Buffer | string | Uint8Array | ArrayBuffer,
|
||||
referenceId: number,
|
||||
width: number = 100,
|
||||
height: number = 100,
|
||||
): IMediaData {
|
||||
public addMedia(buffer: Buffer | string | Uint8Array | ArrayBuffer, width: number = 100, height: number = 100): IMediaData {
|
||||
const key = `${Media.generateId()}.png`;
|
||||
|
||||
return this.createMedia(
|
||||
key,
|
||||
referenceId,
|
||||
{
|
||||
width: width,
|
||||
height: height,
|
||||
@ -78,7 +60,6 @@ export class Media {
|
||||
|
||||
private createMedia(
|
||||
key: string,
|
||||
relationshipsCount: number,
|
||||
dimensions: { readonly width: number; readonly height: number },
|
||||
data: Buffer | string | Uint8Array | ArrayBuffer,
|
||||
filePath?: string,
|
||||
@ -86,7 +67,6 @@ export class Media {
|
||||
const newData = typeof data === "string" ? this.convertDataURIToBinary(data) : data;
|
||||
|
||||
const imageData: IMediaData = {
|
||||
referenceId: relationshipsCount,
|
||||
stream: newData,
|
||||
path: filePath,
|
||||
fileName: key,
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from "./numbering";
|
||||
export * from "./abstract-numbering";
|
||||
export * from "./level";
|
||||
export * from "./num";
|
||||
|
@ -7,6 +7,8 @@ import { LevelForOverride } from "./level";
|
||||
import { Num } from "./num";
|
||||
import { Numbering } from "./numbering";
|
||||
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
|
||||
describe("Numbering", () => {
|
||||
let numbering: Numbering;
|
||||
|
||||
@ -22,7 +24,7 @@ describe("Numbering", () => {
|
||||
expect(abstractNums).to.have.lengthOf(1);
|
||||
expect(abstractNums[0]["w:abstractNum"]).to.deep.include.members([
|
||||
{ _attr: { "w:abstractNumId": 0, "w15:restartNumberingAfterBreak": 0 } },
|
||||
{ "w:multiLevelType": [{ _attr: { "w:val": "hybridMultilevel" } }] },
|
||||
{ "w:multiLevelType": { _attr: { "w:val": "hybridMultilevel" } } },
|
||||
]);
|
||||
|
||||
abstractNums
|
||||
@ -37,9 +39,9 @@ describe("Numbering", () => {
|
||||
{ "w:numFmt": [{ _attr: { "w:val": "bullet" } }] },
|
||||
]);
|
||||
// Once chai 4.0.0 lands and #644 is resolved, we can add the following to the test:
|
||||
// {"w:lvlText": [{"_attr": {"w:val": "•"}}]},
|
||||
// {"w:rPr": [{"w:rFonts": [{"_attr": {"w:ascii": "Symbol", "w:cs": "Symbol", "w:eastAsia": "Symbol", "w:hAnsi": "Symbol", "w:hint": "default"}}]}]},
|
||||
// {"w:pPr": [{"_attr": {}},
|
||||
// {"w:lvlText": {"_attr": {"w:val": "•"}}},
|
||||
// {"w:rPr": [{"w:rFonts": {"_attr": {"w:ascii": "Symbol", "w:cs": "Symbol", "w:eastAsia": "Symbol", "w:hAnsi": "Symbol", "w:hint": "default"}}}]},
|
||||
// {"w:pPr": [
|
||||
// {"w:ind": [{"_attr": {"w:left": 720, "w:hanging": 360}}]}]},
|
||||
});
|
||||
});
|
||||
@ -65,7 +67,7 @@ describe("Numbering", () => {
|
||||
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"][0]._attr["w:val"]).to.equal(a2.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", () => {
|
||||
@ -89,10 +91,10 @@ describe("AbstractNumbering", () => {
|
||||
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)" } }] });
|
||||
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", () => {
|
||||
@ -100,10 +102,10 @@ describe("AbstractNumbering", () => {
|
||||
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)" } }] });
|
||||
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", () => {
|
||||
@ -112,7 +114,7 @@ describe("AbstractNumbering", () => {
|
||||
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 } }] }],
|
||||
"w:pPr": [{ "w:ind": { _attr: { "w:left": 720 } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -121,7 +123,7 @@ describe("AbstractNumbering", () => {
|
||||
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 } }] }],
|
||||
"w:pPr": [{ "w:spacing": { _attr: { "w:before": 50, "w:after": 150 } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -130,7 +132,7 @@ describe("AbstractNumbering", () => {
|
||||
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" } }] }],
|
||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "center" } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -139,7 +141,7 @@ describe("AbstractNumbering", () => {
|
||||
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" } }] }],
|
||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "left" } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -148,7 +150,7 @@ describe("AbstractNumbering", () => {
|
||||
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" } }] }],
|
||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "right" } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -157,7 +159,7 @@ describe("AbstractNumbering", () => {
|
||||
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" } }] }],
|
||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "both" } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -170,16 +172,14 @@ describe("AbstractNumbering", () => {
|
||||
{
|
||||
"w:pBdr": [
|
||||
{
|
||||
"w:bottom": [
|
||||
{
|
||||
_attr: {
|
||||
"w:color": "auto",
|
||||
"w:space": "1",
|
||||
"w:val": "single",
|
||||
"w:sz": "6",
|
||||
},
|
||||
"w:bottom": {
|
||||
_attr: {
|
||||
"w:color": "auto",
|
||||
"w:space": "1",
|
||||
"w:val": "single",
|
||||
"w:sz": "6",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -194,7 +194,7 @@ describe("AbstractNumbering", () => {
|
||||
expect(tree["w:lvl"]).to.include({
|
||||
"w:pPr": [
|
||||
{
|
||||
"w:tabs": [{ "w:tab": [{ _attr: { "w:val": "left", "w:pos": 1200 } }] }],
|
||||
"w:tabs": [{ "w:tab": { _attr: { "w:val": "left", "w:pos": 1200 } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -207,7 +207,7 @@ describe("AbstractNumbering", () => {
|
||||
expect(tree["w:lvl"]).to.include({
|
||||
"w:pPr": [
|
||||
{
|
||||
"w:tabs": [{ "w:tab": [{ _attr: { "w:val": "right", "w:pos": 9026 } }] }],
|
||||
"w:tabs": [{ "w:tab": { _attr: { "w:val": "right", "w:pos": 9026 } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -218,7 +218,7 @@ describe("AbstractNumbering", () => {
|
||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").keepLines();
|
||||
const tree = new Formatter().format(level);
|
||||
expect(tree["w:lvl"]).to.include({
|
||||
"w:pPr": [{ "w:keepLines": [] }],
|
||||
"w:pPr": [{ "w:keepLines": EMPTY_OBJECT }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -227,7 +227,7 @@ describe("AbstractNumbering", () => {
|
||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").keepNext();
|
||||
const tree = new Formatter().format(level);
|
||||
expect(tree["w:lvl"]).to.include({
|
||||
"w:pPr": [{ "w:keepNext": [] }],
|
||||
"w:pPr": [{ "w:keepNext": EMPTY_OBJECT }],
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -238,7 +238,7 @@ describe("AbstractNumbering", () => {
|
||||
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 } }] }],
|
||||
"w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -247,7 +247,7 @@ describe("AbstractNumbering", () => {
|
||||
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 } }] }],
|
||||
"w:rPr": [{ "w:smallCaps": { _attr: { "w:val": true } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -256,7 +256,7 @@ describe("AbstractNumbering", () => {
|
||||
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 } }] }],
|
||||
"w:rPr": [{ "w:caps": { _attr: { "w:val": true } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -265,7 +265,7 @@ describe("AbstractNumbering", () => {
|
||||
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 } }] }],
|
||||
"w:rPr": [{ "w:strike": { _attr: { "w:val": true } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -274,7 +274,7 @@ describe("AbstractNumbering", () => {
|
||||
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 } }] }],
|
||||
"w:rPr": [{ "w:dstrike": { _attr: { "w:val": true } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -283,7 +283,7 @@ describe("AbstractNumbering", () => {
|
||||
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" } }] }],
|
||||
"w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "subscript" } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -292,7 +292,7 @@ describe("AbstractNumbering", () => {
|
||||
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" } }] }],
|
||||
"w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "superscript" } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -302,7 +302,7 @@ describe("AbstractNumbering", () => {
|
||||
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" } }] },
|
||||
{ "w:rFonts": { _attr: { "w:ascii": "Times", "w:cs": "Times", "w:eastAsia": "Times", "w:hAnsi": "Times" } } },
|
||||
],
|
||||
});
|
||||
});
|
||||
@ -312,7 +312,7 @@ describe("AbstractNumbering", () => {
|
||||
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 } }] }],
|
||||
"w:rPr": [{ "w:b": { _attr: { "w:val": true } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -321,7 +321,7 @@ describe("AbstractNumbering", () => {
|
||||
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 } }] }],
|
||||
"w:rPr": [{ "w:i": { _attr: { "w:val": true } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -331,7 +331,7 @@ describe("AbstractNumbering", () => {
|
||||
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" } }] }],
|
||||
"w:rPr": [{ "w:u": { _attr: { "w:val": "single" } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -340,7 +340,7 @@ describe("AbstractNumbering", () => {
|
||||
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" } }] }],
|
||||
"w:rPr": [{ "w:u": { _attr: { "w:val": "double" } } }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -349,7 +349,7 @@ describe("AbstractNumbering", () => {
|
||||
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" } }] }],
|
||||
"w:rPr": [{ "w:u": { _attr: { "w:val": "double", "w:color": "005599" } } }],
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -359,7 +359,7 @@ describe("AbstractNumbering", () => {
|
||||
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" } }] }],
|
||||
"w:rPr": [{ "w:color": { _attr: { "w:val": "123456" } } }],
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -388,20 +388,12 @@ describe("concrete numbering", () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:lvl": [
|
||||
{
|
||||
_attr: {
|
||||
"w:ilvl": 3,
|
||||
"w15:tentative": 1,
|
||||
},
|
||||
"w:lvl": {
|
||||
_attr: {
|
||||
"w:ilvl": 3,
|
||||
"w15:tentative": 1,
|
||||
},
|
||||
{
|
||||
"w:pPr": [],
|
||||
},
|
||||
{
|
||||
"w:rPr": [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -418,29 +410,19 @@ describe("concrete numbering", () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:startOverride": [
|
||||
{
|
||||
_attr: {
|
||||
"w:val": 9,
|
||||
},
|
||||
"w:startOverride": {
|
||||
_attr: {
|
||||
"w:val": 9,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:lvl": [
|
||||
{
|
||||
_attr: {
|
||||
"w:ilvl": 1,
|
||||
"w15:tentative": 1,
|
||||
},
|
||||
"w:lvl": {
|
||||
_attr: {
|
||||
"w:ilvl": 1,
|
||||
"w15:tentative": 1,
|
||||
},
|
||||
{
|
||||
"w:pPr": [],
|
||||
},
|
||||
{
|
||||
"w:rPr": [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -454,7 +436,7 @@ describe("concrete numbering", () => {
|
||||
"w:lvlOverride": [
|
||||
{ _attr: { "w:ilvl": 1 } },
|
||||
{
|
||||
"w:lvl": [{ _attr: { "w15:tentative": 1, "w:ilvl": 1 } }, { "w:pPr": [] }, { "w:rPr": [] }],
|
||||
"w:lvl": { _attr: { "w15:tentative": 1, "w:ilvl": 1 } },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
@ -10,7 +10,7 @@ describe("Spacing", () => {
|
||||
const spacing = new Spacing({ before: 100, after: 120, line: 150 });
|
||||
const tree = new Formatter().format(spacing);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:spacing": [{ _attr: { "w:after": 120, "w:before": 100, "w:line": 150 } }],
|
||||
"w:spacing": { _attr: { "w:after": 120, "w:before": 100, "w:line": 150 } },
|
||||
});
|
||||
});
|
||||
|
||||
@ -18,7 +18,7 @@ describe("Spacing", () => {
|
||||
const spacing = new Spacing({ before: 100 });
|
||||
const tree = new Formatter().format(spacing);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:spacing": [{ _attr: { "w:before": 100 } }],
|
||||
"w:spacing": { _attr: { "w:before": 100 } },
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -30,7 +30,7 @@ describe("ContextualSpacing", () => {
|
||||
const spacing = new ContextualSpacing(true);
|
||||
const tree = new Formatter().format(spacing);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:contextualSpacing": [{ _attr: { "w:val": 1 } }],
|
||||
"w:contextualSpacing": { _attr: { "w:val": 1 } },
|
||||
});
|
||||
});
|
||||
|
||||
@ -38,7 +38,7 @@ describe("ContextualSpacing", () => {
|
||||
const spacing = new ContextualSpacing(false);
|
||||
const tree = new Formatter().format(spacing);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:contextualSpacing": [{ _attr: { "w:val": 0 } }],
|
||||
"w:contextualSpacing": { _attr: { "w:val": 0 } },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -18,9 +18,9 @@ class SpacingAttributes extends XmlAttributeComponent<ISpacingProperties> {
|
||||
}
|
||||
|
||||
export class Spacing extends XmlComponent {
|
||||
constructor(opts: ISpacingProperties) {
|
||||
constructor(options: ISpacingProperties) {
|
||||
super("w:spacing");
|
||||
this.root.push(new SpacingAttributes(opts));
|
||||
this.root.push(new SpacingAttributes(options));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,15 +5,16 @@ import { Formatter } from "export/formatter";
|
||||
|
||||
import { ImageParagraph } from "./image";
|
||||
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
|
||||
describe("Image", () => {
|
||||
let image: ImageParagraph;
|
||||
|
||||
beforeEach(() => {
|
||||
image = new ImageParagraph({
|
||||
referenceId: 0,
|
||||
stream: new Buffer(""),
|
||||
path: "",
|
||||
fileName: "",
|
||||
fileName: "test.png",
|
||||
dimensions: {
|
||||
pixels: {
|
||||
x: 10,
|
||||
@ -46,14 +47,8 @@ describe("Image", () => {
|
||||
const tree = new Formatter().format(image);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [],
|
||||
},
|
||||
{
|
||||
"w:r": [
|
||||
{
|
||||
"w:rPr": [],
|
||||
},
|
||||
{
|
||||
"w:drawing": [
|
||||
{
|
||||
@ -67,49 +62,41 @@ describe("Image", () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:extent": [
|
||||
{
|
||||
_attr: {
|
||||
cx: 20,
|
||||
cy: 20,
|
||||
},
|
||||
"wp:extent": {
|
||||
_attr: {
|
||||
cx: 20,
|
||||
cy: 20,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:effectExtent": [
|
||||
{
|
||||
_attr: {
|
||||
b: 0,
|
||||
l: 0,
|
||||
r: 0,
|
||||
t: 0,
|
||||
},
|
||||
"wp:effectExtent": {
|
||||
_attr: {
|
||||
b: 0,
|
||||
l: 0,
|
||||
r: 0,
|
||||
t: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:docPr": [
|
||||
{
|
||||
_attr: {
|
||||
descr: "",
|
||||
id: 0,
|
||||
name: "",
|
||||
},
|
||||
"wp:docPr": {
|
||||
_attr: {
|
||||
descr: "",
|
||||
id: 0,
|
||||
name: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"wp:cNvGraphicFramePr": [
|
||||
{
|
||||
"a:graphicFrameLocks": [
|
||||
{
|
||||
_attr: {
|
||||
noChangeAspect: 1,
|
||||
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||
},
|
||||
"a:graphicFrameLocks": {
|
||||
_attr: {
|
||||
noChangeAspect: 1,
|
||||
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -138,27 +125,23 @@ describe("Image", () => {
|
||||
{
|
||||
"pic:nvPicPr": [
|
||||
{
|
||||
"pic:cNvPr": [
|
||||
{
|
||||
_attr: {
|
||||
desc: "",
|
||||
id: 0,
|
||||
name: "",
|
||||
},
|
||||
"pic:cNvPr": {
|
||||
_attr: {
|
||||
desc: "",
|
||||
id: 0,
|
||||
name: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"pic:cNvPicPr": [
|
||||
{
|
||||
"a:picLocks": [
|
||||
{
|
||||
_attr: {
|
||||
noChangeArrowheads: 1,
|
||||
noChangeAspect: 1,
|
||||
},
|
||||
"a:picLocks": {
|
||||
_attr: {
|
||||
noChangeArrowheads: 1,
|
||||
noChangeAspect: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -167,22 +150,20 @@ describe("Image", () => {
|
||||
{
|
||||
"pic:blipFill": [
|
||||
{
|
||||
"a:blip": [
|
||||
{
|
||||
_attr: {
|
||||
cstate: "none",
|
||||
"r:embed": "rId0",
|
||||
},
|
||||
"a:blip": {
|
||||
_attr: {
|
||||
cstate: "none",
|
||||
"r:embed": "rId{test.png}",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:srcRect": [],
|
||||
"a:srcRect": EMPTY_OBJECT,
|
||||
},
|
||||
{
|
||||
"a:stretch": [
|
||||
{
|
||||
"a:fillRect": [],
|
||||
"a:fillRect": EMPTY_OBJECT,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -198,24 +179,20 @@ describe("Image", () => {
|
||||
{
|
||||
"a:xfrm": [
|
||||
{
|
||||
"a:ext": [
|
||||
{
|
||||
_attr: {
|
||||
cx: 10,
|
||||
cy: 10,
|
||||
},
|
||||
"a:ext": {
|
||||
_attr: {
|
||||
cx: 10,
|
||||
cy: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:off": [
|
||||
{
|
||||
_attr: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
"a:off": {
|
||||
_attr: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -227,7 +204,7 @@ describe("Image", () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
"a:avLst": [],
|
||||
"a:avLst": EMPTY_OBJECT,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -31,7 +31,7 @@ describe("Hyperlink", () => {
|
||||
const tree = new Formatter().format(hyperlink);
|
||||
const runJson = {
|
||||
"w:r": [
|
||||
{ "w:rPr": [{ "w:rStyle": [{ _attr: { "w:val": "Hyperlink" } }] }] },
|
||||
{ "w:rPr": [{ "w:rStyle": { _attr: { "w:val": "Hyperlink" } } }] },
|
||||
{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "https://example.com"] },
|
||||
],
|
||||
};
|
||||
|
@ -5,6 +5,7 @@ import { HyperlinkAttributes, IHyperlinkAttributesProperties } from "./hyperlink
|
||||
|
||||
export class Hyperlink extends XmlComponent {
|
||||
public readonly linkId: number;
|
||||
private readonly textRun: TextRun;
|
||||
|
||||
constructor(text: string, relationshipsCount: number, anchor?: string) {
|
||||
super("w:hyperlink");
|
||||
@ -19,6 +20,11 @@ export class Hyperlink extends XmlComponent {
|
||||
|
||||
const attributes = new HyperlinkAttributes(props);
|
||||
this.root.push(attributes);
|
||||
this.root.push(new TextRun(text).style("Hyperlink"));
|
||||
this.textRun = new TextRun(text).style("Hyperlink");
|
||||
this.root.push(this.textRun);
|
||||
}
|
||||
|
||||
public get TextRun(): TextRun {
|
||||
return this.textRun;
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,3 @@
|
||||
export * from "./hyperlink";
|
||||
export * from "./bookmark";
|
||||
export * from "./outline-level";
|
||||
|
23
src/file/paragraph/links/outline-level.spec.ts
Normal file
23
src/file/paragraph/links/outline-level.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { assert } from "chai";
|
||||
|
||||
import { Utility } from "tests/utility";
|
||||
|
||||
import { OutlineLevel } from "./outline-level";
|
||||
|
||||
describe("ParagraphOutlineLevel", () => {
|
||||
let outlineLevel: OutlineLevel;
|
||||
|
||||
describe("#constructor()", () => {
|
||||
it("should create an outlineLevel with given value", () => {
|
||||
outlineLevel = new OutlineLevel("0");
|
||||
const newJson = Utility.jsonify(outlineLevel);
|
||||
assert.equal(newJson.root[0].root.val, "0");
|
||||
});
|
||||
|
||||
it("should create an outlineLevel with blank val", () => {
|
||||
outlineLevel = new OutlineLevel("");
|
||||
const newJson = Utility.jsonify(outlineLevel);
|
||||
assert.equal(newJson.root[0].root.val, "");
|
||||
});
|
||||
});
|
||||
});
|
16
src/file/paragraph/links/outline-level.ts
Normal file
16
src/file/paragraph/links/outline-level.ts
Normal file
@ -0,0 +1,16 @@
|
||||
// http://officeopenxml.com/WPparagraph.php
|
||||
import { Attributes, XmlComponent } from "file/xml-components";
|
||||
|
||||
export class OutlineLevel extends XmlComponent {
|
||||
public readonly level: string;
|
||||
|
||||
constructor(level: string) {
|
||||
super("w:outlineLvl");
|
||||
this.level = level;
|
||||
this.root.push(
|
||||
new Attributes({
|
||||
val: level,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -4,6 +4,9 @@ import { Formatter } from "export/formatter";
|
||||
import * as file from "file";
|
||||
|
||||
import { Numbering } from "../numbering";
|
||||
import { LeaderType } from "./formatting";
|
||||
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
|
||||
describe("Paragraph", () => {
|
||||
let paragraph: file.Paragraph;
|
||||
@ -39,7 +42,7 @@ describe("Paragraph", () => {
|
||||
expect(tree)
|
||||
.to.be.an("array")
|
||||
.which.includes({
|
||||
"w:r": [{ "w:rPr": [] }, { "w:t": [{ _attr: { "xml:space": "preserve" } }, "this is a test run"] }],
|
||||
"w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "this is a test run"] }],
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -51,7 +54,7 @@ describe("Paragraph", () => {
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:pStyle": [{ _attr: { "w:val": "Heading1" } }] }],
|
||||
"w:pPr": [{ "w:pStyle": { _attr: { "w:val": "Heading1" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -65,7 +68,7 @@ describe("Paragraph", () => {
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:pStyle": [{ _attr: { "w:val": "Heading2" } }] }],
|
||||
"w:pPr": [{ "w:pStyle": { _attr: { "w:val": "Heading2" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -79,7 +82,49 @@ describe("Paragraph", () => {
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:pStyle": [{ _attr: { "w:val": "Heading3" } }] }],
|
||||
"w:pPr": [{ "w:pStyle": { _attr: { "w:val": "Heading3" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#heading4()", () => {
|
||||
it("should add heading style to JSON", () => {
|
||||
paragraph.heading4();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:pStyle": { _attr: { "w:val": "Heading4" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#heading5()", () => {
|
||||
it("should add heading style to JSON", () => {
|
||||
paragraph.heading5();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:pStyle": { _attr: { "w:val": "Heading5" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#heading6()", () => {
|
||||
it("should add heading style to JSON", () => {
|
||||
paragraph.heading6();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:pStyle": { _attr: { "w:val": "Heading6" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -93,7 +138,7 @@ describe("Paragraph", () => {
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:pStyle": [{ _attr: { "w:val": "Title" } }] }],
|
||||
"w:pPr": [{ "w:pStyle": { _attr: { "w:val": "Title" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -107,7 +152,228 @@ describe("Paragraph", () => {
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:jc": [{ _attr: { "w:val": "center" } }] }],
|
||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "center" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#left()", () => {
|
||||
it("should add left alignment to JSON", () => {
|
||||
paragraph.left();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "left" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#right()", () => {
|
||||
it("should add right alignment to JSON", () => {
|
||||
paragraph.right();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "right" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#start()", () => {
|
||||
it("should add start alignment to JSON", () => {
|
||||
paragraph.start();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "start" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#end()", () => {
|
||||
it("should add end alignment to JSON", () => {
|
||||
paragraph.end();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "end" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#distribute()", () => {
|
||||
it("should add distribute alignment to JSON", () => {
|
||||
paragraph.distribute();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "distribute" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#justified()", () => {
|
||||
it("should add justified alignment to JSON", () => {
|
||||
paragraph.justified();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:jc": { _attr: { "w:val": "both" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#maxRightTabStop()", () => {
|
||||
it("should add maxRightTabStop to JSON", () => {
|
||||
paragraph.maxRightTabStop();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [
|
||||
{
|
||||
"w:tabs": [
|
||||
{
|
||||
"w:tab": {
|
||||
_attr: {
|
||||
"w:pos": 9026,
|
||||
"w:val": "right",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#leftTabStop()", () => {
|
||||
it("should add leftTabStop to JSON", () => {
|
||||
paragraph.leftTabStop(100, LeaderType.HYPHEN);
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [
|
||||
{
|
||||
"w:tabs": [
|
||||
{
|
||||
"w:tab": {
|
||||
_attr: {
|
||||
"w:pos": 100,
|
||||
"w:val": "left",
|
||||
"w:leader": "hyphen",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#rightTabStop()", () => {
|
||||
it("should add rightTabStop to JSON", () => {
|
||||
paragraph.rightTabStop(100, LeaderType.DOT);
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [
|
||||
{
|
||||
"w:tabs": [
|
||||
{
|
||||
"w:tab": {
|
||||
_attr: {
|
||||
"w:pos": 100,
|
||||
"w:val": "right",
|
||||
"w:leader": "dot",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#centerTabStop()", () => {
|
||||
it("should add centerTabStop to JSON", () => {
|
||||
paragraph.centerTabStop(100, LeaderType.MIDDLE_DOT);
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [
|
||||
{
|
||||
"w:tabs": [
|
||||
{
|
||||
"w:tab": {
|
||||
_attr: {
|
||||
"w:pos": 100,
|
||||
"w:val": "center",
|
||||
"w:leader": "middleDot",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#contextualSpacing()", () => {
|
||||
it("should add contextualSpacing to JSON, and set 1 if true", () => {
|
||||
paragraph.contextualSpacing(true);
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:contextualSpacing": { _attr: { "w:val": 1 } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add contextualSpacing to JSON, and set 0 if false", () => {
|
||||
paragraph.contextualSpacing(false);
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:contextualSpacing": { _attr: { "w:val": 0 } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -125,16 +391,14 @@ describe("Paragraph", () => {
|
||||
{
|
||||
"w:pBdr": [
|
||||
{
|
||||
"w:bottom": [
|
||||
{
|
||||
_attr: {
|
||||
"w:val": "single",
|
||||
"w:color": "auto",
|
||||
"w:space": "1",
|
||||
"w:sz": "6",
|
||||
},
|
||||
"w:bottom": {
|
||||
_attr: {
|
||||
"w:val": "single",
|
||||
"w:color": "auto",
|
||||
"w:space": "1",
|
||||
"w:sz": "6",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -158,28 +422,24 @@ describe("Paragraph", () => {
|
||||
{
|
||||
"w:pBdr": [
|
||||
{
|
||||
"w:left": [
|
||||
{
|
||||
_attr: {
|
||||
"w:color": "auto",
|
||||
"w:space": "1",
|
||||
"w:sz": "6",
|
||||
"w:val": "single",
|
||||
},
|
||||
"w:left": {
|
||||
_attr: {
|
||||
"w:color": "auto",
|
||||
"w:space": "1",
|
||||
"w:sz": "6",
|
||||
"w:val": "single",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:right": [
|
||||
{
|
||||
_attr: {
|
||||
"w:color": "auto",
|
||||
"w:space": "1",
|
||||
"w:sz": "6",
|
||||
"w:val": "single",
|
||||
},
|
||||
"w:right": {
|
||||
_attr: {
|
||||
"w:color": "auto",
|
||||
"w:space": "1",
|
||||
"w:sz": "6",
|
||||
"w:val": "single",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -197,10 +457,7 @@ describe("Paragraph", () => {
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [],
|
||||
},
|
||||
{
|
||||
"w:r": [{ "w:rPr": [] }, { "w:br": [{ _attr: { "w:type": "page" } }] }],
|
||||
"w:r": [{ "w:br": { _attr: { "w:type": "page" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -216,7 +473,7 @@ describe("Paragraph", () => {
|
||||
{
|
||||
"w:pPr": [
|
||||
{
|
||||
"w:pageBreakBefore": [],
|
||||
"w:pageBreakBefore": EMPTY_OBJECT,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -238,7 +495,7 @@ describe("Paragraph", () => {
|
||||
.which.is.an("array")
|
||||
.which.has.length.at.least(1);
|
||||
expect(tree["w:p"][0]["w:pPr"][0]).to.deep.equal({
|
||||
"w:pStyle": [{ _attr: { "w:val": "ListParagraph" } }],
|
||||
"w:pStyle": { _attr: { "w:val": "ListParagraph" } },
|
||||
});
|
||||
});
|
||||
|
||||
@ -254,7 +511,7 @@ describe("Paragraph", () => {
|
||||
.which.is.an("array")
|
||||
.which.has.length.at.least(1);
|
||||
expect(tree["w:p"][0]["w:pPr"][0]).to.deep.equal({
|
||||
"w:pStyle": [{ _attr: { "w:val": "ListParagraph" } }],
|
||||
"w:pStyle": { _attr: { "w:val": "ListParagraph" } },
|
||||
});
|
||||
});
|
||||
|
||||
@ -270,7 +527,7 @@ describe("Paragraph", () => {
|
||||
.which.is.an("array")
|
||||
.which.has.length.at.least(2);
|
||||
expect(tree["w:p"][0]["w:pPr"][1]).to.deep.equal({
|
||||
"w:numPr": [{ "w:ilvl": [{ _attr: { "w:val": 1 } }] }, { "w:numId": [{ _attr: { "w:val": 1 } }] }],
|
||||
"w:numPr": [{ "w:ilvl": { _attr: { "w:val": 1 } } }, { "w:numId": { _attr: { "w:val": 1 } } }],
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -293,7 +550,7 @@ describe("Paragraph", () => {
|
||||
.which.is.an("array")
|
||||
.which.has.length.at.least(1);
|
||||
expect(tree["w:p"][0]["w:pPr"][0]).to.deep.equal({
|
||||
"w:pStyle": [{ _attr: { "w:val": "ListParagraph" } }],
|
||||
"w:pStyle": { _attr: { "w:val": "ListParagraph" } },
|
||||
});
|
||||
});
|
||||
|
||||
@ -309,11 +566,11 @@ describe("Paragraph", () => {
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [
|
||||
{ "w:pStyle": [{ _attr: { "w:val": "ListParagraph" } }] },
|
||||
{ "w:pStyle": { _attr: { "w:val": "ListParagraph" } } },
|
||||
{
|
||||
"w:numPr": [
|
||||
{ "w:ilvl": [{ _attr: { "w:val": 0 } }] },
|
||||
{ "w:numId": [{ _attr: { "w:val": letterNumbering.id } }] },
|
||||
{ "w:ilvl": { _attr: { "w:val": 0 } } },
|
||||
{ "w:numId": { _attr: { "w:val": letterNumbering.id } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@ -330,7 +587,7 @@ describe("Paragraph", () => {
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:pStyle": [{ _attr: { "w:val": "myFancyStyle" } }] }],
|
||||
"w:pPr": [{ "w:pStyle": { _attr: { "w:val": "myFancyStyle" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -344,7 +601,7 @@ describe("Paragraph", () => {
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:ind": [{ _attr: { "w:left": 720 } }] }],
|
||||
"w:pPr": [{ "w:ind": { _attr: { "w:left": 720 } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -358,7 +615,7 @@ describe("Paragraph", () => {
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:spacing": [{ _attr: { "w:before": 90, "w:line": 50 } }] }],
|
||||
"w:pPr": [{ "w:spacing": { _attr: { "w:before": 90, "w:line": 50 } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -370,7 +627,7 @@ describe("Paragraph", () => {
|
||||
paragraph.keepLines();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [{ "w:pPr": [{ "w:keepLines": [] }] }],
|
||||
"w:p": [{ "w:pPr": [{ "w:keepLines": EMPTY_OBJECT }] }],
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -380,7 +637,7 @@ describe("Paragraph", () => {
|
||||
paragraph.keepNext();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [{ "w:pPr": [{ "w:keepNext": [] }] }],
|
||||
"w:p": [{ "w:pPr": [{ "w:keepNext": EMPTY_OBJECT }] }],
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -390,7 +647,21 @@ describe("Paragraph", () => {
|
||||
paragraph.bidirectional();
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [{ "w:pPr": [{ "w:bidi": [] }] }],
|
||||
"w:p": [{ "w:pPr": [{ "w:bidi": EMPTY_OBJECT }] }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#outlineLevel", () => {
|
||||
it("should set paragraph outline level to the given value", () => {
|
||||
paragraph.outlineLevel("0");
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:pPr": [{ "w:outlineLvl": { _attr: { "w:val": "0" } } }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -14,7 +14,7 @@ import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spa
|
||||
import { Style } from "./formatting/style";
|
||||
import { CenterTabStop, LeaderType, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop";
|
||||
import { NumberProperties } from "./formatting/unordered-list";
|
||||
import { Bookmark, Hyperlink } from "./links";
|
||||
import { Bookmark, Hyperlink, OutlineLevel } from "./links";
|
||||
import { ParagraphProperties } from "./properties";
|
||||
import { PictureRun, Run, SequentialIdentifier, TextRun } from "./run";
|
||||
|
||||
@ -241,13 +241,13 @@ export class Paragraph extends XmlComponent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public addTabStop(run: Run): Paragraph {
|
||||
this.root.splice(1, 0, run);
|
||||
return this;
|
||||
}
|
||||
|
||||
public addSequentialIdentifier(identifier: string): Paragraph {
|
||||
this.root.push(new SequentialIdentifier(identifier));
|
||||
return this;
|
||||
}
|
||||
|
||||
public outlineLevel(level: string): Paragraph {
|
||||
this.properties.push(new OutlineLevel(level));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user