Compare commits
100 Commits
Author | SHA1 | Date | |
---|---|---|---|
e5bfa99b92 | |||
f640f17fa6 | |||
494a32d45d | |||
8040a455be | |||
f091cff7c9 | |||
401ef7336b | |||
18134519be | |||
7980f14efb | |||
40d6a41305 | |||
9cfd835171 | |||
95a9f592eb | |||
61411fd0f3 | |||
a84eb16392 | |||
3355a6f472 | |||
071a8ea9f7 | |||
ea3777d28b | |||
3346b97ee7 | |||
2b834a75a8 | |||
21df53d547 | |||
9143c1c2c1 | |||
83cab7563d | |||
b05fbe7f6e | |||
e6a57738f4 | |||
cc6e35165a | |||
7791ddf76e | |||
4742cf0f3f | |||
8858970491 | |||
f7d18bfead | |||
0ce1b7fa15 | |||
4633592711 | |||
54697ab6b1 | |||
1eed844b9a | |||
dd89fe2463 | |||
9c66db97ff | |||
fea6afdfe4 | |||
6ec2e742ef | |||
3f80b054fc | |||
96d81873d8 | |||
c429ae9920 | |||
6b4e769f48 | |||
b67a9de0e9 | |||
ac9f65a068 | |||
7e8ebb2af2 | |||
f53fe2f881 | |||
6fdd88527a | |||
b6f431e14d | |||
23dee01f06 | |||
5532f91423 | |||
c849d5f3e5 | |||
3f3fd05cb1 | |||
a5bedf9a5b | |||
9d9dd62f00 | |||
97f76fb62c | |||
3508fd97ec | |||
90f6f68693 | |||
733775d3b9 | |||
f3aa6a9203 | |||
ffdcc7baca | |||
a1e20f4c9a | |||
048ae6a58c | |||
fafa54e4c9 | |||
60dbb32e9e | |||
52b07fd9cb | |||
8ec5bc05e0 | |||
6e0c12afb3 | |||
466e880bfc | |||
0a8feca6ab | |||
f2b50478bf | |||
fc71ebdfef | |||
10114bb12d | |||
e6d4741955 | |||
4d7387524c | |||
5fe4405d76 | |||
79dffc873a | |||
26ee12759c | |||
2e00634bc4 | |||
d63e6bf6b1 | |||
a95366e54e | |||
1c376abeb6 | |||
353d888abd | |||
b6bd532295 | |||
c5b004166d | |||
2f43600daf | |||
b39c7ce323 | |||
5b00279996 | |||
d1d1e01aff | |||
980bc597e2 | |||
a05c5edd49 | |||
985452f5f4 | |||
482674b3b3 | |||
fcbfed9068 | |||
385ad92331 | |||
571f8b526b | |||
7688aa99f6 | |||
4994bca34c | |||
1a3603dbfb | |||
e382dbff84 | |||
e08be3d7a4 | |||
03c4190c2c | |||
010fde6258 |
7
.gitignore
vendored
7
.gitignore
vendored
@ -58,5 +58,8 @@ yarn.lock
|
||||
# Documents
|
||||
My Document.docx
|
||||
|
||||
# Temporary folder
|
||||
tmp
|
||||
# Temporary folder
|
||||
tmp
|
||||
|
||||
# nyc
|
||||
.nyc_output
|
||||
|
25
.nycrc
Normal file
25
.nycrc
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"check-coverage": true,
|
||||
"lines": 87.54,
|
||||
"functions": 83.61,
|
||||
"branches": 72.57,
|
||||
"statements": 87.32,
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts"
|
||||
],
|
||||
"reporter": [
|
||||
"lcov",
|
||||
"text",
|
||||
"json"
|
||||
],
|
||||
"extension": [
|
||||
".ts"
|
||||
],
|
||||
"cache": true,
|
||||
"all": true,
|
||||
"instrument": false,
|
||||
"sourceMap": true
|
||||
}
|
14
.travis.yml
14
.travis.yml
@ -3,9 +3,10 @@ node_js:
|
||||
- 9
|
||||
install:
|
||||
- npm install
|
||||
- npm install -g codecov
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
- npm run test.coverage
|
||||
- npm run style
|
||||
- npm run build
|
||||
- npm run ts-node -- ./demo/demo1.ts
|
||||
@ -32,12 +33,23 @@ script:
|
||||
- npm run ts-node -- ./demo/demo22.ts
|
||||
- npm run ts-node -- ./demo/demo23.ts
|
||||
- npm run ts-node -- ./demo/demo24.ts
|
||||
# - npm run ts-node -- ./demo/demo25.ts
|
||||
- npm run ts-node -- ./demo/demo26.ts
|
||||
- npm run ts-node -- ./demo/demo27.ts
|
||||
- npm run ts-node -- ./demo/demo28.ts
|
||||
- npm run ts-node -- ./demo/demo29.ts
|
||||
- npm run ts-node -- ./demo/demo30.ts
|
||||
- npm run ts-node -- ./demo/demo31.ts
|
||||
- npm run ts-node -- ./demo/demo32.ts
|
||||
- npm run ts-node -- ./demo/demo33.ts
|
||||
- npm run ts-node -- ./demo/demo34.ts
|
||||
after_failure:
|
||||
- "cat /home/travis/builds/dolanmiu/docx/npm-debug.log"
|
||||
after_success:
|
||||
- npm run typedoc
|
||||
- echo "docx.js.org" > docs/.nojekyll
|
||||
- echo "docx.js.org" > docs/CNAME
|
||||
- codecov
|
||||
deploy:
|
||||
provider: pages
|
||||
skip-cleanup: true
|
||||
|
24
.vscode/tasks.json
vendored
Normal file
24
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "typescript",
|
||||
"tsconfig": "tsconfig.json",
|
||||
"option": "watch",
|
||||
"problemMatcher": [
|
||||
"$tsc-watch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "ts-node",
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
[![Known Vulnerabilities][snky-image]][snky-url]
|
||||
[![Chat on Gitter][gitter-image]][gitter-url]
|
||||
[![PRs Welcome][pr-image]][pr-url]
|
||||
[![codecov][codecov-image]][codecov-url]
|
||||
|
||||
<p align="center">
|
||||
<img src="https://i.imgur.com/H5FA1Qy.gif" alt="drawing" width="800"/>
|
||||
@ -29,6 +30,10 @@ Here are examples of `docx` being used with basic `HTML/JS` in a browser environ
|
||||
* https://codepen.io/anon/pen/dqoVgQ
|
||||
* https://jsfiddle.net/3xhezb5w/2
|
||||
|
||||
Here is an example of `docx` working in `Angular`:
|
||||
|
||||
* https://stackblitz.com/edit/angular-afvxtz
|
||||
|
||||
## Node
|
||||
|
||||
Press `endpoint` on the `RunKit` website:
|
||||
@ -77,3 +82,5 @@ Made with 💖
|
||||
[gitter-url]: https://gitter.im/docx-lib/Lobby
|
||||
[pr-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
|
||||
[pr-url]: http://makeapullrequest.com
|
||||
[codecov-image]: https://codecov.io/gh/dolanmiu/docx/branch/master/graph/badge.svg
|
||||
[codecov-url]: https://codecov.io/gh/dolanmiu/docx
|
||||
|
@ -15,8 +15,12 @@ const footer = doc.createFooter();
|
||||
footer.createParagraph("Footer on another page");
|
||||
|
||||
doc.addSection({
|
||||
headerId: header.Header.ReferenceId,
|
||||
footerId: footer.Footer.ReferenceId,
|
||||
headers: {
|
||||
default: header,
|
||||
},
|
||||
footers: {
|
||||
default: footer,
|
||||
},
|
||||
pageNumberStart: 1,
|
||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||
});
|
||||
@ -24,8 +28,12 @@ doc.addSection({
|
||||
doc.createParagraph("hello");
|
||||
|
||||
doc.addSection({
|
||||
headerId: header.Header.ReferenceId,
|
||||
footerId: footer.Footer.ReferenceId,
|
||||
headers: {
|
||||
default: header,
|
||||
},
|
||||
footers: {
|
||||
default: footer,
|
||||
},
|
||||
pageNumberStart: 1,
|
||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||
orientation: PageOrientation.LANDSCAPE,
|
||||
|
@ -8,11 +8,11 @@ const doc = new Document();
|
||||
const paragraph = new Paragraph("Hello World");
|
||||
doc.addParagraph(paragraph);
|
||||
|
||||
const image = Media.addImage(doc, "./demo/images/image1.jpeg");
|
||||
const image2 = Media.addImage(doc, "./demo/images/dog.png");
|
||||
const image3 = Media.addImage(doc, "./demo/images/cat.jpg");
|
||||
const image4 = Media.addImage(doc, "./demo/images/parrots.bmp");
|
||||
const image5 = Media.addImage(doc, "./demo/images/pizza.gif");
|
||||
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||
const image2 = Media.addImage(doc, fs.readFileSync("./demo/images/dog.png"));
|
||||
const image3 = Media.addImage(doc, fs.readFileSync("./demo/images/cat.jpg"));
|
||||
const image4 = Media.addImage(doc, fs.readFileSync("./demo/images/parrots.bmp"));
|
||||
const image5 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));
|
||||
|
||||
const imageBase64Data = `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`;
|
||||
const image6 = Media.addImage(doc, Buffer.from(imageBase64Data, "base64"), 100, 100);
|
||||
|
@ -8,7 +8,7 @@ const doc = new Document();
|
||||
const table = doc.createTable(4, 4);
|
||||
table.getCell(2, 2).addContent(new Paragraph("Hello"));
|
||||
|
||||
const image = Media.addImage(doc, "./demo/images/image1.jpeg");
|
||||
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||
table.getCell(1, 1).addContent(image.Paragraph);
|
||||
|
||||
const packer = new Packer();
|
||||
|
@ -30,5 +30,4 @@ const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
console.log("Document created successfully at project root!");
|
||||
});
|
||||
|
29
demo/demo30.ts
Normal file
29
demo/demo30.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import * as fs from "fs";
|
||||
import { Document, ImportDotx, Packer, Paragraph } from "../build";
|
||||
|
||||
const importDotx = new ImportDotx();
|
||||
const filePath = "./demo/dotx/template.dotx";
|
||||
|
||||
fs.readFile(filePath, (err, data) => {
|
||||
if (err) {
|
||||
throw new Error(`Failed to read file ${filePath}.`);
|
||||
}
|
||||
|
||||
importDotx.extract(data).then((templateDocument) => {
|
||||
// This any needs fixing
|
||||
const sectionProps = {
|
||||
titlePage: templateDocument.titlePageIsDefined,
|
||||
} as any;
|
||||
|
||||
const doc = new Document(undefined, sectionProps, {
|
||||
template: templateDocument,
|
||||
});
|
||||
const paragraph = new Paragraph("Hello World");
|
||||
doc.addParagraph(paragraph);
|
||||
|
||||
const packer = new Packer();
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
||||
});
|
||||
});
|
22
demo/demo33.ts
Normal file
22
demo/demo33.ts
Normal file
@ -0,0 +1,22 @@
|
||||
// Sequential Captions
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph, TextRun } from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const paragraph = new Paragraph("Hello World 1->").addSequentialIdentifier("Caption").addRun(new TextRun(" text after sequencial caption 2->")).addSequentialIdentifier("Caption");
|
||||
const paragraph2 = new Paragraph("Hello World 1->").addSequentialIdentifier("Label").addRun(new TextRun(" text after sequencial caption 2->")).addSequentialIdentifier("Label");
|
||||
const paragraph3 = new Paragraph("Hello World 1->").addSequentialIdentifier("Another").addRun(new TextRun(" text after sequencial caption 3->")).addSequentialIdentifier("Label");
|
||||
const paragraph4 = new Paragraph("Hello World 2->").addSequentialIdentifier("Another").addRun(new TextRun(" text after sequencial caption 4->")).addSequentialIdentifier("Label");
|
||||
|
||||
doc.addParagraph(paragraph);
|
||||
doc.addParagraph(paragraph2);
|
||||
doc.addParagraph(paragraph3);
|
||||
doc.addParagraph(paragraph4);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
32
demo/demo34.ts
Normal file
32
demo/demo34.ts
Normal file
@ -0,0 +1,32 @@
|
||||
// Example of how you would create a table with float positions
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import {
|
||||
Document,
|
||||
Packer,
|
||||
Paragraph,
|
||||
RelativeHorizontalPosition,
|
||||
RelativeVerticalPosition,
|
||||
TableAnchorType,
|
||||
WidthType,
|
||||
} from "../build";
|
||||
|
||||
const doc = new Document();
|
||||
|
||||
const table = doc.createTable(2, 2).float({
|
||||
horizontalAnchor: TableAnchorType.MARGIN,
|
||||
verticalAnchor: TableAnchorType.MARGIN,
|
||||
relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
|
||||
relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
|
||||
});
|
||||
table.setFixedWidthLayout();
|
||||
table.setWidth(WidthType.DXA, 4535);
|
||||
|
||||
table.getCell(0, 0).addContent(new Paragraph("Hello"));
|
||||
table.getRow(0).mergeCells(0, 1);
|
||||
|
||||
const packer = new Packer();
|
||||
|
||||
packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
BIN
demo/dotx/template.dotx
Normal file
BIN
demo/dotx/template.dotx
Normal file
Binary file not shown.
@ -1,7 +1,5 @@
|
||||
# Contribution Guidelines
|
||||
|
||||
## Writing Code
|
||||
|
||||
* Include documentation reference(s) at the top of each file:
|
||||
|
||||
```js
|
||||
@ -12,6 +10,59 @@
|
||||
|
||||
* Follow the `TSLint` rules
|
||||
|
||||
## Always think about the user
|
||||
|
||||
The number one pillar for contribution is to **ALWAYS** think about how the user will use the library.
|
||||
|
||||
Put yourself in their position, and imagine how they would feel about your feature you wrote.
|
||||
|
||||
1. Is it easy to use?
|
||||
2. Has it been documented well?
|
||||
3. Is it intuative?
|
||||
4. Is it consistent with the rest of the API?
|
||||
5. Is it fun to use?
|
||||
|
||||
## Good Commit Names
|
||||
|
||||
Please write good commit messages when making a commit: https://chris.beams.io/posts/git-commit/
|
||||
|
||||
**Do not:**
|
||||
```
|
||||
c // What?
|
||||
rtl // Adding acryonyms without explaining anything else is not helpful
|
||||
works! // Glad its working, but the message is not helpful
|
||||
demo updated // Getting better, but capitalize the first letter
|
||||
Unesesary coment removed // Make sure to use correct spelling
|
||||
```
|
||||
|
||||
## No leaky components in API interface
|
||||
|
||||
This mainly applies to the API the end user will consume.
|
||||
|
||||
Try to make method parameters accept primatives, 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, no need for:
|
||||
```js
|
||||
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 construct the object, and inject it in
|
||||
```js
|
||||
public float(tableFloatProperties: TableFloatProperties): Table
|
||||
```
|
||||
|
||||
**Do**
|
||||
`ITableFloatOptions` is an interface for a JSON of primatives.
|
||||
```js
|
||||
public float(tableFloatOptions: ITableFloatOptions): Table
|
||||
```
|
||||
|
||||
## Add vs Create
|
||||
|
||||
This is just a guideline, and the rules can sometimes be broken.
|
||||
@ -39,24 +90,104 @@ Getters and Setters are done with a capital letter like so:
|
||||
|
||||
```js
|
||||
public get Level() {
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
There is no performance advantage by doing this. It means we don't need to prefix all private variables with the ugly `_`:
|
||||
|
||||
**Do not:**
|
||||
**Do not:**
|
||||
|
||||
```js
|
||||
private get _level: string;
|
||||
```
|
||||
|
||||
**Do**
|
||||
**Do**
|
||||
|
||||
```js
|
||||
private get level: string;
|
||||
```
|
||||
|
||||
## Interfaces over type alias
|
||||
|
||||
Do not use `type`, but rather use `Interfaces`. `type` cannot be extended, and a class cannot implement it.
|
||||
|
||||
> "In general, use what you want ( type alias / interface ) just be consistent"
|
||||
> "always use interface for public API's definition when authoring a library or 3rd party ambient type definitions"
|
||||
>
|
||||
> * https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c
|
||||
|
||||
`Interface` is generally preferred over `type`: https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types
|
||||
|
||||
**Do not:**
|
||||
|
||||
```js
|
||||
type RelationshipFileInfo = { id: number, target: string };
|
||||
```
|
||||
|
||||
**Do:**
|
||||
|
||||
```js
|
||||
interface IRelationshipFileInfo {
|
||||
id: number;
|
||||
target: string;
|
||||
}
|
||||
```
|
||||
|
||||
## String enums vs type
|
||||
|
||||
To take full advantage of TypeScript's typing system, its best to use `string enums`:
|
||||
|
||||
**Do not:**
|
||||
|
||||
```js
|
||||
type WeaponType = "bow" | "sword" | "wand";
|
||||
```
|
||||
|
||||
**Do:**
|
||||
|
||||
```js
|
||||
enum WeaponType = {
|
||||
BOW = "bow",
|
||||
SWORD = "sword",
|
||||
WAND = "wand",
|
||||
}
|
||||
```
|
||||
|
||||
## Spell correctly, full and in American English
|
||||
|
||||
I am not sure where these habits in software development come from, but I do not believe it is beneficial:
|
||||
|
||||
**Do not:**
|
||||
```js
|
||||
readdy // misspelling
|
||||
perm // abbreviation
|
||||
conf // abbreviation
|
||||
cnty // abbreviation
|
||||
relationFile // abbreviation
|
||||
colour // U.K. English
|
||||
```
|
||||
|
||||
**Do:**
|
||||
```js
|
||||
ready
|
||||
permission
|
||||
config
|
||||
country
|
||||
relationshipFile
|
||||
color
|
||||
```
|
||||
|
||||
## Keep files small (within reason)
|
||||
|
||||
To minimize merge conflicts, reduce complexity, and improve readability, keep the files small.
|
||||
|
||||
## Name files and folders with `/foo-bar/kebab-case.ts`
|
||||
|
||||
To be consistent and in-line with the project, name files `like-this.ts`.
|
||||
|
||||
https://stackoverflow.com/questions/7273316/what-is-the-javascript-filename-naming-convention
|
||||
|
||||
## Testing
|
||||
|
||||
Please write a test of every file you make and suffix it with `.spec.ts`.
|
||||
@ -78,3 +209,5 @@ describe("ClassName", () => {
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Try not to use the `tests/utility.ts` file as this is being deprecated.
|
||||
|
@ -75,7 +75,7 @@ abstractNum.createLevel(0, "upperRoman", "%1", "start").addParagraphProperty(new
|
||||
abstractNum.createLevel(1, "decimal", "%2.", "start").addParagraphProperty(new Indent(1440, 980));
|
||||
abstractNum.createLevel(2, "lowerLetter", "%3)", "start").addParagraphProperty(new Indent(2160, 1700));
|
||||
|
||||
const concrete = numbering.createConcreteNumbering(numberedAbstract);
|
||||
const concrete = numbering.createConcreteNumbering(abstractNum);
|
||||
```
|
||||
|
||||
You can then apply your concrete style to paragraphs using their
|
||||
|
17
package.json
17
package.json
@ -1,22 +1,22 @@
|
||||
{
|
||||
"name": "docx",
|
||||
"version": "4.3.0",
|
||||
"version": "4.4.0",
|
||||
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
"pretest": "rimraf ./build",
|
||||
"test": "mocha-webpack \"src/**/*.ts\"",
|
||||
"test-watch": "mocha-webpack \"src/**/*.ts\" --watch",
|
||||
"test.coverage": "nyc npm test",
|
||||
"test.watch": "npm test -- --watch",
|
||||
"prepublishOnly": "npm run build",
|
||||
"lint": "tslint --project .",
|
||||
"build": "npm run webpack && npm run fix-types",
|
||||
"tsc": "rimraf ./build && tsc -p .",
|
||||
"webpack": "rimraf ./build && webpack",
|
||||
"build.web": "webpack --config webpack.web.config.js",
|
||||
"demo": "npm run build && npm run ts-node ./demo",
|
||||
"typedoc": "typedoc src/index.ts",
|
||||
"style": "prettier -l \"src/**/*.ts\"",
|
||||
"style.fix": "prettier \"src/**/*.ts\" --write",
|
||||
"style.fix": "npm run style -- --write",
|
||||
"fix-types": "node types-absolute-fixer.js",
|
||||
"ts-node": "ts-node"
|
||||
},
|
||||
@ -48,11 +48,11 @@
|
||||
"types": "./build/index.d.ts",
|
||||
"dependencies": {
|
||||
"@types/image-size": "0.0.29",
|
||||
"@types/jszip": "^3.1.3",
|
||||
"fast-xml-parser": "^3.3.6",
|
||||
"@types/jszip": "^3.1.4",
|
||||
"image-size": "^0.6.2",
|
||||
"jszip": "^3.1.5",
|
||||
"xml": "^1.0.1"
|
||||
"xml": "^1.0.1",
|
||||
"xml-js": "^1.6.8"
|
||||
},
|
||||
"author": "Dolan Miu",
|
||||
"license": "MIT",
|
||||
@ -67,9 +67,11 @@
|
||||
"awesome-typescript-loader": "^3.4.1",
|
||||
"chai": "^3.5.0",
|
||||
"glob": "^7.1.2",
|
||||
"istanbul-instrumenter-loader": "^3.0.1",
|
||||
"jszip": "^3.1.5",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha-webpack": "^1.0.1",
|
||||
"nyc": "^13.1.0",
|
||||
"pre-commit": "^1.2.2",
|
||||
"prettier": "^1.12.1",
|
||||
"prompt": "^1.0.0",
|
||||
@ -79,6 +81,7 @@
|
||||
"sinon": "^5.0.7",
|
||||
"ts-node": "^7.0.1",
|
||||
"tslint": "^5.11.0",
|
||||
"tslint-immutable": "^4.9.0",
|
||||
"typedoc": "^0.11.1",
|
||||
"typescript": "2.9.2",
|
||||
"webpack": "^3.10.0"
|
||||
|
@ -2,6 +2,12 @@ import { BaseXmlComponent, IXmlableObject } from "file/xml-components";
|
||||
|
||||
export class Formatter {
|
||||
public format(input: BaseXmlComponent): IXmlableObject {
|
||||
return input.prepForXml();
|
||||
const output = input.prepForXml();
|
||||
|
||||
if (output) {
|
||||
return output;
|
||||
} else {
|
||||
throw Error("XMLComponent did not format correctly");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
/* tslint:disable:typedef space-before-function-paren */
|
||||
import { expect } from "chai";
|
||||
import { File } from "../../file";
|
||||
|
||||
import { File } from "file";
|
||||
|
||||
import { Compiler } from "./next-compiler";
|
||||
|
||||
describe("Compiler", () => {
|
||||
|
@ -5,25 +5,25 @@ import { File } from "file";
|
||||
import { Formatter } from "../formatter";
|
||||
|
||||
interface IXmlifyedFile {
|
||||
data: string;
|
||||
path: string;
|
||||
readonly data: string;
|
||||
readonly path: string;
|
||||
}
|
||||
|
||||
interface IXmlifyedFileMapping {
|
||||
Document: IXmlifyedFile;
|
||||
Styles: IXmlifyedFile;
|
||||
Properties: IXmlifyedFile;
|
||||
Numbering: IXmlifyedFile;
|
||||
Relationships: IXmlifyedFile;
|
||||
FileRelationships: IXmlifyedFile;
|
||||
Headers: IXmlifyedFile[];
|
||||
Footers: IXmlifyedFile[];
|
||||
HeaderRelationships: IXmlifyedFile[];
|
||||
FooterRelationships: IXmlifyedFile[];
|
||||
ContentTypes: IXmlifyedFile;
|
||||
AppProperties: IXmlifyedFile;
|
||||
FootNotes: IXmlifyedFile;
|
||||
Settings: IXmlifyedFile;
|
||||
readonly Document: IXmlifyedFile;
|
||||
readonly Styles: IXmlifyedFile;
|
||||
readonly Properties: IXmlifyedFile;
|
||||
readonly Numbering: IXmlifyedFile;
|
||||
readonly Relationships: IXmlifyedFile;
|
||||
readonly FileRelationships: IXmlifyedFile;
|
||||
readonly Headers: IXmlifyedFile[];
|
||||
readonly Footers: IXmlifyedFile[];
|
||||
readonly HeaderRelationships: IXmlifyedFile[];
|
||||
readonly FooterRelationships: IXmlifyedFile[];
|
||||
readonly ContentTypes: IXmlifyedFile;
|
||||
readonly AppProperties: IXmlifyedFile;
|
||||
readonly FootNotes: IXmlifyedFile;
|
||||
readonly Settings: IXmlifyedFile;
|
||||
}
|
||||
|
||||
export class Compiler {
|
||||
@ -59,6 +59,18 @@ export class Compiler {
|
||||
zip.file(`word/media/${data.fileName}`, mediaData);
|
||||
}
|
||||
|
||||
for (const header of file.Headers) {
|
||||
for (const data of header.Media.Array) {
|
||||
zip.file(`word/media/${data.fileName}`, data.stream);
|
||||
}
|
||||
}
|
||||
|
||||
for (const footer of file.Footers) {
|
||||
for (const data of footer.Media.Array) {
|
||||
zip.file(`word/media/${data.fileName}`, data.stream);
|
||||
}
|
||||
}
|
||||
|
||||
return zip;
|
||||
}
|
||||
|
||||
@ -128,4 +140,13 @@ export class Compiler {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/* By default docx collapse empty tags. <a></a> -> <a/>. this function mimic it
|
||||
so comparing (diff) original docx file and the library output is easier
|
||||
Currently not used, so commenting out */
|
||||
// private collapseEmptyTags(xmlData: string): string {
|
||||
// const regEx = /<(([^ <>]+)[^<>]*)><\/\2>/g;
|
||||
// const collapsed = xmlData.replace(regEx, "<$1/>");
|
||||
// return collapsed;
|
||||
// }
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
import { assert } from "chai";
|
||||
import { stub } from "sinon";
|
||||
|
||||
import { File, Paragraph } from "../../file";
|
||||
import { File, Paragraph } from "file";
|
||||
|
||||
import { Packer } from "./packer";
|
||||
|
||||
describe("Packer", () => {
|
||||
|
@ -10,21 +10,30 @@ export class Packer {
|
||||
|
||||
public async toBuffer(file: File): Promise<Buffer> {
|
||||
const zip = await this.compiler.compile(file);
|
||||
const zipData = (await zip.generateAsync({ type: "nodebuffer" })) as Buffer;
|
||||
const zipData = (await zip.generateAsync({
|
||||
type: "nodebuffer",
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
})) as Buffer;
|
||||
|
||||
return zipData;
|
||||
}
|
||||
|
||||
public async toBase64String(file: File): Promise<string> {
|
||||
const zip = await this.compiler.compile(file);
|
||||
const zipData = (await zip.generateAsync({ type: "base64" })) as string;
|
||||
const zipData = (await zip.generateAsync({
|
||||
type: "base64",
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
})) as string;
|
||||
|
||||
return zipData;
|
||||
}
|
||||
|
||||
public async toBlob(file: File): Promise<Blob> {
|
||||
const zip = await this.compiler.compile(file);
|
||||
const zipData = (await zip.generateAsync({ type: "blob" })) as Blob;
|
||||
const zipData = (await zip.generateAsync({
|
||||
type: "blob",
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
})) as Blob;
|
||||
|
||||
return zipData;
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IAppPropertiesAttributes {
|
||||
xmlns: string;
|
||||
vt: string;
|
||||
readonly xmlns: string;
|
||||
readonly vt: string;
|
||||
}
|
||||
|
||||
export class AppPropertiesAttributes extends XmlAttributeComponent<IAppPropertiesAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
xmlns: "xmlns",
|
||||
vt: "xmlns:vt",
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IContentTypeAttributes {
|
||||
xmlns?: string;
|
||||
readonly xmlns?: string;
|
||||
}
|
||||
|
||||
export class ContentTypeAttributes extends XmlAttributeComponent<IContentTypeAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
xmlns: "xmlns",
|
||||
};
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
// tslint:disable:no-string-literal
|
||||
|
||||
import { expect } from "chai";
|
||||
import { Formatter } from "../../export/formatter";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { ContentTypes } from "./content-types";
|
||||
|
||||
describe("ContentTypes", () => {
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IDefaultAttributes {
|
||||
contentType: string;
|
||||
extension?: string;
|
||||
readonly contentType: string;
|
||||
readonly extension?: string;
|
||||
}
|
||||
|
||||
export class DefaultAttributes extends XmlAttributeComponent<IDefaultAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
contentType: "ContentType",
|
||||
extension: "Extension",
|
||||
};
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IOverrideAttributes {
|
||||
contentType: string;
|
||||
partName?: string;
|
||||
readonly contentType: string;
|
||||
readonly partName?: string;
|
||||
}
|
||||
|
||||
export class OverrideAttributes extends XmlAttributeComponent<IOverrideAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
contentType: "ContentType",
|
||||
partName: "PartName",
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../export/formatter";
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { CoreProperties } from "./properties";
|
||||
|
||||
describe("Properties", () => {
|
||||
|
@ -3,14 +3,14 @@ import { DocumentAttributes } from "../document/document-attributes";
|
||||
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
|
||||
|
||||
export interface IPropertiesOptions {
|
||||
title?: string;
|
||||
subject?: string;
|
||||
creator?: string;
|
||||
keywords?: string;
|
||||
description?: string;
|
||||
lastModifiedBy?: string;
|
||||
revision?: string;
|
||||
externalStyles?: string;
|
||||
readonly title?: string;
|
||||
readonly subject?: string;
|
||||
readonly creator?: string;
|
||||
readonly keywords?: string;
|
||||
readonly description?: string;
|
||||
readonly lastModifiedBy?: string;
|
||||
readonly revision?: string;
|
||||
readonly externalStyles?: string;
|
||||
}
|
||||
|
||||
export class CoreProperties extends XmlComponent {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../export/formatter";
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { Body } from "./body";
|
||||
|
||||
describe("Body", () => {
|
||||
@ -16,11 +17,11 @@ describe("Body", () => {
|
||||
expect(formatted)
|
||||
.to.have.property("w:sectPr")
|
||||
.and.to.be.an.instanceof(Array);
|
||||
expect(formatted["w:sectPr"]).to.have.length(7);
|
||||
expect(formatted["w:sectPr"]).to.have.length(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe("addSection", () => {
|
||||
describe("#addSection", () => {
|
||||
it("should add section with options", () => {
|
||||
body.addSection({
|
||||
width: 10000,
|
||||
@ -38,5 +39,93 @@ describe("Body", () => {
|
||||
const newSection = formatted[1]["w:sectPr"];
|
||||
expect(newSection[0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 10000, "w:w": 10000, "w:orient": "portrait" } }] });
|
||||
});
|
||||
|
||||
it("should add section with default parameters", () => {
|
||||
body.addSection({
|
||||
width: 10000,
|
||||
height: 10000,
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(body);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:body": [
|
||||
{
|
||||
"w:p": [
|
||||
{ "w:pPr": [] },
|
||||
{
|
||||
"w:pPr": [
|
||||
{
|
||||
"w:sectPr": [
|
||||
{ "w:pgSz": [{ _attr: { "w:w": 11906, "w:h": 16838, "w:orient": "portrait" } }] },
|
||||
{
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:bottom": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:footer": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ "w:cols": [{ _attr: { "w:space": 708 } }] },
|
||||
{ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] },
|
||||
{ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:sectPr": [
|
||||
{ "w:pgSz": [{ _attr: { "w:w": 10000, "w:h": 10000, "w:orient": "portrait" } }] },
|
||||
{
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:bottom": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:footer": 708,
|
||||
"w:gutter": 0,
|
||||
"w:mirrorMargins": false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ "w:cols": [{ _attr: { "w:space": 708 } }] },
|
||||
{ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] },
|
||||
{ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getParagraphs", () => {
|
||||
it("should get no paragraphs", () => {
|
||||
const paragraphs = body.getParagraphs();
|
||||
|
||||
expect(paragraphs).to.be.an.instanceof(Array);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#DefaultSection", () => {
|
||||
it("should get section", () => {
|
||||
const section = body.DefaultSection;
|
||||
|
||||
const tree = new Formatter().format(section);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -35,11 +35,9 @@ export class Body extends XmlComponent {
|
||||
this.sections.push(new SectionProperties(params));
|
||||
}
|
||||
}
|
||||
public prepForXml(): IXmlableObject {
|
||||
public prepForXml(): IXmlableObject | undefined {
|
||||
if (this.sections.length === 1) {
|
||||
this.root.push(this.sections[0]);
|
||||
} else if (this.sections.length > 1) {
|
||||
throw new Error("Invalid usage of sections. At the end of the body element there must be ONE section.");
|
||||
}
|
||||
|
||||
return super.prepForXml();
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IColumnsAttributes {
|
||||
space?: number;
|
||||
readonly space?: number;
|
||||
}
|
||||
|
||||
export class ColumnsAttributes extends XmlAttributeComponent<IColumnsAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
space: "w:space",
|
||||
};
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IDocGridAttributesProperties {
|
||||
linePitch?: number;
|
||||
readonly linePitch?: number;
|
||||
}
|
||||
|
||||
export class DocGridAttributes extends XmlAttributeComponent<IDocGridAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
linePitch: "w:linePitch",
|
||||
};
|
||||
}
|
||||
|
@ -7,12 +7,12 @@ export enum FooterReferenceType {
|
||||
}
|
||||
|
||||
export interface IFooterReferenceAttributes {
|
||||
type: string;
|
||||
id: string;
|
||||
readonly type: string;
|
||||
readonly id: string;
|
||||
}
|
||||
|
||||
export class FooterReferenceAttributes extends XmlAttributeComponent<IFooterReferenceAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
type: "w:type",
|
||||
id: "r:id",
|
||||
};
|
||||
|
@ -2,8 +2,8 @@ import { XmlComponent } from "file/xml-components";
|
||||
import { FooterReferenceAttributes, FooterReferenceType } from "./footer-reference-attributes";
|
||||
|
||||
export interface IFooterOptions {
|
||||
footerType?: FooterReferenceType;
|
||||
footerId?: number;
|
||||
readonly footerType?: FooterReferenceType;
|
||||
readonly footerId?: number;
|
||||
}
|
||||
|
||||
export class FooterReference extends XmlComponent {
|
||||
|
@ -7,12 +7,12 @@ export enum HeaderReferenceType {
|
||||
}
|
||||
|
||||
export interface IHeaderReferenceAttributes {
|
||||
type: string;
|
||||
id: string;
|
||||
readonly type: string;
|
||||
readonly id: string;
|
||||
}
|
||||
|
||||
export class HeaderReferenceAttributes extends XmlAttributeComponent<IHeaderReferenceAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
type: "w:type",
|
||||
id: "r:id",
|
||||
};
|
||||
|
@ -2,8 +2,8 @@ import { XmlComponent } from "file/xml-components";
|
||||
import { HeaderReferenceAttributes, HeaderReferenceType } from "./header-reference-attributes";
|
||||
|
||||
export interface IHeaderOptions {
|
||||
headerType?: HeaderReferenceType;
|
||||
headerId?: number;
|
||||
readonly headerType?: HeaderReferenceType;
|
||||
readonly headerId?: number;
|
||||
}
|
||||
|
||||
export class HeaderReference extends XmlComponent {
|
||||
|
@ -1,16 +1,15 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../../../export/formatter";
|
||||
import { BorderStyle } from "../../../../styles";
|
||||
import { Formatter } from "export/formatter";
|
||||
import { BorderStyle } from "file/styles";
|
||||
|
||||
import { PageBorderDisplay, PageBorders, PageBorderZOrder } from "./page-borders";
|
||||
|
||||
describe("PageBorders", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("should create empty element when no options are passed", () => {
|
||||
const properties = new PageBorders();
|
||||
const tree = new Formatter().format(properties);
|
||||
|
||||
expect(tree).to.equal("");
|
||||
expect(() => new Formatter().format(properties)).to.throw();
|
||||
});
|
||||
|
||||
it("should create page borders with some configuration", () => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
// http://officeopenxml.com/WPsectionBorders.php
|
||||
import { BorderStyle } from "file/styles";
|
||||
import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
import { BorderStyle } from "../../../../styles";
|
||||
|
||||
export enum PageBorderDisplay {
|
||||
ALL_PAGES = "allPages",
|
||||
@ -19,28 +19,28 @@ export enum PageBorderZOrder {
|
||||
}
|
||||
|
||||
export interface IPageBorderAttributes {
|
||||
display?: PageBorderDisplay;
|
||||
offsetFrom?: PageBorderOffsetFrom;
|
||||
zOrder?: PageBorderZOrder;
|
||||
readonly display?: PageBorderDisplay;
|
||||
readonly offsetFrom?: PageBorderOffsetFrom;
|
||||
readonly zOrder?: PageBorderZOrder;
|
||||
}
|
||||
|
||||
export interface IPageBorderConfiguration {
|
||||
style?: BorderStyle;
|
||||
size?: number;
|
||||
color?: string;
|
||||
space?: number;
|
||||
readonly style?: BorderStyle;
|
||||
readonly size?: number;
|
||||
readonly color?: string;
|
||||
readonly space?: number;
|
||||
}
|
||||
|
||||
export interface IPageBordersOptions {
|
||||
pageBorders?: IPageBorderAttributes;
|
||||
pageBorderTop?: IPageBorderConfiguration;
|
||||
pageBorderRight?: IPageBorderConfiguration;
|
||||
pageBorderBottom?: IPageBorderConfiguration;
|
||||
pageBorderLeft?: IPageBorderConfiguration;
|
||||
readonly pageBorders?: IPageBorderAttributes;
|
||||
readonly pageBorderTop?: IPageBorderConfiguration;
|
||||
readonly pageBorderRight?: IPageBorderConfiguration;
|
||||
readonly pageBorderBottom?: IPageBorderConfiguration;
|
||||
readonly pageBorderLeft?: IPageBorderConfiguration;
|
||||
}
|
||||
|
||||
class PageBordeAttributes extends XmlAttributeComponent<IPageBorderConfiguration> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
style: "w:val",
|
||||
size: "w:size",
|
||||
color: "w:color",
|
||||
@ -57,7 +57,7 @@ class PageBorder extends XmlComponent {
|
||||
}
|
||||
|
||||
class PageBordersAttributes extends XmlAttributeComponent<IPageBorderAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
display: "w:display",
|
||||
offsetFrom: "w:offsetFrom",
|
||||
zOrder: "w:zOrder",
|
||||
@ -98,7 +98,9 @@ export class PageBorders extends XmlComponent {
|
||||
}
|
||||
}
|
||||
|
||||
public prepForXml(): IXmlableObject {
|
||||
return this.root.length > 0 ? super.prepForXml() : "";
|
||||
public prepForXml(): IXmlableObject | undefined {
|
||||
if (this.root.length > 0) {
|
||||
return super.prepForXml();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IPageMarginAttributes {
|
||||
top?: number;
|
||||
right?: number;
|
||||
bottom?: number;
|
||||
left?: number;
|
||||
header?: number;
|
||||
footer?: number;
|
||||
gutter?: number;
|
||||
mirror?: boolean;
|
||||
readonly top?: number;
|
||||
readonly right?: number;
|
||||
readonly bottom?: number;
|
||||
readonly left?: number;
|
||||
readonly header?: number;
|
||||
readonly footer?: number;
|
||||
readonly gutter?: number;
|
||||
readonly mirror?: boolean;
|
||||
}
|
||||
|
||||
export class PageMarginAttributes extends XmlAttributeComponent<IPageMarginAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
top: "w:top",
|
||||
right: "w:right",
|
||||
bottom: "w:bottom",
|
||||
|
@ -17,12 +17,12 @@ export enum PageNumberFormat {
|
||||
}
|
||||
|
||||
export interface IPageNumberTypeAttributes {
|
||||
pageNumberStart?: number;
|
||||
pageNumberFormatType?: PageNumberFormat;
|
||||
readonly pageNumberStart?: number;
|
||||
readonly pageNumberFormatType?: PageNumberFormat;
|
||||
}
|
||||
|
||||
export class PageNumberTypeAttributes extends XmlAttributeComponent<IPageNumberTypeAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
pageNumberStart: "w:start",
|
||||
pageNumberFormatType: "w:fmt",
|
||||
};
|
||||
|
@ -6,13 +6,13 @@ export enum PageOrientation {
|
||||
}
|
||||
|
||||
export interface IPageSizeAttributes {
|
||||
width?: number;
|
||||
height?: number;
|
||||
orientation?: PageOrientation;
|
||||
readonly width?: number;
|
||||
readonly height?: number;
|
||||
readonly orientation?: PageOrientation;
|
||||
}
|
||||
|
||||
export class PageSizeAttributes extends XmlAttributeComponent<IPageSizeAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
width: "w:w",
|
||||
height: "w:h",
|
||||
orientation: "w:orient",
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../../../export/formatter";
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { PageSize } from "./page-size";
|
||||
import { PageOrientation } from "./page-size-attributes";
|
||||
|
||||
|
@ -1,12 +1,19 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../../export/formatter";
|
||||
import { FooterReferenceType, PageBorderOffsetFrom, PageNumberFormat } from "./";
|
||||
import { Formatter } from "export/formatter";
|
||||
import { FooterWrapper } from "file/footer-wrapper";
|
||||
import { HeaderWrapper } from "file/header-wrapper";
|
||||
import { Media } from "file/media";
|
||||
|
||||
import { PageBorderOffsetFrom } from "./page-border";
|
||||
import { PageNumberFormat } from "./page-number";
|
||||
import { SectionProperties } from "./section-properties";
|
||||
|
||||
describe("SectionProperties", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("should create section properties with options", () => {
|
||||
const media = new Media();
|
||||
|
||||
const properties = new SectionProperties({
|
||||
width: 11906,
|
||||
height: 16838,
|
||||
@ -20,9 +27,12 @@ describe("SectionProperties", () => {
|
||||
mirror: false,
|
||||
space: 708,
|
||||
linePitch: 360,
|
||||
headerId: 100,
|
||||
footerId: 200,
|
||||
footerType: FooterReferenceType.EVEN,
|
||||
headers: {
|
||||
default: new HeaderWrapper(media, 100),
|
||||
},
|
||||
footers: {
|
||||
even: new FooterWrapper(media, 200),
|
||||
},
|
||||
pageNumberStart: 10,
|
||||
pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT,
|
||||
});
|
||||
@ -78,9 +88,7 @@ describe("SectionProperties", () => {
|
||||
});
|
||||
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] });
|
||||
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] });
|
||||
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": [{ _attr: { "r:id": "rId0", "w:type": "default" } }] });
|
||||
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": [{ _attr: { "r:id": "rId0", "w:type": "default" } }] });
|
||||
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] });
|
||||
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] });
|
||||
});
|
||||
|
||||
it("should create section properties with changed options", () => {
|
||||
@ -170,7 +178,8 @@ describe("SectionProperties", () => {
|
||||
});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"][7]).to.deep.equal({
|
||||
const pgBorders = tree["w:sectPr"].find((item) => item["w:pgBorders"] !== undefined);
|
||||
expect(pgBorders).to.deep.equal({
|
||||
"w:pgBorders": [{ _attr: { "w:offsetFrom": "page" } }],
|
||||
});
|
||||
});
|
||||
|
@ -1,115 +1,172 @@
|
||||
// http://officeopenxml.com/WPsection.php
|
||||
import { FooterWrapper } from "file/footer-wrapper";
|
||||
import { HeaderWrapper } from "file/header-wrapper";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { FooterReferenceType, IPageBordersOptions, IPageNumberTypeAttributes, PageBorders, PageNumberFormat, PageNumberType } from "./";
|
||||
|
||||
import { Columns } from "./columns/columns";
|
||||
import { IColumnsAttributes } from "./columns/columns-attributes";
|
||||
import { DocumentGrid } from "./doc-grid/doc-grid";
|
||||
import { IDocGridAttributesProperties } from "./doc-grid/doc-grid-attributes";
|
||||
import { FooterReference, IFooterOptions } from "./footer-reference/footer-reference";
|
||||
import { HeaderReference, IHeaderOptions } from "./header-reference/header-reference";
|
||||
import { HeaderReferenceType } from "./header-reference/header-reference-attributes";
|
||||
import { FooterReferenceType } from "./footer-reference";
|
||||
import { FooterReference } from "./footer-reference/footer-reference";
|
||||
import { HeaderReferenceType } from "./header-reference";
|
||||
import { HeaderReference } from "./header-reference/header-reference";
|
||||
import { IPageBordersOptions, PageBorders } from "./page-border";
|
||||
import { PageMargin } from "./page-margin/page-margin";
|
||||
import { IPageMarginAttributes } from "./page-margin/page-margin-attributes";
|
||||
import { IPageNumberTypeAttributes, PageNumberFormat, PageNumberType } from "./page-number";
|
||||
import { PageSize } from "./page-size/page-size";
|
||||
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
|
||||
import { TitlePage } from "./title-page/title-page";
|
||||
|
||||
export interface IHeaderFooterGroup<T> {
|
||||
readonly default?: T;
|
||||
readonly first?: T;
|
||||
readonly even?: T;
|
||||
}
|
||||
|
||||
interface IHeadersOptions {
|
||||
readonly headers?: IHeaderFooterGroup<HeaderWrapper>;
|
||||
}
|
||||
|
||||
interface IFootersOptions {
|
||||
readonly footers?: IHeaderFooterGroup<FooterWrapper>;
|
||||
}
|
||||
|
||||
interface ITitlePageOptions {
|
||||
readonly titlePage?: boolean;
|
||||
}
|
||||
|
||||
export type SectionPropertiesOptions = IPageSizeAttributes &
|
||||
IPageMarginAttributes &
|
||||
IColumnsAttributes &
|
||||
IDocGridAttributesProperties &
|
||||
IHeaderOptions &
|
||||
IFooterOptions &
|
||||
IHeadersOptions &
|
||||
IFootersOptions &
|
||||
IPageNumberTypeAttributes &
|
||||
IPageBordersOptions;
|
||||
IPageBordersOptions &
|
||||
ITitlePageOptions;
|
||||
|
||||
export class SectionProperties extends XmlComponent {
|
||||
private readonly options: SectionPropertiesOptions;
|
||||
|
||||
constructor(options?: SectionPropertiesOptions) {
|
||||
constructor(options: SectionPropertiesOptions = {}) {
|
||||
super("w:sectPr");
|
||||
|
||||
const defaultOptions = {
|
||||
width: 11906,
|
||||
height: 16838,
|
||||
top: 1440,
|
||||
right: 1440,
|
||||
bottom: 1440,
|
||||
left: 1440,
|
||||
header: 708,
|
||||
footer: 708,
|
||||
gutter: 0,
|
||||
mirror: false,
|
||||
space: 708,
|
||||
linePitch: 360,
|
||||
orientation: PageOrientation.PORTRAIT,
|
||||
headerType: HeaderReferenceType.DEFAULT,
|
||||
headerId: 0,
|
||||
footerType: FooterReferenceType.DEFAULT,
|
||||
footerId: 0,
|
||||
pageNumberStart: undefined,
|
||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||
pageBorders: undefined,
|
||||
pageBorderTop: undefined,
|
||||
pageBorderRight: undefined,
|
||||
pageBorderBottom: undefined,
|
||||
pageBorderLeft: undefined,
|
||||
};
|
||||
const {
|
||||
width = 11906,
|
||||
height = 16838,
|
||||
top = 1440,
|
||||
right = 1440,
|
||||
bottom = 1440,
|
||||
left = 1440,
|
||||
header = 708,
|
||||
footer = 708,
|
||||
gutter = 0,
|
||||
mirror = false,
|
||||
space = 708,
|
||||
linePitch = 360,
|
||||
orientation = PageOrientation.PORTRAIT,
|
||||
headers,
|
||||
footers,
|
||||
pageNumberFormatType = PageNumberFormat.DECIMAL,
|
||||
pageNumberStart,
|
||||
pageBorders,
|
||||
pageBorderTop,
|
||||
pageBorderRight,
|
||||
pageBorderBottom,
|
||||
pageBorderLeft,
|
||||
titlePage = false,
|
||||
} = options;
|
||||
|
||||
const mergedOptions = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
};
|
||||
this.options = options;
|
||||
this.root.push(new PageSize(width, height, orientation));
|
||||
this.root.push(new PageMargin(top, right, bottom, left, header, footer, gutter, mirror));
|
||||
this.root.push(new Columns(space));
|
||||
this.root.push(new DocumentGrid(linePitch));
|
||||
|
||||
this.root.push(new PageSize(mergedOptions.width, mergedOptions.height, mergedOptions.orientation));
|
||||
this.root.push(
|
||||
new PageMargin(
|
||||
mergedOptions.top,
|
||||
mergedOptions.right,
|
||||
mergedOptions.bottom,
|
||||
mergedOptions.left,
|
||||
mergedOptions.header,
|
||||
mergedOptions.footer,
|
||||
mergedOptions.gutter,
|
||||
mergedOptions.mirror,
|
||||
),
|
||||
);
|
||||
this.root.push(new Columns(mergedOptions.space));
|
||||
this.root.push(new DocumentGrid(mergedOptions.linePitch));
|
||||
this.addHeaders(headers);
|
||||
this.addFooters(footers);
|
||||
|
||||
this.root.push(
|
||||
new HeaderReference({
|
||||
headerType: mergedOptions.headerType,
|
||||
headerId: mergedOptions.headerId,
|
||||
}),
|
||||
);
|
||||
this.root.push(
|
||||
new FooterReference({
|
||||
footerType: mergedOptions.footerType,
|
||||
footerId: mergedOptions.footerId,
|
||||
}),
|
||||
);
|
||||
this.root.push(new PageNumberType(pageNumberStart, pageNumberFormatType));
|
||||
|
||||
this.root.push(new PageNumberType(mergedOptions.pageNumberStart, mergedOptions.pageNumberFormatType));
|
||||
|
||||
if (
|
||||
mergedOptions.pageBorders ||
|
||||
mergedOptions.pageBorderTop ||
|
||||
mergedOptions.pageBorderRight ||
|
||||
mergedOptions.pageBorderBottom ||
|
||||
mergedOptions.pageBorderLeft
|
||||
) {
|
||||
if (pageBorders || pageBorderTop || pageBorderRight || pageBorderBottom || pageBorderLeft) {
|
||||
this.root.push(
|
||||
new PageBorders({
|
||||
pageBorders: mergedOptions.pageBorders,
|
||||
pageBorderTop: mergedOptions.pageBorderTop,
|
||||
pageBorderRight: mergedOptions.pageBorderRight,
|
||||
pageBorderBottom: mergedOptions.pageBorderBottom,
|
||||
pageBorderLeft: mergedOptions.pageBorderLeft,
|
||||
pageBorders: pageBorders,
|
||||
pageBorderTop: pageBorderTop,
|
||||
pageBorderRight: pageBorderRight,
|
||||
pageBorderBottom: pageBorderBottom,
|
||||
pageBorderLeft: pageBorderLeft,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
this.options = mergedOptions;
|
||||
if (titlePage) {
|
||||
this.root.push(new TitlePage());
|
||||
}
|
||||
}
|
||||
|
||||
private addHeaders(headers?: IHeaderFooterGroup<HeaderWrapper>): void {
|
||||
if (headers) {
|
||||
if (headers.default) {
|
||||
this.root.push(
|
||||
new HeaderReference({
|
||||
headerType: HeaderReferenceType.DEFAULT,
|
||||
headerId: headers.default.Header.ReferenceId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (headers.first) {
|
||||
this.root.push(
|
||||
new HeaderReference({
|
||||
headerType: HeaderReferenceType.FIRST,
|
||||
headerId: headers.first.Header.ReferenceId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (headers.even) {
|
||||
this.root.push(
|
||||
new HeaderReference({
|
||||
headerType: HeaderReferenceType.EVEN,
|
||||
headerId: headers.even.Header.ReferenceId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private addFooters(footers?: IHeaderFooterGroup<FooterWrapper>): void {
|
||||
if (footers) {
|
||||
if (footers.default) {
|
||||
this.root.push(
|
||||
new FooterReference({
|
||||
footerType: FooterReferenceType.DEFAULT,
|
||||
footerId: footers.default.Footer.ReferenceId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (footers.first) {
|
||||
this.root.push(
|
||||
new FooterReference({
|
||||
footerType: FooterReferenceType.FIRST,
|
||||
footerId: footers.first.Footer.ReferenceId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (footers.even) {
|
||||
this.root.push(
|
||||
new FooterReference({
|
||||
footerType: FooterReferenceType.EVEN,
|
||||
footerId: footers.even.Footer.ReferenceId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get Options(): SectionPropertiesOptions {
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IHeaderReferenceAttributes {
|
||||
value: string;
|
||||
readonly value: string;
|
||||
}
|
||||
|
||||
export class TitlePageAttributes extends XmlAttributeComponent<IHeaderReferenceAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
value: "w:val",
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../../../export/formatter";
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { TitlePage } from "./title-page";
|
||||
|
||||
describe("PageSize", () => {
|
||||
|
@ -1,33 +1,33 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IDocumentAttributesProperties {
|
||||
wpc?: string;
|
||||
mc?: string;
|
||||
o?: string;
|
||||
r?: string;
|
||||
m?: string;
|
||||
v?: string;
|
||||
wp14?: string;
|
||||
wp?: string;
|
||||
w10?: string;
|
||||
w?: string;
|
||||
w14?: string;
|
||||
w15?: string;
|
||||
wpg?: string;
|
||||
wpi?: string;
|
||||
wne?: string;
|
||||
wps?: string;
|
||||
Ignorable?: string;
|
||||
cp?: string;
|
||||
dc?: string;
|
||||
dcterms?: string;
|
||||
dcmitype?: string;
|
||||
xsi?: string;
|
||||
type?: string;
|
||||
readonly wpc?: string;
|
||||
readonly mc?: string;
|
||||
readonly o?: string;
|
||||
readonly r?: string;
|
||||
readonly m?: string;
|
||||
readonly v?: string;
|
||||
readonly wp14?: string;
|
||||
readonly wp?: string;
|
||||
readonly w10?: string;
|
||||
readonly w?: string;
|
||||
readonly w14?: string;
|
||||
readonly w15?: string;
|
||||
readonly wpg?: string;
|
||||
readonly wpi?: string;
|
||||
readonly wne?: string;
|
||||
readonly wps?: string;
|
||||
readonly Ignorable?: string;
|
||||
readonly cp?: string;
|
||||
readonly dc?: string;
|
||||
readonly dcterms?: string;
|
||||
readonly dcmitype?: string;
|
||||
readonly xsi?: string;
|
||||
readonly type?: string;
|
||||
}
|
||||
|
||||
export class DocumentAttributes extends XmlAttributeComponent<IDocumentAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
wpc: "xmlns:wpc",
|
||||
mc: "xmlns:mc",
|
||||
o: "xmlns:o",
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { assert, expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../export/formatter";
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { Paragraph } from "../paragraph";
|
||||
import { Table } from "../table";
|
||||
import { Document } from "./document";
|
||||
|
@ -2,16 +2,16 @@ import { XmlAttributeComponent } from "file/xml-components";
|
||||
import { IDistance } from "../drawing";
|
||||
|
||||
export interface IAnchorAttributes extends IDistance {
|
||||
allowOverlap?: "0" | "1";
|
||||
behindDoc?: "0" | "1";
|
||||
layoutInCell?: "0" | "1";
|
||||
locked?: "0" | "1";
|
||||
relativeHeight?: number;
|
||||
simplePos?: "0" | "1";
|
||||
readonly allowOverlap?: "0" | "1";
|
||||
readonly behindDoc?: "0" | "1";
|
||||
readonly layoutInCell?: "0" | "1";
|
||||
readonly locked?: "0" | "1";
|
||||
readonly relativeHeight?: number;
|
||||
readonly simplePos?: "0" | "1";
|
||||
}
|
||||
|
||||
export class AnchorAttributes extends XmlAttributeComponent<IAnchorAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
distT: "distT",
|
||||
distB: "distB",
|
||||
distL: "distL",
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { assert } from "chai";
|
||||
|
||||
import { Utility } from "../../../tests/utility";
|
||||
import { IDrawingOptions, TextWrapStyle } from ".././";
|
||||
import { Anchor } from "./";
|
||||
import { Utility } from "tests/utility";
|
||||
|
||||
import { IDrawingOptions } from "../drawing";
|
||||
import { TextWrapStyle } from "../text-wrap";
|
||||
import { Anchor } from "./anchor";
|
||||
|
||||
function createDrawing(drawingOptions: IDrawingOptions): Anchor {
|
||||
return new Anchor(
|
||||
@ -101,7 +103,7 @@ describe("Anchor", () => {
|
||||
assert.equal(graphic.rootKey, "a:graphic");
|
||||
});
|
||||
|
||||
it("should create a Drawing with text wrapping", () => {
|
||||
it("should create a Drawing with square text wrapping", () => {
|
||||
anchor = createDrawing({
|
||||
textWrapping: {
|
||||
textWrapStyle: TextWrapStyle.SQUARE,
|
||||
@ -114,5 +116,44 @@ describe("Anchor", () => {
|
||||
const textWrap = newJson.root[6];
|
||||
assert.equal(textWrap.rootKey, "wp:wrapSquare");
|
||||
});
|
||||
|
||||
it("should create a Drawing with no text wrapping", () => {
|
||||
anchor = createDrawing({
|
||||
textWrapping: {
|
||||
textWrapStyle: TextWrapStyle.NONE,
|
||||
},
|
||||
});
|
||||
const newJson = Utility.jsonify(anchor);
|
||||
assert.equal(newJson.root.length, 10);
|
||||
|
||||
const textWrap = newJson.root[6];
|
||||
assert.equal(textWrap.rootKey, "wp:wrapNone");
|
||||
});
|
||||
|
||||
it("should create a Drawing with tight text wrapping", () => {
|
||||
anchor = createDrawing({
|
||||
textWrapping: {
|
||||
textWrapStyle: TextWrapStyle.TIGHT,
|
||||
},
|
||||
});
|
||||
const newJson = Utility.jsonify(anchor);
|
||||
assert.equal(newJson.root.length, 10);
|
||||
|
||||
const textWrap = newJson.root[6];
|
||||
assert.equal(textWrap.rootKey, "wp:wrapTight");
|
||||
});
|
||||
|
||||
it("should create a Drawing with tight text wrapping", () => {
|
||||
anchor = createDrawing({
|
||||
textWrapping: {
|
||||
textWrapStyle: TextWrapStyle.TOP_AND_BOTTOM,
|
||||
},
|
||||
});
|
||||
const newJson = Utility.jsonify(anchor);
|
||||
assert.equal(newJson.root.length, 10);
|
||||
|
||||
const textWrap = newJson.root[6];
|
||||
assert.equal(textWrap.rootKey, "wp:wrapTopAndBottom");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IDocPropertiesAttributes {
|
||||
id?: number;
|
||||
name?: string;
|
||||
descr?: string;
|
||||
readonly id?: number;
|
||||
readonly name?: string;
|
||||
readonly descr?: string;
|
||||
}
|
||||
|
||||
export class DocPropertiesAttributes extends XmlAttributeComponent<IDocPropertiesAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
id: "id",
|
||||
name: "name",
|
||||
descr: "descr",
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { assert } from "chai";
|
||||
|
||||
import { Utility } from "../../tests/utility";
|
||||
import { Drawing, IDrawingOptions, PlacementPosition } from "./";
|
||||
import { Utility } from "tests/utility";
|
||||
|
||||
import { Drawing, IDrawingOptions, PlacementPosition } from "./drawing";
|
||||
|
||||
const imageBase64Data = `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`;
|
||||
|
||||
|
@ -11,16 +11,16 @@ export enum PlacementPosition {
|
||||
}
|
||||
|
||||
export interface IDistance {
|
||||
distT?: number;
|
||||
distB?: number;
|
||||
distL?: number;
|
||||
distR?: number;
|
||||
readonly distT?: number;
|
||||
readonly distB?: number;
|
||||
readonly distL?: number;
|
||||
readonly distR?: number;
|
||||
}
|
||||
|
||||
export interface IDrawingOptions {
|
||||
position?: PlacementPosition;
|
||||
textWrapping?: ITextWrapping;
|
||||
floating?: IFloating;
|
||||
readonly position?: PlacementPosition;
|
||||
readonly textWrapping?: ITextWrapping;
|
||||
readonly floating?: IFloating;
|
||||
}
|
||||
|
||||
const defaultDrawingOptions: IDrawingOptions = {
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IEffectExtentAttributes {
|
||||
b?: number;
|
||||
l?: number;
|
||||
r?: number;
|
||||
t?: number;
|
||||
readonly b?: number;
|
||||
readonly l?: number;
|
||||
readonly r?: number;
|
||||
readonly t?: number;
|
||||
}
|
||||
|
||||
export class EffectExtentAttributes extends XmlAttributeComponent<IEffectExtentAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
b: "b",
|
||||
l: "l",
|
||||
r: "r",
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IExtentAttributes {
|
||||
cx?: number;
|
||||
cy?: number;
|
||||
readonly cx?: number;
|
||||
readonly cy?: number;
|
||||
}
|
||||
|
||||
export class ExtentAttributes extends XmlAttributeComponent<IExtentAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
cx: "cx",
|
||||
cy: "cy",
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { ExtentAttributes } from "./extent-attributes";
|
||||
|
||||
export class Extent extends XmlComponent {
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { assert } from "chai";
|
||||
|
||||
import { VerticalPositionAlign } from ".";
|
||||
import { Utility } from "../../../tests/utility";
|
||||
import { Utility } from "tests/utility";
|
||||
|
||||
import { Align } from "./align";
|
||||
import { VerticalPositionAlign } from "./floating-position";
|
||||
|
||||
describe("Align", () => {
|
||||
describe("#constructor()", () => {
|
||||
|
@ -39,22 +39,22 @@ export enum VerticalPositionAlign {
|
||||
}
|
||||
|
||||
export interface IHorizontalPositionOptions {
|
||||
relative: HorizontalPositionRelativeFrom;
|
||||
align?: HorizontalPositionAlign;
|
||||
offset?: number;
|
||||
readonly relative: HorizontalPositionRelativeFrom;
|
||||
readonly align?: HorizontalPositionAlign;
|
||||
readonly offset?: number;
|
||||
}
|
||||
|
||||
export interface IVerticalPositionOptions {
|
||||
relative: VerticalPositionRelativeFrom;
|
||||
align?: VerticalPositionAlign;
|
||||
offset?: number;
|
||||
readonly relative: VerticalPositionRelativeFrom;
|
||||
readonly align?: VerticalPositionAlign;
|
||||
readonly offset?: number;
|
||||
}
|
||||
|
||||
export interface IFloating {
|
||||
horizontalPosition: IHorizontalPositionOptions;
|
||||
verticalPosition: IVerticalPositionOptions;
|
||||
allowOverlap?: boolean;
|
||||
lockAnchor?: boolean;
|
||||
behindDocument?: boolean;
|
||||
layoutInCell?: boolean;
|
||||
readonly horizontalPosition: IHorizontalPositionOptions;
|
||||
readonly verticalPosition: IVerticalPositionOptions;
|
||||
readonly allowOverlap?: boolean;
|
||||
readonly lockAnchor?: boolean;
|
||||
readonly behindDocument?: boolean;
|
||||
readonly layoutInCell?: boolean;
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { assert } from "chai";
|
||||
|
||||
import { HorizontalPositionAlign, HorizontalPositionRelativeFrom } from ".";
|
||||
import { Utility } from "../../../tests/utility";
|
||||
import { Utility } from "tests/utility";
|
||||
|
||||
import { HorizontalPositionAlign, HorizontalPositionRelativeFrom } from "./floating-position";
|
||||
import { HorizontalPosition } from "./horizontal-position";
|
||||
|
||||
describe("HorizontalPosition", () => {
|
||||
|
@ -5,11 +5,11 @@ import { HorizontalPositionRelativeFrom, IHorizontalPositionOptions } from "./fl
|
||||
import { PositionOffset } from "./position-offset";
|
||||
|
||||
interface IHorizontalPositionAttributes {
|
||||
relativeFrom: HorizontalPositionRelativeFrom;
|
||||
readonly relativeFrom: HorizontalPositionRelativeFrom;
|
||||
}
|
||||
|
||||
class HorizontalPositionAttributes extends XmlAttributeComponent<IHorizontalPositionAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
relativeFrom: "relativeFrom",
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { assert } from "chai";
|
||||
|
||||
import { Utility } from "../../../tests/utility";
|
||||
import { Utility } from "tests/utility";
|
||||
|
||||
import { PositionOffset } from "./position-offset";
|
||||
|
||||
describe("PositionOffset", () => {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { assert } from "chai";
|
||||
|
||||
import { Utility } from "../../../tests/utility";
|
||||
import { Utility } from "tests/utility";
|
||||
|
||||
import { SimplePos } from "./simple-pos";
|
||||
|
||||
describe("SimplePos", () => {
|
||||
|
@ -2,12 +2,12 @@
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
interface ISimplePosAttributes {
|
||||
x: number;
|
||||
y: number;
|
||||
readonly x: number;
|
||||
readonly y: number;
|
||||
}
|
||||
|
||||
class SimplePosAttributes extends XmlAttributeComponent<ISimplePosAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
x: "x",
|
||||
y: "y",
|
||||
};
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { assert } from "chai";
|
||||
|
||||
import { VerticalPositionAlign, VerticalPositionRelativeFrom } from ".";
|
||||
import { Utility } from "../../../tests/utility";
|
||||
import { Utility } from "tests/utility";
|
||||
|
||||
import { VerticalPositionAlign, VerticalPositionRelativeFrom } from "./floating-position";
|
||||
import { VerticalPosition } from "./vertical-position";
|
||||
|
||||
describe("VerticalPosition", () => {
|
||||
|
@ -5,11 +5,11 @@ import { IVerticalPositionOptions, VerticalPositionRelativeFrom } from "./floati
|
||||
import { PositionOffset } from "./position-offset";
|
||||
|
||||
interface IVerticalPositionAttributes {
|
||||
relativeFrom: VerticalPositionRelativeFrom;
|
||||
readonly relativeFrom: VerticalPositionRelativeFrom;
|
||||
}
|
||||
|
||||
class VerticalPositionAttributes extends XmlAttributeComponent<IVerticalPositionAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
relativeFrom: "relativeFrom",
|
||||
};
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IGraphicFrameLockAttributes {
|
||||
xmlns?: string;
|
||||
noChangeAspect?: number;
|
||||
readonly xmlns?: string;
|
||||
readonly noChangeAspect?: number;
|
||||
}
|
||||
|
||||
export class GraphicFrameLockAttributes extends XmlAttributeComponent<IGraphicFrameLockAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
xmlns: "xmlns:a",
|
||||
noChangeAspect: "noChangeAspect",
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IGraphicDataAttributes {
|
||||
uri?: string;
|
||||
readonly uri?: string;
|
||||
}
|
||||
|
||||
export class GraphicDataAttributes extends XmlAttributeComponent<IGraphicDataAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
uri: "uri",
|
||||
};
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
interface IBlipProperties {
|
||||
embed: string;
|
||||
cstate: string;
|
||||
readonly embed: string;
|
||||
readonly cstate: string;
|
||||
}
|
||||
|
||||
class BlipAttributes extends XmlAttributeComponent<IBlipProperties> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
embed: "r:embed",
|
||||
cstate: "cstate",
|
||||
};
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IPicLocksAttributes {
|
||||
noChangeAspect?: number;
|
||||
noChangeArrowheads?: number;
|
||||
readonly noChangeAspect?: number;
|
||||
readonly noChangeArrowheads?: number;
|
||||
}
|
||||
|
||||
export class PicLocksAttributes extends XmlAttributeComponent<IPicLocksAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
noChangeAspect: "noChangeAspect",
|
||||
noChangeArrowheads: "noChangeArrowheads",
|
||||
};
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface INonVisualPropertiesAttributes {
|
||||
id?: number;
|
||||
name?: string;
|
||||
descr?: string;
|
||||
readonly id?: number;
|
||||
readonly name?: string;
|
||||
readonly descr?: string;
|
||||
}
|
||||
|
||||
export class NonVisualPropertiesAttributes extends XmlAttributeComponent<INonVisualPropertiesAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
id: "id",
|
||||
name: "name",
|
||||
descr: "desc",
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IPicAttributes {
|
||||
xmlns?: string;
|
||||
readonly xmlns?: string;
|
||||
}
|
||||
|
||||
export class PicAttributes extends XmlAttributeComponent<IPicAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
xmlns: "xmlns:pic",
|
||||
};
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IExtentsAttributes {
|
||||
cx?: number;
|
||||
cy?: number;
|
||||
readonly cx?: number;
|
||||
readonly cy?: number;
|
||||
}
|
||||
|
||||
export class ExtentsAttributes extends XmlAttributeComponent<IExtentsAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
cx: "cx",
|
||||
cy: "cy",
|
||||
};
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IOffsetAttributes {
|
||||
x?: number;
|
||||
y?: number;
|
||||
readonly x?: number;
|
||||
readonly y?: number;
|
||||
}
|
||||
|
||||
export class OffsetAttributes extends XmlAttributeComponent<IOffsetAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
x: "x",
|
||||
y: "y",
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IPresetGeometryAttributes {
|
||||
prst?: string;
|
||||
readonly prst?: string;
|
||||
}
|
||||
|
||||
export class PresetGeometryAttributes extends XmlAttributeComponent<IPresetGeometryAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
prst: "prst",
|
||||
};
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IShapePropertiesAttributes {
|
||||
bwMode?: string;
|
||||
readonly bwMode?: string;
|
||||
}
|
||||
|
||||
export class ShapePropertiesAttributes extends XmlAttributeComponent<IShapePropertiesAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
bwMode: "bwMode",
|
||||
};
|
||||
}
|
||||
|
@ -2,11 +2,11 @@ import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
import { GraphicData } from "./graphic-data";
|
||||
|
||||
interface IGraphicProperties {
|
||||
a: string;
|
||||
readonly a: string;
|
||||
}
|
||||
|
||||
class GraphicAttributes extends XmlAttributeComponent<IGraphicProperties> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
a: "xmlns:a",
|
||||
};
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { IDistance } from "../drawing";
|
||||
export interface IInlineAttributes extends IDistance {}
|
||||
|
||||
export class InlineAttributes extends XmlAttributeComponent<IInlineAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
distT: "distT",
|
||||
distB: "distB",
|
||||
distL: "distL",
|
||||
|
@ -16,7 +16,7 @@ export enum WrapTextOption {
|
||||
}
|
||||
|
||||
export interface ITextWrapping {
|
||||
textWrapStyle: TextWrapStyle;
|
||||
wrapTextOption?: WrapTextOption;
|
||||
distanceFromText?: IDistance;
|
||||
readonly textWrapStyle: TextWrapStyle;
|
||||
readonly wrapTextOption?: WrapTextOption;
|
||||
readonly distanceFromText?: IDistance;
|
||||
}
|
||||
|
@ -4,11 +4,11 @@ import { ITextWrapping, WrapTextOption } from ".";
|
||||
import { IDistance } from "../drawing";
|
||||
|
||||
interface IWrapSquareAttributes extends IDistance {
|
||||
wrapText?: WrapTextOption;
|
||||
readonly wrapText?: WrapTextOption;
|
||||
}
|
||||
|
||||
class WrapSquareAttributes extends XmlAttributeComponent<IWrapSquareAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
distT: "distT",
|
||||
distB: "distB",
|
||||
distL: "distL",
|
||||
|
@ -3,25 +3,25 @@ import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
import { IDistance } from "../drawing";
|
||||
|
||||
interface IWrapTightAttributes {
|
||||
distT?: number;
|
||||
distB?: number;
|
||||
readonly distT?: number;
|
||||
readonly distB?: number;
|
||||
}
|
||||
|
||||
class WrapTightAttributes extends XmlAttributeComponent<IWrapTightAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
distT: "distT",
|
||||
distB: "distB",
|
||||
};
|
||||
}
|
||||
|
||||
export class WrapTight extends XmlComponent {
|
||||
constructor(distanceFromText?: IDistance) {
|
||||
super("wp:wrapTight");
|
||||
|
||||
distanceFromText = distanceFromText || {
|
||||
constructor(
|
||||
distanceFromText: IDistance = {
|
||||
distT: 0,
|
||||
distB: 0,
|
||||
};
|
||||
},
|
||||
) {
|
||||
super("wp:wrapTight");
|
||||
|
||||
this.root.push(
|
||||
new WrapTightAttributes({
|
||||
|
@ -3,25 +3,25 @@ import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
import { IDistance } from "../drawing";
|
||||
|
||||
interface IWrapTopAndBottomAttributes {
|
||||
distT?: number;
|
||||
distB?: number;
|
||||
readonly distT?: number;
|
||||
readonly distB?: number;
|
||||
}
|
||||
|
||||
class WrapTopAndBottomAttributes extends XmlAttributeComponent<IWrapTopAndBottomAttributes> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
distT: "distT",
|
||||
distB: "distB",
|
||||
};
|
||||
}
|
||||
|
||||
export class WrapTopAndBottom extends XmlComponent {
|
||||
constructor(distanceFromText?: IDistance) {
|
||||
super("wp:wrapTopAndBottom");
|
||||
|
||||
distanceFromText = distanceFromText || {
|
||||
constructor(
|
||||
distanceFromText: IDistance = {
|
||||
distT: 0,
|
||||
distB: 0,
|
||||
};
|
||||
},
|
||||
) {
|
||||
super("wp:wrapTopAndBottom");
|
||||
|
||||
this.root.push(
|
||||
new WrapTopAndBottomAttributes({
|
||||
|
11
src/file/file-properties.ts
Normal file
11
src/file/file-properties.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { IDocumentTemplate } from "../import-dotx";
|
||||
|
||||
export interface IFileProperties {
|
||||
readonly template?: IDocumentTemplate;
|
||||
}
|
||||
|
||||
// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export const WORKAROUND = "";
|
58
src/file/file.spec.ts
Normal file
58
src/file/file.spec.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { File } from "./file";
|
||||
|
||||
describe("File", () => {
|
||||
describe("#constructor", () => {
|
||||
it("should create with correct headers", () => {
|
||||
const doc = new File();
|
||||
const header = doc.createHeader();
|
||||
const footer = doc.createFooter();
|
||||
|
||||
doc.addSection({
|
||||
headers: {
|
||||
default: header,
|
||||
},
|
||||
footers: {
|
||||
default: footer,
|
||||
},
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(doc.Document.Body);
|
||||
|
||||
expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"][0]._attr["w:type"]).to.equal("default");
|
||||
expect(tree["w:body"][1]["w:sectPr"][5]["w:footerReference"][0]._attr["w:type"]).to.equal("default");
|
||||
});
|
||||
|
||||
it("should create with correct headers", () => {
|
||||
const doc = new File();
|
||||
const header = doc.createHeader();
|
||||
const footer = doc.createFooter();
|
||||
|
||||
doc.addSection({
|
||||
headers: {
|
||||
default: header,
|
||||
first: header,
|
||||
even: header,
|
||||
},
|
||||
footers: {
|
||||
default: footer,
|
||||
first: footer,
|
||||
even: footer,
|
||||
},
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(doc.Document.Body);
|
||||
|
||||
expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"][0]._attr["w:type"]).to.equal("default");
|
||||
expect(tree["w:body"][1]["w:sectPr"][5]["w:headerReference"][0]._attr["w:type"]).to.equal("first");
|
||||
expect(tree["w:body"][1]["w:sectPr"][6]["w:headerReference"][0]._attr["w:type"]).to.equal("even");
|
||||
|
||||
expect(tree["w:body"][1]["w:sectPr"][7]["w:footerReference"][0]._attr["w:type"]).to.equal("default");
|
||||
expect(tree["w:body"][1]["w:sectPr"][8]["w:footerReference"][0]._attr["w:type"]).to.equal("first");
|
||||
expect(tree["w:body"][1]["w:sectPr"][9]["w:footerReference"][0]._attr["w:type"]).to.equal("even");
|
||||
});
|
||||
});
|
||||
});
|
343
src/file/file.ts
343
src/file/file.ts
@ -2,10 +2,17 @@ import { AppProperties } from "./app-properties/app-properties";
|
||||
import { ContentTypes } from "./content-types/content-types";
|
||||
import { CoreProperties, IPropertiesOptions } from "./core-properties";
|
||||
import { Document } from "./document";
|
||||
import { FooterReferenceType, HeaderReference, HeaderReferenceType, SectionPropertiesOptions } from "./document/body/section-properties";
|
||||
import { FooterWrapper } from "./footer-wrapper";
|
||||
import {
|
||||
FooterReferenceType,
|
||||
HeaderReference,
|
||||
HeaderReferenceType,
|
||||
IHeaderFooterGroup,
|
||||
SectionPropertiesOptions,
|
||||
} from "./document/body/section-properties";
|
||||
import { IFileProperties } from "./file-properties";
|
||||
import { FooterWrapper, IDocumentFooter } from "./footer-wrapper";
|
||||
import { FootNotes } from "./footnotes";
|
||||
import { HeaderWrapper } from "./header-wrapper";
|
||||
import { HeaderWrapper, IDocumentHeader } from "./header-wrapper";
|
||||
import { Image, Media } from "./media";
|
||||
import { Numbering } from "./numbering";
|
||||
import { Bookmark, Hyperlink, Paragraph } from "./paragraph";
|
||||
@ -18,33 +25,53 @@ import { Table } from "./table";
|
||||
import { TableOfContents } from "./table-of-contents";
|
||||
|
||||
export class File {
|
||||
// tslint:disable-next-line:readonly-keyword
|
||||
private currentRelationshipId: number = 1;
|
||||
|
||||
private readonly document: Document;
|
||||
private styles: Styles;
|
||||
private readonly headers: IDocumentHeader[] = [];
|
||||
private readonly footers: IDocumentFooter[] = [];
|
||||
private readonly docRelationships: Relationships;
|
||||
private readonly coreProperties: CoreProperties;
|
||||
private readonly numbering: Numbering;
|
||||
private readonly media: Media;
|
||||
private readonly docRelationships: Relationships;
|
||||
private readonly fileRelationships: Relationships;
|
||||
private readonly headerWrapper: HeaderWrapper[] = [];
|
||||
private readonly footerWrapper: FooterWrapper[] = [];
|
||||
private readonly footNotes: FootNotes;
|
||||
private readonly settings: Settings;
|
||||
|
||||
private readonly contentTypes: ContentTypes;
|
||||
private readonly appProperties: AppProperties;
|
||||
// tslint:disable-next-line:readonly-keyword
|
||||
private styles: Styles;
|
||||
|
||||
private currentRelationshipId: number = 1;
|
||||
constructor(
|
||||
options: IPropertiesOptions = {
|
||||
creator: "Un-named",
|
||||
revision: "1",
|
||||
lastModifiedBy: "Un-named",
|
||||
},
|
||||
sectionPropertiesOptions: SectionPropertiesOptions = {},
|
||||
fileProperties: IFileProperties = {},
|
||||
) {
|
||||
this.coreProperties = new CoreProperties(options);
|
||||
this.numbering = new Numbering();
|
||||
this.docRelationships = new Relationships();
|
||||
this.media = new Media();
|
||||
this.fileRelationships = new Relationships();
|
||||
this.appProperties = new AppProperties();
|
||||
this.footNotes = new FootNotes();
|
||||
this.contentTypes = new ContentTypes();
|
||||
|
||||
constructor(options?: IPropertiesOptions, sectionPropertiesOptions?: SectionPropertiesOptions) {
|
||||
if (!options) {
|
||||
options = {
|
||||
creator: "Un-named",
|
||||
revision: "1",
|
||||
lastModifiedBy: "Un-named",
|
||||
};
|
||||
if (fileProperties.template) {
|
||||
this.currentRelationshipId = fileProperties.template.currentRelationshipId + 1;
|
||||
}
|
||||
|
||||
if (options.externalStyles) {
|
||||
// set up styles
|
||||
if (fileProperties.template && options.externalStyles) {
|
||||
throw Error("can not use both template and external styles");
|
||||
}
|
||||
if (fileProperties.template) {
|
||||
this.styles = fileProperties.template.styles;
|
||||
} else if (options.externalStyles) {
|
||||
const stylesFactory = new ExternalStylesFactory();
|
||||
this.styles = stylesFactory.newInstance(options.externalStyles);
|
||||
} else {
|
||||
@ -52,62 +79,31 @@ export class File {
|
||||
this.styles = stylesFactory.newInstance();
|
||||
}
|
||||
|
||||
this.coreProperties = new CoreProperties(options);
|
||||
this.numbering = new Numbering();
|
||||
this.docRelationships = new Relationships();
|
||||
this.docRelationships.createRelationship(
|
||||
this.currentRelationshipId++,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
||||
"styles.xml",
|
||||
);
|
||||
this.docRelationships.createRelationship(
|
||||
this.currentRelationshipId++,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering",
|
||||
"numbering.xml",
|
||||
);
|
||||
this.contentTypes = new ContentTypes();
|
||||
this.addDefaultRelationships();
|
||||
|
||||
this.docRelationships.createRelationship(
|
||||
this.currentRelationshipId++,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
|
||||
"footnotes.xml",
|
||||
);
|
||||
this.media = new Media();
|
||||
|
||||
const header = this.createHeader();
|
||||
const footer = this.createFooter();
|
||||
|
||||
this.fileRelationships = new Relationships();
|
||||
this.fileRelationships.createRelationship(
|
||||
1,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
||||
"word/document.xml",
|
||||
);
|
||||
this.fileRelationships.createRelationship(
|
||||
2,
|
||||
"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
|
||||
"docProps/core.xml",
|
||||
);
|
||||
this.fileRelationships.createRelationship(
|
||||
3,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties",
|
||||
"docProps/app.xml",
|
||||
);
|
||||
this.appProperties = new AppProperties();
|
||||
|
||||
this.footNotes = new FootNotes();
|
||||
if (!sectionPropertiesOptions) {
|
||||
sectionPropertiesOptions = {
|
||||
footerType: FooterReferenceType.DEFAULT,
|
||||
headerType: HeaderReferenceType.DEFAULT,
|
||||
headerId: header.Header.ReferenceId,
|
||||
footerId: footer.Footer.ReferenceId,
|
||||
};
|
||||
if (fileProperties.template && fileProperties.template.headers) {
|
||||
for (const templateHeader of fileProperties.template.headers) {
|
||||
this.addHeaderToDocument(templateHeader.header, templateHeader.type);
|
||||
}
|
||||
} else {
|
||||
sectionPropertiesOptions.headerId = header.Header.ReferenceId;
|
||||
sectionPropertiesOptions.footerId = footer.Footer.ReferenceId;
|
||||
this.createHeader();
|
||||
}
|
||||
this.document = new Document(sectionPropertiesOptions);
|
||||
|
||||
if (fileProperties.template && fileProperties.template.footers) {
|
||||
for (const templateFooter of fileProperties.template.footers) {
|
||||
this.addFooterToDocument(templateFooter.footer, templateFooter.type);
|
||||
}
|
||||
} else {
|
||||
this.createFooter();
|
||||
}
|
||||
|
||||
const newSectionPropertiesOptions = {
|
||||
...sectionPropertiesOptions,
|
||||
headers: this.groupHeaders(this.headers, sectionPropertiesOptions.headers),
|
||||
footers: this.groupFooters(this.footers, sectionPropertiesOptions.footers),
|
||||
};
|
||||
|
||||
this.document = new Document(newSectionPropertiesOptions);
|
||||
this.settings = new Settings();
|
||||
}
|
||||
|
||||
@ -115,8 +111,9 @@ export class File {
|
||||
this.document.addTableOfContents(toc);
|
||||
}
|
||||
|
||||
public addParagraph(paragraph: Paragraph): void {
|
||||
public addParagraph(paragraph: Paragraph): File {
|
||||
this.document.addParagraph(paragraph);
|
||||
return this;
|
||||
}
|
||||
|
||||
public createParagraph(text?: string): Paragraph {
|
||||
@ -144,8 +141,8 @@ export class File {
|
||||
}
|
||||
|
||||
public createHyperlink(link: string, text?: string): Hyperlink {
|
||||
text = text === undefined ? link : text;
|
||||
const hyperlink = new Hyperlink(text, this.docRelationships.RelationshipCount);
|
||||
const newText = text === undefined ? link : text;
|
||||
const hyperlink = new Hyperlink(newText, this.docRelationships.RelationshipCount);
|
||||
this.docRelationships.createRelationship(
|
||||
hyperlink.linkId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
||||
@ -156,16 +153,16 @@ export class File {
|
||||
}
|
||||
|
||||
public createInternalHyperLink(anchor: string, text?: string): Hyperlink {
|
||||
text = text === undefined ? anchor : text;
|
||||
const hyperlink = new Hyperlink(text, this.docRelationships.RelationshipCount, anchor);
|
||||
const newText = text === undefined ? anchor : text;
|
||||
const hyperlink = new Hyperlink(newText, this.docRelationships.RelationshipCount, anchor);
|
||||
// NOTE: unlike File#createHyperlink(), since the link is to an internal bookmark
|
||||
// we don't need to create a new relationship.
|
||||
return hyperlink;
|
||||
}
|
||||
|
||||
public createBookmark(name: string, text?: string): Bookmark {
|
||||
text = text === undefined ? name : text;
|
||||
const bookmark = new Bookmark(name, text, this.docRelationships.RelationshipCount);
|
||||
const newText = text === undefined ? name : text;
|
||||
const bookmark = new Bookmark(name, newText, this.docRelationships.RelationshipCount);
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
@ -179,25 +176,13 @@ export class File {
|
||||
|
||||
public createHeader(): HeaderWrapper {
|
||||
const header = new HeaderWrapper(this.media, this.currentRelationshipId++);
|
||||
this.headerWrapper.push(header);
|
||||
this.docRelationships.createRelationship(
|
||||
header.Header.ReferenceId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
|
||||
`header${this.headerWrapper.length}.xml`,
|
||||
);
|
||||
this.contentTypes.addHeader(this.headerWrapper.length);
|
||||
this.addHeaderToDocument(header);
|
||||
return header;
|
||||
}
|
||||
|
||||
public createFooter(): FooterWrapper {
|
||||
const footer = new FooterWrapper(this.media, this.currentRelationshipId++);
|
||||
this.footerWrapper.push(footer);
|
||||
this.docRelationships.createRelationship(
|
||||
footer.Footer.ReferenceId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
|
||||
`footer${this.footerWrapper.length}.xml`,
|
||||
);
|
||||
this.contentTypes.addFooter(this.footerWrapper.length);
|
||||
this.addFooterToDocument(footer);
|
||||
return footer;
|
||||
}
|
||||
|
||||
@ -214,6 +199,152 @@ export class File {
|
||||
return headerWrapper;
|
||||
}
|
||||
|
||||
public getFooterByReferenceNumber(refId: number): FooterWrapper {
|
||||
const entry = this.footers.map((item) => item.footer).find((h) => h.Footer.ReferenceId === refId);
|
||||
if (entry) {
|
||||
return entry;
|
||||
}
|
||||
throw new Error(`There is no footer with given reference id ${refId}`);
|
||||
}
|
||||
|
||||
public getHeaderByReferenceNumber(refId: number): HeaderWrapper {
|
||||
const entry = this.headers.map((item) => item.header).find((h) => h.Header.ReferenceId === refId);
|
||||
if (entry) {
|
||||
return entry;
|
||||
}
|
||||
throw new Error(`There is no header with given reference id ${refId}`);
|
||||
}
|
||||
|
||||
public verifyUpdateFields(): void {
|
||||
if (this.document.getTablesOfContents().length) {
|
||||
this.settings.addUpdateFields();
|
||||
}
|
||||
}
|
||||
|
||||
private addHeaderToDocument(header: HeaderWrapper, type: HeaderReferenceType = HeaderReferenceType.DEFAULT): void {
|
||||
this.headers.push({ header, type });
|
||||
this.docRelationships.createRelationship(
|
||||
header.Header.ReferenceId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
|
||||
`header${this.headers.length}.xml`,
|
||||
);
|
||||
this.contentTypes.addHeader(this.headers.length);
|
||||
}
|
||||
|
||||
private addFooterToDocument(footer: FooterWrapper, type: FooterReferenceType = FooterReferenceType.DEFAULT): void {
|
||||
this.footers.push({ footer, type });
|
||||
this.docRelationships.createRelationship(
|
||||
footer.Footer.ReferenceId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
|
||||
`footer${this.footers.length}.xml`,
|
||||
);
|
||||
this.contentTypes.addFooter(this.footers.length);
|
||||
}
|
||||
|
||||
private addDefaultRelationships(): void {
|
||||
this.fileRelationships.createRelationship(
|
||||
1,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
||||
"word/document.xml",
|
||||
);
|
||||
this.fileRelationships.createRelationship(
|
||||
2,
|
||||
"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
|
||||
"docProps/core.xml",
|
||||
);
|
||||
this.fileRelationships.createRelationship(
|
||||
3,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties",
|
||||
"docProps/app.xml",
|
||||
);
|
||||
|
||||
this.docRelationships.createRelationship(
|
||||
this.currentRelationshipId++,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
||||
"styles.xml",
|
||||
);
|
||||
this.docRelationships.createRelationship(
|
||||
this.currentRelationshipId++,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering",
|
||||
"numbering.xml",
|
||||
);
|
||||
this.docRelationships.createRelationship(
|
||||
this.currentRelationshipId++,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
|
||||
"footnotes.xml",
|
||||
);
|
||||
}
|
||||
|
||||
private groupHeaders(headers: IDocumentHeader[], group: IHeaderFooterGroup<HeaderWrapper> = {}): IHeaderFooterGroup<HeaderWrapper> {
|
||||
let newGroup = group;
|
||||
|
||||
for (const header of headers) {
|
||||
switch (header.type) {
|
||||
case HeaderReferenceType.DEFAULT:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
default: header.header,
|
||||
};
|
||||
break;
|
||||
case HeaderReferenceType.FIRST:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
first: header.header,
|
||||
};
|
||||
break;
|
||||
case HeaderReferenceType.EVEN:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
even: header.header,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
default: header.header,
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return newGroup;
|
||||
}
|
||||
|
||||
private groupFooters(footers: IDocumentFooter[], group: IHeaderFooterGroup<FooterWrapper> = {}): IHeaderFooterGroup<FooterWrapper> {
|
||||
let newGroup = group;
|
||||
|
||||
for (const footer of footers) {
|
||||
switch (footer.type) {
|
||||
case FooterReferenceType.DEFAULT:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
default: footer.footer,
|
||||
};
|
||||
break;
|
||||
case FooterReferenceType.FIRST:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
first: footer.footer,
|
||||
};
|
||||
break;
|
||||
case FooterReferenceType.EVEN:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
even: footer.footer,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
newGroup = {
|
||||
...newGroup,
|
||||
default: footer.footer,
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return newGroup;
|
||||
}
|
||||
|
||||
public get Document(): Document {
|
||||
return this.document;
|
||||
}
|
||||
@ -247,35 +378,19 @@ export class File {
|
||||
}
|
||||
|
||||
public get Header(): HeaderWrapper {
|
||||
return this.headerWrapper[0];
|
||||
return this.headers[0].header;
|
||||
}
|
||||
|
||||
public get Headers(): HeaderWrapper[] {
|
||||
return this.headerWrapper;
|
||||
}
|
||||
|
||||
public HeaderByRefNumber(refId: number): HeaderWrapper {
|
||||
const entry = this.headerWrapper.find((h) => h.Header.ReferenceId === refId);
|
||||
if (entry) {
|
||||
return entry;
|
||||
}
|
||||
throw new Error(`There is no header with given reference id ${refId}`);
|
||||
return this.headers.map((item) => item.header);
|
||||
}
|
||||
|
||||
public get Footer(): FooterWrapper {
|
||||
return this.footerWrapper[0];
|
||||
return this.footers[0].footer;
|
||||
}
|
||||
|
||||
public get Footers(): FooterWrapper[] {
|
||||
return this.footerWrapper;
|
||||
}
|
||||
|
||||
public FooterByRefNumber(refId: number): FooterWrapper {
|
||||
const entry = this.footerWrapper.find((h) => h.Footer.ReferenceId === refId);
|
||||
if (entry) {
|
||||
return entry;
|
||||
}
|
||||
throw new Error(`There is no footer with given reference id ${refId}`);
|
||||
return this.footers.map((item) => item.footer);
|
||||
}
|
||||
|
||||
public get ContentTypes(): ContentTypes {
|
||||
@ -293,10 +408,4 @@ export class File {
|
||||
public get Settings(): Settings {
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
public verifyUpdateFields(): void {
|
||||
if (this.document.getTablesOfContents().length) {
|
||||
this.settings.addUpdateFields();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,23 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { FooterReferenceType } from "./document";
|
||||
import { Footer } from "./footer/footer";
|
||||
import { Image, Media } from "./media";
|
||||
import { Image, IMediaData, Media } from "./media";
|
||||
import { ImageParagraph, Paragraph } from "./paragraph";
|
||||
import { Relationships } from "./relationships";
|
||||
import { Table } from "./table";
|
||||
|
||||
export interface IDocumentFooter {
|
||||
readonly footer: FooterWrapper;
|
||||
readonly type: FooterReferenceType;
|
||||
}
|
||||
|
||||
export class FooterWrapper {
|
||||
private readonly footer: Footer;
|
||||
private readonly relationships: Relationships;
|
||||
|
||||
constructor(private readonly media: Media, referenceId: number) {
|
||||
this.footer = new Footer(referenceId);
|
||||
constructor(private readonly media: Media, referenceId: number, initContent?: XmlComponent) {
|
||||
this.footer = new Footer(referenceId, initContent);
|
||||
this.relationships = new Relationships();
|
||||
}
|
||||
|
||||
@ -32,17 +39,33 @@ export class FooterWrapper {
|
||||
return this.footer.createTable(rows, cols);
|
||||
}
|
||||
|
||||
public addChildElement(childElement: XmlComponent | string): void {
|
||||
public addChildElement(childElement: XmlComponent): void {
|
||||
this.footer.addChildElement(childElement);
|
||||
}
|
||||
|
||||
public createImage(image: Buffer, width?: number, height?: number): void {
|
||||
const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount, width, height);
|
||||
public addImageRelationship(image: Buffer, refId: number, width?: number, height?: number): IMediaData {
|
||||
const mediaData = this.media.addMedia(image, refId, width, height);
|
||||
this.relationships.createRelationship(
|
||||
mediaData.referenceId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
`media/${mediaData.fileName}`,
|
||||
);
|
||||
return mediaData;
|
||||
}
|
||||
|
||||
public addHyperlinkRelationship(target: string, refId: number, targetMode?: "External" | undefined): void {
|
||||
this.relationships.createRelationship(
|
||||
refId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
||||
target,
|
||||
targetMode,
|
||||
);
|
||||
}
|
||||
|
||||
public createImage(image: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): void {
|
||||
// TODO
|
||||
// tslint:disable-next-line:no-any
|
||||
const mediaData = this.addImageRelationship(image as any, this.relationships.RelationshipCount, width, height);
|
||||
this.addImage(new Image(new ImageParagraph(mediaData)));
|
||||
}
|
||||
|
||||
@ -58,4 +81,8 @@ export class FooterWrapper {
|
||||
public get Relationships(): Relationships {
|
||||
return this.relationships;
|
||||
}
|
||||
|
||||
public get Media(): Media {
|
||||
return this.media;
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,32 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IFooterAttributesProperties {
|
||||
wpc?: string;
|
||||
mc?: string;
|
||||
o?: string;
|
||||
r?: string;
|
||||
m?: string;
|
||||
v?: string;
|
||||
wp14?: string;
|
||||
wp?: string;
|
||||
w10?: string;
|
||||
w?: string;
|
||||
w14?: string;
|
||||
w15?: string;
|
||||
wpg?: string;
|
||||
wpi?: string;
|
||||
wne?: string;
|
||||
wps?: string;
|
||||
cp?: string;
|
||||
dc?: string;
|
||||
dcterms?: string;
|
||||
dcmitype?: string;
|
||||
xsi?: string;
|
||||
type?: string;
|
||||
readonly wpc?: string;
|
||||
readonly mc?: string;
|
||||
readonly o?: string;
|
||||
readonly r?: string;
|
||||
readonly m?: string;
|
||||
readonly v?: string;
|
||||
readonly wp14?: string;
|
||||
readonly wp?: string;
|
||||
readonly w10?: string;
|
||||
readonly w?: string;
|
||||
readonly w14?: string;
|
||||
readonly w15?: string;
|
||||
readonly wpg?: string;
|
||||
readonly wpi?: string;
|
||||
readonly wne?: string;
|
||||
readonly wps?: string;
|
||||
readonly cp?: string;
|
||||
readonly dc?: string;
|
||||
readonly dcterms?: string;
|
||||
readonly dcmitype?: string;
|
||||
readonly xsi?: string;
|
||||
readonly type?: string;
|
||||
}
|
||||
|
||||
export class FooterAttributes extends XmlAttributeComponent<IFooterAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
wpc: "xmlns:wpc",
|
||||
mc: "xmlns:mc",
|
||||
o: "xmlns:o",
|
||||
|
@ -1,14 +1,14 @@
|
||||
// http://officeopenxml.com/WPfooters.php
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { InitializableXmlComponent, XmlComponent } from "file/xml-components";
|
||||
import { Paragraph } from "../paragraph";
|
||||
import { Table } from "../table";
|
||||
import { FooterAttributes } from "./footer-attributes";
|
||||
|
||||
export class Footer extends XmlComponent {
|
||||
export class Footer extends InitializableXmlComponent {
|
||||
private readonly refId: number;
|
||||
|
||||
constructor(referenceNumber: number) {
|
||||
super("w:ftr");
|
||||
constructor(referenceNumber: number, initContent?: XmlComponent) {
|
||||
super("w:ftr", initContent);
|
||||
this.refId = referenceNumber;
|
||||
this.root.push(
|
||||
new FooterAttributes({
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IFootnoteAttributesProperties {
|
||||
type?: string;
|
||||
id: number;
|
||||
readonly type?: string;
|
||||
readonly id: number;
|
||||
}
|
||||
|
||||
export class FootnoteAttributes extends XmlAttributeComponent<IFootnoteAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
type: "w:type",
|
||||
id: "w:id",
|
||||
};
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { expect } from "chai";
|
||||
import { Formatter } from "../../../export/formatter";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { Footnote, FootnoteType } from "./footnote";
|
||||
|
||||
describe("Footnote", () => {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Paragraph } from "file/paragraph";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { Paragraph } from "../../paragraph";
|
||||
|
||||
import { FootnoteAttributes } from "./footnote-attributes";
|
||||
import { FootnoteRefRun } from "./run/footnote-ref-run";
|
||||
|
||||
|
@ -3,11 +3,11 @@ import { Style } from "file/paragraph/run/style";
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
export interface IFootNoteReferenceRunAttributesProperties {
|
||||
id: number;
|
||||
readonly id: number;
|
||||
}
|
||||
|
||||
export class FootNoteReferenceRunAttributes extends XmlAttributeComponent<IFootNoteReferenceRunAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
id: "w:id",
|
||||
};
|
||||
}
|
||||
|
@ -1,27 +1,27 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IFootnotesAttributesProperties {
|
||||
wpc?: string;
|
||||
mc?: string;
|
||||
o?: string;
|
||||
r?: string;
|
||||
m?: string;
|
||||
v?: string;
|
||||
wp14?: string;
|
||||
wp?: string;
|
||||
w10?: string;
|
||||
w?: string;
|
||||
w14?: string;
|
||||
w15?: string;
|
||||
wpg?: string;
|
||||
wpi?: string;
|
||||
wne?: string;
|
||||
wps?: string;
|
||||
Ignorable?: string;
|
||||
readonly wpc?: string;
|
||||
readonly mc?: string;
|
||||
readonly o?: string;
|
||||
readonly r?: string;
|
||||
readonly m?: string;
|
||||
readonly v?: string;
|
||||
readonly wp14?: string;
|
||||
readonly wp?: string;
|
||||
readonly w10?: string;
|
||||
readonly w?: string;
|
||||
readonly w14?: string;
|
||||
readonly w15?: string;
|
||||
readonly wpg?: string;
|
||||
readonly wpi?: string;
|
||||
readonly wne?: string;
|
||||
readonly wps?: string;
|
||||
readonly Ignorable?: string;
|
||||
}
|
||||
|
||||
export class FootnotesAttributes extends XmlAttributeComponent<IFootnotesAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
wpc: "xmlns:wpc",
|
||||
mc: "xmlns:mc",
|
||||
o: "xmlns:o",
|
||||
|
@ -6,12 +6,13 @@ import { SeperatorRun } from "./footnote/run/seperator-run";
|
||||
import { FootnotesAttributes } from "./footnotes-attributes";
|
||||
|
||||
export class FootNotes extends XmlComponent {
|
||||
private counter: number;
|
||||
// tslint:disable-next-line:readonly-keyword
|
||||
private currentId: number;
|
||||
|
||||
constructor() {
|
||||
super("w:footnotes");
|
||||
|
||||
this.counter = 1;
|
||||
this.currentId = 1;
|
||||
|
||||
this.root.push(
|
||||
new FootnotesAttributes({
|
||||
@ -61,10 +62,10 @@ export class FootNotes extends XmlComponent {
|
||||
}
|
||||
|
||||
public createFootNote(paragraph: Paragraph): void {
|
||||
const footnote = new Footnote(this.counter);
|
||||
const footnote = new Footnote(this.currentId);
|
||||
footnote.addParagraph(paragraph);
|
||||
this.root.push(footnote);
|
||||
|
||||
this.counter++;
|
||||
this.currentId++;
|
||||
}
|
||||
}
|
||||
|
29
src/file/header-wrapper.spec.ts
Normal file
29
src/file/header-wrapper.spec.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { expect } from "chai";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import { HeaderWrapper } from "./header-wrapper";
|
||||
import { Media } from "./media";
|
||||
import { Paragraph } from "./paragraph";
|
||||
import { Table } from "./table";
|
||||
|
||||
describe("HeaderWrapper", () => {
|
||||
describe("#addParagraph", () => {
|
||||
it("should call the underlying header's addParagraph", () => {
|
||||
const file = new HeaderWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Header, "addParagraph");
|
||||
file.addParagraph(new Paragraph());
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addTable", () => {
|
||||
it("should call the underlying header's addParagraph", () => {
|
||||
const file = new HeaderWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Header, "addTable");
|
||||
file.addTable(new Table(1, 1));
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,16 +1,23 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { HeaderReferenceType } from "./document";
|
||||
import { Header } from "./header/header";
|
||||
import { Image, Media } from "./media";
|
||||
import { Image, IMediaData, Media } from "./media";
|
||||
import { ImageParagraph, Paragraph } from "./paragraph";
|
||||
import { Relationships } from "./relationships";
|
||||
import { Table } from "./table";
|
||||
|
||||
export interface IDocumentHeader {
|
||||
readonly header: HeaderWrapper;
|
||||
readonly type: HeaderReferenceType;
|
||||
}
|
||||
|
||||
export class HeaderWrapper {
|
||||
private readonly header: Header;
|
||||
private readonly relationships: Relationships;
|
||||
|
||||
constructor(private readonly media: Media, referenceId: number) {
|
||||
this.header = new Header(referenceId);
|
||||
constructor(private readonly media: Media, referenceId: number, initContent?: XmlComponent) {
|
||||
this.header = new Header(referenceId, initContent);
|
||||
this.relationships = new Relationships();
|
||||
}
|
||||
|
||||
@ -36,13 +43,29 @@ export class HeaderWrapper {
|
||||
this.header.addChildElement(childElement);
|
||||
}
|
||||
|
||||
public createImage(image: Buffer, width?: number, height?: number): void {
|
||||
const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount, width, height);
|
||||
public addImageRelationship(image: Buffer, refId: number, width?: number, height?: number): IMediaData {
|
||||
const mediaData = this.media.addMedia(image, refId, width, height);
|
||||
this.relationships.createRelationship(
|
||||
mediaData.referenceId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
`media/${mediaData.fileName}`,
|
||||
);
|
||||
return mediaData;
|
||||
}
|
||||
|
||||
public addHyperlinkRelationship(target: string, refId: number, targetMode?: "External" | undefined): void {
|
||||
this.relationships.createRelationship(
|
||||
refId,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
||||
target,
|
||||
targetMode,
|
||||
);
|
||||
}
|
||||
|
||||
public createImage(image: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): void {
|
||||
// TODO
|
||||
// tslint:disable-next-line:no-any
|
||||
const mediaData = this.addImageRelationship(image as any, this.relationships.RelationshipCount, width, height);
|
||||
this.addImage(new Image(new ImageParagraph(mediaData)));
|
||||
}
|
||||
|
||||
@ -58,4 +81,8 @@ export class HeaderWrapper {
|
||||
public get Relationships(): Relationships {
|
||||
return this.relationships;
|
||||
}
|
||||
|
||||
public get Media(): Media {
|
||||
return this.media;
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,43 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IHeaderAttributesProperties {
|
||||
wpc?: string;
|
||||
mc?: string;
|
||||
o?: string;
|
||||
r?: string;
|
||||
m?: string;
|
||||
v?: string;
|
||||
wp14?: string;
|
||||
wp?: string;
|
||||
w10?: string;
|
||||
w?: string;
|
||||
w14?: string;
|
||||
w15?: string;
|
||||
wpg?: string;
|
||||
wpi?: string;
|
||||
wne?: string;
|
||||
wps?: string;
|
||||
cp?: string;
|
||||
dc?: string;
|
||||
dcterms?: string;
|
||||
dcmitype?: string;
|
||||
xsi?: string;
|
||||
type?: string;
|
||||
readonly wpc?: string;
|
||||
readonly mc?: string;
|
||||
readonly o?: string;
|
||||
readonly r?: string;
|
||||
readonly m?: string;
|
||||
readonly v?: string;
|
||||
readonly wp14?: string;
|
||||
readonly wp?: string;
|
||||
readonly w10?: string;
|
||||
readonly w?: string;
|
||||
readonly w14?: string;
|
||||
readonly w15?: string;
|
||||
readonly wpg?: string;
|
||||
readonly wpi?: string;
|
||||
readonly wne?: string;
|
||||
readonly wps?: string;
|
||||
readonly cp?: string;
|
||||
readonly dc?: string;
|
||||
readonly dcterms?: string;
|
||||
readonly dcmitype?: string;
|
||||
readonly xsi?: string;
|
||||
readonly type?: string;
|
||||
readonly cx?: string;
|
||||
readonly cx1?: string;
|
||||
readonly cx2?: string;
|
||||
readonly cx3?: string;
|
||||
readonly cx4?: string;
|
||||
readonly cx5?: string;
|
||||
readonly cx6?: string;
|
||||
readonly cx7?: string;
|
||||
readonly cx8?: string;
|
||||
readonly w16cid: string;
|
||||
readonly w16se: string;
|
||||
}
|
||||
|
||||
export class HeaderAttributes extends XmlAttributeComponent<IHeaderAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
protected readonly xmlKeys = {
|
||||
wpc: "xmlns:wpc",
|
||||
mc: "xmlns:mc",
|
||||
o: "xmlns:o",
|
||||
@ -49,5 +60,16 @@ export class HeaderAttributes extends XmlAttributeComponent<IHeaderAttributesPro
|
||||
dcmitype: "xmlns:dcmitype",
|
||||
xsi: "xmlns:xsi",
|
||||
type: "xsi:type",
|
||||
cx: "xmlns:cx",
|
||||
cx1: "xmlns:cx1",
|
||||
cx2: "xmlns:cx2",
|
||||
cx3: "xmlns:cx3",
|
||||
cx4: "xmlns:cx4",
|
||||
cx5: "xmlns:cx5",
|
||||
cx6: "xmlns:cx6",
|
||||
cx7: "xmlns:cx7",
|
||||
cx8: "xmlns:cx8",
|
||||
w16cid: "xmlns:w16cid",
|
||||
w16se: "xmlns:w16se",
|
||||
};
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
// http://officeopenxml.com/WPheaders.php
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { InitializableXmlComponent, XmlComponent } from "file/xml-components";
|
||||
import { Paragraph } from "../paragraph";
|
||||
import { Table } from "../table";
|
||||
import { HeaderAttributes } from "./header-attributes";
|
||||
|
||||
export class Header extends XmlComponent {
|
||||
export class Header extends InitializableXmlComponent {
|
||||
private readonly refId: number;
|
||||
|
||||
constructor(referenceNumber: number) {
|
||||
super("w:hdr");
|
||||
constructor(referenceNumber: number, initContent?: XmlComponent) {
|
||||
super("w:hdr", initContent);
|
||||
|
||||
this.refId = referenceNumber;
|
||||
|
||||
this.root.push(
|
||||
new HeaderAttributes({
|
||||
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
||||
@ -29,6 +30,17 @@ export class Header extends XmlComponent {
|
||||
wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
|
||||
wne: "http://schemas.microsoft.com/office/word/2006/wordml",
|
||||
wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
||||
cx: "http://schemas.microsoft.com/office/drawing/2014/chartex",
|
||||
cx1: "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex",
|
||||
cx2: "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
|
||||
cx3: "http://schemas.microsoft.com/office/drawing/2016/5/9/chartex",
|
||||
cx4: "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex",
|
||||
cx5: "http://schemas.microsoft.com/office/drawing/2016/5/11/chartex",
|
||||
cx6: "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex",
|
||||
cx7: "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex",
|
||||
cx8: "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex",
|
||||
w16cid: "http://schemas.microsoft.com/office/word/2016/wordml/cid",
|
||||
w16se: "http://schemas.microsoft.com/office/word/2015/wordml/symex",
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
export * from "./paragraph";
|
||||
export * from "./table";
|
||||
export * from "./file";
|
||||
export * from "./file-properties";
|
||||
export * from "./numbering";
|
||||
export * from "./media";
|
||||
export * from "./drawing";
|
||||
|
@ -1,20 +1,20 @@
|
||||
export interface IMediaDataDimensions {
|
||||
pixels: {
|
||||
x: number;
|
||||
y: number;
|
||||
readonly pixels: {
|
||||
readonly x: number;
|
||||
readonly y: number;
|
||||
};
|
||||
emus: {
|
||||
x: number;
|
||||
y: number;
|
||||
readonly emus: {
|
||||
readonly x: number;
|
||||
readonly y: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IMediaData {
|
||||
referenceId: number;
|
||||
stream: Buffer | Uint8Array | ArrayBuffer;
|
||||
path?: string;
|
||||
fileName: string;
|
||||
dimensions: IMediaDataDimensions;
|
||||
readonly referenceId: number;
|
||||
readonly stream: Buffer | Uint8Array | ArrayBuffer;
|
||||
readonly path?: string;
|
||||
readonly fileName: string;
|
||||
readonly dimensions: IMediaDataDimensions;
|
||||
}
|
||||
|
||||
// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432
|
||||
|
140
src/file/media/media.spec.ts
Normal file
140
src/file/media/media.spec.ts
Normal file
@ -0,0 +1,140 @@
|
||||
// tslint:disable:object-literal-key-quotes
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { File } from "../file";
|
||||
import { Media } from "./media";
|
||||
|
||||
describe("Media", () => {
|
||||
describe("#addImage", () => {
|
||||
it("should add image", () => {
|
||||
const file = new File();
|
||||
const image = Media.addImage(file, "");
|
||||
|
||||
let tree = new Formatter().format(image.Paragraph);
|
||||
expect(tree["w:p"]).to.be.an.instanceof(Array);
|
||||
|
||||
tree = new Formatter().format(image.Run);
|
||||
expect(tree["w:r"]).to.be.an.instanceof(Array);
|
||||
});
|
||||
|
||||
it("should ensure the correct relationship id is used when adding image", () => {
|
||||
const file = new File();
|
||||
const image1 = Media.addImage(file, "test");
|
||||
|
||||
const tree = new Formatter().format(image1.Paragraph);
|
||||
const inlineElements = tree["w:p"][1]["w:r"][1]["w:drawing"][0]["wp:inline"];
|
||||
const graphicData = inlineElements.find((x) => x["a:graphic"]);
|
||||
|
||||
expect(graphicData["a:graphic"][1]["a:graphicData"][1]["pic:pic"][2]["pic:blipFill"][0]["a:blip"][0]).to.deep.equal({
|
||||
_attr: {
|
||||
"r:embed": `rId${file.DocumentRelationships.RelationshipCount}`,
|
||||
cstate: "none",
|
||||
},
|
||||
});
|
||||
|
||||
const image2 = Media.addImage(file, "test");
|
||||
const tree2 = new Formatter().format(image2.Paragraph);
|
||||
const inlineElements2 = tree2["w:p"][1]["w:r"][1]["w:drawing"][0]["wp:inline"];
|
||||
const graphicData2 = inlineElements2.find((x) => x["a:graphic"]);
|
||||
|
||||
expect(graphicData2["a:graphic"][1]["a:graphicData"][1]["pic:pic"][2]["pic:blipFill"][0]["a:blip"][0]).to.deep.equal({
|
||||
_attr: {
|
||||
"r:embed": `rId${file.DocumentRelationships.RelationshipCount}`,
|
||||
cstate: "none",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addMedia", () => {
|
||||
it("should add media", () => {
|
||||
// tslint:disable-next-line:no-any
|
||||
(Media as any).generateId = () => "test";
|
||||
|
||||
const image = new Media().addMedia("", 1);
|
||||
expect(image.fileName).to.equal("test.png");
|
||||
expect(image.referenceId).to.equal(1);
|
||||
expect(image.dimensions).to.deep.equal({
|
||||
pixels: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
},
|
||||
emus: {
|
||||
x: 952500,
|
||||
y: 952500,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should return UInt8Array if atob is present", () => {
|
||||
// tslint:disable-next-line
|
||||
((process as any).atob as any) = () => "atob result";
|
||||
// tslint:disable-next-line:no-any
|
||||
(Media as any).generateId = () => "test";
|
||||
|
||||
const image = new Media().addMedia("", 1);
|
||||
expect(image.stream).to.be.an.instanceof(Uint8Array);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getMedia", () => {
|
||||
it("should get media", () => {
|
||||
// tslint:disable-next-line:no-any
|
||||
(Media as any).generateId = () => "test";
|
||||
|
||||
const media = new Media();
|
||||
media.addMedia("", 1);
|
||||
|
||||
const image = media.getMedia("test.png");
|
||||
|
||||
expect(image.fileName).to.equal("test.png");
|
||||
expect(image.referenceId).to.equal(1);
|
||||
expect(image.dimensions).to.deep.equal({
|
||||
pixels: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
},
|
||||
emus: {
|
||||
x: 952500,
|
||||
y: 952500,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("Get media", () => {
|
||||
const media = new Media();
|
||||
|
||||
expect(() => media.getMedia("test.png")).to.throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe("#Array", () => {
|
||||
it("Get images as array", () => {
|
||||
// tslint:disable-next-line:no-any
|
||||
(Media as any).generateId = () => "test";
|
||||
|
||||
const media = new Media();
|
||||
media.addMedia("", 1);
|
||||
|
||||
const array = media.Array;
|
||||
expect(array).to.be.an.instanceof(Array);
|
||||
expect(array.length).to.equal(1);
|
||||
|
||||
const image = array[0];
|
||||
expect(image.fileName).to.equal("test.png");
|
||||
expect(image.referenceId).to.equal(1);
|
||||
expect(image.dimensions).to.deep.equal({
|
||||
pixels: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
},
|
||||
emus: {
|
||||
x: 952500,
|
||||
y: 952500,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user