Compare commits

..

144 Commits
1.2.0 ... 2.0.1

Author SHA1 Message Date
a706455a7c Merge pull request #30 from felipeochoa/overrides
Add support for numbering level overrides
2017-04-12 18:34:07 +01:00
8c388a95c4 bump patch version 2017-04-12 16:01:26 +02:00
9a41d78ecb add an additional demo 2017-04-12 16:00:16 +02:00
6fc4ad782a add #overrideLevel method to concrete numberings 2017-04-12 16:00:16 +02:00
16b9057ac6 prepare Level to be extended for overrides 2017-04-12 15:57:26 +02:00
28c62c1ee4 version 2.0.0 2017-04-01 13:31:24 +01:00
91a757619e fixed unit tests 2017-04-01 12:30:27 +01:00
f89049e4c1 version bump 2017-04-01 12:23:01 +01:00
086f5f5d03 Merge branch 'image-support' 2017-04-01 12:22:23 +01:00
a2694f1d7d edited demo code 2017-04-01 12:21:59 +01:00
fd350d53db fixed some typescript compile issues 2017-04-01 12:18:23 +01:00
af48af2854 added error checking and updated demo 2017-03-29 22:57:52 +01:00
15009884e3 made image take in IData instead of image path 2017-03-29 22:47:34 +01:00
ad462dfe10 added relationships 2017-03-27 01:28:52 +01:00
68fe380575 renamed template constant 2017-03-27 01:28:31 +01:00
5b2a311ad4 media now adds object into docx /media folder 2017-03-26 20:57:43 +01:00
5db2503e7b added media class and packer to handle it 2017-03-25 21:50:33 +00:00
55a1d23f29 fixed missing comma 2017-03-14 11:33:46 +00:00
0bfd753a0d fixed json, missing comma 2017-03-14 11:28:05 +00:00
22adabd2d9 updated keywords 2017-03-14 10:51:05 +00:00
6d5c89ff47 corrected underline 2017-03-14 10:48:24 +00:00
96a9bb1a1e corrected underline 2017-03-14 10:47:50 +00:00
2be00d3f1e corrected example 2017-03-14 10:38:05 +00:00
30b21217a9 added create picture run method 2017-03-13 23:41:14 +00:00
260a680f56 Merge branch 'master' into image-support 2017-03-13 23:37:09 +00:00
486777b108 named this addRun because I realised Text can be a Run, but also a Picture can be a run 2017-03-13 23:35:24 +00:00
3730e2b60e clean up of packer 2017-03-13 23:30:26 +00:00
e7e6d02cc5 Merge pull request #28 from felipeochoa/npm-fixes
NPM fixes
2017-03-13 22:38:13 +00:00
c7b7b585be fix path to template when installed via npm
I'm not sure why, buy app-root-path was not picking up the correct
path for this module when installed via NPM. Since we're already using
relative paths for imports, I don't think we gain much from using it
anyway, so I've changed to using path.resolve instead
2017-03-13 20:40:16 +01:00
0c128ae4a5 add template to npm bundle 2017-03-13 20:40:16 +01:00
d7a70ffe03 use gemnasium rather than david-dm 2017-03-13 16:14:03 +00:00
e1c699ec41 Merge pull request #27 from felipeochoa/styles
More styles harmony
2017-03-13 12:31:25 +00:00
245f0ae2ea added gitter integration 2017-03-13 12:30:22 +00:00
762fc0ca9d added run formatting methods to levels 2017-03-13 12:38:16 +01:00
457d074a59 added paragraph formatting methods to numbering Level 2017-03-13 12:38:16 +01:00
ba80440faf formatting and correct heading values 2017-03-13 01:21:26 +00:00
ae41e22df5 moved Documents section to Wiki.
Cleaned up Wiki
Added felipeochoa mention
removed TOC
2017-03-13 01:19:39 +00:00
bf2e5201c8 made usage much more clear 2017-03-13 01:11:11 +00:00
2662bcc60b removed Paragraph from readme, and put it in the Wiki 2017-03-13 01:03:57 +00:00
efa4f1933a moved Text section to Wiki 2017-03-13 00:57:57 +00:00
8c58ec2ba4 moved bullet points to Wiki 2017-03-13 00:54:05 +00:00
e6ed3a55ff updated contents 2017-03-13 00:51:01 +00:00
69abf509c9 removed tab stops and placed it in the Wiki 2017-03-13 00:50:29 +00:00
66eace4a33 added a attribute to graphic 2017-03-13 00:40:39 +00:00
eeb2187db4 added source rectangle and stretch in the blip fill 2017-03-13 00:33:59 +00:00
fa44acca1b added the remaining classes required 2017-03-13 00:02:56 +00:00
34ce662f70 added basic test for drawing 2017-03-12 23:32:02 +00:00
8d72fa5542 added more boilerplate files to drawing 2017-03-12 23:31:43 +00:00
64d642b593 Merge branch 'master' into image-support 2017-03-12 21:44:17 +00:00
d9e533156f fixed the build :P removed .docx extension if one is already present 2017-03-12 21:43:52 +00:00
90e5cc3ad5 Revert "fixed the build :P removed .docx extension if one is already present"
This reverts commit 9f61ca7abe.
2017-03-12 21:43:15 +00:00
9f61ca7abe fixed the build :P removed .docx extension if one is already present 2017-03-12 21:42:45 +00:00
6af79a884c Merge branch 'master' into image-support 2017-03-12 21:38:38 +00:00
353401d148 updated readme to add demo folder 2017-03-12 21:37:36 +00:00
f17360d45a add simple demo page 2017-03-12 21:34:49 +00:00
4bc6ed0eda added more files to drawing 2017-03-12 21:30:46 +00:00
bf22866a8d Merge pull request #26 from felipeochoa/styles
Standardizing formatting methods
2017-03-12 21:26:47 +00:00
a6e40d9d92 tightened type checking for tab stops 2017-03-12 22:06:43 +01:00
0b78e33444 added #leftTabStop and #maxRightTabStop methods to paragraph styles 2017-03-12 22:05:54 +01:00
6689f76c28 added #thematicBreak method to paragraph styles 2017-03-12 21:58:12 +01:00
df8ba1e8b8 added alignment methods to paragraph style 2017-03-12 21:40:01 +01:00
b3524971ac clean up imports into paragraph and paragraph style
(this prep for importing it from styles)
2017-03-12 21:39:29 +01:00
1cd681bc2d added #font method to paragraph styles 2017-03-12 17:53:32 +01:00
7f4d1bf3e7 added #SubScript and #SuperScript methods to ParagraphStyle 2017-03-12 17:52:03 +01:00
8c6cd012e7 eliminated duplicate implementation of strikethrough 2017-03-12 17:51:52 +01:00
371398bc2c added #strike and #doubleStrike methods to paragraphStyle 2017-03-12 17:44:42 +01:00
9a90818729 added #smallCaps and #allCaps methods to ParagraphStyle 2017-03-12 17:43:00 +01:00
90049a31ee added #style method to runs 2017-03-12 17:35:15 +01:00
3dbd917667 added #color and #size methods to Run 2017-03-12 17:31:36 +01:00
d85c6ce56a allow Run#underline to take style and color arguments 2017-03-12 17:28:52 +01:00
db24f67e34 Merge pull request #21 from felipeochoa/tables
Tables!
2017-03-11 20:31:57 +00:00
fb6a4383ff renamed cell#push to cell#addContent 2017-03-11 21:15:45 +01:00
c33cdb450c added initial constructor 2017-03-11 18:21:31 +00:00
62b6b33254 created drawing file and refactored run components in components folder 2017-03-11 16:56:35 +00:00
64ab91765a edited contents 2017-03-11 16:06:09 +00:00
c0ba937c3a simplified readme 2017-03-11 16:05:20 +00:00
70f4613e1b appease the linter 2017-03-11 10:30:25 +01:00
bd9d6b74f5 added Cell#createParagraph method 2017-03-11 10:30:08 +01:00
bd3eb3e214 move the cell-paragraph validation into prepForXml
Instead of forcing table cells to only have a single paragraph as
their content, we now check whether they end in a paragraph (and
insert one if necessary) during #prepForXml
2017-03-11 10:22:30 +01:00
c10b576a3a added fixedWidthLayout option and method 2017-03-11 09:59:29 +01:00
210b97d00b added document#createTable 2017-03-11 09:02:36 +01:00
62a238de84 more sane width management 2017-03-11 09:02:22 +01:00
7b6f5bbaef forgot to test #getCell 2017-03-10 17:48:05 +01:00
a45048a464 fix a handful of strict null errors; make test config also be strict
These errors only arose because I didn't run `npm build`, only `npm
test`. Then, when I tried building the null checks failed. Keeping the
two config fiels in sync will help prevent this issue in the future
2017-03-10 17:45:37 +01:00
c0b0649f37 added tests for table v1 2017-03-10 17:39:37 +01:00
e7e5c61a90 clean up initial table attempt (linter, interfaces, etc.) 2017-03-10 17:11:08 +01:00
f1fb0c4f22 initial attempt at Table 2017-03-10 16:55:39 +01:00
0de30f6c59 Merge pull request #19 from felipeochoa/cleanup
fix unused imports linter warnings
2017-03-10 15:43:19 +00:00
599be5bf2b got a little carried away with the purge! fixed failing errors 2017-03-10 16:35:48 +01:00
9d7573ed9c fix unused imports linter warnings 2017-03-10 16:09:21 +01:00
738cac0253 Merge pull request #17 from felipeochoa/cleanup
More cleanup
2017-03-10 14:53:42 +00:00
35ddda444a rename paragraph#createRun to paragraph#createTextRun 2017-03-10 14:44:25 +01:00
8d11ec3422 add paragraph.createRun method 2017-03-10 14:36:16 +01:00
ea647d84df add document.createParagraph method 2017-03-10 14:31:21 +01:00
8c91c04afa clean up latentstyles to use modern attribute implementation 2017-03-10 14:31:21 +01:00
a89718ea83 fix a couple of compiler null warnings 2017-03-10 14:31:21 +01:00
62911d6e44 make XmlAttributeComponent into a generic class
This change brings increased type safety to uses of
XmlAttributeComponent. Now the compiler is checkign for us that the
properties that get passed in to every subclass match the intended
interface, and also that the xmlKeys property -> xml attribute mapping
has all the right keys
2017-03-10 14:19:45 +01:00
766069c7cc make packers a top-level export 2017-03-10 13:54:49 +01:00
09f1d473c3 remove lodash from dependencies as it's no longer used 2017-03-10 13:54:44 +01:00
74ea0f699f remove npm and install from dependencies
These aren't actually needed at run time or build time
2017-03-10 13:54:37 +01:00
b00e4fa5b5 moved exporting to the wiki 2017-03-10 11:35:43 +00:00
7ffa59d7fa formatting 2017-03-10 11:27:48 +00:00
d662391a64 removed LICENCE in readme.
It is already in the repository as the LICENCE file. No need to repeat
2017-03-10 11:26:04 +00:00
7a9661c55a moved examples to Wiki 2017-03-10 11:24:41 +00:00
f80a5654b4 adding unused variable checking 2017-03-10 10:33:42 +00:00
001b756866 Merge pull request #16 from felipeochoa/cleanup
Cleanup
2017-03-10 10:32:05 +00:00
fd1aab48f7 removed some dead code causing tslint problems 2017-03-10 10:35:38 +01:00
7ff838357a removed empty Attributes from ParagraphProperties
I checked the spec, and "w:pPr" does not take any attributes. I also
converted the paragraph tests to use the deep.equal style to get rid
of some more jsonify!
2017-03-10 10:32:40 +01:00
958b5ea307 improved type annotation for Attributes#prepForXml 2017-03-10 08:58:29 +01:00
3a6cf81c60 use strict null checks for added type safety
From the TypeScript manual "In strict null checking mode, the null and
undefined values are not in the domain of every type and are only
assignable to themselves and any (the one exception being that
undefined is also assignable to void)."

In other words, this setting means the compiler will complain if you
try to use a variable, but it might be null or undefined. Since the
project builds and tests pass with it, I think it's good to turn it
on. If it becomes annoying, you can always turn it off later
2017-03-10 08:54:55 +01:00
10684b34aa clean up unused imports and a linter warning 2017-03-10 08:52:47 +01:00
2fc9156e93 remove console.log statements 2017-03-10 08:25:11 +01:00
c521d0ce63 rename toXml -> prepForXml 2017-03-10 08:23:27 +01:00
50ca71087e reverted back as they’re both buggy 2017-03-10 00:52:43 +00:00
72eff21027 used html as markdown version is buggy 2017-03-10 00:51:42 +00:00
11bbb5e285 linting 2017-03-09 23:32:52 +00:00
18bba1870e added types for app-root-path 2017-03-09 23:20:37 +00:00
c6eef3fd79 tslint fixes 2017-03-09 23:20:28 +00:00
06353a6977 linting 2017-03-09 22:57:37 +00:00
2b28dd42ee unclear variable name. please improve 2017-03-09 22:57:30 +00:00
7022b39d2e linting 2017-03-09 22:56:08 +00:00
5fac776dca listing stuff 2017-03-09 22:31:55 +00:00
0e8d92f8bb Merge pull request #14 from felipeochoa/formatting
Clean up xml formatting (clearVariables) and introduced toXml()
2017-03-09 22:17:04 +00:00
30fef5238e fix some linter warnings 2017-03-09 21:06:54 +01:00
0fe6ff95a2 fix an incorrect test 2017-03-09 20:49:34 +01:00
11e918114d clear out clearVariables! In comes toXml 2017-03-09 20:49:14 +01:00
879b9163a3 Allow XmlComponent to take strings and remove XmlUnit 2017-03-09 20:21:49 +01:00
4a2b8a1e04 remove unused XmlProperty code 2017-03-09 19:04:17 +01:00
d6f5fe0ae8 Remove extraneous multi-property from base numbering object 2017-03-09 17:59:17 +01:00
dd4ff944d7 Merge pull request #12 from felipeochoa/styles
Styles cleanup
2017-03-09 15:25:19 +00:00
aac81ffa12 add Styles to top-level exports 2017-03-09 13:27:53 +01:00
d93432678b added #createParagraphStyle method to Styles 2017-03-09 13:27:40 +01:00
ac7799a875 add an #underline method to ParagraphStyle 2017-03-09 13:19:50 +01:00
766bcabcb8 allow Underline to be called with type and color; fix default 2017-03-09 12:42:19 +01:00
f06094c91d clean up underlineTests linter warnings 2017-03-09 12:36:33 +01:00
3b18b8267a fix paragraphTests.ts linter warnings 2017-03-09 12:25:59 +01:00
20ec9b679e added Spacing for paragraphs and ParagraphStyle 2017-03-09 12:16:49 +01:00
449d1bc2c3 added fluent formatting methods to ParagraphStyle 2017-03-09 11:27:47 +01:00
b4dca79d72 added optional name parameter to style constructor 2017-03-09 11:11:07 +01:00
e6e0658812 clean up style & components attributes 2017-03-09 11:11:07 +01:00
18ca93e50a add paragraph#style method to quickly set the style 2017-03-09 11:11:07 +01:00
f5144e6d72 ts/styles linter warnings 2017-03-09 11:11:07 +01:00
2684f16579 move indent.ts over to paragraph and add a #indent method 2017-03-09 09:59:48 +01:00
77255c9d5e modified build script and version bump 2017-03-08 23:35:22 +00:00
109 changed files with 3112 additions and 1393 deletions

393
README.md
View File

@ -3,56 +3,36 @@
</p>
<p align="center">
Generate .docx files with JS/TS very easily
Generate .docx files with JS/TS very easily, written in TS.
</p>
---
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] [![Known Vulnerabilities][snky-image]][snky-url]
-----
# docx
> A tool to create Word Documents (.docx) with JS or TS, written in TS.
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][gemnasium-image]][gemnasium-url] [![Known Vulnerabilities][snky-image]][snky-url] [![Chat on Gitter][gitter-image]][gitter-url]
[![NPM](https://nodei.co/npm/docx.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/docx/)
# Table of Contents
- [Install](#)
- [Usage](#)
- [Create simple Word Document](#)
- [Create Paragraph](#)
- [Styles](#)
- [Heading1 - Heading5](#)
- [Title](#)
- [Text Alignment](#)
- [Example](#)
- [Thematic Break (Page Break)](#)
- [Text](#)
- [Typographical Emphasis](#)
- [Bold](#)
- [Italics](#)
- [Underline](#)
- [Break](#)
- [Chaining](#)
- [Bullet Points](#)
- [Tab Stops](#)
- [Left Tab Stop](#)
- [Center Tab Stop](#)
- [Right Tab Stop](#)
- [Max Right Tab Stop](#)
- [Example](#)
- [Exporting](#)
- [Express](#)
- [Standalone .docx file](#)
- [Examples](#)
- [License](#)
# docx
# Install
## Install
```sh
$ npm install --save docx
```
## Demo
# Usage
```sh
$ npm run demo
```
will run the demo app in the `demo` folder, which creates a file called "My Document.docx" in the root of the project
## Guide
Please refer to [the Wiki](https://github.com/dolanmiu/docx/wiki) for details on how to use this library, examples and much more!
## Simple Usage
```js
// Used to create docx files
@ -61,326 +41,34 @@ var docx = require('docx');
// Create document
var doc = new docx.Document();
// Add some content in the document
var paragraph = new docx.Paragraph("Some cool text here.");
// Add more text into the paragraph if you wish
paragraph.addRun(new docx.TextRun('Lorem Ipsum Foo Bar'));
doc.addParagraph(paragraph);
// Used to export the file into a .docx file
var exporter = new docx.LocalPacker(doc);
// Or use the express packer to make the file downloadable.
// res is express' Response object
var exporter = new docx.ExpressPacker(doc, res);
var exporter = new docx.LocalPacker(doc);
```
## Create simple Word Document
```js
var doc = new docx.Document();
var paragraph = new docx.Paragraph();
var text = new docx.TextRun('Hello World');
paragraph.addText(text);
doc.addParagraph(paragraph);
exporter.pack('My First Document');
// done! A file called 'My First Document.docx'
// will be in your file system if you used LocalPacker
// Or it will start downloading if you are using Express
```
### Document properties
You can add properties to the Word document by specifying options, for example:
```js
var doc = new docx.Document({
creator: 'Dolan Miu',
description: 'My extremely interesting document',
title: 'My Document'
});
```
## Examples
Check [the Wiki](https://github.com/dolanmiu/docx/wiki/Examples) for examples.
#### Full list of options:
```
creator
description
title
subject
keywords
lastModifiedBy
revision
```
-----
You can mix and match whatever properties you want, or provide no properties.
Made with 💖
## Create Paragraph
Every text block in OpenXML is organised in paragraphs. You can add more text to the paragraph by doing this:
```js
var paragraph = new docx.Paragraph(),
```
```js
var text = new docx.TextRun('Lorem Ipsum Foo Bar');
var paragraph = new docx.Paragraph();
paragraph.addText(text);
```
```js
var paragraph = new docx.Paragraph("Short hand notation for adding text.");
```
After you create the paragraph, you must add the paragraph into the `document`:
```js
doc.addParagraph(paragraph);
```
### Styles
Styles is a very important part of the look of a word document. At the moment, only headings and title is supported, but son the rest will be supported along with custom styles!
![Word 2013 Styles menu](http://content.gcflearnfree.org/topics/233/style_apply_choose.png "Word 2013 Styles menu")
#### Heading1 - Heading5
```js
paragraph.heading1();
paragraph.heading2();
paragraph.heading3();
paragraph.heading4();
paragraph.heading5();
```
#### Title
```js
paragraph.title();
```
### Text Alignment
To change the text alignment of a paragraph, for center, left, right or justified:
```js
paragraph.center();
```
```js
paragraph.left();
```
```js
paragraph.right();
```
```js
paragraph.justified();
```
#### Example
```js
paragraph.heading1().center();
```
The above will create a `heading 1` which is `centered`.
### Thematic Break
To add a break in the page, simply add `.thematicBreak()` on a paragraph:
```js
var paragraph = new docx.Paragraph("Amazing Heading").heading1().thematicBreak();
```
The above example will create a heading with a page break directly under it.
### Page Break
To move to a new page (insert a page break), simply add `.pageBreak()` on a paragraph:
```js
var paragraph = new docx.Paragraph("Amazing Heading").heading1().pageBreak();
```
The above example will create a heading and start a new page immediately afterwards.
## Text
Paragraphs need `text run` objects. To create text:
```js
var text = new docx.TextRun("My awesome text here for my university dissertation");
paragraph.addText(text);
```
Text objects have methods inside which changes the way the text is displayed.
### Typographical Emphasis
More info [here](https://english.stackexchange.com/questions/97081/what-is-the-typography-term-which-refers-to-the-usage-of-bold-italics-and-unde)
#### Bold
```js
text.bold();
```
#### Italics
```js
text.italic();
```
#### Underline
```js
text.underline();
```
#### Strike through
```js
text.strike();
```
#### Double strike through
```js
text.doubleStrike();
```
#### Superscript
```js
text.superScript();
```
#### Subscript
```js
text.subScript();
```
#### All Capitals
```js
text.allCaps();
```
#### Small Capitals
```js
text.smallCaps();
```
### Break
Sometimes you would want to put text underneath another line of text but inside the same paragraph.
```js
text.break();
```
### Chaining
What if you want to create a paragraph which is ***bold*** and ***italic***?
```js
paragraph.bold().italic();
```
## Bullet Points
To make a bullet point, simply make a paragraph into a bullet point:
```js
var text = new docx.TextRun("Bullet points");
var paragraph = new docx.Paragraph(text).bullet();
var text2 = new docx.TextRun("Are awesome");
var paragraph2 = new docx.Paragraph(text2).bullet();
doc.addParagraph(paragraph);
doc.addParagraph(paragraph2);
```
This will produce:
* Bullet points
* Are awesome
## Tab Stops
If you do not know why tab stops are useful, then I recommend you do a bit of research. It enables side by side text which is nicely laid out without the need for tables, or constantly pressing space bar.
**Note**: At the moment, the unit of measurement for a tab stop is counter intuitive for a human. It is using OpenXMLs own measuring system. For example, 2268 roughly translates to 3cm. Therefore in the future, I may consider changing it to percentages or even cm.
![Word 2013 Tabs](http://www.teachucomp.com/wp-content/uploads/blog-4-22-2015-UsingTabStopsInWord-1024x577.png "Word 2013 Tab Stops")
Simply call the relevant methods on the paragraph listed below. Then just add a `tab()` method call to a text object. Adding multiple `tabStops` will mean you would have to chain `tab()` until the desired `tabStop` is selected. Example is shown below.
### Left Tab Stop
```js
paragraph.leftTabStop(2268);
```
2268 is the distance from the left side.
### Center Tab Stop
```js
paragraph.centerTabStp(2268);
```
2268 is the distance from the left side.
### Right Tab Stop
```js
paragraph.rightTabStop(2268);
```
2268 is the distance from the left side.
### Max Right Tab Stop
```js
paragraph.maxRightTabStop();
```
This will create a tab stop on the very edge of the right hand side. Handy for right aligning and left aligning text on the same line.
### Example
```js
var paragraph = new docx.Paragraph().maxRightTabStop();
var leftText = new docx.TextRun("Hey everyone").bold();
var rightText = new docx.TextRun("11th November 2015").tab();
paragraph.addText(leftText);
paragraph.addText(rightText);
```
The example above will create a left aligned text, and a right aligned text on the same line. The laymans approach to this problem would be to either use text boxes or tables. YUK!
```js
var paragraph = new docx.Paragraph();
paragraph.maxRightTabStop();
paragraph.leftTabStop(1000);
var text = new docx.TextRun("Second tab stop here I come!").tab().tab();
paragraph.addText(text);
```
The above shows the use of two tab stops, and how to select/use it.
# Exporting
I used the express exporter in my [website](http://www.dolan.bio). It's very useful, and is the preferred way if you want to make a downloadable file for a visitor. it is much better than generating a physical file on the server, and then passing a download link to that file.
## Express
Simply use the exporter, and pass in the necessary parameters:
```js
var docx = require('docx');
var doc = new docx.Document();
var exporter = new docx.ExpressPacker(doc, res);
exporter.pack('My Document');
```
where `res` is the response object obtained through the Express router. It is that simple. The file will begin downloading in the browser.
## Standalone .docx file
```js
var docx = require('docx');
var doc = new docx.Document();
var exporter = new docx.LocalPacker(doc);
exporter.pack('My Document');
```
# Examples
## In practice
I used this library in my personal portfolio/CV website. Click generate CV for a demonstration. [http://www.dolan.bio](http://www.dolan.bio)
## General
#### Simple paragraph
```js
var doc = new docx.Document();
var paragraph = new docx.Paragraph("Hello World");
var institutionText = new docx.TextRun("University College London").bold(),
var dateText = new docx.TextRun("5th Dec 2015").tab().bold();
paragraph.addText(institutionText);
paragraph.addText(dateText);
doc.addParagraph(paragraph);
var exporter = new docx.LocalPacker(doc);
exporter.pack('My Document');
```
Or:
```js
var doc = new docx.Document();
var paragraph = new docx.Paragraph("Hello World");
var institutionText = new docx.TextRun("University College London").bold(),
var dateText = new docx.TextRun("5th Dec 2015").tab().bold();
paragraph.addText(institutionText);
paragraph.addText(dateText);
var exporter = new docx.ExpressPacker(doc, res);
exporter.pack('My Document');
```
Would produce:
***University College London***
***5th Dec 2015***
Made with 💖 by Dolan Miu 🍆 💦 😝
# License
MIT © [Dolan Miu](http://www.dolan.bio)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Huge thanks to [@felipeochoa](https://github.com/felipeochoa) for awesome contributions to this project
[npm-image]: https://badge.fury.io/js/docx.svg
[npm-url]: https://npmjs.org/package/docx
@ -389,4 +77,9 @@ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
[daviddm-image]: https://david-dm.org/dolanmiu/docx.svg?theme=shields.io
[daviddm-url]: https://david-dm.org/dolanmiu/docx
[snky-image]: https://snyk.io/test/github/dolanmiu/docx/badge.svg
[snky-url]: https://snyk.io/test/github/dolanmiu/docx
[snky-url]: https://snyk.io/test/github/dolanmiu/docx
[gitter-image]: https://badges.gitter.im/dolanmiu/docx.svg
[gitter-url]: https://gitter.im/docx-lib/Lobby
[gemnasium-image]: https://gemnasium.com/badges/github.com/dolanmiu/docx.svg
[gemnasium-url]: https://gemnasium.com/github.com/dolanmiu/docx

22
demo/demo.js Normal file
View File

@ -0,0 +1,22 @@
const docx = require('../build');
var doc = new docx.Document();
var paragraph = new docx.Paragraph("Hello World");
var institutionText = new docx.TextRun("University College London").bold();
var dateText = new docx.TextRun("5th Dec 2015").tab().bold();
paragraph.addRun(institutionText);
paragraph.addRun(dateText);
doc.addParagraph(paragraph);
// Feature coming soon
// var media = new docx.Media();
// media.addMedia("happy-penguins", "./demo/penguins.jpg");
// var pictureRun = new docx.PictureRun(media.getMedia("happy-penguins"));
// var exporter = new docx.LocalPacker(doc);
var exporter = new docx.LocalPacker(doc);
exporter.pack('My Document');
console.log('Document created succesfully at project root!');

70
demo/demo2.js Normal file
View File

@ -0,0 +1,70 @@
const docx = require('../build');
const styles = new docx.Styles();
styles.createParagraphStyle('Heading1', 'Heading 1')
.basedOn("Normal")
.next("Normal")
.quickFormat()
.size(28)
.bold()
.italics()
.spacing({after: 120});
styles.createParagraphStyle('Heading2', 'Heading 2')
.basedOn("Normal")
.next("Normal")
.quickFormat()
.size(26)
.bold()
.underline('double', 'FF0000')
.spacing({before: 240, after: 120});
styles.createParagraphStyle('aside', 'Aside')
.basedOn('Normal')
.next('Normal')
.color('999999')
.italics()
.indent(720)
.spacing({line: 276});
styles.createParagraphStyle('wellSpaced', 'Well Spaced')
.basedOn('Normal')
.spacing({line: 276, before: 20 * 72 * .1, after: 20 * 72 * .05});
styles.createParagraphStyle('ListParagraph', 'List Paragraph')
.quickFormat()
.basedOn('Normal');
const numbering = new docx.Numbering();
const numberedAbstract = numbering.createAbstractNumbering();
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "left");
const doc = new docx.Document({
creator: 'Clippy',
title: 'Sample Document',
description: 'A brief example of using docx',
});
doc.createParagraph('Test heading1, bold and italicized').heading1();
doc.createParagraph('Some simple content');
doc.createParagraph('Test heading2 with double red underline').heading2();
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
const letterNumbering5 = numbering.createConcreteNumbering(numberedAbstract);
letterNumbering5.overrideLevel(0, 5);
doc.createParagraph('Option1').setNumbering(letterNumbering, 0);
doc.createParagraph('Option5 -- override 2 to 5').setNumbering(letterNumbering5, 0);
doc.createParagraph('Option3').setNumbering(letterNumbering, 0);
doc.createParagraph()
.createTextRun('Some monospaced content')
.font('Monospace');
doc.createParagraph('An aside, in light gray italics and indented').style('aside');
doc.createParagraph('This is normal, but well-spaced text').style('wellSpaced');
doc.createParagraph('This is normal');
const exporter = new docx.LocalPacker(doc, styles, undefined, numbering);
exporter.pack('test.docx');

BIN
demo/penguins.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -1,17 +1,21 @@
{
"name": "docx",
"version": "1.2.0",
"version": "2.0.1",
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
"main": "build/index.js",
"scripts": {
"pretest": "rimraf ./build-tests && tsc -p ts/test-tsconfig.json",
"test": "mocha ./build-tests --recursive",
"prepublishOnly": "tsc -p ts",
"lint": "tslint --project ./ts"
"prepublishOnly": "npm run build",
"lint": "tslint --project ./ts",
"build": "rimraf ./build && tsc -p ts",
"demo": "npm run build && node ./demo/demo.js",
"demo2": "npm run build && node ./demo/demo2.js"
},
"files": [
"ts",
"build"
"build",
"template"
],
"repository": {
"type": "git",
@ -19,7 +23,10 @@
},
"keywords": [
"office",
"word",
"generate",
"creator",
"create",
"document",
"doc",
"officegen",
@ -28,12 +35,7 @@
"dependencies": {
"@types/archiver": "^0.15.37",
"@types/express": "^4.0.35",
"@types/lodash": "^4.14.54",
"app-root-path": "^2.0.1",
"archiver": "^1.3.0",
"install": "^0.8.7",
"lodash": "^4.6.1",
"npm": "^4.3.0",
"xml": "^1.0.1"
},
"author": "Dolan Miu",

View File

@ -1,15 +1,12 @@
import { Attributes, XmlComponent } from "../../xml-components";
import { SectionProperties } from "./section-properties";
import { XmlComponent } from "../../xml-components";
export class Body extends XmlComponent {
constructor() {
super("w:body");
// this.root.push(new SectionProperties()); not actually needed
}
public push(component: XmlComponent): void {
// this.root.splice(this.body.length - 1, 0, component);
this.root.push(component);
}
}

View File

@ -26,39 +26,30 @@ interface IDocumentAttributesProperties {
type?: string;
}
export class DocumentAttributes extends XmlAttributeComponent {
constructor(properties?: IDocumentAttributesProperties) {
super({
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",
cp: "xmlns:cp",
dc: "xmlns:dc",
dcterms: "xmlns:dcterms",
dcmitype: "xmlns:dcmitype",
xsi: "xmlns:xsi",
type: "xsi:type",
}, properties);
this.root = properties;
if (!properties) {
this.root = {};
}
}
export class DocumentAttributes extends XmlAttributeComponent<IDocumentAttributesProperties> {
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",
cp: "xmlns:cp",
dc: "xmlns:dc",
dcterms: "xmlns:dcterms",
dcmitype: "xmlns:dcmitype",
xsi: "xmlns:xsi",
type: "xsi:type",
};
}

View File

@ -1,7 +1,9 @@
import { Paragraph } from "../paragraph";
import { Table } from "../table";
import { XmlComponent } from "../xml-components";
import { Body } from "./body";
import { DocumentAttributes } from "./document-attributes";
export class Document extends XmlComponent {
private body: Body;
@ -34,8 +36,20 @@ export class Document extends XmlComponent {
this.body.push(paragraph);
}
public clearVariables(): void {
this.body.clearVariables();
delete this.body;
public createParagraph(text?: string): Paragraph {
const para = new Paragraph(text);
this.addParagraph(para);
return para;
}
public addTable(table: Table): void {
this.body.push(table);
}
public createTable(rows: number, cols: number): Table {
const table = new Table(rows, cols);
this.addTable(table);
return table;
}
}

View File

@ -2,3 +2,6 @@ export { Document } from "./document";
export { Paragraph } from "./paragraph";
export { Run } from "./run";
export { TextRun } from "./run/text-run";
export { PictureRun } from "./run/picture-run";
export { Table } from "./table";
// Perhaps all run related stuff can be exported from run, instead of exporting Run, TextRun, PictureRun seperately.

View File

@ -0,0 +1,15 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
type alignmentOptions = "left" | "center" | "right" | "both";
class AlignmentAttributes extends XmlAttributeComponent<{val: alignmentOptions}> {
protected xmlKeys = {val: "w:val"};
}
export class Alignment extends XmlComponent {
constructor(type: alignmentOptions) {
super("w:jc");
this.root.push(new AlignmentAttributes({val: type}));
}
}

View File

@ -0,0 +1,9 @@
export { Alignment } from "./alignment";
export { ThematicBreak } from "./border";
export { Indent } from "./indent";
export { PageBreak } from "./page-break";
export { ParagraphProperties } from "./properties";
export { ISpacingProperties, Spacing } from "./spacing";
export { Style } from "./style";
export { LeftTabStop, MaxRightTabStop } from "./tab-stop";
export { NumberProperties } from "./unordered-list";

View File

@ -0,0 +1,24 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
interface IIndentAttributesProperties {
left?: number;
hanging?: number;
}
class IndentAttributes extends XmlAttributeComponent<IIndentAttributesProperties> {
protected xmlKeys = {
left: "w:left",
hanging: "w:hanging",
};
}
export class Indent extends XmlComponent {
constructor(left: number, hanging?: number) {
super("w:ind");
this.root.push(new IndentAttributes({
left: left,
hanging: hanging,
}));
}
}

View File

@ -1,23 +1,20 @@
import { IData } from "../../media/data";
import { Num } from "../../numbering/num";
import { Run } from "../run";
import { PictureRun } from "../run/picture-run";
import { TextRun } from "../run/text-run";
import { Attributes, XmlComponent } from "../xml-components";
import { XmlComponent } from "../xml-components";
import { Alignment } from "./alignment";
import { ThematicBreak } from "./border";
import { Indent } from "./indent";
import { PageBreak } from "./page-break";
import { ParagraphProperties } from "./properties";
import { ISpacingProperties, Spacing } from "./spacing";
import { Style } from "./style";
import { LeftTabStop, MaxRightTabStop } from "./tab-stop";
import { NumberProperties } from "./unordered-list";
class Alignment extends XmlComponent {
constructor(type: string) {
super("w:jc");
this.root.push(new Attributes({
val: type,
}));
}
}
export class Paragraph extends XmlComponent {
private properties: ParagraphProperties;
@ -30,11 +27,23 @@ export class Paragraph extends XmlComponent {
}
}
public addText(run: TextRun): Paragraph {
public addRun(run: Run): Paragraph {
this.root.push(run);
return this;
}
public createTextRun(text: string): TextRun {
const run = new TextRun(text);
this.addRun(run);
return run;
}
public createPictureRun(imageData: IData): PictureRun {
const run = new PictureRun(imageData);
this.addRun(run);
return run;
}
public heading1(): Paragraph {
this.properties.push(new Style("Heading1"));
return this;
@ -116,4 +125,19 @@ export class Paragraph extends XmlComponent {
this.properties.push(new NumberProperties(numbering.id, indentLevel));
return this;
}
public style(styleId: string): Paragraph {
this.properties.push(new Style(styleId));
return this;
}
public indent(start: number, hanging?: number): Paragraph {
this.properties.push(new Indent(start, hanging));
return this;
}
public spacing(params: ISpacingProperties): Paragraph {
this.properties.push(new Spacing(params));
return this;
};
}

View File

@ -1,10 +1,9 @@
import { Attributes, XmlComponent } from "../xml-components";
import { XmlComponent } from "../xml-components";
export class ParagraphProperties extends XmlComponent {
constructor() {
super("w:pPr");
this.root.push(new Attributes());
}
public push(item: XmlComponent): void {

View File

@ -0,0 +1,22 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
export interface ISpacingProperties {
after?: number;
before?: number;
line?: number;
}
class SpacingAttributes extends XmlAttributeComponent<ISpacingProperties> {
protected xmlKeys = {
after: "w:after",
before: "w:before",
line: "w:line",
};
}
export class Spacing extends XmlComponent {
constructor(opts: ISpacingProperties) {
super("w:spacing");
this.root.push(new SpacingAttributes(opts));
}
}

View File

@ -1,4 +1,4 @@
import { Attributes, XmlComponent } from "../xml-components";
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
class TabStop extends XmlComponent {
@ -8,11 +8,17 @@ class TabStop extends XmlComponent {
}
}
export type tabOptions = "left" | "right";
class TabAttributes extends XmlAttributeComponent<{val: tabOptions, pos: string | number}> {
protected xmlKeys = {val: "w:val", pos: "w:pos"};
}
class Tab extends XmlComponent {
constructor(value: string, position: any) {
constructor(value: tabOptions, position: string | number) {
super("w:tab");
this.root.push(new Attributes({
this.root.push(new TabAttributes({
val: value,
pos: position,
}));

View File

@ -1,5 +1,4 @@
import { Attributes, XmlComponent } from "../xml-components";
import { Style } from "./style";
export class NumberProperties extends XmlComponent {

View File

@ -1,4 +1,7 @@
import { Attributes, XmlComponent } from "../xml-components";
export { Underline } from "./underline";
export { SubScript, SuperScript } from "./script";
export { RunFonts } from "./run-fonts";
export class Bold extends XmlComponent {

View File

@ -1,14 +1,14 @@
import { Break } from "./break";
import { Caps, SmallCaps } from "./caps";
import { Bold, Italics } from "./formatting";
import { Bold, Color, DoubleStrike, Italics, Size, Strike } from "./formatting";
import { RunProperties } from "./properties";
import { RunFonts } from "./run-fonts";
import { SubScript, SuperScript } from "./script";
import { DoubleStrike, Strike } from "./strike";
import { Style } from "./style";
import { Tab } from "./tab";
import { Underline } from "./underline";
import { Attributes, XmlComponent } from "../xml-components";
import { XmlComponent } from "../xml-components";
export class Run extends XmlComponent {
private properties: RunProperties;
@ -29,8 +29,18 @@ export class Run extends XmlComponent {
return this;
}
public underline(): Run {
this.properties.push(new Underline());
public underline(underlineType?: string, color?: string): Run {
this.properties.push(new Underline(underlineType, color));
return this;
}
public color(color: string): Run {
this.properties.push(new Color(color));
return this;
}
public size(size: number): Run {
this.properties.push(new Size(size));
return this;
}
@ -78,4 +88,9 @@ export class Run extends XmlComponent {
this.properties.push(new RunFonts(fontName));
return this;
}
public style(styleId: string): Run {
this.properties.push(new Style(styleId));
return this;
}
}

View File

@ -0,0 +1,16 @@
import { IData } from "../../media/data";
import { Run } from "../run";
import { Drawing } from "./run-components/drawing";
export class PictureRun extends Run {
constructor(imageData: IData) {
super();
if (imageData === undefined) {
throw new Error("imageData cannot be undefined");
}
this.root.push(new Drawing(imageData));
}
}

View File

@ -0,0 +1,16 @@
import { IData } from "../../../../media/data";
import { XmlComponent } from "../../../xml-components";
import { Inline } from "./inline";
export class Drawing extends XmlComponent {
constructor(imageData: IData) {
super("w:drawing");
if (imageData === undefined) {
throw new Error("imageData cannot be undefined");
}
this.root.push(new Inline(imageData.referenceId));
}
}

View File

@ -0,0 +1,10 @@
import { XmlComponent } from "../../../../../../xml-components";
import { Pic } from "./pic";
export class GraphicData extends XmlComponent {
constructor(referenceId: number) {
super("a:graphicData");
this.root.push(new Pic(referenceId));
}
}

View File

@ -0,0 +1,14 @@
import { XmlComponent } from "../../../../../../../../xml-components";
import { Blip } from "./blip";
import { SourceRectangle } from "./source-rectangle";
import { Stretch } from "./stretch";
export class BlipFill extends XmlComponent {
constructor(referenceId: number) {
super("pic:blipFill");
this.root.push(new Blip(referenceId));
this.root.push(new SourceRectangle());
this.root.push(new Stretch());
}
}

View File

@ -0,0 +1,21 @@
import { XmlAttributeComponent, XmlComponent } from "../../../../../../../../xml-components";
interface IBlipProperties {
embed: string;
}
class BlipAttributes extends XmlAttributeComponent<IBlipProperties> {
protected xmlKeys = {
embed: "r:embed",
};
}
export class Blip extends XmlComponent {
constructor(referenceId: number) {
super("a:blip");
this.root.push(new BlipAttributes({
embed: `rId${referenceId}`,
}));
}
}

View File

@ -0,0 +1,8 @@
import { XmlComponent } from "../../../../../../../../xml-components";
export class SourceRectangle extends XmlComponent {
constructor() {
super("a:srcRect");
}
}

View File

@ -0,0 +1,16 @@
import { XmlComponent } from "../../../../../../../../xml-components";
class FillRectangle extends XmlComponent {
constructor() {
super("a:fillRect");
}
}
export class Stretch extends XmlComponent {
constructor() {
super("a:stretch");
this.root.push(new FillRectangle());
}
}

View File

@ -0,0 +1,10 @@
import { XmlComponent } from "../../../../../../../xml-components";
import { BlipFill } from "./blip/blip-fill";
export class Pic extends XmlComponent {
constructor(referenceId: number) {
super("pic:pic");
this.root.push(new BlipFill(referenceId));
}
}

View File

@ -0,0 +1,24 @@
import { XmlAttributeComponent, XmlComponent } from "../../../../../xml-components";
import { GraphicData } from "./graphic-data";
interface IGraphicProperties {
a: string;
}
class GraphicAttributes extends XmlAttributeComponent<IGraphicProperties> {
protected xmlKeys = {
a: "xmlns:a",
};
}
export class Graphic extends XmlComponent {
constructor(referenceId: number) {
super("a:graphic");
this.root.push(new GraphicAttributes({
a: "http://schemas.openxmlformats.org/drawingml/2006/main",
}));
this.root.push(new GraphicData(referenceId));
}
}

View File

@ -0,0 +1,10 @@
import { XmlComponent } from "../../../../xml-components";
import { Graphic } from "./graphic";
export class Inline extends XmlComponent {
constructor(referenceId: number) {
super("wp:inline");
this.root.push(new Graphic(referenceId));
}
}

View File

@ -0,0 +1,11 @@
import { XmlComponent } from "../../xml-components";
export class Text extends XmlComponent {
constructor(text: string) {
super("w:t");
if (text) {
this.root.push(text);
}
}
}

View File

@ -6,15 +6,12 @@ interface IRunFontAttributesProperties {
hint?: string;
}
class RunFontAttributes extends XmlAttributeComponent {
constructor(properties: IRunFontAttributesProperties) {
super({
ascii: "w:ascii",
hAnsi: "w:hAnsi",
hint: "w:hint",
}, properties);
}
class RunFontAttributes extends XmlAttributeComponent<IRunFontAttributesProperties> {
protected xmlKeys = {
ascii: "w:ascii",
hAnsi: "w:hAnsi",
hint: "w:hint",
};
}
export class RunFonts extends XmlComponent {

View File

@ -1,15 +0,0 @@
import { XmlComponent } from "../xml-components";
export class Strike extends XmlComponent {
constructor() {
super("w:strike");
}
}
export class DoubleStrike extends XmlComponent {
constructor() {
super("w:dstrike");
}
}

13
ts/docx/run/style.ts Normal file
View File

@ -0,0 +1,13 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
class StyleAttributes extends XmlAttributeComponent<{val: string}> {
protected xmlKeys = {val: "w:val"};
}
export class Style extends XmlComponent {
constructor(styleId: string) {
super("w:rStyle");
this.root.push(new StyleAttributes({val: styleId}));
}
}

View File

@ -1,5 +1,5 @@
import { Run } from "../run";
import { Text } from "./text";
import { Text } from "./run-components/text";
export class TextRun extends Run {

View File

@ -1,9 +0,0 @@
import { XmlUnitComponent } from "../xml-components";
export class Text extends XmlUnitComponent {
constructor(text: string) {
super("w:t");
this.root = text;
}
}

View File

@ -13,8 +13,8 @@ abstract class BaseUnderline extends XmlComponent {
export class Underline extends BaseUnderline {
constructor() {
super("");
constructor(underlineType: string = "single", color?: string) {
super(underlineType, color);
}
}

View File

@ -1,3 +0,0 @@
export class Table {
}

21
ts/docx/table/grid.ts Normal file
View File

@ -0,0 +1,21 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
export class TableGrid extends XmlComponent {
constructor(cols: number[]) {
super("w:tblGrid");
cols.forEach((col) => this.root.push(new GridCol(col)));
}
}
class GridColAttributes extends XmlAttributeComponent<{w: number}> {
protected xmlKeys = {w: "w:w"};
}
export class GridCol extends XmlComponent {
constructor(width?: number) {
super("w:gridCol");
if (width !== undefined) {
this.root.push(new GridColAttributes({w: width}));
}
}
}

123
ts/docx/table/index.ts Normal file
View File

@ -0,0 +1,123 @@
import { Paragraph } from "../paragraph";
import { XmlComponent } from "../xml-components";
import { TableGrid } from "./grid";
import { TableProperties, widthTypes } from "./properties";
export class Table extends XmlComponent {
private properties: TableProperties;
private rows: TableRow[];
private grid: TableGrid;
constructor(rows: number, cols: number) {
super("w:tbl");
this.properties = new TableProperties();
this.root.push(this.properties);
const gridCols: number[] = [];
for (let i = 0; i < cols; i++) {
/*
0-width columns don't get rendered correctly, so we need
to give them some value. A reasonable default would be
~6in / numCols, but if we do that it becomes very hard
to resize the table using setWidth, unless the layout
algorithm is set to 'fixed'. Instead, the approach here
means even in 'auto' layout, setting a width on the
table will make it look reasonable, as the layout
algorithm will expand columns to fit its content
*/
gridCols.push(1);
}
this.grid = new TableGrid(gridCols);
this.root.push(this.grid);
this.rows = [];
for (let i = 0; i < rows; i++) {
const cells: TableCell[] = [];
for (let j = 0; j < cols; j++) {
cells.push(new TableCell());
}
const row = new TableRow(cells);
this.rows.push(row);
this.root.push(row);
}
}
public getRow(ix: number): TableRow {
return this.rows[ix];
}
public getCell(row: number, col: number): TableCell {
return this.getRow(row).getCell(col);
}
public setWidth(type: widthTypes, width: number | string): Table {
this.properties.setWidth(type, width);
return this;
}
public fixedWidthLayout(): Table {
this.properties.fixedWidthLayout();
return this;
}
}
class TableRow extends XmlComponent {
private properties: TableRowProperties;
private cells: TableCell[];
constructor(cells: TableCell[]) {
super("w:tr");
this.properties = new TableRowProperties();
this.root.push(this.properties);
this.cells = cells;
cells.forEach((c) => this.root.push(c));
}
public getCell(ix: number): TableCell {
return this.cells[ix];
}
}
class TableRowProperties extends XmlComponent {
constructor() {
super("w:trPr");
}
}
class TableCell extends XmlComponent {
private properties: TableCellProperties;
constructor() {
super("w:tc");
this.properties = new TableCellProperties();
this.root.push(this.properties);
}
public addContent(content: Paragraph | Table): TableCell {
this.root.push(content);
return this;
}
public prepForXml(): object {
// Cells must end with a paragraph
const retval = super.prepForXml();
const content = retval["w:tc"];
if (!content[content.length - 1]["w:p"]) {
content.push(new Paragraph().prepForXml());
}
return retval;
}
public createParagraph(text?: string): Paragraph {
const para = new Paragraph(text);
this.addContent(para);
return para;
}
}
class TableCellProperties extends XmlComponent {
constructor() {
super("w:tcPr");
}
}

View File

@ -0,0 +1,48 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
export type widthTypes = "dxa" | "pct" | "nil" | "auto";
export class TableProperties extends XmlComponent {
constructor() {
super("w:tblPr");
}
public setWidth(type: widthTypes, w: number | string): TableProperties {
this.root.push(new PreferredTableWidth(type, w));
return this;
}
public fixedWidthLayout(): TableProperties {
this.root.push(new TableLayout("fixed"));
return this;
}
}
interface ITableWidth {
type: widthTypes;
w: number | string;
}
class TableWidthAttributes extends XmlAttributeComponent<ITableWidth> {
protected xmlKeys = {type: "w:type", w: "w:w"};
}
class PreferredTableWidth extends XmlComponent {
constructor(type: widthTypes, w: number | string) {
super("w:tblW");
this.root.push(new TableWidthAttributes({type, w}));
}
}
type tableLayout = "autofit" | "fixed";
class TableLayoutAttributes extends XmlAttributeComponent<{type: tableLayout}> {
protected xmlKeys = {type: "w:type"};
}
class TableLayout extends XmlComponent {
constructor(type: tableLayout) {
super("w:tblLayout");
this.root.push(new TableLayoutAttributes({type}));
}
}

View File

@ -19,32 +19,29 @@ interface IAttributesProperties {
footer?: string;
gutter?: string;
linePitch?: string;
pos?: string;
pos?: string | number; // Little strange. Perhaps it is normal. Need to clarify in the spec.
}
export class Attributes extends XmlAttributeComponent {
constructor(properties?: IAttributesProperties) {
super({
val: "w:val",
color: "w:color",
space: "w:space",
sz: "w:sz",
type: "w:type",
rsidR: "w:rsidR",
rsidRPr: "w:rsidRPr",
rsidSect: "w:rsidSect",
w: "w:w",
h: "w:h",
top: "w:top",
right: "w:right",
bottom: "w:bottom",
left: "w:left",
header: "w:header",
footer: "w:footer",
gutter: "w:gutter",
linePitch: "w:linePitch",
pos: "w:pos",
}, properties);
}
export class Attributes extends XmlAttributeComponent<IAttributesProperties> {
protected xmlKeys = {
val: "w:val",
color: "w:color",
space: "w:space",
sz: "w:sz",
type: "w:type",
rsidR: "w:rsidR",
rsidRPr: "w:rsidRPr",
rsidSect: "w:rsidSect",
w: "w:w",
h: "w:h",
top: "w:top",
right: "w:right",
bottom: "w:bottom",
left: "w:left",
header: "w:header",
footer: "w:footer",
gutter: "w:gutter",
linePitch: "w:linePitch",
pos: "w:pos",
};
}

View File

@ -5,9 +5,5 @@ export abstract class BaseXmlComponent {
this.rootKey = rootKey;
}
public abstract replaceKey(): void;
public clearVariables(): void {
// Do Nothing
}
public abstract prepForXml(): object;
}

View File

@ -1,30 +1,25 @@
import * as _ from "lodash";
import { BaseXmlComponent } from "./base";
export abstract class XmlAttributeComponent extends BaseXmlComponent {
protected root: Object;
private xmlKeys: Object;
type AttributeMap<T> = {[P in keyof T]: string};
constructor(xmlKeys: Object, properties: Object) {
export abstract class XmlAttributeComponent<T> extends BaseXmlComponent {
protected root: T;
protected xmlKeys: AttributeMap<T>;
constructor(properties: T) {
super("_attr");
this.xmlKeys = xmlKeys;
this.root = properties;
if (!properties) {
this.root = {};
}
}
public replaceKey(): void {
if (this.root !== undefined) {
_.forOwn(this.root, (value, key) => {
public prepForXml(): {_attr: {[key: string]: (string | number | boolean)}} {
const attrs = {};
Object.keys(this.root).forEach((key) => {
const value = this.root[key];
if (value !== undefined) {
const newKey = this.xmlKeys[key];
this.root[newKey] = value;
delete this.root[key];
});
this[this.rootKey] = this.root;
delete this.root;
}
attrs[newKey] = value;
}
});
return {_attr: attrs};
}
}

View File

@ -1,30 +1,26 @@
import * as _ from "lodash";
import { BaseXmlComponent } from "./base";
export { BaseXmlComponent };
export abstract class XmlComponent extends BaseXmlComponent {
protected root: BaseXmlComponent[];
protected root: Array<BaseXmlComponent | string>;
constructor(rootKey: string) {
super(rootKey);
this.root = new Array<BaseXmlComponent>();
}
public replaceKey(): void {
// console.log(this.rootKey);
// console.log(this.root);
if (this.root !== undefined) {
this.root.forEach((root) => {
if (root && root instanceof BaseXmlComponent) {
root.replaceKey();
}
});
this[this.rootKey] = this.root;
delete this.root;
}
public prepForXml(): object {
const children = this.root.map((comp) => {
if (comp instanceof BaseXmlComponent) {
return comp.prepForXml();
}
return comp;
}).filter((comp) => comp); // Exclude null, undefined, and empty strings
return {
[this.rootKey]: children,
};
}
}
export * from "./attributes"
export * from "./default-attributes";
export * from "./unit";
export * from "./property";

View File

@ -1,57 +0,0 @@
import { ParagraphProperties } from "../paragraph/properties";
import { RunProperties } from "../run/properties";
import { XmlComponent } from "./";
export class ParagraphPropertyXmlComponent extends XmlComponent {
private paragraphProperties: ParagraphProperties;
constructor(rootKey: string) {
super(rootKey);
this.paragraphProperties = new ParagraphProperties();
this.root.push(this.paragraphProperties);
}
public clearVariables(): void {
this.paragraphProperties.clearVariables();
delete this.paragraphProperties;
}
}
export class RunPropertyXmlComponent extends XmlComponent {
private runProperties: RunProperties;
constructor(rootKey: string) {
super(rootKey);
this.runProperties = new RunProperties();
this.root.push(this.runProperties);
}
public clearVariables(): void {
this.runProperties.clearVariables();
delete this.runProperties;
}
}
export class MultiPropertyXmlComponent extends XmlComponent {
private runProperties: RunProperties;
private paragraphProperties: ParagraphProperties;
constructor(rootKey: string) {
super(rootKey);
this.runProperties = new RunProperties();
this.root.push(this.runProperties);
this.paragraphProperties = new ParagraphProperties();
this.root.push(this.paragraphProperties);
}
public clearVariables(): void {
this.runProperties.clearVariables();
this.paragraphProperties.clearVariables();
delete this.runProperties;
delete this.paragraphProperties;
}
}

View File

@ -1,16 +0,0 @@
import {BaseXmlComponent} from "./base";
export abstract class XmlUnitComponent extends BaseXmlComponent {
protected root: string;
constructor(rootKey: string) {
super(rootKey);
}
public replaceKey(): void {
if (this.root !== undefined) {
this[this.rootKey] = this.root;
delete this.root;
}
}
}

View File

@ -1,52 +1,7 @@
import * as _ from "lodash";
import {XmlComponent} from "../docx/xml-components";
import { BaseXmlComponent } from "../docx/xml-components";
export class Formatter {
public format(input: any): Object {
input.clearVariables();
this.replaceKeys(input);
const newJson = this.clense(input);
// console.log(JSON.stringify(newJson, null, " "));
return newJson;
public format(input: BaseXmlComponent): any {
return input.prepForXml();
}
private replaceKeys(input: XmlComponent): Object {
input.replaceKey();
return input;
}
private clense(input: any): Object {
const newJson = this.jsonify(input);
this.deepTraverseJson(newJson, (parent, value, key) => {
if (key === "properties") {
delete parent[key];
}
if (key === "xmlKeys") {
delete parent[key];
}
if (key === "rootKey") {
delete parent[key];
}
});
return newJson;
}
private jsonify(obj: Object): Object {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
private deepTraverseJson(json: Object, lambda: (json: any, value: any, key: any) => void): void {
_.forOwn(json, (value, key) => {
if (_.isObject(value) && key !== "xmlKeys" && key !== "rootKey") {
this.deepTraverseJson(value, lambda);
}
lambda(json, value, key);
});
}
}
}

View File

@ -1,2 +1,3 @@
export { LocalPacker } from "./packer/local";
export { ExpressPacker } from "./packer/express";
export { ExpressPacker } from "./packer/express";
export { Packer } from "./packer/packer";

View File

@ -1,16 +1,16 @@
import * as express from "express";
import * as fs from "fs";
import { Document } from "../../docx/document";
import { Media } from "../../media";
import { Numbering } from "../../numbering";
import { Properties } from "../../properties";
import { Styles } from "../../styles";
import { Packer } from "./packer";
export class ExpressPacker extends Packer {
private res: express.Response;
constructor(document: Document, res: express.Response, styles?: any, properties?: Properties, numbering?: Numbering) {
super(document, styles, properties, numbering);
constructor(document: Document, res: express.Response, styles?: Styles, properties?: Properties, numbering?: Numbering, media?: Media) {
super(document, styles, properties, numbering, media);
this.res = res;
this.res.on("close", () => {
@ -19,7 +19,7 @@ export class ExpressPacker extends Packer {
}
public pack(name: string): void {
this.res.attachment(name + ".docx");
this.res.attachment(`${name}.docx`);
super.pack(this.res);
}
}

View File

@ -1,19 +1,21 @@
import * as fs from "fs";
import { Document } from "../../docx/document";
import { Media } from "../../media";
import { Numbering } from "../../numbering";
import { Properties } from "../../properties";
import { Styles } from "../../styles";
import { Packer } from "./packer";
export class LocalPacker extends Packer {
private stream: fs.WriteStream;
constructor(document: Document, styles?: any, properties?: Properties, numbering?: Numbering) {
super(document, styles, properties, numbering);
constructor(document: Document, styles?: Styles, properties?: Properties, numbering?: Numbering, media?: Media) {
super(document, styles, properties, numbering, media);
}
public pack(path: string): void {
this.stream = fs.createWriteStream(path);
path = path.replace(/.docx$/, "");
this.stream = fs.createWriteStream(`${path}.docx`);
super.pack(this.stream);
}
}

View File

@ -1,48 +1,42 @@
import * as archiver from "archiver";
import * as fs from "fs";
import * as path from "path";
import * as xml from "xml";
import { Document } from "../../docx";
import { Media } from "../../media";
import { Numbering } from "../../numbering";
import { Properties } from "../../properties";
import { Styles } from "../../styles";
import { DefaultStylesFactory } from "../../styles/factory";
import { Formatter } from "../formatter";
const appRoot = require("app-root-path");
const TEMPLATE_PATH = path.resolve(__dirname, "../../../template");
export abstract class Packer {
protected archive: any;
protected document: Document;
private formatter: Formatter;
private style: Styles;
private properties: Properties;
private numbering: Numbering;
constructor(document: Document, style?: any, properties?: Properties, numbering?: Numbering) {
constructor(
protected document: Document,
style?: Styles,
private properties: Properties = new Properties({
creator: "Un-named",
revision: "1",
lastModifiedBy: "Un-named",
}),
private numbering: Numbering = new Numbering(),
private media: Media = new Media(),
) {
this.formatter = new Formatter();
this.document = document;
this.style = style;
this.properties = properties;
this.numbering = numbering;
this.archive = archiver.create("zip", {});
if (!style) {
if (style) {
this.style = style;
} else {
const stylesFactory = new DefaultStylesFactory();
this.style = stylesFactory.newInstance();
}
if (!properties) {
this.properties = new Properties({
creator: "Un-named",
revision: "1",
lastModifiedBy: "Un-named",
});
}
if (!numbering) {
this.numbering = new Numbering();
}
this.archive.on("error", (err) => {
throw err;
});
@ -50,31 +44,26 @@ export abstract class Packer {
public pack(output: any): void {
this.archive.pipe(output);
console.log(appRoot.path + "/template");
this.archive.glob("**", {
expand: true,
cwd: appRoot.path + "/template",
cwd: TEMPLATE_PATH,
});
this.archive.glob("**/.rels", {
expand: true,
cwd: appRoot.path + "/template",
cwd: TEMPLATE_PATH,
});
// this.archive.file(appRoot.path + "/template/[Content_Types].xml", { name: "[Content_Types].xml" });
// console.log(__dirname + "/packer.js");
// this.archive.file(__dirname + "/packer.js", { name: "/[Content_Types].xml" });
/*this.archive.directory(appRoot.path + "/template", {
name: "/root/g.txt",
prefix: "root"
});*/
const xmlDocument = xml(this.formatter.format(this.document));
const xmlStyles = xml(this.formatter.format(this.style));
const xmlProperties = xml(this.formatter.format(this.properties), { declaration: { standalone: "yes", encoding: "UTF-8" } });
const xmlProperties = xml(this.formatter.format(this.properties), {
declaration: {
standalone: "yes",
encoding: "UTF-8",
},
});
const xmlNumbering = xml(this.formatter.format(this.numbering));
// console.log(JSON.stringify(this.numbering, null, " "));
console.log(xmlNumbering);
this.archive.append(xmlDocument, {
name: "word/document.xml",
});
@ -91,6 +80,12 @@ export abstract class Packer {
name: "word/numbering.xml",
});
for (const data of this.media.array) {
this.archive.append(data.stream, {
name: `media/${data.fileName}`,
});
}
this.archive.finalize();
}
}

View File

@ -1,3 +1,6 @@
export * from "./docx";
export * from "./export";
export { Numbering } from "./numbering";
export { Styles } from "./styles";
export { Media } from "./media";
export * from "./export";

8
ts/media/data.ts Normal file
View File

@ -0,0 +1,8 @@
import * as fs from "fs";
export interface IData {
referenceId: number;
stream: fs.ReadStream;
path: string;
fileName: string;
}

40
ts/media/index.ts Normal file
View File

@ -0,0 +1,40 @@
import * as fs from "fs";
import * as path from "path";
import { IData } from "./data";
export class Media {
private map: Map<string, IData>;
constructor() {
this.map = new Map<string, IData>();
}
public getMedia(key: string): IData {
const data = this.map.get(key);
if (data === undefined) {
throw new Error(`Cannot find image with the key ${key}`);
}
return data;
}
public addMedia(key: string, filePath: string): void {
this.map.set(key, {
referenceId: this.map.values.length,
stream: fs.createReadStream(filePath),
path: filePath,
fileName: path.basename(filePath),
});
}
public get array(): IData[] {
const array = new Array<IData>();
this.map.forEach((data) => {
array.push(data);
});
return array;
}
}

View File

@ -1,4 +1,3 @@
import * as _ from "lodash";
import { XmlAttributeComponent, XmlComponent } from "../docx/xml-components";
import { Level } from "./level";
import { MultiLevelType } from "./multi-level-type";
@ -8,14 +7,11 @@ interface IAbstractNumberingAttributesProperties {
restartNumberingAfterBreak?: number;
}
class AbstractNumberingAttributes extends XmlAttributeComponent {
constructor(properties: IAbstractNumberingAttributesProperties) {
super({
abstractNumId: "w:abstractNumId",
restartNumberingAfterBreak: "w15:restartNumberingAfterBreak",
}, properties);
}
class AbstractNumberingAttributes extends XmlAttributeComponent<IAbstractNumberingAttributesProperties> {
protected xmlKeys = {
abstractNumId: "w:abstractNumId",
restartNumberingAfterBreak: "w15:restartNumberingAfterBreak",
};
}
export class AbstractNumbering extends XmlComponent {
@ -40,11 +36,4 @@ export class AbstractNumbering extends XmlComponent {
this.addLevel(level);
return level;
}
public clearVariables(): void {
_.forEach(this.root, (element) => {
element.clearVariables();
});
delete this.id;
}
}

View File

@ -1,27 +0,0 @@
import { XmlAttributeComponent, XmlComponent } from "../docx/xml-components";
interface IndentAttributesProperties {
left: number;
hanging: number;
}
class IndentAttributes extends XmlAttributeComponent {
constructor(properties: IndentAttributesProperties) {
super({
left: "w:left",
hanging: "w:hanging",
}, properties);
}
}
export class Indent extends XmlComponent {
constructor(left: number, hanging: number) {
super("w:ind");
this.root.push(new IndentAttributes({
left: left,
hanging: hanging,
}));
}
}

View File

@ -1,13 +1,11 @@
import * as _ from "lodash";
import { DocumentAttributes } from "../docx/document/document-attributes";
import { Indent } from "../docx/paragraph/indent";
import { RunFonts } from "../docx/run/run-fonts";
import { MultiPropertyXmlComponent } from "../docx/xml-components";
import { XmlComponent } from "../docx/xml-components";
import { AbstractNumbering } from "./abstract-numbering";
import { Indent } from "./indent";
import { Level } from "./level";
import { Num } from "./num";
export class Numbering extends MultiPropertyXmlComponent {
export class Numbering extends XmlComponent {
private nextId: number;
constructor() {
@ -86,12 +84,4 @@ export class Numbering extends MultiPropertyXmlComponent {
this.root.push(num);
return num;
}
public clearVariables(): void {
super.clearVariables();
_.forEach(this.root, (element) => {
element.clearVariables();
});
delete this.nextId;
}
}

View File

@ -1,20 +1,19 @@
import * as paragraph from "../docx/paragraph/formatting";
import { ParagraphProperties } from "../docx/paragraph/properties";
import * as formatting from "../docx/run/formatting";
import { RunProperties } from "../docx/run/properties";
import { Attributes, MultiPropertyXmlComponent, XmlAttributeComponent, XmlComponent } from "../docx/xml-components";
import { Attributes, XmlAttributeComponent, XmlComponent } from "../docx/xml-components";
interface ILevelAttributesProperties {
ilvl?: number;
tentative?: number;
}
class LevelAttributes extends XmlAttributeComponent {
constructor(properties: ILevelAttributesProperties) {
super({
ilvl: "w:ilvl",
tentative: "w15:tentative",
}, properties);
}
class LevelAttributes extends XmlAttributeComponent<ILevelAttributesProperties> {
protected xmlKeys = {
ilvl: "w:ilvl",
tentative: "w15:tentative",
};
}
class Start extends XmlComponent {
@ -57,21 +56,29 @@ class LevelJc extends XmlComponent {
}
}
export class Level extends XmlComponent {
class LevelBase extends XmlComponent {
private paragraphProperties: ParagraphProperties;
private runProperties: RunProperties;
constructor(level: number, numberFormat: string, levelText: string, lvlJc: string) {
constructor(level: number, start?: number, numberFormat?: string, levelText?: string, lvlJc?: string) {
super("w:lvl");
this.root.push(new LevelAttributes({
ilvl: level,
tentative: 1,
}));
this.root.push(new Start(1));
this.root.push(new NumberFormat(numberFormat));
this.root.push(new LevelText(levelText));
this.root.push(new LevelJc(lvlJc));
if (start !== undefined) {
this.root.push(new Start(start));
}
if (numberFormat !== undefined) {
this.root.push(new NumberFormat(numberFormat));
}
if (levelText !== undefined) {
this.root.push(new LevelText(levelText));
}
if (lvlJc !== undefined) {
this.root.push(new LevelJc(lvlJc));
}
this.paragraphProperties = new ParagraphProperties();
this.runProperties = new RunProperties();
@ -80,14 +87,6 @@ export class Level extends XmlComponent {
this.root.push(this.runProperties);
}
public clearVariables(): void {
this.paragraphProperties.clearVariables();
this.runProperties.clearVariables();
delete this.paragraphProperties;
delete this.runProperties;
}
public addParagraphProperty(property: XmlComponent): Level {
this.paragraphProperties.push(property);
return this;
@ -97,4 +96,123 @@ export class Level extends XmlComponent {
this.runProperties.push(property);
return this;
}
// ---------- Run formatting ---------------------- //
public size(twips: number): Level {
this.addRunProperty(new formatting.Size(twips));
return this;
}
public bold(): Level {
this.addRunProperty(new formatting.Bold());
return this;
}
public italics(): Level {
this.addRunProperty(new formatting.Italics());
return this;
}
public smallCaps(): Level {
this.addRunProperty(new formatting.SmallCaps());
return this;
}
public allCaps(): Level {
this.addRunProperty(new formatting.Caps());
return this;
}
public strike(): Level {
this.addRunProperty(new formatting.Strike());
return this;
}
public doubleStrike(): Level {
this.addRunProperty(new formatting.DoubleStrike());
return this;
}
public subScript(): Level {
this.addRunProperty(new formatting.SubScript());
return this;
}
public superScript(): Level {
this.addRunProperty(new formatting.SuperScript());
return this;
}
public underline(underlineType?: string, color?: string): Level {
this.addRunProperty(new formatting.Underline(underlineType, color));
return this;
}
public color(color: string): Level {
this.addRunProperty(new formatting.Color(color));
return this;
}
public font(fontName: string): Level {
this.addRunProperty(new formatting.RunFonts(fontName));
return this;
}
// --------------------- Paragraph formatting ------------------------ //
public center(): Level {
this.addParagraphProperty(new paragraph.Alignment("center"));
return this;
}
public left(): Level {
this.addParagraphProperty(new paragraph.Alignment("left"));
return this;
}
public right(): Level {
this.addParagraphProperty(new paragraph.Alignment("right"));
return this;
}
public justified(): Level {
this.addParagraphProperty(new paragraph.Alignment("both"));
return this;
}
public thematicBreak(): Level {
this.addParagraphProperty(new paragraph.ThematicBreak());
return this;
}
public maxRightTabStop(): Level {
this.addParagraphProperty(new paragraph.MaxRightTabStop());
return this;
}
public leftTabStop(position: number): Level {
this.addParagraphProperty(new paragraph.LeftTabStop(position));
return this;
}
public indent(left: number, hanging?: number): Level {
this.addParagraphProperty(new paragraph.Indent(left, hanging));
return this;
}
public spacing(params: paragraph.ISpacingProperties): Level {
this.addParagraphProperty(new paragraph.Spacing(params));
return this;
};
}
export class Level extends LevelBase {
// This is the level that sits under abstractNum. We make a
// handful of properties required
constructor(level: number, numberFormat: string, levelText: string, lvlJc: string) {
super(level, 1, numberFormat, levelText, lvlJc);
}
}
export class LevelForOverride extends LevelBase {}

View File

@ -1,4 +1,5 @@
import { Attributes, XmlAttributeComponent, XmlComponent } from "../docx/xml-components";
import { LevelForOverride } from "./level";
class AbstractNumId extends XmlComponent {
@ -14,13 +15,8 @@ interface INumAttributesProperties {
numId: number;
}
class NumAttributes extends XmlAttributeComponent {
constructor(properties: INumAttributesProperties) {
super({
numId: "w:numId",
}, properties);
}
class NumAttributes extends XmlAttributeComponent<INumAttributesProperties> {
protected xmlKeys = {numId: "w:numId"};
}
export class Num extends XmlComponent {
@ -35,8 +31,50 @@ export class Num extends XmlComponent {
this.id = numId;
}
public clearVariables(): void {
super.clearVariables();
delete this.id;
public overrideLevel(num: number, start?: number): LevelOverride {
const olvl = new LevelOverride(num, start);
this.root.push(olvl);
return olvl;
}
}
class LevelOverrideAttributes extends XmlAttributeComponent<{ilvl: number}> {
protected xmlKeys = {ilvl: "w:ilvl"};
}
class LevelOverride extends XmlComponent {
private levelNum: number;
private lvl?: LevelForOverride;
constructor(levelNum: number, start?: number) {
super("w:lvlOverride");
this.root.push(new LevelOverrideAttributes({ilvl: levelNum}));
if (start !== undefined) {
this.root.push(new StartOverride(start));
}
this.levelNum = levelNum;
}
get level(): LevelForOverride {
let lvl: LevelForOverride;
if (!this.lvl) {
lvl = new LevelForOverride(this.levelNum);
this.root.push(lvl);
this.lvl = lvl;
} else {
lvl = this.lvl;
}
return lvl;
}
}
class StartOverrideAttributes extends XmlAttributeComponent<{val: number}> {
protected xmlKeys = {val: "w:val"};
}
class StartOverride extends XmlComponent {
constructor(start: number) {
super("w:startOverride");
this.root.push(new StartOverrideAttributes({val: start}));
}
}

View File

@ -1,66 +1,64 @@
import { DocumentAttributes } from "../docx/document/document-attributes";
import { XmlUnitComponent } from "../docx/xml-components";
import { XmlComponent } from "../docx/xml-components";
export class Title extends XmlUnitComponent {
export class Title extends XmlComponent {
constructor(value: string) {
super("dc:title");
this.root = value;
this.root.push(value);
}
}
export class Subject extends XmlUnitComponent {
export class Subject extends XmlComponent {
constructor(value: string) {
super("dc:subject");
this.root = value;
this.root.push(value);
}
}
export class Creator extends XmlUnitComponent {
export class Creator extends XmlComponent {
constructor(value: string) {
super("dc:creator");
this.root = value;
this.root.push(value);
}
}
export class Keywords extends XmlUnitComponent {
export class Keywords extends XmlComponent {
constructor(value: string) {
super("cp:keywords");
this.root = value;
this.root.push(value);
}
}
export class Description extends XmlUnitComponent {
export class Description extends XmlComponent {
constructor(value: string) {
super("dc:description");
this.root = value;
this.root.push(value);
}
}
export class LastModifiedBy extends XmlUnitComponent {
export class LastModifiedBy extends XmlComponent {
constructor(value: string) {
super("cp:lastModifiedBy");
this.root = value;
this.root.push(value);
}
}
export class Revision extends XmlUnitComponent {
export class Revision extends XmlComponent {
constructor(value: string) {
super("cp:revision");
const revision = value;
this.root = value;
this.root.push(value);
}
}
abstract class DateComponent extends XmlComponent {
protected getCurrentDate(): any {
protected getCurrentDate(): string {
const date = new Date();
const year = date.getFullYear();
const month = ("0" + (date.getMonth() + 1)).slice(-2);

View File

@ -23,13 +23,27 @@ export class Properties extends XmlComponent {
dcmitype: "http://purl.org/dc/dcmitype/",
xsi: "http://www.w3.org/2001/XMLSchema-instance",
}));
this.root.push(new Title(options.title));
this.root.push(new Subject(options.subject));
this.root.push(new Creator(options.creator));
this.root.push(new Keywords(options.keywords));
this.root.push(new Description(options.description));
this.root.push(new LastModifiedBy(options.lastModifiedBy));
this.root.push(new Revision(options.revision));
if (options.title) {
this.root.push(new Title(options.title));
}
if (options.subject) {
this.root.push(new Subject(options.subject));
}
if (options.creator) {
this.root.push(new Creator(options.creator));
}
if (options.keywords) {
this.root.push(new Keywords(options.keywords));
}
if (options.description) {
this.root.push(new Description(options.description));
}
if (options.lastModifiedBy) {
this.root.push(new LastModifiedBy(options.lastModifiedBy));
}
if (options.revision) {
this.root.push(new Revision(options.revision));
}
this.root.push(new Created());
this.root.push(new Modified());
}

View File

@ -0,0 +1,11 @@
import { XmlAttributeComponent } from "../docx/xml-components";
interface IRelationshipsAttributesProperties {
xmlns: string;
}
export class RelationshipsAttributes extends XmlAttributeComponent<IRelationshipsAttributesProperties> {
protected xmlKeys = {
xmlns: "xmlns",
};
}

14
ts/relationships/index.ts Normal file
View File

@ -0,0 +1,14 @@
import { XmlComponent } from "../docx/xml-components";
import { RelationshipsAttributes } from "./attributes";
export class Relationships extends XmlComponent {
constructor() {
super("Relationships");
this.root.push(new RelationshipsAttributes({
xmlns: "http://schemas.openxmlformats.org/package/2006/relationships",
}));
// this.root.push(new Created());
}
}

View File

View File

@ -1,6 +1,6 @@
import {XmlComponent} from "../../docx/xml-components";
import {ParagraphPropertiesDefaults} from "./paragraph-properties";
import {RunPropertiesDefaults} from "./run-properties";
import { XmlComponent } from "../../docx/xml-components";
import { ParagraphPropertiesDefaults } from "./paragraph-properties";
import { RunPropertiesDefaults } from "./run-properties";
export class DocumentDefaults extends XmlComponent {
@ -14,11 +14,4 @@ export class DocumentDefaults extends XmlComponent {
this.root.push(this.runPropertiesDefaults);
this.root.push(this.paragraphPropertiesDefaults);
}
clearVariables(): void {
this.runPropertiesDefaults.clearVariables();
this.paragraphPropertiesDefaults.clearVariables();
delete this.runPropertiesDefaults;
delete this.paragraphPropertiesDefaults;
}
}
}

View File

@ -1,5 +1,5 @@
import {XmlComponent} from "../../docx/xml-components";
import {ParagraphProperties} from "../../docx/paragraph/properties";
import { ParagraphProperties } from "../../docx/paragraph/properties";
import { XmlComponent } from "../../docx/xml-components";
export class ParagraphPropertiesDefaults extends XmlComponent {
@ -7,4 +7,4 @@ export class ParagraphPropertiesDefaults extends XmlComponent {
super("w:pPrDefault");
this.root.push(new ParagraphProperties());
}
}
}

View File

@ -1,5 +1,5 @@
import {XmlComponent} from "../../docx/xml-components";
import {RunProperties} from "../../docx/run/properties";
import { RunProperties } from "../../docx/run/properties";
import { XmlComponent } from "../../docx/xml-components";
export class RunPropertiesDefaults extends XmlComponent {
@ -7,4 +7,4 @@ export class RunPropertiesDefaults extends XmlComponent {
super("w:rPrDefault");
this.root.push(new RunProperties());
}
}
}

View File

@ -1,56 +1,54 @@
import {Styles} from "./";
import {DocumentDefaults} from "./defaults";
import {ParagraphPropertiesDefaults} from "./defaults/paragraph-properties";
import {RunPropertiesDefaults} from "./defaults/run-properties";
import {Heading1Style, Heading2Style, TitleStyle, Heading3Style, Heading4Style, Heading5Style, Heading6Style, ListParagraph} from "./style";
// import {StyleAttributes} from "./style/attributes";
import {ParagraphProperties} from "../docx/paragraph/properties";
import {RunProperties} from "../docx/run/properties";
import {Color, Size, Italics} from "../docx/run/formatting";
import { Color, Italics, Size } from "../docx/run/formatting";
import { Styles } from "./";
import { DocumentDefaults } from "./defaults";
import {
Heading1Style, Heading2Style, Heading3Style, Heading4Style, Heading5Style, Heading6Style,
ListParagraph, TitleStyle,
} from "./style";
export class DefaultStylesFactory {
newInstance(): Styles {
let styles = new Styles();
public newInstance(): Styles {
const styles = new Styles();
styles.push(new DocumentDefaults());
let titleStyle = new TitleStyle();
const titleStyle = new TitleStyle();
titleStyle.addRunProperty(new Size(56));
styles.push(titleStyle);
let heading1Style = new Heading1Style();
const heading1Style = new Heading1Style();
heading1Style.addRunProperty(new Color("2E74B5"));
heading1Style.addRunProperty(new Size(32));
styles.push(heading1Style);
let heading2Style = new Heading2Style();
const heading2Style = new Heading2Style();
heading2Style.addRunProperty(new Color("2E74B5"));
heading2Style.addRunProperty(new Size(26));
styles.push(heading2Style);
let heading3Style = new Heading3Style();
const heading3Style = new Heading3Style();
heading3Style.addRunProperty(new Color("1F4D78"));
heading3Style.addRunProperty(new Size(24));
styles.push(heading3Style);
let heading4Style = new Heading4Style();
const heading4Style = new Heading4Style();
heading4Style.addRunProperty(new Color("2E74B5"));
heading4Style.addRunProperty(new Italics());
styles.push(heading4Style);
let heading5Style = new Heading5Style();
const heading5Style = new Heading5Style();
heading5Style.addRunProperty(new Color("2E74B5"));
styles.push(heading5Style);
let heading6Style = new Heading6Style();
const heading6Style = new Heading6Style();
heading6Style.addRunProperty(new Color("1F4D78"));
styles.push(heading6Style);
let listParagraph = new ListParagraph();
const listParagraph = new ListParagraph();
// listParagraph.addParagraphProperty();
styles.push(listParagraph);
// console.log(JSON.stringify(styles, null, " "));
return styles;
}
}
}

View File

@ -1,9 +1,6 @@
import {XmlComponent} from "../docx/xml-components";
import {DocumentAttributes} from "../docx/document/document-attributes";
import {DocumentDefaults} from "./defaults";
import {LatentStyles} from "./latent-styles";
import {LatentStyleException} from "./latent-styles/exceptions";
import {LatentStyleExceptionAttributes} from "./latent-styles/exceptions/attributes";
import { DocumentAttributes } from "../docx/document/document-attributes";
import { XmlComponent } from "../docx/xml-components";
import { ParagraphStyle } from "./style";
export class Styles extends XmlComponent {
@ -15,7 +12,7 @@ export class Styles extends XmlComponent {
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",
Ignorable: "w14 w15"
Ignorable: "w14 w15",
}));
// let latentStyles = new LatentStyles();
// latentStyles.push(new LatentStyleException(new LatentStyleExceptionAttributes({
@ -24,13 +21,14 @@ export class Styles extends XmlComponent {
// this.root.push(latentStyles);
}
push(style: XmlComponent): void {
public push(style: XmlComponent): Styles {
this.root.push(style);
return this;
}
clearVariables() {
this.root.forEach(element => {
element.clearVariables();
});
public createParagraphStyle(styleId: string, name?: string): ParagraphStyle {
const para = new ParagraphStyle(styleId, name);
this.push(para);
return para;
}
}
}

View File

@ -0,0 +1,27 @@
import { XmlAttributeComponent, XmlComponent } from "../../docx/xml-components";
interface ILatentStyleExceptionAttributesProperties {
name?: string;
uiPriority?: string;
qFormat?: string;
semiHidden?: string;
unhideWhenUsed?: string;
}
class LatentStyleExceptionAttributes extends XmlAttributeComponent<ILatentStyleExceptionAttributesProperties> {
protected xmlKeys = {
name: "w:name",
uiPriority: "w:uiPriority",
qFormat: "w:qFormat",
semiHidden: "w:semiHidden",
unhideWhenUsed: "w:unhideWhenUsed",
};
}
export class LatentStyleException extends XmlComponent {
constructor(attributes: ILatentStyleExceptionAttributesProperties) {
super("w:lsdException");
this.root.push(new LatentStyleExceptionAttributes(attributes));
}
}

View File

@ -1,31 +0,0 @@
import {XmlComponent} from "../../../docx/xml-components";
interface LatentStyleExceptionAttributesProperties {
name?: string;
uiPriority?: string;
qFormat?: string;
semiHidden?: string;
unhideWhenUsed?: string;
}
export class LatentStyleExceptionAttributes extends XmlComponent {
private _attr: Object;
xmlKeys = {
name: "w:name",
uiPriority: "w:uiPriority",
qFormat: "w:qFormat",
semiHidden: "w:semiHidden",
unhideWhenUsed: "w:unhideWhenUsed"
};
constructor(properties?: LatentStyleExceptionAttributesProperties) {
super("_attr");
this._attr = properties;
if (!properties) {
this._attr = {};
}
this._attr["xmlKeys"] = this.xmlKeys;
}
}

View File

@ -1,10 +0,0 @@
import {XmlComponent} from "../../../docx/xml-components";
import {LatentStyleExceptionAttributes} from "./attributes";
export class LatentStyleException extends XmlComponent {
constructor(attributes: LatentStyleExceptionAttributes) {
super("w:lsdException");
this.root.push(attributes);
}
}

View File

@ -1,5 +1,5 @@
import {XmlComponent} from "../../docx/xml-components";
import {LatentStyleException} from "./exceptions";
import { XmlComponent } from "../../docx/xml-components";
import { LatentStyleException } from "./exceptions";
export class LatentStyles extends XmlComponent {
@ -7,7 +7,7 @@ export class LatentStyles extends XmlComponent {
super("w:latentStyles");
}
push(latentException: LatentStyleException): void {
public push(latentException: LatentStyleException): void {
this.root.push(latentException);
}
}
}

View File

@ -1,24 +0,0 @@
import {XmlComponent} from "../../docx/xml-components";
import {XmlAttributeComponent} from "../../docx/xml-components";
interface StyleAttributesProperties {
type?: string;
styleId?: string;
default?: string;
customStyle?: string;
val?: string;
}
export class StyleAttributes extends XmlAttributeComponent {
private _attr: Object;
constructor(properties: StyleAttributesProperties) {
super({
type: "w:type",
styleId: "w:styleId",
default: "w:default",
customStyle: "w:customStyle",
val: "w:val"
}, properties);
}
}

View File

@ -1,13 +1,18 @@
import {XmlComponent} from "../../docx/xml-components";
import {StyleAttributes} from "./attributes";
import { XmlAttributeComponent, XmlComponent } from "../../docx/xml-components";
interface IComponentAttributes {
val: string;
}
class ComponentAttributes extends XmlAttributeComponent<IComponentAttributes> {
protected xmlKeys = {val: "w:val"};
}
export class Name extends XmlComponent {
constructor(value: string) {
super("w:name");
this.root.push(new StyleAttributes({
val: value
}));
this.root.push(new ComponentAttributes({val: value}));
}
}
@ -15,9 +20,7 @@ export class BasedOn extends XmlComponent {
constructor(value: string) {
super("w:basedOn");
this.root.push(new StyleAttributes({
val: value
}));
this.root.push(new ComponentAttributes({val: value}));
}
}
@ -25,10 +28,7 @@ export class Next extends XmlComponent {
constructor(value: string) {
super("w:next");
this.root.push(new StyleAttributes({
styleId: "1",
val: value
}));
this.root.push(new ComponentAttributes({val: value}));
}
}
@ -36,9 +36,7 @@ export class Link extends XmlComponent {
constructor(value: string) {
super("w:link");
this.root.push(new StyleAttributes({
val: value
}));
this.root.push(new ComponentAttributes({val: value}));
}
}
@ -46,9 +44,8 @@ export class UiPriority extends XmlComponent {
constructor(value: string) {
super("w:uiPriority");
this.root.push(new StyleAttributes({
val: value
}));
// TODO: this value should be a ST_DecimalNumber
this.root.push(new ComponentAttributes({val: value}));
}
}
@ -73,4 +70,4 @@ export class RsId extends XmlComponent {
export class SemiHidden extends XmlComponent {
}
}

View File

@ -1,63 +1,194 @@
import {XmlComponent} from "../../docx/xml-components";
import {StyleAttributes} from "./attributes";
import {ParagraphProperties} from "../../docx/paragraph/properties";
import {RunProperties} from "../../docx/run/properties";
import {Name, BasedOn, Next, QuickFormat} from "./components";
import * as paragraph from "../../docx/paragraph/formatting";
import * as formatting from "../../docx/run/formatting";
import { RunProperties } from "../../docx/run/properties";
import { XmlAttributeComponent, XmlComponent } from "../../docx/xml-components";
import { BasedOn, Name, Next, QuickFormat } from "./components";
export interface IStyleAttributes {
type?: string;
styleId?: string;
default?: boolean;
customStyle?: string;
}
class StyleAttributes extends XmlAttributeComponent<IStyleAttributes> {
protected xmlKeys = {
type: "w:type",
styleId: "w:styleId",
default: "w:default",
customStyle: "w:customStyle",
};
}
export class Style extends XmlComponent {
constructor(attributes: StyleAttributes) {
constructor(attributes: IStyleAttributes, name?: string) {
super("w:style");
this.root.push(attributes);
this.root.push(new StyleAttributes(attributes));
if (name) {
this.root.push(new Name(name));
}
}
push(styleSegment: XmlComponent): void {
public push(styleSegment: XmlComponent): void {
this.root.push(styleSegment);
}
}
export class ParagraphStyle extends Style {
private paragraphProperties: ParagraphProperties;
private paragraphProperties: paragraph.ParagraphProperties;
private runProperties: RunProperties;
constructor(styleId: string) {
let attributes = new StyleAttributes({
type: "paragraph",
styleId: styleId
});
super(attributes);
this.paragraphProperties = new ParagraphProperties();
constructor(styleId: string, name?: string) {
super({type: "paragraph", styleId: styleId}, name);
this.paragraphProperties = new paragraph.ParagraphProperties();
this.runProperties = new RunProperties();
this.root.push(this.paragraphProperties);
this.root.push(this.runProperties);
}
clearVariables(): void {
this.paragraphProperties.clearVariables();
this.runProperties.clearVariables();
delete this.paragraphProperties;
delete this.runProperties;
}
addParagraphProperty(property: XmlComponent): void {
public addParagraphProperty(property: XmlComponent): void {
this.paragraphProperties.push(property);
}
addRunProperty(property: XmlComponent): void {
public addRunProperty(property: XmlComponent): void {
this.runProperties.push(property);
}
public basedOn(parentId: string): ParagraphStyle {
this.root.push(new BasedOn(parentId));
return this;
}
public quickFormat(): ParagraphStyle {
this.root.push(new QuickFormat());
return this;
}
public next(nextId: string): ParagraphStyle {
this.root.push(new Next(nextId));
return this;
}
// ---------- Run formatting ---------------------- //
public size(twips: number): ParagraphStyle {
this.addRunProperty(new formatting.Size(twips));
return this;
}
public bold(): ParagraphStyle {
this.addRunProperty(new formatting.Bold());
return this;
}
public italics(): ParagraphStyle {
this.addRunProperty(new formatting.Italics());
return this;
}
public smallCaps(): ParagraphStyle {
this.addRunProperty(new formatting.SmallCaps());
return this;
}
public allCaps(): ParagraphStyle {
this.addRunProperty(new formatting.Caps());
return this;
}
public strike(): ParagraphStyle {
this.addRunProperty(new formatting.Strike());
return this;
}
public doubleStrike(): ParagraphStyle {
this.addRunProperty(new formatting.DoubleStrike());
return this;
}
public subScript(): ParagraphStyle {
this.addRunProperty(new formatting.SubScript());
return this;
}
public superScript(): ParagraphStyle {
this.addRunProperty(new formatting.SuperScript());
return this;
}
public underline(underlineType?: string, color?: string): ParagraphStyle {
this.addRunProperty(new formatting.Underline(underlineType, color));
return this;
}
public color(color: string): ParagraphStyle {
this.addRunProperty(new formatting.Color(color));
return this;
}
public font(fontName: string): ParagraphStyle {
this.addRunProperty(new formatting.RunFonts(fontName));
return this;
}
// --------------------- Paragraph formatting ------------------------ //
public center(): ParagraphStyle {
this.addParagraphProperty(new paragraph.Alignment("center"));
return this;
}
public left(): ParagraphStyle {
this.addParagraphProperty(new paragraph.Alignment("left"));
return this;
}
public right(): ParagraphStyle {
this.addParagraphProperty(new paragraph.Alignment("right"));
return this;
}
public justified(): ParagraphStyle {
this.addParagraphProperty(new paragraph.Alignment("both"));
return this;
}
public thematicBreak(): ParagraphStyle {
this.addParagraphProperty(new paragraph.ThematicBreak());
return this;
}
public maxRightTabStop(): ParagraphStyle {
this.addParagraphProperty(new paragraph.MaxRightTabStop());
return this;
}
public leftTabStop(position: number): ParagraphStyle {
this.addParagraphProperty(new paragraph.LeftTabStop(position));
return this;
}
public indent(left: number, hanging?: number): ParagraphStyle {
this.addParagraphProperty(new paragraph.Indent(left, hanging));
return this;
}
public spacing(params: paragraph.ISpacingProperties): ParagraphStyle {
this.addParagraphProperty(new paragraph.Spacing(params));
return this;
};
}
export class HeadingStyle extends ParagraphStyle {
constructor(styleId: string, name: string) {
super(styleId);
this.root.push(new Name(name));
this.root.push(new BasedOn("Normal"));
this.root.push(new Next("Normal"));
this.root.push(new QuickFormat());
super(styleId, name);
this.basedOn("Normal");
this.next("Normal");
this.quickFormat();
}
}
@ -118,4 +249,4 @@ export class ListParagraph extends ParagraphStyle {
this.root.push(new BasedOn("Normal"));
this.root.push(new QuickFormat());
}
}
}

View File

@ -1,7 +1,12 @@
{
"compilerOptions": {
"target": "es6",
"strictNullChecks": true,
"sourceMap": true,
"removeComments": true,
"preserveConstEnums": true,
"outDir": "../build-tests",
"sourceRoot": "./",
"rootDir": "./",
"module": "commonjs"
}

View File

@ -1,19 +1,17 @@
/* tslint:disable */
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { Utility } from "./utility";
/* tslint:enable */
describe("", () => {
beforeEach(() => {
// TODO
});
describe("#methodName()", () => {
it("should ", () => {
// TODO
});
});
});
});

View File

@ -1,15 +1,11 @@
import { Body } from "../../../docx/document/body";
import { assert } from "chai";
import { SectionProperties } from "../../../docx/document/body/section-properties";
import { PageSize } from "../../../docx/document/body/page-size";
import { PageMargin } from "../../../docx/document/body/page-margin";
import { Body } from "../../../docx/document/body";
import { Columns } from "../../../docx/document/body/columns";
import { DocumentGrid } from "../../../docx/document/body/doc-grid";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { PageMargin } from "../../../docx/document/body/page-margin";
import { PageSize } from "../../../docx/document/body/page-size";
import { SectionProperties } from "../../../docx/document/body/section-properties";
import { Utility } from "../../utility";
describe("Body", () => {
let body: Body;
@ -26,28 +22,28 @@ describe("Body", () => {
describe("#constructor()", () => {
it("should create the Section Properties", () => {
let newJson = jsonify(body);
const newJson = Utility.jsonify(body);
assert.equal(newJson.root[0].rootKey, "w:sectPr");
});
it("should create the Page Size", () => {
let newJson = jsonify(body);
const newJson = Utility.jsonify(body);
assert.equal(newJson.root[1].rootKey, "w:pgSz");
});
it("should create the Page Margin", () => {
let newJson = jsonify(body);
const newJson = Utility.jsonify(body);
assert.equal(newJson.root[2].rootKey, "w:pgMar");
});
it("should create the Columns", () => {
let newJson = jsonify(body);
const newJson = Utility.jsonify(body);
assert.equal(newJson.root[3].rootKey, "w:cols");
});
it("should create the Document Grid", () => {
let newJson = jsonify(body);
const newJson = Utility.jsonify(body);
assert.equal(newJson.root[4].rootKey, "w:docGrid");
});
});
});
});

View File

@ -1,5 +1,6 @@
import { assert, expect } from "chai";
import * as docx from "../../../docx";
import { assert } from "chai";
import { Formatter } from "../../../export/formatter";
describe("Document", () => {
let document: docx.Document;
@ -11,7 +12,7 @@ describe("Document", () => {
describe("#constructor()", () => {
it("should create valid JSON", () => {
let stringifiedJson = JSON.stringify(document);
const stringifiedJson = JSON.stringify(document);
let newJson;
try {
@ -22,4 +23,51 @@ describe("Document", () => {
assert.isTrue(true);
});
});
});
describe("#createParagraph", () => {
it("should create a new paragraph and append it to body", () => {
const para = document.createParagraph();
expect(para).to.be.an.instanceof(docx.Paragraph);
const body = new Formatter().format(document)["w:document"][1]["w:body"];
expect(body).to.be.an("array").which.has.length.at.least(1);
expect(body[0]).to.have.property("w:p");
});
it("should use the text given to create a run in the paragraph", () => {
const para = document.createParagraph("sample paragraph text");
expect(para).to.be.an.instanceof(docx.Paragraph);
const body = new Formatter().format(document)["w:document"][1]["w:body"];
expect(body).to.be.an("array").which.has.length.at.least(1);
expect(body[0]).to.have.property("w:p").which.includes({
"w:r": [
{"w:rPr": []},
{"w:t": ["sample paragraph text"]},
],
});
});
});
describe("#createTable", () => {
it("should create a new table and append it to body", () => {
const table = document.createTable(2, 3);
expect(table).to.be.an.instanceof(docx.Table);
const body = new Formatter().format(document)["w:document"][1]["w:body"];
expect(body).to.be.an("array").which.has.length.at.least(1);
expect(body[0]).to.have.property("w:tbl");
});
it("should create a table with the correct dimensions", () => {
document.createTable(2, 3);
const body = new Formatter().format(document)["w:document"][1]["w:body"];
expect(body).to.be.an("array").which.has.length.at.least(1);
expect(body[0]).to.have.property("w:tbl").which.includes({
"w:tblGrid": [
{"w:gridCol": [{_attr: {"w:w": 1}}]},
{"w:gridCol": [{_attr: {"w:w": 1}}]},
{"w:gridCol": [{_attr: {"w:w": 1}}]},
],
});
expect(body[0]["w:tbl"].filter((x) => x["w:tr"])).to.have.length(2);
});
});
});

View File

@ -1,13 +1,9 @@
import { ThematicBreak } from "../../../docx/paragraph/border";
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { ThematicBreak } from "../../../docx/paragraph/border";
import { Utility } from "../../utility";
describe("Border", () => {
// TODO: Need tests here
});
describe("ThematicBreak", () => {
@ -19,14 +15,14 @@ describe("ThematicBreak", () => {
describe("#constructor()", () => {
it("should create a Thematic Break with correct border properties", () => {
let newJson = jsonify(thematicBreak);
let attributes = {
const newJson = Utility.jsonify(thematicBreak);
const attributes = {
color: "auto",
space: "1",
val: "single",
sz: "6"
sz: "6",
};
assert.equal(JSON.stringify(newJson.root[0].root[0].root), JSON.stringify(attributes));
});
});
});
});

View File

@ -1,10 +1,6 @@
import { PageBreak } from "../../../docx/paragraph/page-break";
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { PageBreak } from "../../../docx/paragraph/page-break";
import { Utility } from "../../utility";
describe("PageBreak", () => {
let pageBreak: PageBreak;
@ -15,21 +11,21 @@ describe("PageBreak", () => {
describe("#constructor()", () => {
it("should create a Page Break with correct attributes", () => {
let newJson = jsonify(pageBreak);
let attributes = {
type: "page"
const newJson = Utility.jsonify(pageBreak);
const attributes = {
type: "page",
};
assert.equal(JSON.stringify(newJson.root[1].root[0].root), JSON.stringify(attributes));
});
it("should create a Page Break with w:r", () => {
let newJson = jsonify(pageBreak);
const newJson = Utility.jsonify(pageBreak);
assert.equal(newJson.rootKey, "w:r");
});
it("should create a Page Break with a Break inside", () => {
let newJson = jsonify(pageBreak);
const newJson = Utility.jsonify(pageBreak);
assert.equal(newJson.root[1].rootKey, "w:br");
});
});
});
});

View File

@ -1,10 +1,6 @@
import { Style } from "../../../docx/paragraph/style";
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { Style } from "../../../docx/paragraph/style";
import { Utility } from "../../utility";
describe("ParagraphStyle", () => {
let style: Style;
@ -12,15 +8,14 @@ describe("ParagraphStyle", () => {
describe("#constructor()", () => {
it("should create a style with given value", () => {
style = new Style("test");
let newJson = jsonify(style);
const newJson = Utility.jsonify(style);
assert.equal(newJson.root[0].root.val, "test");
});
it("should create a style with blank val", () => {
style = new Style("");
let newJson = jsonify(style);
const newJson = Utility.jsonify(style);
assert.equal(newJson.root[0].root.val, "");
});
});
});
});

View File

@ -1,12 +1,7 @@
import { assert, expect } from "chai";
import * as docx from "../../../docx";
import { Formatter } from "../../../export/formatter";
import { Numbering } from "../../../numbering";
import { assert, expect } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
describe("Paragraph", () => {
let paragraph: docx.Paragraph;
@ -18,7 +13,7 @@ describe("Paragraph", () => {
describe("#constructor()", () => {
it("should create valid JSON", () => {
let stringifiedJson = JSON.stringify(paragraph);
const stringifiedJson = JSON.stringify(paragraph);
let newJson;
try {
@ -30,89 +25,158 @@ describe("Paragraph", () => {
});
it("should create have valid properties", () => {
let stringifiedJson = JSON.stringify(paragraph);
let newJson = JSON.parse(stringifiedJson);
const stringifiedJson = JSON.stringify(paragraph);
const newJson = JSON.parse(stringifiedJson);
assert.equal(newJson.root[0].rootKey, "w:pPr");
});
});
describe("#createTextRun", () => {
it("should add a new run to the paragraph and return it", () => {
const run = paragraph.createTextRun("this is a test run");
expect(run).to.be.instanceof(docx.TextRun);
const tree = new Formatter().format(paragraph)["w:p"];
expect(tree).to.be.an("array").which.includes({
"w:r": [
{"w:rPr": []},
{"w:t": ["this is a test run"]},
],
});
});
});
describe("#heading1()", () => {
it("should add heading style to JSON", () => {
paragraph.heading1();
let newJson = jsonify(paragraph);
assert.equal(newJson.root[0].root[1].root[0].root.val, "Heading1");
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [{"w:pStyle": [{_attr: {"w:val": "Heading1"}}]}],
},
],
});
});
});
describe("#heading2()", () => {
it("should add heading style to JSON", () => {
paragraph.heading2();
let newJson = jsonify(paragraph);
assert.equal(newJson.root[0].root[1].root[0].root.val, "Heading2");
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [{"w:pStyle": [{_attr: {"w:val": "Heading2"}}]}],
},
],
});
});
});
describe("#heading3()", () => {
it("should add heading style to JSON", () => {
paragraph.heading3();
let newJson = jsonify(paragraph);
assert.equal(newJson.root[0].root[1].root[0].root.val, "Heading3");
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [{"w:pStyle": [{_attr: {"w:val": "Heading3"}}]}],
},
],
});
});
});
describe("#title()", () => {
it("should add title style to JSON", () => {
paragraph.title();
let newJson = jsonify(paragraph);
assert.equal(newJson.root[0].root[1].root[0].root.val, "Title");
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [{"w:pStyle": [{_attr: {"w:val": "Title"}}]}],
},
],
});
});
});
describe("#center()", () => {
it("should add center alignment to JSON", () => {
paragraph.center();
let newJson = jsonify(paragraph);
assert.equal(newJson.root[0].root[1].root[0].root.val, "center");
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [{"w:jc": [{_attr: {"w:val": "center"}}]}],
},
],
});
});
});
describe("#thematicBreak()", () => {
it("should add thematic break to JSON", () => {
paragraph.thematicBreak();
let newJson = jsonify(paragraph);
assert.equal(newJson.root[0].root[1].rootKey, "w:pBdr");
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [{
"w:pPr": [{
"w:pBdr": [{
"w:bottom": [{
_attr: {
"w:val": "single",
"w:color": "auto",
"w:space": "1",
"w:sz": "6",
},
}],
}],
}],
}],
});
});
});
describe("#pageBreak()", () => {
it("should add page break to JSON", () => {
paragraph.pageBreak();
let newJson = jsonify(paragraph);
assert.equal(newJson.root[0].root[1].root[1].rootKey, "w:br");
});
it("should add page break with 'page' type", () => {
paragraph.pageBreak();
let newJson = jsonify(paragraph);
assert.equal(newJson.root[0].root[1].root[1].root[0].root.type, "page");
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [{
"w:pPr": [{
"w:r": [
{"w:rPr": []},
{"w:br": [{_attr: {"w:type": "page"}}]},
],
}],
}],
});
});
});
describe("#bullet()", () => {
it("should add list paragraph style to JSON", () => {
paragraph.bullet();
let newJson = jsonify(paragraph);
assert.equal(newJson.root[0].root[1].root[0].root.val, "ListParagraph");
const tree = new Formatter().format(paragraph);
expect(tree).to.have.property("w:p").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]).to.have.property("w:pPr").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]["w:pPr"][0]).to.deep.equal({
"w:pStyle": [{_attr: {"w:val": "ListParagraph"}}],
});
});
it("it should add numbered properties", () => {
paragraph.bullet();
let newJson = jsonify(paragraph);
assert.isDefined(newJson.root[0].root[2]);
const tree = new Formatter().format(paragraph);
expect(tree).to.have.property("w:p").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]).to.have.property("w:pPr").which.is.an("array").which.has.length.at.least(2);
expect(tree["w:p"][0]["w:pPr"][1]).to.deep.equal({
"w:numPr": [
{"w:ilvl": [{_attr: {"w:val": 0}}]},
{"w:numId": [{_attr: {"w:val": 1}}]},
],
});
});
});
@ -124,8 +188,12 @@ describe("Paragraph", () => {
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
paragraph.setNumbering(letterNumbering, 0);
let newJson = jsonify(paragraph);
assert.equal(newJson.root[0].root[1].root[0].root.val, "ListParagraph");
const tree = new Formatter().format(paragraph);
expect(tree).to.have.property("w:p").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]).to.have.property("w:pPr").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]["w:pPr"][0]).to.deep.equal({
"w:pStyle": [{_attr: {"w:val": "ListParagraph"}}],
});
});
it("it should add numbered properties", () => {
@ -136,23 +204,69 @@ describe("Paragraph", () => {
paragraph.setNumbering(letterNumbering, 0);
const tree = new Formatter().format(paragraph);
console.log(JSON.stringify(tree, null, 2));
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [
{"_attr": {}},
{"w:pStyle": [{"_attr": {"w:val": "ListParagraph"}}]},
{"w:pStyle": [{_attr: {"w:val": "ListParagraph"}}]},
{
"w:numPr": [
{"w:ilvl": [{"_attr": {"w:val": 0}}]},
{"w:numId": [{"_attr": {"w:val": letterNumbering.id}}]}
]
{"w:ilvl": [{_attr: {"w:val": 0}}]},
{"w:numId": [{_attr: {"w:val": letterNumbering.id}}]},
],
},
],
},
]
})
],
});
});
});
describe("#style", () => {
it("should set the paragraph style to the given styleId", () => {
paragraph.style("myFancyStyle");
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [
{"w:pStyle": [{_attr: {"w:val": "myFancyStyle"}}]},
],
},
],
});
});
});
describe("#indent", () => {
it("should set the paragraph indent to the given values", () => {
paragraph.indent(720);
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [
{"w:ind": [{_attr: {"w:left": 720}}]},
],
},
],
});
});
});
describe("#spacing", () => {
it("should set the paragraph spacing to the given values", () => {
paragraph.spacing({before: 90, line: 50});
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [
{"w:spacing": [{_attr: {"w:before": 90, "w:line": 50}}]},
],
},
],
});
});
});
});

View File

@ -0,0 +1,23 @@
import { expect } from "chai";
import { Spacing } from "../../../docx/paragraph/spacing";
import { Formatter } from "../../../export/formatter";
describe("Spacing", () => {
describe("#constructor", () => {
it("should set the properties given", () => {
const spacing = new Spacing({before: 100, after: 120, line: 150});
const tree = new Formatter().format(spacing);
expect(tree).to.deep.equal({
"w:spacing": [{_attr: {"w:after": 120, "w:before": 100, "w:line": 150}}],
});
});
it("should only set the given properties", () => {
const spacing = new Spacing({before: 100});
const tree = new Formatter().format(spacing);
expect(tree).to.deep.equal({
"w:spacing": [{_attr: {"w:before": 100}}],
});
});
});
});

View File

@ -1,10 +1,6 @@
import { LeftTabStop, MaxRightTabStop } from "../../../docx/paragraph/tab-stop";
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { LeftTabStop, MaxRightTabStop } from "../../../docx/paragraph/tab-stop";
import { Utility } from "../../utility";
describe("LeftTabStop", () => {
let tabStop: LeftTabStop;
@ -15,23 +11,23 @@ describe("LeftTabStop", () => {
describe("#constructor()", () => {
it("should create a Tab Stop with correct attributes", () => {
let newJson = jsonify(tabStop);
let attributes = {
const newJson = Utility.jsonify(tabStop);
const attributes = {
val: "left",
pos: 100
pos: 100,
};
assert.equal(JSON.stringify(newJson.root[0].root[0].root), JSON.stringify(attributes));
});
it("should create a Tab Stop with w:tab", () => {
let newJson = jsonify(tabStop);
const newJson = Utility.jsonify(tabStop);
assert.equal(newJson.root[0].rootKey, "w:tab");
});
});
});
describe("RightTabStop", () => {
// TODO
});
describe("MaxRightTabStop", () => {
@ -43,18 +39,18 @@ describe("MaxRightTabStop", () => {
describe("#constructor()", () => {
it("should create a Tab Stop with correct attributes", () => {
let newJson = jsonify(tabStop);
const newJson = Utility.jsonify(tabStop);
let attributes = {
const attributes = {
val: "right",
pos: 9026
pos: 9026,
};
assert.equal(JSON.stringify(newJson.root[0].root[0].root), JSON.stringify(attributes));
});
it("should create a Tab Stop with w:tab", () => {
let newJson = jsonify(tabStop);
const newJson = Utility.jsonify(tabStop);
assert.equal(newJson.root[0].rootKey, "w:tab");
});
});
});
});

View File

@ -1,10 +1,6 @@
import { NumberProperties } from "../../../docx/paragraph/unordered-list";
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { NumberProperties } from "../../../docx/paragraph/unordered-list";
import { Utility } from "../../utility";
describe("NumberProperties", () => {
let numberProperties: NumberProperties;
@ -15,20 +11,20 @@ describe("NumberProperties", () => {
describe("#constructor()", () => {
it("should create a Number Properties with correct root key", () => {
let newJson = jsonify(numberProperties);
const newJson = Utility.jsonify(numberProperties);
assert.equal(newJson.rootKey, "w:numPr");
});
it("should create a Page Break with a Indent Level inside", () => {
let newJson = jsonify(numberProperties);
const newJson = Utility.jsonify(numberProperties);
assert.equal(newJson.root[0].rootKey, "w:ilvl");
assert.equal(newJson.root[0].root[0].root.val, 10);
});
it("should create a Page Break with a Number Id inside", () => {
let newJson = jsonify(numberProperties);
const newJson = Utility.jsonify(numberProperties);
assert.equal(newJson.root[1].rootKey, "w:numId");
assert.equal(newJson.root[1].root[0].root.val, 5);
});
});
});
});

View File

@ -1,10 +1,6 @@
import { Break } from "../../../docx/run/break";
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { Break } from "../../../docx/run/break";
import { Utility } from "../../utility";
describe("Break", () => {
let currentBreak: Break;
@ -15,8 +11,8 @@ describe("Break", () => {
describe("#constructor()", () => {
it("should create a Break with correct root key", () => {
let newJson = jsonify(currentBreak);
const newJson = Utility.jsonify(currentBreak);
assert.equal(newJson.rootKey, "w:br");
});
});
});
});

View File

@ -0,0 +1,26 @@
import { assert } from "chai";
import * as fs from "fs";
import { Drawing } from "../../../../docx/run/run-components/drawing";
import { Utility } from "../../../utility";
describe("Drawing", () => {
let currentBreak: Drawing;
beforeEach(() => {
const path = "./demo/penguins.jpg";
currentBreak = new Drawing({
fileName: "test.jpg",
referenceId: 1,
stream: fs.createReadStream(path),
path: path,
});
});
describe("#constructor()", () => {
it("should create a Drawing with correct root key", () => {
const newJson = Utility.jsonify(currentBreak);
assert.equal(newJson.rootKey, "w:drawing");
console.log(JSON.stringify(newJson, null, 2));
});
});
});

View File

@ -1,11 +1,7 @@
import { assert, expect } from "chai";
import { Run } from "../../../docx/run";
import { TextRun } from "../../../docx/run/text-run";
import { Formatter } from "../../../export/formatter";
function jsonify(obj: object) {
return JSON.parse(JSON.stringify(obj));
}
import { Utility } from "../../utility";
describe("Run", () => {
let run: Run;
@ -17,7 +13,7 @@ describe("Run", () => {
describe("#bold()", () => {
it("it should add bold to the properties", () => {
run.bold();
const newJson = jsonify(run);
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:b");
});
});
@ -25,7 +21,7 @@ describe("Run", () => {
describe("#italic()", () => {
it("it should add italics to the properties", () => {
run.italic();
const newJson = jsonify(run);
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:i");
});
});
@ -33,15 +29,35 @@ describe("Run", () => {
describe("#underline()", () => {
it("it should add underline to the properties", () => {
run.underline();
const newJson = jsonify(run);
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:u");
});
it("should default to 'single' and no color", () => {
run.underline();
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:u": [{_attr: {"w:val": "single"}}]}]},
],
});
});
it("should set the style type and color if given", () => {
run.underline("double", "990011");
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:u": [{_attr: {"w:val": "double", "w:color": "990011"}}]}]},
],
});
});
});
describe("#smallCaps()", () => {
it("it should add smallCaps to the properties", () => {
run.smallCaps();
const newJson = jsonify(run);
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:smallCaps");
});
});
@ -49,7 +65,7 @@ describe("Run", () => {
describe("#caps()", () => {
it("it should add caps to the properties", () => {
run.allCaps();
const newJson = jsonify(run);
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:caps");
});
});
@ -57,7 +73,7 @@ describe("Run", () => {
describe("#strike()", () => {
it("it should add strike to the properties", () => {
run.strike();
const newJson = jsonify(run);
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:strike");
});
});
@ -65,7 +81,7 @@ describe("Run", () => {
describe("#doubleStrike()", () => {
it("it should add caps to the properties", () => {
run.doubleStrike();
const newJson = jsonify(run);
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:dstrike");
});
});
@ -73,7 +89,7 @@ describe("Run", () => {
describe("#break()", () => {
it("it should add break to the run", () => {
run.break();
const newJson = jsonify(run);
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[1].rootKey, "w:br");
});
});
@ -81,7 +97,7 @@ describe("Run", () => {
describe("#tab()", () => {
it("it should add break to the run", () => {
run.tab();
const newJson = jsonify(run);
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[1].rootKey, "w:tab");
});
});
@ -101,4 +117,40 @@ describe("Run", () => {
});
});
});
describe("#color", () => {
it("should set the run to the color given", () => {
run.color("001122");
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:color": [{_attr: {"w:val": "001122"}}]}]},
],
});
});
});
describe("#size", () => {
it("should set the run to the given size", () => {
run.size(24);
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:sz": [{_attr: {"w:val": 24}}]}]},
],
});
});
});
describe("#style", () => {
it("should set the style to the given styleId", () => {
run.style("myRunStyle");
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:rStyle": [{_attr: {"w:val": "myRunStyle"}}]}]},
],
});
});
});
});

View File

@ -1,10 +1,6 @@
import { SubScript, SuperScript } from "../../../docx/run/script";
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { SubScript, SuperScript } from "../../../docx/run/script";
import { Utility } from "../../utility";
describe("SubScript", () => {
let subScript: SubScript;
@ -15,15 +11,15 @@ describe("SubScript", () => {
describe("#constructor()", () => {
it("should create a Sub Script with correct attributes", () => {
let newJson = jsonify(subScript);
let attributes = {
val: "subscript"
const newJson = Utility.jsonify(subScript);
const attributes = {
val: "subscript",
};
assert.equal(JSON.stringify(newJson.root[0].root), JSON.stringify(attributes));
});
it("should create a Sub Script with correct root key", () => {
let newJson = jsonify(subScript);
const newJson = Utility.jsonify(subScript);
assert.equal(newJson.rootKey, "w:vertAlign");
});
});
@ -38,16 +34,16 @@ describe("SuperScript", () => {
describe("#constructor()", () => {
it("should create a Super Script with correct attributes", () => {
let newJson = jsonify(superScript);
let attributes = {
val: "superscript"
const newJson = Utility.jsonify(superScript);
const attributes = {
val: "superscript",
};
assert.equal(JSON.stringify(newJson.root[0].root), JSON.stringify(attributes));
});
it("should create a Super Script with correct root key", () => {
let newJson = jsonify(superScript);
const newJson = Utility.jsonify(superScript);
assert.equal(newJson.rootKey, "w:vertAlign");
});
});
});
});

View File

@ -1,10 +1,6 @@
import { Strike, DoubleStrike } from "../../../docx/run/strike";
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { DoubleStrike, Strike } from "../../../docx/run/formatting";
import { Utility } from "../../utility";
describe("Strike", () => {
let strike: Strike;
@ -15,7 +11,7 @@ describe("Strike", () => {
describe("#constructor()", () => {
it("should create a Strike with correct root key", () => {
let newJson = jsonify(strike);
const newJson = Utility.jsonify(strike);
assert.equal(newJson.rootKey, "w:strike");
});
});
@ -30,8 +26,8 @@ describe("DoubleStrike", () => {
describe("#constructor()", () => {
it("should create a Double Strike with correct root key", () => {
let newJson = jsonify(strike);
const newJson = Utility.jsonify(strike);
assert.equal(newJson.rootKey, "w:dstrike");
});
});
});
});

View File

@ -1,10 +1,6 @@
import { Tab } from "../../../docx/run/tab";
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { Tab } from "../../../docx/run/tab";
import { Utility } from "../../utility";
describe("Tab", () => {
let tab: Tab;
@ -15,8 +11,8 @@ describe("Tab", () => {
describe("#constructor()", () => {
it("should create a Tab with correct root key", () => {
let newJson = jsonify(tab);
const newJson = Utility.jsonify(tab);
assert.equal(newJson.rootKey, "w:tab");
});
});
});
});

View File

@ -1,10 +1,6 @@
import { TextRun } from "../../../docx/run/text-run";
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { TextRun } from "../../../docx/run/text-run";
import { Utility } from "../../utility";
describe("TextRun", () => {
let run: TextRun;
@ -13,8 +9,8 @@ describe("TextRun", () => {
it("should add text into run", () => {
run = new TextRun("test");
let newJson = jsonify(run);
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[1].root, "test");
});
});
});
});

View File

@ -1,21 +1,33 @@
import { assert, expect } from "chai";
import * as u from "../../../docx/run/underline";
import { TextRun } from "../../../docx/run/text-run";
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { Formatter } from "../../../export/formatter";
import { Utility } from "../../utility";
describe("Underline", () => {
describe("#constructor()", () => {
it("should create a new Underline object with u:u as the rootKey", () => {
let underline = new u.Underline();
let newJson = jsonify(underline);
const underline = new u.Underline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.rootKey, "w:u");
});
it("should default to 'single' and no color", () => {
const underline = new u.Underline();
const tree = new Formatter().format(underline);
expect(tree).to.deep.equal({
"w:u": [{_attr: {"w:val": "single"}}],
});
});
it("should use the given style type and color", () => {
const underline = new u.Underline("double", "FF00CC");
const tree = new Formatter().format(underline);
expect(tree).to.deep.equal({
"w:u": [{_attr: {"w:val": "double", "w:color": "FF00CC"}}],
});
});
});
});
@ -23,14 +35,14 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should have u:u as the rootKey", () => {
let underline = new u.DashDotDotHeavyUnderline();
let newJson = jsonify(underline);
const underline = new u.DashDotDotHeavyUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.rootKey, "w:u");
});
it("should put value in attribute", () => {
let underline = new u.DashDotDotHeavyUnderline();
let newJson = jsonify(underline);
const underline = new u.DashDotDotHeavyUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dashDotDotHeavy");
});
});
@ -40,8 +52,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.DashDotHeavyUnderline();
let newJson = jsonify(underline);
const underline = new u.DashDotHeavyUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dashDotHeavy");
});
});
@ -51,8 +63,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.DashLongHeavyUnderline();
let newJson = jsonify(underline);
const underline = new u.DashLongHeavyUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dashLongHeavy");
});
});
@ -62,8 +74,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.DashLongUnderline();
let newJson = jsonify(underline);
const underline = new u.DashLongUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dashLong");
});
});
@ -73,8 +85,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.DashUnderline();
let newJson = jsonify(underline);
const underline = new u.DashUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dash");
});
});
@ -84,8 +96,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.DotDashUnderline();
let newJson = jsonify(underline);
const underline = new u.DotDashUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dotDash");
});
});
@ -95,8 +107,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.DotDotDashUnderline();
let newJson = jsonify(underline);
const underline = new u.DotDotDashUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dotDotDash");
});
});
@ -106,8 +118,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.DottedHeavyUnderline();
let newJson = jsonify(underline);
const underline = new u.DottedHeavyUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dottedHeavy");
});
});
@ -117,8 +129,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.DottedUnderline();
let newJson = jsonify(underline);
const underline = new u.DottedUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dotted");
});
});
@ -128,8 +140,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.DoubleUnderline();
let newJson = jsonify(underline);
const underline = new u.DoubleUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "double");
});
});
@ -139,8 +151,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.SingleUnderline();
let newJson = jsonify(underline);
const underline = new u.SingleUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "single");
});
});
@ -150,8 +162,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.ThickUnderline();
let newJson = jsonify(underline);
const underline = new u.ThickUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "thick");
});
});
@ -161,8 +173,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.WaveUnderline();
let newJson = jsonify(underline);
const underline = new u.WaveUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "wave");
});
});
@ -172,8 +184,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.WavyDoubleUnderline();
let newJson = jsonify(underline);
const underline = new u.WavyDoubleUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "wavyDouble");
});
});
@ -183,8 +195,8 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.WavyHeavyUnderline();
let newJson = jsonify(underline);
const underline = new u.WavyHeavyUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "wavyHeavy");
});
});
@ -194,9 +206,9 @@ describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
let underline = new u.WordsUnderline();
let newJson = jsonify(underline);
const underline = new u.WordsUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "words");
});
});
});
});

View File

@ -0,0 +1,37 @@
import { expect } from "chai";
import { GridCol, TableGrid } from "../../../docx/table/grid";
import { Formatter } from "../../../export/formatter";
describe("GridCol", () => {
describe("#constructor", () => {
it("sets the width attribute to the value given", () => {
const grid = new GridCol(1234);
const tree = new Formatter().format(grid);
expect(tree).to.deep.equal({
"w:gridCol": [{_attr: {"w:w": 1234}}],
});
});
it("does not set a width attribute if not given", () => {
const grid = new GridCol();
const tree = new Formatter().format(grid);
expect(tree).to.deep.equal({"w:gridCol": []});
});
});
});
describe("TableGrid", () => {
describe("#constructor", () => {
it("creates a column for each width given", () => {
const grid = new TableGrid([1234, 321, 123]);
const tree = new Formatter().format(grid);
expect(tree).to.deep.equal({
"w:tblGrid": [
{"w:gridCol": [{_attr: {"w:w": 1234}}]},
{"w:gridCol": [{_attr: {"w:w": 321}}]},
{"w:gridCol": [{_attr: {"w:w": 123}}]},
],
});
});
});
});

View File

@ -0,0 +1,37 @@
import { expect } from "chai";
import { TableProperties } from "../../../docx/table/properties";
import { Formatter } from "../../../export/formatter";
describe("TableProperties", () => {
describe("#constructor", () => {
it("creates an initially empty property object", () => {
const tp = new TableProperties();
const tree = new Formatter().format(tp);
expect(tree).to.deep.equal({"w:tblPr": []});
});
});
describe("#setWidth", () => {
it("adds a table width property", () => {
const tp = new TableProperties().setWidth("dxa", 1234);
const tree = new Formatter().format(tp);
expect(tree).to.deep.equal({
"w:tblPr": [
{"w:tblW": [{_attr: {"w:type": "dxa", "w:w": 1234}}]},
],
});
});
});
describe("#fixedWidthLayout", () => {
it("sets the table to fixed width layout", () => {
const tp = new TableProperties().fixedWidthLayout();
const tree = new Formatter().format(tp);
expect(tree).to.deep.equal({
"w:tblPr": [
{"w:tblLayout": [{_attr: {"w:type": "fixed"}}]},
],
});
});
});
});

View File

@ -0,0 +1,185 @@
import { expect } from "chai";
import { Paragraph } from "../../../docx/paragraph";
import { Table } from "../../../docx/table";
import { Formatter } from "../../../export/formatter";
describe("Table", () => {
describe("#constructor", () => {
it("creates a table with the correct number of rows and columns", () => {
const table = new Table(3, 2);
const tree = new Formatter().format(table);
const cell = {"w:tc": [{"w:tcPr": []}, {"w:p": [{"w:pPr": []}]}]};
expect(tree).to.deep.equal({
"w:tbl": [
{"w:tblPr": []},
{"w:tblGrid": [
{"w:gridCol": [{_attr: {"w:w": 1}}]},
{"w:gridCol": [{_attr: {"w:w": 1}}]},
]},
{"w:tr": [{"w:trPr": []}, cell, cell]},
{"w:tr": [{"w:trPr": []}, cell, cell]},
{"w:tr": [{"w:trPr": []}, cell, cell]},
],
});
});
});
describe("#getRow and Row#getCell", () => {
it("returns the correct row", () => {
const table = new Table(2, 2);
table.getRow(0).getCell(0).addContent(new Paragraph("A1"));
table.getRow(0).getCell(1).addContent(new Paragraph("B1"));
table.getRow(1).getCell(0).addContent(new Paragraph("A2"));
table.getRow(1).getCell(1).addContent(new Paragraph("B2"));
const tree = new Formatter().format(table);
const cell = (c) => ({"w:tc": [
{"w:tcPr": []},
{"w:p": [
{"w:pPr": []},
{"w:r": [{"w:rPr": []}, {"w:t": [c]}]},
]},
]});
expect(tree).to.deep.equal({
"w:tbl": [
{"w:tblPr": []},
{"w:tblGrid": [
{"w:gridCol": [{_attr: {"w:w": 1}}]},
{"w:gridCol": [{_attr: {"w:w": 1}}]},
]},
{"w:tr": [{"w:trPr": []}, cell("A1"), cell("B1")]},
{"w:tr": [{"w:trPr": []}, cell("A2"), cell("B2")]},
],
});
});
});
describe("#getCell", () => {
it("returns the correct cell", () => {
const table = new Table(2, 2);
table.getCell(0, 0).addContent(new Paragraph("A1"));
table.getCell(0, 1).addContent(new Paragraph("B1"));
table.getCell(1, 0).addContent(new Paragraph("A2"));
table.getCell(1, 1).addContent(new Paragraph("B2"));
const tree = new Formatter().format(table);
const cell = (c) => ({"w:tc": [
{"w:tcPr": []},
{"w:p": [
{"w:pPr": []},
{"w:r": [{"w:rPr": []}, {"w:t": [c]}]},
]},
]});
expect(tree).to.deep.equal({
"w:tbl": [
{"w:tblPr": []},
{"w:tblGrid": [
{"w:gridCol": [{_attr: {"w:w": 1}}]},
{"w:gridCol": [{_attr: {"w:w": 1}}]},
]},
{"w:tr": [{"w:trPr": []}, cell("A1"), cell("B1")]},
{"w:tr": [{"w:trPr": []}, cell("A2"), cell("B2")]},
],
});
});
});
describe("#setWidth", () => {
it("sets the preferred width on the table", () => {
const table = new Table(2, 2).setWidth("pct", 1000);
const tree = new Formatter().format(table);
expect(tree).to.have.property("w:tbl").which.is.an("array").with.has.length.at.least(1);
expect(tree["w:tbl"][0]).to.deep.equal({
"w:tblPr": [
{"w:tblW": [{_attr: {"w:type": "pct", "w:w": 1000}}]},
],
});
});
});
describe("#fixedWidthLayout", () => {
it("sets the table to fixed width layout", () => {
const table = new Table(2, 2).fixedWidthLayout();
const tree = new Formatter().format(table);
expect(tree).to.have.property("w:tbl").which.is.an("array").with.has.length.at.least(1);
expect(tree["w:tbl"][0]).to.deep.equal({
"w:tblPr": [
{"w:tblLayout": [{_attr: {"w:type": "fixed"}}]},
],
});
});
});
describe("Cell", () => {
describe("#prepForXml", () => {
it("inserts a paragraph at the end of the cell if it is empty", () => {
const table = new Table(1, 1);
const tree = new Formatter().format(table);
expect(tree).to.have.property("w:tbl").which.is.an("array");
const row = tree["w:tbl"].find((x) => x["w:tr"]);
expect(row).not.to.be.undefined;
expect(row["w:tr"]).to.be.an("array").which.has.length.at.least(1);
expect(row["w:tr"].find((x) => x["w:tc"])).to.deep.equal({
"w:tc": [
{"w:tcPr": []},
{"w:p": [{"w:pPr": []}]},
],
});
});
it("inserts a paragraph at the end of the cell even if it has a child table", () => {
const parentTable = new Table(1, 1);
parentTable.getCell(0, 0).addContent(new Table(1, 1));
const tree = new Formatter().format(parentTable);
expect(tree).to.have.property("w:tbl").which.is.an("array");
const row = tree["w:tbl"].find((x) => x["w:tr"]);
expect(row).not.to.be.undefined;
expect(row["w:tr"]).to.be.an("array").which.has.length.at.least(1);
const cell = row["w:tr"].find((x) => x["w:tc"]);
expect(cell).not.to.be.undefined;
expect(cell["w:tc"][cell["w:tc"].length - 1]).to.deep.equal({
"w:p": [{"w:pPr": []}],
});
});
it("does not insert a paragraph if it already ends with one", () => {
const parentTable = new Table(1, 1);
parentTable.getCell(0, 0).addContent(new Paragraph("Hello"));
const tree = new Formatter().format(parentTable);
expect(tree).to.have.property("w:tbl").which.is.an("array");
const row = tree["w:tbl"].find((x) => x["w:tr"]);
expect(row).not.to.be.undefined;
expect(row["w:tr"]).to.be.an("array").which.has.length.at.least(1);
expect(row["w:tr"].find((x) => x["w:tc"])).to.deep.equal({
"w:tc": [
{"w:tcPr": []},
{"w:p": [
{"w:pPr": []},
{"w:r": [{"w:rPr": []}, {"w:t": ["Hello"]}]},
]},
],
});
});
});
describe("#createParagraph", () => {
it("inserts a new paragraph in the cell", () => {
const table = new Table(1, 1);
const para = table.getCell(0, 0).createParagraph("Test paragraph");
expect(para).to.be.an.instanceof(Paragraph);
const tree = new Formatter().format(table);
expect(tree).to.have.property("w:tbl").which.is.an("array");
const row = tree["w:tbl"].find((x) => x["w:tr"]);
expect(row).not.to.be.undefined;
expect(row["w:tr"]).to.be.an("array").which.has.length.at.least(1);
expect(row["w:tr"].find((x) => x["w:tc"])).to.deep.equal({
"w:tc": [
{"w:tcPr": []},
{"w:p": [
{"w:pPr": []},
{"w:r": [{"w:rPr": []}, {"w:t": ["Test paragraph"]}]},
]},
],
});
});
});
});
});

View File

@ -1,38 +1,25 @@
import { Attributes } from "../../../docx/xml-components";
import { assert } from "chai";
import { Attributes } from "../../../docx/xml-components";
describe("Attribute", () => {
let attributes: Attributes;
beforeEach(() => {
attributes = new Attributes();
});
describe("#constructor()", () => {
it("should not add val with empty constructor", () => {
let newAttrs = new Attributes();
let stringifiedJson = JSON.stringify(newAttrs);
let newJson = JSON.parse(stringifiedJson);
assert.isUndefined(newJson.root.val);
});
it("should have val as defined with populated constructor", () => {
let newAttrs = new Attributes({
val: "test"
const newAttrs = new Attributes({
val: "test",
});
let stringifiedJson = JSON.stringify(newAttrs);
let newJson = JSON.parse(stringifiedJson);
const stringifiedJson = JSON.stringify(newAttrs);
const newJson = JSON.parse(stringifiedJson);
assert.equal(newJson.root.val, "test");
});
it("should have space value as defined with populated constructor", () => {
let newAttrs = new Attributes({
space: "spaceTest"
const newAttrs = new Attributes({
space: "spaceTest",
});
let stringifiedJson = JSON.stringify(newAttrs);
let newJson = JSON.parse(stringifiedJson);
const stringifiedJson = JSON.stringify(newAttrs);
const newJson = JSON.parse(stringifiedJson);
assert.equal(newJson.root.space, "spaceTest");
});
});
});
});

View File

@ -1,10 +1,6 @@
import { XmlComponent } from "../../../docx/xml-components";
import { assert } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
}
import { XmlComponent } from "../../../docx/xml-components";
import { Utility } from "../../utility";
class TestComponent extends XmlComponent {
@ -20,17 +16,8 @@ describe("XmlComponent", () => {
describe("#constructor()", () => {
it("should create an Xml Component which has the correct rootKey", () => {
let newJson = jsonify(xmlComponent);
const newJson = Utility.jsonify(xmlComponent);
assert.equal(newJson.rootKey, "w:test");
});
});
describe("#replaceKey", () => {
it("should replace the key to the specified root key", () => {
xmlComponent.replaceKey();
let newJson = jsonify(xmlComponent);
assert.isDefined(newJson["w:test"]);
});
});
});
});

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