Compare commits
69 Commits
Author | SHA1 | Date | |
---|---|---|---|
26a3fa6946 | |||
ee1a7818b6 | |||
8e455f1097 | |||
827c46cf47 | |||
61cbee829d | |||
f0ad1e9194 | |||
bbd339f0e4 | |||
858af64dc3 | |||
01e34c1b28 | |||
41acb475a9 | |||
20978f8c24 | |||
265a3dbe21 | |||
627f5b4bf0 | |||
1538c1aaeb | |||
839661e5f8 | |||
fff6244597 | |||
b2280f64a1 | |||
21d53c41d0 | |||
6e94bbb5e5 | |||
7d655cd3f7 | |||
00821677c8 | |||
784de3e430 | |||
3213c4838d | |||
1b9bc8eb1d | |||
bf58d0b864 | |||
28ca8392ed | |||
5238c55bc2 | |||
901f10c387 | |||
defa22ffe5 | |||
84919c0cc0 | |||
1e8ca123b0 | |||
3ae7c6aedf | |||
6a3ed4bbf8 | |||
e632d323c9 | |||
d2b35ab8f2 | |||
e60f39df41 | |||
076431e04d | |||
75ab44403c | |||
e2d6097819 | |||
ab12ff1257 | |||
25a0212f4e | |||
a8993f14d6 | |||
1834cd86da | |||
437de27ed8 | |||
96f08482da | |||
edec2eca7a | |||
9202524d83 | |||
0461907533 | |||
445a2896d2 | |||
e2d8f1b6b1 | |||
7baa696a76 | |||
3f7ca6bbff | |||
36e1c5fe6a | |||
75a03f1576 | |||
437e83ab78 | |||
b8232f7a02 | |||
49eadb0efc | |||
40dc90e585 | |||
0de302d192 | |||
80bab95f6c | |||
ba3d551c9f | |||
d14fe31f97 | |||
057f41e355 | |||
8c9b61b37a | |||
11e54b3e2c | |||
fa7cb0bef1 | |||
3977c8ab3b | |||
e8f92efe05 | |||
994df8531b |
11
.nycrc
11
.nycrc
@ -1,14 +1,15 @@
|
||||
{
|
||||
"check-coverage": true,
|
||||
"lines": 93.53,
|
||||
"functions": 89.63,
|
||||
"branches": 88.57,
|
||||
"statements": 93.34,
|
||||
"lines": 96.81,
|
||||
"functions": 93.80,
|
||||
"branches": 92.63,
|
||||
"statements": 96.80,
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts"
|
||||
"src/**/*.spec.ts",
|
||||
"src/import-dotx/import-dotx.ts"
|
||||
],
|
||||
"reporter": [
|
||||
"lcov",
|
||||
|
11
README.md
11
README.md
@ -34,6 +34,15 @@ Here is an example of `docx` working in `Angular`:
|
||||
|
||||
* https://stackblitz.com/edit/angular-afvxtz
|
||||
|
||||
Here is an example of `docx` working in `React`:
|
||||
|
||||
* https://stackblitz.com/edit/react-ts-qq25sp
|
||||
* https://stackblitz.com/edit/react-ts-qdqu7z (adding images to Word Document)
|
||||
|
||||
Here is an example of `docx` working in `Vue.js`:
|
||||
|
||||
* https://stackblitz.com/edit/vuejs-docx
|
||||
|
||||
## Node
|
||||
|
||||
Press `endpoint` on the `RunKit` website:
|
||||
@ -50,7 +59,7 @@ Press `endpoint` on the `RunKit` website:
|
||||
* https://runkit.com/dolanmiu/docx-demo8 - Header and Footer
|
||||
* https://runkit.com/dolanmiu/docx-demo10 - **My CV generated with docx**
|
||||
|
||||
More [here](https://docx.js.org/#/examples) and [here](https://github.com/dolanmiu/docx/tree/master/demo)
|
||||
More [here](https://github.com/dolanmiu/docx/tree/master/demo)
|
||||
|
||||
# How to use & Documentation
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Also includes an example on how to center tables
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { AlignmentType, Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build";
|
||||
import { AlignmentType, BorderStyle, Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
@ -184,7 +184,7 @@ const table5 = new Table({
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [],
|
||||
children: [new Paragraph("1,0")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("1,2")],
|
||||
@ -195,10 +195,10 @@ const table5 = new Table({
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [],
|
||||
children: [new Paragraph("2,0")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [],
|
||||
children: [new Paragraph("2,1")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@ -209,6 +209,163 @@ const table5 = new Table({
|
||||
},
|
||||
});
|
||||
|
||||
const borders = {
|
||||
top: {
|
||||
style: BorderStyle.DASH_SMALL_GAP,
|
||||
size: 1,
|
||||
color: "red",
|
||||
},
|
||||
bottom: {
|
||||
style: BorderStyle.DASH_SMALL_GAP,
|
||||
size: 1,
|
||||
color: "red",
|
||||
},
|
||||
left: {
|
||||
style: BorderStyle.DASH_SMALL_GAP,
|
||||
size: 1,
|
||||
color: "red",
|
||||
},
|
||||
right: {
|
||||
style: BorderStyle.DASH_SMALL_GAP,
|
||||
size: 1,
|
||||
color: "red",
|
||||
},
|
||||
};
|
||||
|
||||
const table6 = new Table({
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
borders,
|
||||
children: [new Paragraph("0,0")],
|
||||
rowSpan: 2,
|
||||
}),
|
||||
new TableCell({
|
||||
borders,
|
||||
children: [new Paragraph("0,1")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
borders,
|
||||
children: [new Paragraph("1,1")],
|
||||
rowSpan: 2,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
borders,
|
||||
children: [new Paragraph("2,0")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
width: {
|
||||
size: 100,
|
||||
type: WidthType.PERCENTAGE,
|
||||
},
|
||||
});
|
||||
|
||||
const table7 = new Table({
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("0,0")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("0,1")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("0,2")],
|
||||
rowSpan: 2,
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("0,3")],
|
||||
rowSpan: 3,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("1,0")],
|
||||
columnSpan: 2,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("2,0")],
|
||||
columnSpan: 2,
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("2,2")],
|
||||
rowSpan: 2,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("3,0")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("3,1")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("3,3")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
width: {
|
||||
size: 100,
|
||||
type: WidthType.PERCENTAGE,
|
||||
},
|
||||
});
|
||||
|
||||
const table8 = new Table({
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({ children: [new Paragraph("1,1")] }),
|
||||
new TableCell({ children: [new Paragraph("1,2")] }),
|
||||
new TableCell({ children: [new Paragraph("1,3")] }),
|
||||
new TableCell({ children: [new Paragraph("1,4")], rowSpan: 4, borders }),
|
||||
],
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({ children: [new Paragraph("2,1")] }),
|
||||
new TableCell({ children: [new Paragraph("2,2")] }),
|
||||
new TableCell({ children: [new Paragraph("2,3")], rowSpan: 3 }),
|
||||
],
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({ children: [new Paragraph("3,1")] }),
|
||||
new TableCell({ children: [new Paragraph("3,2")], rowSpan: 2 }),
|
||||
],
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({ children: [new Paragraph("4,1")] }),
|
||||
],
|
||||
}),
|
||||
],
|
||||
width: {
|
||||
size: 100,
|
||||
type: WidthType.PERCENTAGE,
|
||||
},
|
||||
});
|
||||
|
||||
doc.addSection({
|
||||
children: [
|
||||
table,
|
||||
@ -222,10 +379,16 @@ doc.addSection({
|
||||
heading: HeadingLevel.HEADING_2,
|
||||
}),
|
||||
table3,
|
||||
new Paragraph("Merging columns"),
|
||||
new Paragraph("Merging columns 1"),
|
||||
table4,
|
||||
new Paragraph("More Merging columns"),
|
||||
new Paragraph("Merging columns 2"),
|
||||
table5,
|
||||
new Paragraph("Merging columns 3"),
|
||||
table6,
|
||||
new Paragraph("Merging columns 4"),
|
||||
table7,
|
||||
new Paragraph("Merging columns 5"),
|
||||
table8,
|
||||
],
|
||||
});
|
||||
|
||||
|
@ -1,13 +1,3 @@
|
||||
<p align="center">
|
||||
<img alt="clippy the assistant" src="https://i.imgur.com/37uBGhO.gif">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
Easily generate .docx files with JS/TS. Works for Node and on the Browser. :100:
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
# Welcome
|
||||
|
||||
## Installation
|
||||
@ -66,12 +56,6 @@ Packer.toBuffer(doc).then((buffer) => {
|
||||
// Done! A file called 'My First Document.docx' will be in your file system.
|
||||
```
|
||||
|
||||
## Honoured Mentions
|
||||
|
||||
[@felipeochoa](https://github.com/felipeochoa)
|
||||
|
||||
[@h4buli](https://github.com/h4buli)
|
||||
|
||||
<p align="center">
|
||||
<img alt="clippy the assistant" src="http://i60.tinypic.com/339pvtt.png">
|
||||
</p>
|
||||
|
10
docs/_coverpage.md
Normal file
10
docs/_coverpage.md
Normal file
@ -0,0 +1,10 @@
|
||||
<img src="https://i.imgur.com/37uBGhO.gif" alt="drawing" style="width:200px;"/>
|
||||
|
||||
> Easily generate .docx files with JS/TS. Works for Node and on the Browser. :100:
|
||||
|
||||
- Simple, declarative API
|
||||
- 50+ usage examples
|
||||
- Battle tested, mature, 95%+ coverage
|
||||
|
||||
[GitHub](https://github.com/dolanmiu/docx)
|
||||
[Get Started](#Welcome)
|
@ -1,6 +1,6 @@
|
||||
* [Getting Started](/)
|
||||
|
||||
* [Examples](examples.md)
|
||||
* [Examples](https://github.com/dolanmiu/docx/tree/master/demo)
|
||||
|
||||
* API
|
||||
|
||||
|
@ -1,25 +1,23 @@
|
||||
# Contribution Guidelines
|
||||
|
||||
* Include documentation reference(s) at the top of each file:
|
||||
- Include documentation reference(s) at the top of each file:
|
||||
|
||||
```ts
|
||||
// http://officeopenxml.com/WPdocument.php
|
||||
```
|
||||
|
||||
* Follow Prettier standards, and consider using the [Prettier VSCode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) plugin.
|
||||
- Follow Prettier standards, and consider using the [Prettier VSCode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) plugin.
|
||||
|
||||
* Follow the `TSLint` rules
|
||||
- Follow the `TSLint` rules
|
||||
|
||||
## Always think about the user
|
||||
|
||||
The number one pillar for contribution to `docx` is to **ALWAYS** think about how the user will use `docx`.
|
||||
|
||||
Put yourself in their position, and imagine how they would feel about your feature you wrote.
|
||||
|
||||
1. Is it easy to use?
|
||||
2. Has it been documented well?
|
||||
3. Is it intuitive?
|
||||
4. Is it consistent with the rest of the API?
|
||||
4. Is it declarative?
|
||||
5. Is it fun to use?
|
||||
|
||||
## Good Commit Names
|
||||
@ -27,6 +25,7 @@ Put yourself in their position, and imagine how they would feel about your featu
|
||||
Please write good commit messages when making a commit: https://chris.beams.io/posts/git-commit/
|
||||
|
||||
**Do not:**
|
||||
|
||||
```
|
||||
c // What?
|
||||
rtl // Adding acryonyms without explaining anything else is not helpful
|
||||
@ -35,34 +34,6 @@ demo updated // Getting better, but capitalize the first letter
|
||||
Unesesary coment removed // Make sure to use correct spelling
|
||||
```
|
||||
|
||||
## No leaky components in API interface
|
||||
|
||||
> This mainly applies to the API the end user will consume.
|
||||
|
||||
Try to make method parameters of the outside API accept primitives, or `json` objects, so that child components are created **inside** the component, rather than being **injected** in.
|
||||
|
||||
This is so that:
|
||||
|
||||
1. Imports are much cleaner for the end user, no need for:
|
||||
```ts
|
||||
import { ChildComponent } from "./my-feature/sub-component/deeper/.../my-deep.component";
|
||||
```
|
||||
|
||||
2. This is what I consider "leakage". The code is aware of the underlying implementation of the component.
|
||||
3. It means the end user does not need to import and create the child component to be injected.
|
||||
|
||||
**Do not**
|
||||
|
||||
`TableFloatProperties` is a class. The outside world would have to `new` up the object, and inject it in like so:
|
||||
|
||||
```ts
|
||||
public float(tableFloatProperties: TableFloatProperties): Table
|
||||
```
|
||||
|
||||
```ts
|
||||
table.float(new TableFloatProperties(...));
|
||||
```
|
||||
|
||||
**Do**
|
||||
|
||||
`ITableFloatOptions` is an interface for a JSON of primitives. The end user would need to pass in a json object and not need to worry about the internals:
|
||||
@ -71,31 +42,29 @@ This is so that:
|
||||
public float(tableFloatOptions: ITableFloatOptions): Table
|
||||
```
|
||||
|
||||
## Delcariative API
|
||||
|
||||
Make sure the API is declarative, so no _method calling_ or _mutation_. This is a design decision, consistent with the rest of the project. There are benefits to delcariative code over other styles of code, explained here: https://dzone.com/articles/why-declarative-coding-makes-you-a-better-programm
|
||||
|
||||
**Do not:**
|
||||
|
||||
```ts
|
||||
table.float({...});
|
||||
const paragraph = doc.createParagraph();
|
||||
const text = paragraph.createText();
|
||||
text.contents = "Hello World";
|
||||
```
|
||||
|
||||
## Add vs Create
|
||||
**Do**
|
||||
|
||||
This is just a guideline, and the rules can sometimes be broken.
|
||||
|
||||
* Use `create` if the method `new`'s up an element inside:
|
||||
|
||||
```ts
|
||||
public createParagraph() {
|
||||
const paragraph = new Paragraph();
|
||||
this.root.push(paragraph);
|
||||
}
|
||||
```
|
||||
|
||||
* Use `add` if you add the element into the method as a parameter.
|
||||
*Note:* This may look like its breaking the previous guideline, but it has semantically different meanings. The previous one is using data to construct an object, whereas this one is simply adding elements into the document:
|
||||
|
||||
```ts
|
||||
public add(paragraph: Paragraph) {
|
||||
this.root.push(paragraph);
|
||||
}
|
||||
```
|
||||
```ts
|
||||
doc.addSection({
|
||||
children: [
|
||||
new Paragraph({
|
||||
children: [new TextRun("Hello World")],
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## Getters and Setters
|
||||
|
||||
@ -107,7 +76,7 @@ public get Level() {
|
||||
}
|
||||
```
|
||||
|
||||
There is no performance advantage by doing this. It means we don't need to prefix all private variables with the ugly `_`:
|
||||
This is the convention of this project. There is no performance advantage by doing this. It means we don't need to prefix all private variables with `_`:
|
||||
|
||||
**Do not:**
|
||||
|
||||
@ -121,30 +90,6 @@ private get _level: string;
|
||||
private get level: string;
|
||||
```
|
||||
|
||||
## Temporal Methods
|
||||
|
||||
Some methods are `non-temporal`, which means regardless of when you call the method, it will have the same affect on the document. For example, setting the width of a table at the end of the document will have the same effect as setting the width at the start:
|
||||
|
||||
```ts
|
||||
table.setWidth(1000); // now removed as of version 5.0.0
|
||||
```
|
||||
|
||||
Whereas some methods are `temporal`, which means depending on the time-frame they are called, it would produce a difference result. For example, moving `createParagraph()` around your code will physically alter the document.
|
||||
|
||||
```ts
|
||||
doc.createParagraph("hello");
|
||||
```
|
||||
|
||||
If a method is `non-temporal`, put it in the objects `constructor`. For example:
|
||||
|
||||
```ts
|
||||
const table = new Table(width: number);
|
||||
```
|
||||
|
||||
`Non-temporal` methods are usually methods which can only be used one time and one time only. For example, `.float()`. It does not make sense to call `.float()` again if its already floating.
|
||||
|
||||
I am not sure what the real term is, but this will do.
|
||||
|
||||
## Interfaces over type alias
|
||||
|
||||
Do not use `type`, but rather use `Interfaces`. `type` cannot be extended, and a class cannot implement it.
|
||||
@ -152,14 +97,14 @@ Do not use `type`, but rather use `Interfaces`. `type` cannot be extended, and a
|
||||
> "In general, use what you want ( type alias / interface ) just be consistent"
|
||||
> "always use interface for public API's definition when authoring a library or 3rd party ambient type definitions"
|
||||
>
|
||||
> * https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c
|
||||
> - https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c
|
||||
|
||||
`Interface` is generally preferred over `type`: https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types
|
||||
|
||||
**Do not:**
|
||||
|
||||
```ts
|
||||
type RelationshipFileInfo = { id: number, target: string };
|
||||
type RelationshipFileInfo = { id: number; target: string };
|
||||
```
|
||||
|
||||
**Do:**
|
||||
@ -193,26 +138,26 @@ enum WeaponType = {
|
||||
|
||||
## Spell correctly, in full and in American English
|
||||
|
||||
I am not sure where these habits in software development come from, but I do not believe it is beneficial:
|
||||
|
||||
**Do not:**
|
||||
|
||||
```ts
|
||||
readdy // misspelling
|
||||
perm // abbreviation
|
||||
conf // abbreviation
|
||||
cnty // abbreviation
|
||||
relationFile // abbreviation
|
||||
colour // U.K. English
|
||||
readdy; // misspelling
|
||||
perm; // abbreviation
|
||||
conf; // abbreviation
|
||||
cnty; // abbreviation
|
||||
relationFile; // abbreviation
|
||||
colour; // U.K. English
|
||||
```
|
||||
|
||||
**Do:**
|
||||
|
||||
```ts
|
||||
ready
|
||||
permission
|
||||
config
|
||||
country
|
||||
relationshipFile
|
||||
color
|
||||
ready;
|
||||
permission;
|
||||
config;
|
||||
country;
|
||||
relationshipFile;
|
||||
color;
|
||||
```
|
||||
|
||||
## Keep files small (within reason)
|
||||
|
219
docs/examples.md
219
docs/examples.md
@ -1,219 +0,0 @@
|
||||
# Examples
|
||||
|
||||
> All examples can run independently and can be found in the `/demo` folder of the project
|
||||
|
||||
All the examples below can be ran locally, to do so, run the following command:
|
||||
|
||||
```sh
|
||||
npm run demo
|
||||
```
|
||||
|
||||
This command will run the `demo selector app` in the `/demo` folder. It will prompt you to select a demo number, which will run a demo from that folder.
|
||||
|
||||
## Simple
|
||||
|
||||
A simple hello world of the `docx` library:
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo1.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo1.ts_
|
||||
|
||||
## Styles
|
||||
|
||||
### Styling with JS
|
||||
|
||||
This example shows how to customise the look and feel of a document using JS configuration
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo2.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo2.ts_
|
||||
|
||||
### Styling with XML
|
||||
|
||||
This example shows how to customise the look and feel of a document using XML configuration
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo13.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts_
|
||||
|
||||
## Numbering
|
||||
|
||||
This example shows many levels of numbering
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo3.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo3.ts_
|
||||
|
||||
## Table
|
||||
|
||||
Example of simple table
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
|
||||
|
||||
### Styling table borders
|
||||
|
||||
Styling the borders of a table
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
|
||||
|
||||
## Images
|
||||
|
||||
### Add image to the document
|
||||
|
||||
Importing Images from file system path
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
|
||||
|
||||
### Add images to header and footer
|
||||
|
||||
Example showing how to add image to headers and footers
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
|
||||
|
||||
### Scaling images
|
||||
|
||||
Example showing how to scale images
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo12.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo12.ts_
|
||||
|
||||
### Add Image to media before adding to document
|
||||
|
||||
This is the best way to add an image to a document because you can add the same image in two locations without increasing document size by re-using the same image
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo23.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo23.ts_
|
||||
|
||||
### Add image to table
|
||||
|
||||
As before, to add an image to a table, you would need to add it to the `Media` object first
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
|
||||
|
||||
### Images using Base64 URI
|
||||
|
||||
If you want to use a Base64 image instead
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo18.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo18.ts_
|
||||
|
||||
## Margins
|
||||
|
||||
Example showing how to set custom margins
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo6.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo6.ts_
|
||||
|
||||
## Orientation
|
||||
|
||||
Example showing how to set the document to `landscape` or `portrait`
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo7.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo7.ts_
|
||||
|
||||
## Headers & Footers
|
||||
|
||||
Example showing how to add headers and footers
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo8.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo8.ts_
|
||||
|
||||
## Multiple headers and footers
|
||||
|
||||
Check out `Sections` for this feature
|
||||
|
||||
## Page Breaks
|
||||
|
||||
### Normal page breaks
|
||||
|
||||
Example showing how to page break
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo14.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo14.ts_
|
||||
|
||||
### Page break before
|
||||
|
||||
Example showing how to page break before like in Word
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo15.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo15.ts_
|
||||
|
||||
## Sections
|
||||
|
||||
Example of how sections work. Sections allow multiple headers and footers, and `landscape`/`portrait` inside the same document.
|
||||
Also you can have different page number formats and starts for different sections.
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo16.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo16.ts_
|
||||
|
||||
## Footnotes
|
||||
|
||||
Example of how to add footnotes. Good for references
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo17.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo17.ts_
|
||||
|
||||
## Packers
|
||||
|
||||
## Buffer output
|
||||
|
||||
Example showing how to use the Buffer packer and then write that buffer to the file system
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo19.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo19.ts_
|
||||
|
||||
|
||||
## Bookmarks
|
||||
|
||||
Example showing how to make bookmarks to make internal hyperlinks within the document
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo21.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo21.ts_
|
||||
|
||||
## Bidirectional text
|
||||
|
||||
Example showing how to use bidirectional text for certain languages such as Hebrew
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo22.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo22.ts_
|
||||
|
||||
## Showcase
|
||||
|
||||
### My CV
|
||||
|
||||
Example showing how to add headers and footers
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo10.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo10.ts_
|
||||
|
||||
### Style and Images
|
||||
|
||||
This example shows how to customise the look and feel of a document and add images
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo11.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo11.ts_
|
@ -1,40 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>docx - Generate .docx documents with JavaScript</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="Generate .docx documents with JavaScript">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta name="description" content="Generate .docx documents with JavaScript" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/style.min.css"
|
||||
title="docsify-darklight-theme"
|
||||
type="text/css"
|
||||
/>
|
||||
</head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: 'docx',
|
||||
repo: 'https://github.com/dolanmiu/docx',
|
||||
name: "docx",
|
||||
repo: "https://github.com/dolanmiu/docx",
|
||||
loadSidebar: true,
|
||||
subMaxLevel: 2,
|
||||
search: 'auto',
|
||||
}
|
||||
search: "auto",
|
||||
coverpage: true,
|
||||
};
|
||||
</script>
|
||||
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
|
||||
<script src="//unpkg.com/docsify/lib/plugins/emoji.min.js"></script>
|
||||
<script src="https://unpkg.com/docsify-copy-code@2"></script>
|
||||
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
|
||||
<script src="//unpkg.com/prismjs/components/prism-typescript.min.js"></script>
|
||||
<script
|
||||
src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js"
|
||||
type="text/javascript">
|
||||
</script>
|
||||
</body>
|
||||
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -2,11 +2,7 @@
|
||||
|
||||
> Packers are the way in which `docx` turns your code into `.docx` format. It is completely decoupled from the `docx.Document`.
|
||||
|
||||
Packers in `version 4` and above are now one single `Packer`. It works in both a node and browser environment (Angular etc). Now, the packer returns a `Buffer`, `Blob` or `base64 string`. It is up to you to take that and persist it with node's `fs`, send it down as a downloadable file, or anything else you wish. As of version 4, this library will not have options to export to PDF.
|
||||
|
||||
## Version 5
|
||||
|
||||
Packers in `version 5` and above are now static methods on `Packer`.
|
||||
Packers works in both a node and browser environment (Angular etc). Now, the packer returns a `Buffer`, `Blob` or `base64 string`. It is up to you to take that and persist it with node's `fs`, send it down as a downloadable file, or anything else you wish. As of `version 4+`, this library will not have options to export to PDF.
|
||||
|
||||
### Export as Buffer
|
||||
|
||||
@ -36,117 +32,3 @@ Packer.toBlob(doc).then((blob) => {
|
||||
saveAs(blob, "example.docx");
|
||||
});
|
||||
```
|
||||
|
||||
## Version 4
|
||||
|
||||
The `Packer` in `version 4` requires an instance of `Packer`, so be sure to `new` it.
|
||||
|
||||
### Export as Buffer
|
||||
|
||||
This will return a NodeJS `Buffer`. If this is used in the browser, it will return a `UInt8Array` instead.
|
||||
|
||||
```ts
|
||||
const packer = new docx.Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
||||
```
|
||||
|
||||
### Export as a `base64` string
|
||||
|
||||
```ts
|
||||
const packer = new docx.Packer();
|
||||
|
||||
packer.toBase64String(doc).then((string) => {
|
||||
console.log(string);
|
||||
});
|
||||
```
|
||||
|
||||
### Export as Blob
|
||||
|
||||
This is useful if you want to send it as an downloadable in a browser environment.
|
||||
|
||||
```ts
|
||||
const packer = new docx.Packer();
|
||||
|
||||
packer.toBlob(doc).then((blob) => {
|
||||
// saveAs from FileSaver will download the file
|
||||
saveAs(blob, "example.docx");
|
||||
});
|
||||
```
|
||||
|
||||
## Version 3 and below
|
||||
|
||||
### File System Packer
|
||||
|
||||
```ts
|
||||
const docx = require("docx");
|
||||
|
||||
const doc = new docx.Document();
|
||||
const exporter = new docx.LocalPacker(doc);
|
||||
exporter.pack("My Document");
|
||||
// Word Document is in file system
|
||||
```
|
||||
|
||||
### Buffer Packer
|
||||
|
||||
```ts
|
||||
const docx = require("docx");
|
||||
|
||||
const doc = new docx.Document();
|
||||
const exporter = new docx.BufferPacker(doc);
|
||||
const buffer = exporter.pack();
|
||||
```
|
||||
|
||||
### Stream Packer
|
||||
|
||||
Creates a `node` `Readable` stream
|
||||
|
||||
```ts
|
||||
const docx = require("docx");
|
||||
|
||||
const doc = new docx.Document();
|
||||
const exporter = new docx.StreamPacker(doc);
|
||||
const stream = exporter.pack();
|
||||
```
|
||||
|
||||
### Express Packer
|
||||
|
||||
The old express packer is now deprecated and may disappear soon, so you should upgrade.
|
||||
|
||||
The reason for this is because it means this project needs to know about and use `express`, which for a Word document generator, does not sound right. Seperation of concerns.
|
||||
|
||||
It will still be usable (for now), but it is ill advised.
|
||||
|
||||
I used the express exporter in my [website](http://www.dolan.bio).
|
||||
|
||||
The recommended way is to use the `StreamPacker` and handle the `express` magic outside of the library:
|
||||
|
||||
```ts
|
||||
const docx = require("docx");
|
||||
|
||||
const doc = new docx.Document();
|
||||
const exporter = new docx.StreamPacker(doc);
|
||||
|
||||
const stream = exporter.pack();
|
||||
|
||||
// Express' response object
|
||||
res.attachment("yourfile.xlsx");
|
||||
stream.pipe(res);
|
||||
```
|
||||
|
||||
where `res` is the response object obtained through the Express router. It is that simple. The file will begin downloading in the browser.
|
||||
|
||||
### PDF Exporting
|
||||
|
||||
You can export your word document as a PDF file like so:
|
||||
|
||||
```ts
|
||||
const exporter = new docx.LocalPacker(doc);
|
||||
exporter.packPdf("My Document");
|
||||
|
||||
// Express
|
||||
const exporter = new docx.ExpressPacker(doc, res);
|
||||
exporter.packPdf("My Document");
|
||||
```
|
||||
|
3154
package-lock.json
generated
3154
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docx",
|
||||
"version": "5.2.0",
|
||||
"version": "5.2.2",
|
||||
"description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
@ -64,10 +64,10 @@
|
||||
"homepage": "https://github.com/dolanmiu/docx#readme",
|
||||
"devDependencies": {
|
||||
"@types/chai": "^3.4.35",
|
||||
"@types/mocha": "^2.2.39",
|
||||
"@types/mocha": "^8.0.0",
|
||||
"@types/request-promise": "^4.1.42",
|
||||
"@types/shortid": "0.0.29",
|
||||
"@types/sinon": "^4.3.1",
|
||||
"@types/sinon": "^9.0.4",
|
||||
"@types/webpack": "^4.4.24",
|
||||
"awesome-typescript-loader": "^3.4.1",
|
||||
"chai": "^3.5.0",
|
||||
@ -77,7 +77,7 @@
|
||||
"jszip": "^3.1.5",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha-webpack": "^1.0.1",
|
||||
"nyc": "^14.1.1",
|
||||
"nyc": "^15.1.0",
|
||||
"pre-commit": "^1.2.2",
|
||||
"prettier": "^1.15.2",
|
||||
"prompt": "^1.0.0",
|
||||
@ -85,12 +85,12 @@
|
||||
"request": "^2.88.0",
|
||||
"request-promise": "^4.2.2",
|
||||
"rimraf": "^2.5.2",
|
||||
"shelljs": "^0.7.7",
|
||||
"sinon": "^5.0.7",
|
||||
"ts-node": "^7.0.1",
|
||||
"shelljs": "^0.8.4",
|
||||
"sinon": "^9.0.2",
|
||||
"ts-node": "^8.10.2",
|
||||
"tslint": "^5.11.0",
|
||||
"tslint-immutable": "^4.9.0",
|
||||
"typedoc": "^0.11.1",
|
||||
"typedoc": "^0.16.11",
|
||||
"typescript": "2.9.2",
|
||||
"webpack": "^3.10.0"
|
||||
},
|
||||
|
@ -45,7 +45,7 @@ export class Body extends XmlComponent {
|
||||
private createSectionParagraph(section: SectionProperties): Paragraph {
|
||||
const paragraph = new Paragraph({});
|
||||
const properties = new ParagraphProperties({});
|
||||
properties.addChildElement(section);
|
||||
properties.push(section);
|
||||
paragraph.addChildElement(properties);
|
||||
return paragraph;
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ export enum PageNumberFormat {
|
||||
ORDINAL_TEXT = "ordinalText",
|
||||
UPPER_LETTER = "upperLetter",
|
||||
UPPER_ROMAN = "upperRoman",
|
||||
DECIMAL_FULL_WIDTH = "decimalFullWidth",
|
||||
}
|
||||
|
||||
export interface IPageNumberTypeAttributes {
|
||||
|
@ -7,6 +7,7 @@ import { AlignmentType, EmphasisMarkType, TabStopPosition } from "../paragraph";
|
||||
import { UnderlineType } from "../paragraph/run/underline";
|
||||
import { ShadingType } from "../table";
|
||||
import { AbstractNumbering } from "./abstract-numbering";
|
||||
import { LevelSuffix } from "./level";
|
||||
|
||||
describe("AbstractNumbering", () => {
|
||||
it("stores its ID at its .id property", () => {
|
||||
@ -48,6 +49,20 @@ describe("AbstractNumbering", () => {
|
||||
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } });
|
||||
});
|
||||
|
||||
it("has suffix", () => {
|
||||
const abstractNumbering = new AbstractNumbering(1, [
|
||||
{
|
||||
level: 3,
|
||||
format: "lowerLetter",
|
||||
text: "%1)",
|
||||
alignment: AlignmentType.END,
|
||||
suffix: LevelSuffix.SPACE,
|
||||
},
|
||||
]);
|
||||
const tree = new Formatter().format(abstractNumbering);
|
||||
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:suff": { _attr: { "w:val": "space" } } });
|
||||
});
|
||||
|
||||
describe("formatting methods: paragraph properties", () => {
|
||||
it("#indent", () => {
|
||||
const abstractNumbering = new AbstractNumbering(1, [
|
||||
@ -283,22 +298,41 @@ describe("AbstractNumbering", () => {
|
||||
});
|
||||
|
||||
describe("formatting methods: run properties", () => {
|
||||
it("#size", () => {
|
||||
const sizeTests = [
|
||||
{
|
||||
size: 24,
|
||||
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
|
||||
},
|
||||
{
|
||||
size: 24,
|
||||
sizeComplexScript: true,
|
||||
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
|
||||
},
|
||||
{
|
||||
size: 24,
|
||||
sizeComplexScript: false,
|
||||
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }],
|
||||
},
|
||||
{
|
||||
size: 24,
|
||||
sizeComplexScript: 26,
|
||||
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 26 } } }],
|
||||
},
|
||||
];
|
||||
sizeTests.forEach(({ size, sizeComplexScript, expected }) => {
|
||||
it(`#size ${size} cs ${sizeComplexScript}`, () => {
|
||||
const abstractNumbering = new AbstractNumbering(1, [
|
||||
{
|
||||
level: 0,
|
||||
format: "lowerRoman",
|
||||
text: "%0.",
|
||||
style: {
|
||||
run: {
|
||||
size: 24,
|
||||
},
|
||||
run: { size, sizeComplexScript },
|
||||
},
|
||||
},
|
||||
]);
|
||||
const tree = new Formatter().format(abstractNumbering);
|
||||
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||
"w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }],
|
||||
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": expected });
|
||||
});
|
||||
});
|
||||
|
||||
@ -478,83 +512,185 @@ describe("AbstractNumbering", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("#bold", () => {
|
||||
const abstractNumbering = new AbstractNumbering(1, [
|
||||
const boldTests = [
|
||||
{
|
||||
level: 0,
|
||||
format: "lowerRoman",
|
||||
text: "%0.",
|
||||
style: {
|
||||
run: {
|
||||
bold: true,
|
||||
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
bold: true,
|
||||
boldComplexScript: true,
|
||||
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
bold: true,
|
||||
boldComplexScript: false,
|
||||
expected: [{ "w:b": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
]);
|
||||
const tree = new Formatter().format(abstractNumbering);
|
||||
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||
"w:rPr": [{ "w:b": { _attr: { "w:val": true } } }],
|
||||
});
|
||||
});
|
||||
|
||||
it("#italics", () => {
|
||||
];
|
||||
boldTests.forEach(({ bold, boldComplexScript, expected }) => {
|
||||
it(`#bold ${bold} cs ${boldComplexScript}`, () => {
|
||||
const abstractNumbering = new AbstractNumbering(1, [
|
||||
{
|
||||
level: 0,
|
||||
format: "lowerRoman",
|
||||
text: "%0.",
|
||||
style: {
|
||||
run: {
|
||||
run: { bold, boldComplexScript },
|
||||
},
|
||||
},
|
||||
]);
|
||||
const tree = new Formatter().format(abstractNumbering);
|
||||
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": expected });
|
||||
});
|
||||
});
|
||||
|
||||
const italicsTests = [
|
||||
{
|
||||
italics: true,
|
||||
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
italics: true,
|
||||
italicsComplexScript: true,
|
||||
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
italics: true,
|
||||
italicsComplexScript: false,
|
||||
expected: [{ "w:i": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
]);
|
||||
const tree = new Formatter().format(abstractNumbering);
|
||||
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||
"w:rPr": [{ "w:i": { _attr: { "w:val": true } } }],
|
||||
});
|
||||
});
|
||||
|
||||
it("#highlight", () => {
|
||||
];
|
||||
italicsTests.forEach(({ italics, italicsComplexScript, expected }) => {
|
||||
it(`#italics ${italics} cs ${italicsComplexScript}`, () => {
|
||||
const abstractNumbering = new AbstractNumbering(1, [
|
||||
{
|
||||
level: 0,
|
||||
format: "lowerRoman",
|
||||
text: "%0.",
|
||||
style: {
|
||||
run: {
|
||||
run: { italics, italicsComplexScript },
|
||||
},
|
||||
},
|
||||
]);
|
||||
const tree = new Formatter().format(abstractNumbering);
|
||||
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": expected });
|
||||
});
|
||||
});
|
||||
|
||||
const highlightTests = [
|
||||
{
|
||||
highlight: "005599",
|
||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
|
||||
},
|
||||
{
|
||||
highlight: "005599",
|
||||
highlightComplexScript: true,
|
||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
|
||||
},
|
||||
{
|
||||
highlight: "005599",
|
||||
highlightComplexScript: false,
|
||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
||||
},
|
||||
]);
|
||||
const tree = new Formatter().format(abstractNumbering);
|
||||
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||
"w:rPr": [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
||||
});
|
||||
});
|
||||
|
||||
it("#shadow", () => {
|
||||
{
|
||||
highlight: "005599",
|
||||
highlightComplexScript: "550099",
|
||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "550099" } } }],
|
||||
},
|
||||
];
|
||||
highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => {
|
||||
it(`#highlight ${highlight} cs ${highlightComplexScript}`, () => {
|
||||
const abstractNumbering = new AbstractNumbering(1, [
|
||||
{
|
||||
level: 0,
|
||||
format: "lowerRoman",
|
||||
text: "%0.",
|
||||
style: {
|
||||
run: {
|
||||
run: { highlight, highlightComplexScript },
|
||||
},
|
||||
},
|
||||
]);
|
||||
const tree = new Formatter().format(abstractNumbering);
|
||||
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": expected });
|
||||
});
|
||||
});
|
||||
|
||||
const shadingTests = [
|
||||
{
|
||||
shadow: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
expected: [
|
||||
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
shading: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
expected: [
|
||||
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
shading: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
shadingComplexScript: true,
|
||||
expected: [
|
||||
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
shading: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
shadingComplexScript: false,
|
||||
expected: [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
|
||||
},
|
||||
{
|
||||
shading: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
shadingComplexScript: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "00FF00",
|
||||
},
|
||||
expected: [
|
||||
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "00FF00" } } },
|
||||
],
|
||||
},
|
||||
];
|
||||
shadingTests.forEach(({ shadow, shading, shadingComplexScript, expected }) => {
|
||||
it("#shadow correctly", () => {
|
||||
const abstractNumbering = new AbstractNumbering(1, [
|
||||
{
|
||||
level: 0,
|
||||
format: "lowerRoman",
|
||||
text: "%0.",
|
||||
style: {
|
||||
run: { shadow, shading, shadingComplexScript },
|
||||
},
|
||||
},
|
||||
]);
|
||||
const tree = new Formatter().format(abstractNumbering);
|
||||
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
|
||||
"w:rPr": [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
|
||||
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": expected });
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,19 +1,7 @@
|
||||
import { Attributes, XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
import {
|
||||
Alignment,
|
||||
AlignmentType,
|
||||
Indent,
|
||||
KeepLines,
|
||||
KeepNext,
|
||||
Spacing,
|
||||
TabStop,
|
||||
TabStopType,
|
||||
ThematicBreak,
|
||||
} from "../paragraph/formatting";
|
||||
import { ParagraphProperties } from "../paragraph/properties";
|
||||
import * as formatting from "../paragraph/run/formatting";
|
||||
import { RunProperties } from "../paragraph/run/properties";
|
||||
import { IParagraphStyleOptions2, IRunStyleOptions } from "../styles/style-options";
|
||||
import { AlignmentType } from "../paragraph/formatting";
|
||||
import { IParagraphStylePropertiesOptions, ParagraphProperties } from "../paragraph/properties";
|
||||
import { IRunStylePropertiesOptions, RunProperties } from "../paragraph/run/properties";
|
||||
|
||||
interface ILevelAttributesProperties {
|
||||
readonly ilvl?: number;
|
||||
@ -85,8 +73,8 @@ export interface ILevelsOptions {
|
||||
readonly start?: number;
|
||||
readonly suffix?: LevelSuffix;
|
||||
readonly style?: {
|
||||
readonly run?: IRunStyleOptions;
|
||||
readonly paragraph?: IParagraphStyleOptions2;
|
||||
readonly run?: IRunStylePropertiesOptions;
|
||||
readonly paragraph?: IParagraphStylePropertiesOptions;
|
||||
};
|
||||
}
|
||||
|
||||
@ -125,8 +113,8 @@ export class LevelBase extends XmlComponent {
|
||||
this.root.push(new LevelText(text));
|
||||
}
|
||||
|
||||
this.paragraphProperties = new ParagraphProperties({});
|
||||
this.runProperties = new RunProperties();
|
||||
this.paragraphProperties = new ParagraphProperties(style && style.paragraph);
|
||||
this.runProperties = new RunProperties(style && style.run);
|
||||
|
||||
this.root.push(this.paragraphProperties);
|
||||
this.root.push(this.runProperties);
|
||||
@ -134,104 +122,6 @@ export class LevelBase extends XmlComponent {
|
||||
if (suffix) {
|
||||
this.root.push(new Suffix(suffix));
|
||||
}
|
||||
|
||||
if (style) {
|
||||
if (style.run) {
|
||||
if (style.run.size) {
|
||||
this.runProperties.push(new formatting.Size(style.run.size));
|
||||
}
|
||||
|
||||
if (style.run.bold) {
|
||||
this.runProperties.push(new formatting.Bold());
|
||||
}
|
||||
|
||||
if (style.run.italics) {
|
||||
this.runProperties.push(new formatting.Italics());
|
||||
}
|
||||
|
||||
if (style.run.smallCaps) {
|
||||
this.runProperties.push(new formatting.SmallCaps());
|
||||
}
|
||||
|
||||
if (style.run.allCaps) {
|
||||
this.runProperties.push(new formatting.Caps());
|
||||
}
|
||||
|
||||
if (style.run.strike) {
|
||||
this.runProperties.push(new formatting.Strike());
|
||||
}
|
||||
|
||||
if (style.run.doubleStrike) {
|
||||
this.runProperties.push(new formatting.DoubleStrike());
|
||||
}
|
||||
|
||||
if (style.run.subScript) {
|
||||
this.runProperties.push(new formatting.SubScript());
|
||||
}
|
||||
|
||||
if (style.run.superScript) {
|
||||
this.runProperties.push(new formatting.SuperScript());
|
||||
}
|
||||
|
||||
if (style.run.underline) {
|
||||
this.runProperties.push(new formatting.Underline(style.run.underline.type, style.run.underline.color));
|
||||
}
|
||||
|
||||
if (style.run.emphasisMark) {
|
||||
this.runProperties.push(new formatting.EmphasisMark(style.run.emphasisMark.type));
|
||||
}
|
||||
|
||||
if (style.run.color) {
|
||||
this.runProperties.push(new formatting.Color(style.run.color));
|
||||
}
|
||||
|
||||
if (style.run.font) {
|
||||
this.runProperties.push(new formatting.RunFonts(style.run.font));
|
||||
}
|
||||
|
||||
if (style.run.highlight) {
|
||||
this.runProperties.push(new formatting.Highlight(style.run.highlight));
|
||||
}
|
||||
|
||||
if (style.run.shadow) {
|
||||
this.runProperties.push(new formatting.Shading(style.run.shadow.type, style.run.shadow.fill, style.run.shadow.color));
|
||||
}
|
||||
}
|
||||
|
||||
if (style.paragraph) {
|
||||
if (style.paragraph.alignment) {
|
||||
this.paragraphProperties.push(new Alignment(style.paragraph.alignment));
|
||||
}
|
||||
|
||||
if (style.paragraph.thematicBreak) {
|
||||
this.paragraphProperties.push(new ThematicBreak());
|
||||
}
|
||||
|
||||
if (style.paragraph.rightTabStop) {
|
||||
this.paragraphProperties.push(new TabStop(TabStopType.RIGHT, style.paragraph.rightTabStop));
|
||||
}
|
||||
|
||||
if (style.paragraph.leftTabStop) {
|
||||
this.paragraphProperties.push(new TabStop(TabStopType.LEFT, style.paragraph.leftTabStop));
|
||||
}
|
||||
|
||||
if (style.paragraph.indent) {
|
||||
this.paragraphProperties.push(new Indent(style.paragraph.indent));
|
||||
}
|
||||
|
||||
if (style.paragraph.spacing) {
|
||||
this.paragraphProperties.push(new Spacing(style.paragraph.spacing));
|
||||
}
|
||||
|
||||
if (style.paragraph.keepNext) {
|
||||
this.paragraphProperties.push(new KeepNext());
|
||||
}
|
||||
|
||||
if (style.paragraph.keepLines) {
|
||||
this.paragraphProperties.push(new KeepLines());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,48 +3,13 @@ import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run"
|
||||
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
||||
|
||||
import { File } from "../file";
|
||||
import { Alignment, AlignmentType } from "./formatting/alignment";
|
||||
import { Bidirectional } from "./formatting/bidirectional";
|
||||
import { IBorderOptions, ThematicBreak } from "./formatting/border";
|
||||
import { IIndentAttributesProperties, Indent } from "./formatting/indent";
|
||||
import { KeepLines, KeepNext } from "./formatting/keep";
|
||||
import { PageBreak, PageBreakBefore } from "./formatting/page-break";
|
||||
import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spacing";
|
||||
import { HeadingLevel, Style } from "./formatting/style";
|
||||
import { LeaderType, TabStop, TabStopPosition, TabStopType } from "./formatting/tab-stop";
|
||||
import { NumberProperties } from "./formatting/unordered-list";
|
||||
import { Bookmark, HyperlinkRef, OutlineLevel } from "./links";
|
||||
import { ParagraphProperties } from "./properties";
|
||||
import { PageBreak } from "./formatting/page-break";
|
||||
import { Bookmark, HyperlinkRef } from "./links";
|
||||
import { IParagraphPropertiesOptions, ParagraphProperties } from "./properties";
|
||||
import { PictureRun, Run, SequentialIdentifier, SymbolRun, TextRun } from "./run";
|
||||
|
||||
export interface IParagraphOptions {
|
||||
export interface IParagraphOptions extends IParagraphPropertiesOptions {
|
||||
readonly text?: string;
|
||||
readonly border?: IBorderOptions;
|
||||
readonly spacing?: ISpacingProperties;
|
||||
readonly outlineLevel?: number;
|
||||
readonly alignment?: AlignmentType;
|
||||
readonly heading?: HeadingLevel;
|
||||
readonly bidirectional?: boolean;
|
||||
readonly thematicBreak?: boolean;
|
||||
readonly pageBreakBefore?: boolean;
|
||||
readonly contextualSpacing?: boolean;
|
||||
readonly indent?: IIndentAttributesProperties;
|
||||
readonly keepLines?: boolean;
|
||||
readonly keepNext?: boolean;
|
||||
readonly tabStops?: Array<{
|
||||
readonly position: number | TabStopPosition;
|
||||
readonly type: TabStopType;
|
||||
readonly leader?: LeaderType;
|
||||
}>;
|
||||
readonly style?: string;
|
||||
readonly bullet?: {
|
||||
readonly level: number;
|
||||
};
|
||||
readonly numbering?: {
|
||||
readonly reference: string;
|
||||
readonly level: number;
|
||||
readonly custom?: boolean;
|
||||
};
|
||||
readonly children?: Array<
|
||||
TextRun | PictureRun | SymbolRun | Bookmark | PageBreak | SequentialIdentifier | FootnoteReferenceRun | HyperlinkRef
|
||||
>;
|
||||
@ -70,9 +35,7 @@ export class Paragraph extends XmlComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
this.properties = new ParagraphProperties({
|
||||
border: options.border,
|
||||
});
|
||||
this.properties = new ParagraphProperties(options);
|
||||
|
||||
this.root.push(this.properties);
|
||||
|
||||
@ -80,72 +43,6 @@ export class Paragraph extends XmlComponent {
|
||||
this.root.push(new TextRun(options.text));
|
||||
}
|
||||
|
||||
if (options.spacing) {
|
||||
this.properties.push(new Spacing(options.spacing));
|
||||
}
|
||||
|
||||
if (options.outlineLevel !== undefined) {
|
||||
this.properties.push(new OutlineLevel(options.outlineLevel));
|
||||
}
|
||||
|
||||
if (options.alignment) {
|
||||
this.properties.push(new Alignment(options.alignment));
|
||||
}
|
||||
|
||||
if (options.heading) {
|
||||
this.properties.push(new Style(options.heading));
|
||||
}
|
||||
|
||||
if (options.bidirectional) {
|
||||
this.properties.push(new Bidirectional());
|
||||
}
|
||||
|
||||
if (options.thematicBreak) {
|
||||
this.properties.push(new ThematicBreak());
|
||||
}
|
||||
|
||||
if (options.pageBreakBefore) {
|
||||
this.properties.push(new PageBreakBefore());
|
||||
}
|
||||
|
||||
if (options.contextualSpacing) {
|
||||
this.properties.push(new ContextualSpacing(options.contextualSpacing));
|
||||
}
|
||||
|
||||
if (options.indent) {
|
||||
this.properties.push(new Indent(options.indent));
|
||||
}
|
||||
|
||||
if (options.keepLines) {
|
||||
this.properties.push(new KeepLines());
|
||||
}
|
||||
|
||||
if (options.keepNext) {
|
||||
this.properties.push(new KeepNext());
|
||||
}
|
||||
|
||||
if (options.tabStops) {
|
||||
for (const tabStop of options.tabStops) {
|
||||
this.properties.push(new TabStop(tabStop.type, tabStop.position, tabStop.leader));
|
||||
}
|
||||
}
|
||||
|
||||
if (options.style) {
|
||||
this.properties.push(new Style(options.style));
|
||||
}
|
||||
|
||||
if (options.bullet) {
|
||||
this.properties.push(new Style("ListParagraph"));
|
||||
this.properties.push(new NumberProperties(1, options.bullet.level));
|
||||
}
|
||||
|
||||
if (options.numbering) {
|
||||
if (!options.numbering.custom) {
|
||||
this.properties.push(new Style("ListParagraph"));
|
||||
}
|
||||
this.properties.push(new NumberProperties(options.numbering.reference, options.numbering.level));
|
||||
}
|
||||
|
||||
if (options.children) {
|
||||
for (const child of options.children) {
|
||||
if (child instanceof Bookmark) {
|
||||
|
@ -1,19 +1,136 @@
|
||||
// http://officeopenxml.com/WPparagraphProperties.php
|
||||
import { IgnoreIfEmptyXmlComponent, XmlComponent } from "file/xml-components";
|
||||
import { Alignment, AlignmentType } from "./formatting/alignment";
|
||||
import { Bidirectional } from "./formatting/bidirectional";
|
||||
import { Border, IBorderOptions, ThematicBreak } from "./formatting/border";
|
||||
import { IIndentAttributesProperties, Indent } from "./formatting/indent";
|
||||
import { KeepLines, KeepNext } from "./formatting/keep";
|
||||
import { PageBreakBefore } from "./formatting/page-break";
|
||||
import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spacing";
|
||||
import { HeadingLevel, Style } from "./formatting/style";
|
||||
import { LeaderType, TabStop, TabStopPosition, TabStopType } from "./formatting/tab-stop";
|
||||
import { NumberProperties } from "./formatting/unordered-list";
|
||||
import { OutlineLevel } from "./links";
|
||||
|
||||
import { Border, IBorderOptions } from "./formatting/border";
|
||||
export interface IParagraphStylePropertiesOptions {
|
||||
readonly alignment?: AlignmentType;
|
||||
readonly thematicBreak?: boolean;
|
||||
readonly contextualSpacing?: boolean;
|
||||
readonly rightTabStop?: number;
|
||||
readonly leftTabStop?: number;
|
||||
readonly indent?: IIndentAttributesProperties;
|
||||
readonly spacing?: ISpacingProperties;
|
||||
readonly keepNext?: boolean;
|
||||
readonly keepLines?: boolean;
|
||||
readonly outlineLevel?: number;
|
||||
}
|
||||
|
||||
interface IParagraphPropertiesOptions {
|
||||
export interface IParagraphPropertiesOptions extends IParagraphStylePropertiesOptions {
|
||||
readonly border?: IBorderOptions;
|
||||
readonly heading?: HeadingLevel;
|
||||
readonly bidirectional?: boolean;
|
||||
readonly pageBreakBefore?: boolean;
|
||||
readonly tabStops?: Array<{
|
||||
readonly position: number | TabStopPosition;
|
||||
readonly type: TabStopType;
|
||||
readonly leader?: LeaderType;
|
||||
}>;
|
||||
readonly style?: string;
|
||||
readonly bullet?: {
|
||||
readonly level: number;
|
||||
};
|
||||
readonly numbering?: {
|
||||
readonly reference: string;
|
||||
readonly level: number;
|
||||
readonly custom?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export class ParagraphProperties extends IgnoreIfEmptyXmlComponent {
|
||||
constructor(options: IParagraphPropertiesOptions) {
|
||||
constructor(options?: IParagraphPropertiesOptions) {
|
||||
super("w:pPr");
|
||||
|
||||
if (!options) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.border) {
|
||||
this.push(new Border(options.border));
|
||||
}
|
||||
|
||||
if (options.spacing) {
|
||||
this.push(new Spacing(options.spacing));
|
||||
}
|
||||
|
||||
if (options.outlineLevel !== undefined) {
|
||||
this.push(new OutlineLevel(options.outlineLevel));
|
||||
}
|
||||
|
||||
if (options.alignment) {
|
||||
this.push(new Alignment(options.alignment));
|
||||
}
|
||||
|
||||
if (options.heading) {
|
||||
this.push(new Style(options.heading));
|
||||
}
|
||||
|
||||
if (options.bidirectional) {
|
||||
this.push(new Bidirectional());
|
||||
}
|
||||
|
||||
if (options.thematicBreak) {
|
||||
this.push(new ThematicBreak());
|
||||
}
|
||||
|
||||
if (options.pageBreakBefore) {
|
||||
this.push(new PageBreakBefore());
|
||||
}
|
||||
|
||||
if (options.contextualSpacing) {
|
||||
this.push(new ContextualSpacing(options.contextualSpacing));
|
||||
}
|
||||
|
||||
if (options.indent) {
|
||||
this.push(new Indent(options.indent));
|
||||
}
|
||||
|
||||
if (options.keepLines) {
|
||||
this.push(new KeepLines());
|
||||
}
|
||||
|
||||
if (options.keepNext) {
|
||||
this.push(new KeepNext());
|
||||
}
|
||||
|
||||
if (options.tabStops) {
|
||||
for (const tabStop of options.tabStops) {
|
||||
this.push(new TabStop(tabStop.type, tabStop.position, tabStop.leader));
|
||||
}
|
||||
}
|
||||
|
||||
if (options.style) {
|
||||
this.push(new Style(options.style));
|
||||
}
|
||||
|
||||
if (options.bullet) {
|
||||
this.push(new Style("ListParagraph"));
|
||||
this.push(new NumberProperties(1, options.bullet.level));
|
||||
}
|
||||
|
||||
if (options.numbering) {
|
||||
if (!options.numbering.custom) {
|
||||
this.push(new Style("ListParagraph"));
|
||||
}
|
||||
this.push(new NumberProperties(options.numbering.reference, options.numbering.level));
|
||||
}
|
||||
|
||||
if (options.rightTabStop) {
|
||||
this.push(new TabStop(TabStopType.RIGHT, options.rightTabStop));
|
||||
}
|
||||
|
||||
if (options.leftTabStop) {
|
||||
this.push(new TabStop(TabStopType.LEFT, options.leftTabStop));
|
||||
}
|
||||
}
|
||||
|
||||
public push(item: XmlComponent): void {
|
||||
|
@ -1,13 +0,0 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
export class SmallCaps extends XmlComponent {
|
||||
constructor() {
|
||||
super("w:smallCaps");
|
||||
}
|
||||
}
|
||||
|
||||
export class Caps extends XmlComponent {
|
||||
constructor() {
|
||||
super("w:caps");
|
||||
}
|
||||
}
|
@ -115,17 +115,6 @@ export class Imprint extends XmlComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/* export class Shadow extends XmlComponent {
|
||||
constructor() {
|
||||
super("w:shadow");
|
||||
this.root.push(
|
||||
new Attributes({
|
||||
val: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
} */
|
||||
|
||||
export class SmallCaps extends XmlComponent {
|
||||
constructor() {
|
||||
super("w:smallCaps");
|
||||
|
@ -1,4 +1,5 @@
|
||||
export * from "./run";
|
||||
export * from "./properties";
|
||||
export * from "./text-run";
|
||||
export * from "./symbol-run";
|
||||
export * from "./picture-run";
|
||||
|
@ -1,8 +1,183 @@
|
||||
import { ShadingType } from "file/table";
|
||||
import { IgnoreIfEmptyXmlComponent, XmlComponent } from "file/xml-components";
|
||||
import { EmphasisMark, EmphasisMarkType } from "./emphasis-mark";
|
||||
import {
|
||||
Bold,
|
||||
BoldComplexScript,
|
||||
Caps,
|
||||
CharacterSpacing,
|
||||
Color,
|
||||
DoubleStrike,
|
||||
Highlight,
|
||||
HighlightComplexScript,
|
||||
Italics,
|
||||
ItalicsComplexScript,
|
||||
RightToLeft,
|
||||
Shading,
|
||||
ShadowComplexScript,
|
||||
Size,
|
||||
SizeComplexScript,
|
||||
SmallCaps,
|
||||
Strike,
|
||||
} from "./formatting";
|
||||
import { IFontAttributesProperties, RunFonts } from "./run-fonts";
|
||||
import { SubScript, SuperScript } from "./script";
|
||||
import { Style } from "./style";
|
||||
import { Underline, UnderlineType } from "./underline";
|
||||
|
||||
interface IFontOptions {
|
||||
readonly name: string;
|
||||
readonly hint?: string;
|
||||
}
|
||||
|
||||
export interface IRunStylePropertiesOptions {
|
||||
readonly bold?: boolean;
|
||||
readonly boldComplexScript?: boolean;
|
||||
readonly italics?: boolean;
|
||||
readonly italicsComplexScript?: boolean;
|
||||
readonly underline?: {
|
||||
readonly color?: string;
|
||||
readonly type?: UnderlineType;
|
||||
};
|
||||
readonly emphasisMark?: {
|
||||
readonly type?: EmphasisMarkType;
|
||||
};
|
||||
readonly color?: string;
|
||||
readonly size?: number;
|
||||
readonly sizeComplexScript?: boolean | number;
|
||||
readonly rightToLeft?: boolean;
|
||||
readonly smallCaps?: boolean;
|
||||
readonly allCaps?: boolean;
|
||||
readonly strike?: boolean;
|
||||
readonly doubleStrike?: boolean;
|
||||
readonly subScript?: boolean;
|
||||
readonly superScript?: boolean;
|
||||
readonly font?: string | IFontOptions | IFontAttributesProperties;
|
||||
readonly highlight?: string;
|
||||
readonly highlightComplexScript?: boolean | string;
|
||||
readonly characterSpacing?: number;
|
||||
readonly shading?: {
|
||||
readonly type: ShadingType;
|
||||
readonly fill: string;
|
||||
readonly color: string;
|
||||
};
|
||||
readonly shadingComplexScript?: boolean | IRunStylePropertiesOptions["shading"];
|
||||
readonly shadow?: IRunStylePropertiesOptions["shading"];
|
||||
}
|
||||
|
||||
export interface IRunPropertiesOptions extends IRunStylePropertiesOptions {
|
||||
readonly style?: string;
|
||||
}
|
||||
|
||||
export class RunProperties extends IgnoreIfEmptyXmlComponent {
|
||||
constructor() {
|
||||
constructor(options?: IRunPropertiesOptions) {
|
||||
super("w:rPr");
|
||||
|
||||
if (!options) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.bold) {
|
||||
this.push(new Bold());
|
||||
}
|
||||
if ((options.boldComplexScript === undefined && options.bold) || options.boldComplexScript) {
|
||||
this.push(new BoldComplexScript());
|
||||
}
|
||||
|
||||
if (options.italics) {
|
||||
this.push(new Italics());
|
||||
}
|
||||
if ((options.italicsComplexScript === undefined && options.italics) || options.italicsComplexScript) {
|
||||
this.push(new ItalicsComplexScript());
|
||||
}
|
||||
|
||||
if (options.underline) {
|
||||
this.push(new Underline(options.underline.type, options.underline.color));
|
||||
}
|
||||
|
||||
if (options.emphasisMark) {
|
||||
this.push(new EmphasisMark(options.emphasisMark.type));
|
||||
}
|
||||
|
||||
if (options.color) {
|
||||
this.push(new Color(options.color));
|
||||
}
|
||||
|
||||
if (options.size) {
|
||||
this.push(new Size(options.size));
|
||||
}
|
||||
const szCs =
|
||||
options.sizeComplexScript === undefined || options.sizeComplexScript === true ? options.size : options.sizeComplexScript;
|
||||
if (szCs) {
|
||||
this.push(new SizeComplexScript(szCs));
|
||||
}
|
||||
|
||||
if (options.rightToLeft) {
|
||||
this.push(new RightToLeft());
|
||||
}
|
||||
|
||||
if (options.smallCaps) {
|
||||
this.push(new SmallCaps());
|
||||
}
|
||||
|
||||
if (options.allCaps) {
|
||||
this.push(new Caps());
|
||||
}
|
||||
|
||||
if (options.strike) {
|
||||
this.push(new Strike());
|
||||
}
|
||||
|
||||
if (options.doubleStrike) {
|
||||
this.push(new DoubleStrike());
|
||||
}
|
||||
|
||||
if (options.subScript) {
|
||||
this.push(new SubScript());
|
||||
}
|
||||
|
||||
if (options.superScript) {
|
||||
this.push(new SuperScript());
|
||||
}
|
||||
|
||||
if (options.style) {
|
||||
this.push(new Style(options.style));
|
||||
}
|
||||
|
||||
if (options.font) {
|
||||
if (typeof options.font === "string") {
|
||||
this.push(new RunFonts(options.font));
|
||||
} else if ("name" in options.font) {
|
||||
this.push(new RunFonts(options.font.name, options.font.hint));
|
||||
} else {
|
||||
this.push(new RunFonts(options.font));
|
||||
}
|
||||
}
|
||||
|
||||
if (options.highlight) {
|
||||
this.push(new Highlight(options.highlight));
|
||||
}
|
||||
const highlightCs =
|
||||
options.highlightComplexScript === undefined || options.highlightComplexScript === true
|
||||
? options.highlight
|
||||
: options.highlightComplexScript;
|
||||
if (highlightCs) {
|
||||
this.push(new HighlightComplexScript(highlightCs));
|
||||
}
|
||||
|
||||
if (options.characterSpacing) {
|
||||
this.push(new CharacterSpacing(options.characterSpacing));
|
||||
}
|
||||
|
||||
const shading = options.shading || options.shadow;
|
||||
if (shading) {
|
||||
this.push(new Shading(shading.type, shading.fill, shading.color));
|
||||
}
|
||||
const shdCs =
|
||||
options.shadingComplexScript === undefined || options.shadingComplexScript === true ? shading : options.shadingComplexScript;
|
||||
if (shdCs) {
|
||||
this.push(new ShadowComplexScript(shdCs.type, shdCs.fill, shdCs.color));
|
||||
}
|
||||
}
|
||||
|
||||
public push(item: XmlComponent): void {
|
||||
|
@ -116,7 +116,7 @@ describe("Run", () => {
|
||||
});
|
||||
const tree = new Formatter().format(run);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:r": [{ "w:rPr": [{ "w:smallCaps": {} }] }],
|
||||
"w:r": [{ "w:rPr": [{ "w:smallCaps": { _attr: { "w:val": true } } }] }],
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -128,7 +128,7 @@ describe("Run", () => {
|
||||
});
|
||||
const tree = new Formatter().format(run);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:r": [{ "w:rPr": [{ "w:caps": {} }] }],
|
||||
"w:r": [{ "w:rPr": [{ "w:caps": { _attr: { "w:val": true } } }] }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,69 +1,15 @@
|
||||
// http://officeopenxml.com/WPtext.php
|
||||
import { ShadingType } from "file/table";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
||||
import { FieldInstruction } from "file/table-of-contents/field-instruction";
|
||||
import { Break } from "./break";
|
||||
import { Caps, SmallCaps } from "./caps";
|
||||
import { EmphasisMark, EmphasisMarkType } from "./emphasis-mark";
|
||||
import { Begin, End, Separate } from "./field";
|
||||
import {
|
||||
Bold,
|
||||
BoldComplexScript,
|
||||
Color,
|
||||
DoubleStrike,
|
||||
Highlight,
|
||||
HighlightComplexScript,
|
||||
Italics,
|
||||
ItalicsComplexScript,
|
||||
RightToLeft,
|
||||
Shading,
|
||||
ShadowComplexScript,
|
||||
Size,
|
||||
SizeComplexScript,
|
||||
Strike,
|
||||
} from "./formatting";
|
||||
import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number";
|
||||
import { RunProperties } from "./properties";
|
||||
import { IRunPropertiesOptions, RunProperties } from "./properties";
|
||||
import { Text } from "./run-components/text";
|
||||
import { IFontAttributesProperties, RunFonts } from "./run-fonts";
|
||||
import { SubScript, SuperScript } from "./script";
|
||||
import { Style } from "./style";
|
||||
import { Underline, UnderlineType } from "./underline";
|
||||
|
||||
interface IFontOptions {
|
||||
readonly name: string;
|
||||
readonly hint?: string;
|
||||
}
|
||||
|
||||
export interface IRunOptions {
|
||||
readonly bold?: true;
|
||||
readonly italics?: true;
|
||||
readonly underline?: {
|
||||
readonly color?: string;
|
||||
readonly type?: UnderlineType;
|
||||
};
|
||||
readonly emphasisMark?: {
|
||||
readonly type?: EmphasisMarkType;
|
||||
};
|
||||
readonly color?: string;
|
||||
readonly size?: number;
|
||||
readonly rightToLeft?: boolean;
|
||||
readonly smallCaps?: boolean;
|
||||
readonly allCaps?: boolean;
|
||||
readonly strike?: boolean;
|
||||
readonly doubleStrike?: boolean;
|
||||
readonly subScript?: boolean;
|
||||
readonly superScript?: boolean;
|
||||
readonly style?: string;
|
||||
readonly font?: IFontOptions | IFontAttributesProperties;
|
||||
readonly highlight?: string;
|
||||
readonly shading?: {
|
||||
readonly type: ShadingType;
|
||||
readonly fill: string;
|
||||
readonly color: string;
|
||||
};
|
||||
export interface IRunOptions extends IRunPropertiesOptions {
|
||||
readonly children?: Array<Begin | FieldInstruction | Separate | End | PageNumber | FootnoteReferenceRun | string>;
|
||||
readonly text?: string;
|
||||
}
|
||||
@ -79,86 +25,9 @@ export class Run extends XmlComponent {
|
||||
|
||||
constructor(options: IRunOptions) {
|
||||
super("w:r");
|
||||
this.properties = new RunProperties();
|
||||
this.properties = new RunProperties(options);
|
||||
this.root.push(this.properties);
|
||||
|
||||
if (options.bold) {
|
||||
this.properties.push(new Bold());
|
||||
this.properties.push(new BoldComplexScript());
|
||||
}
|
||||
|
||||
if (options.italics) {
|
||||
this.properties.push(new Italics());
|
||||
this.properties.push(new ItalicsComplexScript());
|
||||
}
|
||||
|
||||
if (options.underline) {
|
||||
this.properties.push(new Underline(options.underline.type, options.underline.color));
|
||||
}
|
||||
|
||||
if (options.emphasisMark) {
|
||||
this.properties.push(new EmphasisMark(options.emphasisMark.type));
|
||||
}
|
||||
|
||||
if (options.color) {
|
||||
this.properties.push(new Color(options.color));
|
||||
}
|
||||
|
||||
if (options.size) {
|
||||
this.properties.push(new Size(options.size));
|
||||
this.properties.push(new SizeComplexScript(options.size));
|
||||
}
|
||||
|
||||
if (options.rightToLeft) {
|
||||
this.properties.push(new RightToLeft());
|
||||
}
|
||||
|
||||
if (options.smallCaps) {
|
||||
this.properties.push(new SmallCaps());
|
||||
}
|
||||
|
||||
if (options.allCaps) {
|
||||
this.properties.push(new Caps());
|
||||
}
|
||||
|
||||
if (options.strike) {
|
||||
this.properties.push(new Strike());
|
||||
}
|
||||
|
||||
if (options.doubleStrike) {
|
||||
this.properties.push(new DoubleStrike());
|
||||
}
|
||||
|
||||
if (options.subScript) {
|
||||
this.properties.push(new SubScript());
|
||||
}
|
||||
|
||||
if (options.superScript) {
|
||||
this.properties.push(new SuperScript());
|
||||
}
|
||||
|
||||
if (options.style) {
|
||||
this.properties.push(new Style(options.style));
|
||||
}
|
||||
|
||||
if (options.font) {
|
||||
if ("name" in options.font) {
|
||||
this.properties.push(new RunFonts(options.font.name, options.font.hint));
|
||||
} else {
|
||||
this.properties.push(new RunFonts(options.font));
|
||||
}
|
||||
}
|
||||
|
||||
if (options.highlight) {
|
||||
this.properties.push(new Highlight(options.highlight));
|
||||
this.properties.push(new HighlightComplexScript(options.highlight));
|
||||
}
|
||||
|
||||
if (options.shading) {
|
||||
this.properties.push(new Shading(options.shading.type, options.shading.fill, options.shading.color));
|
||||
this.properties.push(new ShadowComplexScript(options.shading.type, options.shading.fill, options.shading.color));
|
||||
}
|
||||
|
||||
if (options.children) {
|
||||
for (const child of options.children) {
|
||||
if (typeof child === "string") {
|
||||
|
45
src/file/styles/defaults/document-defaults.spec.ts
Normal file
45
src/file/styles/defaults/document-defaults.spec.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { DocumentDefaults } from "./document-defaults";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
describe("DocumentDefaults", () => {
|
||||
it("#constructor", () => {
|
||||
const defaults = new DocumentDefaults({
|
||||
paragraph: { spacing: { line: 240 } },
|
||||
run: { color: "808080" },
|
||||
});
|
||||
const tree = new Formatter().format(defaults);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:docDefaults": [
|
||||
{
|
||||
"w:rPrDefault": [
|
||||
{
|
||||
"w:rPr": [
|
||||
{
|
||||
"w:color": { _attr: { "w:val": "808080" } },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:pPrDefault": [
|
||||
{
|
||||
"w:pPr": [
|
||||
{
|
||||
"w:spacing": {
|
||||
_attr: {
|
||||
"w:line": 240,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
25
src/file/styles/defaults/document-defaults.ts
Normal file
25
src/file/styles/defaults/document-defaults.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { IParagraphStylePropertiesOptions } from "file/paragraph/properties";
|
||||
import { IRunStylePropertiesOptions } from "file/paragraph/run/properties";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { ParagraphPropertiesDefaults } from "./paragraph-properties";
|
||||
import { RunPropertiesDefaults } from "./run-properties";
|
||||
|
||||
export interface IDocumentDefaultsOptions {
|
||||
readonly paragraph?: IParagraphStylePropertiesOptions;
|
||||
readonly run?: IRunStylePropertiesOptions;
|
||||
}
|
||||
|
||||
export class DocumentDefaults extends XmlComponent {
|
||||
private readonly runPropertiesDefaults: RunPropertiesDefaults;
|
||||
private readonly paragraphPropertiesDefaults: ParagraphPropertiesDefaults;
|
||||
|
||||
constructor(options?: IDocumentDefaultsOptions) {
|
||||
super("w:docDefaults");
|
||||
|
||||
this.runPropertiesDefaults = new RunPropertiesDefaults(options && options.run);
|
||||
this.paragraphPropertiesDefaults = new ParagraphPropertiesDefaults(options && options.paragraph);
|
||||
|
||||
this.root.push(this.runPropertiesDefaults);
|
||||
this.root.push(this.paragraphPropertiesDefaults);
|
||||
}
|
||||
}
|
@ -1,16 +1,3 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { ParagraphPropertiesDefaults } from "./paragraph-properties";
|
||||
import { RunPropertiesDefaults } from "./run-properties";
|
||||
|
||||
export class DocumentDefaults extends XmlComponent {
|
||||
private readonly runPropertiesDefaults: RunPropertiesDefaults;
|
||||
private readonly paragraphPropertiesDefaults: ParagraphPropertiesDefaults;
|
||||
|
||||
constructor() {
|
||||
super("w:docDefaults");
|
||||
this.runPropertiesDefaults = new RunPropertiesDefaults();
|
||||
this.paragraphPropertiesDefaults = new ParagraphPropertiesDefaults();
|
||||
this.root.push(this.runPropertiesDefaults);
|
||||
this.root.push(this.paragraphPropertiesDefaults);
|
||||
}
|
||||
}
|
||||
export * from "./paragraph-properties";
|
||||
export * from "./run-properties";
|
||||
export * from "./document-defaults";
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ParagraphProperties } from "file/paragraph/properties";
|
||||
import { IParagraphStylePropertiesOptions, ParagraphProperties } from "file/paragraph/properties";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
export class ParagraphPropertiesDefaults extends XmlComponent {
|
||||
constructor() {
|
||||
constructor(options?: IParagraphStylePropertiesOptions) {
|
||||
super("w:pPrDefault");
|
||||
this.root.push(new ParagraphProperties({}));
|
||||
this.root.push(new ParagraphProperties(options));
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,12 @@
|
||||
import { Size, SizeComplexScript } from "file/paragraph/run/formatting";
|
||||
import { RunProperties } from "file/paragraph/run/properties";
|
||||
import { IFontAttributesProperties, RunFonts } from "file/paragraph/run/run-fonts";
|
||||
import { IRunStylePropertiesOptions, RunProperties } from "file/paragraph/run/properties";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
export class RunPropertiesDefaults extends XmlComponent {
|
||||
private readonly properties: RunProperties;
|
||||
|
||||
constructor() {
|
||||
constructor(options?: IRunStylePropertiesOptions) {
|
||||
super("w:rPrDefault");
|
||||
this.properties = new RunProperties();
|
||||
this.properties = new RunProperties(options);
|
||||
this.root.push(this.properties);
|
||||
}
|
||||
|
||||
public size(size: number): RunPropertiesDefaults {
|
||||
this.properties.push(new Size(size));
|
||||
this.properties.push(new SizeComplexScript(size));
|
||||
return this;
|
||||
}
|
||||
|
||||
public font(font: string | IFontAttributesProperties): RunPropertiesDefaults {
|
||||
this.properties.push(new RunFonts(font));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export * from "./styles";
|
||||
export * from "./style/character-style";
|
||||
export * from "./style/paragraph-style";
|
||||
export * from "./style-options";
|
||||
export * from "./defaults";
|
||||
|
@ -1,56 +0,0 @@
|
||||
import {
|
||||
AlignmentType,
|
||||
EmphasisMarkType,
|
||||
IFontAttributesProperties,
|
||||
IIndentAttributesProperties,
|
||||
ISpacingProperties,
|
||||
UnderlineType,
|
||||
} from "../paragraph";
|
||||
import { ShadingType } from "../table";
|
||||
|
||||
export interface IRunStyleOptions {
|
||||
readonly size?: number;
|
||||
readonly bold?: boolean;
|
||||
readonly italics?: boolean;
|
||||
readonly smallCaps?: boolean;
|
||||
readonly allCaps?: boolean;
|
||||
readonly strike?: boolean;
|
||||
readonly doubleStrike?: boolean;
|
||||
readonly subScript?: boolean;
|
||||
readonly superScript?: boolean;
|
||||
readonly underline?: {
|
||||
readonly type?: UnderlineType;
|
||||
readonly color?: string;
|
||||
};
|
||||
readonly emphasisMark?: {
|
||||
readonly type?: EmphasisMarkType;
|
||||
};
|
||||
readonly color?: string;
|
||||
readonly font?: string | IFontAttributesProperties;
|
||||
readonly characterSpacing?: number;
|
||||
readonly highlight?: string;
|
||||
readonly shadow?: {
|
||||
readonly type: ShadingType;
|
||||
readonly fill: string;
|
||||
readonly color: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IParagraphStyleOptions2 {
|
||||
readonly alignment?: AlignmentType;
|
||||
readonly thematicBreak?: boolean;
|
||||
readonly contextualSpacing?: boolean;
|
||||
readonly rightTabStop?: number;
|
||||
readonly leftTabStop?: number;
|
||||
readonly indent?: IIndentAttributesProperties;
|
||||
readonly spacing?: ISpacingProperties;
|
||||
readonly keepNext?: boolean;
|
||||
readonly keepLines?: boolean;
|
||||
readonly outlineLevel?: number;
|
||||
}
|
||||
|
||||
// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export const WORKAROUND4 = "";
|
@ -334,19 +334,39 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
|
||||
describe("formatting methods: run properties", () => {
|
||||
it("#size", () => {
|
||||
const sizeTests = [
|
||||
{
|
||||
size: 24,
|
||||
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
|
||||
},
|
||||
{
|
||||
size: 24,
|
||||
sizeComplexScript: true,
|
||||
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
|
||||
},
|
||||
{
|
||||
size: 24,
|
||||
sizeComplexScript: false,
|
||||
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }],
|
||||
},
|
||||
{
|
||||
size: 24,
|
||||
sizeComplexScript: 26,
|
||||
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 26 } } }],
|
||||
},
|
||||
];
|
||||
sizeTests.forEach(({ size, sizeComplexScript, expected }) => {
|
||||
it(`#size ${size} cs ${sizeComplexScript}`, () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
size: 24,
|
||||
},
|
||||
run: { size, sizeComplexScript },
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
|
||||
"w:rPr": expected,
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
@ -361,6 +381,7 @@ describe("CharacterStyle", () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#underline", () => {
|
||||
it("should set underline to 'single' if no arguments are given", () => {
|
||||
@ -577,19 +598,34 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("#bold", () => {
|
||||
const boldTests = [
|
||||
{
|
||||
bold: true,
|
||||
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
bold: true,
|
||||
boldComplexScript: true,
|
||||
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
bold: true,
|
||||
boldComplexScript: false,
|
||||
expected: [{ "w:b": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
];
|
||||
boldTests.forEach(({ bold, boldComplexScript, expected }) => {
|
||||
it(`#bold ${bold} cs ${boldComplexScript}`, () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
bold: true,
|
||||
},
|
||||
run: { bold, boldComplexScript },
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:b": { _attr: { "w:val": true } } }],
|
||||
"w:rPr": expected,
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
@ -604,20 +640,36 @@ describe("CharacterStyle", () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("#italics", () => {
|
||||
const italicsTests = [
|
||||
{
|
||||
italics: true,
|
||||
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
italics: true,
|
||||
italicsComplexScript: true,
|
||||
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
italics: true,
|
||||
italicsComplexScript: false,
|
||||
expected: [{ "w:i": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
];
|
||||
italicsTests.forEach(({ italics, italicsComplexScript, expected }) => {
|
||||
it(`#italics ${italics} cs ${italicsComplexScript}`, () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
italics: true,
|
||||
},
|
||||
run: { italics, italicsComplexScript },
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:i": { _attr: { "w:val": true } } }],
|
||||
"w:rPr": expected,
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
@ -632,6 +684,7 @@ describe("CharacterStyle", () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("#link", () => {
|
||||
const style = new CharacterStyle({ id: "myStyleId", link: "MyLink" });
|
||||
@ -673,19 +726,39 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("#highlight", () => {
|
||||
const highlightTests = [
|
||||
{
|
||||
highlight: "005599",
|
||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
|
||||
},
|
||||
{
|
||||
highlight: "005599",
|
||||
highlightComplexScript: true,
|
||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
|
||||
},
|
||||
{
|
||||
highlight: "005599",
|
||||
highlightComplexScript: false,
|
||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
||||
},
|
||||
{
|
||||
highlight: "005599",
|
||||
highlightComplexScript: "550099",
|
||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "550099" } } }],
|
||||
},
|
||||
];
|
||||
highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => {
|
||||
it(`#highlight ${highlight} cs ${highlightComplexScript}`, () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
highlight: "005599",
|
||||
},
|
||||
run: { highlight, highlightComplexScript },
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
||||
"w:rPr": expected,
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
@ -700,34 +773,81 @@ describe("CharacterStyle", () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("#shadow", () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
const shadingTests = [
|
||||
{
|
||||
shadow: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
expected: [
|
||||
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
shading: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
expected: [
|
||||
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
shading: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
shadingComplexScript: true,
|
||||
expected: [
|
||||
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
shading: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
shadingComplexScript: false,
|
||||
expected: [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
|
||||
},
|
||||
{
|
||||
shading: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
shadingComplexScript: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "00FF00",
|
||||
},
|
||||
expected: [
|
||||
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "00FF00" } } },
|
||||
],
|
||||
},
|
||||
];
|
||||
shadingTests.forEach(({ shadow, shading, shadingComplexScript, expected }) => {
|
||||
it("#shadow correctly", () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: { shadow, shading, shadingComplexScript },
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [
|
||||
{
|
||||
"w:shd": {
|
||||
_attr: {
|
||||
"w:val": "pct10",
|
||||
"w:fill": "00FFFF",
|
||||
"w:color": "FF0000",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
"w:rPr": expected,
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
@ -743,4 +863,5 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,8 +1,4 @@
|
||||
import { EmphasisMarkType } from "file/paragraph/run/emphasis-mark";
|
||||
import * as formatting from "file/paragraph/run/formatting";
|
||||
import { RunProperties } from "file/paragraph/run/properties";
|
||||
import { IFontAttributesProperties } from "file/paragraph/run/run-fonts";
|
||||
import { UnderlineType } from "file/paragraph/run/underline";
|
||||
import { IRunStylePropertiesOptions, RunProperties } from "file/paragraph/run/properties";
|
||||
|
||||
import { BasedOn, Link, SemiHidden, UiPriority, UnhideWhenUsed } from "./components";
|
||||
import { Style } from "./style";
|
||||
@ -11,33 +7,7 @@ export interface IBaseCharacterStyleOptions {
|
||||
readonly basedOn?: string;
|
||||
readonly link?: string;
|
||||
readonly semiHidden?: boolean;
|
||||
readonly run?: {
|
||||
readonly size?: number;
|
||||
readonly bold?: boolean;
|
||||
readonly italics?: boolean;
|
||||
readonly smallCaps?: boolean;
|
||||
readonly allCaps?: boolean;
|
||||
readonly strike?: boolean;
|
||||
readonly doubleStrike?: boolean;
|
||||
readonly subScript?: boolean;
|
||||
readonly superScript?: boolean;
|
||||
readonly underline?: {
|
||||
readonly type?: UnderlineType;
|
||||
readonly color?: string;
|
||||
};
|
||||
readonly emphasisMark?: {
|
||||
readonly type?: EmphasisMarkType;
|
||||
};
|
||||
readonly color?: string;
|
||||
readonly font?: string | IFontAttributesProperties;
|
||||
readonly characterSpacing?: number;
|
||||
readonly highlight?: string;
|
||||
readonly shadow?: {
|
||||
readonly type: string;
|
||||
readonly fill: string;
|
||||
readonly color: string;
|
||||
};
|
||||
};
|
||||
readonly run?: IRunStylePropertiesOptions;
|
||||
}
|
||||
|
||||
export interface ICharacterStyleOptions extends IBaseCharacterStyleOptions {
|
||||
@ -50,7 +20,9 @@ export class CharacterStyle extends Style {
|
||||
|
||||
constructor(options: ICharacterStyleOptions) {
|
||||
super({ type: "character", styleId: options.id }, options.name);
|
||||
this.runProperties = new RunProperties();
|
||||
|
||||
this.runProperties = new RunProperties(options.run);
|
||||
|
||||
this.root.push(this.runProperties);
|
||||
this.root.push(new UiPriority(99));
|
||||
this.root.push(new UnhideWhenUsed());
|
||||
@ -66,72 +38,5 @@ export class CharacterStyle extends Style {
|
||||
if (options.semiHidden) {
|
||||
this.root.push(new SemiHidden());
|
||||
}
|
||||
|
||||
if (options.run) {
|
||||
if (options.run.size) {
|
||||
this.runProperties.push(new formatting.Size(options.run.size));
|
||||
this.runProperties.push(new formatting.SizeComplexScript(options.run.size));
|
||||
}
|
||||
|
||||
if (options.run.bold) {
|
||||
this.runProperties.push(new formatting.Bold());
|
||||
}
|
||||
|
||||
if (options.run.italics) {
|
||||
this.runProperties.push(new formatting.Italics());
|
||||
}
|
||||
|
||||
if (options.run.smallCaps) {
|
||||
this.runProperties.push(new formatting.SmallCaps());
|
||||
}
|
||||
|
||||
if (options.run.allCaps) {
|
||||
this.runProperties.push(new formatting.Caps());
|
||||
}
|
||||
|
||||
if (options.run.strike) {
|
||||
this.runProperties.push(new formatting.Strike());
|
||||
}
|
||||
|
||||
if (options.run.doubleStrike) {
|
||||
this.runProperties.push(new formatting.DoubleStrike());
|
||||
}
|
||||
|
||||
if (options.run.subScript) {
|
||||
this.runProperties.push(new formatting.SubScript());
|
||||
}
|
||||
|
||||
if (options.run.superScript) {
|
||||
this.runProperties.push(new formatting.SuperScript());
|
||||
}
|
||||
|
||||
if (options.run.underline) {
|
||||
this.runProperties.push(new formatting.Underline(options.run.underline.type, options.run.underline.color));
|
||||
}
|
||||
|
||||
if (options.run.emphasisMark) {
|
||||
this.runProperties.push(new formatting.EmphasisMark(options.run.emphasisMark.type));
|
||||
}
|
||||
|
||||
if (options.run.color) {
|
||||
this.runProperties.push(new formatting.Color(options.run.color));
|
||||
}
|
||||
|
||||
if (options.run.font) {
|
||||
this.runProperties.push(new formatting.RunFonts(options.run.font));
|
||||
}
|
||||
|
||||
if (options.run.characterSpacing) {
|
||||
this.runProperties.push(new formatting.CharacterSpacing(options.run.characterSpacing));
|
||||
}
|
||||
|
||||
if (options.run.highlight) {
|
||||
this.runProperties.push(new formatting.Highlight(options.run.highlight));
|
||||
}
|
||||
|
||||
if (options.run.shadow) {
|
||||
this.runProperties.push(new formatting.Shading(options.run.shadow.type, options.run.shadow.fill, options.run.shadow.color));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -358,21 +358,37 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
describe("formatting methods: run properties", () => {
|
||||
it("#size", () => {
|
||||
const sizeTests = [
|
||||
{
|
||||
size: 24,
|
||||
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
|
||||
},
|
||||
{
|
||||
size: 24,
|
||||
sizeComplexScript: true,
|
||||
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
|
||||
},
|
||||
{
|
||||
size: 24,
|
||||
sizeComplexScript: false,
|
||||
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }],
|
||||
},
|
||||
{
|
||||
size: 24,
|
||||
sizeComplexScript: 26,
|
||||
expected: [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 26 } } }],
|
||||
},
|
||||
];
|
||||
sizeTests.forEach(({ size, sizeComplexScript, expected }) => {
|
||||
it(`#size ${size} cs ${sizeComplexScript}`, () => {
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
size: 24,
|
||||
},
|
||||
run: { size, sizeComplexScript },
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }, { "w:szCs": { _attr: { "w:val": 24 } } }],
|
||||
},
|
||||
],
|
||||
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:rPr": expected }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -543,89 +559,169 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("#bold", () => {
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
const boldTests = [
|
||||
{
|
||||
bold: true,
|
||||
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
bold: true,
|
||||
boldComplexScript: true,
|
||||
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
bold: true,
|
||||
boldComplexScript: false,
|
||||
expected: [{ "w:b": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
];
|
||||
boldTests.forEach(({ bold, boldComplexScript, expected }) => {
|
||||
it(`#bold ${bold} cs ${boldComplexScript}`, () => {
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: { bold, boldComplexScript },
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:b": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
],
|
||||
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:rPr": expected }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("#italics", () => {
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
const italicsTests = [
|
||||
{
|
||||
italics: true,
|
||||
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
italics: true,
|
||||
italicsComplexScript: true,
|
||||
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
italics: true,
|
||||
italicsComplexScript: false,
|
||||
expected: [{ "w:i": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
];
|
||||
italicsTests.forEach(({ italics, italicsComplexScript, expected }) => {
|
||||
it(`#italics ${italics} cs ${italicsComplexScript}`, () => {
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: { italics, italicsComplexScript },
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:i": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
],
|
||||
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:rPr": expected }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("#highlight", () => {
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
const highlightTests = [
|
||||
{
|
||||
highlight: "005599",
|
||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
|
||||
},
|
||||
{
|
||||
highlight: "005599",
|
||||
highlightComplexScript: true,
|
||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
|
||||
},
|
||||
{
|
||||
highlight: "005599",
|
||||
highlightComplexScript: false,
|
||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
||||
},
|
||||
{
|
||||
highlight: "005599",
|
||||
highlightComplexScript: "550099",
|
||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "550099" } } }],
|
||||
},
|
||||
];
|
||||
highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => {
|
||||
it(`#highlight ${highlight} cs ${highlightComplexScript}`, () => {
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: { highlight, highlightComplexScript },
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
||||
},
|
||||
],
|
||||
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:rPr": expected }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("#shadow", () => {
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
const shadingTests = [
|
||||
{
|
||||
shadow: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
expected: [
|
||||
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
shading: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
expected: [
|
||||
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
shading: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
shadingComplexScript: true,
|
||||
expected: [
|
||||
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
shading: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
shadingComplexScript: false,
|
||||
expected: [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
|
||||
},
|
||||
{
|
||||
shading: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
shadingComplexScript: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "00FF00",
|
||||
},
|
||||
expected: [
|
||||
{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } },
|
||||
{ "w:shdCs": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "00FF00" } } },
|
||||
],
|
||||
},
|
||||
];
|
||||
shadingTests.forEach(({ shadow, shading, shadingComplexScript, expected }) => {
|
||||
it("#shadow correctly", () => {
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: { shadow, shading, shadingComplexScript },
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [
|
||||
{
|
||||
"w:shd": {
|
||||
_attr: {
|
||||
"w:val": "pct10",
|
||||
"w:fill": "00FFFF",
|
||||
"w:color": "FF0000",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:rPr": expected }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,19 +1,6 @@
|
||||
import {
|
||||
Alignment,
|
||||
ContextualSpacing,
|
||||
Indent,
|
||||
KeepLines,
|
||||
KeepNext,
|
||||
OutlineLevel,
|
||||
ParagraphProperties,
|
||||
Spacing,
|
||||
ThematicBreak,
|
||||
} from "file/paragraph";
|
||||
import { TabStop, TabStopType } from "file/paragraph/formatting";
|
||||
import * as formatting from "file/paragraph/run/formatting";
|
||||
import { IParagraphStylePropertiesOptions, IRunStylePropertiesOptions, ParagraphProperties } from "file/paragraph";
|
||||
import { RunProperties } from "file/paragraph/run/properties";
|
||||
|
||||
import { IParagraphStyleOptions2, IRunStyleOptions } from "../style-options";
|
||||
import { BasedOn, Link, Next, QuickFormat, SemiHidden, UiPriority, UnhideWhenUsed } from "./components";
|
||||
import { Style } from "./style";
|
||||
|
||||
@ -25,22 +12,25 @@ export interface IBaseParagraphStyleOptions {
|
||||
readonly semiHidden?: boolean;
|
||||
readonly uiPriority?: number;
|
||||
readonly unhideWhenUsed?: boolean;
|
||||
readonly run?: IRunStyleOptions;
|
||||
readonly paragraph?: IParagraphStyleOptions2;
|
||||
readonly paragraph?: IParagraphStylePropertiesOptions;
|
||||
readonly run?: IRunStylePropertiesOptions;
|
||||
}
|
||||
|
||||
export interface IParagraphStyleOptions extends IBaseParagraphStyleOptions {
|
||||
readonly id: string;
|
||||
readonly name?: string;
|
||||
}
|
||||
|
||||
export class ParagraphStyle extends Style {
|
||||
private readonly paragraphProperties: ParagraphProperties;
|
||||
private readonly runProperties: RunProperties;
|
||||
|
||||
constructor(options: IParagraphStyleOptions) {
|
||||
super({ type: "paragraph", styleId: options.id }, options.name);
|
||||
this.paragraphProperties = new ParagraphProperties({});
|
||||
this.runProperties = new RunProperties();
|
||||
|
||||
this.paragraphProperties = new ParagraphProperties(options.paragraph);
|
||||
this.runProperties = new RunProperties(options.run);
|
||||
|
||||
this.root.push(this.paragraphProperties);
|
||||
this.root.push(this.runProperties);
|
||||
|
||||
@ -71,114 +61,5 @@ export class ParagraphStyle extends Style {
|
||||
if (options.unhideWhenUsed) {
|
||||
this.root.push(new UnhideWhenUsed());
|
||||
}
|
||||
|
||||
if (options.run) {
|
||||
if (options.run.size) {
|
||||
this.runProperties.push(new formatting.Size(options.run.size));
|
||||
this.runProperties.push(new formatting.SizeComplexScript(options.run.size));
|
||||
}
|
||||
|
||||
if (options.run.bold) {
|
||||
this.runProperties.push(new formatting.Bold());
|
||||
}
|
||||
|
||||
if (options.run.italics) {
|
||||
this.runProperties.push(new formatting.Italics());
|
||||
}
|
||||
|
||||
if (options.run.smallCaps) {
|
||||
this.runProperties.push(new formatting.SmallCaps());
|
||||
}
|
||||
|
||||
if (options.run.allCaps) {
|
||||
this.runProperties.push(new formatting.Caps());
|
||||
}
|
||||
|
||||
if (options.run.strike) {
|
||||
this.runProperties.push(new formatting.Strike());
|
||||
}
|
||||
|
||||
if (options.run.doubleStrike) {
|
||||
this.runProperties.push(new formatting.DoubleStrike());
|
||||
}
|
||||
|
||||
if (options.run.subScript) {
|
||||
this.runProperties.push(new formatting.SubScript());
|
||||
}
|
||||
|
||||
if (options.run.superScript) {
|
||||
this.runProperties.push(new formatting.SuperScript());
|
||||
}
|
||||
|
||||
if (options.run.underline) {
|
||||
this.runProperties.push(new formatting.Underline(options.run.underline.type, options.run.underline.color));
|
||||
}
|
||||
|
||||
if (options.run.emphasisMark) {
|
||||
this.runProperties.push(new formatting.EmphasisMark(options.run.emphasisMark.type));
|
||||
}
|
||||
|
||||
if (options.run.color) {
|
||||
this.runProperties.push(new formatting.Color(options.run.color));
|
||||
}
|
||||
|
||||
if (options.run.font) {
|
||||
this.runProperties.push(new formatting.RunFonts(options.run.font));
|
||||
}
|
||||
|
||||
if (options.run.characterSpacing) {
|
||||
this.runProperties.push(new formatting.CharacterSpacing(options.run.characterSpacing));
|
||||
}
|
||||
|
||||
if (options.run.highlight) {
|
||||
this.runProperties.push(new formatting.Highlight(options.run.highlight));
|
||||
}
|
||||
|
||||
if (options.run.shadow) {
|
||||
this.runProperties.push(new formatting.Shading(options.run.shadow.type, options.run.shadow.fill, options.run.shadow.color));
|
||||
}
|
||||
}
|
||||
|
||||
if (options.paragraph) {
|
||||
if (options.paragraph.alignment) {
|
||||
this.paragraphProperties.push(new Alignment(options.paragraph.alignment));
|
||||
}
|
||||
|
||||
if (options.paragraph.thematicBreak) {
|
||||
this.paragraphProperties.push(new ThematicBreak());
|
||||
}
|
||||
|
||||
if (options.paragraph.contextualSpacing) {
|
||||
this.paragraphProperties.push(new ContextualSpacing(options.paragraph.contextualSpacing));
|
||||
}
|
||||
|
||||
if (options.paragraph.rightTabStop) {
|
||||
this.paragraphProperties.push(new TabStop(TabStopType.RIGHT, options.paragraph.rightTabStop));
|
||||
}
|
||||
|
||||
if (options.paragraph.leftTabStop) {
|
||||
this.paragraphProperties.push(new TabStop(TabStopType.LEFT, options.paragraph.leftTabStop));
|
||||
}
|
||||
|
||||
if (options.paragraph.indent) {
|
||||
this.paragraphProperties.push(new Indent(options.paragraph.indent));
|
||||
}
|
||||
|
||||
if (options.paragraph.spacing) {
|
||||
this.paragraphProperties.push(new Spacing(options.paragraph.spacing));
|
||||
}
|
||||
|
||||
if (options.paragraph.keepNext) {
|
||||
this.paragraphProperties.push(new KeepNext());
|
||||
}
|
||||
|
||||
if (options.paragraph.keepLines) {
|
||||
this.paragraphProperties.push(new KeepLines());
|
||||
}
|
||||
|
||||
if (options.paragraph.outlineLevel) {
|
||||
this.paragraphProperties.push(new OutlineLevel(options.paragraph.outlineLevel));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,9 @@ export class TableCell extends XmlComponent {
|
||||
|
||||
if (options.verticalMerge) {
|
||||
this.properties.addVerticalMerge(options.verticalMerge);
|
||||
} else if (options.rowSpan && options.rowSpan > 1) {
|
||||
// if cell already have a `verticalMerge`, don't handle `rowSpan`
|
||||
this.properties.addVerticalMerge(VerticalMergeType.RESTART);
|
||||
}
|
||||
|
||||
if (options.margins) {
|
||||
@ -84,10 +87,6 @@ export class TableCell extends XmlComponent {
|
||||
this.properties.addGridSpan(options.columnSpan);
|
||||
}
|
||||
|
||||
if (options.rowSpan && options.rowSpan > 1) {
|
||||
this.properties.addVerticalMerge(VerticalMergeType.RESTART);
|
||||
}
|
||||
|
||||
if (options.width) {
|
||||
this.properties.setWidth(options.width.size, options.width.type);
|
||||
}
|
||||
|
@ -182,4 +182,97 @@ describe("TableRow", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#rootIndexToColumnIndex", () => {
|
||||
it("should get the correct virtual column index by root index", () => {
|
||||
const tableRow = new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
columnSpan: 3,
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
columnSpan: 3,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
expect(tableRow.rootIndexToColumnIndex(1)).to.equal(0);
|
||||
expect(tableRow.rootIndexToColumnIndex(2)).to.equal(3);
|
||||
expect(tableRow.rootIndexToColumnIndex(3)).to.equal(4);
|
||||
expect(tableRow.rootIndexToColumnIndex(4)).to.equal(5);
|
||||
|
||||
expect(() => tableRow.rootIndexToColumnIndex(0)).to.throw(`cell 'rootIndex' should between 1 to 4`);
|
||||
expect(() => tableRow.rootIndexToColumnIndex(5)).to.throw(`cell 'rootIndex' should between 1 to 4`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#columnIndexToRootIndex", () => {
|
||||
it("should get the correct root index by virtual column index", () => {
|
||||
const tableRow = new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
columnSpan: 3,
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
columnSpan: 3,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
expect(tableRow.columnIndexToRootIndex(0)).to.equal(1);
|
||||
expect(tableRow.columnIndexToRootIndex(1)).to.equal(1);
|
||||
expect(tableRow.columnIndexToRootIndex(2)).to.equal(1);
|
||||
|
||||
expect(tableRow.columnIndexToRootIndex(3)).to.equal(2);
|
||||
expect(tableRow.columnIndexToRootIndex(4)).to.equal(3);
|
||||
|
||||
expect(tableRow.columnIndexToRootIndex(5)).to.equal(4);
|
||||
expect(tableRow.columnIndexToRootIndex(6)).to.equal(4);
|
||||
expect(tableRow.columnIndexToRootIndex(7)).to.equal(4);
|
||||
|
||||
expect(() => tableRow.columnIndexToRootIndex(-1)).to.throw(`cell 'columnIndex' should not less than zero`);
|
||||
expect(() => tableRow.columnIndexToRootIndex(8)).to.throw(`cell 'columnIndex' should not great than 7`);
|
||||
});
|
||||
|
||||
it("should allow end new cell index", () => {
|
||||
const tableRow = new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
columnSpan: 3,
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
columnSpan: 3,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
expect(tableRow.columnIndexToRootIndex(8, true)).to.equal(5);
|
||||
// for column 10, just place the new cell at the end of row
|
||||
expect(tableRow.columnIndexToRootIndex(10, true)).to.equal(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -46,8 +46,56 @@ export class TableRow extends XmlComponent {
|
||||
return this.options.children;
|
||||
}
|
||||
|
||||
public get cells(): TableCell[] {
|
||||
return this.root.filter((xmlComponent) => xmlComponent instanceof TableCell);
|
||||
}
|
||||
|
||||
public addCellToIndex(cell: TableCell, index: number): void {
|
||||
// Offset because properties is also in root.
|
||||
this.root.splice(index + 1, 0, cell);
|
||||
}
|
||||
|
||||
public addCellToColumnIndex(cell: TableCell, columnIndex: number): void {
|
||||
const rootIndex = this.columnIndexToRootIndex(columnIndex, true);
|
||||
this.addCellToIndex(cell, rootIndex - 1);
|
||||
}
|
||||
|
||||
public rootIndexToColumnIndex(rootIndex: number): number {
|
||||
// convert the root index to the virtual column index
|
||||
if (rootIndex < 1 || rootIndex >= this.root.length) {
|
||||
throw new Error(`cell 'rootIndex' should between 1 to ${this.root.length - 1}`);
|
||||
}
|
||||
let colIdx = 0;
|
||||
// Offset because properties is also in root.
|
||||
for (let rootIdx = 1; rootIdx < rootIndex; rootIdx++) {
|
||||
const cell = this.root[rootIdx] as TableCell;
|
||||
colIdx += cell.options.columnSpan || 1;
|
||||
}
|
||||
return colIdx;
|
||||
}
|
||||
|
||||
public columnIndexToRootIndex(columnIndex: number, allowEndNewCell: boolean = false): number {
|
||||
// convert the virtual column index to the root index
|
||||
// `allowEndNewCell` for get index to inert new cell
|
||||
if (columnIndex < 0) {
|
||||
throw new Error(`cell 'columnIndex' should not less than zero`);
|
||||
}
|
||||
let colIdx = 0;
|
||||
// Offset because properties is also in root.
|
||||
let rootIdx = 1;
|
||||
while (colIdx <= columnIndex) {
|
||||
if (rootIdx >= this.root.length) {
|
||||
if (allowEndNewCell) {
|
||||
// for inserting verticalMerge CONTINUE cell at end of row
|
||||
return this.root.length;
|
||||
} else {
|
||||
throw new Error(`cell 'columnIndex' should not great than ${colIdx - 1}`);
|
||||
}
|
||||
}
|
||||
const cell = this.root[rootIdx] as TableCell;
|
||||
rootIdx += 1;
|
||||
colIdx += (cell && cell.options.columnSpan) || 1;
|
||||
}
|
||||
return rootIdx - 1;
|
||||
}
|
||||
}
|
||||
|
@ -188,6 +188,72 @@ describe("Table", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a table with the correct columnSpan and rowSpan", () => {
|
||||
const table = new Table({
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
columnSpan: 2,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
rowSpan: 2,
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(table);
|
||||
const cellP = { "w:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "hello"] }] }] };
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tbl": [
|
||||
{ "w:tblPr": [DEFAULT_TABLE_PROPERTIES, BORDERS, WIDTHS] },
|
||||
{
|
||||
"w:tblGrid": [{ "w:gridCol": { _attr: { "w:w": 100 } } }, { "w:gridCol": { _attr: { "w:w": 100 } } }],
|
||||
},
|
||||
{
|
||||
"w:tr": [
|
||||
{
|
||||
"w:tc": [{ "w:tcPr": [{ "w:gridSpan": { _attr: { "w:val": 2 } } }] }, cellP],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:tr": [
|
||||
{
|
||||
"w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "restart" } } }] }, cellP],
|
||||
},
|
||||
{ "w:tc": [cellP] },
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:tr": [
|
||||
{
|
||||
"w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }, { "w:p": {} }],
|
||||
},
|
||||
{ "w:tc": [cellP] },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("sets the table to fixed width layout", () => {
|
||||
const table = new Table({
|
||||
rows: [
|
||||
|
@ -78,27 +78,29 @@ export class Table extends XmlComponent {
|
||||
this.root.push(row);
|
||||
}
|
||||
|
||||
for (const row of rows) {
|
||||
row.Children.forEach((cell, cellIndex) => {
|
||||
const column = rows.map((r) => r.Children[cellIndex]);
|
||||
rows.forEach((row, rowIndex) => {
|
||||
if (rowIndex === rows.length - 1) {
|
||||
// don't process the end row
|
||||
return;
|
||||
}
|
||||
let columnIndex = 0;
|
||||
row.cells.forEach((cell) => {
|
||||
// Row Span has to be added in this method and not the constructor because it needs to know information about the column which happens after Table Cell construction
|
||||
// Row Span of 1 will crash word as it will add RESTART and not a corresponding CONTINUE
|
||||
if (cell.options.rowSpan && cell.options.rowSpan > 1) {
|
||||
const thisCellsColumnIndex = column.indexOf(cell);
|
||||
const endColumnIndex = thisCellsColumnIndex + (cell.options.rowSpan - 1);
|
||||
|
||||
for (let i = thisCellsColumnIndex + 1; i <= endColumnIndex; i++) {
|
||||
rows[i].addCellToIndex(
|
||||
new TableCell({
|
||||
const continueCell = new TableCell({
|
||||
// the inserted CONTINUE cell has rowSpan, and will be handled when process the next row
|
||||
rowSpan: cell.options.rowSpan - 1,
|
||||
columnSpan: cell.options.columnSpan,
|
||||
borders: cell.options.borders,
|
||||
children: [],
|
||||
verticalMerge: VerticalMergeType.CONTINUE,
|
||||
}),
|
||||
i,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
rows[rowIndex + 1].addCellToColumnIndex(continueCell, columnIndex);
|
||||
}
|
||||
columnIndex += cell.options.columnSpan || 1;
|
||||
});
|
||||
});
|
||||
|
||||
if (float) {
|
||||
this.properties.setTableFloatProperties(float);
|
||||
|
Reference in New Issue
Block a user