Compare commits

..

25 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
56 changed files with 2068 additions and 995 deletions

6
.nycrc
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,48 +1,89 @@
// Example on how to customise the look at feel using Styles // Example on how to customise the look at feel using Styles
// Import from 'docx' rather than '../build' if you install from npm // Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { Document, HeadingLevel, Packer, Paragraph, TextRun } from "../build"; import { Document, HeadingLevel, Packer, Paragraph, TextRun, UnderlineType } from "../build";
const doc = new Document({ const doc = new Document({
creator: "Clippy", creator: "Clippy",
title: "Sample Document", title: "Sample Document",
description: "A brief example of using docx", description: "A brief example of using docx",
styles: {
paragraphStyles: [
{
id: "Heading1",
name: "Heading 1",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
size: 28,
bold: true,
italics: true,
},
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(); const numberedAbstract = doc.Numbering.createAbstractNumbering();
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "left"); numberedAbstract.createLevel(0, "lowerLetter", "%1)", "left");
@ -82,14 +123,16 @@ doc.addSection({
level: 0, level: 0,
}, },
}), }),
new Paragraph({}).addRun( new Paragraph({
new TextRun({ children: [
text: "Some monospaced content", new TextRun({
font: { text: "Some monospaced content",
name: "Monospace", font: {
}, name: "Monospace",
}), },
), }),
],
}),
new Paragraph({ new Paragraph({
text: "An aside, in light gray italics and indented", text: "An aside, in light gray italics and indented",
style: "aside", style: "aside",

View File

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

View File

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

View File

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

View File

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

View File

@ -4,12 +4,14 @@ import * as fs from "fs";
import { Document, Packer, Paragraph } from "../build"; import { Document, Packer, Paragraph } from "../build";
const doc = new Document(); const doc = new Document();
const paragraph = new Paragraph({});
const link = doc.createHyperlink("http://www.example.com", "Hyperlink"); const link = doc.createHyperlink("http://www.example.com", "Hyperlink");
paragraph.addHyperLink(link);
doc.addSection({ doc.addSection({
children: [paragraph], children: [
new Paragraph({
children: [link],
}),
],
}); });
Packer.toBuffer(doc).then((buffer) => { Packer.toBuffer(doc).then((buffer) => {

View File

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

View File

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

View File

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

53
docs/usage/symbols.md Normal file
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. > 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") ![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 ## Example
```ts ```ts
const paragraph = new docx.Paragraph().maxRightTabStop(); const paragraph = new Paragraph({
const leftText = new docx.TextRun("Hey everyone").bold(); children: [new TextRun("Hey everyone").bold(), new TextRun("11th November 1999").tab()],
const rightText = new docx.TextRun("11th November 2015").tab(); tabStops: [
paragraph.addRun(leftText); {
paragraph.addRun(rightText); type: TabStopType.RIGHT,
position: TabStopPosition.MAX,
},
],
});
``` ```
The example above will create a left aligned text, and a right aligned text on the same line. The laymans approach to this problem would be to either use text boxes or tables. YUK!
The example above will create a left aligned text, and a right aligned text on the same line. The laymans approach to this problem would be to either use text boxes or tables. Not ideal!
```ts ```ts
const paragraph = new docx.Paragraph(); const paragraph = new Paragraph({
paragraph.maxRightTabStop(); children: [new TextRun("Second tab stop here I come!").tab().tab()],
paragraph.leftTabStop(1000); tabStops: [
const text = new docx.TextRun("Second tab stop here I come!").tab().tab(); {
paragraph.addRun(text); type: TabStopType.RIGHT,
position: TabStopPosition.MAX,
},
{
type: TabStopType.LEFT,
position: 1000,
},
],
});
``` ```
The above shows the use of two tab stops, and how to select/use it. The above shows the use of two tab stops, and how to select/use it.
## Left Tab Stop You can add multiple tab stops of the same `type` too.
```ts ```ts
paragraph.leftTabStop(2268); const paragraph = new Paragraph({
children: [new TextRun("Multiple tab stops!").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. 2268 is the distance from the left side.
## Center Tab Stop ## Center Tab Stop
```ts ```ts
paragraph.centerTabStop(2268); const paragraph = new Paragraph({
tabStops: [
{
type: TabStopType.CENTER,
position: 2268,
},
],
});
``` ```
2268 is the distance from the left side.
2268 is the distance from the center.
## Right Tab Stop ## Right Tab Stop
```ts ```ts
paragraph.rightTabStop(2268); const paragraph = new Paragraph({
tabStops: [
{
type: TabStopType.RIGHT,
position: 2268,
},
],
});
``` ```
2268 is the distance from the left side.
2268 is the distance fro0oum the left side.
## Max Right Tab Stop ## Max Right Tab Stop
```ts ```ts
paragraph.maxRightTabStop(); const paragraph = new Paragraph({
tabStops: [
{
type: TabStopType.RIGHT,
position: TabStopPosition.MAX,
},
],
});
``` ```
This will create a tab stop on the very edge of the right hand side. Handy for right aligning and left aligning text on the same line. This will create a tab stop on the very edge of the right hand side. Handy for right aligning and left aligning text on the same line.

View File

@ -47,30 +47,6 @@ Here is the list of all options that you can use to generate your tables of cont
## Examples ## Examples
```ts
// Let's define the options for generate a TOC for heading 1-5 and MySpectacularStyle,
// making the entries be hyperlinks for the paragraph
const toc = new TableOfContents("Summary", {
hyperlink: true,
headingStyleRange: "1-5",
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)],
});
doc.addTableOfContents(toc);
doc.add(new Paragraph("Header #1").heading1().pageBreakBefore());
doc.add(new Paragraph("I'm a little text, very nicely written.'"));
doc.add(new Paragraph("Header #2").heading1().pageBreakBefore());
doc.add(new Paragraph("I'm another text very nicely written.'"));
doc.add(new Paragraph("Header #2.1").heading2());
doc.add(new Paragraph("I'm another text very nicely written.'"));
doc.add(new Paragraph("My Spectacular Style #1").style("MySpectacularStyle").pageBreakBefore());
```
### Complete example
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/28-table-of-contents.ts ':include') [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/28-table-of-contents.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/28-table-of-contents.ts_ _Source: https://github.com/dolanmiu/docx/blob/master/demo/28-table-of-contents.ts_

View File

@ -328,7 +328,7 @@ const cell = new TableCell({
## Examples ## Examples
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/4-basic-table.ts ":include") [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/4-basic-table.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/4-basic-table.ts_ _Source: https://github.com/dolanmiu/docx/blob/master/demo/4-basic-table.ts_
@ -336,7 +336,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/4-basic-table.ts_
Example showing how to add colourful borders to tables Example showing how to add colourful borders to tables
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/20-table-cell-borders.ts ":include") [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/20-table-cell-borders.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/20-table-cell-borders.ts_ _Source: https://github.com/dolanmiu/docx/blob/master/demo/20-table-cell-borders.ts_
@ -344,11 +344,11 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/20-table-cell-borders
Example showing how to add images to tables Example showing how to add images to tables
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/24-images-to-table-cell.ts ":include") [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/24-images-to-table-cell.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/24-images-to-table-cell.ts_ _Source: https://github.com/dolanmiu/docx/blob/master/demo/24-images-to-table-cell.ts_
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/36-image-to-table-cell.ts ":include") [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/36-image-to-table-cell.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/36-image-to-table-cell.ts_ _Source: https://github.com/dolanmiu/docx/blob/master/demo/36-image-to-table-cell.ts_
@ -356,7 +356,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/36-image-to-table-cel
Example showing how align text in a table cell Example showing how align text in a table cell
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/31-tables.ts ":include") [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/31-tables.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/31-tables.ts_ _Source: https://github.com/dolanmiu/docx/blob/master/demo/31-tables.ts_
@ -364,11 +364,11 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/31-tables.ts_
Example showing merging of columns and rows and shading Example showing merging of columns and rows and shading
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/32-merge-table-cells.ts ":include") [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/32-merge-and-shade-table-cells.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/32-merge-table-cells.ts_ _Source: https://github.com/dolanmiu/docx/blob/master/demo/32-merge-and-shade-table-cells.ts_
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/41-merge-table-cells-2.ts ":include") [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/41-merge-table-cells-2.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/41-merge-table-cells-2.ts_ _Source: https://github.com/dolanmiu/docx/blob/master/demo/41-merge-table-cells-2.ts_
@ -376,12 +376,12 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/41-merge-table-cells-
Example showing merging of columns and rows Example showing merging of columns and rows
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/43-images-to-table-cell-2.ts ":include") [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/43-images-to-table-cell-2.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/43-images-to-table-cell-2.ts_ _Source: https://github.com/dolanmiu/docx/blob/master/demo/43-images-to-table-cell-2.ts_
### Floating tables ### Floating tables
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/34-floating-tables.ts ":include") [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/34-floating-tables.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/34-floating-tables.ts_ _Source: https://github.com/dolanmiu/docx/blob/master/demo/34-floating-tables.ts_

View File

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

View File

@ -28,15 +28,52 @@ describe("Formatter", () => {
}); });
it("should format simple paragraph with bold text", () => { it("should format simple paragraph with bold text", () => {
const paragraph = new Paragraph(""); const paragraph = new Paragraph({
paragraph.addRun( children: [
new TextRun({ new TextRun({
text: "test", text: "test",
bold: true, bold: true,
}), }),
); ],
const newJson = formatter.format(paragraph); });
assert.isDefined(newJson["w:p"][1]["w:r"][0]["w:rPr"][0]["w:b"]._attr["w:val"]);
const tree = formatter.format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:r": [
{
"w:rPr": [
{
"w:b": {
_attr: {
"w:val": true,
},
},
},
{
"w:bCs": {
_attr: {
"w:val": true,
},
},
},
],
},
{
"w:t": [
{
_attr: {
"xml:space": "preserve",
},
},
"test",
],
},
],
},
],
});
}); });
it("should format attributes (rsidSect)", () => { it("should format attributes (rsidSect)", () => {

View File

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

View File

@ -22,6 +22,9 @@ describe("Body", () => {
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:body": [ "w:body": [
{
"w:p": {},
},
{ {
"w:sectPr": [ "w:sectPr": [
{ "w:pgSz": { _attr: { "w:w": 10000, "w:h": 10000, "w:orient": "portrait" } } }, { "w:pgSz": { _attr: { "w:w": 10000, "w:h": 10000, "w:orient": "portrait" } } },

View File

@ -1,5 +1,5 @@
import { IXmlableObject, XmlComponent } from "file/xml-components"; import { IXmlableObject, XmlComponent } from "file/xml-components";
import { TableOfContents } from "../.."; import { Paragraph, ParagraphProperties, TableOfContents } from "../..";
import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties"; import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties";
export class Body extends XmlComponent { export class Body extends XmlComponent {
@ -18,6 +18,9 @@ export class Body extends XmlComponent {
* @param options new section options * @param options new section options
*/ */
public addSection(options: SectionPropertiesOptions): void { public addSection(options: SectionPropertiesOptions): void {
const currentSection = this.sections.pop() as SectionProperties;
this.root.push(this.createSectionParagraph(currentSection));
this.sections.push(new SectionProperties(options)); this.sections.push(new SectionProperties(options));
} }
@ -36,4 +39,12 @@ export class Body extends XmlComponent {
public getTablesOfContents(): TableOfContents[] { public getTablesOfContents(): TableOfContents[] {
return this.root.filter((child) => child instanceof TableOfContents) as TableOfContents[]; return this.root.filter((child) => child instanceof TableOfContents) as TableOfContents[];
} }
private createSectionParagraph(section: SectionProperties): Paragraph {
const paragraph = new Paragraph({});
const properties = new ParagraphProperties({});
properties.addChildElement(section);
paragraph.addChildElement(properties);
return paragraph;
}
} }

View File

@ -20,8 +20,8 @@ describe("File", () => {
const tree = new Formatter().format(doc.Document.Body); const tree = new Formatter().format(doc.Document.Body);
expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][0]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][1]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default");
}); });
it("should create with correct headers and footers", () => { it("should create with correct headers and footers", () => {
@ -39,8 +39,8 @@ describe("File", () => {
const tree = new Formatter().format(doc.Document.Body); const tree = new Formatter().format(doc.Document.Body);
expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][0]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][1]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default");
}); });
it("should create with first headers and footers", () => { it("should create with first headers and footers", () => {
@ -58,8 +58,8 @@ describe("File", () => {
const tree = new Formatter().format(doc.Document.Body); const tree = new Formatter().format(doc.Document.Body);
expect(tree["w:body"][0]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first"); expect(tree["w:body"][1]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first");
expect(tree["w:body"][0]["w:sectPr"][7]["w:footerReference"]._attr["w:type"]).to.equal("first"); expect(tree["w:body"][1]["w:sectPr"][7]["w:footerReference"]._attr["w:type"]).to.equal("first");
}); });
it("should create with correct headers", () => { it("should create with correct headers", () => {
@ -81,13 +81,13 @@ describe("File", () => {
const tree = new Formatter().format(doc.Document.Body); const tree = new Formatter().format(doc.Document.Body);
expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][0]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first"); expect(tree["w:body"][1]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first");
expect(tree["w:body"][0]["w:sectPr"][6]["w:headerReference"]._attr["w:type"]).to.equal("even"); expect(tree["w:body"][1]["w:sectPr"][6]["w:headerReference"]._attr["w:type"]).to.equal("even");
expect(tree["w:body"][0]["w:sectPr"][7]["w:footerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][1]["w:sectPr"][7]["w:footerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][0]["w:sectPr"][8]["w:footerReference"]._attr["w:type"]).to.equal("first"); expect(tree["w:body"][1]["w:sectPr"][8]["w:footerReference"]._attr["w:type"]).to.equal("first");
expect(tree["w:body"][0]["w:sectPr"][9]["w:footerReference"]._attr["w:type"]).to.equal("even"); expect(tree["w:body"][1]["w:sectPr"][9]["w:footerReference"]._attr["w:type"]).to.equal("even");
}); });
}); });

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,16 +11,11 @@ import { KeepLines, KeepNext } from "./formatting/keep";
import { PageBreak, PageBreakBefore } from "./formatting/page-break"; import { PageBreak, PageBreakBefore } from "./formatting/page-break";
import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spacing"; import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spacing";
import { HeadingLevel, Style } from "./formatting/style"; import { HeadingLevel, Style } from "./formatting/style";
import { CenterTabStop, LeaderType, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop"; import { LeaderType, TabStop, TabStopPosition, TabStopType } from "./formatting/tab-stop";
import { NumberProperties } from "./formatting/unordered-list"; import { NumberProperties } from "./formatting/unordered-list";
import { Bookmark, Hyperlink, OutlineLevel } from "./links"; import { Bookmark, Hyperlink, OutlineLevel } from "./links";
import { ParagraphProperties } from "./properties"; import { ParagraphProperties } from "./properties";
import { PictureRun, Run, SequentialIdentifier, TextRun } from "./run"; import { PictureRun, Run, SequentialIdentifier, SymbolRun, TextRun } from "./run";
interface ITabStopOptions {
readonly position: number;
readonly leader?: LeaderType;
}
export interface IParagraphOptions { export interface IParagraphOptions {
readonly text?: string; readonly text?: string;
@ -36,14 +31,11 @@ export interface IParagraphOptions {
readonly indent?: IIndentAttributesProperties; readonly indent?: IIndentAttributesProperties;
readonly keepLines?: boolean; readonly keepLines?: boolean;
readonly keepNext?: boolean; readonly keepNext?: boolean;
readonly tabStop?: { readonly tabStops?: Array<{
readonly left?: ITabStopOptions; readonly position: number | TabStopPosition;
readonly right?: ITabStopOptions; readonly type: TabStopType;
readonly maxRight?: { readonly leader?: LeaderType;
readonly leader?: LeaderType; }>;
};
readonly center?: ITabStopOptions;
};
readonly style?: string; readonly style?: string;
readonly bullet?: { readonly bullet?: {
readonly level: number; readonly level: number;
@ -53,7 +45,7 @@ export interface IParagraphOptions {
readonly level: number; readonly level: number;
readonly custom?: boolean; readonly custom?: boolean;
}; };
readonly children?: Array<TextRun | PictureRun | Hyperlink>; readonly children?: Array<TextRun | PictureRun | Hyperlink | SymbolRun | Bookmark | PageBreak | SequentialIdentifier>;
} }
export class Paragraph extends XmlComponent { export class Paragraph extends XmlComponent {
@ -130,21 +122,9 @@ export class Paragraph extends XmlComponent {
this.properties.push(new KeepNext()); this.properties.push(new KeepNext());
} }
if (options.tabStop) { if (options.tabStops) {
if (options.tabStop.left) { for (const tabStop of options.tabStops) {
this.properties.push(new LeftTabStop(options.tabStop.left.position, options.tabStop.left.leader)); this.properties.push(new TabStop(tabStop.type, tabStop.position, tabStop.leader));
}
if (options.tabStop.right) {
this.properties.push(new RightTabStop(options.tabStop.right.position, options.tabStop.right.leader));
}
if (options.tabStop.maxRight) {
this.properties.push(new MaxRightTabStop(options.tabStop.maxRight.leader));
}
if (options.tabStop.center) {
this.properties.push(new CenterTabStop(options.tabStop.center.position, options.tabStop.center.leader));
} }
} }
@ -166,34 +146,18 @@ export class Paragraph extends XmlComponent {
if (options.children) { if (options.children) {
for (const child of options.children) { for (const child of options.children) {
if (child instanceof Bookmark) {
this.root.push(child.start);
this.root.push(child.text);
this.root.push(child.end);
continue;
}
this.root.push(child); this.root.push(child);
} }
} }
} }
public addRun(run: Run): Paragraph {
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 pageBreak(): Paragraph {
this.root.push(new PageBreak());
return this;
}
public referenceFootnote(id: number): Paragraph { public referenceFootnote(id: number): Paragraph {
this.root.push(new FootnoteReferenceRun(id)); this.root.push(new FootnoteReferenceRun(id));
return this; return this;
@ -203,9 +167,4 @@ export class Paragraph extends XmlComponent {
this.root.splice(1, 0, run); this.root.splice(1, 0, run);
return this; return this;
} }
public addSequentialIdentifier(identifier: string): Paragraph {
this.root.push(new SequentialIdentifier(identifier));
return this;
}
} }

View File

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

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

@ -2,6 +2,7 @@
import { ShadingType } from "file/table"; import { ShadingType } from "file/table";
import { XmlComponent } from "file/xml-components"; import { XmlComponent } from "file/xml-components";
import { FieldInstruction } from "file/table-of-contents/field-instruction";
import { Break } from "./break"; import { Break } from "./break";
import { Caps, SmallCaps } from "./caps"; import { Caps, SmallCaps } from "./caps";
import { Begin, End, Separate } from "./field"; import { Begin, End, Separate } from "./field";
@ -56,6 +57,7 @@ export interface IRunOptions {
readonly fill: string; readonly fill: string;
readonly color: string; readonly color: string;
}; };
readonly children?: Array<Begin | FieldInstruction | Separate | End>;
} }
export class Run extends XmlComponent { 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 Shading(options.shading.type, options.shading.fill, options.shading.color));
this.properties.push(new ShadowComplexScript(options.shading.type, options.shading.fill, options.shading.color)); this.properties.push(new ShadowComplexScript(options.shading.type, options.shading.fill, options.shading.color));
} }
if (options.children) {
for (const child of options.children) {
this.root.push(child);
}
}
} }
public break(): Run { public break(): Run {

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,16 @@
import { expect } from "chai"; import { expect } from "chai";
import { Formatter } from "export/formatter"; 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 { CharacterStyle } from "./character-style";
import { EMPTY_OBJECT } from "file/xml-components";
describe("CharacterStyle", () => { describe("CharacterStyle", () => {
describe("#constructor", () => { describe("#constructor", () => {
it("should set the style type to character and use the given style id", () => { 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); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -17,7 +18,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -29,7 +30,10 @@ describe("CharacterStyle", () => {
}); });
it("should set the name of the style, if given", () => { 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); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -38,7 +42,222 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _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", () => { describe("formatting methods: style attributes", () => {
it("#basedOn", () => { it("#basedOn", () => {
const style = new CharacterStyle("myStyleId").basedOn("otherId"); const style = new CharacterStyle({ id: "myStyleId", basedOn: "otherId" });
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -60,7 +279,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -75,7 +294,12 @@ describe("CharacterStyle", () => {
describe("formatting methods: run properties", () => { describe("formatting methods: run properties", () => {
it("#size", () => { it("#size", () => {
const style = new CharacterStyle("myStyleId").size(24); const style = new CharacterStyle({
id: "myStyleId",
run: {
size: 24,
},
});
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -86,7 +310,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -99,7 +323,12 @@ describe("CharacterStyle", () => {
describe("#underline", () => { describe("#underline", () => {
it("should set underline to 'single' if no arguments are given", () => { 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); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -110,7 +339,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -122,7 +351,14 @@ describe("CharacterStyle", () => {
}); });
it("should set the style if given", () => { 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); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -133,7 +369,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -145,7 +381,15 @@ describe("CharacterStyle", () => {
}); });
it("should set the style and color if given", () => { 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); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -156,7 +400,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -169,7 +413,12 @@ describe("CharacterStyle", () => {
}); });
it("#superScript", () => { it("#superScript", () => {
const style = new CharacterStyle("myStyleId").superScript(); const style = new CharacterStyle({
id: "myStyleId",
run: {
superScript: true,
},
});
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -188,7 +437,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -200,7 +449,12 @@ describe("CharacterStyle", () => {
}); });
it("#color", () => { it("#color", () => {
const style = new CharacterStyle("myStyleId").color("123456"); const style = new CharacterStyle({
id: "myStyleId",
run: {
color: "123456",
},
});
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -211,7 +465,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -223,7 +477,12 @@ describe("CharacterStyle", () => {
}); });
it("#bold", () => { it("#bold", () => {
const style = new CharacterStyle("myStyleId").bold(); const style = new CharacterStyle({
id: "myStyleId",
run: {
bold: true,
},
});
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -234,7 +493,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -246,7 +505,12 @@ describe("CharacterStyle", () => {
}); });
it("#italics", () => { it("#italics", () => {
const style = new CharacterStyle("myStyleId").italics(); const style = new CharacterStyle({
id: "myStyleId",
run: {
italics: true,
},
});
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -257,7 +521,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -269,7 +533,7 @@ describe("CharacterStyle", () => {
}); });
it("#link", () => { it("#link", () => {
const style = new CharacterStyle("myStyleId").link("MyLink"); const style = new CharacterStyle({ id: "myStyleId", link: "MyLink" });
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -277,7 +541,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -290,7 +554,7 @@ describe("CharacterStyle", () => {
}); });
it("#semiHidden", () => { it("#semiHidden", () => {
const style = new CharacterStyle("myStyleId").semiHidden(); const style = new CharacterStyle({ id: "myStyleId", semiHidden: true });
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -298,7 +562,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -309,7 +573,12 @@ describe("CharacterStyle", () => {
}); });
it("#highlight", () => { it("#highlight", () => {
const style = new CharacterStyle("myStyleId").highlight("005599"); const style = new CharacterStyle({
id: "myStyleId",
run: {
highlight: "005599",
},
});
const tree = new Formatter().format(style); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -320,7 +589,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -332,7 +601,16 @@ describe("CharacterStyle", () => {
}); });
it("#shadow", () => { 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); const tree = new Formatter().format(style);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({
"w:style": [ "w:style": [
@ -343,7 +621,7 @@ describe("CharacterStyle", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,8 +25,4 @@ export class Style extends XmlComponent {
this.root.push(new Name(name)); 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 { Formatter } from "export/formatter";
import { EMPTY_OBJECT } from "file/xml-components";
import { CharacterStyle, ParagraphStyle } from "./style";
import { Styles } from "./styles"; import { Styles } from "./styles";
import { EMPTY_OBJECT } from "file/xml-components";
describe("Styles", () => { 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", () => { describe("#createParagraphStyle", () => {
it("should create a new paragraph style and push it onto this collection", () => { it("should create a new paragraph style and push it onto this collection", () => {
const pStyle = styles.createParagraphStyle("pStyleId"); const styles = new Styles({
expect(pStyle).to.instanceOf(ParagraphStyle); paragraphStyles: [
{
id: "pStyleId",
},
],
});
const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr); const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr);
expect(tree).to.deep.equal([ expect(tree).to.deep.equal([
{ {
@ -35,8 +24,14 @@ describe("Styles", () => {
}); });
it("should set the paragraph name if given", () => { it("should set the paragraph name if given", () => {
const pStyle = styles.createParagraphStyle("pStyleId", "Paragraph Style"); const styles = new Styles({
expect(pStyle).to.instanceOf(ParagraphStyle); paragraphStyles: [
{
id: "pStyleId",
name: "Paragraph Style",
},
],
});
const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr); const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr);
expect(tree).to.deep.equal([ expect(tree).to.deep.equal([
{ {
@ -51,8 +46,13 @@ describe("Styles", () => {
describe("#createCharacterStyle", () => { describe("#createCharacterStyle", () => {
it("should create a new character style and push it onto this collection", () => { it("should create a new character style and push it onto this collection", () => {
const cStyle = styles.createCharacterStyle("pStyleId"); const styles = new Styles({
expect(cStyle).to.instanceOf(CharacterStyle); characterStyles: [
{
id: "pStyleId",
},
],
});
const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr); const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr);
expect(tree).to.deep.equal([ expect(tree).to.deep.equal([
{ {
@ -61,7 +61,7 @@ describe("Styles", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },
@ -74,8 +74,14 @@ describe("Styles", () => {
}); });
it("should set the character name if given", () => { it("should set the character name if given", () => {
const cStyle = styles.createCharacterStyle("pStyleId", "Character Style"); const styles = new Styles({
expect(cStyle).to.instanceOf(CharacterStyle); characterStyles: [
{
id: "pStyleId",
name: "Character Style",
},
],
});
const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr); const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr);
expect(tree).to.deep.equal([ expect(tree).to.deep.equal([
{ {
@ -85,7 +91,7 @@ describe("Styles", () => {
{ {
"w:uiPriority": { "w:uiPriority": {
_attr: { _attr: {
"w:val": "99", "w:val": 99,
}, },
}, },
}, },

View File

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

View File

@ -52,7 +52,7 @@ export class FieldInstruction extends XmlComponent {
instruction = `${instruction} \\s "${this.properties.seqFieldIdentifierForPrefix}"`; instruction = `${instruction} \\s "${this.properties.seqFieldIdentifierForPrefix}"`;
} }
if (this.properties.stylesWithLevels && this.properties.stylesWithLevels.length) { 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}"`; instruction = `${instruction} \\t "${styles}"`;
} }
if (this.properties.useAppliedParagraphOutlineLevel) { if (this.properties.useAppliedParagraphOutlineLevel) {

View File

@ -146,7 +146,7 @@ const COMPLETE_TOC = {
"xml:space": "preserve", "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 content = new StructuredDocumentTagContent();
const beginParagraph = new Paragraph({}); const beginParagraph = new Paragraph({
const beginRun = new Run({}); children: [
beginRun.addChildElement(new Begin(true)); new Run({
beginRun.addChildElement(new FieldInstruction(properties)); children: [new Begin(true), new FieldInstruction(properties), new Separate()],
beginRun.addChildElement(new Separate()); }),
beginParagraph.addRun(beginRun); ],
});
content.addChildElement(beginParagraph); content.addChildElement(beginParagraph);
const endParagraph = new Paragraph({}); const endParagraph = new Paragraph({
const endRun = new Run({}); children: [
endRun.addChildElement(new End()); new Run({
endParagraph.addRun(endRun); children: [new End()],
}),
],
});
content.addChildElement(endParagraph); content.addChildElement(endParagraph);
this.root.push(content); this.root.push(content);