Compare commits
92 Commits
Author | SHA1 | Date | |
---|---|---|---|
f091cff7c9 | |||
3f80b054fc | |||
96d81873d8 | |||
c429ae9920 | |||
6b4e769f48 | |||
b67a9de0e9 | |||
ac9f65a068 | |||
7e8ebb2af2 | |||
f53fe2f881 | |||
6fdd88527a | |||
b6f431e14d | |||
23dee01f06 | |||
a466578467 | |||
3a9420fedf | |||
8ac19a83b2 | |||
9f0b2f7074 | |||
e02ac43c07 | |||
ccffdad4c0 | |||
2fb5845501 | |||
ddd84a1765 | |||
f6a13aed86 | |||
2da3ba0262 | |||
e08c7cbbfb | |||
a6de5d8a21 | |||
00a20b7cfc | |||
f27c95191b | |||
c140d2c37c | |||
3a42f2a2f0 | |||
00efedaa09 | |||
72e3d229dc | |||
b12d6ef484 | |||
8ee6fd3e67 | |||
808c5b00a0 | |||
4de6b51e76 | |||
4ca81df401 | |||
8b463b3bb6 | |||
17d696e33a | |||
2f59867db6 | |||
90f3da74cf | |||
28d8659cb5 | |||
5d1740a2ef | |||
7aa44568d8 | |||
bf1f702e5a | |||
78ad3e340a | |||
63a965beab | |||
815354e06f | |||
1cff104bae | |||
4805efad2e | |||
12e2ae9e91 | |||
c07b5cf709 | |||
0684738ec2 | |||
6da3efdacc | |||
7cd8864fb9 | |||
a9c69664c7 | |||
50569224c3 | |||
877aa325bb | |||
eb797d8986 | |||
fcb542471b | |||
baf0f17bd6 | |||
8e911698a5 | |||
0eb36be053 | |||
53b24965aa | |||
edce1bef92 | |||
e0d54d3af3 | |||
a9167b4809 | |||
5b28eb0d00 | |||
f1b176670c | |||
11ce9a5206 | |||
8b667b8d4c | |||
a601a82a37 | |||
dcf3767a05 | |||
a9278009f8 | |||
5220f9e07c | |||
0b88064ca2 | |||
2233ccb968 | |||
311fde01a1 | |||
146d0ad9e5 | |||
aedfca377f | |||
7926f6c189 | |||
0303a9f553 | |||
e9a007d446 | |||
12c1f82efe | |||
b1711ae293 | |||
a367951d07 | |||
c55f82c425 | |||
2dab11e8b3 | |||
d1044d262e | |||
8d83219bb6 | |||
a710483918 | |||
4f8ecf631b | |||
a6a8012b39 | |||
d5b6225a90 |
7
.gitignore
vendored
7
.gitignore
vendored
@ -48,8 +48,15 @@ docs/.nojekyll
|
|||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
.history
|
.history
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
.idea
|
||||||
|
|
||||||
# Lock files
|
# Lock files
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
# Documents
|
# Documents
|
||||||
My Document.docx
|
My Document.docx
|
||||||
|
|
||||||
|
# Temporary folder
|
||||||
|
tmp
|
21
README.md
21
README.md
@ -1,5 +1,5 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="clippy the assistant" src="http://i60.tinypic.com/339pvtt.png">
|
<img alt="clippy the assistant" src="https://i.imgur.com/37uBGhO.gif">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@ -17,11 +17,24 @@
|
|||||||
[![PRs Welcome][pr-image]][pr-url]
|
[![PRs Welcome][pr-image]][pr-url]
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://thumbs.gfycat.com/ComplexEminentEquine-size_restricted.gif" alt="drawing" width="800"/>
|
<img src="https://i.imgur.com/H5FA1Qy.gif" alt="drawing" width="800"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# Demo
|
# Demo
|
||||||
|
|
||||||
|
## Browser
|
||||||
|
|
||||||
|
Here are examples of `docx` being used with basic `HTML/JS` in a browser environment.
|
||||||
|
|
||||||
|
* https://codepen.io/anon/pen/dqoVgQ
|
||||||
|
* https://jsfiddle.net/3xhezb5w/2
|
||||||
|
|
||||||
|
Here is an example of `docx` working in `Angular`:
|
||||||
|
|
||||||
|
* https://stackblitz.com/edit/angular-afvxtz
|
||||||
|
|
||||||
|
## Node
|
||||||
|
|
||||||
Press `endpoint` on the `RunKit` website:
|
Press `endpoint` on the `RunKit` website:
|
||||||
|
|
||||||

|

|
||||||
@ -33,9 +46,11 @@ Press `endpoint` on the `RunKit` website:
|
|||||||
* https://runkit.com/dolanmiu/docx-demo5 - Images
|
* https://runkit.com/dolanmiu/docx-demo5 - Images
|
||||||
* https://runkit.com/dolanmiu/docx-demo6 - Margins
|
* https://runkit.com/dolanmiu/docx-demo6 - Margins
|
||||||
* https://runkit.com/dolanmiu/docx-demo7 - Landscape
|
* https://runkit.com/dolanmiu/docx-demo7 - Landscape
|
||||||
* https://runkit.com/dolanmiu/docx-demo8/1.0.1 - Header and Footer
|
* https://runkit.com/dolanmiu/docx-demo8 - Header and Footer
|
||||||
* https://runkit.com/dolanmiu/docx-demo10 - **My CV generated with docx**
|
* https://runkit.com/dolanmiu/docx-demo10 - **My CV generated with docx**
|
||||||
|
|
||||||
|
More [here](https://docx.js.org/#/examples) and [here](https://github.com/dolanmiu/docx/tree/master/demo)
|
||||||
|
|
||||||
# How to use & Documentation
|
# How to use & Documentation
|
||||||
|
|
||||||
Please refer to the [documentation at https://docx.js.org/](https://docx.js.org/) for details on how to use this library, examples and much more!
|
Please refer to the [documentation at https://docx.js.org/](https://docx.js.org/) for details on how to use this library, examples and much more!
|
||||||
|
34
demo/demo27.ts
Normal file
34
demo/demo27.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Packer } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
const myStyles = doc.Styles;
|
||||||
|
|
||||||
|
// The first argument is an ID you use to apply the style to paragraphs
|
||||||
|
// The second argument is a human-friendly name to show in the UI
|
||||||
|
myStyles.createParagraphStyle("myWonkyStyle", "My Wonky Style")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.color("990000")
|
||||||
|
.italics()
|
||||||
|
.indent({left: 720}) // 720 TWIP === 720 / 20 pt === .5 in
|
||||||
|
.spacing({line: 276}); // 276 / 240 = 1.15x line spacing
|
||||||
|
|
||||||
|
myStyles.createParagraphStyle("Heading2", "Heading 2")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.quickFormat()
|
||||||
|
.size(26) // 26 half-points === 13pt font
|
||||||
|
.bold()
|
||||||
|
.underline("double", "FF0000")
|
||||||
|
.spacing({before: 240, after: 120}); // TWIP for both
|
||||||
|
|
||||||
|
doc.createParagraph("Hello").style("myWonkyStyle");
|
||||||
|
doc.createParagraph("World").heading2(); // Uses the Heading2 style
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
console.log("Document created successfully at project root!");
|
||||||
|
});
|
43
demo/demo28.ts
Normal file
43
demo/demo28.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Creates two paragraphs, one with a border and one without
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { File, Packer, Paragraph, StyleLevel, TableOfContents } from "../build";
|
||||||
|
|
||||||
|
const doc = new File();
|
||||||
|
|
||||||
|
// The first argument is an ID you use to apply the style to paragraphs
|
||||||
|
// The second argument is a human-friendly name to show in the UI
|
||||||
|
doc.Styles.createParagraphStyle("MySpectacularStyle", "My Spectacular Style")
|
||||||
|
.basedOn("Heading1")
|
||||||
|
.next("Heading1")
|
||||||
|
.color("990000")
|
||||||
|
.italics();
|
||||||
|
|
||||||
|
// WordprocessingML docs for TableOfContents can be found here:
|
||||||
|
// http://officeopenxml.com/WPtableOfContents.php
|
||||||
|
|
||||||
|
// Let's define the properties 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.addParagraph(new Paragraph("Header #1").heading1().pageBreakBefore());
|
||||||
|
doc.addParagraph(new Paragraph("I'm a little text very nicely written.'"));
|
||||||
|
|
||||||
|
doc.addParagraph(new Paragraph("Header #2").heading1().pageBreakBefore());
|
||||||
|
doc.addParagraph(new Paragraph("I'm a other text very nicely written.'"));
|
||||||
|
doc.addParagraph(new Paragraph("Header #2.1").heading2());
|
||||||
|
doc.addParagraph(new Paragraph("I'm a another text very nicely written.'"));
|
||||||
|
|
||||||
|
doc.addParagraph(new Paragraph("My Spectacular Style #1").style("MySpectacularStyle").pageBreakBefore());
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
32
demo/demo29.ts
Normal file
32
demo/demo29.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Indent, Numbering, Packer, Paragraph } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const numbering = new Numbering();
|
||||||
|
|
||||||
|
const abstractNum = numbering.createAbstractNumbering();
|
||||||
|
abstractNum.createLevel(0, "upperRoman", "%1", "start").addParagraphProperty(new Indent({ left: 720, hanging: 260 }));
|
||||||
|
|
||||||
|
const concrete = numbering.createConcreteNumbering(abstractNum);
|
||||||
|
|
||||||
|
const item1 = new Paragraph("line with contextual spacing");
|
||||||
|
const item2 = new Paragraph("line with contextual spacing");
|
||||||
|
const item3 = new Paragraph("line without contextual spacing");
|
||||||
|
const item4 = new Paragraph("line without contextual spacing");
|
||||||
|
|
||||||
|
item1.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(true);
|
||||||
|
item2.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(true);
|
||||||
|
item3.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(false);
|
||||||
|
item4.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(false);
|
||||||
|
|
||||||
|
doc.addParagraph(item1);
|
||||||
|
doc.addParagraph(item2);
|
||||||
|
doc.addParagraph(item3);
|
||||||
|
doc.addParagraph(item4);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
26
demo/demo31.ts
Normal file
26
demo/demo31.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Example of how you would create a table and add data to it
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Packer, Paragraph, VerticalAlign } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const table = doc.createTable(2, 2);
|
||||||
|
table
|
||||||
|
.getCell(1, 1)
|
||||||
|
.addContent(new Paragraph("This text should be in the middle of the cell"))
|
||||||
|
.CellProperties.setVerticalAlign(VerticalAlign.CENTER);
|
||||||
|
|
||||||
|
table
|
||||||
|
.getCell(1, 0)
|
||||||
|
.addContent(
|
||||||
|
new Paragraph(
|
||||||
|
"Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah",
|
||||||
|
).heading1(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
35
demo/demo32.ts
Normal file
35
demo/demo32.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Example of how you would create a table and add data to it
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Packer, Paragraph } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
let table = doc.createTable(2, 2);
|
||||||
|
|
||||||
|
table.getCell(0, 0).addContent(new Paragraph("Hello"));
|
||||||
|
table.getRow(0).mergeCells(0, 1);
|
||||||
|
|
||||||
|
doc.createParagraph("Another table").heading2();
|
||||||
|
|
||||||
|
table = doc.createTable(2, 3);
|
||||||
|
table.getCell(0, 0).addContent(new Paragraph("World"));
|
||||||
|
table.getRow(0).mergeCells(0, 2);
|
||||||
|
|
||||||
|
doc.createParagraph("Another table").heading2();
|
||||||
|
|
||||||
|
table = doc.createTable(2, 4);
|
||||||
|
table.getCell(0, 0).addContent(new Paragraph("Foo"));
|
||||||
|
|
||||||
|
table.getCell(1, 0).addContent(new Paragraph("Bar1"));
|
||||||
|
table.getCell(1, 1).addContent(new Paragraph("Bar2"));
|
||||||
|
table.getCell(1, 2).addContent(new Paragraph("Bar3"));
|
||||||
|
table.getCell(1, 3).addContent(new Paragraph("Bar4"));
|
||||||
|
|
||||||
|
table.getRow(0).mergeCells(0, 3);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
22
demo/demo33.ts
Normal file
22
demo/demo33.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Sequential Captions
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, Packer, Paragraph, TextRun } from "../build";
|
||||||
|
|
||||||
|
const doc = new Document();
|
||||||
|
|
||||||
|
const paragraph = new Paragraph("Hello World 1->").addSequentialIdentifier("Caption").addRun(new TextRun(" text after sequencial caption 2->")).addSequentialIdentifier("Caption");
|
||||||
|
const paragraph2 = new Paragraph("Hello World 1->").addSequentialIdentifier("Label").addRun(new TextRun(" text after sequencial caption 2->")).addSequentialIdentifier("Label");
|
||||||
|
const paragraph3 = new Paragraph("Hello World 1->").addSequentialIdentifier("Another").addRun(new TextRun(" text after sequencial caption 3->")).addSequentialIdentifier("Label");
|
||||||
|
const paragraph4 = new Paragraph("Hello World 2->").addSequentialIdentifier("Another").addRun(new TextRun(" text after sequencial caption 4->")).addSequentialIdentifier("Label");
|
||||||
|
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
doc.addParagraph(paragraph2);
|
||||||
|
doc.addParagraph(paragraph3);
|
||||||
|
doc.addParagraph(paragraph4);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
@ -1,5 +1,5 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="clippy the assistant" src="http://i60.tinypic.com/339pvtt.png">
|
<img alt="clippy the assistant" src="https://i.imgur.com/pwCV6L8.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@ -54,6 +54,10 @@ exporter.pack("My First Document");
|
|||||||
|
|
||||||
[@h4buli](https://github.com/h4buli)
|
[@h4buli](https://github.com/h4buli)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="clippy the assistant" src="http://i60.tinypic.com/339pvtt.png">
|
||||||
|
</p>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Made with 💖
|
Made with 💖
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
* [Bullet Points](usage/bullet-points.md)
|
* [Bullet Points](usage/bullet-points.md)
|
||||||
* [Numbering](usage/numbering.md)
|
* [Numbering](usage/numbering.md)
|
||||||
* [Tab Stops](usage/tab-stops.md)
|
* [Tab Stops](usage/tab-stops.md)
|
||||||
|
* [Table of Contents](usage/table-of-contents.md)
|
||||||
* Styling
|
* Styling
|
||||||
* [Styling with JS](usage/styling-with-js.md)
|
* [Styling with JS](usage/styling-with-js.md)
|
||||||
* [Styling with XML](usage/styling-with-xml.md)
|
* [Styling with XML](usage/styling-with-xml.md)
|
||||||
|
@ -1,5 +1,30 @@
|
|||||||
# Contribution Guidelines
|
# Contribution Guidelines
|
||||||
|
|
||||||
|
## Always think about the user
|
||||||
|
|
||||||
|
The number one pillar for contribution is to **ALWAYS** think about how the user will use the library.
|
||||||
|
|
||||||
|
Put yourself in their position, and imagine how they would feel about your feature you wrote.
|
||||||
|
|
||||||
|
1. Is it easy to use?
|
||||||
|
2. Has it been documented well?
|
||||||
|
3. Is it intuative?
|
||||||
|
4. Is it consistent with the rest of the API?
|
||||||
|
5. Is it fun to use?
|
||||||
|
|
||||||
|
## Good Commit Names
|
||||||
|
|
||||||
|
Please write good commit messages when making a commit: https://chris.beams.io/posts/git-commit/
|
||||||
|
|
||||||
|
**Do not:**
|
||||||
|
```
|
||||||
|
c // What?
|
||||||
|
rtl // Adding acryonyms without explaining anything else is not helpful
|
||||||
|
works! // Glad its working, but the message is not helpful
|
||||||
|
demo updated // Getting better, but capitalize the first letter
|
||||||
|
Unesesary coment removed // Make sure to use correct spelling
|
||||||
|
```
|
||||||
|
|
||||||
## Writing Code
|
## Writing Code
|
||||||
|
|
||||||
* Include documentation reference(s) at the top of each file:
|
* Include documentation reference(s) at the top of each file:
|
||||||
@ -45,18 +70,98 @@ public get Level() {
|
|||||||
|
|
||||||
There is no performance advantage by doing this. It means we don't need to prefix all private variables with the ugly `_`:
|
There is no performance advantage by doing this. It means we don't need to prefix all private variables with the ugly `_`:
|
||||||
|
|
||||||
**Do not:**
|
**Do not:**
|
||||||
|
|
||||||
```js
|
```js
|
||||||
private get _level: string;
|
private get _level: string;
|
||||||
```
|
```
|
||||||
|
|
||||||
**Do**
|
**Do**
|
||||||
|
|
||||||
```js
|
```js
|
||||||
private get level: string;
|
private get level: string;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Interfaces over type alias
|
||||||
|
|
||||||
|
Do not use `type`, but rather use `Interfaces`. `type` cannot be extended, and a class cannot implement it.
|
||||||
|
|
||||||
|
> "In general, use what you want ( type alias / interface ) just be consistent"
|
||||||
|
> "always use interface for public API's definition when authoring a library or 3rd party ambient type definitions"
|
||||||
|
>
|
||||||
|
> * https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c
|
||||||
|
|
||||||
|
`Interface` is generally preferred over `type`: https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types
|
||||||
|
|
||||||
|
**Do not:**
|
||||||
|
|
||||||
|
```js
|
||||||
|
type RelationshipFileInfo = { id: number, target: string };
|
||||||
|
```
|
||||||
|
|
||||||
|
**Do:**
|
||||||
|
|
||||||
|
```js
|
||||||
|
interface IRelationshipFileInfo {
|
||||||
|
id: number;
|
||||||
|
target: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## String enums vs type
|
||||||
|
|
||||||
|
To take full advantage of TypeScript's typing system, its best to use `string enums`:
|
||||||
|
|
||||||
|
**Do not:**
|
||||||
|
|
||||||
|
```js
|
||||||
|
type WeaponType = "bow" | "sword" | "wand";
|
||||||
|
```
|
||||||
|
|
||||||
|
**Do:**
|
||||||
|
|
||||||
|
```js
|
||||||
|
enum WeaponType = {
|
||||||
|
BOW = "bow",
|
||||||
|
SWORD = "sword",
|
||||||
|
WAND = "wand",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Spell correctly, full and in American English
|
||||||
|
|
||||||
|
I am not sure where these habit in software development comes from, but I do not believe it is beneficial:
|
||||||
|
|
||||||
|
**Do not:**
|
||||||
|
```js
|
||||||
|
readdy // misspelling
|
||||||
|
perm // abbreviation
|
||||||
|
conf // abbreviation
|
||||||
|
cnty // abbreviation
|
||||||
|
relationFile // abbreviation
|
||||||
|
colour // U.K. English
|
||||||
|
```
|
||||||
|
|
||||||
|
**Do:**
|
||||||
|
```js
|
||||||
|
ready
|
||||||
|
permission
|
||||||
|
config
|
||||||
|
country
|
||||||
|
relationshipFile
|
||||||
|
color
|
||||||
|
```
|
||||||
|
|
||||||
|
## Keep files small (within reason)
|
||||||
|
|
||||||
|
To minimize merge conflicts, reduce complexity, and improve readability, keep the files small.
|
||||||
|
|
||||||
|
## Name files and folders with `/foo-bar/kebab-case.ts`
|
||||||
|
|
||||||
|
To be consistent and in-line with the project, name files `like-this.ts`.
|
||||||
|
|
||||||
|
https://stackoverflow.com/questions/7273316/what-is-the-javascript-filename-naming-convention
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Please write a test of every file you make and suffix it with `.spec.ts`.
|
Please write a test of every file you make and suffix it with `.spec.ts`.
|
||||||
@ -78,3 +183,5 @@ describe("ClassName", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Try not to use the `tests/utility.ts` file as this is being deprecated.
|
||||||
|
@ -20,6 +20,7 @@ const name = new TextRun("Name:")
|
|||||||
* `.size(halfPts)`: Set the font size, measured in half-points
|
* `.size(halfPts)`: Set the font size, measured in half-points
|
||||||
* `.font(name)`: Set the run's font
|
* `.font(name)`: Set the run's font
|
||||||
* `.style(name)`: Apply a named run style
|
* `.style(name)`: Apply a named run style
|
||||||
|
* `.characterSpacing(value)`: Set the character spacing adjustment (in TWIPs)
|
||||||
* For paragraph formatting:
|
* For paragraph formatting:
|
||||||
* `.heading1()`, `.heading2()`, `.heading3()`, `.heading4()`, `.heading5()`, `.title()`: apply the appropriate style to the paragraph
|
* `.heading1()`, `.heading2()`, `.heading3()`, `.heading4()`, `.heading5()`, `.title()`: apply the appropriate style to the paragraph
|
||||||
* `.left()`, `.center()`, `.right()`, `.justified()`: set the paragraph's alignment
|
* `.left()`, `.center()`, `.right()`, `.justified()`: set the paragraph's alignment
|
||||||
|
76
docs/usage/table-of-contents.md
Normal file
76
docs/usage/table-of-contents.md
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
You can generate table of contents with `docx`. More information can be found [here](http://officeopenxml.com/WPtableOfContents.php).
|
||||||
|
|
||||||
|
>Tables of Contents are fields and, by design, it's content is only generated or updated by Word. We can't do it programatically.
|
||||||
|
>This is why, when you open a the file, Word you will prompt the message "This document contains fields that may refer to other files. Do you want to update the fields in this document?".
|
||||||
|
>You have say yes to Word generate the content of all table of contents.
|
||||||
|
|
||||||
|
The complete documentation can be found [here](https://www.ecma-international.org/publications/standards/Ecma-376.htm) (at Part 1, Page 1251).
|
||||||
|
|
||||||
|
## How to
|
||||||
|
|
||||||
|
All you need to do is create a `TableOfContents` object and assign it to the document.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const toc = new TableOfContents("Summary", {
|
||||||
|
hyperlink: true,
|
||||||
|
headingStyleRange: "1-5",
|
||||||
|
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)]
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.addTableOfContents(toc);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Table of Contents Options
|
||||||
|
|
||||||
|
Here is the list of all options that you can use to generate your tables of contents:
|
||||||
|
|
||||||
|
| Option | Type | TOC Field Switch | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
|captionLabel|string|`\a`|Includes captioned items, but omits caption labels and numbers. The identifier designated by `text` in this switch's field-argument corresponds to the caption label. Use ``\c`` to build a table of captions with labels and numbers.|
|
||||||
|
|entriesFromBookmark|string|`\b`|Includes entries only from the portion of the document marked by the bookmark named by `text` in this switch's field-argument.|
|
||||||
|
|captionLabelIncludingNumbers|string|`\c`|Includes figures, tables, charts, and other items that are numbered by a SEQ field (§17.16.5.56). The sequence identifier designated by `text` in this switch's field-argument, which corresponds to the caption label, shall match the identifier in the corresponding SEQ field.|
|
||||||
|
|sequenceAndPageNumbersSeparator|string|`\d`|When used with `\s`, the `text` in this switch's field-argument defines the separator between sequence and page numbers. The default separator is a hyphen (-).|
|
||||||
|
|tcFieldIdentifier|string|`\f`|Includes only those TC fields whose identifier exactly matches the `text` in this switch's field-argument (which is typically a letter).|
|
||||||
|
|hyperlink|boolean|`\h`|Makes the table of contents entries hyperlinks.|
|
||||||
|
|tcFieldLevelRange|string|`\l`|Includes TC fields that assign entries to one of the levels specified by `text` in this switch's field-argument as a range having the form startLevel-endLevel, where startLevel and endLevel are integers, and startLevel has a value equal-to or less-than endLevel. TC fields that assign entries to lower levels are skipped.|
|
||||||
|
|pageNumbersEntryLevelsRange|string|`\n`|Without field-argument, omits page numbers from the table of contents. Page numbers are omitted from all levels unless a range of entry levels is specified by `text` in this switch's field-argument. A range is specified as for `\l`.|
|
||||||
|
|headingStyleRange|string|`\o`|Uses paragraphs formatted with all or the specified range of builtin heading styles. Headings in a style range are specified by `text` in this switch's field-argument using the notation specified as for `\l`, where each integer corresponds to the style with a style ID of HeadingX (e.g. 1 corresponds to Heading1). If no heading range is specified, all heading levels used in the document are listed.|
|
||||||
|
|entryAndPageNumberSeparator|string|`\p`|`text` in this switch's field-argument specifies a sequence of characters that separate an entry and its page number. The default is a tab with leader dots.|
|
||||||
|
|seqFieldIdentifierForPrefix|string|`\s`|For entries numbered with a SEQ field (§17.16.5.56), adds a prefix to the page number. The prefix depends on the type of entry. `text` in this switch's field-argument shall match the identifier in the SEQ field.|
|
||||||
|
|stylesWithLevels|StyleLevel[]|`\t`| Uses paragraphs formatted with styles other than the built-in heading styles. `text` in this switch's field-argument specifies those styles as a set of comma-separated doublets, with each doublet being a comma-separated set of style name and table of content level. `\t` can be combined with `\o`.|
|
||||||
|
|useAppliedParagraphOutlineLevel|boolean|`\u`|Uses the applied paragraph outline level.|
|
||||||
|
|preserveTabInEntries|boolean|`\w`|Preserves tab entries within table entries.|
|
||||||
|
|preserveNewLineInEntries|boolean|`\x`|Preserves newline characters within table entries.|
|
||||||
|
|hideTabAndPageNumbersInWebView|boolean|`\z`|Hides tab leader and page numbers in web page view (§17.18.102).|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 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.addParagraph(new Paragraph("Header #1").heading1().pageBreakBefore());
|
||||||
|
doc.addParagraph(new Paragraph("I'm a little text, very nicely written.'"));
|
||||||
|
|
||||||
|
doc.addParagraph(new Paragraph("Header #2").heading1().pageBreakBefore());
|
||||||
|
doc.addParagraph(new Paragraph("I'm another text very nicely written.'"));
|
||||||
|
doc.addParagraph(new Paragraph("Header #2.1").heading2());
|
||||||
|
doc.addParagraph(new Paragraph("I'm another text very nicely written.'"));
|
||||||
|
|
||||||
|
doc.addParagraph(new Paragraph("My Spectacular Style #1").style("MySpectacularStyle").pageBreakBefore());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Complete example
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo28.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo28.ts_
|
BIN
logo/logo-small.gif
Normal file
BIN
logo/logo-small.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
BIN
logo/logo-small.png
Normal file
BIN
logo/logo-small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
BIN
logo/logo-small.psd
Normal file
BIN
logo/logo-small.psd
Normal file
Binary file not shown.
BIN
logo/logo.psd
Normal file
BIN
logo/logo.psd
Normal file
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "docx",
|
"name": "docx",
|
||||||
"version": "4.0.0",
|
"version": "4.4.0",
|
||||||
"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": {
|
||||||
@ -82,5 +82,8 @@
|
|||||||
"typedoc": "^0.11.1",
|
"typedoc": "^0.11.1",
|
||||||
"typescript": "2.9.2",
|
"typescript": "2.9.2",
|
||||||
"webpack": "^3.10.0"
|
"webpack": "^3.10.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ describe("Compiler", () => {
|
|||||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||||
|
|
||||||
expect(fileNames).is.an.instanceof(Array);
|
expect(fileNames).is.an.instanceof(Array);
|
||||||
expect(fileNames).has.length(17);
|
expect(fileNames).has.length(18);
|
||||||
expect(fileNames).to.include("word/document.xml");
|
expect(fileNames).to.include("word/document.xml");
|
||||||
expect(fileNames).to.include("word/styles.xml");
|
expect(fileNames).to.include("word/styles.xml");
|
||||||
expect(fileNames).to.include("docProps/core.xml");
|
expect(fileNames).to.include("docProps/core.xml");
|
||||||
@ -29,6 +29,7 @@ describe("Compiler", () => {
|
|||||||
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
||||||
expect(fileNames).to.include("word/footer1.xml");
|
expect(fileNames).to.include("word/footer1.xml");
|
||||||
expect(fileNames).to.include("word/footnotes.xml");
|
expect(fileNames).to.include("word/footnotes.xml");
|
||||||
|
expect(fileNames).to.include("word/settings.xml");
|
||||||
expect(fileNames).to.include("word/_rels/footer1.xml.rels");
|
expect(fileNames).to.include("word/_rels/footer1.xml.rels");
|
||||||
expect(fileNames).to.include("word/_rels/document.xml.rels");
|
expect(fileNames).to.include("word/_rels/document.xml.rels");
|
||||||
expect(fileNames).to.include("[Content_Types].xml");
|
expect(fileNames).to.include("[Content_Types].xml");
|
||||||
@ -47,7 +48,7 @@ describe("Compiler", () => {
|
|||||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||||
|
|
||||||
expect(fileNames).is.an.instanceof(Array);
|
expect(fileNames).is.an.instanceof(Array);
|
||||||
expect(fileNames).has.length(25);
|
expect(fileNames).has.length(26);
|
||||||
|
|
||||||
expect(fileNames).to.include("word/header1.xml");
|
expect(fileNames).to.include("word/header1.xml");
|
||||||
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
||||||
|
@ -23,6 +23,7 @@ interface IXmlifyedFileMapping {
|
|||||||
ContentTypes: IXmlifyedFile;
|
ContentTypes: IXmlifyedFile;
|
||||||
AppProperties: IXmlifyedFile;
|
AppProperties: IXmlifyedFile;
|
||||||
FootNotes: IXmlifyedFile;
|
FootNotes: IXmlifyedFile;
|
||||||
|
Settings: IXmlifyedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Compiler {
|
export class Compiler {
|
||||||
@ -62,6 +63,7 @@ export class Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private xmlifyFile(file: File): IXmlifyedFileMapping {
|
private xmlifyFile(file: File): IXmlifyedFileMapping {
|
||||||
|
file.verifyUpdateFields();
|
||||||
return {
|
return {
|
||||||
Document: {
|
Document: {
|
||||||
data: xml(this.formatter.format(file.Document), true),
|
data: xml(this.formatter.format(file.Document), true),
|
||||||
@ -120,6 +122,10 @@ export class Compiler {
|
|||||||
data: xml(this.formatter.format(file.FootNotes)),
|
data: xml(this.formatter.format(file.FootNotes)),
|
||||||
path: "word/footnotes.xml",
|
path: "word/footnotes.xml",
|
||||||
},
|
},
|
||||||
|
Settings: {
|
||||||
|
data: xml(this.formatter.format(file.Settings)),
|
||||||
|
path: "word/settings.xml",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
||||||
import { Paragraph, ParagraphProperties } 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 {
|
||||||
@ -53,6 +53,14 @@ export class Body extends XmlComponent {
|
|||||||
return this.defaultSection;
|
return this.defaultSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTablesOfContents(): TableOfContents[] {
|
||||||
|
return this.root.filter((child) => child instanceof TableOfContents) as TableOfContents[];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getParagraphs(): Paragraph[] {
|
||||||
|
return this.root.filter((child) => child instanceof Paragraph) as Paragraph[];
|
||||||
|
}
|
||||||
|
|
||||||
private createSectionParagraph(section: SectionProperties): Paragraph {
|
private createSectionParagraph(section: SectionProperties): Paragraph {
|
||||||
const paragraph = new Paragraph();
|
const paragraph = new Paragraph();
|
||||||
const properties = new ParagraphProperties();
|
const properties = new ParagraphProperties();
|
||||||
|
@ -8,6 +8,7 @@ export interface IPageMarginAttributes {
|
|||||||
header?: number;
|
header?: number;
|
||||||
footer?: number;
|
footer?: number;
|
||||||
gutter?: number;
|
gutter?: number;
|
||||||
|
mirror?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PageMarginAttributes extends XmlAttributeComponent<IPageMarginAttributes> {
|
export class PageMarginAttributes extends XmlAttributeComponent<IPageMarginAttributes> {
|
||||||
@ -19,5 +20,6 @@ export class PageMarginAttributes extends XmlAttributeComponent<IPageMarginAttri
|
|||||||
header: "w:header",
|
header: "w:header",
|
||||||
footer: "w:footer",
|
footer: "w:footer",
|
||||||
gutter: "w:gutter",
|
gutter: "w:gutter",
|
||||||
|
mirror: "w:mirrorMargins",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
|
|||||||
import { PageMarginAttributes } from "./page-margin-attributes";
|
import { PageMarginAttributes } from "./page-margin-attributes";
|
||||||
|
|
||||||
export class PageMargin extends XmlComponent {
|
export class PageMargin extends XmlComponent {
|
||||||
constructor(top: number, right: number, bottom: number, left: number, header: number, footer: number, gutter: number) {
|
constructor(top: number, right: number, bottom: number, left: number, header: number, footer: number, gutter: number, mirror: boolean) {
|
||||||
super("w:pgMar");
|
super("w:pgMar");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new PageMarginAttributes({
|
new PageMarginAttributes({
|
||||||
@ -13,6 +13,7 @@ export class PageMargin extends XmlComponent {
|
|||||||
header: header,
|
header: header,
|
||||||
footer: footer,
|
footer: footer,
|
||||||
gutter: gutter,
|
gutter: gutter,
|
||||||
|
mirror: mirror,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ describe("SectionProperties", () => {
|
|||||||
header: 708,
|
header: 708,
|
||||||
footer: 708,
|
footer: 708,
|
||||||
gutter: 0,
|
gutter: 0,
|
||||||
|
mirror: false,
|
||||||
space: 708,
|
space: 708,
|
||||||
linePitch: 360,
|
linePitch: 360,
|
||||||
headerId: 100,
|
headerId: 100,
|
||||||
@ -40,6 +41,7 @@ describe("SectionProperties", () => {
|
|||||||
"w:left": 1440,
|
"w:left": 1440,
|
||||||
"w:header": 708,
|
"w:header": 708,
|
||||||
"w:gutter": 0,
|
"w:gutter": 0,
|
||||||
|
"w:mirrorMargins": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -69,6 +71,7 @@ describe("SectionProperties", () => {
|
|||||||
"w:left": 1440,
|
"w:left": 1440,
|
||||||
"w:header": 708,
|
"w:header": 708,
|
||||||
"w:gutter": 0,
|
"w:gutter": 0,
|
||||||
|
"w:mirrorMargins": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -99,6 +102,7 @@ describe("SectionProperties", () => {
|
|||||||
"w:left": 1440,
|
"w:left": 1440,
|
||||||
"w:header": 708,
|
"w:header": 708,
|
||||||
"w:gutter": 0,
|
"w:gutter": 0,
|
||||||
|
"w:mirrorMargins": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -124,6 +128,7 @@ describe("SectionProperties", () => {
|
|||||||
"w:left": 1440,
|
"w:left": 1440,
|
||||||
"w:header": 708,
|
"w:header": 708,
|
||||||
"w:gutter": 0,
|
"w:gutter": 0,
|
||||||
|
"w:mirrorMargins": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -150,6 +155,7 @@ describe("SectionProperties", () => {
|
|||||||
"w:left": 1440,
|
"w:left": 1440,
|
||||||
"w:header": 708,
|
"w:header": 708,
|
||||||
"w:gutter": 0,
|
"w:gutter": 0,
|
||||||
|
"w:mirrorMargins": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -38,6 +38,7 @@ export class SectionProperties extends XmlComponent {
|
|||||||
header: 708,
|
header: 708,
|
||||||
footer: 708,
|
footer: 708,
|
||||||
gutter: 0,
|
gutter: 0,
|
||||||
|
mirror: false,
|
||||||
space: 708,
|
space: 708,
|
||||||
linePitch: 360,
|
linePitch: 360,
|
||||||
orientation: PageOrientation.PORTRAIT,
|
orientation: PageOrientation.PORTRAIT,
|
||||||
@ -69,6 +70,7 @@ export class SectionProperties extends XmlComponent {
|
|||||||
mergedOptions.header,
|
mergedOptions.header,
|
||||||
mergedOptions.footer,
|
mergedOptions.footer,
|
||||||
mergedOptions.gutter,
|
mergedOptions.gutter,
|
||||||
|
mergedOptions.mirror,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
this.root.push(new Columns(mergedOptions.space));
|
this.root.push(new Columns(mergedOptions.space));
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
import { Paragraph } from "../paragraph";
|
import { Paragraph } from "../paragraph";
|
||||||
import { Table } from "../table";
|
import { Table } from "../table";
|
||||||
|
import { TableOfContents } from "../table-of-contents";
|
||||||
import { Body } from "./body";
|
import { Body } from "./body";
|
||||||
import { SectionPropertiesOptions } from "./body/section-properties";
|
import { SectionPropertiesOptions } from "./body/section-properties";
|
||||||
import { DocumentAttributes } from "./document-attributes";
|
import { DocumentAttributes } from "./document-attributes";
|
||||||
@ -41,6 +42,11 @@ export class Document extends XmlComponent {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addTableOfContents(toc: TableOfContents): Document {
|
||||||
|
this.body.push(toc);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public createParagraph(text?: string): Paragraph {
|
public createParagraph(text?: string): Paragraph {
|
||||||
const para = new Paragraph(text);
|
const para = new Paragraph(text);
|
||||||
this.addParagraph(para);
|
this.addParagraph(para);
|
||||||
@ -60,4 +66,12 @@ export class Document extends XmlComponent {
|
|||||||
public get Body(): Body {
|
public get Body(): Body {
|
||||||
return this.body;
|
return this.body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTablesOfContents(): TableOfContents[] {
|
||||||
|
return this.body.getTablesOfContents();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getParagraphs(): Paragraph[] {
|
||||||
|
return this.body.getParagraphs();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
export * from "./document";
|
export * from "./document";
|
||||||
|
export * from "./document-attributes";
|
||||||
export * from "./body";
|
export * from "./body";
|
||||||
|
@ -10,14 +10,16 @@ import { Image, Media } from "./media";
|
|||||||
import { Numbering } from "./numbering";
|
import { Numbering } from "./numbering";
|
||||||
import { Bookmark, Hyperlink, Paragraph } from "./paragraph";
|
import { Bookmark, Hyperlink, Paragraph } from "./paragraph";
|
||||||
import { Relationships } from "./relationships";
|
import { Relationships } from "./relationships";
|
||||||
|
import { Settings } from "./settings";
|
||||||
import { Styles } from "./styles";
|
import { Styles } from "./styles";
|
||||||
import { ExternalStylesFactory } from "./styles/external-styles-factory";
|
import { ExternalStylesFactory } from "./styles/external-styles-factory";
|
||||||
import { DefaultStylesFactory } from "./styles/factory";
|
import { DefaultStylesFactory } from "./styles/factory";
|
||||||
import { Table } from "./table";
|
import { Table } from "./table";
|
||||||
|
import { TableOfContents } from "./table-of-contents";
|
||||||
|
|
||||||
export class File {
|
export class File {
|
||||||
private readonly document: Document;
|
private readonly document: Document;
|
||||||
private readonly styles: Styles;
|
private styles: Styles;
|
||||||
private readonly coreProperties: CoreProperties;
|
private readonly coreProperties: CoreProperties;
|
||||||
private readonly numbering: Numbering;
|
private readonly numbering: Numbering;
|
||||||
private readonly media: Media;
|
private readonly media: Media;
|
||||||
@ -26,6 +28,7 @@ export class File {
|
|||||||
private readonly headerWrapper: HeaderWrapper[] = [];
|
private readonly headerWrapper: HeaderWrapper[] = [];
|
||||||
private readonly footerWrapper: FooterWrapper[] = [];
|
private readonly footerWrapper: FooterWrapper[] = [];
|
||||||
private readonly footNotes: FootNotes;
|
private readonly footNotes: FootNotes;
|
||||||
|
private readonly settings: Settings;
|
||||||
|
|
||||||
private readonly contentTypes: ContentTypes;
|
private readonly contentTypes: ContentTypes;
|
||||||
private readonly appProperties: AppProperties;
|
private readonly appProperties: AppProperties;
|
||||||
@ -105,6 +108,11 @@ export class File {
|
|||||||
sectionPropertiesOptions.footerId = footer.Footer.ReferenceId;
|
sectionPropertiesOptions.footerId = footer.Footer.ReferenceId;
|
||||||
}
|
}
|
||||||
this.document = new Document(sectionPropertiesOptions);
|
this.document = new Document(sectionPropertiesOptions);
|
||||||
|
this.settings = new Settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public addTableOfContents(toc: TableOfContents): void {
|
||||||
|
this.document.addTableOfContents(toc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addParagraph(paragraph: Paragraph): void {
|
public addParagraph(paragraph: Paragraph): void {
|
||||||
@ -214,6 +222,10 @@ 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;
|
||||||
}
|
}
|
||||||
@ -277,4 +289,14 @@ export class File {
|
|||||||
public get FootNotes(): FootNotes {
|
public get FootNotes(): FootNotes {
|
||||||
return this.footNotes;
|
return this.footNotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get Settings(): Settings {
|
||||||
|
return this.settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public verifyUpdateFields(): void {
|
||||||
|
if (this.document.getTablesOfContents().length) {
|
||||||
|
this.settings.addUpdateFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,5 @@ export * from "./media";
|
|||||||
export * from "./drawing";
|
export * from "./drawing";
|
||||||
export * from "./document";
|
export * from "./document";
|
||||||
export * from "./styles";
|
export * from "./styles";
|
||||||
|
export * from "./table-of-contents";
|
||||||
export * from "./xml-components";
|
export * from "./xml-components";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// http://officeopenxml.com/WPspacing.php
|
// http://officeopenxml.com/WPspacing.php
|
||||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
import { Attributes, XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
export interface ISpacingProperties {
|
export interface ISpacingProperties {
|
||||||
after?: number;
|
after?: number;
|
||||||
@ -23,3 +23,14 @@ export class Spacing extends XmlComponent {
|
|||||||
this.root.push(new SpacingAttributes(opts));
|
this.root.push(new SpacingAttributes(opts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ContextualSpacing extends XmlComponent {
|
||||||
|
constructor(value: boolean) {
|
||||||
|
super("w:contextualSpacing");
|
||||||
|
this.root.push(
|
||||||
|
new Attributes({
|
||||||
|
val: value === false ? 0 : 1,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import { Attributes, XmlComponent } from "file/xml-components";
|
import { Attributes, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
export class Style extends XmlComponent {
|
export class Style extends XmlComponent {
|
||||||
constructor(type: string) {
|
public readonly styleId: string;
|
||||||
|
|
||||||
|
constructor(styleId: string) {
|
||||||
super("w:pStyle");
|
super("w:pStyle");
|
||||||
|
this.styleId = styleId;
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new Attributes({
|
new Attributes({
|
||||||
val: type,
|
val: styleId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { assert } from "chai";
|
import { assert } from "chai";
|
||||||
|
|
||||||
import { Utility } from "../../../tests/utility";
|
import { Utility } from "../../../tests/utility";
|
||||||
import { LeftTabStop, MaxRightTabStop } from "./tab-stop";
|
import { LeftTabStop, MaxRightTabStop, RightTabStop } from "./tab-stop";
|
||||||
|
|
||||||
describe("LeftTabStop", () => {
|
describe("LeftTabStop", () => {
|
||||||
let tabStop: LeftTabStop;
|
let tabStop: LeftTabStop;
|
||||||
@ -28,7 +28,28 @@ describe("LeftTabStop", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("RightTabStop", () => {
|
describe("RightTabStop", () => {
|
||||||
// TODO
|
let tabStop: RightTabStop;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tabStop = new RightTabStop(100, "dot");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a Tab Stop with correct attributes", () => {
|
||||||
|
const newJson = Utility.jsonify(tabStop);
|
||||||
|
const attributes = {
|
||||||
|
val: "right",
|
||||||
|
pos: 100,
|
||||||
|
leader: "dot",
|
||||||
|
};
|
||||||
|
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");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("MaxRightTabStop", () => {
|
describe("MaxRightTabStop", () => {
|
||||||
|
@ -2,50 +2,52 @@
|
|||||||
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: Tab) {
|
constructor(tab: TabStopItem) {
|
||||||
super("w:tabs");
|
super("w:tabs");
|
||||||
this.root.push(tab);
|
this.root.push(tab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TabValue = "left" | "right" | "center" | "bar" | "clear" | "decimal" | "end" | "num" | "start";
|
export type TabValue = "left" | "right" | "center" | "bar" | "clear" | "decimal" | "end" | "num" | "start";
|
||||||
|
export type LeaderType = "dot" | "hyphen" | "middleDot" | "none" | "underscore";
|
||||||
|
|
||||||
export class TabAttributes extends XmlAttributeComponent<{ val: TabValue; pos: string | number }> {
|
export class TabAttributes extends XmlAttributeComponent<{ val: TabValue; pos: string | number; leader?: LeaderType }> {
|
||||||
protected xmlKeys = { val: "w:val", pos: "w:pos" };
|
protected xmlKeys = { val: "w:val", pos: "w:pos", leader: "w:leader" };
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Tab extends XmlComponent {
|
export class TabStopItem extends XmlComponent {
|
||||||
constructor(value: TabValue, position: string | number) {
|
constructor(value: TabValue, position: string | number, leader?: LeaderType) {
|
||||||
super("w:tab");
|
super("w:tab");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new TabAttributes({
|
new TabAttributes({
|
||||||
val: value,
|
val: value,
|
||||||
pos: position,
|
pos: position,
|
||||||
|
leader,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MaxRightTabStop extends TabStop {
|
export class MaxRightTabStop extends TabStop {
|
||||||
constructor() {
|
constructor(leader?: LeaderType) {
|
||||||
super(new Tab("right", 9026));
|
super(new TabStopItem("right", 9026, leader));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LeftTabStop extends TabStop {
|
export class LeftTabStop extends TabStop {
|
||||||
constructor(position: number) {
|
constructor(position: number, leader?: LeaderType) {
|
||||||
super(new Tab("left", position));
|
super(new TabStopItem("left", position, leader));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RightTabStop extends TabStop {
|
export class RightTabStop extends TabStop {
|
||||||
constructor(position: number) {
|
constructor(position: number, leader?: LeaderType) {
|
||||||
super(new Tab("right", position));
|
super(new TabStopItem("right", position, leader));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CenterTabStop extends TabStop {
|
export class CenterTabStop extends TabStop {
|
||||||
constructor(position: number) {
|
constructor(position: number, leader?: LeaderType) {
|
||||||
super(new Tab("center", position));
|
super(new TabStopItem("center", position, leader));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,13 +10,13 @@ import { Border, ThematicBreak } from "./formatting/border";
|
|||||||
import { IIndentAttributesProperties, Indent } from "./formatting/indent";
|
import { IIndentAttributesProperties, Indent } from "./formatting/indent";
|
||||||
import { KeepLines, KeepNext } from "./formatting/keep";
|
import { KeepLines, KeepNext } from "./formatting/keep";
|
||||||
import { PageBreak, PageBreakBefore } from "./formatting/page-break";
|
import { PageBreak, PageBreakBefore } from "./formatting/page-break";
|
||||||
import { ISpacingProperties, Spacing } from "./formatting/spacing";
|
import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spacing";
|
||||||
import { Style } from "./formatting/style";
|
import { Style } from "./formatting/style";
|
||||||
import { CenterTabStop, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop";
|
import { CenterTabStop, LeaderType, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop";
|
||||||
import { NumberProperties } from "./formatting/unordered-list";
|
import { NumberProperties } from "./formatting/unordered-list";
|
||||||
import { Bookmark, Hyperlink } from "./links";
|
import { Bookmark, Hyperlink } from "./links";
|
||||||
import { ParagraphProperties } from "./properties";
|
import { ParagraphProperties } from "./properties";
|
||||||
import { PictureRun, Run, TextRun } from "./run";
|
import { PictureRun, Run, SequentialIdentifier, TextRun } from "./run";
|
||||||
|
|
||||||
export class Paragraph extends XmlComponent {
|
export class Paragraph extends XmlComponent {
|
||||||
private readonly properties: ParagraphProperties;
|
private readonly properties: ParagraphProperties;
|
||||||
@ -30,6 +30,10 @@ export class Paragraph extends XmlComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get paragraphProperties(): ParagraphProperties {
|
||||||
|
return this.properties;
|
||||||
|
}
|
||||||
|
|
||||||
public get Borders(): Border {
|
public get Borders(): Border {
|
||||||
return this.properties.paragraphBorder;
|
return this.properties.paragraphBorder;
|
||||||
}
|
}
|
||||||
@ -155,23 +159,23 @@ export class Paragraph extends XmlComponent {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public maxRightTabStop(): Paragraph {
|
public maxRightTabStop(leader?: LeaderType): Paragraph {
|
||||||
this.properties.push(new MaxRightTabStop());
|
this.properties.push(new MaxRightTabStop(leader));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public leftTabStop(position: number): Paragraph {
|
public leftTabStop(position: number, leader?: LeaderType): Paragraph {
|
||||||
this.properties.push(new LeftTabStop(position));
|
this.properties.push(new LeftTabStop(position, leader));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public rightTabStop(position: number): Paragraph {
|
public rightTabStop(position: number, leader?: LeaderType): Paragraph {
|
||||||
this.properties.push(new RightTabStop(position));
|
this.properties.push(new RightTabStop(position, leader));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public centerTabStop(position: number): Paragraph {
|
public centerTabStop(position: number, leader?: LeaderType): Paragraph {
|
||||||
this.properties.push(new CenterTabStop(position));
|
this.properties.push(new CenterTabStop(position, leader));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,6 +211,11 @@ export class Paragraph extends XmlComponent {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public contextualSpacing(value: boolean): Paragraph {
|
||||||
|
this.properties.push(new ContextualSpacing(value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public keepNext(): Paragraph {
|
public keepNext(): Paragraph {
|
||||||
this.properties.push(new KeepNext());
|
this.properties.push(new KeepNext());
|
||||||
return this;
|
return this;
|
||||||
@ -232,7 +241,13 @@ export class Paragraph extends XmlComponent {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get Properties(): ParagraphProperties {
|
public addTabStop(run: Run): Paragraph {
|
||||||
return this.properties;
|
this.root.splice(1, 0, run);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addSequentialIdentifier(identifier: string): Paragraph {
|
||||||
|
this.root.push(new SequentialIdentifier(identifier));
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
src/file/paragraph/run/field.ts
Normal file
26
src/file/paragraph/run/field.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
class FidCharAttrs extends XmlAttributeComponent<{ type: "begin" | "end" | "separate"; dirty?: boolean }> {
|
||||||
|
protected xmlKeys = { type: "w:fldCharType", dirty: "w:dirty" };
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Begin extends XmlComponent {
|
||||||
|
constructor(dirty?: boolean) {
|
||||||
|
super("w:fldChar");
|
||||||
|
this.root.push(new FidCharAttrs({ type: "begin", dirty }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Separate extends XmlComponent {
|
||||||
|
constructor(dirty?: boolean) {
|
||||||
|
super("w:fldChar");
|
||||||
|
this.root.push(new FidCharAttrs({ type: "separate", dirty }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class End extends XmlComponent {
|
||||||
|
constructor(dirty?: boolean) {
|
||||||
|
super("w:fldChar");
|
||||||
|
this.root.push(new FidCharAttrs({ type: "end", dirty }));
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,17 @@ export class BoldComplexScript extends XmlComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CharacterSpacing extends XmlComponent {
|
||||||
|
constructor(value: number) {
|
||||||
|
super("w:spacing");
|
||||||
|
this.root.push(
|
||||||
|
new Attributes({
|
||||||
|
val: value,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Italics extends XmlComponent {
|
export class Italics extends XmlComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("w:i");
|
super("w:i");
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
export * from "./run";
|
export * from "./run";
|
||||||
export * from "./text-run";
|
export * from "./text-run";
|
||||||
export * from "./picture-run";
|
export * from "./picture-run";
|
||||||
|
export * from "./sequential-identifier";
|
||||||
|
@ -1,20 +1,9 @@
|
|||||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
class FidCharAttrs extends XmlAttributeComponent<{ type: "begin" | "end" | "separate" }> {
|
|
||||||
protected xmlKeys = { type: "w:fldCharType" };
|
|
||||||
}
|
|
||||||
|
|
||||||
class TextAttributes extends XmlAttributeComponent<{ space: "default" | "preserve" }> {
|
class TextAttributes extends XmlAttributeComponent<{ space: "default" | "preserve" }> {
|
||||||
protected xmlKeys = { space: "xml:space" };
|
protected xmlKeys = { space: "xml:space" };
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Begin extends XmlComponent {
|
|
||||||
constructor() {
|
|
||||||
super("w:fldChar");
|
|
||||||
this.root.push(new FidCharAttrs({ type: "begin" }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Page extends XmlComponent {
|
export class Page extends XmlComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("w:instrText");
|
super("w:instrText");
|
||||||
@ -22,17 +11,3 @@ export class Page extends XmlComponent {
|
|||||||
this.root.push("PAGE");
|
this.root.push("PAGE");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Separate extends XmlComponent {
|
|
||||||
constructor() {
|
|
||||||
super("w:fldChar");
|
|
||||||
this.root.push(new FidCharAttrs({ type: "separate" }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class End extends XmlComponent {
|
|
||||||
constructor() {
|
|
||||||
super("w:fldChar");
|
|
||||||
this.root.push(new FidCharAttrs({ type: "end" }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// http://officeopenxml.com/WPtext.php
|
// http://officeopenxml.com/WPtext.php
|
||||||
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 {
|
import {
|
||||||
Bold,
|
Bold,
|
||||||
BoldComplexScript,
|
BoldComplexScript,
|
||||||
@ -13,7 +14,7 @@ import {
|
|||||||
SizeComplexScript,
|
SizeComplexScript,
|
||||||
Strike,
|
Strike,
|
||||||
} from "./formatting";
|
} from "./formatting";
|
||||||
import { Begin, End, Page, Separate } from "./page-number";
|
import { Page } from "./page-number";
|
||||||
import { RunProperties } from "./properties";
|
import { RunProperties } from "./properties";
|
||||||
import { RunFonts } from "./run-fonts";
|
import { RunFonts } from "./run-fonts";
|
||||||
import { SubScript, SuperScript } from "./script";
|
import { SubScript, SuperScript } from "./script";
|
||||||
|
19
src/file/paragraph/run/sequential-identifier-instruction.ts
Normal file
19
src/file/paragraph/run/sequential-identifier-instruction.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// http://officeopenxml.com/WPfieldInstructions.php
|
||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
enum SpaceType {
|
||||||
|
DEFAULT = "default",
|
||||||
|
PRESERVE = "preserve",
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextAttributes extends XmlAttributeComponent<{ space: SpaceType }> {
|
||||||
|
protected xmlKeys = { space: "xml:space" };
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SequentialIdentifierInstruction extends XmlComponent {
|
||||||
|
constructor(identifier: string) {
|
||||||
|
super("w:instrText");
|
||||||
|
this.root.push(new TextAttributes({ space: SpaceType.PRESERVE }));
|
||||||
|
this.root.push(`SEQ ${identifier}`);
|
||||||
|
}
|
||||||
|
}
|
60
src/file/paragraph/run/sequential-identifier.spec.ts
Normal file
60
src/file/paragraph/run/sequential-identifier.spec.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "../../../export/formatter";
|
||||||
|
import { SequentialIdentifier } from "./sequential-identifier";
|
||||||
|
|
||||||
|
describe("Sequential Identifier", () => {
|
||||||
|
describe("#constructor", () => {
|
||||||
|
it("should construct a SEQ without options", () => {
|
||||||
|
const seq = new SequentialIdentifier("Figure");
|
||||||
|
const tree = new Formatter().format(seq);
|
||||||
|
expect(tree).to.be.deep.equal(DEFAULT_SEQ);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const DEFAULT_SEQ = {
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:fldChar": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:fldCharType": "begin",
|
||||||
|
"w:dirty": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:instrText": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"xml:space": "preserve",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"SEQ Figure",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:fldChar": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:fldCharType": "separate",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:fldChar": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:fldCharType": "end",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
13
src/file/paragraph/run/sequential-identifier.ts
Normal file
13
src/file/paragraph/run/sequential-identifier.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Run } from "file/paragraph/run";
|
||||||
|
import { Begin, End, Separate } from "file/paragraph/run/field";
|
||||||
|
import { SequentialIdentifierInstruction } from "./sequential-identifier-instruction";
|
||||||
|
|
||||||
|
export class SequentialIdentifier extends Run {
|
||||||
|
constructor(identifier: string) {
|
||||||
|
super();
|
||||||
|
this.root.push(new Begin(true));
|
||||||
|
this.root.push(new SequentialIdentifierInstruction(identifier));
|
||||||
|
this.root.push(new Separate());
|
||||||
|
this.root.push(new End());
|
||||||
|
}
|
||||||
|
}
|
2
src/file/settings/index.ts
Normal file
2
src/file/settings/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./settings";
|
||||||
|
export * from "./update-fields";
|
60
src/file/settings/settings.spec.ts
Normal file
60
src/file/settings/settings.spec.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
import { Formatter } from "../../export/formatter";
|
||||||
|
import { Settings } from "./";
|
||||||
|
describe("Settings", () => {
|
||||||
|
describe("#constructor", () => {
|
||||||
|
it("should create a empty Settings with correct rootKey", () => {
|
||||||
|
const settings = new Settings();
|
||||||
|
const tree = new Formatter().format(settings);
|
||||||
|
let keys = Object.keys(tree);
|
||||||
|
expect(keys).is.an.instanceof(Array);
|
||||||
|
expect(keys).has.length(1);
|
||||||
|
expect(keys[0]).to.be.equal("w:settings");
|
||||||
|
expect(tree["w:settings"]).is.an.instanceof(Array);
|
||||||
|
expect(tree["w:settings"]).has.length(1);
|
||||||
|
keys = Object.keys(tree["w:settings"][0]);
|
||||||
|
expect(keys).is.an.instanceof(Array);
|
||||||
|
expect(keys).has.length(1);
|
||||||
|
expect(keys[0]).to.be.equal("_attr");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("#addUpdateFields", () => {
|
||||||
|
const assertSettingsWithUpdateFields = (settings: Settings) => {
|
||||||
|
const tree = new Formatter().format(settings);
|
||||||
|
let keys = Object.keys(tree);
|
||||||
|
expect(keys).is.an.instanceof(Array);
|
||||||
|
expect(keys).has.length(1);
|
||||||
|
expect(keys[0]).to.be.equal("w:settings");
|
||||||
|
const rootArray = tree["w:settings"];
|
||||||
|
expect(rootArray).is.an.instanceof(Array);
|
||||||
|
expect(rootArray).has.length(2);
|
||||||
|
keys = Object.keys(rootArray[0]);
|
||||||
|
expect(keys).is.an.instanceof(Array);
|
||||||
|
expect(keys).has.length(1);
|
||||||
|
expect(keys[0]).to.be.equal("_attr");
|
||||||
|
keys = Object.keys(rootArray[1]);
|
||||||
|
expect(keys).is.an.instanceof(Array);
|
||||||
|
expect(keys).has.length(1);
|
||||||
|
expect(keys[0]).to.be.equal("w:updateFields");
|
||||||
|
const updateFieldsArray = rootArray[1]["w:updateFields"];
|
||||||
|
keys = Object.keys(updateFieldsArray[0]);
|
||||||
|
expect(keys).is.an.instanceof(Array);
|
||||||
|
expect(keys).has.length(1);
|
||||||
|
expect(keys[0]).to.be.equal("_attr");
|
||||||
|
const updateFieldsAttr = updateFieldsArray[0]._attr;
|
||||||
|
expect(updateFieldsAttr["w:val"]).to.be.equal(true);
|
||||||
|
};
|
||||||
|
it("should add a UpdateFields with value true", () => {
|
||||||
|
const settings = new Settings();
|
||||||
|
settings.addUpdateFields();
|
||||||
|
assertSettingsWithUpdateFields(settings);
|
||||||
|
});
|
||||||
|
it("should add a UpdateFields with value true only once", () => {
|
||||||
|
const settings = new Settings();
|
||||||
|
settings.addUpdateFields();
|
||||||
|
assertSettingsWithUpdateFields(settings);
|
||||||
|
settings.addUpdateFields();
|
||||||
|
assertSettingsWithUpdateFields(settings);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
73
src/file/settings/settings.ts
Normal file
73
src/file/settings/settings.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
import { UpdateFields } from "./update-fields";
|
||||||
|
export interface ISettingsAttributesProperties {
|
||||||
|
wpc?: string;
|
||||||
|
mc?: string;
|
||||||
|
o?: string;
|
||||||
|
r?: string;
|
||||||
|
m?: string;
|
||||||
|
v?: string;
|
||||||
|
wp14?: string;
|
||||||
|
wp?: string;
|
||||||
|
w10?: string;
|
||||||
|
w?: string;
|
||||||
|
w14?: string;
|
||||||
|
w15?: string;
|
||||||
|
wpg?: string;
|
||||||
|
wpi?: string;
|
||||||
|
wne?: string;
|
||||||
|
wps?: string;
|
||||||
|
Ignorable?: string;
|
||||||
|
}
|
||||||
|
export class SettingsAttributes extends XmlAttributeComponent<ISettingsAttributesProperties> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
wpc: "xmlns:wpc",
|
||||||
|
mc: "xmlns:mc",
|
||||||
|
o: "xmlns:o",
|
||||||
|
r: "xmlns:r",
|
||||||
|
m: "xmlns:m",
|
||||||
|
v: "xmlns:v",
|
||||||
|
wp14: "xmlns:wp14",
|
||||||
|
wp: "xmlns:wp",
|
||||||
|
w10: "xmlns:w10",
|
||||||
|
w: "xmlns:w",
|
||||||
|
w14: "xmlns:w14",
|
||||||
|
w15: "xmlns:w15",
|
||||||
|
wpg: "xmlns:wpg",
|
||||||
|
wpi: "xmlns:wpi",
|
||||||
|
wne: "xmlns:wne",
|
||||||
|
wps: "xmlns:wps",
|
||||||
|
Ignorable: "mc:Ignorable",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export class Settings extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("w:settings");
|
||||||
|
this.root.push(
|
||||||
|
new SettingsAttributes({
|
||||||
|
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
||||||
|
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
||||||
|
o: "urn:schemas-microsoft-com:office:office",
|
||||||
|
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||||
|
m: "http://schemas.openxmlformats.org/officeDocument/2006/math",
|
||||||
|
v: "urn:schemas-microsoft-com:vml",
|
||||||
|
wp14: "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
|
||||||
|
wp: "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||||
|
w10: "urn:schemas-microsoft-com:office:word",
|
||||||
|
w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||||
|
w14: "http://schemas.microsoft.com/office/word/2010/wordml",
|
||||||
|
w15: "http://schemas.microsoft.com/office/word/2012/wordml",
|
||||||
|
wpg: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
|
||||||
|
wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
|
||||||
|
wne: "http://schemas.microsoft.com/office/word/2006/wordml",
|
||||||
|
wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
||||||
|
Ignorable: "w14 w15 wp14",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public addUpdateFields(): void {
|
||||||
|
if (!this.root.find((child) => child instanceof UpdateFields)) {
|
||||||
|
this.addChildElement(new UpdateFields());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
src/file/settings/update-fields.spec.ts
Normal file
40
src/file/settings/update-fields.spec.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
import { Formatter } from "../../export/formatter";
|
||||||
|
import { UpdateFields } from "./";
|
||||||
|
const UF_TRUE = {
|
||||||
|
"w:updateFields": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:val": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const UF_FALSE = {
|
||||||
|
"w:updateFields": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:val": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
describe("Update Fields", () => {
|
||||||
|
describe("#constructor", () => {
|
||||||
|
it("should construct a Update Fields with TRUE value by default", () => {
|
||||||
|
const uf = new UpdateFields();
|
||||||
|
const tree = new Formatter().format(uf);
|
||||||
|
expect(tree).to.be.deep.equal(UF_TRUE);
|
||||||
|
});
|
||||||
|
it("should construct a Update Fields with TRUE value", () => {
|
||||||
|
const uf = new UpdateFields(true);
|
||||||
|
const tree = new Formatter().format(uf);
|
||||||
|
expect(tree).to.be.deep.equal(UF_TRUE);
|
||||||
|
});
|
||||||
|
it("should construct a Update Fields with FALSE value", () => {
|
||||||
|
const uf = new UpdateFields(false);
|
||||||
|
const tree = new Formatter().format(uf);
|
||||||
|
expect(tree).to.be.deep.equal(UF_FALSE);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
19
src/file/settings/update-fields.ts
Normal file
19
src/file/settings/update-fields.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
export interface IUpdateFieldsAttributesProperties {
|
||||||
|
enabled: boolean;
|
||||||
|
}
|
||||||
|
export class UpdateFieldsAttributes extends XmlAttributeComponent<IUpdateFieldsAttributesProperties> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
enabled: "w:val",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export class UpdateFields extends XmlComponent {
|
||||||
|
constructor(enabled: boolean = true) {
|
||||||
|
super("w:updateFields");
|
||||||
|
this.root.push(
|
||||||
|
new UpdateFieldsAttributes({
|
||||||
|
enabled,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -47,12 +47,14 @@ export class ParagraphStyle extends Style {
|
|||||||
this.root.push(this.runProperties);
|
this.root.push(this.runProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addParagraphProperty(property: XmlComponent): void {
|
public addParagraphProperty(property: XmlComponent): ParagraphStyle {
|
||||||
this.paragraphProperties.push(property);
|
this.paragraphProperties.push(property);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public addRunProperty(property: XmlComponent): void {
|
public addRunProperty(property: XmlComponent): ParagraphStyle {
|
||||||
this.runProperties.push(property);
|
this.runProperties.push(property);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public basedOn(parentId: string): ParagraphStyle {
|
public basedOn(parentId: string): ParagraphStyle {
|
||||||
@ -73,121 +75,101 @@ export class ParagraphStyle extends Style {
|
|||||||
// ---------- Run formatting ---------------------- //
|
// ---------- Run formatting ---------------------- //
|
||||||
|
|
||||||
public size(twips: number): ParagraphStyle {
|
public size(twips: number): ParagraphStyle {
|
||||||
this.addRunProperty(new formatting.Size(twips));
|
return this.addRunProperty(new formatting.Size(twips)).addRunProperty(new formatting.SizeComplexScript(twips));
|
||||||
this.addRunProperty(new formatting.SizeComplexScript(twips));
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bold(): ParagraphStyle {
|
public bold(): ParagraphStyle {
|
||||||
this.addRunProperty(new formatting.Bold());
|
return this.addRunProperty(new formatting.Bold());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public italics(): ParagraphStyle {
|
public italics(): ParagraphStyle {
|
||||||
this.addRunProperty(new formatting.Italics());
|
return this.addRunProperty(new formatting.Italics());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public smallCaps(): ParagraphStyle {
|
public smallCaps(): ParagraphStyle {
|
||||||
this.addRunProperty(new formatting.SmallCaps());
|
return this.addRunProperty(new formatting.SmallCaps());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public allCaps(): ParagraphStyle {
|
public allCaps(): ParagraphStyle {
|
||||||
this.addRunProperty(new formatting.Caps());
|
return this.addRunProperty(new formatting.Caps());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public strike(): ParagraphStyle {
|
public strike(): ParagraphStyle {
|
||||||
this.addRunProperty(new formatting.Strike());
|
return this.addRunProperty(new formatting.Strike());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public doubleStrike(): ParagraphStyle {
|
public doubleStrike(): ParagraphStyle {
|
||||||
this.addRunProperty(new formatting.DoubleStrike());
|
return this.addRunProperty(new formatting.DoubleStrike());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public subScript(): ParagraphStyle {
|
public subScript(): ParagraphStyle {
|
||||||
this.addRunProperty(new formatting.SubScript());
|
return this.addRunProperty(new formatting.SubScript());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public superScript(): ParagraphStyle {
|
public superScript(): ParagraphStyle {
|
||||||
this.addRunProperty(new formatting.SuperScript());
|
return this.addRunProperty(new formatting.SuperScript());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public underline(underlineType?: string, color?: string): ParagraphStyle {
|
public underline(underlineType?: string, color?: string): ParagraphStyle {
|
||||||
this.addRunProperty(new formatting.Underline(underlineType, color));
|
return this.addRunProperty(new formatting.Underline(underlineType, color));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public color(color: string): ParagraphStyle {
|
public color(color: string): ParagraphStyle {
|
||||||
this.addRunProperty(new formatting.Color(color));
|
return this.addRunProperty(new formatting.Color(color));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public font(fontName: string): ParagraphStyle {
|
public font(fontName: string): ParagraphStyle {
|
||||||
this.addRunProperty(new formatting.RunFonts(fontName));
|
return this.addRunProperty(new formatting.RunFonts(fontName));
|
||||||
return this;
|
}
|
||||||
|
|
||||||
|
public characterSpacing(value: number): ParagraphStyle {
|
||||||
|
return this.addRunProperty(new formatting.CharacterSpacing(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------- Paragraph formatting ------------------------ //
|
// --------------------- Paragraph formatting ------------------------ //
|
||||||
|
|
||||||
public center(): ParagraphStyle {
|
public center(): ParagraphStyle {
|
||||||
this.addParagraphProperty(new paragraph.Alignment("center"));
|
return this.addParagraphProperty(new paragraph.Alignment("center"));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public left(): ParagraphStyle {
|
public left(): ParagraphStyle {
|
||||||
this.addParagraphProperty(new paragraph.Alignment("left"));
|
return this.addParagraphProperty(new paragraph.Alignment("left"));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public right(): ParagraphStyle {
|
public right(): ParagraphStyle {
|
||||||
this.addParagraphProperty(new paragraph.Alignment("right"));
|
return this.addParagraphProperty(new paragraph.Alignment("right"));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public justified(): ParagraphStyle {
|
public justified(): ParagraphStyle {
|
||||||
this.addParagraphProperty(new paragraph.Alignment("both"));
|
return this.addParagraphProperty(new paragraph.Alignment("both"));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public thematicBreak(): ParagraphStyle {
|
public thematicBreak(): ParagraphStyle {
|
||||||
this.addParagraphProperty(new paragraph.ThematicBreak());
|
return this.addParagraphProperty(new paragraph.ThematicBreak());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public maxRightTabStop(): ParagraphStyle {
|
public maxRightTabStop(): ParagraphStyle {
|
||||||
this.addParagraphProperty(new paragraph.MaxRightTabStop());
|
return this.addParagraphProperty(new paragraph.MaxRightTabStop());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public leftTabStop(position: number): ParagraphStyle {
|
public leftTabStop(position: number): ParagraphStyle {
|
||||||
this.addParagraphProperty(new paragraph.LeftTabStop(position));
|
return this.addParagraphProperty(new paragraph.LeftTabStop(position));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public indent(attrs: object): ParagraphStyle {
|
public indent(attrs: object): ParagraphStyle {
|
||||||
this.addParagraphProperty(new paragraph.Indent(attrs));
|
return this.addParagraphProperty(new paragraph.Indent(attrs));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public spacing(params: paragraph.ISpacingProperties): ParagraphStyle {
|
public spacing(params: paragraph.ISpacingProperties): ParagraphStyle {
|
||||||
this.addParagraphProperty(new paragraph.Spacing(params));
|
return this.addParagraphProperty(new paragraph.Spacing(params));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public keepNext(): ParagraphStyle {
|
public keepNext(): ParagraphStyle {
|
||||||
this.addParagraphProperty(new paragraph.KeepNext());
|
return this.addParagraphProperty(new paragraph.KeepNext());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public keepLines(): ParagraphStyle {
|
public keepLines(): ParagraphStyle {
|
||||||
this.addParagraphProperty(new paragraph.KeepLines());
|
return this.addParagraphProperty(new paragraph.KeepLines());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,24 +249,21 @@ export class CharacterStyle extends Style {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public addRunProperty(property: XmlComponent): void {
|
public addRunProperty(property: XmlComponent): CharacterStyle {
|
||||||
this.runProperties.push(property);
|
this.runProperties.push(property);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public color(color: string): CharacterStyle {
|
public color(color: string): CharacterStyle {
|
||||||
this.addRunProperty(new formatting.Color(color));
|
return this.addRunProperty(new formatting.Color(color));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public underline(underlineType?: string, color?: string): CharacterStyle {
|
public underline(underlineType?: string, color?: string): CharacterStyle {
|
||||||
this.addRunProperty(new formatting.Underline(underlineType, color));
|
return this.addRunProperty(new formatting.Underline(underlineType, color));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public size(twips: number): CharacterStyle {
|
public size(twips: number): CharacterStyle {
|
||||||
this.addRunProperty(new formatting.Size(twips));
|
return this.addRunProperty(new formatting.Size(twips)).addRunProperty(new formatting.SizeComplexScript(twips));
|
||||||
this.addRunProperty(new formatting.SizeComplexScript(twips));
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,6 +219,20 @@ describe("ParagraphStyle", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("#character spacing", () => {
|
||||||
|
const style = new ParagraphStyle("myStyleId").characterSpacing(24);
|
||||||
|
const tree = new Formatter().format(style);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:style": [
|
||||||
|
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
|
||||||
|
{ "w:pPr": [] },
|
||||||
|
{
|
||||||
|
"w:rPr": [{ "w:spacing": [{ _attr: { "w:val": 24 } }] }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("#left", () => {
|
it("#left", () => {
|
||||||
const style = new ParagraphStyle("myStyleId").left();
|
const style = new ParagraphStyle("myStyleId").left();
|
||||||
const tree = new Formatter().format(style);
|
const tree = new Formatter().format(style);
|
||||||
|
12
src/file/table-of-contents/alias.ts
Normal file
12
src/file/table-of-contents/alias.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
class AliasAttributes extends XmlAttributeComponent<{ alias: string }> {
|
||||||
|
protected xmlKeys = { alias: "w:val" };
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Alias extends XmlComponent {
|
||||||
|
constructor(alias: string) {
|
||||||
|
super("w:alias");
|
||||||
|
this.root.push(new AliasAttributes({ alias }));
|
||||||
|
}
|
||||||
|
}
|
76
src/file/table-of-contents/field-instruction.ts
Normal file
76
src/file/table-of-contents/field-instruction.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// http://officeopenxml.com/WPfieldInstructions.php
|
||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
import { ITableOfContentsOptions } from "./table-of-contents-properties";
|
||||||
|
|
||||||
|
enum SpaceType {
|
||||||
|
DEFAULT = "default",
|
||||||
|
PRESERVE = "preserve",
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextAttributes extends XmlAttributeComponent<{ space: SpaceType }> {
|
||||||
|
protected xmlKeys = { space: "xml:space" };
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FieldInstruction extends XmlComponent {
|
||||||
|
private readonly properties: ITableOfContentsOptions;
|
||||||
|
|
||||||
|
constructor(properties: ITableOfContentsOptions = {}) {
|
||||||
|
super("w:instrText");
|
||||||
|
|
||||||
|
this.properties = properties;
|
||||||
|
|
||||||
|
this.root.push(new TextAttributes({ space: SpaceType.PRESERVE }));
|
||||||
|
let instruction = "TOC";
|
||||||
|
|
||||||
|
if (this.properties.captionLabel) {
|
||||||
|
instruction = `${instruction} \\a "${this.properties.captionLabel}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.entriesFromBookmark) {
|
||||||
|
instruction = `${instruction} \\b "${this.properties.entriesFromBookmark}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.captionLabelIncludingNumbers) {
|
||||||
|
instruction = `${instruction} \\c "${this.properties.captionLabelIncludingNumbers}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.sequenceAndPageNumbersSeparator) {
|
||||||
|
instruction = `${instruction} \\d "${this.properties.sequenceAndPageNumbersSeparator}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.tcFieldIdentifier) {
|
||||||
|
instruction = `${instruction} \\f "${this.properties.tcFieldIdentifier}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.hyperlink) {
|
||||||
|
instruction = `${instruction} \\h`;
|
||||||
|
}
|
||||||
|
if (this.properties.tcFieldLevelRange) {
|
||||||
|
instruction = `${instruction} \\l "${this.properties.tcFieldLevelRange}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.pageNumbersEntryLevelsRange) {
|
||||||
|
instruction = `${instruction} \\n "${this.properties.pageNumbersEntryLevelsRange}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.headingStyleRange) {
|
||||||
|
instruction = `${instruction} \\o "${this.properties.headingStyleRange}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.entryAndPageNumberSeparator) {
|
||||||
|
instruction = `${instruction} \\p "${this.properties.entryAndPageNumberSeparator}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.seqFieldIdentifierForPrefix) {
|
||||||
|
instruction = `${instruction} \\s "${this.properties.seqFieldIdentifierForPrefix}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.stylesWithLevels && this.properties.stylesWithLevels.length) {
|
||||||
|
const styles = this.properties.stylesWithLevels.map((sl) => `${sl.styleName};${sl.level}`).join(";");
|
||||||
|
instruction = `${instruction} \\t "${styles}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.useAppliedParagraphOutlineLevel) {
|
||||||
|
instruction = `${instruction} \\u`;
|
||||||
|
}
|
||||||
|
if (this.properties.preserveTabInEntries) {
|
||||||
|
instruction = `${instruction} \\w`;
|
||||||
|
}
|
||||||
|
if (this.properties.preserveNewLineInEntries) {
|
||||||
|
instruction = `${instruction} \\x`;
|
||||||
|
}
|
||||||
|
if (this.properties.hideTabAndPageNumbersInWebView) {
|
||||||
|
instruction = `${instruction} \\z`;
|
||||||
|
}
|
||||||
|
this.root.push(instruction);
|
||||||
|
}
|
||||||
|
}
|
2
src/file/table-of-contents/index.ts
Normal file
2
src/file/table-of-contents/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./table-of-contents";
|
||||||
|
export * from "./table-of-contents-properties";
|
7
src/file/table-of-contents/sdt-content.ts
Normal file
7
src/file/table-of-contents/sdt-content.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export class StructuredDocumentTagContent extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("w:sdtContent");
|
||||||
|
}
|
||||||
|
}
|
10
src/file/table-of-contents/sdt-properties.ts
Normal file
10
src/file/table-of-contents/sdt-properties.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// http://www.datypic.com/sc/ooxml/e-w_sdtPr-1.html
|
||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { Alias } from "./alias";
|
||||||
|
|
||||||
|
export class StructuredDocumentTagProperties extends XmlComponent {
|
||||||
|
constructor(alias: string) {
|
||||||
|
super("w:sdtPr");
|
||||||
|
this.root.push(new Alias(alias));
|
||||||
|
}
|
||||||
|
}
|
122
src/file/table-of-contents/table-of-contents-properties.ts
Normal file
122
src/file/table-of-contents/table-of-contents-properties.ts
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
export class StyleLevel {
|
||||||
|
public styleName: string;
|
||||||
|
public level: number;
|
||||||
|
|
||||||
|
constructor(styleName: string, level: number) {
|
||||||
|
this.styleName = styleName;
|
||||||
|
this.level = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options according to this docs:
|
||||||
|
* https://www.ecma-international.org/publications/standards/Ecma-376.htm
|
||||||
|
* Part 1 - Page 1251
|
||||||
|
*
|
||||||
|
* Short Guide:
|
||||||
|
* http://officeopenxml.com/WPtableOfContents.php
|
||||||
|
*/
|
||||||
|
export interface ITableOfContentsOptions {
|
||||||
|
/**
|
||||||
|
* \a option - Includes captioned items, but omits caption labels and numbers.
|
||||||
|
* The identifier designated by text in this switch's field-argument corresponds to the caption label.
|
||||||
|
* Use captionLabelIncludingNumbers (\c) to build a table of captions with labels and numbers.
|
||||||
|
*/
|
||||||
|
captionLabel?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \b option - Includes entries only from the portion of the document marked by
|
||||||
|
* the bookmark named by text in this switch's field-argument.
|
||||||
|
*/
|
||||||
|
entriesFromBookmark?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \c option - Includes figures, tables, charts, and other items that are numbered
|
||||||
|
* by a SEQ field (§17.16.5.56). The sequence identifier designated by text in this switch's
|
||||||
|
* field-argument, which corresponds to the caption label, shall match the identifier in the
|
||||||
|
* corresponding SEQ field.
|
||||||
|
*/
|
||||||
|
captionLabelIncludingNumbers?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \d option - When used with \s, the text in this switch's field-argument defines
|
||||||
|
* the separator between sequence and page numbers. The default separator is a hyphen (-).
|
||||||
|
*/
|
||||||
|
sequenceAndPageNumbersSeparator?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \f option - Includes only those TC fields whose identifier exactly matches the
|
||||||
|
* text in this switch's field-argument (which is typically a letter).
|
||||||
|
*/
|
||||||
|
tcFieldIdentifier?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \h option - Makes the table of contents entries hyperlinks.
|
||||||
|
*/
|
||||||
|
hyperlink?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \l option - Includes TC fields that assign entries to one of the levels specified
|
||||||
|
* by text in this switch's field-argument as a range having the form startLevel-endLevel,
|
||||||
|
* where startLevel and endLevel are integers, and startLevel has a value equal-to or less-than endLevel.
|
||||||
|
* TC fields that assign entries to lower levels are skipped.
|
||||||
|
*/
|
||||||
|
tcFieldLevelRange?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \n option - Without field-argument, omits page numbers from the table of contents.
|
||||||
|
* Page numbers are omitted from all levels unless a range of entry levels is specified by
|
||||||
|
* text in this switch's field-argument. A range is specified as for \l.
|
||||||
|
*/
|
||||||
|
pageNumbersEntryLevelsRange?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \o option - Uses paragraphs formatted with all or the specified range of builtin
|
||||||
|
* heading styles. Headings in a style range are specified by text in this switch's
|
||||||
|
* field-argument using the notation specified as for \l, where each integer corresponds
|
||||||
|
* to the style with a style ID of HeadingX (e.g. 1 corresponds to Heading1).
|
||||||
|
* If no heading range is specified, all heading levels used in the document are listed.
|
||||||
|
*/
|
||||||
|
headingStyleRange?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \p option - Text in this switch's field-argument specifies a sequence of characters
|
||||||
|
* that separate an entry and its page number. The default is a tab with leader dots.
|
||||||
|
*/
|
||||||
|
entryAndPageNumberSeparator?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \s option - For entries numbered with a SEQ field (§17.16.5.56), adds a prefix to the page number.
|
||||||
|
* The prefix depends on the type of entry. text in this switch's field-argument shall match the
|
||||||
|
* identifier in the SEQ field.
|
||||||
|
*/
|
||||||
|
seqFieldIdentifierForPrefix?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \t field-argument Uses paragraphs formatted with styles other than the built-in heading styles.
|
||||||
|
* Text in this switch's field-argument specifies those styles as a set of comma-separated doublets,
|
||||||
|
* with each doublet being a comma-separated set of style name and table of content level.
|
||||||
|
* \t can be combined with \o.
|
||||||
|
*/
|
||||||
|
stylesWithLevels?: StyleLevel[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \u Uses the applied paragraph outline level.
|
||||||
|
*/
|
||||||
|
useAppliedParagraphOutlineLevel?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \w Preserves tab entries within table entries.
|
||||||
|
*/
|
||||||
|
preserveTabInEntries?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \x Preserves newline characters within table entries.
|
||||||
|
*/
|
||||||
|
preserveNewLineInEntries?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \z Hides tab leader and page numbers in web page view (§17.18.102).
|
||||||
|
*/
|
||||||
|
hideTabAndPageNumbersInWebView?: boolean;
|
||||||
|
}
|
219
src/file/table-of-contents/table-of-contents.spec.ts
Normal file
219
src/file/table-of-contents/table-of-contents.spec.ts
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "../../export/formatter";
|
||||||
|
import { ITableOfContentsOptions, StyleLevel, TableOfContents } from "./";
|
||||||
|
|
||||||
|
describe("Table of Contents", () => {
|
||||||
|
describe("#constructor", () => {
|
||||||
|
it("should construct a TOC without options", () => {
|
||||||
|
const toc = new TableOfContents();
|
||||||
|
const tree = new Formatter().format(toc);
|
||||||
|
expect(tree).to.be.deep.equal(DEFAULT_TOC);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should construct a TOC with all the options and alias", () => {
|
||||||
|
const props: ITableOfContentsOptions = {};
|
||||||
|
|
||||||
|
props.captionLabel = "A";
|
||||||
|
props.entriesFromBookmark = "B";
|
||||||
|
props.captionLabelIncludingNumbers = "C";
|
||||||
|
props.sequenceAndPageNumbersSeparator = "D";
|
||||||
|
props.tcFieldIdentifier = "F";
|
||||||
|
props.hyperlink = true;
|
||||||
|
props.tcFieldLevelRange = "L";
|
||||||
|
props.pageNumbersEntryLevelsRange = "N";
|
||||||
|
props.headingStyleRange = "O";
|
||||||
|
props.entryAndPageNumberSeparator = "P";
|
||||||
|
props.seqFieldIdentifierForPrefix = "S";
|
||||||
|
|
||||||
|
const styles = new Array<StyleLevel>();
|
||||||
|
styles.push(new StyleLevel("SL", 1));
|
||||||
|
styles.push(new StyleLevel("SL", 2));
|
||||||
|
props.stylesWithLevels = styles;
|
||||||
|
props.useAppliedParagraphOutlineLevel = true;
|
||||||
|
props.preserveTabInEntries = true;
|
||||||
|
props.preserveNewLineInEntries = true;
|
||||||
|
props.hideTabAndPageNumbersInWebView = true;
|
||||||
|
|
||||||
|
const toc = new TableOfContents("Summary", props);
|
||||||
|
const tree = new Formatter().format(toc);
|
||||||
|
expect(tree).to.be.deep.equal(COMPLETE_TOC);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const DEFAULT_TOC = {
|
||||||
|
"w:sdt": [
|
||||||
|
{
|
||||||
|
"w:sdtPr": [
|
||||||
|
{
|
||||||
|
"w:alias": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:val": "Table of Contents",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:sdtContent": [
|
||||||
|
{
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:pPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:fldChar": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:fldCharType": "begin",
|
||||||
|
"w:dirty": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:instrText": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"xml:space": "preserve",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"TOC",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:fldChar": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:fldCharType": "separate",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:pPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:fldChar": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:fldCharType": "end",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const COMPLETE_TOC = {
|
||||||
|
"w:sdt": [
|
||||||
|
{
|
||||||
|
"w:sdtPr": [
|
||||||
|
{
|
||||||
|
"w:alias": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:val": "Summary",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:sdtContent": [
|
||||||
|
{
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:pPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:fldChar": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:fldCharType": "begin",
|
||||||
|
"w:dirty": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:instrText": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"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',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:fldChar": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:fldCharType": "separate",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:pPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:fldChar": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:fldCharType": "end",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
35
src/file/table-of-contents/table-of-contents.ts
Normal file
35
src/file/table-of-contents/table-of-contents.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// http://officeopenxml.com/WPtableOfContents.php
|
||||||
|
// http://www.datypic.com/sc/ooxml/e-w_sdt-1.html
|
||||||
|
import { Paragraph } from "file/paragraph";
|
||||||
|
import { Run } from "file/paragraph/run";
|
||||||
|
import { Begin, End, Separate } from "file/paragraph/run/field";
|
||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { FieldInstruction } from "./field-instruction";
|
||||||
|
import { StructuredDocumentTagContent } from "./sdt-content";
|
||||||
|
import { StructuredDocumentTagProperties } from "./sdt-properties";
|
||||||
|
import { ITableOfContentsOptions } from "./table-of-contents-properties";
|
||||||
|
|
||||||
|
export class TableOfContents extends XmlComponent {
|
||||||
|
constructor(alias: string = "Table of Contents", properties?: ITableOfContentsOptions) {
|
||||||
|
super("w:sdt");
|
||||||
|
this.root.push(new StructuredDocumentTagProperties(alias));
|
||||||
|
|
||||||
|
const content = new StructuredDocumentTagContent();
|
||||||
|
|
||||||
|
const beginParagraph = new Paragraph();
|
||||||
|
const beginRun = new Run();
|
||||||
|
beginRun.addChildElement(new Begin(true));
|
||||||
|
beginRun.addChildElement(new FieldInstruction(properties));
|
||||||
|
beginRun.addChildElement(new Separate());
|
||||||
|
beginParagraph.addRun(beginRun);
|
||||||
|
content.addChildElement(beginParagraph);
|
||||||
|
|
||||||
|
const endParagraph = new Paragraph();
|
||||||
|
const endRun = new Run();
|
||||||
|
endRun.addChildElement(new End());
|
||||||
|
endParagraph.addRun(endRun);
|
||||||
|
content.addChildElement(endParagraph);
|
||||||
|
|
||||||
|
this.root.push(content);
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
// http://officeopenxml.com/WPtableGrid.php
|
||||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
export class TableGrid extends XmlComponent {
|
export class TableGrid extends XmlComponent {
|
||||||
|
@ -3,13 +3,13 @@ import { WidthType } from "./table-cell";
|
|||||||
import { TableCellMargin } from "./table-cell-margin";
|
import { TableCellMargin } from "./table-cell-margin";
|
||||||
|
|
||||||
export class TableProperties extends XmlComponent {
|
export class TableProperties extends XmlComponent {
|
||||||
private readonly cellMargain: TableCellMargin;
|
private readonly cellMargin: TableCellMargin;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("w:tblPr");
|
super("w:tblPr");
|
||||||
|
|
||||||
this.cellMargain = new TableCellMargin();
|
this.cellMargin = new TableCellMargin();
|
||||||
this.root.push(this.cellMargain);
|
this.root.push(this.cellMargin);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setWidth(type: WidthType, w: number | string): TableProperties {
|
public setWidth(type: WidthType, w: number | string): TableProperties {
|
||||||
@ -28,7 +28,7 @@ export class TableProperties extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get CellMargin(): TableCellMargin {
|
public get CellMargin(): TableCellMargin {
|
||||||
return this.cellMargain;
|
return this.cellMargin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// http://officeopenxml.com/WPtableGrid.php
|
||||||
import {
|
import {
|
||||||
GridSpan,
|
GridSpan,
|
||||||
TableCellBorders,
|
TableCellBorders,
|
||||||
@ -60,7 +61,13 @@ export class Table extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getRow(ix: number): TableRow {
|
public getRow(ix: number): TableRow {
|
||||||
return this.rows[ix];
|
const row = this.rows[ix];
|
||||||
|
|
||||||
|
if (!row) {
|
||||||
|
throw Error("Index out of bounds when trying to get row on table");
|
||||||
|
}
|
||||||
|
|
||||||
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCell(row: number, col: number): TableCell {
|
public getCell(row: number, col: number): TableCell {
|
||||||
@ -93,17 +100,29 @@ export class TableRow extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getCell(ix: number): TableCell {
|
public getCell(ix: number): TableCell {
|
||||||
return this.cells[ix];
|
const cell = this.cells[ix];
|
||||||
|
|
||||||
|
if (!cell) {
|
||||||
|
throw Error("Index out of bounds when trying to get cell on row");
|
||||||
|
}
|
||||||
|
|
||||||
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
public addGridSpan(ix: number, cellSpan: number): TableCell {
|
public addGridSpan(index: number, cellSpan: number): TableCell {
|
||||||
const remainCell = this.cells[ix];
|
const remainCell = this.cells[index];
|
||||||
remainCell.CellProperties.addGridSpan(cellSpan);
|
remainCell.CellProperties.addGridSpan(cellSpan);
|
||||||
this.cells.splice(ix + 1, cellSpan - 1);
|
this.cells.splice(index + 1, cellSpan - 1);
|
||||||
this.root.splice(ix + 2, cellSpan - 1);
|
this.root.splice(index + 2, cellSpan - 1);
|
||||||
|
|
||||||
return remainCell;
|
return remainCell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public mergeCells(startIndex: number, endIndex: number): TableCell {
|
||||||
|
const cellSpan = endIndex - startIndex + 1;
|
||||||
|
|
||||||
|
return this.addGridSpan(startIndex, cellSpan);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TableRowProperties extends XmlComponent {
|
export class TableRowProperties extends XmlComponent {
|
||||||
|
@ -30,7 +30,6 @@ export abstract class XmlComponent extends BaseXmlComponent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Unused method
|
|
||||||
public addChildElement(child: XmlComponent | string): XmlComponent {
|
public addChildElement(child: XmlComponent | string): XmlComponent {
|
||||||
this.root.push(child);
|
this.root.push(child);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user