Compare commits

..

63 Commits

Author SHA1 Message Date
75c3c2f985 Version bump 2019-10-10 21:27:27 +01:00
fefefdd473 Merge pull request #421 from dolanmiu/feat/declaritive-styles-and-tab-stop
Declaritive styles and  multiple tab stops
2019-10-10 21:26:30 +01:00
db59474f1e Remove unused method 2019-10-10 21:10:03 +01:00
1d5e806ff4 Add character style tests 2019-10-10 21:03:38 +01:00
bf4885c7cf Merge branch 'master' of github.com:dolanmiu/docx into feat/declaritive-styles-and-tab-stop 2019-10-10 01:58:13 +01:00
3b289be5ce Fix demo 2019-10-10 01:25:37 +01:00
2bb7e08ade Remove unnecessary method 2019-10-10 01:19:55 +01:00
b571a7550f Fix demo 2019-10-10 01:18:20 +01:00
721de30587 Fix demos 2019-10-10 01:08:01 +01:00
f16126e948 Fix tests 2019-10-09 21:19:41 +01:00
40d1a3a7c2 Multiple tab stops 2019-10-09 20:56:31 +01:00
0d4c7a5fc0 Update documentation 2019-10-09 02:03:39 +01:00
5d401dfb27 Merge pull request #419 from jamesmontalvo3/section-fix
Revert "fix: try to remove unnecessary paragraph", which caused #418
2019-10-05 14:16:06 +01:00
a37c9d8f2f Revert "fix: try to remove unnecessary paragraph", which caused #418
This reverts commit cb74868247.
2019-10-03 22:12:23 -05:00
10ab3c70bf Add back default styles 2019-10-04 02:37:22 +01:00
591b2f4e04 Declarative styles 2019-10-04 01:20:41 +01:00
2536fbe752 Merge branch 'master' of github.com:dolanmiu/docx into feat/declaritive-styles-and-tab-stop
# Conflicts:
#	src/file/paragraph/paragraph.ts
2019-10-03 20:48:34 +01:00
33549d5ec3 Merge pull request #413 from jamesmontalvo3/master
Add SymbolRun to allow adding symbols inline
2019-10-02 21:41:24 +01:00
720c6172e3 Merge pull request #415 from jamesmontalvo3/export-run-fonts
Export RunFonts to be able to pass to Level.addRunProperty()
2019-10-02 21:40:40 +01:00
bbad2f5cde Merge pull request #416 from kalda341/fix-toc-t-flag
Fix table of contents \t flag
2019-10-02 21:27:21 +01:00
d2a0baa221 Fix table of contents \t flag 2019-10-02 14:49:55 +13:00
ad62f5459b Export RunFonts to be able to pass to Level.addRunProperty() 2019-10-01 15:23:01 -05:00
dfb910defb Add SymbolRun to allow adding symbols inline 2019-10-01 12:29:07 -05:00
04b6d8e54a Declarative hyperlinks, bookmarks, tab stops and page breaks 2019-09-30 22:56:21 +01:00
d2dded860d Fix example links 2019-09-30 20:58:18 +01:00
38f2638ea0 Version bump 2019-09-29 04:45:30 +01:00
2b874e3f69 Merge pull request #407 from dolanmiu/feat/declaritive-tables
Feat/declarative tables
2019-09-29 04:43:37 +01:00
b43ed12c84 Fix tests 2019-09-29 04:38:07 +01:00
59be381213 Update travis config 2019-09-29 04:25:40 +01:00
172c333357 Add tests and clean up code 2019-09-29 04:17:21 +01:00
c5eb3d5670 Add addMargin test 2019-09-26 02:24:43 +01:00
44b95f2f15 Add shading test 2019-09-26 02:14:52 +01:00
bd888219fc Amend table documentation 2019-09-26 02:03:17 +01:00
2842619196 Update table documentation 2019-09-25 01:59:30 +01:00
b2de74a0e6 Fix tests 2019-09-25 01:09:53 +01:00
7aa4134e2b Refactor row merging to table level 2019-09-25 00:57:24 +01:00
cc36ea7542 Optimise formatting to not over-format the same files 2019-09-22 20:45:24 +01:00
c11af71ed7 Add test such that it only should call prep once 2019-09-22 19:09:34 +01:00
a9d4ebc898 Add declarative column merge 2019-09-22 02:39:38 +01:00
d2f82052b4 Improve documentation 2019-09-19 22:49:09 +01:00
f88a309d55 Merge branch 'master' of github.com:dolanmiu/docx into feat/declaritive-tables 2019-09-13 00:52:26 +01:00
418adca9f3 Declarative tables 2019-09-13 00:51:20 +01:00
993f8f81f7 Merge pull request #392 from wainstead/parameter-fix
Fix: probable copy/paste error
2019-09-01 16:19:03 +01:00
99718784f1 Fix: probable copy/paste error
Fix the argument to "left:" which should likely be 2160
2019-08-30 09:29:45 -04:00
59fc1ed632 Merge pull request #391 from bokuweb/remove-extra-line
fix: try to remove unnecessary paragraph
2019-08-29 20:34:32 +01:00
cb74868247 fix: try to remove unnecessary paragraph 2019-08-29 20:52:05 +09:00
bd6ae2c0dc Remove unnecessary documentation 2019-08-22 22:49:57 +01:00
60a599a550 Improve bullet points documentation 2019-08-22 00:28:42 +01:00
4ac55a787e Fix includes 2019-08-21 00:53:49 +01:00
cb52a1ef42 Formatting 2019-08-21 00:51:26 +01:00
7827d158d7 Updated documentation 2019-08-21 00:05:46 +01:00
535f2d75b0 Fix embed links in documentation 2019-08-20 22:23:14 +01:00
5ecdb48d43 Stop unnecessary casting 2019-08-15 00:48:36 +01:00
2502fe7f39 Increase thresholds 2019-08-14 15:50:12 +01:00
02487fbb22 Merge pull request #376 from mforman1/Numbering
Add total number of pages in a section
2019-08-14 13:29:35 +01:00
2abff6991f Rename demo 2019-08-14 14:13:28 +03:00
49e85275c3 Fix tests 2019-08-13 10:53:29 +03:00
96efdf8b1a Merge branch 'master' of github.com:dolanmiu/docx into Numbering
# Conflicts:
#	src/file/paragraph/run/run.ts
2019-08-13 10:40:49 +03:00
b52a4adaff Merge pull request #375 from fpirsch/fpirsch-compression
Enable zip compression
2019-08-09 11:17:22 +01:00
a9fc40dad4 Add total number of pages in a section 2019-08-09 11:56:22 +03:00
b609a17362 Update packer.ts 2019-08-09 09:41:58 +02:00
4b49fdbf8e Update packer.ts 2019-08-09 00:19:44 +01:00
fd52c8cf47 Enable zip compression 2019-08-08 16:40:16 +02:00
109 changed files with 4621 additions and 2033 deletions

8
.nycrc
View File

@ -1,9 +1,9 @@
{
"check-coverage": true,
"lines": 87.54,
"functions": 83.61,
"branches": 72.57,
"statements": 87.32,
"lines": 92.35,
"functions": 88.28,
"branches": 84.64,
"statements": 92.16,
"include": [
"src/**/*.ts"
],

View File

@ -42,7 +42,7 @@ script:
- npm run ts-node -- ./demo/29-numbered-lists.ts
- npm run ts-node -- ./demo/30-template-document.ts
- npm run ts-node -- ./demo/31-tables.ts
- npm run ts-node -- ./demo/32-merge-table-cells.ts
- npm run ts-node -- ./demo/32-merge-and-shade-table-cells.ts
# - npm run e2e "My Document.docx" // Need to fix
- npm run ts-node -- ./demo/33-sequential-captions.ts
- npm run ts-node -- ./demo/34-floating-tables.ts

View File

@ -1,7 +1,7 @@
// Generate a CV
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { AlignmentType, Document, HeadingLevel, Packer, Paragraph, TextRun } from "../build";
import { AlignmentType, Document, HeadingLevel, Packer, Paragraph, TabStopPosition, TabStopType, TextRun } from "../build";
// tslint:disable:no-shadowed-variable
@ -226,9 +226,12 @@ class DocumentCreator {
public createInstitutionHeader(institutionName: string, dateText: string): Paragraph {
return new Paragraph({
tabStop: {
maxRight: {},
tabStops: [
{
type: TabStopType.RIGHT,
position: TabStopPosition.MAX,
},
],
children: [
new TextRun({
text: institutionName,

View File

@ -1,93 +1,188 @@
// Setting styles with JavaScript configuration
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { AlignmentType, Document, Footer, HeadingLevel, Media, Packer, Paragraph, Table } from "../build";
import {
AlignmentType,
Document,
Footer,
HeadingLevel,
Media,
Packer,
Paragraph,
Table,
TableCell,
TableRow,
TabStopPosition,
UnderlineType,
} from "../build";
const doc = new Document();
doc.Styles.createParagraphStyle("Heading1", "Heading 1")
.basedOn("Normal")
.next("Normal")
.quickFormat()
.font("Calibri")
.size(52)
.center()
.bold()
.color("000000")
.spacing({ line: 340 })
.underline("single", "000000");
doc.Styles.createParagraphStyle("Heading2", "Heading 2")
.basedOn("Normal")
.next("Normal")
.font("Calibri")
.quickFormat()
.size(26)
.bold()
.spacing({ line: 340 });
doc.Styles.createParagraphStyle("Heading3", "Heading 3")
.basedOn("Normal")
.next("Normal")
.font("Calibri")
.quickFormat()
.size(26)
.bold()
.spacing({ line: 276 });
doc.Styles.createParagraphStyle("Heading4", "Heading 4")
.basedOn("Normal")
.next("Normal")
.justified()
.font("Calibri")
.size(26)
.bold();
doc.Styles.createParagraphStyle("normalPara", "Normal Para")
.basedOn("Normal")
.next("Normal")
.font("Calibri")
.quickFormat()
.leftTabStop(453.543307087)
.maxRightTabStop()
.size(26)
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
doc.Styles.createParagraphStyle("normalPara2", "Normal Para2")
.basedOn("Normal")
.next("Normal")
.quickFormat()
.font("Calibri")
.size(26)
.justified()
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
doc.Styles.createParagraphStyle("aside", "Aside")
.basedOn("Normal")
.next("Normal")
.color("999999")
.italics()
.indent({ left: 720 })
.spacing({ line: 276 });
doc.Styles.createParagraphStyle("wellSpaced", "Well Spaced")
.basedOn("Normal")
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph")
.quickFormat()
.basedOn("Normal");
const doc = new Document({
styles: {
paragraphStyles: [
{
id: "Heading1",
name: "Heading 1",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
font: "Calibri",
size: 52,
bold: true,
color: "000000",
underline: {
type: UnderlineType.SINGLE,
color: "000000",
},
},
paragraph: {
alignment: AlignmentType.CENTER,
spacing: { line: 340 },
},
},
{
id: "Heading2",
name: "Heading 2",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
font: "Calibri",
size: 26,
bold: true,
},
paragraph: {
spacing: { line: 340 },
},
},
{
id: "Heading3",
name: "Heading 3",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
font: "Calibri",
size: 26,
bold: true,
},
paragraph: {
spacing: { line: 276 },
},
},
{
id: "Heading4",
name: "Heading 4",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
font: "Calibri",
size: 26,
bold: true,
},
paragraph: {
alignment: AlignmentType.JUSTIFIED,
},
},
{
id: "normalPara",
name: "Normal Para",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
font: "Calibri",
size: 26,
bold: true,
},
paragraph: {
spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 },
rightTabStop: TabStopPosition.MAX,
leftTabStop: 453.543307087,
},
},
{
id: "normalPara2",
name: "Normal Para2",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
font: "Calibri",
size: 26,
},
paragraph: {
alignment: AlignmentType.JUSTIFIED,
spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 },
},
},
{
id: "aside",
name: "Aside",
basedOn: "Normal",
next: "Normal",
run: {
color: "999999",
italics: true,
},
paragraph: {
spacing: { line: 276 },
indent: { left: 720 },
},
},
{
id: "wellSpaced",
name: "Well Spaced",
basedOn: "Normal",
paragraph: {
spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 },
},
},
{
id: "ListParagraph",
name: "List Paragraph",
basedOn: "Normal",
quickFormat: true,
},
],
},
});
const image = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));
const table = new Table({
rows: 4,
columns: 4,
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("Test cell 1.")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("Test cell 2.")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("Test cell 3.")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("Test cell 4.")],
}),
],
}),
],
});
table
.getRow(0)
.getCell(0)
.add(new Paragraph("Pole No."));
const image1 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));
const image2 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));

View File

@ -1,7 +1,7 @@
// Page numbers
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { AlignmentType, Document, Header, Packer, Paragraph, TextRun } from "../build";
import { AlignmentType, Document, Header, Packer, PageBreak, Paragraph, TextRun } from "../build";
const doc = new Document();
@ -26,8 +26,8 @@ doc.addSection({
},
children: [
new Paragraph({
text: "First Page",
}).pageBreak(),
children: [new TextRun("First Page"), new PageBreak()],
}),
new Paragraph("Second Page"),
],
});

View File

@ -6,7 +6,7 @@ import { Document, Footer, Header, Packer, PageNumberFormat, PageOrientation, Pa
const doc = new Document();
doc.addSection({
children: [new Paragraph("Hello World").pageBreak()],
children: [new Paragraph("Hello World")],
});
doc.addSection({

View File

@ -1,48 +1,89 @@
// Example on how to customise the look at feel using Styles
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, HeadingLevel, Packer, Paragraph, TextRun } from "../build";
import { Document, HeadingLevel, Packer, Paragraph, TextRun, UnderlineType } from "../build";
const doc = new Document({
creator: "Clippy",
title: "Sample Document",
description: "A brief example of using docx",
styles: {
paragraphStyles: [
{
id: "Heading1",
name: "Heading 1",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
size: 28,
bold: true,
italics: true,
},
paragraph: {
spacing: {
after: 120,
},
},
},
{
id: "Heading2",
name: "Heading 2",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
size: 26,
bold: true,
underline: {
type: UnderlineType.DOUBLE,
color: "FF0000",
},
},
paragraph: {
spacing: {
before: 240,
after: 120,
},
},
},
{
id: "aside",
name: "Aside",
basedOn: "Normal",
next: "Normal",
run: {
color: "999999",
italics: true,
},
paragraph: {
indent: {
left: 720,
},
spacing: {
line: 276,
},
},
},
{
id: "wellSpaced",
name: "Well Spaced",
basedOn: "Normal",
quickFormat: true,
paragraph: {
spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 },
},
},
{
id: "ListParagraph",
name: "List Paragraph",
basedOn: "Normal",
quickFormat: true,
},
],
},
});
doc.Styles.createParagraphStyle("Heading1", "Heading 1")
.basedOn("Normal")
.next("Normal")
.quickFormat()
.size(28)
.bold()
.italics()
.spacing({ after: 120 });
doc.Styles.createParagraphStyle("Heading2", "Heading 2")
.basedOn("Normal")
.next("Normal")
.quickFormat()
.size(26)
.bold()
.underline("double", "FF0000")
.spacing({ before: 240, after: 120 });
doc.Styles.createParagraphStyle("aside", "Aside")
.basedOn("Normal")
.next("Normal")
.color("999999")
.italics()
.indent({ left: 720 })
.spacing({ line: 276 });
doc.Styles.createParagraphStyle("wellSpaced", "Well Spaced")
.basedOn("Normal")
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph")
.quickFormat()
.basedOn("Normal");
const numberedAbstract = doc.Numbering.createAbstractNumbering();
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "left");
@ -82,14 +123,16 @@ doc.addSection({
level: 0,
},
}),
new Paragraph({}).addRun(
new Paragraph({
children: [
new TextRun({
text: "Some monospaced content",
font: {
name: "Monospace",
},
}),
),
],
}),
new Paragraph({
text: "An aside, in light gray italics and indented",
style: "aside",

View File

@ -1,23 +1,102 @@
// Add custom borders to table cell
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { BorderStyle, Document, Packer, Paragraph, Table } from "../build";
import { BorderStyle, Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build";
const doc = new Document();
const table = new Table({
rows: 4,
columns: 4,
rows: [
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [new Paragraph("Hello")],
borders: {
top: {
style: BorderStyle.DASH_DOT_STROKED,
size: 3,
color: "red",
},
bottom: {
style: BorderStyle.DOUBLE,
size: 3,
color: "blue",
},
left: {
style: BorderStyle.DASH_DOT_STROKED,
size: 3,
color: "green",
},
right: {
style: BorderStyle.DASH_DOT_STROKED,
size: 3,
color: "#ff8000",
},
},
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
],
});
doc.addSection({ children: [table] });
table
.getCell(2, 2)
.add(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");
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);

View File

@ -1,7 +1,7 @@
// This demo shows how to create bookmarks then link to them with internal hyperlinks
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, HeadingLevel, Packer, Paragraph } from "../build";
import { Document, HeadingLevel, Packer, PageBreak, Paragraph } from "../build";
const LOREM_IPSUM =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mi velit, convallis convallis scelerisque nec, faucibus nec leo. Phasellus at posuere mauris, tempus dignissim velit. Integer et tortor dolor. Duis auctor efficitur mattis. Vivamus ut metus accumsan tellus auctor sollicitudin venenatis et nibh. Cras quis massa ac metus fringilla venenatis. Proin rutrum mauris purus, ut suscipit magna consectetur id. Integer consectetur sollicitudin ante, vitae faucibus neque efficitur in. Praesent ultricies nibh lectus. Mauris pharetra id odio eget iaculis. Duis dictum, risus id pellentesque rutrum, lorem quam malesuada massa, quis ullamcorper turpis urna a diam. Cras vulputate metus vel massa porta ullamcorper. Etiam porta condimentum nulla nec tristique. Sed nulla urna, pharetra non tortor sed, sollicitudin molestie diam. Maecenas enim leo, feugiat eget vehicula id, sollicitudin vitae ante.";
@ -22,11 +22,16 @@ doc.addSection({
children: [
new Paragraph({
heading: HeadingLevel.HEADING_1,
}).addBookmark(bookmark),
children: [bookmark],
}),
new Paragraph("\n"),
new Paragraph(LOREM_IPSUM),
new Paragraph({}).pageBreak(),
new Paragraph({}).addHyperLink(hyperlink),
new Paragraph({
children: [new PageBreak()],
}),
new Paragraph({
children: [hyperlink],
}),
],
});

View File

@ -1,24 +1,85 @@
// Add image to table cell
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Media, Packer, Paragraph, Table } from "../build";
import { Document, Media, Packer, Paragraph, Table, TableCell, TableRow } from "../build";
const doc = new Document();
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
const table = new Table({
rows: 4,
columns: 4,
rows: [
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [new Paragraph(image)],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [new Paragraph("Hello")],
}),
new TableCell({
children: [],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
],
});
doc.addSection({
children: [table],
});
table.getCell(2, 2).add(new Paragraph("Hello"));
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
table.getCell(1, 1).add(new Paragraph(image));
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

View File

@ -1,28 +1,53 @@
// Custom styles using JavaScript configuration
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, HeadingLevel, Packer, Paragraph } from "../build";
import { Document, HeadingLevel, Packer, Paragraph, UnderlineType } from "../build";
const doc = new Document();
// The first argument is an ID you use to apply the style to paragraphs
// The second argument is a human-friendly name to show in the UI
doc.Styles.createParagraphStyle("myWonkyStyle", "My Wonky Style")
.basedOn("Normal")
.next("Normal")
.color("990000")
.italics()
.indent({ left: 720 }) // 720 TWIP === 720 / 20 pt === .5 in
.spacing({ line: 276 }); // 276 / 240 = 1.15x line spacing
doc.Styles.createParagraphStyle("Heading2", "Heading 2")
.basedOn("Normal")
.next("Normal")
.quickFormat()
.size(26) // 26 half-points === 13pt font
.bold()
.underline("double", "FF0000")
.spacing({ before: 240, after: 120 }); // TWIP for both
const doc = new Document({
styles: {
paragraphStyles: [
{
id: "myWonkyStyle",
name: "My Wonky Style",
basedOn: "Normal",
next: "Normal",
run: {
color: "990000",
italics: true,
},
paragraph: {
indent: {
left: 720,
},
spacing: {
line: 276,
},
},
},
{
id: "Heading2",
name: "Heading 2",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
bold: true,
size: 26,
underline: {
type: UnderlineType.DOUBLE,
color: "FF0000",
},
},
paragraph: {
spacing: {
before: 240,
after: 120,
},
},
},
],
},
});
doc.addSection({
children: [

View File

@ -3,15 +3,23 @@
import * as fs from "fs";
import { File, HeadingLevel, Packer, Paragraph, StyleLevel, TableOfContents } from "../build";
const doc = new File();
// The first argument is an ID you use to apply the style to paragraphs
// The second argument is a human-friendly name to show in the UI
doc.Styles.createParagraphStyle("MySpectacularStyle", "My Spectacular Style")
.basedOn("Heading1")
.next("Heading1")
.color("990000")
.italics();
const doc = new File({
styles: {
paragraphStyles: [
{
id: "MySpectacularStyle",
name: "My Spectacular Style",
basedOn: "Heading1",
next: "Heading1",
quickFormat: true,
run: {
italics: true,
color: "990000",
},
},
],
},
});
// WordprocessingML docs for TableOfContents can be found here:
// http://officeopenxml.com/WPtableOfContents.php

View File

@ -10,7 +10,7 @@ const numbering = new Numbering();
const abstractNum = numbering.createAbstractNumbering();
abstractNum.createLevel(0, "upperRoman", "%1", "start").indent({ left: 720, hanging: 260 });
abstractNum.createLevel(1, "decimal", "%2.", "start").indent({ left: 1440, hanging: 980 });
abstractNum.createLevel(2, "lowerLetter", "%3)", "start").indent({ left: 14402160, hanging: 1700 });
abstractNum.createLevel(2, "lowerLetter", "%3)", "start").indent({ left: 2160, hanging: 1700 });
const concrete = numbering.createConcreteNumbering(abstractNum);

View File

@ -1,27 +1,47 @@
// Example of how you would create a table and add data to it
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, HeadingLevel, Packer, Paragraph, Table, VerticalAlign } from "../build";
import { Document, HeadingLevel, Packer, Paragraph, Table, TableCell, TableRow, VerticalAlign } from "../build";
const doc = new Document();
const table = new Table({
rows: 2,
columns: 2,
});
table
.getCell(1, 1)
.add(new Paragraph("This text should be in the middle of the cell"))
.setVerticalAlign(VerticalAlign.CENTER);
table.getCell(1, 0).add(
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph({}), new Paragraph({})],
verticalAlign: VerticalAlign.CENTER,
}),
new TableCell({
children: [new Paragraph({}), new Paragraph({})],
verticalAlign: VerticalAlign.CENTER,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [
new Paragraph({
text:
"Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah",
heading: HeadingLevel.HEADING_1,
}),
);
],
}),
new TableCell({
children: [
new Paragraph({
text: "This text should be in the middle of the cell",
}),
],
verticalAlign: VerticalAlign.CENTER,
}),
],
}),
],
});
doc.addSection({
children: [table],

View File

@ -0,0 +1,231 @@
// Example of how you would merge cells together (Rows and Columns) and apply shading
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build";
const doc = new Document();
const table = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("Hello")],
columnSpan: 2,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
],
});
const table2 = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("World")],
margins: {
top: 1000,
bottom: 1000,
left: 1000,
right: 1000,
},
columnSpan: 3,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
],
width: {
size: 100,
type: WidthType.AUTO,
},
columnWidths: [1000, 1000, 1000],
});
const table3 = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("Foo")],
}),
new TableCell({
children: [new Paragraph("v")],
columnSpan: 3,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("Bar1")],
shading: {
fill: "b79c2f",
val: ShadingType.REVERSE_DIAGONAL_STRIPE,
color: "auto",
},
}),
new TableCell({
children: [new Paragraph("Bar2")],
shading: {
fill: "42c5f4",
val: ShadingType.PERCENT_95,
color: "auto",
},
}),
new TableCell({
children: [new Paragraph("Bar3")],
shading: {
fill: "880aa8",
val: ShadingType.PERCENT_10,
color: "e2df0b",
},
}),
new TableCell({
children: [new Paragraph("Bar4")],
shading: {
fill: "FF0000",
val: ShadingType.CLEAR,
color: "auto",
},
}),
],
}),
],
width: {
size: 7000,
type: WidthType.DXA,
},
margins: {
top: 400,
bottom: 400,
right: 400,
left: 400,
},
});
const table4 = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("0,0")],
columnSpan: 2,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("1,0")],
}),
new TableCell({
children: [new Paragraph("1,1")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("2,0")],
columnSpan: 2,
}),
],
}),
],
width: {
size: 100,
type: WidthType.PERCENTAGE,
},
});
const table5 = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("0,0")],
}),
new TableCell({
children: [new Paragraph("0,1")],
rowSpan: 2,
}),
new TableCell({
children: [new Paragraph("0,2")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [new Paragraph("1,2")],
rowSpan: 2,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
],
width: {
size: 100,
type: WidthType.PERCENTAGE,
},
});
doc.addSection({
children: [
table,
new Paragraph({
text: "Another table",
heading: HeadingLevel.HEADING_2,
}),
table2,
new Paragraph({
text: "Another table",
heading: HeadingLevel.HEADING_2,
}),
table3,
new Paragraph("Merging columns"),
table4,
new Paragraph("More Merging columns"),
table5,
],
});
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

View File

@ -1,113 +0,0 @@
// 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, HeadingLevel, Packer, Paragraph, ShadingType, Table, WidthType } from "../build";
const doc = new Document();
const table = new Table({
rows: 2,
columns: 2,
});
table.getCell(0, 0).add(new Paragraph("Hello"));
table.getRow(0).mergeCells(0, 1);
const table2 = new Table({
rows: 2,
columns: 3,
width: 100,
widthUnitType: WidthType.AUTO,
columnWidths: [1000, 1000, 1000],
});
table2
.getCell(0, 0)
.add(new Paragraph("World"))
.setMargins({
top: 1000,
bottom: 1000,
left: 1000,
right: 1000,
});
table.getRow(0).mergeCells(0, 2);
const table3 = new Table({
rows: 2,
columns: 4,
width: 7000,
widthUnitType: WidthType.DXA,
margins: {
top: 400,
bottom: 400,
right: 400,
left: 400,
},
});
table3.getCell(0, 0).add(new Paragraph("Foo"));
table3.getCell(0, 1).add(new Paragraph("v"));
table3
.getCell(1, 0)
.add(new Paragraph("Bar1"))
.setShading({
fill: "b79c2f",
val: ShadingType.REVERSE_DIAGONAL_STRIPE,
color: "auto",
});
table3
.getCell(1, 1)
.add(new Paragraph("Bar2"))
.setShading({
fill: "42c5f4",
val: ShadingType.PERCENT_95,
color: "auto",
});
table3
.getCell(1, 2)
.add(new Paragraph("Bar3"))
.setShading({
fill: "880aa8",
val: ShadingType.PERCENT_10,
color: "e2df0b",
});
table3
.getCell(1, 3)
.add(new Paragraph("Bar4"))
.setShading({
fill: "FF0000",
val: ShadingType.CLEAR,
color: "auto",
});
table3.getRow(0).mergeCells(0, 3);
const table4 = new Table({
rows: 2,
columns: 2,
width: 100,
widthUnitType: WidthType.PERCENTAGE,
});
doc.addSection({
children: [
table,
new Paragraph({
text: "Another table",
heading: HeadingLevel.HEADING_2,
}),
table2,
new Paragraph({
text: "Another table",
heading: HeadingLevel.HEADING_2,
}),
table3,
new Paragraph("hi"),
table4,
],
});
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

View File

@ -1,28 +1,44 @@
// Sequential Captions
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph, TextRun } from "../build";
import { Document, Packer, Paragraph, SequentialIdentifier, TextRun } from "../build";
const doc = new Document();
doc.addSection({
children: [
new Paragraph("Hello World 1->")
.addSequentialIdentifier("Caption")
.addRun(new TextRun(" text after sequencial caption 2->"))
.addSequentialIdentifier("Caption"),
new Paragraph("Hello World 1->")
.addSequentialIdentifier("Label")
.addRun(new TextRun(" text after sequencial caption 2->"))
.addSequentialIdentifier("Label"),
new Paragraph("Hello World 1->")
.addSequentialIdentifier("Another")
.addRun(new TextRun(" text after sequencial caption 3->"))
.addSequentialIdentifier("Label"),
new Paragraph("Hello World 2->")
.addSequentialIdentifier("Another")
.addRun(new TextRun(" text after sequencial caption 4->"))
.addSequentialIdentifier("Label"),
new Paragraph({
children: [
new TextRun("Hello World 1->"),
new SequentialIdentifier("Caption"),
new TextRun(" text after sequencial caption 2->"),
new SequentialIdentifier("Caption"),
],
}),
new Paragraph({
children: [
new TextRun("Hello World 1->"),
new SequentialIdentifier("Label"),
new TextRun(" text after sequencial caption 2->"),
new SequentialIdentifier("Label"),
],
}),
new Paragraph({
children: [
new TextRun("Hello World 1->"),
new SequentialIdentifier("Another"),
new TextRun(" text after sequencial caption 3->"),
new SequentialIdentifier("Label"),
],
}),
new Paragraph({
children: [
new TextRun("Hello World 2->"),
new SequentialIdentifier("Another"),
new TextRun(" text after sequencial caption 4->"),
new SequentialIdentifier("Label"),
],
}),
],
});

View File

@ -9,29 +9,48 @@ import {
RelativeVerticalPosition,
Table,
TableAnchorType,
TableCell,
TableLayoutType,
TableRow,
WidthType,
} from "../build";
const doc = new Document();
const table = new Table({
rows: 2,
columns: 2,
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("Hello")],
columnSpan: 2,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
],
float: {
horizontalAnchor: TableAnchorType.MARGIN,
verticalAnchor: TableAnchorType.MARGIN,
relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
},
width: 4535,
widthUnitType: WidthType.DXA,
width: {
size: 4535,
type: WidthType.DXA,
},
layout: TableLayoutType.FIXED,
});
table.getCell(0, 0).add(new Paragraph("Hello"));
table.getRow(0).mergeCells(0, 1);
doc.addSection({
children: [table],
});

View File

@ -4,12 +4,14 @@ 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");
paragraph.addHyperLink(link);
doc.addSection({
children: [paragraph],
children: [
new Paragraph({
children: [link],
}),
],
});
Packer.toBuffer(doc).then((buffer) => {

View File

@ -1,16 +1,61 @@
// Add image to table cell
// Add image to table cell in a header and body
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Header, Media, Packer, Paragraph, Table } from "../build";
import { Document, Header, Media, Packer, Paragraph, Table, TableCell, TableRow } 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,
rows: [
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [new Paragraph(image)],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
],
});
table.getCell(1, 1).add(new Paragraph(image));
// Adding same table in the body and in the header
doc.addSection({

View File

@ -1,17 +1,35 @@
// Example of how you would create a table and add data to it
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph, Table } from "../build";
import { Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build";
const doc = new Document();
const table = new Table({
rows: 4,
columns: 4,
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("Hello")],
}),
new TableCell({
children: [],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [new Paragraph("World")],
}),
],
}),
],
});
table.getCell(2, 2).add(new Paragraph("Hello"));
doc.addSection({
children: [table],
});

View File

@ -1,52 +1,259 @@
// Multiple cells merging in the same table
// Multiple cells merging in the same table - Rows and Columns
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph, Table } from "../build";
import { Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build";
const doc = new Document();
const table = new Table({
rows: 13,
columns: 6,
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("0,0")],
}),
new TableCell({
children: [new Paragraph("0,1")],
columnSpan: 2,
}),
new TableCell({
children: [new Paragraph("0,3")],
}),
new TableCell({
children: [new Paragraph("0,4")],
columnSpan: 2,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("1,0")],
columnSpan: 2,
}),
new TableCell({
children: [new Paragraph("1,2")],
columnSpan: 2,
}),
new TableCell({
children: [new Paragraph("1,4")],
columnSpan: 2,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("2,0")],
}),
new TableCell({
children: [new Paragraph("2,1")],
columnSpan: 2,
}),
new TableCell({
children: [new Paragraph("2,3")],
}),
new TableCell({
children: [new Paragraph("2,4")],
columnSpan: 2,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("3,0")],
}),
new TableCell({
children: [new Paragraph("3,1")],
}),
new TableCell({
children: [new Paragraph("3,2")],
}),
new TableCell({
children: [new Paragraph("3,3")],
}),
new TableCell({
children: [new Paragraph("3,4")],
}),
new TableCell({
children: [new Paragraph("3,5")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("4,0")],
columnSpan: 5,
}),
new TableCell({
children: [new Paragraph("4,5")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
],
});
let row = 0;
table.getCell(row, 0).add(new Paragraph("0,0"));
table.getCell(row, 1).add(new Paragraph("0,1"));
table.getCell(row, 3).add(new Paragraph("0,3"));
table.getCell(row, 4).add(new Paragraph("0,4"));
table.getRow(row).mergeCells(4, 5);
table.getRow(row).mergeCells(1, 2);
row = 1;
table.getCell(row, 0).add(new Paragraph("1,0"));
table.getCell(row, 2).add(new Paragraph("1,2"));
table.getCell(row, 4).add(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).add(new Paragraph("2,0"));
table.getCell(row, 1).add(new Paragraph("2,1"));
table.getCell(row, 2).add(new Paragraph("2,2"));
table.getCell(row, 3).add(new Paragraph("2,3"));
table.getCell(row, 4).add(new Paragraph("2,4"));
table.getRow(row).mergeCells(4, 5);
table.getRow(row).mergeCells(1, 2);
row = 3;
table.getCell(row, 0).add(new Paragraph("3,0"));
table.getCell(row, 1).add(new Paragraph("3,1"));
table.getCell(row, 2).add(new Paragraph("3,2"));
table.getCell(row, 3).add(new Paragraph("3,3"));
table.getCell(row, 4).add(new Paragraph("3,4"));
table.getCell(row, 5).add(new Paragraph("3,5"));
row = 4;
table.getCell(row, 0).add(new Paragraph("4,0"));
table.getCell(row, 5).add(new Paragraph("4,5"));
table.getRow(row).mergeCells(0, 4);
const table2 = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("0,0")],
}),
new TableCell({
children: [new Paragraph("0,1")],
rowSpan: 2,
}),
new TableCell({
children: [new Paragraph("0,2")],
}),
new TableCell({
children: [new Paragraph("0,3")],
}),
new TableCell({
children: [new Paragraph("0,4")],
}),
new TableCell({
children: [new Paragraph("0,5")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("1,0")],
}),
new TableCell({
children: [new Paragraph("1,2")],
}),
new TableCell({
children: [new Paragraph("1,3")],
}),
new TableCell({
children: [new Paragraph("1,4")],
}),
new TableCell({
children: [new Paragraph("1,5")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("2,0")],
}),
new TableCell({
children: [new Paragraph("2,1")],
}),
new TableCell({
children: [new Paragraph("2,2")],
}),
new TableCell({
children: [new Paragraph("2,3")],
}),
new TableCell({
children: [new Paragraph("2,4")],
}),
new TableCell({
children: [new Paragraph("2,5")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("3,0")],
}),
new TableCell({
children: [new Paragraph("3,1")],
}),
new TableCell({
children: [new Paragraph("3,2")],
}),
new TableCell({
children: [new Paragraph("3,3")],
}),
new TableCell({
children: [new Paragraph("3,4")],
}),
new TableCell({
children: [new Paragraph("3,5")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [new Paragraph("4,0")],
}),
new TableCell({
children: [new Paragraph("4,1")],
}),
new TableCell({
children: [new Paragraph("4,2")],
}),
new TableCell({
children: [new Paragraph("4,3")],
}),
new TableCell({
children: [new Paragraph("4,4")],
}),
new TableCell({
children: [new Paragraph("4,5")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
],
});
doc.addSection({
children: [table],
children: [table, new Paragraph(""), table2],
});
Packer.toBuffer(doc).then((buffer) => {

View File

@ -1,18 +1,80 @@
// 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, Table } from "../build";
import { Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build";
const doc = new Document();
const table = new Table({
rows: 4,
columns: 4,
rows: [
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
rowSpan: 2,
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [new Paragraph("Hello")],
}),
new TableCell({
children: [],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
],
});
table.getCell(2, 2).add(new Paragraph("Hello"));
table.getColumn(3).mergeCells(1, 2);
doc.addSection({
children: [table],
});

View File

@ -8,7 +8,7 @@ const doc = new Document();
doc.addSection({
properties: {
column: {
width: 708,
space: 708,
count: 2,
},
},
@ -23,7 +23,7 @@ doc.addSection({
doc.addSection({
properties: {
column: {
width: 708,
space: 708,
count: 3,
},
},

View File

@ -0,0 +1,63 @@
// Multiple sections with total number of pages in each section
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { AlignmentType, Document, Packer, PageNumberFormat, TextRun, Header, Paragraph, Footer, PageBreak } from "../build";
const doc = new Document();
const header = new Header({
children: [
new Paragraph({
children: [
new TextRun("Header on another page"),
new TextRun("Page Number: ").pageNumber(),
new TextRun(" to ").numberOfTotalPagesSection(),
],
alignment: AlignmentType.CENTER,
}),
],
});
const footer = new Footer({
children: [new Paragraph("Foo Bar corp. ")],
});
doc.addSection({
headers: {
default: header,
},
footers: {
default: footer,
},
properties: {
pageNumberStart: 1,
pageNumberFormatType: PageNumberFormat.DECIMAL,
},
children: [
new Paragraph({
children: [new TextRun("Section 1"), new PageBreak(), new TextRun("Section 1"), new PageBreak()],
}),
],
});
doc.addSection({
headers: {
default: header,
},
footers: {
default: footer,
},
properties: {
pageNumberStart: 1,
pageNumberFormatType: PageNumberFormat.DECIMAL,
},
children: [
new Paragraph({
children: [new TextRun("Section 2"), new PageBreak(), new TextRun("Section 2"), new PageBreak()],
}),
],
});
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

View File

@ -5,17 +5,16 @@
To make a bullet point, simply make a paragraph into a bullet point:
```ts
const text = new docx.TextRun("Bullet points");
const paragraph = new docx.Paragraph(text).bullet();
const text2 = new docx.TextRun("Are awesome");
const paragraph2 = new docx.Paragraph(text2).bullet();
doc.add(paragraph);
doc.add(paragraph2);
const text = new TextRun("Bullet points");
const paragraph = new Paragraph({
text: "Bullet points",
bullet: {
level: 0, // How deep you want the bullet to me
},
});
```
### This will produce:
* Bullet points
* Are awesome
- Bullet points
- Are awesome

View File

@ -2,52 +2,46 @@
!> Headers and Footers requires an understanding of [Sections](usage/sections.md).
## Example
Creating Headers and footers is simple. Access the `Header` and `Footer` by doing so like this:
Every Section has a sections which you can define its Headers and Footers:
```ts
doc.Header;
doc.Footer;
```
You can call the same methods as you would with a `File`:
```ts
doc.Header.createParagraph("Header text");
doc.Footer.createParagraph("Footer text");
```
Even add images:
```ts
doc.Header.createImage([BUFFER_OF_YOUR_IMAGE]);
doc.Footer.createImage([BUFFER_OF_YOUR_IMAGE]);
```
Refer to [`demo8.ts`](https://github.com/dolanmiu/docx/blob/master/demo/demo8.ts) for more information.
## Multiple Headers and Footers
Also all the supported section properties are implemented according to: http://officeopenxml.com/WPsection.php
### Example
```ts
const header = this.document.createHeader();
const footer = this.document.createFooter();
// Add new section with another header and footer
doc.addSection({
headers: {
default: header
default: new Header({ // The standard default header
children: [],
}),
first: new Header({ // The first header
children: [],
}),
even: new Header({ // The header on every other page
children: [],
}),
},
footers: {
default: footer
default: new Footer({ // The standard default footer
children: [],
}),
first: new Footer({ // The first footer
children: [],
}),
even: new Footer({ // The footer on every other page
children: [],
}),
},
pageNumberStart: 1,
pageNumberFormatType: docx.PageNumberFormat.DECIMAL,
children: [],
});
```
If you want more head
## Example
Example showing basic header and footer
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/8-header-footer.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/8-header-footer.ts_
## Multiple Headers and Footers
More headers and footers can be accomplished by creating more `Section`. New headers and footers can be set per `Section`

View File

@ -227,22 +227,22 @@ Media.addImage(doc, 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/5-images.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/5-images.ts_
### Add images to header and footer
Example showing how to add image to headers and footers
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/9-images-in-header-and-footer.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/9-images-in-header-and-footer.ts_
### Floating images
Example showing how to float images on top of text and optimally give a `margin`
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo38.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/38-text-wrapping.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo38.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/38-text-wrapping.ts_

View File

@ -86,11 +86,3 @@ topLevelP.setNumbering(concrete, 0);
subP.setNumbering(concrete, 1);
subSubP.setNumbering(concrete, 2);
```
Finally, you need to let your exporter know about your numbering
styles when you're ready to render the document:
```ts
const packer = new Packer(doc, undefined, undefined, numbering);
packer.pack(myOutput);
```

View File

@ -61,6 +61,6 @@ doc.Header.createParagraph().addRun(new TextRun("Page ").pageNumber()).addRun(ne
Adding page numbers to Header and Footer
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo39.ts ':include')
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/39-page-numbers.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo39.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/39-page-numbers.ts_

View File

@ -2,7 +2,7 @@
> Everything (text, images, graphs etc) in OpenXML is organised in paragraphs.
!> Paragraphs requires an understanding of [Sections](usage/sections.md).
!> Paragraphs requires an understanding of [Sections](sections.md).
You can create `Paragraphs` in the following ways:
@ -16,11 +16,15 @@ const paragraph = new Paragraph("Short hand Hello World");
### Children Method
This method is useful for adding different `text` with different styles or adding `images` inline.
This method is useful for adding different [text](text.md) with different styles, [symbols](symbols.md), or adding [images](images.md) inline.
```ts
const paragraph = new Paragraph({
children: [new TextRun("Lorem Ipsum Foo Bar"), new TextRun("Hello World")],
children: [
new TextRun("Lorem Ipsum Foo Bar"),
new TextRun("Hello World"),
new SymbolRun("F071"),
],
});
```
@ -244,10 +248,15 @@ The above example will create a heading with a page break directly under it.
## Page Break
To move to a new page (insert a page break), simply add `.pageBreak()` on a paragraph:
To move to a new page (insert a page break):
```ts
const paragraph = new docx.Paragraph("Amazing Heading").pageBreak();
const paragraph = new docx.Paragraph({
children: [
new TextRun("Amazing Heading"),
new PageBreak(),
]
});
```
The above example will create a heading and start a new page immediately afterwards.
@ -265,7 +274,7 @@ const paragraph = new Paragraph({
![Page Break Before in Word](https://user-images.githubusercontent.com/34742290/40176503-df3a8398-59db-11e8-8b9c-d719f13aa8b4.png)
Example: https://github.com/dolanmiu/docx/blob/master/demo/demo15.ts
Example: https://github.com/dolanmiu/docx/blob/master/demo/15-page-break-before.ts
## Page break control

View File

@ -3,111 +3,146 @@
## Example
```ts
const para = new Paragraph("To whom it may concern:").heading2().center();
const para = new Paragraph({
text: "To whom it may concern:",
heading: HeadingLevel.HEADING_2,
alignment: AlignmentType.CENTER,
});
const name = new TextRun("Name:")
.bold()
.font("Calibri")
.allCaps();
const name = new TextRun({
text: "Name:",
bold: true,
font: "Calibri",
allCaps: true,
});
```
## Available methods
## Available Options
* For run formatting:
* `.bold()`, `.italics()`, `.smallCaps()`, `.allCaps()`, `.strike()`, `.doubleStrike()`, `.subScript()`, `.superScript()`: Set the formatting property to true
* `.underline(style="single", color=null)`: Set the underline style and color
* `.color(color)`: Set the text color, using 6 hex characters for RRGGBB (no leading `#`)
* `.size(halfPts)`: Set the font size, measured in half-points
* `.font(name)`: Set the run's font
* `.style(name)`: Apply a named run style
* `.characterSpacing(value)`: Set the character spacing adjustment (in TWIPs)
* For paragraph formatting:
* `.heading1()`, `.heading2()`, `.heading3()`, `.heading4()`, `.heading5()`, `.title()`: apply the appropriate style to the paragraph
* `.left()`, `.center()`, `.right()`, `.justified()`: set the paragraph's alignment
* `.thematicBreak()`, `.pageBreak()`: Insert a thick rule or a page break beneath the paragraph
* `.leftTabStop(position)`: Add a left tab stop (measured in TWIPs from the left)
* `.maxRightTabStop()`: Add a right tab stop at the far right
* `.bullet()`: Use the default bullet style
* `.setNumbering(numbering, indentLevel)`: Use a custom numbering format for the paragraph
* `.style(name)`: Apply a named paragraph style
* `.indent(start, hanging=0)`: Set the paragraph's indent level (in TWIPs)
* `.spacing({before=0, after=0, line=0})`: Set the line and before/after on the paragraph. Before/after is measured in TWIPs, line is measured in 240ths of a line
### Run formatting
Paragraph styles have all the run formatting methods, except `style()`, and `.left()`, `.center()`, `.right()`, `.justified()`, `.thematicBreak()`, `.leftTabStop(position)`, `.maxRightTabStop()`, `.indent(start, hanging=0)`, and `.spacing({before=0, after=0, line=0})` methods.
- `bold`, `italics`, `smallCaps`, `allCaps`, `strike`, `doubleStrike`, `subScript`, `superScript`: Set the formatting property to true
- `underline(style="single", color=null)`: Set the underline style and color
- `color(color)`: Set the text color, using 6 hex characters for RRGGBB (no leading `#`)
- `size(halfPts)`: Set the font size, measured in half-points
- `font(name)`: Set the run's font
- `style(name)`: Apply a named run style
- `characterSpacing(value)`: Set the character spacing adjustment (in TWIPs)
### Paragraph formatting
- `heading1`, `heading2`, `heading3`, `heading4`, `heading5`, `title`: apply the appropriate style to the paragraph
- `left`, `center`, `right`, `justified`: set the paragraph's alignment
- `thematicBreak`, `pageBreak`: Insert a thick rule or a page break beneath the paragraph
- `leftTabStop(position)`: Add a left tab stop (measured in TWIPs from the left)
- `maxRightTabStop`: Add a right tab stop at the far right
- `bullet`: Use the default bullet style
- `setNumbering(numbering, indentLevel)`: Use a custom numbering format for the paragraph
- `style(name)`: Apply a named paragraph style
- `indent(start, hanging=0)`: Set the paragraph's indent level (in TWIPs)
- `spacing({before=0, after=0, line=0})`: Set the line and before/after on the paragraph. Before/after is measured in TWIPs, line is measured in 240ths of a line
Paragraph styles have all the run formatting methods, except `style()`, and `left()`, `center()`, `right()`, `justified()`, `thematicBreak()`, `leftTabStop(position)`, `maxRightTabStop()`, `indent(start, hanging=0)`, and `spacing({before=0, after=0, line=0})` methods.
## Detailed guide
There are 4 items in DOCX that can be styled:
There are 4 items in `docx` that can be styled:
* Characters: Attributes that can change within a paragraph. e.g., bold, italics, etc.
* Paragraphs: Attributes like indent, text alignment, line spacing, etc.
* Tables: Border styles, table formats, etc.
* List items: These are the numbers and bullets that are automatically inserted
- Characters: Attributes that can change within a paragraph. e.g., bold, italics, etc.
- Paragraphs: Attributes like indent, text alignment, line spacing, etc.
- Tables: Border styles, table formats, etc.
- List items: These are the numbers and bullets that are automatically inserted
There are a few different ways of styling this content in DOCX, which somewhat resemble the HTML/CSS approach. In order of greatest to lowest priority:
There are a few different ways of styling this content in `docx`, which somewhat resemble the HTML/CSS approach. In order of greatest to lowest priority:
1. Direct formatting (AKA inline formatting)
2. Centrally defined styles (similar to external CSS)
1. Direct formatting (inline formatting)
2. Declaritive Styles (similar to external CSS)
3. Document defaults (similar to a `*` rule in CSS)
Unlike CSS, less specific rules don't _necessarily_ override parent rules. The rules are a bit wonky, but if you're interested, see the [advanced formatting section](#Advanced formatting).
### Direct formatting (AKA inline formatting)
### Direct formatting (inline formatting)
This is the type of formatting that your uncle uses when he types out documents: _N ... a ... m ... e ... :_ Then he grabs the mouse, highlights _Name:_ and moves over to the **B** for bold. This manner of formatting results in markup that is similar to writing `<span style="bold: true">Name:</span>` if you were typing out HTML. DOCX (the format) allows you to specify this for any of the four types of items. `docx` (the library) only supports this type of formatting for paragraphs and characters, using a _fluent_ api. Thus you could do:
This is the type of formatting that your uncle uses when he types out documents: _N ... a ... m ... e ... :_ Then he grabs the mouse, highlights _Name:_ and moves over to the **B** for bold. This manner of formatting results in markup that is similar to writing `<span style="bold: true">Name:</span>` if you were typing out HTML. `docx` (the format) allows you to specify this for any of the four types of items. `docx` (the library) only supports this type of formatting for paragraphs and characters, using a _fluent_ api. Thus you could do:
```ts
const name = new TextRun("Name:")
.bold()
.font("Calibri")
.allCaps();
const name = new TextRun({
text: "Name:",
bold: true,
font: "Calibri",
allCaps: true,
});
```
Or for paragraph formatting:
```ts
const para = new Paragraph("To whom it may concern:").heading2().center();
const para = new Paragraph({
text: "To whom it may concern:",
heading: HeadingLevel.HEADING_2,
alignment: AlignmentType.CENTER,
});
```
### Centrally defined styles (similar to external CSS)
### Declaritive Styles (similar to external CSS)
DOCX files contain a styles section separate from the main content, much like how HTML includes CSS files. Unlike CSS, DOCX distinguishes between styles meant for tables (which show up in the table formatting toolbar), styles for lists (which show up under bullets and numbering), and styles for runs and paragraphs, which show up as dropdowns offering standard styles, like "Heading 1", "Caption", or any custom styles defined in that document. <!-- TODO: add pictures of the panes -->. `docx` allows you to define these styles using a fluent interface as well.
`docx` files contain a styles section separate from the main content, much like how HTML includes CSS files. Unlike CSS, `docx` distinguishes between styles meant for tables (which show up in the table formatting toolbar), styles for lists (which show up under bullets and numbering), and styles for runs and paragraphs, which show up as dropdowns offering standard styles, like "Heading 1", "Caption", or any custom styles defined in that document. <!-- TODO: add pictures of the panes -->. `docx` allows you to define these styles using a fluent interface as well.
There are three parts to using custom styles with `docx`:
1. Create a container object for the style definitions:
```ts
const myStyles = new docx.Styles();
```
2. Define your custom styles, similar to the way you would format a paragraph or run
To add styles, define your custom styles in the `document`:
```ts
// The first argument is an ID you use to apply the style to paragraphs
// The second argument is a human-friendly name to show in the UI
myStyles
.createParagraphStyle("myWonkyStyle", "My Wonky Style")
.basedOn("Normal")
.next("Normal")
.color("999999")
.italics()
.indent(720) // 720 TWIP === 720 / 20 pt === .5 in
.spacing({ line: 276 }); // 276 / 240 = 1.15x line spacing
myStyles
.createParagraphStyle("Heading2", "Heading 2")
.basedOn("Normal")
.next("Normal")
.quickFormat()
.size(26) // 26 half-points === 13pt font
.bold()
.underline("double", "FF0000")
.spacing({ before: 240, after: 120 }); // TWIP for both
```
3. When you generate your document, make sure to pass the `styles` container to the `Packer`:
```ts
Packer.pack(myOutStream);
const doc = new Document({
creator: "Clippy",
title: "Sample Document",
description: "A brief example of using docx",
styles: {
paragraphStyles: [
{
id: "myWonkyStyle",
name: "My Wonky Style",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
italics: true,
color: "999999",
},
paragraph: {
spacing: {
line: 276,
},
indent: {
left: 720,
},
},
},
{
id: "Heading2",
name: "Heading 2",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
size: 26
bold: true,
color: "999999",
{
type: UnderlineType.DOUBLE,
color: "FF0000",
},
},
paragraph: {
spacing: {
before: 240,
after: 120
},
},
},
]
}
});
```
**Note**: If you are using the `.headingX` or `.title` methods of paragraphs, you must make sure to define `HeadingX` or `Title` styles for these. Otherwise they'll show up unstyled :(. If you are using the `.bullet` or `.setNumbering` methods, you need to define a `ListParagraph` style or the numbers may not show up.
@ -144,19 +179,29 @@ To determine the value of a styling property, you must first identify whether it
The following properties are treated in a special manner; they're called toggle properties:
* Bold
* All caps
* Small caps
* Italics
* Single strike-through
* Hidden
* Imprint
* Emboss
* Character outline
* Character shadow
- Bold
- All caps
- Small caps
- Italics
- Single strike-through
- Hidden
- Imprint
- Emboss
- Character outline
- Character shadow
For these properties, the rules state the following conflict resolution in case the property is specified at multiple points for the same item:
* Direct formatting trumps all if specified (either true or false)
* Otherwise, if the property is true in document defaults, the property is set to true
* Otherwise, the property's value is an XOR of its effective table, paragraph, and character values. (So specifying bold `true` on a table style and a paragraph style would result in non-bold text if a paragraph inside the table had that style)
- Direct formatting trumps all if specified (either true or false)
- Otherwise, if the property is true in document defaults, the property is set to true
- Otherwise, the property's value is an XOR of its effective table, paragraph, and character values. (So specifying bold `true` on a table style and a paragraph style would result in non-bold text if a paragraph inside the table had that style)
## Examples
### Declaritive styles
Importing Images from file system path
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/2-declaritive-styles.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/2-declaritive-styles.ts_

View File

@ -44,4 +44,4 @@ doc.add(paragraph);
doc.createParagraph("Some normal text");
```
Example: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts
Example: https://github.com/dolanmiu/docx/blob/master/demo/13-xml-styles.ts

53
docs/usage/symbols.md Normal file
View File

@ -0,0 +1,53 @@
# Symbol Runs
!> SymbolRuns require an understanding of [Paragraphs](paragraph.md).
You can add multiple `symbol runs` in `Paragraphs` along with [text runs](text.md) using the Paragraph's `children` property.
```ts
import { Paragraph, TextRun, SymbolRun } from "docx";
const paragraph = new Paragraph({
children: [
new TextRun("This is a checkbox: "),
new SymbolRun("F071")
],
});
```
## Specifying symbol font
By default symbol runs will use the `Wingdings` font. To switch fonts, pass an object instead of a string to the `SymbolRun` constructor and specify `char` and `symbolfont` properties:
```ts
const symbol = new SymbolRun({
char: "F071",
symbolfont: "Arial",
});
```
## Example symbols
Symbols are specified by their hexidecimal code. Ref http://officeopenxml.com/WPtextSpecialContent-symbol.php. Below are some examples.
- `F071`: empty checkbox
- `F043`: thumbs up
- `F04A`: smile
- `F04C`: frown
- `F022`: scissors
- `F0F0`: right arrow
- `F0FE`: checked box
## Typographical Emphasis
Symbol runs can have their display modified just like text runs. For example, they can be bolded and italicized:
```ts
const symbol = new SymbolRun({
char: "F071",
bold: true,
italics: true,
});
```
See the [text run](text.md) documentation for more info.

View File

@ -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**: The unit of measurement for a tab stop is in [DXA](https://stackoverflow.com/questions/14360183/default-wordml-unit-measurement-pixel-or-point-or-inches)
![Word 2013 Tabs](http://www.teachucomp.com/wp-content/uploads/blog-4-22-2015-UsingTabStopsInWord-1024x577.png "Word 2013 Tab Stops")
@ -11,44 +11,111 @@ Simply call the relevant methods on the paragraph listed below. Then just add a
## Example
```ts
const paragraph = new docx.Paragraph().maxRightTabStop();
const leftText = new docx.TextRun("Hey everyone").bold();
const rightText = new docx.TextRun("11th November 2015").tab();
paragraph.addRun(leftText);
paragraph.addRun(rightText);
const paragraph = new Paragraph({
children: [new TextRun("Hey everyone").bold(), new TextRun("11th November 1999").tab()],
tabStops: [
{
type: TabStopType.RIGHT,
position: TabStopPosition.MAX,
},
],
});
```
The example above will create a left aligned text, and a right aligned text on the same line. The laymans approach to this problem would be to either use text boxes or tables. YUK!
The example above will create a left aligned text, and a right aligned text on the same line. The laymans approach to this problem would be to either use text boxes or tables. Not ideal!
```ts
const paragraph = new docx.Paragraph();
paragraph.maxRightTabStop();
paragraph.leftTabStop(1000);
const text = new docx.TextRun("Second tab stop here I come!").tab().tab();
paragraph.addRun(text);
const paragraph = new Paragraph({
children: [new TextRun("Second tab stop here I come!").tab().tab()],
tabStops: [
{
type: TabStopType.RIGHT,
position: TabStopPosition.MAX,
},
{
type: TabStopType.LEFT,
position: 1000,
},
],
});
```
The above shows the use of two tab stops, and how to select/use it.
## Left Tab Stop
You can add multiple tab stops of the same `type` too.
```ts
paragraph.leftTabStop(2268);
const paragraph = new Paragraph({
children: [new TextRun("Multiple tab stops!").tab().tab()],
tabStops: [
{
type: TabStopType.RIGHT,
position: TabStopPosition.MAX,
},
{
type: TabStopType.RIGHT,
position: 1000,
},
],
});
```
## Left Tab Stop
```ts
const paragraph = new Paragraph({
tabStops: [
{
type: TabStopType.LEFT,
position: 2268,
},
],
});
```
2268 is the distance from the left side.
## Center Tab Stop
```ts
paragraph.centerTabStop(2268);
const paragraph = new Paragraph({
tabStops: [
{
type: TabStopType.CENTER,
position: 2268,
},
],
});
```
2268 is the distance from the left side.
2268 is the distance from the center.
## Right Tab Stop
```ts
paragraph.rightTabStop(2268);
const paragraph = new Paragraph({
tabStops: [
{
type: TabStopType.RIGHT,
position: 2268,
},
],
});
```
2268 is the distance from the left side.
2268 is the distance fro0oum the left side.
## Max Right Tab Stop
```ts
paragraph.maxRightTabStop();
const paragraph = new Paragraph({
tabStops: [
{
type: TabStopType.RIGHT,
position: TabStopPosition.MAX,
},
],
});
```
This will create a tab stop on the very edge of the right hand side. Handy for right aligning and left aligning text on the same line.

View File

@ -16,7 +16,7 @@ All you need to do is create a `TableOfContents` object and assign it to the doc
const toc = new TableOfContents("Summary", {
hyperlink: true,
headingStyleRange: "1-5",
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)]
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)],
});
doc.addTableOfContents(toc);
@ -27,8 +27,8 @@ doc.addTableOfContents(toc);
Here is the list of all options that you can use to generate your tables of contents:
| Option | Type | TOC Field Switch | Description |
| --- | --- | --- | --- |
|captionLabel|string|`\a`|Includes captioned items, but omits caption labels and numbers. The identifier designated by `text` in this switch's field-argument corresponds to the caption label. Use ``\c`` to build a table of captions with labels and numbers.|
| ------------------------------- | ------------ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| captionLabel | string | `\a` | Includes captioned items, but omits caption labels and numbers. The identifier designated by `text` in this switch's field-argument corresponds to the caption label. Use `\c` to build a table of captions with labels and numbers. |
| entriesFromBookmark | string | `\b` | Includes entries only from the portion of the document marked by the bookmark named by `text` in this switch's field-argument. |
| captionLabelIncludingNumbers | string | `\c` | Includes figures, tables, charts, and other items that are numbered by a SEQ field (§17.16.5.56). The sequence identifier designated by `text` in this switch's field-argument, which corresponds to the caption label, shall match the identifier in the corresponding SEQ field. |
| sequenceAndPageNumbersSeparator | string | `\d` | When used with `\s`, the `text` in this switch's field-argument defines the separator between sequence and page numbers. The default separator is a hyphen (-). |
@ -47,30 +47,6 @@ Here is the list of all options that you can use to generate your tables of cont
## Examples
```ts
// Let's define the options for generate a TOC for heading 1-5 and MySpectacularStyle,
// making the entries be hyperlinks for the paragraph
const toc = new TableOfContents("Summary", {
hyperlink: true,
headingStyleRange: "1-5",
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)]
});
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/28-table-of-contents.ts ':include')
doc.addTableOfContents(toc);
doc.add(new Paragraph("Header #1").heading1().pageBreakBefore());
doc.add(new Paragraph("I'm a little text, very nicely written.'"));
doc.add(new Paragraph("Header #2").heading1().pageBreakBefore());
doc.add(new Paragraph("I'm another text very nicely written.'"));
doc.add(new Paragraph("Header #2.1").heading2());
doc.add(new Paragraph("I'm another text very nicely written.'"));
doc.add(new Paragraph("My Spectacular Style #1").style("MySpectacularStyle").pageBreakBefore());
```
### Complete example
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo28.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo28.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/28-table-of-contents.ts_

View File

@ -1,306 +1,387 @@
# Tables
You can create tables with `docx`. More information can be found [here](http://officeopenxml.com/WPtable.php).
!> Paragraphs requires an understanding of [Sections](usage/sections.md).
## Create Table
## Intro
To create a table, simply create one with `new Table()`, then add it to the document: `doc.add()`.
* `Tables` contain a list of `Rows`
* `Rows` contain a list of `TableCells`
* `TableCells` contain a list of `Parahraphs` and/or `Tables`. You can add `Tables` as tables can be nested inside each other
```ts
const table = doc.add(new Table({
rows: [NUMBER OF ROWS],
columns: [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.add(table);
```
The snippet below creates a table of 2 rows and 4 columns.
Create a simple table like so:
```ts
const table = new Table({
rows: 2,
columns: 4,
rows: [Array of `TableRow`s]
});
doc.add(table);
```
## Rows and Columns
You can get a row or a column from a table like so, where `index` is a number:
### Get Row
Then add the table in the `section`
```ts
const row = doc.getRow(index);
doc.addSection({
children: [table],
});
```
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:
## Table
### Set Width
```ts
row.mergeCells(startIndex, endIndex);
const table = new Table({
...,
width: {
size: [TABLE_WIDTH],
type: WidthType,
}
});
```
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
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 table = new Table({
...,
width: {
size: 4535,
type: WidthType.DXA,
}
});
```
const cell = column.getCell(2);
### Pagination
#### Prevent row pagination
To prevent breaking contents of a row across multiple pages, call `cantSplit`:
```ts
const table = new Table({
rows: [],
cantSplit: true,
});
```
## Table Row
A table consists of multiple `table rows`. Table rows have a list of `children` which accepts a list of `table cells` explained below. You can create a simple `table row` like so:
```ts
const tableRow = new TableRow({
children: [
new TableCell({
children: [new Paragraph("hello")],
}),
],
});
```
Or preferably, add the tableRow directly into the `table` without declaring a variable:
```ts
const table = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("hello")],
}),
],
}),
],
});
```
### Options
Here is a list of options you can add to the `table row`:
| Property | Type | Notes |
| ----------- | ------------------------------------- | -------- |
| children | `Array<TableCell>` | Required |
| cantSplit | `boolean` | Optional |
| tableHeader | `boolean` | Optional |
| height | `{ value: number, rule: HeightRule }` | Optional |
### Repeat row
If a table is paginated on multiple pages, it is possible to repeat a row at the top of each new page by setting `tableHeader` to `true`:
```ts
const row = new TableRow({
...,
tableHeader: true,
});
```
## Table Cells
Cells need to be added in the `table row`, you can create a table cell like:
```ts
const tableCell = new TableCell({
children: [new Paragraph("hello")],
});
```
Or preferably, add the tableRow directly into the `table row` without declaring a variable:
```ts
const tableRow = new TableRow({
children: [
new TableCell({
children: [new Paragraph("hello")],
}),
],
});
```
### Options
| Property | Type | Notes |
| ------------- | ----------------------------------- | ----------------------------------------------------------- |
| children | `Array<Paragraph | Table>` | Required. You can nest tables by adding a table into a cell |
| shading | `ITableShadingAttributesProperties` | Optional |
| margins | `ITableCellMarginOptions` | Optional |
| verticalAlign | `VerticalAlign` | Optional |
| columnSpan | `number` | Optional |
| rowSpan | `number` | Optional |
| borders | `BorderOptions` | Optional |
| width | `{ size: number type: WidthType }` | Optional |
#### Border Options
| Property | Type | Notes |
| -------- | ----------------------------------------------------- | -------- |
| top | `{ style: BorderStyle, size: number, color: string }` | Optional |
| bottom | `{ style: BorderStyle, size: number, color: string }` | Optional |
| left | `{ style: BorderStyle, size: number, color: string }` | Optional |
| right | `{ style: BorderStyle, size: number, color: string }` | Optional |
##### Example
```ts
const cell = new TableCell({
...,
borders: {
top: {
style: BorderStyle.DASH_DOT_STROKED,
size: 1,
color: "red",
},
bottom: {
style: BorderStyle.THICK_THIN_MEDIUM_GAP,
size: 5,
color: "889900",
},
},
});
```
##### 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
const cell = new TableCell({
...,
borders: {
top: {
style: BorderStyle.DOT_DOT_DASH,
size: 3,
color: "green",
},
bottom: {
style: BorderStyle.DOT_DOT_DASH,
size: 3,
color: "ff8000",
},
},
});
```
### Add paragraph to a cell
Once you have got the cell, you can add data to it with the `add()` method.
Once you have got the cell, you can add data to it:
```ts
cell.add(new Paragraph("Hello"));
const cell = new TableCell({
children: [new Paragraph("Hello")],
});
```
### Set width of a cell
You can specify the width of a cell using:
`cell.Properties.setWidth(width, format)`
```ts
const cell = new TableCell({
...,
width: {
size: number,
type: WidthType,
},
});
```
format can be:
`WidthType` values 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
| Property | Notes |
| -------- | --------------------------------- |
| AUTO | |
| DXA | value is in twentieths of a point |
| NIL | is considered as zero |
| PCT | percent of table width |
### Example
#### Example
```ts
cell.Properties.setWidth(100, WidthType.DXA);
```
```ts
cell.Properties.setWidth("50%", WidthType.PCT);
```
### Nested Tables
## Borders
BorderStyle can be imported from `docx`. Size determines the thickness. HTML color can be a hex code or alias such as `red`.
To have a table within a table, simply add it in the `children` block of a `table cell`:
```ts
cell.Borders.addTopBorder([BorderStyle], [SIZE], [HTML COLOR]);
const cell = new TableCell({
children: [new Table(...)],
});
```
```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
### Vertical Align
Sets the vertical alignment of the contents of the cell
```ts
import { VerticalAlign } from "docx";
cell.setVerticalAlign([VerticalAlign TYPE]);
const cell = new TableCell({
...,
verticalAlign: VerticalAlign,
});
```
`VerticalAlign` values can be:
| Property | Notes |
| -------- | ------------------------------------------ |
| BOTTOM | Align the contents on the bottom |
| CENTER | Align the contents on the center |
| TOP | Align the contents on the top. The default |
For example, to center align a cell:
```ts
cell.setVerticalAlign(VerticalAlign.CENTER);
const cell = new TableCell({
verticalAlign: VerticalAlign.CENTER,
});
```
## Rows
## Merging cells together
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.
### Row Merge
When cell rows are merged, it counts as multiple rows, so be sure to remove excess cells. It is similar to how HTML's `rowspan` works.
https://www.w3schools.com/tags/att_td_rowspan.asp
```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]);
const cell = new TableCell({
...,
rowSpan: [NUMBER_OF_CELLS_TO_MERGE],
});
```
#### Example
This will merge 3 cells together starting from index `0`:
The example will merge three rows together.
```ts
table.getRow(0).mergeCells(0, 2);
const cell = new TableCell({
...,
rowSpan: 3,
});
```
### Merging on a column
### Column Merge
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
When cell columns are merged, it counts as multiple columns, so be sure to remove excess cells. It is similar to how HTML's `colspan` works.
https://www.w3schools.com/tags/att_td_colspan.asp
```ts
cell.add(new Table(1, 1));
const cell = new TableCell({
...,
columnSpan: [NUMBER_OF_CELLS_TO_MERGE],
});
```
## Pagination
#### Example
###Prevent row pagination
To prevent breaking contents of a row across multiple pages, call `cantSplit()`:
The example will merge three columns together.
```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();
const cell = new TableCell({
...,
columnSpan: 3,
});
```
## Examples
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/4-basic-table.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/4-basic-table.ts_
### Custom borders
Example showing how to add colourful borders to tables
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/20-table-cell-borders.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/20-table-cell-borders.ts_
### Adding images
Example showing how to add images to tables
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/24-images-to-table-cell.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/24-images-to-table-cell.ts_
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo36.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/36-image-to-table-cell.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo36.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/36-image-to-table-cell.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")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/31-tables.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo31.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/31-tables.ts_
### Merging rows
### Shading
Example showing merging of `rows`
Example showing merging of columns and rows and shading
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo32.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/32-merge-and-shade-table-cells.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo32.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/32-merge-and-shade-table-cells.ts_
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo41.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/41-merge-table-cells-2.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo41.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/41-merge-table-cells-2.ts_
### Merging columns
Example showing merging of `columns`
Example showing merging of columns and rows
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo43.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/43-images-to-table-cell-2.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo43.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/43-images-to-table-cell-2.ts_
### Floating tables
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo34.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/34-floating-tables.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo34.ts_
_Source: https://github.com/dolanmiu/docx/blob/master/demo/34-floating-tables.ts_

View File

@ -1,6 +1,6 @@
{
"name": "docx",
"version": "5.0.0-rc5",
"version": "5.0.0-rc7",
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
"main": "build/index.js",
"scripts": {

View File

@ -1,7 +1,8 @@
import { assert, expect } from "chai";
import * as sinon from "sinon";
import { Formatter } from "export/formatter";
import * as file from "file";
import { Paragraph, TextRun } from "file";
import { CoreProperties } from "file/core-properties";
import { Attributes } from "file/xml-components";
@ -14,28 +15,65 @@ describe("Formatter", () => {
describe("#format()", () => {
it("should format simple paragraph", () => {
const paragraph = new file.Paragraph("");
const paragraph = new Paragraph("");
const newJson = formatter.format(paragraph);
assert.isDefined(newJson["w:p"]);
});
it("should remove xmlKeys", () => {
const paragraph = new file.Paragraph("");
const paragraph = new Paragraph("");
const newJson = formatter.format(paragraph);
const stringifiedJson = JSON.stringify(newJson);
assert(stringifiedJson.indexOf("xmlKeys") < 0);
});
it("should format simple paragraph with bold text", () => {
const paragraph = new file.Paragraph("");
paragraph.addRun(
new file.TextRun({
const paragraph = new Paragraph({
children: [
new TextRun({
text: "test",
bold: true,
}),
);
const newJson = formatter.format(paragraph);
assert.isDefined(newJson["w:p"][1]["w:r"][0]["w:rPr"][0]["w:b"]._attr["w:val"]);
],
});
const tree = formatter.format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:r": [
{
"w:rPr": [
{
"w:b": {
_attr: {
"w:val": true,
},
},
},
{
"w:bCs": {
_attr: {
"w:val": true,
},
},
},
],
},
{
"w:t": [
{
_attr: {
"xml:space": "preserve",
},
},
"test",
],
},
],
},
],
});
});
it("should format attributes (rsidSect)", () => {
@ -63,7 +101,7 @@ describe("Formatter", () => {
});
it("should should change 'p' tag into 'w:p' tag", () => {
const paragraph = new file.Paragraph("");
const paragraph = new Paragraph("");
const newJson = formatter.format(paragraph);
assert.isDefined(newJson["w:p"]);
});
@ -76,5 +114,13 @@ describe("Formatter", () => {
const newJson = formatter.format(properties);
assert.isDefined(newJson["cp:coreProperties"]);
});
it("should call the prep method only once", () => {
const paragraph = new Paragraph("");
const spy = sinon.spy(paragraph, "prepForXml");
formatter.format(paragraph);
expect(spy.calledOnce).to.equal(true);
});
});
});

View File

@ -1,7 +1,8 @@
/* tslint:disable:typedef space-before-function-paren */
import { expect } from "chai";
import * as sinon from "sinon";
import { File, Footer, Header } from "file";
import { File, Footer, Header, Paragraph } from "file";
import { Compiler } from "./next-compiler";
@ -72,5 +73,22 @@ describe("Compiler", () => {
expect(fileNames).to.include("word/footer2.xml");
expect(fileNames).to.include("word/_rels/footer2.xml.rels");
});
it("should call the format method X times equalling X files to be formatted", () => {
// This test is required because before, there was a case where Document was formatted twice, which was inefficient
// This also caused issues such as running prepForXml multiple times as format() was ran multiple times.
const paragraph = new Paragraph("");
const doc = new File();
doc.addSection({
properties: {},
children: [paragraph],
});
// tslint:disable-next-line: no-string-literal
const spy = sinon.spy(compiler["formatter"], "format");
compiler.compile(file);
expect(spy.callCount).to.equal(10);
});
});
});

View File

@ -68,13 +68,13 @@ export class Compiler {
file.verifyUpdateFields();
const documentRelationshipCount = file.DocumentRelationships.RelationshipCount + 1;
const documentXmlData = xml(this.formatter.format(file.Document), prettify);
const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media);
return {
Relationships: {
data: (() => {
const xmlData = xml(this.formatter.format(file.Document), prettify);
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
mediaDatas.forEach((mediaData, i) => {
documentMediaDatas.forEach((mediaData, i) => {
file.DocumentRelationships.createRelationship(
documentRelationshipCount + i,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
@ -88,9 +88,7 @@ export class Compiler {
},
Document: {
data: (() => {
const tempXmlData = xml(this.formatter.format(file.Document), prettify);
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, documentRelationshipCount);
const xmlData = this.imageReplacer.replace(documentXmlData, documentMediaDatas, documentRelationshipCount);
return xmlData;
})(),

View File

@ -4,30 +4,33 @@ import { Compiler } from "./next-compiler";
export class Packer {
public static async toBuffer(file: File, prettify?: boolean): Promise<Buffer> {
const zip = this.compiler.compile(file, prettify);
const zipData = (await zip.generateAsync({
const zipData = await zip.generateAsync({
type: "nodebuffer",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
})) as Buffer;
compression: "DEFLATE",
});
return zipData;
}
public static async toBase64String(file: File, prettify?: boolean): Promise<string> {
const zip = this.compiler.compile(file, prettify);
const zipData = (await zip.generateAsync({
const zipData = await zip.generateAsync({
type: "base64",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
})) as string;
compression: "DEFLATE",
});
return zipData;
}
public static async toBlob(file: File, prettify?: boolean): Promise<Blob> {
const zip = this.compiler.compile(file, prettify);
const zipData = (await zip.generateAsync({
const zipData = await zip.generateAsync({
type: "blob",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
})) as Blob;
compression: "DEFLATE",
});
return zipData;
}

View File

@ -1,5 +1,7 @@
import { XmlComponent } from "file/xml-components";
import { DocumentAttributes } from "../document/document-attributes";
import { IStylesOptions } from "../styles";
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
export interface IPropertiesOptions {
@ -11,6 +13,7 @@ export interface IPropertiesOptions {
readonly lastModifiedBy?: string;
readonly revision?: string;
readonly externalStyles?: string;
readonly styles?: IStylesOptions;
}
export class CoreProperties extends XmlComponent {

View File

@ -0,0 +1,42 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { FooterReference } from "./footer-reference";
import { FooterReferenceType } from "./footer-reference-attributes";
describe("footerReference", () => {
it("should create", () => {
const footer = new FooterReference({
footerType: FooterReferenceType.DEFAULT,
footerId: 1,
});
const tree = new Formatter().format(footer);
expect(tree).to.deep.equal({
"w:footerReference": {
_attr: {
"r:id": "rId1",
"w:type": "default",
},
},
});
});
it("should create without a footer type", () => {
const footer = new FooterReference({
footerId: 1,
});
const tree = new Formatter().format(footer);
expect(tree).to.deep.equal({
"w:footerReference": {
_attr: {
"r:id": "rId1",
"w:type": "default",
},
},
});
});
});

View File

@ -0,0 +1,42 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { HeaderReference } from "./header-reference";
import { HeaderReferenceType } from "./header-reference-attributes";
describe("HeaderReference", () => {
it("should create", () => {
const footer = new HeaderReference({
headerType: HeaderReferenceType.DEFAULT,
headerId: 1,
});
const tree = new Formatter().format(footer);
expect(tree).to.deep.equal({
"w:headerReference": {
_attr: {
"r:id": "rId1",
"w:type": "default",
},
},
});
});
it("should create without a header type", () => {
const footer = new HeaderReference({
headerId: 1,
});
const tree = new Formatter().format(footer);
expect(tree).to.deep.equal({
"w:headerReference": {
_attr: {
"r:id": "rId1",
"w:type": "default",
},
},
});
});
});

View File

@ -38,6 +38,7 @@ describe("SectionProperties", () => {
},
pageNumberStart: 10,
pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT,
titlePage: true,
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);

View File

@ -6,7 +6,7 @@ import { Formatter } from "export/formatter";
import { File } from "./file";
import { Footer, Header } from "./header";
import { Paragraph } from "./paragraph";
import { Table } from "./table";
import { Table, TableCell, TableRow } from "./table";
import { TableOfContents } from "./table-of-contents";
describe("File", () => {
@ -108,8 +108,15 @@ describe("File", () => {
file.addSection({
children: [
new Table({
rows: 1,
columns: 1,
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("hello")],
}),
],
}),
],
}),
],
});

View File

@ -46,8 +46,6 @@ export interface ISectionOptions {
export class File {
// tslint:disable-next-line:readonly-keyword
private currentRelationshipId: number = 1;
// tslint:disable-next-line:readonly-keyword
private styles: Styles;
private readonly document: Document;
private readonly headers: IDocumentHeader[] = [];
@ -61,6 +59,7 @@ export class File {
private readonly settings: Settings;
private readonly contentTypes: ContentTypes;
private readonly appProperties: AppProperties;
private readonly styles: Styles;
constructor(
options: IPropertiesOptions = {
@ -97,9 +96,16 @@ export class File {
} else if (options.externalStyles) {
const stylesFactory = new ExternalStylesFactory();
this.styles = stylesFactory.newInstance(options.externalStyles);
} else if (options.styles) {
const stylesFactory = new DefaultStylesFactory();
const defaultStyles = stylesFactory.newInstance();
this.styles = new Styles({
...defaultStyles,
...options.styles,
});
} else {
const stylesFactory = new DefaultStylesFactory();
this.styles = stylesFactory.newInstance();
this.styles = new Styles(stylesFactory.newInstance());
}
this.addDefaultRelationships();
@ -277,10 +283,6 @@ export class File {
return this.styles;
}
public set Styles(styles: Styles) {
this.styles = styles;
}
public get CoreProperties(): CoreProperties {
return this.coreProperties;
}

View File

@ -4,7 +4,7 @@ import * as sinon from "sinon";
import { FooterWrapper } from "./footer-wrapper";
import { Media } from "./media";
import { Paragraph } from "./paragraph";
import { Table } from "./table";
import { Table, TableCell, TableRow } from "./table";
describe("FooterWrapper", () => {
describe("#add", () => {
@ -21,22 +21,20 @@ describe("FooterWrapper", () => {
const spy = sinon.spy(file.Footer, "add");
file.add(
new Table({
rows: 1,
columns: 1,
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("hello")],
}),
],
}),
],
}),
);
expect(spy.called).to.equal(true);
});
it("should call the underlying footer's addImage", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Footer, "add");
// tslint:disable-next-line:no-any
file.addImage({} as any);
expect(spy.called).to.equal(true);
});
});
describe("#addChildElement", () => {

View File

@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
import { FooterReferenceType } from "./document";
import { Footer } from "./footer/footer";
import { Image, Media } from "./media";
import { Media } from "./media";
import { Paragraph } from "./paragraph";
import { Relationships } from "./relationships";
import { Table } from "./table";
@ -25,11 +25,6 @@ export class FooterWrapper {
this.footer.add(item);
}
public addImage(image: Image): FooterWrapper {
this.footer.add(image.Paragraph);
return this;
}
public addChildElement(childElement: XmlComponent): void {
this.footer.addChildElement(childElement);
}

View File

@ -0,0 +1,47 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { Paragraph } from "../paragraph";
import { Footer } from "./footer";
describe("Footer", () => {
it("should create", () => {
const footer = new Footer(1);
const tree = new Formatter().format(footer);
expect(tree).to.deep.equal({
"w:ftr": {
_attr: {
"xmlns:m": "http://schemas.openxmlformats.org/officeDocument/2006/math",
"xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006",
"xmlns:o": "urn:schemas-microsoft-com:office:office",
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
"xmlns:v": "urn:schemas-microsoft-com:vml",
"xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
"xmlns:w10": "urn:schemas-microsoft-com:office:word",
"xmlns:w14": "http://schemas.microsoft.com/office/word/2010/wordml",
"xmlns:w15": "http://schemas.microsoft.com/office/word/2012/wordml",
"xmlns:wne": "http://schemas.microsoft.com/office/word/2006/wordml",
"xmlns:wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
"xmlns:wp14": "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
"xmlns:wpc": "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
"xmlns:wpg": "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
"xmlns:wpi": "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
"xmlns:wps": "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
},
},
});
});
it("should create with initContent", () => {
const header = new Footer(1, new Paragraph({}));
const tree = new Formatter().format(header);
expect(tree).to.deep.equal({
"w:ftr": {},
});
});
});

View File

@ -44,7 +44,8 @@ export class FootNotes extends XmlComponent {
line: 240,
lineRule: "auto",
},
}).addRun(new SeperatorRun()),
children: [new SeperatorRun()],
}),
);
this.root.push(begin);
@ -56,7 +57,8 @@ export class FootNotes extends XmlComponent {
line: 240,
lineRule: "auto",
},
}).addRun(new ContinuationSeperatorRun()),
children: [new ContinuationSeperatorRun()],
}),
);
this.root.push(spacing);
}

View File

@ -4,7 +4,7 @@ import * as sinon from "sinon";
import { HeaderWrapper } from "./header-wrapper";
import { Media } from "./media";
import { Paragraph } from "./paragraph";
import { Table } from "./table";
import { Table, TableCell, TableRow } from "./table";
describe("HeaderWrapper", () => {
describe("#add", () => {
@ -21,8 +21,15 @@ describe("HeaderWrapper", () => {
const spy = sinon.spy(wrapper.Header, "add");
wrapper.add(
new Table({
rows: 1,
columns: 1,
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("hello")],
}),
],
}),
],
}),
);
@ -30,17 +37,6 @@ describe("HeaderWrapper", () => {
});
});
describe("#addImage", () => {
it("should call the underlying header's addImage", () => {
const file = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(file.Header, "add");
// tslint:disable-next-line:no-any
file.addImage({} as any);
expect(spy.called).to.equal(true);
});
});
describe("#addChildElement", () => {
it("should call the underlying header's addChildElement", () => {
const file = new HeaderWrapper(new Media(), 1);

View File

@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
import { HeaderReferenceType } from "./document";
import { Header } from "./header/header";
import { Image, Media } from "./media";
import { Media } from "./media";
import { Paragraph } from "./paragraph";
import { Relationships } from "./relationships";
import { Table } from "./table";
@ -27,11 +27,6 @@ export class HeaderWrapper {
return this;
}
public addImage(image: Image): HeaderWrapper {
this.header.add(image.Paragraph);
return this;
}
public addChildElement(childElement: XmlComponent | string): void {
this.header.addChildElement(childElement);
}

View File

@ -0,0 +1,58 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { Paragraph } from "../paragraph";
import { Header } from "./header";
describe("Header", () => {
it("should create", () => {
const header = new Header(1);
const tree = new Formatter().format(header);
expect(tree).to.deep.equal({
"w:hdr": {
_attr: {
"xmlns:cx": "http://schemas.microsoft.com/office/drawing/2014/chartex",
"xmlns:cx1": "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex",
"xmlns:cx2": "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
"xmlns:cx3": "http://schemas.microsoft.com/office/drawing/2016/5/9/chartex",
"xmlns:cx4": "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex",
"xmlns:cx5": "http://schemas.microsoft.com/office/drawing/2016/5/11/chartex",
"xmlns:cx6": "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex",
"xmlns:cx7": "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex",
"xmlns:cx8": "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex",
"xmlns:m": "http://schemas.openxmlformats.org/officeDocument/2006/math",
"xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006",
"xmlns:o": "urn:schemas-microsoft-com:office:office",
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
"xmlns:v": "urn:schemas-microsoft-com:vml",
"xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
"xmlns:w10": "urn:schemas-microsoft-com:office:word",
"xmlns:w14": "http://schemas.microsoft.com/office/word/2010/wordml",
"xmlns:w15": "http://schemas.microsoft.com/office/word/2012/wordml",
"xmlns:w16cid": "http://schemas.microsoft.com/office/word/2016/wordml/cid",
"xmlns:w16se": "http://schemas.microsoft.com/office/word/2015/wordml/symex",
"xmlns:wne": "http://schemas.microsoft.com/office/word/2006/wordml",
"xmlns:wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
"xmlns:wp14": "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
"xmlns:wpc": "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
"xmlns:wpg": "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
"xmlns:wpi": "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
"xmlns:wps": "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
},
},
});
});
it("should create with initContent", () => {
const header = new Header(1, new Paragraph({}));
const tree = new Formatter().format(header);
expect(tree).to.deep.equal({
"w:hdr": {},
});
});
});

View File

@ -1,13 +0,0 @@
import { ImageParagraph, PictureRun } from "../paragraph";
export class Image {
constructor(private readonly paragraph: ImageParagraph) {}
public get Paragraph(): ImageParagraph {
return this.paragraph;
}
public get Run(): PictureRun {
return this.paragraph.Run;
}
}

View File

@ -1,3 +1,2 @@
export * from "./media";
export * from "./data";
export * from "./image";

View File

@ -80,6 +80,16 @@ describe("Media", () => {
const image = new Media().addMedia("");
expect(image.stream).to.be.an.instanceof(Uint8Array);
});
it("should use data as is if its not a string", () => {
// tslint:disable-next-line
((process as any).atob as any) = () => "atob result";
// tslint:disable-next-line:no-any
(Media as any).generateId = () => "test";
const image = new Media().addMedia(new Buffer(""));
expect(image.stream).to.be.an.instanceof(Uint8Array);
});
});
describe("#getMedia", () => {

View File

@ -7,14 +7,15 @@ import {
ISpacingProperties,
KeepLines,
KeepNext,
LeftTabStop,
MaxRightTabStop,
Spacing,
TabStop,
TabStopType,
ThematicBreak,
} from "../paragraph/formatting";
import { ParagraphProperties } from "../paragraph/properties";
import * as formatting from "../paragraph/run/formatting";
import { RunProperties } from "../paragraph/run/properties";
import { UnderlineType } from "../paragraph/run/underline";
interface ILevelAttributesProperties {
readonly ilvl?: number;
@ -184,7 +185,7 @@ export class LevelBase extends XmlComponent {
return this;
}
public underline(underlineType?: string, color?: string): Level {
public underline(underlineType?: UnderlineType, color?: string): Level {
this.addRunProperty(new formatting.Underline(underlineType, color));
return this;
}
@ -235,13 +236,12 @@ export class LevelBase extends XmlComponent {
return this;
}
public maxRightTabStop(): Level {
this.addParagraphProperty(new MaxRightTabStop());
return this;
public rightTabStop(position: number): Level {
return this.addParagraphProperty(new TabStop(TabStopType.RIGHT, position));
}
public leftTabStop(position: number): Level {
this.addParagraphProperty(new LeftTabStop(position));
this.addParagraphProperty(new TabStop(TabStopType.LEFT, position));
return this;
}

View File

@ -8,6 +8,8 @@ import { Num } from "./num";
import { Numbering } from "./numbering";
import { EMPTY_OBJECT } from "file/xml-components";
import { TabStopPosition } from "../paragraph";
import { UnderlineType } from "../paragraph/run/underline";
describe("Numbering", () => {
let numbering: Numbering;
@ -202,7 +204,7 @@ describe("AbstractNumbering", () => {
it("#maxRightTabStop", () => {
const abstractNumbering = new AbstractNumbering(1);
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").maxRightTabStop();
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").rightTabStop(TabStopPosition.MAX);
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:pPr": [
@ -355,7 +357,7 @@ describe("AbstractNumbering", () => {
it("should set the style if given", () => {
const abstractNumbering = new AbstractNumbering(1);
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline("double");
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline(UnderlineType.DOUBLE);
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:u": { _attr: { "w:val": "double" } } }],
@ -364,7 +366,7 @@ describe("AbstractNumbering", () => {
it("should set the style and color if given", () => {
const abstractNumbering = new AbstractNumbering(1);
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline("double", "005599");
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline(UnderlineType.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" } } }],

View File

@ -1,11 +1,87 @@
import { assert, expect } from "chai";
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { ThematicBreak } from "./border";
import { Border, ThematicBreak } from "./border";
describe("Border", () => {
// TODO: Need tests here
describe("#constructor", () => {
it("should create", () => {
const border = new Border({
top: {
color: "red",
space: 1,
value: "test",
size: 2,
},
bottom: {
color: "red",
space: 3,
value: "test",
size: 4,
},
left: {
color: "red",
space: 5,
value: "test",
size: 6,
},
right: {
color: "red",
space: 7,
value: "test",
size: 8,
},
});
const tree = new Formatter().format(border);
expect(tree).to.deep.equal({
"w:pBdr": [
{
"w:top": {
_attr: {
"w:color": "red",
"w:space": 1,
"w:sz": 2,
"w:val": "test",
},
},
},
{
"w:bottom": {
_attr: {
"w:color": "red",
"w:space": 3,
"w:sz": 4,
"w:val": "test",
},
},
},
{
"w:left": {
_attr: {
"w:color": "red",
"w:space": 5,
"w:sz": 6,
"w:val": "test",
},
},
},
{
"w:right": {
_attr: {
"w:color": "red",
"w:space": 7,
"w:sz": 8,
"w:val": "test",
},
},
},
],
});
});
});
});
describe("ThematicBreak", () => {
@ -16,17 +92,6 @@ describe("ThematicBreak", () => {
});
describe("#constructor()", () => {
it("should create valid JSON", () => {
const stringifiedJson = JSON.stringify(thematicBreak);
try {
JSON.parse(stringifiedJson);
} catch (e) {
assert.isTrue(false);
}
assert.isTrue(true);
});
it("should create a Thematic Break with correct border properties", () => {
const tree = new Formatter().format(thematicBreak);
expect(tree).to.deep.equal({

View File

@ -2,13 +2,13 @@ import { assert } from "chai";
import { Utility } from "tests/utility";
import { LeaderType, LeftTabStop, MaxRightTabStop, RightTabStop } from "./tab-stop";
import { LeaderType, TabStop, TabStopType } from "./tab-stop";
describe("LeftTabStop", () => {
let tabStop: LeftTabStop;
let tabStop: TabStop;
beforeEach(() => {
tabStop = new LeftTabStop(100);
tabStop = new TabStop(TabStopType.LEFT, 100);
});
describe("#constructor()", () => {
@ -29,10 +29,10 @@ describe("LeftTabStop", () => {
});
describe("RightTabStop", () => {
let tabStop: RightTabStop;
let tabStop: TabStop;
beforeEach(() => {
tabStop = new RightTabStop(100, LeaderType.DOT);
tabStop = new TabStop(TabStopType.RIGHT, 100, LeaderType.DOT);
});
describe("#constructor()", () => {
@ -52,28 +52,3 @@ describe("RightTabStop", () => {
});
});
});
describe("MaxRightTabStop", () => {
let tabStop: MaxRightTabStop;
beforeEach(() => {
tabStop = new MaxRightTabStop();
});
describe("#constructor()", () => {
it("should create a Tab Stop with correct attributes", () => {
const newJson = Utility.jsonify(tabStop);
const attributes = {
val: "right",
pos: 9026,
};
assert.equal(JSON.stringify(newJson.root[0].root[0].root), JSON.stringify(attributes));
});
it("should create a Tab Stop with w:tab", () => {
const newJson = Utility.jsonify(tabStop);
assert.equal(newJson.root[0].rootKey, "w:tab");
});
});
});

View File

@ -2,13 +2,13 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export class TabStop extends XmlComponent {
constructor(tab: TabStopItem) {
constructor(type: TabStopType, position: number, leader?: LeaderType) {
super("w:tabs");
this.root.push(tab);
this.root.push(new TabStopItem(type, position, leader));
}
}
export enum TabValue {
export enum TabStopType {
LEFT = "left",
RIGHT = "right",
CENTER = "center",
@ -28,8 +28,12 @@ export enum LeaderType {
UNDERSCORE = "underscore",
}
export enum TabStopPosition {
MAX = 9026,
}
export class TabAttributes extends XmlAttributeComponent<{
readonly val: TabValue;
readonly val: TabStopType;
readonly pos: string | number;
readonly leader?: LeaderType;
}> {
@ -37,7 +41,7 @@ export class TabAttributes extends XmlAttributeComponent<{
}
export class TabStopItem extends XmlComponent {
constructor(value: TabValue, position: string | number, leader?: LeaderType) {
constructor(value: TabStopType, position: string | number, leader?: LeaderType) {
super("w:tab");
this.root.push(
new TabAttributes({
@ -48,27 +52,3 @@ export class TabStopItem extends XmlComponent {
);
}
}
export class MaxRightTabStop extends TabStop {
constructor(leader?: LeaderType) {
super(new TabStopItem(TabValue.RIGHT, 9026, leader));
}
}
export class LeftTabStop extends TabStop {
constructor(position: number, leader?: LeaderType) {
super(new TabStopItem(TabValue.LEFT, position, leader));
}
}
export class RightTabStop extends TabStop {
constructor(position: number, leader?: LeaderType) {
super(new TabStopItem(TabValue.RIGHT, position, leader));
}
}
export class CenterTabStop extends TabStop {
constructor(position: number, leader?: LeaderType) {
super(new TabStopItem(TabValue.CENTER, position, leader));
}
}

View File

@ -1,39 +0,0 @@
// tslint:disable:object-literal-key-quotes
import { assert } from "chai";
import { ImageParagraph } from "./image";
describe("Image", () => {
let image: ImageParagraph;
beforeEach(() => {
image = new ImageParagraph({
stream: new Buffer(""),
path: "",
fileName: "test.png",
dimensions: {
pixels: {
x: 10,
y: 10,
},
emus: {
x: 10,
y: 10,
},
},
});
});
describe("#constructor()", () => {
it("should create valid JSON", () => {
const stringifiedJson = JSON.stringify(image);
try {
JSON.parse(stringifiedJson);
} catch (e) {
assert.isTrue(false);
}
assert.isTrue(true);
});
});
});

View File

@ -1,18 +0,0 @@
import { IDrawingOptions } from "../drawing";
import { IMediaData } from "../media";
import { Paragraph } from "./paragraph";
import { PictureRun } from "./run";
export class ImageParagraph extends Paragraph {
private readonly pictureRun: PictureRun;
constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) {
super({});
this.pictureRun = new PictureRun(imageData, drawingOptions);
this.root.push(this.pictureRun);
}
public get Run(): PictureRun {
return this.pictureRun;
}
}

View File

@ -3,4 +3,3 @@ export * from "./paragraph";
export * from "./properties";
export * from "./run";
export * from "./links";
export * from "./image";

View File

@ -4,7 +4,7 @@ import { Formatter } from "export/formatter";
import { EMPTY_OBJECT } from "file/xml-components";
import { Numbering } from "../numbering";
import { AlignmentType, HeadingLevel, LeaderType } from "./formatting";
import { AlignmentType, HeadingLevel, LeaderType, PageBreak, TabStopPosition, TabStopType } from "./formatting";
import { Paragraph } from "./paragraph";
describe("Paragraph", () => {
@ -254,11 +254,14 @@ describe("Paragraph", () => {
});
describe("#maxRightTabStop()", () => {
it("should add maxRightTabStop to JSON", () => {
it("should add right tab stop to JSON", () => {
const paragraph = new Paragraph({
tabStop: {
maxRight: {},
tabStops: [
{
type: TabStopType.RIGHT,
position: TabStopPosition.MAX,
},
],
});
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
@ -287,12 +290,13 @@ describe("Paragraph", () => {
describe("#leftTabStop()", () => {
it("should add leftTabStop to JSON", () => {
const paragraph = new Paragraph({
tabStop: {
left: {
tabStops: [
{
type: TabStopType.LEFT,
position: 100,
leader: LeaderType.HYPHEN,
},
},
],
});
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
@ -322,12 +326,13 @@ describe("Paragraph", () => {
describe("#rightTabStop()", () => {
it("should add rightTabStop to JSON", () => {
const paragraph = new Paragraph({
tabStop: {
right: {
tabStops: [
{
type: TabStopType.RIGHT,
position: 100,
leader: LeaderType.DOT,
},
},
],
});
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
@ -357,12 +362,13 @@ describe("Paragraph", () => {
describe("#centerTabStop()", () => {
it("should add centerTabStop to JSON", () => {
const paragraph = new Paragraph({
tabStop: {
center: {
tabStops: [
{
type: TabStopType.CENTER,
position: 100,
leader: LeaderType.MIDDLE_DOT,
},
},
],
});
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
@ -492,8 +498,9 @@ describe("Paragraph", () => {
describe("#pageBreak()", () => {
it("should add page break to JSON", () => {
const paragraph = new Paragraph({});
paragraph.pageBreak();
const paragraph = new Paragraph({
children: [new PageBreak()],
});
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [

View File

@ -1,6 +1,5 @@
// http://officeopenxml.com/WPparagraph.php
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
import { Image } from "file/media";
import { Num } from "file/numbering/num";
import { XmlComponent } from "file/xml-components";
@ -12,16 +11,11 @@ import { KeepLines, KeepNext } from "./formatting/keep";
import { PageBreak, PageBreakBefore } from "./formatting/page-break";
import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spacing";
import { HeadingLevel, Style } from "./formatting/style";
import { CenterTabStop, LeaderType, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop";
import { LeaderType, TabStop, TabStopPosition, TabStopType } from "./formatting/tab-stop";
import { NumberProperties } from "./formatting/unordered-list";
import { Bookmark, Hyperlink, OutlineLevel } from "./links";
import { ParagraphProperties } from "./properties";
import { PictureRun, Run, SequentialIdentifier, TextRun } from "./run";
interface ITabStopOptions {
readonly position: number;
readonly leader?: LeaderType;
}
import { PictureRun, Run, SequentialIdentifier, SymbolRun, TextRun } from "./run";
export interface IParagraphOptions {
readonly text?: string;
@ -37,14 +31,11 @@ export interface IParagraphOptions {
readonly indent?: IIndentAttributesProperties;
readonly keepLines?: boolean;
readonly keepNext?: boolean;
readonly tabStop?: {
readonly left?: ITabStopOptions;
readonly right?: ITabStopOptions;
readonly maxRight?: {
readonly tabStops?: Array<{
readonly position: number | TabStopPosition;
readonly type: TabStopType;
readonly leader?: LeaderType;
};
readonly center?: ITabStopOptions;
};
}>;
readonly style?: string;
readonly bullet?: {
readonly level: number;
@ -54,7 +45,7 @@ export interface IParagraphOptions {
readonly level: number;
readonly custom?: boolean;
};
readonly children?: Array<TextRun | PictureRun | Hyperlink>;
readonly children?: Array<TextRun | PictureRun | Hyperlink | SymbolRun | Bookmark | PageBreak | SequentialIdentifier>;
}
export class Paragraph extends XmlComponent {
@ -131,21 +122,9 @@ export class Paragraph extends XmlComponent {
this.properties.push(new KeepNext());
}
if (options.tabStop) {
if (options.tabStop.left) {
this.properties.push(new LeftTabStop(options.tabStop.left.position, options.tabStop.left.leader));
}
if (options.tabStop.right) {
this.properties.push(new RightTabStop(options.tabStop.right.position, options.tabStop.right.leader));
}
if (options.tabStop.maxRight) {
this.properties.push(new MaxRightTabStop(options.tabStop.maxRight.leader));
}
if (options.tabStop.center) {
this.properties.push(new CenterTabStop(options.tabStop.center.position, options.tabStop.center.leader));
if (options.tabStops) {
for (const tabStop of options.tabStops) {
this.properties.push(new TabStop(tabStop.type, tabStop.position, tabStop.leader));
}
}
@ -167,41 +146,18 @@ export class Paragraph extends XmlComponent {
if (options.children) {
for (const child of options.children) {
if (child instanceof Bookmark) {
this.root.push(child.start);
this.root.push(child.text);
this.root.push(child.end);
continue;
}
this.root.push(child);
}
}
}
public addRun(run: Run): Paragraph {
this.root.push(run);
return this;
}
public addHyperLink(hyperlink: Hyperlink): Paragraph {
this.root.push(hyperlink);
return this;
}
public addBookmark(bookmark: Bookmark): Paragraph {
// Bookmarks by spec have three components, a start, text, and end
this.root.push(bookmark.start);
this.root.push(bookmark.text);
this.root.push(bookmark.end);
return this;
}
public addImage(image: Image): PictureRun {
const run = image.Run;
this.addRun(run);
return run;
}
public pageBreak(): Paragraph {
this.root.push(new PageBreak());
return this;
}
public referenceFootnote(id: number): Paragraph {
this.root.push(new FootnoteReferenceRun(id));
return this;
@ -211,9 +167,4 @@ export class Paragraph extends XmlComponent {
this.root.splice(1, 0, run);
return this;
}
public addSequentialIdentifier(identifier: string): Paragraph {
this.root.push(new SequentialIdentifier(identifier));
return this;
}
}

View File

@ -1,4 +1,7 @@
export * from "./run";
export * from "./text-run";
export * from "./symbol-run";
export * from "./picture-run";
export * from "./run-fonts";
export * from "./sequential-identifier";
export * from "./underline";

View File

@ -2,7 +2,7 @@ import { expect } from "chai";
import { Formatter } from "export/formatter";
import { NumberOfPages, Page } from "./page-number";
import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number";
describe("Page", () => {
describe("#constructor()", () => {
@ -21,3 +21,12 @@ describe("NumberOfPages", () => {
});
});
});
describe("NumberOfPagesSection", () => {
describe("#constructor()", () => {
it("uses the font name for both ascii and hAnsi", () => {
const tree = new Formatter().format(new NumberOfPagesSection());
expect(tree).to.deep.equal({ "w:instrText": [{ _attr: { "xml:space": "preserve" } }, "SECTIONPAGES"] });
});
});
});

View File

@ -20,3 +20,11 @@ export class NumberOfPages extends XmlComponent {
this.root.push("NUMPAGES");
}
}
export class NumberOfPagesSection extends XmlComponent {
constructor() {
super("w:instrText");
this.root.push(new TextAttributes({ space: SpaceType.PRESERVE }));
this.root.push("SECTIONPAGES");
}
}

View File

@ -0,0 +1,28 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { Symbol } from "./symbol";
describe("Symbol", () => {
describe("#constructor", () => {
// Note: if no character is given, the output is a MS Windows logo
it("creates an empty symbol run if no character is given", () => {
const s = new Symbol();
const f = new Formatter().format(s);
expect(f).to.deep.equal({ "w:sym": { _attr: { "w:char": "", "w:font": "Wingdings" } } });
});
it("creates the provided symbol with default font", () => {
const s = new Symbol("F071");
const f = new Formatter().format(s);
expect(f).to.deep.equal({ "w:sym": { _attr: { "w:char": "F071", "w:font": "Wingdings" } } });
});
it("creates the provided symbol with the provided font", () => {
const s = new Symbol("F071", "Arial");
const f = new Formatter().format(s);
expect(f).to.deep.equal({ "w:sym": { _attr: { "w:char": "F071", "w:font": "Arial" } } });
});
});
});

View File

@ -0,0 +1,20 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
interface ISymbolAttributesProperties {
readonly char: string;
readonly symbolfont?: string;
}
class SymbolAttributes extends XmlAttributeComponent<ISymbolAttributesProperties> {
protected readonly xmlKeys = {
char: "w:char",
symbolfont: "w:font",
};
}
export class Symbol extends XmlComponent {
constructor(char: string = "", symbolfont: string = "Wingdings") {
super("w:sym");
this.root.push(new SymbolAttributes({ char: char, symbolfont: symbolfont }));
}
}

View File

@ -284,6 +284,22 @@ describe("Run", () => {
});
});
describe("#numberOfTotalPagesSection", () => {
it("should set the run to the RTL mode", () => {
const run = new Run({});
run.numberOfTotalPagesSection();
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{ "w:fldChar": { _attr: { "w:fldCharType": "begin" } } },
{ "w:instrText": [{ _attr: { "xml:space": "preserve" } }, "SECTIONPAGES"] },
{ "w:fldChar": { _attr: { "w:fldCharType": "separate" } } },
{ "w:fldChar": { _attr: { "w:fldCharType": "end" } } },
],
});
});
});
describe("#pageNumber", () => {
it("should set the run to the RTL mode", () => {
const run = new Run({});

View File

@ -2,6 +2,7 @@
import { ShadingType } from "file/table";
import { XmlComponent } from "file/xml-components";
import { FieldInstruction } from "file/table-of-contents/field-instruction";
import { Break } from "./break";
import { Caps, SmallCaps } from "./caps";
import { Begin, End, Separate } from "./field";
@ -21,7 +22,7 @@ import {
SizeComplexScript,
Strike,
} from "./formatting";
import { NumberOfPages, Page } from "./page-number";
import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number";
import { RunProperties } from "./properties";
import { RunFonts } from "./run-fonts";
import { SubScript, SuperScript } from "./script";
@ -56,6 +57,7 @@ export interface IRunOptions {
readonly fill: string;
readonly color: string;
};
readonly children?: Array<Begin | FieldInstruction | Separate | End>;
}
export class Run extends XmlComponent {
@ -134,6 +136,12 @@ export class Run extends XmlComponent {
this.properties.push(new Shading(options.shading.type, options.shading.fill, options.shading.color));
this.properties.push(new ShadowComplexScript(options.shading.type, options.shading.fill, options.shading.color));
}
if (options.children) {
for (const child of options.children) {
this.root.push(child);
}
}
}
public break(): Run {
@ -161,4 +169,12 @@ export class Run extends XmlComponent {
this.root.push(new End());
return this;
}
public numberOfTotalPagesSection(): Run {
this.root.push(new Begin());
this.root.push(new NumberOfPagesSection());
this.root.push(new Separate());
this.root.push(new End());
return this;
}
}

View File

@ -0,0 +1,76 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { UnderlineType } from "./underline";
import { SymbolRun } from "./symbol-run";
describe("SymbolRun", () => {
let run: SymbolRun;
describe("#constructor()", () => {
it("should create symbol run from text input", () => {
run = new SymbolRun("F071");
const f = new Formatter().format(run);
expect(f).to.deep.equal({
"w:r": [{ "w:sym": { _attr: { "w:char": "F071", "w:font": "Wingdings" } } }],
});
});
it("should create symbol run from object input with just 'char' specified", () => {
run = new SymbolRun({ char: "F071" });
const f = new Formatter().format(run);
expect(f).to.deep.equal({
"w:r": [{ "w:sym": { _attr: { "w:char": "F071", "w:font": "Wingdings" } } }],
});
});
it("should create symbol run from object input with just 'char' specified", () => {
run = new SymbolRun({ char: "F071", symbolfont: "Arial" });
const f = new Formatter().format(run);
expect(f).to.deep.equal({
"w:r": [{ "w:sym": { _attr: { "w:char": "F071", "w:font": "Arial" } } }],
});
});
it("should add other standard run properties", () => {
run = new SymbolRun({
char: "F071",
symbolfont: "Arial",
italics: true,
bold: true,
underline: {
color: "red",
type: UnderlineType.DOUBLE,
},
color: "green",
size: 40,
highlight: "yellow",
});
const f = new Formatter().format(run);
expect(f).to.deep.equal({
"w:r": [
{
"w:rPr": [
{ "w:b": { _attr: { "w:val": true } } },
{ "w:bCs": { _attr: { "w:val": true } } },
{ "w:i": { _attr: { "w:val": true } } },
{ "w:iCs": { _attr: { "w:val": true } } },
{ "w:u": { _attr: { "w:val": "double", "w:color": "red" } } },
{ "w:color": { _attr: { "w:val": "green" } } },
{ "w:sz": { _attr: { "w:val": 40 } } },
{ "w:szCs": { _attr: { "w:val": 40 } } },
{ "w:highlight": { _attr: { "w:val": "yellow" } } },
{ "w:highlightCs": { _attr: { "w:val": "yellow" } } },
],
},
{
"w:sym": { _attr: { "w:char": "F071", "w:font": "Arial" } },
},
],
});
});
});
});

View File

@ -0,0 +1,20 @@
import { IRunOptions, Run } from "./run";
import { Symbol } from "./run-components/symbol";
export interface ISymbolRunOptions extends IRunOptions {
readonly char: string;
readonly symbolfont?: string;
}
export class SymbolRun extends Run {
constructor(options: ISymbolRunOptions | string) {
if (typeof options === "string") {
super({});
this.root.push(new Symbol(options));
return;
}
super(options);
this.root.push(new Symbol(options.char, options.symbolfont));
}
}

View File

@ -27,7 +27,7 @@ describe("Underline", () => {
});
it("should use the given style type and color", () => {
const underline = new u.Underline("double", "FF00CC");
const underline = new u.Underline(u.UnderlineType.DOUBLE, "FF00CC");
const tree = new Formatter().format(underline);
expect(tree).to.deep.equal({
"w:u": { _attr: { "w:val": "double", "w:color": "FF00CC" } },

View File

@ -33,7 +33,7 @@ export abstract class BaseUnderline extends XmlComponent {
}
export class Underline extends BaseUnderline {
constructor(underlineType: string = "single", color?: string) {
constructor(underlineType: UnderlineType = UnderlineType.SINGLE, color?: string) {
super(underlineType, color);
}
}

View File

@ -38,11 +38,13 @@ export class ExternalStylesFactory {
throw new Error("can not find styles element");
}
const importedStyle = new Styles(new ImportedRootElementAttributes(stylesXmlElement.attributes));
const stylesElements = stylesXmlElement.elements || [];
for (const childElm of stylesElements) {
importedStyle.push(convertToXmlComponent(childElm) as ImportedXmlComponent);
}
const importedStyle = new Styles({
initialStyles: new ImportedRootElementAttributes(stylesXmlElement.attributes),
importedStyles: stylesElements.map((childElm) => convertToXmlComponent(childElm) as ImportedXmlComponent),
});
return importedStyle;
}
}

View File

@ -1,7 +1,7 @@
import { DocumentAttributes } from "../document/document-attributes";
import { Color, Italics, Size } from "../paragraph/run/formatting";
import { Styles } from "./";
import { IStylesOptions } from "./styles";
import { DocumentDefaults } from "./defaults";
import {
FootnoteReferenceStyle,
FootnoteText,
@ -18,7 +18,7 @@ import {
} from "./style";
export class DefaultStylesFactory {
public newInstance(): Styles {
public newInstance(): IStylesOptions {
const documentAttributes = new DocumentAttributes({
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
@ -27,57 +27,55 @@ export class DefaultStylesFactory {
w15: "http://schemas.microsoft.com/office/word/2012/wordml",
Ignorable: "w14 w15",
});
const styles = new Styles(documentAttributes);
styles.createDocumentDefaults();
const titleStyle = new TitleStyle();
titleStyle.addRunProperty(new Size(56));
styles.push(titleStyle);
const heading1Style = new Heading1Style();
heading1Style.addRunProperty(new Color("2E74B5"));
heading1Style.addRunProperty(new Size(32));
styles.push(heading1Style);
const heading2Style = new Heading2Style();
heading2Style.addRunProperty(new Color("2E74B5"));
heading2Style.addRunProperty(new Size(26));
styles.push(heading2Style);
const heading3Style = new Heading3Style();
heading3Style.addRunProperty(new Color("1F4D78"));
heading3Style.addRunProperty(new Size(24));
styles.push(heading3Style);
const heading4Style = new Heading4Style();
heading4Style.addRunProperty(new Color("2E74B5"));
heading4Style.addRunProperty(new Italics());
styles.push(heading4Style);
const heading5Style = new Heading5Style();
heading5Style.addRunProperty(new Color("2E74B5"));
styles.push(heading5Style);
const heading6Style = new Heading6Style();
heading6Style.addRunProperty(new Color("1F4D78"));
styles.push(heading6Style);
const listParagraph = new ListParagraph();
// listParagraph.addParagraphProperty();
styles.push(listParagraph);
const hyperLinkStyle = new HyperlinkStyle();
styles.push(hyperLinkStyle);
const footnoteReferenceStyle = new FootnoteReferenceStyle();
styles.push(footnoteReferenceStyle);
const footnoteTextStyle = new FootnoteText();
styles.push(footnoteTextStyle);
const footnoteTextCharStyle = new FootnoteTextChar();
styles.push(footnoteTextCharStyle);
return styles;
return {
initialStyles: documentAttributes,
importedStyles: [
new DocumentDefaults(),
new TitleStyle({
run: {
size: 56,
},
}),
new Heading1Style({
run: {
color: "2E74B5",
size: 32,
},
}),
new Heading2Style({
run: {
color: "2E74B5",
size: 26,
},
}),
new Heading3Style({
run: {
color: "1F4D78",
size: 24,
},
}),
new Heading4Style({
run: {
color: "2E74B5",
italics: true,
},
}),
new Heading5Style({
run: {
color: "2E74B5",
},
}),
new Heading6Style({
run: {
color: "1F4D78",
},
}),
new ListParagraph({}),
new HyperlinkStyle({}),
new FootnoteReferenceStyle({}),
new FootnoteText({}),
new FootnoteTextChar({}),
],
};
}
}

View File

@ -1,15 +1,16 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { UnderlineType } from "file/paragraph/run/underline";
import { ShadingType } from "file/table";
import { EMPTY_OBJECT } from "file/xml-components";
import { CharacterStyle } from "./character-style";
import { EMPTY_OBJECT } from "file/xml-components";
describe("CharacterStyle", () => {
describe("#constructor", () => {
it("should set the style type to character and use the given style id", () => {
const style = new CharacterStyle("myStyleId");
const style = new CharacterStyle({ id: "myStyleId" });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -17,7 +18,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -29,7 +30,10 @@ describe("CharacterStyle", () => {
});
it("should set the name of the style, if given", () => {
const style = new CharacterStyle("myStyleId", "Style Name");
const style = new CharacterStyle({
id: "myStyleId",
name: "Style Name",
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -38,7 +42,222 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
{
"w:unhideWhenUsed": EMPTY_OBJECT,
},
],
});
});
it("should add smallCaps", () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
smallCaps: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:smallCaps": { _attr: { "w:val": true } } }],
},
{
"w:uiPriority": {
_attr: {
"w:val": 99,
},
},
},
{
"w:unhideWhenUsed": EMPTY_OBJECT,
},
],
});
});
it("should add allCaps", () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
allCaps: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:caps": { _attr: { "w:val": true } } }],
},
{
"w:uiPriority": {
_attr: {
"w:val": 99,
},
},
},
{
"w:unhideWhenUsed": EMPTY_OBJECT,
},
],
});
});
it("should add strike", () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
strike: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:strike": { _attr: { "w:val": true } } }],
},
{
"w:uiPriority": {
_attr: {
"w:val": 99,
},
},
},
{
"w:unhideWhenUsed": EMPTY_OBJECT,
},
],
});
});
it("should add double strike", () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
doubleStrike: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:dstrike": { _attr: { "w:val": true } } }],
},
{
"w:uiPriority": {
_attr: {
"w:val": 99,
},
},
},
{
"w:unhideWhenUsed": EMPTY_OBJECT,
},
],
});
});
it("should add sub script", () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
subScript: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [
{
"w:vertAlign": {
_attr: {
"w:val": "subscript",
},
},
},
],
},
{
"w:uiPriority": {
_attr: {
"w:val": 99,
},
},
},
{
"w:unhideWhenUsed": EMPTY_OBJECT,
},
],
});
});
it("should add font", () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
font: "test font",
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [
{
"w:rFonts": {
_attr: {
"w:ascii": "test font",
"w:cs": "test font",
"w:eastAsia": "test font",
"w:hAnsi": "test font",
},
},
},
],
},
{
"w:uiPriority": {
_attr: {
"w:val": 99,
},
},
},
{
"w:unhideWhenUsed": EMPTY_OBJECT,
},
],
});
});
it("should add character spacing", () => {
const style = new CharacterStyle({
id: "myStyleId",
run: {
characterSpacing: 100,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
{
"w:rPr": [{ "w:spacing": { _attr: { "w:val": 100 } } }],
},
{
"w:uiPriority": {
_attr: {
"w:val": 99,
},
},
},
@ -52,7 +271,7 @@ describe("CharacterStyle", () => {
describe("formatting methods: style attributes", () => {
it("#basedOn", () => {
const style = new CharacterStyle("myStyleId").basedOn("otherId");
const style = new CharacterStyle({ id: "myStyleId", basedOn: "otherId" });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -60,7 +279,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -75,7 +294,12 @@ describe("CharacterStyle", () => {
describe("formatting methods: run properties", () => {
it("#size", () => {
const style = new CharacterStyle("myStyleId").size(24);
const style = new CharacterStyle({
id: "myStyleId",
run: {
size: 24,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -86,7 +310,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -99,7 +323,12 @@ describe("CharacterStyle", () => {
describe("#underline", () => {
it("should set underline to 'single' if no arguments are given", () => {
const style = new CharacterStyle("myStyleId").underline();
const style = new CharacterStyle({
id: "myStyleId",
run: {
underline: {},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -110,7 +339,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -122,7 +351,14 @@ describe("CharacterStyle", () => {
});
it("should set the style if given", () => {
const style = new CharacterStyle("myStyleId").underline("double");
const style = new CharacterStyle({
id: "myStyleId",
run: {
underline: {
type: UnderlineType.DOUBLE,
},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -133,7 +369,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -145,7 +381,15 @@ describe("CharacterStyle", () => {
});
it("should set the style and color if given", () => {
const style = new CharacterStyle("myStyleId").underline("double", "005599");
const style = new CharacterStyle({
id: "myStyleId",
run: {
underline: {
type: UnderlineType.DOUBLE,
color: "005599",
},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -156,7 +400,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -169,7 +413,12 @@ describe("CharacterStyle", () => {
});
it("#superScript", () => {
const style = new CharacterStyle("myStyleId").superScript();
const style = new CharacterStyle({
id: "myStyleId",
run: {
superScript: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -188,7 +437,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -200,7 +449,12 @@ describe("CharacterStyle", () => {
});
it("#color", () => {
const style = new CharacterStyle("myStyleId").color("123456");
const style = new CharacterStyle({
id: "myStyleId",
run: {
color: "123456",
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -211,7 +465,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -223,7 +477,12 @@ describe("CharacterStyle", () => {
});
it("#bold", () => {
const style = new CharacterStyle("myStyleId").bold();
const style = new CharacterStyle({
id: "myStyleId",
run: {
bold: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -234,7 +493,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -246,7 +505,12 @@ describe("CharacterStyle", () => {
});
it("#italics", () => {
const style = new CharacterStyle("myStyleId").italics();
const style = new CharacterStyle({
id: "myStyleId",
run: {
italics: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -257,7 +521,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -269,7 +533,7 @@ describe("CharacterStyle", () => {
});
it("#link", () => {
const style = new CharacterStyle("myStyleId").link("MyLink");
const style = new CharacterStyle({ id: "myStyleId", link: "MyLink" });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -277,7 +541,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -290,7 +554,7 @@ describe("CharacterStyle", () => {
});
it("#semiHidden", () => {
const style = new CharacterStyle("myStyleId").semiHidden();
const style = new CharacterStyle({ id: "myStyleId", semiHidden: true });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -298,7 +562,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -309,7 +573,12 @@ describe("CharacterStyle", () => {
});
it("#highlight", () => {
const style = new CharacterStyle("myStyleId").highlight("005599");
const style = new CharacterStyle({
id: "myStyleId",
run: {
highlight: "005599",
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -320,7 +589,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -332,7 +601,16 @@ describe("CharacterStyle", () => {
});
it("#shadow", () => {
const style = new CharacterStyle("myStyleId").shadow("pct10", "00FFFF", "FF0000");
const style = new CharacterStyle({
id: "myStyleId",
run: {
shadow: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -343,7 +621,7 @@ describe("CharacterStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},

View File

@ -1,69 +1,128 @@
import * as formatting from "file/paragraph/run/formatting";
import { RunProperties } from "file/paragraph/run/properties";
import { XmlComponent } from "file/xml-components";
import { UnderlineType } from "file/paragraph/run/underline";
import { BasedOn, Link, SemiHidden, UiPriority, UnhideWhenUsed } from "./components";
import { Style } from "./style";
export interface IBaseCharacterStyleOptions {
readonly basedOn?: string;
readonly link?: string;
readonly semiHidden?: boolean;
readonly run?: {
readonly size?: number;
readonly bold?: boolean;
readonly italics?: boolean;
readonly smallCaps?: boolean;
readonly allCaps?: boolean;
readonly strike?: boolean;
readonly doubleStrike?: boolean;
readonly subScript?: boolean;
readonly superScript?: boolean;
readonly underline?: {
readonly type?: UnderlineType;
readonly color?: string;
};
readonly color?: string;
readonly font?: string;
readonly characterSpacing?: number;
readonly highlight?: string;
readonly shadow?: {
readonly type: string;
readonly fill: string;
readonly color: string;
};
};
}
export interface ICharacterStyleOptions extends IBaseCharacterStyleOptions {
readonly id: string;
readonly name?: string;
}
export class CharacterStyle extends Style {
private readonly runProperties: RunProperties;
constructor(styleId: string, name?: string) {
super({ type: "character", styleId: styleId }, name);
constructor(options: ICharacterStyleOptions) {
super({ type: "character", styleId: options.id }, options.name);
this.runProperties = new RunProperties();
this.root.push(this.runProperties);
this.root.push(new UiPriority("99"));
this.root.push(new UiPriority(99));
this.root.push(new UnhideWhenUsed());
if (options.basedOn) {
this.root.push(new BasedOn(options.basedOn));
}
public basedOn(parentId: string): CharacterStyle {
this.root.push(new BasedOn(parentId));
return this;
if (options.link) {
this.root.push(new Link(options.link));
}
public addRunProperty(property: XmlComponent): CharacterStyle {
this.runProperties.push(property);
return this;
}
public color(color: string): CharacterStyle {
return this.addRunProperty(new formatting.Color(color));
}
public bold(): CharacterStyle {
return this.addRunProperty(new formatting.Bold());
}
public italics(): CharacterStyle {
return this.addRunProperty(new formatting.Italics());
}
public underline(underlineType?: string, color?: string): CharacterStyle {
return this.addRunProperty(new formatting.Underline(underlineType, color));
}
public superScript(): CharacterStyle {
return this.addRunProperty(new formatting.SuperScript());
}
public size(twips: number): CharacterStyle {
return this.addRunProperty(new formatting.Size(twips)).addRunProperty(new formatting.SizeComplexScript(twips));
}
public link(link: string): CharacterStyle {
this.root.push(new Link(link));
return this;
}
public semiHidden(): CharacterStyle {
if (options.semiHidden) {
this.root.push(new SemiHidden());
return this;
}
public highlight(color: string): CharacterStyle {
return this.addRunProperty(new formatting.Highlight(color));
if (options.run) {
if (options.run.size) {
this.runProperties.push(new formatting.Size(options.run.size));
this.runProperties.push(new formatting.SizeComplexScript(options.run.size));
}
public shadow(value: string, fill: string, color: string): CharacterStyle {
return this.addRunProperty(new formatting.Shading(value, fill, color));
if (options.run.bold) {
this.runProperties.push(new formatting.Bold());
}
if (options.run.italics) {
this.runProperties.push(new formatting.Italics());
}
if (options.run.smallCaps) {
this.runProperties.push(new formatting.SmallCaps());
}
if (options.run.allCaps) {
this.runProperties.push(new formatting.Caps());
}
if (options.run.strike) {
this.runProperties.push(new formatting.Strike());
}
if (options.run.doubleStrike) {
this.runProperties.push(new formatting.DoubleStrike());
}
if (options.run.subScript) {
this.runProperties.push(new formatting.SubScript());
}
if (options.run.superScript) {
this.runProperties.push(new formatting.SuperScript());
}
if (options.run.underline) {
this.runProperties.push(new formatting.Underline(options.run.underline.type, options.run.underline.color));
}
if (options.run.color) {
this.runProperties.push(new formatting.Color(options.run.color));
}
if (options.run.font) {
this.runProperties.push(new formatting.RunFonts(options.run.font));
}
if (options.run.characterSpacing) {
this.runProperties.push(new formatting.CharacterSpacing(options.run.characterSpacing));
}
if (options.run.highlight) {
this.runProperties.push(new formatting.Highlight(options.run.highlight));
}
if (options.run.shadow) {
this.runProperties.push(new formatting.Shading(options.run.shadow.type, options.run.shadow.fill, options.run.shadow.color));
}
}
}
}

View File

@ -30,9 +30,9 @@ describe("Style components", () => {
});
it("UiPriority#constructor", () => {
const style = new components.UiPriority("123");
const style = new components.UiPriority(123);
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ "w:uiPriority": { _attr: { "w:val": "123" } } });
expect(tree).to.deep.equal({ "w:uiPriority": { _attr: { "w:val": 123 } } });
});
it("UnhideWhenUsed#constructor", () => {

View File

@ -1,7 +1,8 @@
// http://officeopenxml.com/WPstyleGenProps.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
interface IComponentAttributes {
readonly val: string;
readonly val: string | number;
}
class ComponentAttributes extends XmlAttributeComponent<IComponentAttributes> {
@ -37,7 +38,7 @@ export class Link extends XmlComponent {
}
export class UiPriority extends XmlComponent {
constructor(value: string) {
constructor(value: number) {
super("w:uiPriority");
// TODO: this value should be a ST_DecimalNumber
this.root.push(new ComponentAttributes({ val: value }));

View File

@ -1,12 +1,15 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import * as defaultStyels from "./default-styles";
import * as defaultStyles from "./default-styles";
import { EMPTY_OBJECT } from "file/xml-components";
describe("Default Styles", () => {
it("HeadingStyle#constructor", () => {
const style = new defaultStyels.HeadingStyle("Heading1", "Heading 1");
const style = new defaultStyles.HeadingStyle({
id: "Heading1",
name: "Heading 1",
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -20,7 +23,7 @@ describe("Default Styles", () => {
});
it("TitleStyle#constructor", () => {
const style = new defaultStyels.TitleStyle();
const style = new defaultStyles.TitleStyle({});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -34,7 +37,7 @@ describe("Default Styles", () => {
});
it("Heading1Style#constructor", () => {
const style = new defaultStyels.Heading1Style();
const style = new defaultStyles.Heading1Style({});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -48,7 +51,7 @@ describe("Default Styles", () => {
});
it("Heading2Style#constructor", () => {
const style = new defaultStyels.Heading2Style();
const style = new defaultStyles.Heading2Style({});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -62,7 +65,7 @@ describe("Default Styles", () => {
});
it("Heading3Style#constructor", () => {
const style = new defaultStyels.Heading3Style();
const style = new defaultStyles.Heading3Style({});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -76,7 +79,7 @@ describe("Default Styles", () => {
});
it("Heading4Style#constructor", () => {
const style = new defaultStyels.Heading4Style();
const style = new defaultStyles.Heading4Style({});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -90,7 +93,7 @@ describe("Default Styles", () => {
});
it("Heading5Style#constructor", () => {
const style = new defaultStyels.Heading5Style();
const style = new defaultStyles.Heading5Style({});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -104,7 +107,7 @@ describe("Default Styles", () => {
});
it("Heading6Style#constructor", () => {
const style = new defaultStyels.Heading6Style();
const style = new defaultStyles.Heading6Style({});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -118,7 +121,7 @@ describe("Default Styles", () => {
});
it("ListParagraph#constructor", () => {
const style = new defaultStyels.ListParagraph();
const style = new defaultStyles.ListParagraph({});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -131,7 +134,7 @@ describe("Default Styles", () => {
});
it("FootnoteText#constructor", () => {
const style = new defaultStyels.FootnoteText();
const style = new defaultStyles.FootnoteText({});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -171,14 +174,14 @@ describe("Default Styles", () => {
{ "w:basedOn": { _attr: { "w:val": "Normal" } } },
{ "w:link": { _attr: { "w:val": "FootnoteTextChar" } } },
{
"w:uiPriority": {
_attr: {
"w:val": "99",
},
},
"w:semiHidden": EMPTY_OBJECT,
},
{
"w:semiHidden": EMPTY_OBJECT,
"w:uiPriority": {
_attr: {
"w:val": 99,
},
},
},
{
"w:unhideWhenUsed": EMPTY_OBJECT,
@ -188,7 +191,7 @@ describe("Default Styles", () => {
});
it("FootnoteReferenceStyle#constructor", () => {
const style = new defaultStyels.FootnoteReferenceStyle();
const style = new defaultStyles.FootnoteReferenceStyle({});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -208,7 +211,7 @@ describe("Default Styles", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -225,7 +228,7 @@ describe("Default Styles", () => {
});
it("FootnoteTextChar#constructor", () => {
const style = new defaultStyels.FootnoteTextChar();
const style = new defaultStyles.FootnoteTextChar({});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -252,7 +255,7 @@ describe("Default Styles", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -269,19 +272,28 @@ describe("Default Styles", () => {
});
it("HyperlinkStyle#constructor", () => {
const style = new defaultStyels.HyperlinkStyle();
const style = new defaultStyles.HyperlinkStyle({});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{ _attr: { "w:type": "character", "w:styleId": "Hyperlink" } },
{ "w:name": { _attr: { "w:val": "Hyperlink" } } },
{
"w:rPr": [{ "w:color": { _attr: { "w:val": "0563C1" } } }, { "w:u": { _attr: { "w:val": "single" } } }],
"w:rPr": [
{ "w:u": { _attr: { "w:val": "single" } } },
{
"w:color": {
_attr: {
"w:val": "0563C1",
},
},
},
],
},
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},

View File

@ -1,106 +1,170 @@
import { CharacterStyle } from "./character-style";
import { ParagraphStyle } from "./paragraph-style";
import { UnderlineType } from "file/paragraph/run/underline";
import { CharacterStyle, IBaseCharacterStyleOptions } from "./character-style";
import { IBaseParagraphStyleOptions, IParagraphStyleOptions, ParagraphStyle } from "./paragraph-style";
export class HeadingStyle extends ParagraphStyle {
constructor(styleId: string, name: string) {
super(styleId, name);
this.basedOn("Normal");
this.next("Normal");
this.quickFormat();
constructor(options: IParagraphStyleOptions) {
super({
...options,
basedOn: "Normal",
next: "Normal",
quickFormat: true,
});
}
}
export class TitleStyle extends HeadingStyle {
constructor() {
super("Title", "Title");
constructor(options: IBaseParagraphStyleOptions) {
super({
...options,
id: "Title",
name: "Title",
});
}
}
export class Heading1Style extends HeadingStyle {
constructor() {
super("Heading1", "Heading 1");
constructor(options: IBaseParagraphStyleOptions) {
super({
...options,
id: "Heading1",
name: "Heading 1",
});
}
}
export class Heading2Style extends HeadingStyle {
constructor() {
super("Heading2", "Heading 2");
constructor(options: IBaseParagraphStyleOptions) {
super({
...options,
id: "Heading2",
name: "Heading 2",
});
}
}
export class Heading3Style extends HeadingStyle {
constructor() {
super("Heading3", "Heading 3");
constructor(options: IBaseParagraphStyleOptions) {
super({
...options,
id: "Heading3",
name: "Heading 3",
});
}
}
export class Heading4Style extends HeadingStyle {
constructor() {
super("Heading4", "Heading 4");
constructor(options: IBaseParagraphStyleOptions) {
super({
...options,
id: "Heading4",
name: "Heading 4",
});
}
}
export class Heading5Style extends HeadingStyle {
constructor() {
super("Heading5", "Heading 5");
constructor(options: IBaseParagraphStyleOptions) {
super({
...options,
id: "Heading5",
name: "Heading 5",
});
}
}
export class Heading6Style extends HeadingStyle {
constructor() {
super("Heading6", "Heading 6");
constructor(options: IBaseParagraphStyleOptions) {
super({
...options,
id: "Heading6",
name: "Heading 6",
});
}
}
export class ListParagraph extends ParagraphStyle {
constructor() {
super("ListParagraph", "List Paragraph");
this.basedOn("Normal");
this.quickFormat();
constructor(options: IBaseParagraphStyleOptions) {
super({
...options,
id: "ListParagraph",
name: "List Paragraph",
basedOn: "Normal",
quickFormat: true,
});
}
}
export class FootnoteText extends ParagraphStyle {
constructor() {
super("FootnoteText", "footnote text");
this.basedOn("Normal")
.link("FootnoteTextChar")
.uiPriority("99")
.semiHidden()
.unhideWhenUsed()
.spacing({
constructor(options: IBaseParagraphStyleOptions) {
super({
...options,
id: "FootnoteText",
name: "footnote text",
link: "FootnoteTextChar",
basedOn: "Normal",
uiPriority: 99,
semiHidden: true,
unhideWhenUsed: true,
paragraph: {
spacing: {
after: 0,
line: 240,
lineRule: "auto",
})
.size(20);
},
},
run: {
size: 20,
},
});
}
}
export class FootnoteReferenceStyle extends CharacterStyle {
constructor() {
super("FootnoteReference", "footnote reference");
this.basedOn("DefaultParagraphFont")
.semiHidden()
.superScript();
constructor(options: IBaseCharacterStyleOptions) {
super({
...options,
id: "FootnoteReference",
name: "footnote reference",
basedOn: "DefaultParagraphFont",
semiHidden: true,
run: {
superScript: true,
},
});
}
}
export class FootnoteTextChar extends CharacterStyle {
constructor() {
super("FootnoteTextChar", "Footnote Text Char");
this.basedOn("DefaultParagraphFont")
.link("FootnoteText")
.semiHidden()
.size(20);
constructor(options: IBaseCharacterStyleOptions) {
super({
...options,
id: "FootnoteTextChar",
name: "Footnote Text Char",
basedOn: "DefaultParagraphFont",
link: "FootnoteText",
semiHidden: true,
run: {
size: 20,
},
});
}
}
export class HyperlinkStyle extends CharacterStyle {
constructor() {
super("Hyperlink", "Hyperlink");
this.basedOn("DefaultParagraphFont")
.color("0563C1")
.underline("single");
constructor(options: IBaseCharacterStyleOptions) {
super({
...options,
id: "Hyperlink",
name: "Hyperlink",
basedOn: "DefaultParagraphFont",
run: {
color: "0563C1",
underline: {
type: UnderlineType.SINGLE,
},
},
});
}
}

View File

@ -1,15 +1,17 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { AlignmentType, TabStopPosition } from "file/paragraph";
import { UnderlineType } from "file/paragraph/run/underline";
import { ShadingType } from "file/table";
import { EMPTY_OBJECT } from "file/xml-components";
import { ParagraphStyle } from "./paragraph-style";
import { EMPTY_OBJECT } from "file/xml-components";
describe("ParagraphStyle", () => {
describe("#constructor", () => {
it("should set the style type to paragraph and use the given style id", () => {
const style = new ParagraphStyle("myStyleId");
const style = new ParagraphStyle({ id: "myStyleId" });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": { _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
@ -17,7 +19,10 @@ describe("ParagraphStyle", () => {
});
it("should set the name of the style, if given", () => {
const style = new ParagraphStyle("myStyleId", "Style Name");
const style = new ParagraphStyle({
id: "myStyleId",
name: "Style Name",
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -30,7 +35,7 @@ describe("ParagraphStyle", () => {
describe("formatting methods: style attributes", () => {
it("#basedOn", () => {
const style = new ParagraphStyle("myStyleId").basedOn("otherId");
const style = new ParagraphStyle({ id: "myStyleId", basedOn: "otherId" });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -41,7 +46,7 @@ describe("ParagraphStyle", () => {
});
it("#quickFormat", () => {
const style = new ParagraphStyle("myStyleId").quickFormat();
const style = new ParagraphStyle({ id: "myStyleId", quickFormat: true });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:qFormat": EMPTY_OBJECT }],
@ -49,7 +54,7 @@ describe("ParagraphStyle", () => {
});
it("#next", () => {
const style = new ParagraphStyle("myStyleId").next("otherId");
const style = new ParagraphStyle({ id: "myStyleId", next: "otherId" });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -62,7 +67,12 @@ describe("ParagraphStyle", () => {
describe("formatting methods: paragraph properties", () => {
it("#indent", () => {
const style = new ParagraphStyle("myStyleId").indent({ left: 720 });
const style = new ParagraphStyle({
id: "myStyleId",
paragraph: {
indent: { left: 720 },
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -75,7 +85,7 @@ describe("ParagraphStyle", () => {
});
it("#spacing", () => {
const style = new ParagraphStyle("myStyleId").spacing({ before: 50, after: 150 });
const style = new ParagraphStyle({ id: "myStyleId", paragraph: { spacing: { before: 50, after: 150 } } });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -88,7 +98,12 @@ describe("ParagraphStyle", () => {
});
it("#center", () => {
const style = new ParagraphStyle("myStyleId").center();
const style = new ParagraphStyle({
id: "myStyleId",
paragraph: {
alignment: AlignmentType.CENTER,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -101,7 +116,12 @@ describe("ParagraphStyle", () => {
});
it("#character spacing", () => {
const style = new ParagraphStyle("myStyleId").characterSpacing(24);
const style = new ParagraphStyle({
id: "myStyleId",
run: {
characterSpacing: 24,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -114,7 +134,12 @@ describe("ParagraphStyle", () => {
});
it("#left", () => {
const style = new ParagraphStyle("myStyleId").left();
const style = new ParagraphStyle({
id: "myStyleId",
paragraph: {
alignment: AlignmentType.LEFT,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -127,7 +152,12 @@ describe("ParagraphStyle", () => {
});
it("#right", () => {
const style = new ParagraphStyle("myStyleId").right();
const style = new ParagraphStyle({
id: "myStyleId",
paragraph: {
alignment: AlignmentType.RIGHT,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -140,7 +170,12 @@ describe("ParagraphStyle", () => {
});
it("#justified", () => {
const style = new ParagraphStyle("myStyleId").justified();
const style = new ParagraphStyle({
id: "myStyleId",
paragraph: {
alignment: AlignmentType.JUSTIFIED,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -153,7 +188,12 @@ describe("ParagraphStyle", () => {
});
it("#thematicBreak", () => {
const style = new ParagraphStyle("myStyleId").thematicBreak();
const style = new ParagraphStyle({
id: "myStyleId",
paragraph: {
thematicBreak: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -181,7 +221,12 @@ describe("ParagraphStyle", () => {
});
it("#leftTabStop", () => {
const style = new ParagraphStyle("myStyleId").leftTabStop(1200);
const style = new ParagraphStyle({
id: "myStyleId",
paragraph: {
leftTabStop: 1200,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -198,7 +243,12 @@ describe("ParagraphStyle", () => {
});
it("#maxRightTabStop", () => {
const style = new ParagraphStyle("myStyleId").maxRightTabStop();
const style = new ParagraphStyle({
id: "myStyleId",
paragraph: {
rightTabStop: TabStopPosition.MAX,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -215,7 +265,12 @@ describe("ParagraphStyle", () => {
});
it("#keepLines", () => {
const style = new ParagraphStyle("myStyleId").keepLines();
const style = new ParagraphStyle({
id: "myStyleId",
paragraph: {
keepLines: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:pPr": [{ "w:keepLines": EMPTY_OBJECT }] }],
@ -223,7 +278,12 @@ describe("ParagraphStyle", () => {
});
it("#keepNext", () => {
const style = new ParagraphStyle("myStyleId").keepNext();
const style = new ParagraphStyle({
id: "myStyleId",
paragraph: {
keepNext: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:pPr": [{ "w:keepNext": EMPTY_OBJECT }] }],
@ -231,7 +291,12 @@ describe("ParagraphStyle", () => {
});
it("#outlineLevel", () => {
const style = new ParagraphStyle("myStyleId").outlineLevel(1);
const style = new ParagraphStyle({
id: "myStyleId",
paragraph: {
outlineLevel: 1,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -244,7 +309,12 @@ describe("ParagraphStyle", () => {
describe("formatting methods: run properties", () => {
it("#size", () => {
const style = new ParagraphStyle("myStyleId").size(24);
const style = new ParagraphStyle({
id: "myStyleId",
run: {
size: 24,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -257,7 +327,12 @@ describe("ParagraphStyle", () => {
});
it("#smallCaps", () => {
const style = new ParagraphStyle("myStyleId").smallCaps();
const style = new ParagraphStyle({
id: "myStyleId",
run: {
smallCaps: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -270,7 +345,12 @@ describe("ParagraphStyle", () => {
});
it("#allCaps", () => {
const style = new ParagraphStyle("myStyleId").allCaps();
const style = new ParagraphStyle({
id: "myStyleId",
run: {
allCaps: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -283,7 +363,12 @@ describe("ParagraphStyle", () => {
});
it("#strike", () => {
const style = new ParagraphStyle("myStyleId").strike();
const style = new ParagraphStyle({
id: "myStyleId",
run: {
strike: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -296,7 +381,12 @@ describe("ParagraphStyle", () => {
});
it("#doubleStrike", () => {
const style = new ParagraphStyle("myStyleId").doubleStrike();
const style = new ParagraphStyle({
id: "myStyleId",
run: {
doubleStrike: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -309,7 +399,12 @@ describe("ParagraphStyle", () => {
});
it("#subScript", () => {
const style = new ParagraphStyle("myStyleId").subScript();
const style = new ParagraphStyle({
id: "myStyleId",
run: {
subScript: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -322,7 +417,12 @@ describe("ParagraphStyle", () => {
});
it("#superScript", () => {
const style = new ParagraphStyle("myStyleId").superScript();
const style = new ParagraphStyle({
id: "myStyleId",
run: {
superScript: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -335,7 +435,12 @@ describe("ParagraphStyle", () => {
});
it("#font", () => {
const style = new ParagraphStyle("myStyleId").font("Times");
const style = new ParagraphStyle({
id: "myStyleId",
run: {
font: "Times",
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -350,7 +455,12 @@ describe("ParagraphStyle", () => {
});
it("#bold", () => {
const style = new ParagraphStyle("myStyleId").bold();
const style = new ParagraphStyle({
id: "myStyleId",
run: {
bold: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -363,7 +473,12 @@ describe("ParagraphStyle", () => {
});
it("#italics", () => {
const style = new ParagraphStyle("myStyleId").italics();
const style = new ParagraphStyle({
id: "myStyleId",
run: {
italics: true,
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -376,7 +491,12 @@ describe("ParagraphStyle", () => {
});
it("#highlight", () => {
const style = new ParagraphStyle("myStyleId").highlight("005599");
const style = new ParagraphStyle({
id: "myStyleId",
run: {
highlight: "005599",
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -389,7 +509,16 @@ describe("ParagraphStyle", () => {
});
it("#shadow", () => {
const style = new ParagraphStyle("myStyleId").shadow("pct10", "00FFFF", "FF0000");
const style = new ParagraphStyle({
id: "myStyleId",
run: {
shadow: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -403,7 +532,12 @@ describe("ParagraphStyle", () => {
describe("#underline", () => {
it("should set underline to 'single' if no arguments are given", () => {
const style = new ParagraphStyle("myStyleId").underline();
const style = new ParagraphStyle({
id: "myStyleId",
run: {
underline: {},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -416,7 +550,14 @@ describe("ParagraphStyle", () => {
});
it("should set the style if given", () => {
const style = new ParagraphStyle("myStyleId").underline("double");
const style = new ParagraphStyle({
id: "myStyleId",
run: {
underline: {
type: UnderlineType.DOUBLE,
},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -429,7 +570,15 @@ describe("ParagraphStyle", () => {
});
it("should set the style and color if given", () => {
const style = new ParagraphStyle("myStyleId").underline("double", "005599");
const style = new ParagraphStyle({
id: "myStyleId",
run: {
underline: {
type: UnderlineType.DOUBLE,
color: "005599",
},
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -443,7 +592,12 @@ describe("ParagraphStyle", () => {
});
it("#color", () => {
const style = new ParagraphStyle("myStyleId").color("123456");
const style = new ParagraphStyle({
id: "myStyleId",
run: {
color: "123456",
},
});
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -456,7 +610,7 @@ describe("ParagraphStyle", () => {
});
it("#link", () => {
const style = new ParagraphStyle("myStyleId").link("MyLink");
const style = new ParagraphStyle({ id: "myStyleId", link: "MyLink" });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:link": { _attr: { "w:val": "MyLink" } } }],
@ -464,7 +618,7 @@ describe("ParagraphStyle", () => {
});
it("#semiHidden", () => {
const style = new ParagraphStyle("myStyleId").semiHidden();
const style = new ParagraphStyle({ id: "myStyleId", semiHidden: true });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:semiHidden": EMPTY_OBJECT }],
@ -472,7 +626,7 @@ describe("ParagraphStyle", () => {
});
it("#uiPriority", () => {
const style = new ParagraphStyle("myStyleId").uiPriority("99");
const style = new ParagraphStyle({ id: "myStyleId", uiPriority: 99 });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
@ -480,7 +634,7 @@ describe("ParagraphStyle", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -489,7 +643,7 @@ describe("ParagraphStyle", () => {
});
it("#unhideWhenUsed", () => {
const style = new ParagraphStyle("myStyleId").unhideWhenUsed();
const style = new ParagraphStyle({ id: "myStyleId", unhideWhenUsed: true });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:unhideWhenUsed": EMPTY_OBJECT }],

View File

@ -5,188 +5,207 @@ import {
ISpacingProperties,
KeepLines,
KeepNext,
LeftTabStop,
MaxRightTabStop,
OutlineLevel,
ParagraphProperties,
Spacing,
ThematicBreak,
} from "file/paragraph";
import { IIndentAttributesProperties, TabStop, TabStopType } from "file/paragraph/formatting";
import * as formatting from "file/paragraph/run/formatting";
import { RunProperties } from "file/paragraph/run/properties";
import { XmlComponent } from "file/xml-components";
import { UnderlineType } from "file/paragraph/run/underline";
import { ShadingType } from "file/table";
import { BasedOn, Link, Next, QuickFormat, SemiHidden, UiPriority, UnhideWhenUsed } from "./components";
import { Style } from "./style";
export interface IBaseParagraphStyleOptions {
readonly basedOn?: string;
readonly next?: string;
readonly quickFormat?: boolean;
readonly link?: string;
readonly semiHidden?: boolean;
readonly uiPriority?: number;
readonly unhideWhenUsed?: boolean;
readonly run?: {
readonly size?: number;
readonly bold?: boolean;
readonly italics?: boolean;
readonly smallCaps?: boolean;
readonly allCaps?: boolean;
readonly strike?: boolean;
readonly doubleStrike?: boolean;
readonly subScript?: boolean;
readonly superScript?: boolean;
readonly underline?: {
readonly type?: UnderlineType;
readonly color?: string;
};
readonly color?: string;
readonly font?: string;
readonly characterSpacing?: number;
readonly highlight?: string;
readonly shadow?: {
readonly type: ShadingType;
readonly fill: string;
readonly color: string;
};
};
readonly paragraph?: {
readonly alignment?: AlignmentType;
readonly thematicBreak?: boolean;
readonly rightTabStop?: number;
readonly leftTabStop?: number;
readonly indent?: IIndentAttributesProperties;
readonly spacing?: ISpacingProperties;
readonly keepNext?: boolean;
readonly keepLines?: boolean;
readonly outlineLevel?: number;
};
}
export interface IParagraphStyleOptions extends IBaseParagraphStyleOptions {
readonly id: string;
readonly name?: string;
}
export class ParagraphStyle extends Style {
private readonly paragraphProperties: ParagraphProperties;
private readonly runProperties: RunProperties;
constructor(styleId: string, name?: string) {
super({ type: "paragraph", styleId: styleId }, name);
constructor(options: IParagraphStyleOptions) {
super({ type: "paragraph", styleId: options.id }, options.name);
this.paragraphProperties = new ParagraphProperties({});
this.runProperties = new RunProperties();
this.root.push(this.paragraphProperties);
this.root.push(this.runProperties);
if (options.basedOn) {
this.root.push(new BasedOn(options.basedOn));
}
public addParagraphProperty(property: XmlComponent): ParagraphStyle {
this.paragraphProperties.push(property);
return this;
if (options.next) {
this.root.push(new Next(options.next));
}
public outlineLevel(level: number): ParagraphStyle {
this.paragraphProperties.push(new OutlineLevel(level));
return this;
}
public addRunProperty(property: XmlComponent): ParagraphStyle {
this.runProperties.push(property);
return this;
}
public basedOn(parentId: string): ParagraphStyle {
this.root.push(new BasedOn(parentId));
return this;
}
public quickFormat(): ParagraphStyle {
if (options.quickFormat) {
this.root.push(new QuickFormat());
return this;
}
public next(nextId: string): ParagraphStyle {
this.root.push(new Next(nextId));
return this;
if (options.link) {
this.root.push(new Link(options.link));
}
// ---------- Run formatting ---------------------- //
public size(twips: number): ParagraphStyle {
return this.addRunProperty(new formatting.Size(twips)).addRunProperty(new formatting.SizeComplexScript(twips));
}
public bold(): ParagraphStyle {
return this.addRunProperty(new formatting.Bold());
}
public italics(): ParagraphStyle {
return this.addRunProperty(new formatting.Italics());
}
public smallCaps(): ParagraphStyle {
return this.addRunProperty(new formatting.SmallCaps());
}
public allCaps(): ParagraphStyle {
return this.addRunProperty(new formatting.Caps());
}
public strike(): ParagraphStyle {
return this.addRunProperty(new formatting.Strike());
}
public doubleStrike(): ParagraphStyle {
return this.addRunProperty(new formatting.DoubleStrike());
}
public subScript(): ParagraphStyle {
return this.addRunProperty(new formatting.SubScript());
}
public superScript(): ParagraphStyle {
return this.addRunProperty(new formatting.SuperScript());
}
public underline(underlineType?: string, color?: string): ParagraphStyle {
return this.addRunProperty(new formatting.Underline(underlineType, color));
}
public color(color: string): ParagraphStyle {
return this.addRunProperty(new formatting.Color(color));
}
public font(fontName: string): ParagraphStyle {
return this.addRunProperty(new formatting.RunFonts(fontName));
}
public characterSpacing(value: number): ParagraphStyle {
return this.addRunProperty(new formatting.CharacterSpacing(value));
}
public highlight(color: string): ParagraphStyle {
return this.addRunProperty(new formatting.Highlight(color));
}
public shadow(value: string, fill: string, color: string): ParagraphStyle {
return this.addRunProperty(new formatting.Shading(value, fill, color));
}
// --------------------- Paragraph formatting ------------------------ //
public center(): ParagraphStyle {
return this.addParagraphProperty(new Alignment(AlignmentType.CENTER));
}
public left(): ParagraphStyle {
return this.addParagraphProperty(new Alignment(AlignmentType.LEFT));
}
public right(): ParagraphStyle {
return this.addParagraphProperty(new Alignment(AlignmentType.RIGHT));
}
public justified(): ParagraphStyle {
return this.addParagraphProperty(new Alignment(AlignmentType.BOTH));
}
public thematicBreak(): ParagraphStyle {
return this.addParagraphProperty(new ThematicBreak());
}
public maxRightTabStop(): ParagraphStyle {
return this.addParagraphProperty(new MaxRightTabStop());
}
public leftTabStop(position: number): ParagraphStyle {
return this.addParagraphProperty(new LeftTabStop(position));
}
public indent(attrs: object): ParagraphStyle {
return this.addParagraphProperty(new Indent(attrs));
}
public spacing(params: ISpacingProperties): ParagraphStyle {
return this.addParagraphProperty(new Spacing(params));
}
public keepNext(): ParagraphStyle {
return this.addParagraphProperty(new KeepNext());
}
public keepLines(): ParagraphStyle {
return this.addParagraphProperty(new KeepLines());
}
/*-------------- Style Properties -----------------*/
public link(link: string): ParagraphStyle {
this.root.push(new Link(link));
return this;
}
public semiHidden(): ParagraphStyle {
if (options.semiHidden) {
this.root.push(new SemiHidden());
return this;
}
public uiPriority(priority: string): ParagraphStyle {
this.root.push(new UiPriority(priority));
return this;
if (options.uiPriority) {
this.root.push(new UiPriority(options.uiPriority));
}
public unhideWhenUsed(): ParagraphStyle {
if (options.unhideWhenUsed) {
this.root.push(new UnhideWhenUsed());
return this;
}
if (options.run) {
if (options.run.size) {
this.runProperties.push(new formatting.Size(options.run.size));
this.runProperties.push(new formatting.SizeComplexScript(options.run.size));
}
if (options.run.bold) {
this.runProperties.push(new formatting.Bold());
}
if (options.run.italics) {
this.runProperties.push(new formatting.Italics());
}
if (options.run.smallCaps) {
this.runProperties.push(new formatting.SmallCaps());
}
if (options.run.allCaps) {
this.runProperties.push(new formatting.Caps());
}
if (options.run.strike) {
this.runProperties.push(new formatting.Strike());
}
if (options.run.doubleStrike) {
this.runProperties.push(new formatting.DoubleStrike());
}
if (options.run.subScript) {
this.runProperties.push(new formatting.SubScript());
}
if (options.run.superScript) {
this.runProperties.push(new formatting.SuperScript());
}
if (options.run.underline) {
this.runProperties.push(new formatting.Underline(options.run.underline.type, options.run.underline.color));
}
if (options.run.color) {
this.runProperties.push(new formatting.Color(options.run.color));
}
if (options.run.font) {
this.runProperties.push(new formatting.RunFonts(options.run.font));
}
if (options.run.characterSpacing) {
this.runProperties.push(new formatting.CharacterSpacing(options.run.characterSpacing));
}
if (options.run.highlight) {
this.runProperties.push(new formatting.Highlight(options.run.highlight));
}
if (options.run.shadow) {
this.runProperties.push(new formatting.Shading(options.run.shadow.type, options.run.shadow.fill, options.run.shadow.color));
}
}
if (options.paragraph) {
if (options.paragraph.alignment) {
this.paragraphProperties.push(new Alignment(options.paragraph.alignment));
}
if (options.paragraph.thematicBreak) {
this.paragraphProperties.push(new ThematicBreak());
}
if (options.paragraph.rightTabStop) {
this.paragraphProperties.push(new TabStop(TabStopType.RIGHT, options.paragraph.rightTabStop));
}
if (options.paragraph.leftTabStop) {
this.paragraphProperties.push(new TabStop(TabStopType.LEFT, options.paragraph.leftTabStop));
}
if (options.paragraph.indent) {
this.paragraphProperties.push(new Indent(options.paragraph.indent));
}
if (options.paragraph.spacing) {
this.paragraphProperties.push(new Spacing(options.paragraph.spacing));
}
if (options.paragraph.keepNext) {
this.paragraphProperties.push(new KeepNext());
}
if (options.paragraph.keepLines) {
this.paragraphProperties.push(new KeepLines());
}
if (options.paragraph.outlineLevel) {
this.paragraphProperties.push(new OutlineLevel(options.paragraph.outlineLevel));
}
}
}
}

View File

@ -25,8 +25,4 @@ export class Style extends XmlComponent {
this.root.push(new Name(name));
}
}
public push(styleSegment: XmlComponent): void {
this.root.push(styleSegment);
}
}

View File

@ -1,31 +1,20 @@
import { assert, expect } from "chai";
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { CharacterStyle, ParagraphStyle } from "./style";
import { EMPTY_OBJECT } from "file/xml-components";
import { Styles } from "./styles";
import { EMPTY_OBJECT } from "file/xml-components";
describe("Styles", () => {
let styles: Styles;
beforeEach(() => {
styles = new Styles();
});
describe("#constructor()", () => {
it("should create styles with correct rootKey", () => {
const newJson = JSON.parse(JSON.stringify(styles));
assert.equal(newJson.rootKey, "w:styles");
});
});
describe("#createParagraphStyle", () => {
it("should create a new paragraph style and push it onto this collection", () => {
const pStyle = styles.createParagraphStyle("pStyleId");
expect(pStyle).to.instanceOf(ParagraphStyle);
const styles = new Styles({
paragraphStyles: [
{
id: "pStyleId",
},
],
});
const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr);
expect(tree).to.deep.equal([
{
@ -35,8 +24,14 @@ describe("Styles", () => {
});
it("should set the paragraph name if given", () => {
const pStyle = styles.createParagraphStyle("pStyleId", "Paragraph Style");
expect(pStyle).to.instanceOf(ParagraphStyle);
const styles = new Styles({
paragraphStyles: [
{
id: "pStyleId",
name: "Paragraph Style",
},
],
});
const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr);
expect(tree).to.deep.equal([
{
@ -51,8 +46,13 @@ describe("Styles", () => {
describe("#createCharacterStyle", () => {
it("should create a new character style and push it onto this collection", () => {
const cStyle = styles.createCharacterStyle("pStyleId");
expect(cStyle).to.instanceOf(CharacterStyle);
const styles = new Styles({
characterStyles: [
{
id: "pStyleId",
},
],
});
const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr);
expect(tree).to.deep.equal([
{
@ -61,7 +61,7 @@ describe("Styles", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},
@ -74,8 +74,14 @@ describe("Styles", () => {
});
it("should set the character name if given", () => {
const cStyle = styles.createCharacterStyle("pStyleId", "Character Style");
expect(cStyle).to.instanceOf(CharacterStyle);
const styles = new Styles({
characterStyles: [
{
id: "pStyleId",
name: "Character Style",
},
],
});
const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr);
expect(tree).to.deep.equal([
{
@ -85,7 +91,7 @@ describe("Styles", () => {
{
"w:uiPriority": {
_attr: {
"w:val": "99",
"w:val": 99,
},
},
},

View File

@ -1,36 +1,41 @@
import { BaseXmlComponent, XmlComponent } from "file/xml-components";
import { DocumentDefaults } from "./defaults";
import { BaseXmlComponent, ImportedXmlComponent, XmlComponent } from "file/xml-components";
import { CharacterStyle, ParagraphStyle } from "./style";
import { ICharacterStyleOptions } from "./style/character-style";
import { IParagraphStyleOptions } from "./style/paragraph-style";
export * from "./border";
export interface IStylesOptions {
readonly initialStyles?: BaseXmlComponent;
readonly paragraphStyles?: IParagraphStyleOptions[];
readonly characterStyles?: ICharacterStyleOptions[];
readonly importedStyles?: Array<XmlComponent | ParagraphStyle | CharacterStyle | ImportedXmlComponent>;
}
export class Styles extends XmlComponent {
constructor(initialStyles?: BaseXmlComponent) {
constructor(options: IStylesOptions) {
super("w:styles");
if (initialStyles) {
this.root.push(initialStyles);
}
if (options.initialStyles) {
this.root.push(options.initialStyles);
}
public push(style: XmlComponent): Styles {
if (options.importedStyles) {
for (const style of options.importedStyles) {
this.root.push(style);
return this;
}
}
public createDocumentDefaults(): DocumentDefaults {
const defaults = new DocumentDefaults();
this.push(defaults);
return defaults;
if (options.paragraphStyles) {
for (const style of options.paragraphStyles) {
this.root.push(new ParagraphStyle(style));
}
}
public createParagraphStyle(styleId: string, name?: string): ParagraphStyle {
const paragraphStyle = new ParagraphStyle(styleId, name);
this.push(paragraphStyle);
return paragraphStyle;
}
public createCharacterStyle(styleId: string, name?: string): CharacterStyle {
const characterStyle = new CharacterStyle(styleId, name);
this.push(characterStyle);
return characterStyle;
if (options.characterStyles) {
for (const style of options.characterStyles) {
this.root.push(new CharacterStyle(style));
}
}
}
}

View File

@ -52,7 +52,7 @@ export class FieldInstruction extends XmlComponent {
instruction = `${instruction} \\s "${this.properties.seqFieldIdentifierForPrefix}"`;
}
if (this.properties.stylesWithLevels && this.properties.stylesWithLevels.length) {
const styles = this.properties.stylesWithLevels.map((sl) => `${sl.styleName};${sl.level}`).join(";");
const styles = this.properties.stylesWithLevels.map((sl) => `${sl.styleName},${sl.level}`).join(",");
instruction = `${instruction} \\t "${styles}"`;
}
if (this.properties.useAppliedParagraphOutlineLevel) {

View File

@ -146,7 +146,7 @@ const COMPLETE_TOC = {
"xml:space": "preserve",
},
},
'TOC \\a "A" \\b "B" \\c "C" \\d "D" \\f "F" \\h \\l "L" \\n "N" \\o "O" \\p "P" \\s "S" \\t "SL;1;SL;2" \\u \\w \\x \\z',
'TOC \\a "A" \\b "B" \\c "C" \\d "D" \\f "F" \\h \\l "L" \\n "N" \\o "O" \\p "P" \\s "S" \\t "SL,1,SL,2" \\u \\w \\x \\z',
],
},
{

View File

@ -16,18 +16,24 @@ export class TableOfContents extends XmlComponent {
const content = new StructuredDocumentTagContent();
const beginParagraph = new Paragraph({});
const beginRun = new Run({});
beginRun.addChildElement(new Begin(true));
beginRun.addChildElement(new FieldInstruction(properties));
beginRun.addChildElement(new Separate());
beginParagraph.addRun(beginRun);
const beginParagraph = new Paragraph({
children: [
new Run({
children: [new Begin(true), new FieldInstruction(properties), new Separate()],
}),
],
});
content.addChildElement(beginParagraph);
const endParagraph = new Paragraph({});
const endRun = new Run({});
endRun.addChildElement(new End());
endParagraph.addRun(endRun);
const endParagraph = new Paragraph({
children: [
new Run({
children: [new End()],
}),
],
});
content.addChildElement(endParagraph);
this.root.push(content);

View File

@ -2,9 +2,11 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export class TableGrid extends XmlComponent {
constructor(cols: number[]) {
constructor(widths: number[]) {
super("w:tblGrid");
cols.forEach((col) => this.root.push(new GridCol(col)));
for (const width of widths) {
this.root.push(new GridCol(width));
}
}
}

View File

@ -103,7 +103,7 @@ export class GridSpan extends XmlComponent {
/**
* Vertical merge types.
*/
export enum VMergeType {
export enum VerticalMergeType {
/**
* Cell that is merged with upper one.
*/
@ -114,19 +114,19 @@ export enum VMergeType {
RESTART = "restart",
}
class VMergeAttributes extends XmlAttributeComponent<{ readonly val: VMergeType }> {
class VerticalMergeAttributes extends XmlAttributeComponent<{ readonly val: VerticalMergeType }> {
protected readonly xmlKeys = { val: "w:val" };
}
/**
* Vertical merge element. Should be used in a table cell.
*/
export class VMerge extends XmlComponent {
constructor(value: VMergeType) {
export class VerticalMerge extends XmlComponent {
constructor(value: VerticalMergeType) {
super("w:vMerge");
this.root.push(
new VMergeAttributes({
new VerticalMergeAttributes({
val: value,
}),
);

View File

@ -3,7 +3,7 @@ import { expect } from "chai";
import { Formatter } from "export/formatter";
import { BorderStyle } from "file/styles";
import { VerticalAlign, VMergeType, WidthType } from "./table-cell-components";
import { VerticalAlign, VerticalMergeType, WidthType } from "./table-cell-components";
import { TableCellProperties } from "./table-cell-properties";
describe("TableCellProperties", () => {
@ -30,7 +30,7 @@ describe("TableCellProperties", () => {
describe("#addVerticalMerge", () => {
it("adds vertical merge", () => {
const properties = new TableCellProperties();
properties.addVerticalMerge(VMergeType.CONTINUE);
properties.addVerticalMerge(VerticalMergeType.CONTINUE);
const tree = new Formatter().format(properties);
expect(tree).to.deep.equal({ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] });
});
@ -73,6 +73,54 @@ describe("TableCellProperties", () => {
});
});
describe("#addMargins", () => {
it("sets shading", () => {
const properties = new TableCellProperties();
properties.addMargins({});
const tree = new Formatter().format(properties);
expect(tree).to.deep.equal({
"w:tcPr": [
{
"w:tcMar": [
{
"w:top": {
_attr: {
"w:type": "dxa",
"w:w": 0,
},
},
},
{
"w:bottom": {
_attr: {
"w:type": "dxa",
"w:w": 0,
},
},
},
{
"w:end": {
_attr: {
"w:type": "dxa",
"w:w": 0,
},
},
},
{
"w:start": {
_attr: {
"w:type": "dxa",
"w:w": 0,
},
},
},
],
},
],
});
});
});
describe("#Borders", () => {
it("should return the TableCellBorders if Border has borders", () => {
const properties = new TableCellProperties();

View File

@ -2,7 +2,16 @@ import { IgnoreIfEmptyXmlComponent } from "file/xml-components";
import { ITableShadingAttributesProperties, TableShading } from "../shading";
import { ITableCellMarginOptions, TableCellMargin } from "./cell-margin/table-cell-margins";
import { GridSpan, TableCellBorders, TableCellWidth, VAlign, VerticalAlign, VMerge, VMergeType, WidthType } from "./table-cell-components";
import {
GridSpan,
TableCellBorders,
TableCellWidth,
VAlign,
VerticalAlign,
VerticalMerge,
VerticalMergeType,
WidthType,
} from "./table-cell-components";
export class TableCellProperties extends IgnoreIfEmptyXmlComponent {
private readonly cellBorder: TableCellBorders;
@ -23,8 +32,8 @@ export class TableCellProperties extends IgnoreIfEmptyXmlComponent {
return this;
}
public addVerticalMerge(type: VMergeType): TableCellProperties {
this.root.push(new VMerge(type));
public addVerticalMerge(type: VerticalMergeType): TableCellProperties {
this.root.push(new VerticalMerge(type));
return this;
}

View File

@ -3,7 +3,9 @@ import { expect } from "chai";
import { Formatter } from "export/formatter";
import { BorderStyle } from "file/styles";
import { TableCellBorders, TableCellWidth, WidthType } from "./table-cell-components";
import { ShadingType } from "../shading";
import { TableCell } from "./table-cell";
import { TableCellBorders, TableCellWidth, VerticalAlign, VerticalMergeType, WidthType } from "./table-cell-components";
describe("TableCellBorders", () => {
describe("#prepForXml", () => {
@ -222,3 +224,332 @@ describe("TableCellWidth", () => {
});
});
});
describe("TableCell", () => {
describe("#constructor", () => {
it("should create", () => {
const cell = new TableCell({
children: [],
});
const tree = new Formatter().format(cell);
expect(tree).to.deep.equal({
"w:tc": [
{
"w:p": {},
},
],
});
});
it("should create with vertical align", () => {
const cell = new TableCell({
children: [],
verticalAlign: VerticalAlign.CENTER,
});
const tree = new Formatter().format(cell);
expect(tree).to.deep.equal({
"w:tc": [
{
"w:tcPr": [
{
"w:vAlign": {
_attr: {
"w:val": "center",
},
},
},
],
},
{
"w:p": {},
},
],
});
});
it("should create with vertical merge", () => {
const cell = new TableCell({
children: [],
verticalMerge: VerticalMergeType.RESTART,
});
const tree = new Formatter().format(cell);
expect(tree).to.deep.equal({
"w:tc": [
{
"w:tcPr": [
{
"w:vMerge": {
_attr: {
"w:val": "restart",
},
},
},
],
},
{
"w:p": {},
},
],
});
});
it("should create with margins", () => {
const cell = new TableCell({
children: [],
margins: {
top: 1,
left: 1,
bottom: 1,
right: 1,
},
});
const tree = new Formatter().format(cell);
expect(tree).to.deep.equal({
"w:tc": [
{
"w:tcPr": [
{
"w:tcMar": [
{
"w:top": {
_attr: {
"w:type": "dxa",
"w:w": 1,
},
},
},
{
"w:bottom": {
_attr: {
"w:type": "dxa",
"w:w": 1,
},
},
},
{
"w:end": {
_attr: {
"w:type": "dxa",
"w:w": 1,
},
},
},
{
"w:start": {
_attr: {
"w:type": "dxa",
"w:w": 1,
},
},
},
],
},
],
},
{
"w:p": {},
},
],
});
});
it("should create with shading", () => {
const cell = new TableCell({
children: [],
shading: {
fill: "red",
color: "blue",
val: ShadingType.PERCENT_10,
},
});
const tree = new Formatter().format(cell);
expect(tree).to.deep.equal({
"w:tc": [
{
"w:tcPr": [
{
"w:shd": {
_attr: {
"w:color": "blue",
"w:fill": "red",
"w:val": "pct10",
},
},
},
],
},
{
"w:p": {},
},
],
});
});
it("should create with column span", () => {
const cell = new TableCell({
children: [],
columnSpan: 2,
});
const tree = new Formatter().format(cell);
expect(tree).to.deep.equal({
"w:tc": [
{
"w:tcPr": [
{
"w:gridSpan": {
_attr: {
"w:val": 2,
},
},
},
],
},
{
"w:p": {},
},
],
});
});
describe("rowSpan", () => {
it("should not create with row span if its less than 1", () => {
const cell = new TableCell({
children: [],
rowSpan: 0,
});
const tree = new Formatter().format(cell);
expect(tree).to.deep.equal({
"w:tc": [
{
"w:p": {},
},
],
});
});
it("should create with row span if its greater than 1", () => {
const cell = new TableCell({
children: [],
rowSpan: 2,
});
const tree = new Formatter().format(cell);
expect(tree).to.deep.equal({
"w:tc": [
{
"w:tcPr": [
{
"w:vMerge": {
_attr: {
"w:val": "restart",
},
},
},
],
},
{
"w:p": {},
},
],
});
});
it("should create with borders", () => {
const cell = new TableCell({
children: [],
borders: {
top: {
style: BorderStyle.DASH_DOT_STROKED,
size: 3,
color: "red",
},
bottom: {
style: BorderStyle.DOUBLE,
size: 3,
color: "blue",
},
left: {
style: BorderStyle.DASH_DOT_STROKED,
size: 3,
color: "green",
},
right: {
style: BorderStyle.DASH_DOT_STROKED,
size: 3,
color: "#ff8000",
},
},
});
const tree = new Formatter().format(cell);
expect(tree).to.deep.equal({
"w:tc": [
{
"w:tcPr": [
{
"w:tcBorders": [
{
"w:top": {
_attr: {
"w:color": "red",
"w:sz": 3,
"w:val": "dashDotStroked",
},
},
},
{
"w:bottom": {
_attr: {
"w:color": "blue",
"w:sz": 3,
"w:val": "double",
},
},
},
{
"w:left": {
_attr: {
"w:color": "green",
"w:sz": 3,
"w:val": "dashDotStroked",
},
},
},
{
"w:right": {
_attr: {
"w:color": "#ff8000",
"w:sz": 3,
"w:val": "dashDotStroked",
},
},
},
],
},
],
},
{
"w:p": {},
},
],
});
});
});
});
});

Some files were not shown because too many files have changed in this diff Show More