Compare commits

...

92 Commits
4.0.0 ... 4.4.0

Author SHA1 Message Date
f091cff7c9 Version bump 2018-11-07 01:06:59 +00:00
3f80b054fc Merge pull request #178 from dolanmiu/bugfix/correct-toc-instruction
changed comma by semicolon to work in Word 2016.
2018-10-22 03:37:54 +01:00
96d81873d8 forgot to fix the test 2018-10-21 19:56:31 -02:00
c429ae9920 changed comma by semicolon to work in Word 2016. 2018-10-21 19:37:37 -02:00
6b4e769f48 Merge branch 'master' of https://github.com/dolanmiu/docx 2018-10-21 05:16:32 +01:00
b67a9de0e9 Angular README example 2018-10-21 05:14:14 +01:00
ac9f65a068 Merge pull request #177 from dolanmiu/feat/guidelines
Contribution guidelines improve
2018-10-21 04:51:45 +01:00
7e8ebb2af2 Contribution guidelines improve 2018-10-21 04:50:57 +01:00
f53fe2f881 Merge pull request #175 from dolanmiu/feat/sequential-field
Created classes and methods needed to create Sequential Identifiers
2018-10-21 02:00:20 +01:00
6fdd88527a Merge pull request #176 from dolanmiu/bugfix/correct-toc-instruction
fixed TOC instruction that was missing quotes for some switches
2018-10-20 17:36:44 +01:00
b6f431e14d fixed TOC instruction that was missing quotes for some switches 2018-10-20 09:17:37 -03:00
23dee01f06 Created classes and methods needed to create Sequential Identifiers 2018-10-19 16:50:51 -03:00
a466578467 Version bump 2018-10-15 23:34:35 +01:00
3a9420fedf Merge pull request #149 from dolanmiu/feat/grid-span
Feat/grid span
2018-10-15 23:23:20 +01:00
8ac19a83b2 Rename demos 2018-10-15 22:21:40 +01:00
9f0b2f7074 Improve API 2018-10-15 21:54:33 +01:00
e02ac43c07 Version bump 2018-10-10 20:39:11 +01:00
ccffdad4c0 Merge pull request #171 from amitm02/master
Contextual spacing
2018-09-26 15:57:16 +01:00
2fb5845501 contextual spacing 2018-09-26 17:47:17 +03:00
ddd84a1765 Merge pull request #3 from dolanmiu/master
from base
2018-09-26 17:27:41 +03:00
f6a13aed86 Merge pull request #169 from dolanmiu/feat/table-of-contents
Feat/table of contents
2018-09-26 02:28:44 +01:00
2da3ba0262 Rename variables 2018-09-26 02:17:39 +01:00
e08c7cbbfb Add table of contents to side bar 2018-09-26 02:12:10 +01:00
a6de5d8a21 Merge pull request #168 from dolanmiu/feat/table-of-contents
Change documentation properties to options
2018-09-26 02:10:03 +01:00
00a20b7cfc Merge pull request #166 from dolanmiu/feat/table-of-contents
Feat/table of contents
2018-09-25 23:32:58 +01:00
f27c95191b Change documentation properties to options 2018-09-25 21:19:04 +01:00
c140d2c37c ITableOfContentsProperties to ITableOfContentsOptions 2018-09-25 21:09:30 +01:00
3a42f2a2f0 Make API simplier with interfaces 2018-09-25 20:05:35 +01:00
00efedaa09 Fix spelling and linting and improve readme 2018-09-25 19:49:44 +01:00
72e3d229dc unnecessary comment removed 2018-09-25 02:36:00 -03:00
b12d6ef484 initial documentation 2018-09-25 02:35:08 -03:00
8ee6fd3e67 demo updated 2018-09-25 01:33:44 -03:00
808c5b00a0 table of contents with all the options 2018-09-25 01:18:47 -03:00
4de6b51e76 removed getStyles and clearPageBreak that became useless 2018-09-25 00:10:27 -03:00
4ca81df401 TOC content generation aborted 2018-09-21 11:16:14 -03:00
8b463b3bb6 updated clone deep dependency and make fields dirty to be updated when word is opened 2018-09-21 10:26:28 -03:00
17d696e33a merge with master, demo27 became demo28 2018-09-21 07:40:58 -03:00
2f59867db6 Merge branch 'feat/table-of-contents' of https://github.com/dolanmiu/docx into feat/table-of-contents 2018-09-21 07:37:26 -03:00
90f3da74cf Version bump 2018-09-20 21:04:21 +01:00
28d8659cb5 Merge pull request #161 from bre7/patch-1
Improve fluency (dsl like)
2018-09-20 21:03:38 +01:00
5d1740a2ef Style fixes 2018-09-20 16:54:58 -03:00
7aa44568d8 Improve fluency (dsl like)
addRunProperty / addParagraphProperty should also return this
2018-09-20 15:51:57 -03:00
bf1f702e5a created a clone method for xml-components 2018-09-20 15:13:18 -03:00
78ad3e340a Merge pull request #160 from bre7/patch-1
Added charSpacing to docs
2018-09-20 18:46:30 +01:00
63a965beab Added charSpacing 2018-09-20 14:00:49 -03:00
815354e06f Merge pull request #159 from bre7/master
Added character spacing attribute
2018-09-20 16:27:21 +01:00
1cff104bae correct sdt objects of table of contents 2018-09-20 10:47:10 -03:00
4805efad2e organized imports 2018-09-20 10:31:49 -03:00
12e2ae9e91 making leader a option field and test improvments 2018-09-20 10:30:16 -03:00
c07b5cf709 added settings.xml back 2018-09-20 10:11:59 -03:00
0684738ec2 removed lodash 2018-09-20 07:09:17 -03:00
6da3efdacc Added character spacing attribute 2018-09-19 18:41:55 -03:00
7cd8864fb9 added stdPr and stdContent to table of contents 2018-09-19 11:01:07 -03:00
a9c69664c7 Merge pull request #156 from bre7/master
Added mirror margins option
2018-09-19 12:40:31 +01:00
50569224c3 Fixed tests (included mirrorMargins attr) 2018-09-18 23:50:10 -03:00
877aa325bb Merge pull request #153 from felipeochoa/fix-109
Fix 109
2018-09-19 01:25:32 +01:00
eb797d8986 Ignore Yarn lock & IntelliJ ide 2018-09-18 21:03:50 -03:00
fcb542471b Added mirror margins options 2018-09-18 21:03:20 -03:00
baf0f17bd6 added a method for clear the page breaks of a paragraph 2018-09-18 13:53:30 -03:00
8e911698a5 generating the content for a table of contents 2018-09-18 05:24:19 -03:00
0eb36be053 there is no need for the settings.xml file anymore 2018-09-17 22:09:05 -03:00
53b24965aa Merge pull request #152 from felipeochoa/fix-typo-table-properties
Fix typo in private property
2018-09-17 19:04:58 +01:00
edce1bef92 Allow creating Styles and assigning them to File.Styles
Closes #109
2018-09-17 12:48:21 -05:00
e0d54d3af3 Fix typo in private property 2018-09-17 12:41:17 -05:00
a9167b4809 Update demos 2018-09-13 01:54:37 +01:00
5b28eb0d00 Update logo to be blinking 2018-09-12 21:19:33 +01:00
f1b176670c Add more table demos 2018-09-12 21:03:06 +01:00
11ce9a5206 Add horizontal span 2018-09-12 21:01:52 +01:00
8b667b8d4c Merge pull request #148 from dolanmiu/feat/logo
Updated logo dimensions
2018-09-11 19:18:31 +01:00
a601a82a37 Updated logo dimensions 2018-09-11 19:16:47 +01:00
dcf3767a05 Merge pull request #147 from dolanmiu/feat/logo
Add logo
2018-09-11 19:08:27 +01:00
a9278009f8 Merge branch 'feat/logo' of https://github.com/dolanmiu/docx into feat/logo 2018-09-11 19:04:19 +01:00
5220f9e07c Add correct image 2018-09-11 19:03:53 +01:00
0b88064ca2 Update README.md 2018-09-11 19:03:29 +01:00
2233ccb968 Add logo 2018-09-11 19:00:52 +01:00
311fde01a1 Update Readme 2018-09-10 22:44:44 +01:00
146d0ad9e5 Moved addTableOfContents to File and creating a settings.xml and applying updateFields=true when there is a table of contents 2018-09-10 10:01:26 -03:00
aedfca377f elements need to be inside runs 2018-09-04 18:22:08 -03:00
7926f6c189 escape bars 2018-09-04 17:29:24 -03:00
0303a9f553 Merge pull request #141 from XappMedia/min-eight
Minimum Node v8 Support
2018-09-04 18:26:34 +01:00
e9a007d446 create default properties for table-of-contents instruction 2018-09-04 12:05:41 -03:00
12c1f82efe demo27 for TableOfContents 2018-09-04 11:03:17 -03:00
b1711ae293 first simple version of TOC working 2018-09-03 11:20:36 -03:00
a367951d07 moved Begin, Separate and End from page-number.ts to field.ts 2018-09-03 10:54:53 -03:00
c55f82c425 test improvment 2018-09-03 10:48:50 -03:00
2dab11e8b3 minimum 8 2018-08-31 11:25:05 -04:00
d1044d262e table of contents extending paragraph 2018-08-31 10:22:40 -03:00
8d83219bb6 wrote some initial code 2018-08-29 12:06:01 -03:00
a710483918 Merge pull request #2 from dolanmiu/master
merge from upstream
2018-08-27 18:11:47 +03:00
4f8ecf631b Merge branch 'master' into feat/table-of-contents 2018-08-23 06:21:01 -03:00
a6a8012b39 wrote some inital code 2018-08-22 10:30:19 -03:00
d5b6225a90 starting table of contents 2018-08-21 07:19:46 -03:00
62 changed files with 1455 additions and 137 deletions

7
.gitignore vendored
View File

@ -48,8 +48,15 @@ docs/.nojekyll
!.vscode/extensions.json
.history
# IntelliJ
.idea
# Lock files
package-lock.json
yarn.lock
# Documents
My Document.docx
# Temporary folder
tmp

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
v8

View File

@ -1,5 +1,5 @@
<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 align="center">
@ -17,11 +17,24 @@
[![PRs Welcome][pr-image]][pr-url]
<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>
# 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:
![RunKit Instructions](https://user-images.githubusercontent.com/2917613/38582539-f84311b6-3d07-11e8-90db-5885ae02c3c4.png)
@ -33,9 +46,11 @@ Press `endpoint` on the `RunKit` website:
* https://runkit.com/dolanmiu/docx-demo5 - Images
* https://runkit.com/dolanmiu/docx-demo6 - Margins
* 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**
More [here](https://docx.js.org/#/examples) and [here](https://github.com/dolanmiu/docx/tree/master/demo)
# 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!

34
demo/demo27.ts Normal file
View 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
View 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
View 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
View 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
View 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
View 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);
});

View File

@ -1,5 +1,5 @@
<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 align="center">
@ -54,6 +54,10 @@ exporter.pack("My First Document");
[@h4buli](https://github.com/h4buli)
<p align="center">
<img alt="clippy the assistant" src="http://i60.tinypic.com/339pvtt.png">
</p>
---
Made with 💖

View File

@ -16,6 +16,7 @@
* [Bullet Points](usage/bullet-points.md)
* [Numbering](usage/numbering.md)
* [Tab Stops](usage/tab-stops.md)
* [Table of Contents](usage/table-of-contents.md)
* Styling
* [Styling with JS](usage/styling-with-js.md)
* [Styling with XML](usage/styling-with-xml.md)

View File

@ -1,5 +1,30 @@
# 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
* 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 `_`:
**Do not:**
**Do not:**
```js
private get _level: string;
```
**Do**
**Do**
```js
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
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.

View File

@ -20,6 +20,7 @@ const name = new TextRun("Name:")
* `.size(halfPts)`: Set the font size, measured in half-points
* `.font(name)`: Set the run's font
* `.style(name)`: Apply a named run style
* `.characterSpacing(value)`: Set the character spacing adjustment (in TWIPs)
* For paragraph formatting:
* `.heading1()`, `.heading2()`, `.heading3()`, `.heading4()`, `.heading5()`, `.title()`: apply the appropriate style to the paragraph
* `.left()`, `.center()`, `.right()`, `.justified()`: set the paragraph's alignment

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
logo/logo-small.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
logo/logo-small.psd Normal file

Binary file not shown.

BIN
logo/logo.psd Normal file

Binary file not shown.

View File

@ -1,6 +1,6 @@
{
"name": "docx",
"version": "4.0.0",
"version": "4.4.0",
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
"main": "build/index.js",
"scripts": {
@ -82,5 +82,8 @@
"typedoc": "^0.11.1",
"typescript": "2.9.2",
"webpack": "^3.10.0"
},
"engines": {
"node": ">=8"
}
}

View File

@ -19,7 +19,7 @@ describe("Compiler", () => {
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
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/styles.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/footer1.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/document.xml.rels");
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);
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/_rels/header1.xml.rels");

View File

@ -23,6 +23,7 @@ interface IXmlifyedFileMapping {
ContentTypes: IXmlifyedFile;
AppProperties: IXmlifyedFile;
FootNotes: IXmlifyedFile;
Settings: IXmlifyedFile;
}
export class Compiler {
@ -62,6 +63,7 @@ export class Compiler {
}
private xmlifyFile(file: File): IXmlifyedFileMapping {
file.verifyUpdateFields();
return {
Document: {
data: xml(this.formatter.format(file.Document), true),
@ -120,6 +122,10 @@ export class Compiler {
data: xml(this.formatter.format(file.FootNotes)),
path: "word/footnotes.xml",
},
Settings: {
data: xml(this.formatter.format(file.Settings)),
path: "word/settings.xml",
},
};
}
}

View File

@ -1,5 +1,5 @@
import { IXmlableObject, XmlComponent } from "file/xml-components";
import { Paragraph, ParagraphProperties } from "../..";
import { Paragraph, ParagraphProperties, TableOfContents } from "../..";
import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties";
export class Body extends XmlComponent {
@ -53,6 +53,14 @@ export class Body extends XmlComponent {
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 {
const paragraph = new Paragraph();
const properties = new ParagraphProperties();

View File

@ -8,6 +8,7 @@ export interface IPageMarginAttributes {
header?: number;
footer?: number;
gutter?: number;
mirror?: boolean;
}
export class PageMarginAttributes extends XmlAttributeComponent<IPageMarginAttributes> {
@ -19,5 +20,6 @@ export class PageMarginAttributes extends XmlAttributeComponent<IPageMarginAttri
header: "w:header",
footer: "w:footer",
gutter: "w:gutter",
mirror: "w:mirrorMargins",
};
}

View File

@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
import { PageMarginAttributes } from "./page-margin-attributes";
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");
this.root.push(
new PageMarginAttributes({
@ -13,6 +13,7 @@ export class PageMargin extends XmlComponent {
header: header,
footer: footer,
gutter: gutter,
mirror: mirror,
}),
);
}

View File

@ -17,6 +17,7 @@ describe("SectionProperties", () => {
header: 708,
footer: 708,
gutter: 0,
mirror: false,
space: 708,
linePitch: 360,
headerId: 100,
@ -40,6 +41,7 @@ describe("SectionProperties", () => {
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
},
],
@ -69,6 +71,7 @@ describe("SectionProperties", () => {
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
},
],
@ -99,6 +102,7 @@ describe("SectionProperties", () => {
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
},
],
@ -124,6 +128,7 @@ describe("SectionProperties", () => {
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
},
],
@ -150,6 +155,7 @@ describe("SectionProperties", () => {
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
},
],

View File

@ -38,6 +38,7 @@ export class SectionProperties extends XmlComponent {
header: 708,
footer: 708,
gutter: 0,
mirror: false,
space: 708,
linePitch: 360,
orientation: PageOrientation.PORTRAIT,
@ -69,6 +70,7 @@ export class SectionProperties extends XmlComponent {
mergedOptions.header,
mergedOptions.footer,
mergedOptions.gutter,
mergedOptions.mirror,
),
);
this.root.push(new Columns(mergedOptions.space));

View File

@ -2,6 +2,7 @@
import { XmlComponent } from "file/xml-components";
import { Paragraph } from "../paragraph";
import { Table } from "../table";
import { TableOfContents } from "../table-of-contents";
import { Body } from "./body";
import { SectionPropertiesOptions } from "./body/section-properties";
import { DocumentAttributes } from "./document-attributes";
@ -41,6 +42,11 @@ export class Document extends XmlComponent {
return this;
}
public addTableOfContents(toc: TableOfContents): Document {
this.body.push(toc);
return this;
}
public createParagraph(text?: string): Paragraph {
const para = new Paragraph(text);
this.addParagraph(para);
@ -60,4 +66,12 @@ export class Document extends XmlComponent {
public get Body(): Body {
return this.body;
}
public getTablesOfContents(): TableOfContents[] {
return this.body.getTablesOfContents();
}
public getParagraphs(): Paragraph[] {
return this.body.getParagraphs();
}
}

View File

@ -1,2 +1,3 @@
export * from "./document";
export * from "./document-attributes";
export * from "./body";

View File

@ -10,14 +10,16 @@ import { Image, Media } from "./media";
import { Numbering } from "./numbering";
import { Bookmark, Hyperlink, Paragraph } from "./paragraph";
import { Relationships } from "./relationships";
import { Settings } from "./settings";
import { Styles } from "./styles";
import { ExternalStylesFactory } from "./styles/external-styles-factory";
import { DefaultStylesFactory } from "./styles/factory";
import { Table } from "./table";
import { TableOfContents } from "./table-of-contents";
export class File {
private readonly document: Document;
private readonly styles: Styles;
private styles: Styles;
private readonly coreProperties: CoreProperties;
private readonly numbering: Numbering;
private readonly media: Media;
@ -26,6 +28,7 @@ export class File {
private readonly headerWrapper: HeaderWrapper[] = [];
private readonly footerWrapper: FooterWrapper[] = [];
private readonly footNotes: FootNotes;
private readonly settings: Settings;
private readonly contentTypes: ContentTypes;
private readonly appProperties: AppProperties;
@ -105,6 +108,11 @@ export class File {
sectionPropertiesOptions.footerId = footer.Footer.ReferenceId;
}
this.document = new Document(sectionPropertiesOptions);
this.settings = new Settings();
}
public addTableOfContents(toc: TableOfContents): void {
this.document.addTableOfContents(toc);
}
public addParagraph(paragraph: Paragraph): void {
@ -214,6 +222,10 @@ export class File {
return this.styles;
}
public set Styles(styles: Styles) {
this.styles = styles;
}
public get CoreProperties(): CoreProperties {
return this.coreProperties;
}
@ -277,4 +289,14 @@ export class File {
public get FootNotes(): FootNotes {
return this.footNotes;
}
public get Settings(): Settings {
return this.settings;
}
public verifyUpdateFields(): void {
if (this.document.getTablesOfContents().length) {
this.settings.addUpdateFields();
}
}
}

View File

@ -6,4 +6,5 @@ export * from "./media";
export * from "./drawing";
export * from "./document";
export * from "./styles";
export * from "./table-of-contents";
export * from "./xml-components";

View File

@ -1,5 +1,5 @@
// http://officeopenxml.com/WPspacing.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { Attributes, XmlAttributeComponent, XmlComponent } from "file/xml-components";
export interface ISpacingProperties {
after?: number;
@ -23,3 +23,14 @@ export class Spacing extends XmlComponent {
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,
}),
);
}
}

View File

@ -1,11 +1,14 @@
import { Attributes, XmlComponent } from "file/xml-components";
export class Style extends XmlComponent {
constructor(type: string) {
public readonly styleId: string;
constructor(styleId: string) {
super("w:pStyle");
this.styleId = styleId;
this.root.push(
new Attributes({
val: type,
val: styleId,
}),
);
}

View File

@ -1,7 +1,7 @@
import { assert } from "chai";
import { Utility } from "../../../tests/utility";
import { LeftTabStop, MaxRightTabStop } from "./tab-stop";
import { LeftTabStop, MaxRightTabStop, RightTabStop } from "./tab-stop";
describe("LeftTabStop", () => {
let tabStop: LeftTabStop;
@ -28,7 +28,28 @@ describe("LeftTabStop", () => {
});
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", () => {

View File

@ -2,50 +2,52 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export class TabStop extends XmlComponent {
constructor(tab: Tab) {
constructor(tab: TabStopItem) {
super("w:tabs");
this.root.push(tab);
}
}
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 }> {
protected xmlKeys = { val: "w:val", pos: "w:pos" };
export class TabAttributes extends XmlAttributeComponent<{ val: TabValue; pos: string | number; leader?: LeaderType }> {
protected xmlKeys = { val: "w:val", pos: "w:pos", leader: "w:leader" };
}
export class Tab extends XmlComponent {
constructor(value: TabValue, position: string | number) {
export class TabStopItem extends XmlComponent {
constructor(value: TabValue, position: string | number, leader?: LeaderType) {
super("w:tab");
this.root.push(
new TabAttributes({
val: value,
pos: position,
leader,
}),
);
}
}
export class MaxRightTabStop extends TabStop {
constructor() {
super(new Tab("right", 9026));
constructor(leader?: LeaderType) {
super(new TabStopItem("right", 9026, leader));
}
}
export class LeftTabStop extends TabStop {
constructor(position: number) {
super(new Tab("left", position));
constructor(position: number, leader?: LeaderType) {
super(new TabStopItem("left", position, leader));
}
}
export class RightTabStop extends TabStop {
constructor(position: number) {
super(new Tab("right", position));
constructor(position: number, leader?: LeaderType) {
super(new TabStopItem("right", position, leader));
}
}
export class CenterTabStop extends TabStop {
constructor(position: number) {
super(new Tab("center", position));
constructor(position: number, leader?: LeaderType) {
super(new TabStopItem("center", position, leader));
}
}

View File

@ -10,13 +10,13 @@ import { Border, ThematicBreak } from "./formatting/border";
import { IIndentAttributesProperties, Indent } from "./formatting/indent";
import { KeepLines, KeepNext } from "./formatting/keep";
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 { 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 { Bookmark, Hyperlink } from "./links";
import { ParagraphProperties } from "./properties";
import { PictureRun, Run, TextRun } from "./run";
import { PictureRun, Run, SequentialIdentifier, TextRun } from "./run";
export class Paragraph extends XmlComponent {
private readonly properties: ParagraphProperties;
@ -30,6 +30,10 @@ export class Paragraph extends XmlComponent {
}
}
public get paragraphProperties(): ParagraphProperties {
return this.properties;
}
public get Borders(): Border {
return this.properties.paragraphBorder;
}
@ -155,23 +159,23 @@ export class Paragraph extends XmlComponent {
return this;
}
public maxRightTabStop(): Paragraph {
this.properties.push(new MaxRightTabStop());
public maxRightTabStop(leader?: LeaderType): Paragraph {
this.properties.push(new MaxRightTabStop(leader));
return this;
}
public leftTabStop(position: number): Paragraph {
this.properties.push(new LeftTabStop(position));
public leftTabStop(position: number, leader?: LeaderType): Paragraph {
this.properties.push(new LeftTabStop(position, leader));
return this;
}
public rightTabStop(position: number): Paragraph {
this.properties.push(new RightTabStop(position));
public rightTabStop(position: number, leader?: LeaderType): Paragraph {
this.properties.push(new RightTabStop(position, leader));
return this;
}
public centerTabStop(position: number): Paragraph {
this.properties.push(new CenterTabStop(position));
public centerTabStop(position: number, leader?: LeaderType): Paragraph {
this.properties.push(new CenterTabStop(position, leader));
return this;
}
@ -207,6 +211,11 @@ export class Paragraph extends XmlComponent {
return this;
}
public contextualSpacing(value: boolean): Paragraph {
this.properties.push(new ContextualSpacing(value));
return this;
}
public keepNext(): Paragraph {
this.properties.push(new KeepNext());
return this;
@ -232,7 +241,13 @@ export class Paragraph extends XmlComponent {
return this;
}
public get Properties(): ParagraphProperties {
return this.properties;
public addTabStop(run: Run): Paragraph {
this.root.splice(1, 0, run);
return this;
}
public addSequentialIdentifier(identifier: string): Paragraph {
this.root.push(new SequentialIdentifier(identifier));
return this;
}
}

View 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 }));
}
}

View File

@ -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 {
constructor() {
super("w:i");

View File

@ -1,3 +1,4 @@
export * from "./run";
export * from "./text-run";
export * from "./picture-run";
export * from "./sequential-identifier";

View File

@ -1,20 +1,9 @@
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" }> {
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 {
constructor() {
super("w:instrText");
@ -22,17 +11,3 @@ export class Page extends XmlComponent {
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" }));
}
}

View File

@ -1,6 +1,7 @@
// http://officeopenxml.com/WPtext.php
import { Break } from "./break";
import { Caps, SmallCaps } from "./caps";
import { Begin, End, Separate } from "./field";
import {
Bold,
BoldComplexScript,
@ -13,7 +14,7 @@ import {
SizeComplexScript,
Strike,
} from "./formatting";
import { Begin, End, Page, Separate } from "./page-number";
import { Page } from "./page-number";
import { RunProperties } from "./properties";
import { RunFonts } from "./run-fonts";
import { SubScript, SuperScript } from "./script";

View 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}`);
}
}

View 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",
},
},
],
},
],
};

View 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());
}
}

View File

@ -0,0 +1,2 @@
export * from "./settings";
export * from "./update-fields";

View 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);
});
});
});

View 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());
}
}
}

View 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);
});
});
});

View 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,
}),
);
}
}

View File

@ -47,12 +47,14 @@ export class ParagraphStyle extends Style {
this.root.push(this.runProperties);
}
public addParagraphProperty(property: XmlComponent): void {
public addParagraphProperty(property: XmlComponent): ParagraphStyle {
this.paragraphProperties.push(property);
return this;
}
public addRunProperty(property: XmlComponent): void {
public addRunProperty(property: XmlComponent): ParagraphStyle {
this.runProperties.push(property);
return this;
}
public basedOn(parentId: string): ParagraphStyle {
@ -73,121 +75,101 @@ export class ParagraphStyle extends Style {
// ---------- Run formatting ---------------------- //
public size(twips: number): ParagraphStyle {
this.addRunProperty(new formatting.Size(twips));
this.addRunProperty(new formatting.SizeComplexScript(twips));
return this;
return this.addRunProperty(new formatting.Size(twips)).addRunProperty(new formatting.SizeComplexScript(twips));
}
public bold(): ParagraphStyle {
this.addRunProperty(new formatting.Bold());
return this;
return this.addRunProperty(new formatting.Bold());
}
public italics(): ParagraphStyle {
this.addRunProperty(new formatting.Italics());
return this;
return this.addRunProperty(new formatting.Italics());
}
public smallCaps(): ParagraphStyle {
this.addRunProperty(new formatting.SmallCaps());
return this;
return this.addRunProperty(new formatting.SmallCaps());
}
public allCaps(): ParagraphStyle {
this.addRunProperty(new formatting.Caps());
return this;
return this.addRunProperty(new formatting.Caps());
}
public strike(): ParagraphStyle {
this.addRunProperty(new formatting.Strike());
return this;
return this.addRunProperty(new formatting.Strike());
}
public doubleStrike(): ParagraphStyle {
this.addRunProperty(new formatting.DoubleStrike());
return this;
return this.addRunProperty(new formatting.DoubleStrike());
}
public subScript(): ParagraphStyle {
this.addRunProperty(new formatting.SubScript());
return this;
return this.addRunProperty(new formatting.SubScript());
}
public superScript(): ParagraphStyle {
this.addRunProperty(new formatting.SuperScript());
return this;
return this.addRunProperty(new formatting.SuperScript());
}
public underline(underlineType?: string, color?: string): ParagraphStyle {
this.addRunProperty(new formatting.Underline(underlineType, color));
return this;
return this.addRunProperty(new formatting.Underline(underlineType, color));
}
public color(color: string): ParagraphStyle {
this.addRunProperty(new formatting.Color(color));
return this;
return this.addRunProperty(new formatting.Color(color));
}
public font(fontName: string): ParagraphStyle {
this.addRunProperty(new formatting.RunFonts(fontName));
return this;
return this.addRunProperty(new formatting.RunFonts(fontName));
}
public characterSpacing(value: number): ParagraphStyle {
return this.addRunProperty(new formatting.CharacterSpacing(value));
}
// --------------------- Paragraph formatting ------------------------ //
public center(): ParagraphStyle {
this.addParagraphProperty(new paragraph.Alignment("center"));
return this;
return this.addParagraphProperty(new paragraph.Alignment("center"));
}
public left(): ParagraphStyle {
this.addParagraphProperty(new paragraph.Alignment("left"));
return this;
return this.addParagraphProperty(new paragraph.Alignment("left"));
}
public right(): ParagraphStyle {
this.addParagraphProperty(new paragraph.Alignment("right"));
return this;
return this.addParagraphProperty(new paragraph.Alignment("right"));
}
public justified(): ParagraphStyle {
this.addParagraphProperty(new paragraph.Alignment("both"));
return this;
return this.addParagraphProperty(new paragraph.Alignment("both"));
}
public thematicBreak(): ParagraphStyle {
this.addParagraphProperty(new paragraph.ThematicBreak());
return this;
return this.addParagraphProperty(new paragraph.ThematicBreak());
}
public maxRightTabStop(): ParagraphStyle {
this.addParagraphProperty(new paragraph.MaxRightTabStop());
return this;
return this.addParagraphProperty(new paragraph.MaxRightTabStop());
}
public leftTabStop(position: number): ParagraphStyle {
this.addParagraphProperty(new paragraph.LeftTabStop(position));
return this;
return this.addParagraphProperty(new paragraph.LeftTabStop(position));
}
public indent(attrs: object): ParagraphStyle {
this.addParagraphProperty(new paragraph.Indent(attrs));
return this;
return this.addParagraphProperty(new paragraph.Indent(attrs));
}
public spacing(params: paragraph.ISpacingProperties): ParagraphStyle {
this.addParagraphProperty(new paragraph.Spacing(params));
return this;
return this.addParagraphProperty(new paragraph.Spacing(params));
}
public keepNext(): ParagraphStyle {
this.addParagraphProperty(new paragraph.KeepNext());
return this;
return this.addParagraphProperty(new paragraph.KeepNext());
}
public keepLines(): ParagraphStyle {
this.addParagraphProperty(new paragraph.KeepLines());
return this;
return this.addParagraphProperty(new paragraph.KeepLines());
}
}
@ -267,24 +249,21 @@ export class CharacterStyle extends Style {
return this;
}
public addRunProperty(property: XmlComponent): void {
public addRunProperty(property: XmlComponent): CharacterStyle {
this.runProperties.push(property);
return this;
}
public color(color: string): CharacterStyle {
this.addRunProperty(new formatting.Color(color));
return this;
return this.addRunProperty(new formatting.Color(color));
}
public underline(underlineType?: string, color?: string): CharacterStyle {
this.addRunProperty(new formatting.Underline(underlineType, color));
return this;
return this.addRunProperty(new formatting.Underline(underlineType, color));
}
public size(twips: number): CharacterStyle {
this.addRunProperty(new formatting.Size(twips));
this.addRunProperty(new formatting.SizeComplexScript(twips));
return this;
return this.addRunProperty(new formatting.Size(twips)).addRunProperty(new formatting.SizeComplexScript(twips));
}
}

View File

@ -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", () => {
const style = new ParagraphStyle("myStyleId").left();
const tree = new Formatter().format(style);

View 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 }));
}
}

View 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);
}
}

View File

@ -0,0 +1,2 @@
export * from "./table-of-contents";
export * from "./table-of-contents-properties";

View File

@ -0,0 +1,7 @@
import { XmlComponent } from "file/xml-components";
export class StructuredDocumentTagContent extends XmlComponent {
constructor() {
super("w:sdtContent");
}
}

View 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));
}
}

View 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;
}

View 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",
},
},
],
},
],
},
],
},
],
},
],
};

View 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);
}
}

View File

@ -1,3 +1,4 @@
// http://officeopenxml.com/WPtableGrid.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export class TableGrid extends XmlComponent {

View File

@ -3,13 +3,13 @@ import { WidthType } from "./table-cell";
import { TableCellMargin } from "./table-cell-margin";
export class TableProperties extends XmlComponent {
private readonly cellMargain: TableCellMargin;
private readonly cellMargin: TableCellMargin;
constructor() {
super("w:tblPr");
this.cellMargain = new TableCellMargin();
this.root.push(this.cellMargain);
this.cellMargin = new TableCellMargin();
this.root.push(this.cellMargin);
}
public setWidth(type: WidthType, w: number | string): TableProperties {
@ -28,7 +28,7 @@ export class TableProperties extends XmlComponent {
}
public get CellMargin(): TableCellMargin {
return this.cellMargain;
return this.cellMargin;
}
}

View File

@ -1,3 +1,4 @@
// http://officeopenxml.com/WPtableGrid.php
import {
GridSpan,
TableCellBorders,
@ -60,7 +61,13 @@ export class Table extends XmlComponent {
}
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 {
@ -93,17 +100,29 @@ export class TableRow extends XmlComponent {
}
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 {
const remainCell = this.cells[ix];
public addGridSpan(index: number, cellSpan: number): TableCell {
const remainCell = this.cells[index];
remainCell.CellProperties.addGridSpan(cellSpan);
this.cells.splice(ix + 1, cellSpan - 1);
this.root.splice(ix + 2, cellSpan - 1);
this.cells.splice(index + 1, cellSpan - 1);
this.root.splice(index + 2, cellSpan - 1);
return remainCell;
}
public mergeCells(startIndex: number, endIndex: number): TableCell {
const cellSpan = endIndex - startIndex + 1;
return this.addGridSpan(startIndex, cellSpan);
}
}
export class TableRowProperties extends XmlComponent {

View File

@ -30,7 +30,6 @@ export abstract class XmlComponent extends BaseXmlComponent {
};
}
// TODO: Unused method
public addChildElement(child: XmlComponent | string): XmlComponent {
this.root.push(child);