Compare commits
130 Commits
Author | SHA1 | Date | |
---|---|---|---|
cb8e9fdd36 | |||
53ce3c1526 | |||
a78f06b41b | |||
ce7ef30806 | |||
fd93c0776a | |||
6a762c6c0e | |||
23c5aef276 | |||
69707a7207 | |||
cfd3505414 | |||
ee958dc351 | |||
680f2325a3 | |||
2f0ad3eeb2 | |||
c895a9c7d9 | |||
388a8404f5 | |||
320cb1c418 | |||
8a6b73915f | |||
774355d608 | |||
35dbce3a68 | |||
6d0a267ba4 | |||
df701be572 | |||
1edad47e6e | |||
c873abfe18 | |||
989446ef36 | |||
fc6daed620 | |||
5e921f1dfc | |||
45bbf1b693 | |||
8b8c664f0f | |||
079334f71b | |||
3fe0c76d54 | |||
1e55a3e6a8 | |||
950a2f8b53 | |||
bbe2e1e46e | |||
635c58c131 | |||
ba39d806b7 | |||
355e97cb5e | |||
4339f8cfc0 | |||
448572d7a1 | |||
2132c7b6da | |||
d0bd83d6c5 | |||
1037b7c23d | |||
df197f73ea | |||
ea08f603f5 | |||
fa2f1235f7 | |||
ff5d02c964 | |||
e93d6799fd | |||
f2027230a0 | |||
cb47d4f772 | |||
eebc9fbcfa | |||
0e698491f3 | |||
ae52e8fabb | |||
ccd655ef8b | |||
3dc6e71aaf | |||
e6d3577f74 | |||
ef05024f2f | |||
d3bc784248 | |||
979701331e | |||
76b1682296 | |||
7c31b72f99 | |||
f7c2072cff | |||
392db1cd11 | |||
ca244bcfe1 | |||
2d02f51f25 | |||
592fb5ca9f | |||
a3945bc7f1 | |||
2adde9830c | |||
0355afe11c | |||
998fe3f370 | |||
eb71fc20e6 | |||
ab348bd5f9 | |||
c518d1c6c7 | |||
d8d16b4a7d | |||
928e4b9bc2 | |||
4f900d6566 | |||
362a997187 | |||
89c658746d | |||
75d699f7ef | |||
f7412690b6 | |||
ece2b23407 | |||
f84af9a44b | |||
90dc1103f6 | |||
c469fb24db | |||
32be6e36da | |||
b34ad22ad6 | |||
2358139a6b | |||
43ebfe7a2f | |||
0b8094dea8 | |||
f1d1570b10 | |||
e9e68bc802 | |||
4c369510ce | |||
96413d6c47 | |||
df6c7cf19f | |||
49fc28d86c | |||
cc67a83ce8 | |||
742e2b5089 | |||
d19ff1e300 | |||
01950ed443 | |||
cf1689a3c2 | |||
66d0bab0a5 | |||
5889f20f1e | |||
ebec10e312 | |||
ae8a0c7fd0 | |||
bb49200fad | |||
a6bba0bc6c | |||
fb08f79344 | |||
741c74825e | |||
28539cd47b | |||
8ca7c5a343 | |||
32b56e7071 | |||
7dad717952 | |||
34e928755f | |||
51e0f311fe | |||
cc9dff6b94 | |||
101cc0fdea | |||
d408262fa8 | |||
ed53c30f42 | |||
ebbf6a99c1 | |||
b98c103e45 | |||
518fec0595 | |||
659936f3f0 | |||
b0febf5054 | |||
56b951a2b1 | |||
a3a9958a69 | |||
4f36bbf426 | |||
357bc7f377 | |||
da8405b5b9 | |||
492face7ab | |||
b6351f0260 | |||
3a7f9053b9 | |||
19b122684c | |||
72e89cfc3c |
13
.editorconfig
Normal file
13
.editorconfig
Normal file
@ -0,0 +1,13 @@
|
||||
# Editor configuration, see http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
12
.gitignore
vendored
12
.gitignore
vendored
@ -36,5 +36,13 @@ node_modules
|
||||
build
|
||||
build-tests
|
||||
|
||||
# vscode
|
||||
.vscode
|
||||
# VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history
|
||||
|
||||
# Lock files
|
||||
package-lock.json
|
||||
|
14
.travis.yml
14
.travis.yml
@ -4,12 +4,22 @@ node_js:
|
||||
install:
|
||||
- npm install
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
after_failure:
|
||||
- npm run style
|
||||
- npm run build
|
||||
- node ./demo/demo1.js
|
||||
- node ./demo/demo2.js
|
||||
- node ./demo/demo3.js
|
||||
- node ./demo/demo4.js
|
||||
- node ./demo/demo5.js
|
||||
- node ./demo/demo6.js
|
||||
- node ./demo/demo7.js
|
||||
after_failure:
|
||||
- "cat /home/travis/builds/dolanmiu/docx/npm-debug.log"
|
||||
after_success:
|
||||
- bash ./deploy-docs.sh
|
||||
env:
|
||||
global:
|
||||
- ENCRYPTION_LABEL: "ad385fa3b525"
|
||||
|
||||
|
||||
|
9
.vscode/settings.json
vendored
Normal file
9
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"cSpell.words": ["clippy", "docx", "dolan", "miu", "officegen", "typedoc"],
|
||||
"prettier.trailingComma": "all",
|
||||
"prettier.printWidth": 140,
|
||||
"editor.formatOnSave": false,
|
||||
"prettier.tabWidth": 4,
|
||||
"prettier.arrowParens": "always",
|
||||
"prettier.bracketSpacing": true
|
||||
}
|
16
README.md
16
README.md
@ -8,7 +8,12 @@
|
||||
|
||||
-----
|
||||
|
||||
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][gemnasium-image]][gemnasium-url] [![Known Vulnerabilities][snky-image]][snky-url] [![Chat on Gitter][gitter-image]][gitter-url]
|
||||
[![NPM version][npm-image]][npm-url]
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[![Dependency Status][gemnasium-image]][gemnasium-url]
|
||||
[![Known Vulnerabilities][snky-image]][snky-url]
|
||||
[![Chat on Gitter][gitter-image]][gitter-url]
|
||||
[![code style: prettier][prettier-image]][prettier-url]
|
||||
|
||||
[](https://nodei.co/npm/docx/)
|
||||
|
||||
@ -57,6 +62,8 @@ var exporter = new docx.LocalPacker(doc);
|
||||
var exporter = new docx.ExpressPacker(doc, res);
|
||||
|
||||
exporter.pack('My First Document');
|
||||
// If you want to export it as a .pdf file instead
|
||||
exporter.packPdf('My First Document');
|
||||
|
||||
// done! A file called 'My First Document.docx'
|
||||
// will be in your file system if you used LocalPacker
|
||||
@ -66,6 +73,10 @@ exporter.pack('My First Document');
|
||||
## Examples
|
||||
Check [the Wiki](https://github.com/dolanmiu/docx/wiki/Examples) for examples.
|
||||
|
||||
# Contributing
|
||||
|
||||
Read the contribution guidelines [here](https://github.com/dolanmiu/docx/wiki/Contributing-Guidelines).
|
||||
|
||||
-----
|
||||
|
||||
Made with 💖
|
||||
@ -84,4 +95,5 @@ Huge thanks to [@felipeochoa](https://github.com/felipeochoa) for awesome contri
|
||||
[gitter-url]: https://gitter.im/docx-lib/Lobby
|
||||
[gemnasium-image]: https://gemnasium.com/badges/github.com/dolanmiu/docx.svg
|
||||
[gemnasium-url]: https://gemnasium.com/github.com/dolanmiu/docx
|
||||
|
||||
[prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg
|
||||
[prettier-url]: https://github.com/prettier/prettier
|
||||
|
@ -10,13 +10,7 @@ paragraph.addRun(dateText);
|
||||
|
||||
doc.addParagraph(paragraph);
|
||||
|
||||
// Feature coming soon
|
||||
// var media = new docx.Media();
|
||||
// media.addMedia("happy-penguins", "./demo/penguins.jpg");
|
||||
// var pictureRun = new docx.PictureRun(media.getMedia("happy-penguins"));
|
||||
|
||||
// var exporter = new docx.LocalPacker(doc);
|
||||
var exporter = new docx.LocalPacker(doc);
|
||||
exporter.pack('My Document');
|
||||
|
||||
console.log('Document created succesfully at project root!');
|
||||
console.log('Document created successfully at project root!');
|
||||
|
@ -1,7 +1,12 @@
|
||||
const docx = require('../build');
|
||||
|
||||
const styles = new docx.Styles();
|
||||
styles.createParagraphStyle('Heading1', 'Heading 1')
|
||||
const doc = new docx.Document({
|
||||
creator: 'Clippy',
|
||||
title: 'Sample Document',
|
||||
description: 'A brief example of using docx',
|
||||
});
|
||||
|
||||
doc.Styles.createParagraphStyle('Heading1', 'Heading 1')
|
||||
.basedOn("Normal")
|
||||
.next("Normal")
|
||||
.quickFormat()
|
||||
@ -10,7 +15,7 @@ styles.createParagraphStyle('Heading1', 'Heading 1')
|
||||
.italics()
|
||||
.spacing({after: 120});
|
||||
|
||||
styles.createParagraphStyle('Heading2', 'Heading 2')
|
||||
doc.Styles.createParagraphStyle('Heading2', 'Heading 2')
|
||||
.basedOn("Normal")
|
||||
.next("Normal")
|
||||
.quickFormat()
|
||||
@ -19,7 +24,7 @@ styles.createParagraphStyle('Heading2', 'Heading 2')
|
||||
.underline('double', 'FF0000')
|
||||
.spacing({before: 240, after: 120});
|
||||
|
||||
styles.createParagraphStyle('aside', 'Aside')
|
||||
doc.Styles.createParagraphStyle('aside', 'Aside')
|
||||
.basedOn('Normal')
|
||||
.next('Normal')
|
||||
.color('999999')
|
||||
@ -27,31 +32,24 @@ styles.createParagraphStyle('aside', 'Aside')
|
||||
.indent(720)
|
||||
.spacing({line: 276});
|
||||
|
||||
styles.createParagraphStyle('wellSpaced', 'Well Spaced')
|
||||
doc.Styles.createParagraphStyle('wellSpaced', 'Well Spaced')
|
||||
.basedOn('Normal')
|
||||
.spacing({line: 276, before: 20 * 72 * .1, after: 20 * 72 * .05});
|
||||
|
||||
styles.createParagraphStyle('ListParagraph', 'List Paragraph')
|
||||
doc.Styles.createParagraphStyle('ListParagraph', 'List Paragraph')
|
||||
.quickFormat()
|
||||
.basedOn('Normal');
|
||||
|
||||
|
||||
const numbering = new docx.Numbering();
|
||||
const numberedAbstract = numbering.createAbstractNumbering();
|
||||
const numberedAbstract = doc.Numbering.createAbstractNumbering();
|
||||
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "left");
|
||||
|
||||
const doc = new docx.Document({
|
||||
creator: 'Clippy',
|
||||
title: 'Sample Document',
|
||||
description: 'A brief example of using docx',
|
||||
});
|
||||
|
||||
doc.createParagraph('Test heading1, bold and italicized').heading1();
|
||||
doc.createParagraph('Some simple content');
|
||||
doc.createParagraph('Test heading2 with double red underline').heading2();
|
||||
|
||||
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
|
||||
const letterNumbering5 = numbering.createConcreteNumbering(numberedAbstract);
|
||||
const letterNumbering = doc.Numbering.createConcreteNumbering(numberedAbstract);
|
||||
const letterNumbering5 = doc.Numbering.createConcreteNumbering(numberedAbstract);
|
||||
letterNumbering5.overrideLevel(0, 5);
|
||||
|
||||
doc.createParagraph('Option1').setNumbering(letterNumbering, 0);
|
||||
@ -70,5 +68,7 @@ para.createTextRun(' switching to normal ');
|
||||
para.createTextRun('and then underlined ').underline();
|
||||
para.createTextRun('and back to normal.');
|
||||
|
||||
const exporter = new docx.LocalPacker(doc, styles, undefined, numbering);
|
||||
exporter.pack('test.docx');
|
||||
const exporter = new docx.LocalPacker(doc);
|
||||
exporter.pack('My Document');
|
||||
|
||||
console.log('Document created successfully at project root!');
|
||||
|
@ -32,4 +32,4 @@ doc.addParagraph(subSubP);
|
||||
var exporter = new docx.LocalPacker(doc);
|
||||
exporter.pack('My Document');
|
||||
|
||||
console.log('Document created succesfully at project root!');
|
||||
console.log('Document created successfully at project root!');
|
||||
|
@ -9,4 +9,4 @@ table.getCell(2, 2).addContent(new docx.Paragraph('Hello'));
|
||||
var exporter = new docx.LocalPacker(doc);
|
||||
exporter.pack('My Document');
|
||||
|
||||
console.log('Document created succesfully at project root!');
|
||||
console.log('Document created successfully at project root!');
|
||||
|
17
demo/demo5.js
Normal file
17
demo/demo5.js
Normal file
@ -0,0 +1,17 @@
|
||||
const docx = require('../build');
|
||||
|
||||
var doc = new docx.Document();
|
||||
|
||||
var paragraph = new docx.Paragraph("Hello World");
|
||||
doc.addParagraph(paragraph);
|
||||
|
||||
const image = doc.createImage("./demo/images/image1.jpeg");
|
||||
const image2 = doc.createImage("./demo/images/dog.png");
|
||||
const image3 = doc.createImage("./demo/images/cat.jpg");
|
||||
const image4 = doc.createImage("./demo/images/parrots.bmp");
|
||||
const image5 = doc.createImage("./demo/images/pizza.gif");
|
||||
|
||||
var exporter = new docx.LocalPacker(doc);
|
||||
exporter.pack('My Document');
|
||||
|
||||
console.log('Document created successfully at project root!');
|
25
demo/demo6.js
Normal file
25
demo/demo6.js
Normal file
@ -0,0 +1,25 @@
|
||||
const docx = require("../build");
|
||||
|
||||
var doc = new docx.Document(undefined, {
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
});
|
||||
|
||||
var paragraph = new docx.Paragraph("Hello World");
|
||||
var institutionText = new docx.TextRun("University College London").bold();
|
||||
var dateText = new docx.TextRun("5th Dec 2015").tab().bold();
|
||||
paragraph.addRun(institutionText);
|
||||
paragraph.addRun(dateText);
|
||||
|
||||
doc.addParagraph(paragraph);
|
||||
|
||||
doc.createParagraph("Hello World").heading1();
|
||||
doc.createParagraph("University College London");
|
||||
doc.createParagraph("5th Dec 2015");
|
||||
|
||||
var exporter = new docx.LocalPacker(doc);
|
||||
exporter.pack("My Document");
|
||||
|
||||
console.log("Document created successfully at project root!");
|
14
demo/demo7.js
Normal file
14
demo/demo7.js
Normal file
@ -0,0 +1,14 @@
|
||||
const docx = require("../build");
|
||||
|
||||
var doc = new docx.Document(undefined, {
|
||||
orientation: "landscape",
|
||||
});
|
||||
|
||||
var paragraph = new docx.Paragraph("Hello World");
|
||||
|
||||
doc.addParagraph(paragraph);
|
||||
|
||||
var exporter = new docx.LocalPacker(doc);
|
||||
exporter.pack("My Document");
|
||||
|
||||
console.log("Document created successfully at project root!");
|
13
demo/demo8.js
Normal file
13
demo/demo8.js
Normal file
@ -0,0 +1,13 @@
|
||||
const docx = require('../build');
|
||||
|
||||
var doc = new docx.Document();
|
||||
|
||||
doc.createParagraph("Hello World");
|
||||
|
||||
doc.Header.createParagraph("Header text");
|
||||
doc.Footer.createParagraph("Footer text");
|
||||
|
||||
var exporter = new docx.LocalPacker(doc);
|
||||
exporter.pack('My Document');
|
||||
|
||||
console.log('Document created successfully at project root!');
|
13
demo/demo9.js
Normal file
13
demo/demo9.js
Normal file
@ -0,0 +1,13 @@
|
||||
const docx = require('../build');
|
||||
|
||||
var doc = new docx.Document();
|
||||
|
||||
doc.createParagraph("Hello World");
|
||||
|
||||
doc.Header.createImage("./demo/images/pizza.gif");
|
||||
doc.Footer.createImage("./demo/images/pizza.gif");
|
||||
|
||||
var exporter = new docx.LocalPacker(doc);
|
||||
exporter.pack('My Document');
|
||||
|
||||
console.log('Document created successfully at project root!');
|
BIN
demo/images/cat.jpg
Normal file
BIN
demo/images/cat.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 154 KiB |
BIN
demo/images/dog.png
Normal file
BIN
demo/images/dog.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 190 KiB |
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
BIN
demo/images/parrots.bmp
Normal file
BIN
demo/images/parrots.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 818 KiB |
BIN
demo/images/pizza.gif
Normal file
BIN
demo/images/pizza.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 310 KiB |
39
package.json
39
package.json
@ -1,19 +1,24 @@
|
||||
{
|
||||
"name": "docx",
|
||||
"version": "2.1.3",
|
||||
"version": "3.2.0",
|
||||
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
"pretest": "rimraf ./build-tests && tsc -p ts/test-tsconfig.json",
|
||||
"test": "mocha ./build-tests --recursive",
|
||||
"pretest": "rimraf ./build",
|
||||
"test": "mocha-webpack \"src/**/*.ts\"",
|
||||
"test-watch": "mocha-webpack \"src/**/*.ts\" --watch",
|
||||
"prepublishOnly": "npm run build",
|
||||
"lint": "tslint --project ./ts",
|
||||
"build": "rimraf ./build && tsc -p ts",
|
||||
"lint": "tslint --project .",
|
||||
"build": "npm run webpack && npm run fix-types",
|
||||
"tsc": "rimraf ./build && tsc -p .",
|
||||
"webpack": "rimraf ./build && webpack",
|
||||
"demo": "npm run build && node ./demo",
|
||||
"typedoc": "typedoc --out docs/ ts/ --module commonjs --target ES6 --disableOutputCheck"
|
||||
"typedoc": "npm run build && typedoc --out docs/ src/ --module commonjs --target ES6 --disableOutputCheck",
|
||||
"style": "prettier -l --trailing-comma all --print-width 140 --arrow-parens always \"src/**/*.ts\"",
|
||||
"fix-types": "node types-absolute-fixer.js"
|
||||
},
|
||||
"files": [
|
||||
"ts",
|
||||
"src",
|
||||
"build",
|
||||
"template"
|
||||
],
|
||||
@ -22,6 +27,7 @@
|
||||
"url": "git+https://github.com/dolanmiu/docx.git"
|
||||
},
|
||||
"keywords": [
|
||||
"docx",
|
||||
"office",
|
||||
"word",
|
||||
"generate",
|
||||
@ -34,9 +40,14 @@
|
||||
],
|
||||
"types": "./build/index.d.ts",
|
||||
"dependencies": {
|
||||
"@types/archiver": "^1.3.4",
|
||||
"@types/archiver": "^2.1.0",
|
||||
"@types/express": "^4.0.35",
|
||||
"archiver": "^1.3.0",
|
||||
"@types/image-size": "0.0.29",
|
||||
"@types/request-promise": "^4.1.41",
|
||||
"archiver": "^2.1.1",
|
||||
"image-size": "^0.6.2",
|
||||
"request": "^2.83.0",
|
||||
"request-promise": "^4.2.2",
|
||||
"xml": "^1.0.1"
|
||||
},
|
||||
"author": "Dolan Miu",
|
||||
@ -48,13 +59,19 @@
|
||||
"devDependencies": {
|
||||
"@types/chai": "^3.4.35",
|
||||
"@types/mocha": "^2.2.39",
|
||||
"awesome-typescript-loader": "^3.4.1",
|
||||
"chai": "^3.5.0",
|
||||
"glob": "^7.1.2",
|
||||
"mocha": "^3.2.0",
|
||||
"mocha-webpack": "^1.0.1",
|
||||
"prettier": "^1.10.2",
|
||||
"prompt": "^1.0.0",
|
||||
"replace-in-file": "^3.1.0",
|
||||
"rimraf": "^2.5.2",
|
||||
"shelljs": "^0.7.7",
|
||||
"tslint": "^5.1.0",
|
||||
"typedoc": "^0.5.10",
|
||||
"typescript": "2.4.1"
|
||||
"typedoc": "^0.9.0",
|
||||
"typescript": "2.6.2",
|
||||
"webpack": "^3.10.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { assert } from "chai";
|
||||
|
||||
import * as docx from "../../docx";
|
||||
import { Attributes } from "../../docx/xml-components";
|
||||
import { Formatter } from "../../export/formatter";
|
||||
import { Properties } from "../../properties";
|
||||
import { Utility } from "../utility";
|
||||
import { Formatter } from "../export/formatter";
|
||||
import * as file from "../file";
|
||||
import { CoreProperties } from "../file/core-properties";
|
||||
import { Attributes } from "../file/xml-components";
|
||||
import { Utility } from "../tests/utility";
|
||||
|
||||
describe("Formatter", () => {
|
||||
let formatter: Formatter;
|
||||
@ -15,21 +15,21 @@ describe("Formatter", () => {
|
||||
|
||||
describe("#format()", () => {
|
||||
it("should format simple paragraph", () => {
|
||||
const paragraph = new docx.Paragraph();
|
||||
const paragraph = new file.Paragraph();
|
||||
const newJson = formatter.format(paragraph);
|
||||
assert.isDefined(newJson["w:p"]);
|
||||
});
|
||||
|
||||
it("should remove xmlKeys", () => {
|
||||
const paragraph = new docx.Paragraph();
|
||||
const paragraph = new file.Paragraph();
|
||||
const newJson = formatter.format(paragraph);
|
||||
const stringifiedJson = JSON.stringify(newJson);
|
||||
assert(stringifiedJson.indexOf("xmlKeys") < 0);
|
||||
});
|
||||
|
||||
it("should format simple paragraph with bold text", () => {
|
||||
const paragraph = new docx.Paragraph();
|
||||
paragraph.addRun(new docx.TextRun("test").bold());
|
||||
const paragraph = new file.Paragraph();
|
||||
paragraph.addRun(new file.TextRun("test").bold());
|
||||
const newJson = formatter.format(paragraph);
|
||||
assert.isDefined(newJson["w:p"][1]["w:r"][0]["w:rPr"][0]["w:b"][0]._attr["w:val"]);
|
||||
});
|
||||
@ -61,13 +61,13 @@ describe("Formatter", () => {
|
||||
});
|
||||
|
||||
it("should should change 'p' tag into 'w:p' tag", () => {
|
||||
const paragraph = new docx.Paragraph();
|
||||
const paragraph = new file.Paragraph();
|
||||
const newJson = formatter.format(paragraph);
|
||||
assert.isDefined(newJson["w:p"]);
|
||||
});
|
||||
|
||||
it("should format Properties object correctly", () => {
|
||||
const properties = new Properties({
|
||||
const properties = new CoreProperties({
|
||||
title: "test document",
|
||||
creator: "Dolan",
|
||||
});
|
7
src/export/formatter.ts
Normal file
7
src/export/formatter.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { BaseXmlComponent, IXmlableObject } from "file/xml-components";
|
||||
|
||||
export class Formatter {
|
||||
public format(input: BaseXmlComponent): IXmlableObject {
|
||||
return input.prepForXml();
|
||||
}
|
||||
}
|
3
src/export/index.ts
Normal file
3
src/export/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from "./packer/local";
|
||||
export * from "./packer/express";
|
||||
export * from "./packer/packer";
|
105
src/export/packer/compiler.ts
Normal file
105
src/export/packer/compiler.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import * as archiver from "archiver";
|
||||
import * as express from "express";
|
||||
import * as fs from "fs";
|
||||
import * as xml from "xml";
|
||||
|
||||
import { File } from "file";
|
||||
import { Formatter } from "../formatter";
|
||||
|
||||
export class Compiler {
|
||||
protected archive: archiver.Archiver;
|
||||
private formatter: Formatter;
|
||||
|
||||
constructor(private file: File) {
|
||||
this.formatter = new Formatter();
|
||||
this.archive = archiver.create("zip", {});
|
||||
|
||||
this.archive.on("error", (err) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
public async compile(output: fs.WriteStream | express.Response): Promise<void> {
|
||||
this.archive.pipe(output);
|
||||
|
||||
const xmlDocument = xml(this.formatter.format(this.file.Document), true);
|
||||
const xmlStyles = xml(this.formatter.format(this.file.Styles));
|
||||
const xmlProperties = xml(this.formatter.format(this.file.CoreProperties), {
|
||||
declaration: {
|
||||
standalone: "yes",
|
||||
encoding: "UTF-8",
|
||||
},
|
||||
});
|
||||
const xmlNumbering = xml(this.formatter.format(this.file.Numbering));
|
||||
const xmlRelationships = xml(this.formatter.format(this.file.DocumentRelationships));
|
||||
const xmlFileRelationships = xml(this.formatter.format(this.file.FileRelationships));
|
||||
const xmlHeader = xml(this.formatter.format(this.file.Header.Header));
|
||||
const xmlFooter = xml(this.formatter.format(this.file.Footer.Footer));
|
||||
const xmlHeaderRelationships = xml(this.formatter.format(this.file.Header.Relationships));
|
||||
const xmlFooterRelationships = xml(this.formatter.format(this.file.Footer.Relationships));
|
||||
const xmlContentTypes = xml(this.formatter.format(this.file.ContentTypes));
|
||||
const xmlAppProperties = xml(this.formatter.format(this.file.AppProperties));
|
||||
|
||||
this.archive.append(xmlDocument, {
|
||||
name: "word/document.xml",
|
||||
});
|
||||
|
||||
this.archive.append(xmlStyles, {
|
||||
name: "word/styles.xml",
|
||||
});
|
||||
|
||||
this.archive.append(xmlProperties, {
|
||||
name: "docProps/core.xml",
|
||||
});
|
||||
|
||||
this.archive.append(xmlAppProperties, {
|
||||
name: "docProps/app.xml",
|
||||
});
|
||||
|
||||
this.archive.append(xmlNumbering, {
|
||||
name: "word/numbering.xml",
|
||||
});
|
||||
|
||||
this.archive.append(xmlHeader, {
|
||||
name: "word/header1.xml",
|
||||
});
|
||||
|
||||
this.archive.append(xmlFooter, {
|
||||
name: "word/footer1.xml",
|
||||
});
|
||||
|
||||
this.archive.append(xmlRelationships, {
|
||||
name: "word/_rels/document.xml.rels",
|
||||
});
|
||||
|
||||
this.archive.append(xmlHeaderRelationships, {
|
||||
name: "word/_rels/header1.xml.rels",
|
||||
});
|
||||
|
||||
this.archive.append(xmlFooterRelationships, {
|
||||
name: "word/_rels/footer1.xml.rels",
|
||||
});
|
||||
|
||||
this.archive.append(xmlContentTypes, {
|
||||
name: "[Content_Types].xml",
|
||||
});
|
||||
|
||||
this.archive.append(xmlFileRelationships, {
|
||||
name: "_rels/.rels",
|
||||
});
|
||||
|
||||
for (const data of this.file.Media.array) {
|
||||
this.archive.append(data.stream, {
|
||||
name: `word/media/${data.fileName}`,
|
||||
});
|
||||
}
|
||||
|
||||
this.archive.finalize();
|
||||
|
||||
return new Promise<void>((resolve) => {
|
||||
output.on("close", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
29
src/export/packer/express.ts
Normal file
29
src/export/packer/express.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import * as express from "express";
|
||||
|
||||
import { File } from "file";
|
||||
import { Compiler } from "./compiler";
|
||||
import { IPacker } from "./packer";
|
||||
|
||||
export class ExpressPacker implements IPacker {
|
||||
private readonly packer: Compiler;
|
||||
|
||||
constructor(file: File, private readonly res: express.Response) {
|
||||
this.packer = new Compiler(file);
|
||||
|
||||
this.res = res;
|
||||
|
||||
this.res.on("close", () => {
|
||||
return res
|
||||
.status(200)
|
||||
.send("OK")
|
||||
.end();
|
||||
});
|
||||
}
|
||||
|
||||
public async pack(name: string): Promise<void> {
|
||||
name = name.replace(/.docx$/, "");
|
||||
|
||||
this.res.attachment(`${name}.docx`);
|
||||
await this.packer.compile(this.res);
|
||||
}
|
||||
}
|
42
src/export/packer/local.spec.ts
Normal file
42
src/export/packer/local.spec.ts
Normal file
@ -0,0 +1,42 @@
|
||||
/* tslint:disable:typedef space-before-function-paren */
|
||||
import * as fs from "fs";
|
||||
|
||||
import { LocalPacker } from "../../export/packer/local";
|
||||
import { File, Paragraph } from "../../file";
|
||||
|
||||
describe("LocalPacker", () => {
|
||||
let packer: LocalPacker;
|
||||
|
||||
beforeEach(() => {
|
||||
const file = new File({
|
||||
creator: "Dolan Miu",
|
||||
revision: "1",
|
||||
lastModifiedBy: "Dolan Miu",
|
||||
});
|
||||
const paragraph = new Paragraph("test text");
|
||||
const heading = new Paragraph("Hello world").heading1();
|
||||
file.addParagraph(new Paragraph("title").title());
|
||||
file.addParagraph(heading);
|
||||
file.addParagraph(new Paragraph("heading 2").heading2());
|
||||
file.addParagraph(paragraph);
|
||||
|
||||
packer = new LocalPacker(file);
|
||||
});
|
||||
|
||||
describe("#pack()", () => {
|
||||
it("should create a standard docx file", async function() {
|
||||
this.timeout(99999999);
|
||||
await packer.pack("build/tests/test");
|
||||
fs.statSync("build/tests/test.docx");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#packPdf", () => {
|
||||
it("should create a standard PDF file", async function() {
|
||||
this.timeout(99999999);
|
||||
|
||||
await packer.packPdf("build/tests/pdf-test");
|
||||
fs.statSync("build/tests/pdf-test.pdf");
|
||||
});
|
||||
});
|
||||
});
|
47
src/export/packer/local.ts
Normal file
47
src/export/packer/local.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
|
||||
import { File } from "../../file";
|
||||
import { Compiler } from "./compiler";
|
||||
import { IPacker } from "./packer";
|
||||
import { PdfConvertWrapper } from "./pdf-convert-wrapper";
|
||||
|
||||
export class LocalPacker implements IPacker {
|
||||
private stream: fs.WriteStream;
|
||||
private readonly pdfConverter: PdfConvertWrapper;
|
||||
private readonly packer: Compiler;
|
||||
|
||||
constructor(file: File) {
|
||||
this.pdfConverter = new PdfConvertWrapper();
|
||||
this.packer = new Compiler(file);
|
||||
}
|
||||
|
||||
public async pack(filePath: string): Promise<void> {
|
||||
filePath = filePath.replace(/.docx$/, "");
|
||||
|
||||
this.stream = fs.createWriteStream(`${filePath}.docx`);
|
||||
await this.packer.compile(this.stream);
|
||||
}
|
||||
|
||||
public async packPdf(filePath: string): Promise<void> {
|
||||
filePath = filePath.replace(/.pdf$/, "");
|
||||
|
||||
const fileName = path.basename(filePath, path.extname(filePath));
|
||||
const tempPath = path.join(os.tmpdir(), `${fileName}.docx`);
|
||||
this.stream = fs.createWriteStream(tempPath);
|
||||
await this.packer.compile(this.stream);
|
||||
const text = await this.pdfConverter.convert(tempPath);
|
||||
// const writeFile = util.promisify(fs.writeFile); --use this in future, in 3 years time. Only in node 8
|
||||
// return writeFile(`${filePath}.pdf`, text);
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fs.writeFile(`${filePath}.pdf`, text, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
6
src/export/packer/packer.ts
Normal file
6
src/export/packer/packer.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export interface IPacker {
|
||||
pack(path: string): void;
|
||||
}
|
||||
|
||||
// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432
|
||||
export const WORKAROUND = "";
|
35
src/export/packer/pdf-convert-wrapper.ts
Normal file
35
src/export/packer/pdf-convert-wrapper.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/* tslint:disable:object-literal-key-quotes */
|
||||
// This tslint disable is needed, or it simply won't work
|
||||
import * as fs from "fs";
|
||||
import * as request from "request-promise";
|
||||
|
||||
export interface IConvertOutput {
|
||||
data: string;
|
||||
}
|
||||
|
||||
export class PdfConvertWrapper {
|
||||
public convert(filePath: string): request.RequestPromise {
|
||||
return request.post({
|
||||
url: "http://mirror1.convertonlinefree.com",
|
||||
encoding: null,
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.94 Safari/537.36",
|
||||
},
|
||||
formData: {
|
||||
__EVENTTARGET: "",
|
||||
__EVENTARGUMENT: "",
|
||||
__VIEWSTATE: "",
|
||||
ctl00$MainContent$fu: {
|
||||
value: fs.readFileSync(filePath),
|
||||
options: {
|
||||
filename: "output.docx",
|
||||
contentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
},
|
||||
},
|
||||
ctl00$MainContent$btnConvert: "Convert",
|
||||
ctl00$MainContent$fuZip: "",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
13
src/file/app-properties/app-properties-attributes.ts
Normal file
13
src/file/app-properties/app-properties-attributes.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IAppPropertiesAttributes {
|
||||
xmlns: string;
|
||||
vt: string;
|
||||
}
|
||||
|
||||
export class AppPropertiesAttributes extends XmlAttributeComponent<IAppPropertiesAttributes> {
|
||||
protected xmlKeys = {
|
||||
xmlns: "xmlns",
|
||||
vt: "xmlns:vt",
|
||||
};
|
||||
}
|
15
src/file/app-properties/app-properties.ts
Normal file
15
src/file/app-properties/app-properties.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { AppPropertiesAttributes } from "./app-properties-attributes";
|
||||
|
||||
export class AppProperties extends XmlComponent {
|
||||
constructor() {
|
||||
super("Properties");
|
||||
|
||||
this.root.push(
|
||||
new AppPropertiesAttributes({
|
||||
xmlns: "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",
|
||||
vt: "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes",
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
11
src/file/content-types/content-types-attributes.ts
Normal file
11
src/file/content-types/content-types-attributes.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IContentTypeAttributes {
|
||||
xmlns?: string;
|
||||
}
|
||||
|
||||
export class ContentTypeAttributes extends XmlAttributeComponent<IContentTypeAttributes> {
|
||||
protected xmlKeys = {
|
||||
xmlns: "xmlns",
|
||||
};
|
||||
}
|
34
src/file/content-types/content-types.ts
Normal file
34
src/file/content-types/content-types.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { ContentTypeAttributes } from "./content-types-attributes";
|
||||
import { Default } from "./default/default";
|
||||
import { Override } from "./override/override";
|
||||
|
||||
export class ContentTypes extends XmlComponent {
|
||||
constructor() {
|
||||
super("Types");
|
||||
|
||||
this.root.push(
|
||||
new ContentTypeAttributes({
|
||||
xmlns: "http://schemas.openxmlformats.org/package/2006/content-types",
|
||||
}),
|
||||
);
|
||||
|
||||
this.root.push(new Default("image/png", "png"));
|
||||
this.root.push(new Default("image/jpeg", "jpeg"));
|
||||
this.root.push(new Default("image/jpeg", "jpg"));
|
||||
this.root.push(new Default("image/bmp", "bmp"));
|
||||
this.root.push(new Default("image/gif", "gif"));
|
||||
this.root.push(new Default("application/vnd.openxmlformats-package.relationships+xml", "rels"));
|
||||
this.root.push(new Default("application/xml", "xml"));
|
||||
|
||||
this.root.push(
|
||||
new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", "/word/document.xml"),
|
||||
);
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", "/word/header1.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", "/word/footer1.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", "/word/styles.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-package.core-properties+xml", "/docProps/core.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.extended-properties+xml", "/docProps/app.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", "/word/numbering.xml"));
|
||||
}
|
||||
}
|
13
src/file/content-types/default/default-attributes.ts
Normal file
13
src/file/content-types/default/default-attributes.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IDefaultAttributes {
|
||||
contentType: string;
|
||||
extension?: string;
|
||||
}
|
||||
|
||||
export class DefaultAttributes extends XmlAttributeComponent<IDefaultAttributes> {
|
||||
protected xmlKeys = {
|
||||
contentType: "ContentType",
|
||||
extension: "Extension",
|
||||
};
|
||||
}
|
15
src/file/content-types/default/default.ts
Normal file
15
src/file/content-types/default/default.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { DefaultAttributes } from "./default-attributes";
|
||||
|
||||
export class Default extends XmlComponent {
|
||||
constructor(contentType: string, extension?: string) {
|
||||
super("Default");
|
||||
|
||||
this.root.push(
|
||||
new DefaultAttributes({
|
||||
contentType: contentType,
|
||||
extension: extension,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
13
src/file/content-types/override/override-attributes.ts
Normal file
13
src/file/content-types/override/override-attributes.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IOverrideAttributes {
|
||||
contentType: string;
|
||||
partName?: string;
|
||||
}
|
||||
|
||||
export class OverrideAttributes extends XmlAttributeComponent<IOverrideAttributes> {
|
||||
protected xmlKeys = {
|
||||
contentType: "ContentType",
|
||||
partName: "PartName",
|
||||
};
|
||||
}
|
15
src/file/content-types/override/override.ts
Normal file
15
src/file/content-types/override/override.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { OverrideAttributes } from "./override-attributes";
|
||||
|
||||
export class Override extends XmlComponent {
|
||||
constructor(contentType: string, partName?: string) {
|
||||
super("Override");
|
||||
|
||||
this.root.push(
|
||||
new OverrideAttributes({
|
||||
contentType: contentType,
|
||||
partName: partName,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
import { DocumentAttributes } from "../docx/document/document-attributes";
|
||||
import { XmlComponent } from "../docx/xml-components";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { DocumentAttributes } from "../document/document-attributes";
|
||||
|
||||
export class Title extends XmlComponent {
|
||||
|
||||
constructor(value: string) {
|
||||
super("dc:title");
|
||||
this.root.push(value);
|
||||
@ -10,7 +9,6 @@ export class Title extends XmlComponent {
|
||||
}
|
||||
|
||||
export class Subject extends XmlComponent {
|
||||
|
||||
constructor(value: string) {
|
||||
super("dc:subject");
|
||||
this.root.push(value);
|
||||
@ -18,7 +16,6 @@ export class Subject extends XmlComponent {
|
||||
}
|
||||
|
||||
export class Creator extends XmlComponent {
|
||||
|
||||
constructor(value: string) {
|
||||
super("dc:creator");
|
||||
this.root.push(value);
|
||||
@ -26,7 +23,6 @@ export class Creator extends XmlComponent {
|
||||
}
|
||||
|
||||
export class Keywords extends XmlComponent {
|
||||
|
||||
constructor(value: string) {
|
||||
super("cp:keywords");
|
||||
this.root.push(value);
|
||||
@ -34,7 +30,6 @@ export class Keywords extends XmlComponent {
|
||||
}
|
||||
|
||||
export class Description extends XmlComponent {
|
||||
|
||||
constructor(value: string) {
|
||||
super("dc:description");
|
||||
this.root.push(value);
|
||||
@ -42,7 +37,6 @@ export class Description extends XmlComponent {
|
||||
}
|
||||
|
||||
export class LastModifiedBy extends XmlComponent {
|
||||
|
||||
constructor(value: string) {
|
||||
super("cp:lastModifiedBy");
|
||||
this.root.push(value);
|
||||
@ -50,7 +44,6 @@ export class LastModifiedBy extends XmlComponent {
|
||||
}
|
||||
|
||||
export class Revision extends XmlComponent {
|
||||
|
||||
constructor(value: string) {
|
||||
super("cp:revision");
|
||||
this.root.push(value);
|
||||
@ -72,23 +65,25 @@ export abstract class DateComponent extends XmlComponent {
|
||||
}
|
||||
|
||||
export class Created extends DateComponent {
|
||||
|
||||
constructor() {
|
||||
super("dcterms:created");
|
||||
this.root.push(new DocumentAttributes({
|
||||
type: "dcterms:W3CDTF",
|
||||
}));
|
||||
this.root.push(
|
||||
new DocumentAttributes({
|
||||
type: "dcterms:W3CDTF",
|
||||
}),
|
||||
);
|
||||
this.root.push(this.getCurrentDate());
|
||||
}
|
||||
}
|
||||
|
||||
export class Modified extends DateComponent {
|
||||
|
||||
constructor() {
|
||||
super("dcterms:modified");
|
||||
this.root.push(new DocumentAttributes({
|
||||
type: "dcterms:W3CDTF",
|
||||
}));
|
||||
this.root.push(
|
||||
new DocumentAttributes({
|
||||
type: "dcterms:W3CDTF",
|
||||
}),
|
||||
);
|
||||
this.root.push(this.getCurrentDate());
|
||||
}
|
||||
}
|
1
src/file/core-properties/index.ts
Normal file
1
src/file/core-properties/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./properties";
|
@ -1,13 +1,12 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../export/formatter";
|
||||
import { Properties } from "../properties";
|
||||
import { Formatter } from "../../export/formatter";
|
||||
import { CoreProperties } from "./properties";
|
||||
|
||||
describe("Properties", () => {
|
||||
|
||||
describe("#constructor()", () => {
|
||||
it("sets the appropriate attributes on the top-level", () => {
|
||||
const properties = new Properties({});
|
||||
const properties = new CoreProperties({});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["cp:coreProperties"]);
|
||||
expect(tree["cp:coreProperties"]).to.be.an.instanceof(Array);
|
||||
@ -23,18 +22,16 @@ describe("Properties", () => {
|
||||
});
|
||||
|
||||
it("should create properties with a title", () => {
|
||||
const properties = new Properties({title: "test document"});
|
||||
const properties = new CoreProperties({ title: "test document" });
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["cp:coreProperties"]);
|
||||
expect(tree["cp:coreProperties"]).to.be.an.instanceof(Array);
|
||||
expect(Object.keys(tree["cp:coreProperties"][0])).to.deep.equal(["_attr"]);
|
||||
expect(tree["cp:coreProperties"][1]).to.deep.equal(
|
||||
{"dc:title": ["test document"]},
|
||||
);
|
||||
expect(tree["cp:coreProperties"][1]).to.deep.equal({ "dc:title": ["test document"] });
|
||||
});
|
||||
|
||||
it("should create properties with all the attributes given", () => {
|
||||
const properties = new Properties({
|
||||
const properties = new CoreProperties({
|
||||
title: "test document",
|
||||
subject: "test subject",
|
||||
creator: "me",
|
||||
@ -60,14 +57,14 @@ describe("Properties", () => {
|
||||
"dcterms:created",
|
||||
"dcterms:modified",
|
||||
]);
|
||||
expect(tree["cp:coreProperties"].slice(1, -2).sort((a, b) => key(a) < key(b) ? -1 : 1)).to.deep.equal([
|
||||
{"cp:keywords": ["test docx"]},
|
||||
{"cp:lastModifiedBy": ["the author"]},
|
||||
{"cp:revision": ["123"]},
|
||||
{"dc:creator": ["me"]},
|
||||
{"dc:description": ["testing document"]},
|
||||
{"dc:subject": ["test subject"]},
|
||||
{"dc:title": ["test document"]},
|
||||
expect(tree["cp:coreProperties"].slice(1, -2).sort((a, b) => (key(a) < key(b) ? -1 : 1))).to.deep.equal([
|
||||
{ "cp:keywords": ["test docx"] },
|
||||
{ "cp:lastModifiedBy": ["the author"] },
|
||||
{ "cp:revision": ["123"] },
|
||||
{ "dc:creator": ["me"] },
|
||||
{ "dc:description": ["testing document"] },
|
||||
{ "dc:subject": ["test subject"] },
|
||||
{ "dc:title": ["test document"] },
|
||||
]);
|
||||
});
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
import { DocumentAttributes } from "../docx/document/document-attributes";
|
||||
import { XmlComponent } from "../docx/xml-components";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { DocumentAttributes } from "../document/document-attributes";
|
||||
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
|
||||
|
||||
export interface IPropertiesOptions {
|
||||
@ -12,17 +12,18 @@ export interface IPropertiesOptions {
|
||||
revision?: string;
|
||||
}
|
||||
|
||||
export class Properties extends XmlComponent {
|
||||
|
||||
export class CoreProperties extends XmlComponent {
|
||||
constructor(options: IPropertiesOptions) {
|
||||
super("cp:coreProperties");
|
||||
this.root.push(new DocumentAttributes({
|
||||
cp: "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
|
||||
dc: "http://purl.org/dc/elements/1.1/",
|
||||
dcterms: "http://purl.org/dc/terms/",
|
||||
dcmitype: "http://purl.org/dc/dcmitype/",
|
||||
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
||||
}));
|
||||
this.root.push(
|
||||
new DocumentAttributes({
|
||||
cp: "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
|
||||
dc: "http://purl.org/dc/elements/1.1/",
|
||||
dcterms: "http://purl.org/dc/terms/",
|
||||
dcmitype: "http://purl.org/dc/dcmitype/",
|
||||
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
||||
}),
|
||||
);
|
||||
if (options.title) {
|
||||
this.root.push(new Title(options.title));
|
||||
}
|
39
src/file/document/body/body.spec.ts
Normal file
39
src/file/document/body/body.spec.ts
Normal file
@ -0,0 +1,39 @@
|
||||
// import { assert } from "chai";
|
||||
|
||||
// import { Utility } from "../../../tests/utility";
|
||||
// import { Body } from "./";
|
||||
|
||||
describe("Body", () => {
|
||||
// let body: Body;
|
||||
|
||||
beforeEach(() => {
|
||||
// body = new Body();
|
||||
});
|
||||
|
||||
// describe("#constructor()", () => {
|
||||
// it("should create the Section Properties", () => {
|
||||
// const newJson = Utility.jsonify(body);
|
||||
// assert.equal(newJson.root[0].rootKey, "w:sectPr");
|
||||
// });
|
||||
|
||||
// it("should create the Page Size", () => {
|
||||
// const newJson = Utility.jsonify(body);
|
||||
// assert.equal(newJson.root[1].rootKey, "w:pgSz");
|
||||
// });
|
||||
|
||||
// it("should create the Page Margin", () => {
|
||||
// const newJson = Utility.jsonify(body);
|
||||
// assert.equal(newJson.root[2].rootKey, "w:pgMar");
|
||||
// });
|
||||
|
||||
// it("should create the Columns", () => {
|
||||
// const newJson = Utility.jsonify(body);
|
||||
// assert.equal(newJson.root[3].rootKey, "w:cols");
|
||||
// });
|
||||
|
||||
// it("should create the Document Grid", () => {
|
||||
// const newJson = Utility.jsonify(body);
|
||||
// assert.equal(newJson.root[4].rootKey, "w:docGrid");
|
||||
// });
|
||||
// });
|
||||
});
|
14
src/file/document/body/body.ts
Normal file
14
src/file/document/body/body.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties";
|
||||
|
||||
export class Body extends XmlComponent {
|
||||
constructor(sectionPropertiesOptions?: SectionPropertiesOptions) {
|
||||
super("w:body");
|
||||
|
||||
this.root.push(new SectionProperties(sectionPropertiesOptions));
|
||||
}
|
||||
|
||||
public push(component: XmlComponent): void {
|
||||
this.root.push(component);
|
||||
}
|
||||
}
|
1
src/file/document/body/index.ts
Normal file
1
src/file/document/body/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./body";
|
@ -0,0 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IColumnsAttributes {
|
||||
space?: number;
|
||||
}
|
||||
|
||||
export class ColumnsAttributes extends XmlAttributeComponent<IColumnsAttributes> {
|
||||
protected xmlKeys = {
|
||||
space: "w:space",
|
||||
};
|
||||
}
|
13
src/file/document/body/section-properties/columns/columns.ts
Normal file
13
src/file/document/body/section-properties/columns/columns.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { ColumnsAttributes } from "./columns-attributes";
|
||||
|
||||
export class Columns extends XmlComponent {
|
||||
constructor(space: number) {
|
||||
super("w:cols");
|
||||
this.root.push(
|
||||
new ColumnsAttributes({
|
||||
space: space,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IDocGridAttributesProperties {
|
||||
linePitch?: number;
|
||||
}
|
||||
|
||||
export class DocGridAttributes extends XmlAttributeComponent<IDocGridAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
linePitch: "w:linePitch",
|
||||
};
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { DocGridAttributes } from "./doc-grid-attributes";
|
||||
|
||||
export class DocumentGrid extends XmlComponent {
|
||||
constructor(linePitch: number) {
|
||||
super("w:docGrid");
|
||||
this.root.push(
|
||||
new DocGridAttributes({
|
||||
linePitch: linePitch,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IFooterReferenceAttributes {
|
||||
type: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export class FooterReferenceAttributes extends XmlAttributeComponent<IFooterReferenceAttributes> {
|
||||
protected xmlKeys = {
|
||||
type: "w:type",
|
||||
id: "r:id",
|
||||
};
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { FooterReferenceAttributes } from "./footer-reference-attributes";
|
||||
|
||||
export class FooterReference extends XmlComponent {
|
||||
constructor() {
|
||||
super("w:footerReference");
|
||||
this.root.push(
|
||||
new FooterReferenceAttributes({
|
||||
type: "default",
|
||||
id: `rId${4}`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IHeaderReferenceAttributes {
|
||||
type: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export class HeaderReferenceAttributes extends XmlAttributeComponent<IHeaderReferenceAttributes> {
|
||||
protected xmlKeys = {
|
||||
type: "w:type",
|
||||
id: "r:id",
|
||||
};
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { HeaderReferenceAttributes } from "./header-reference-attributes";
|
||||
|
||||
export class HeaderReference extends XmlComponent {
|
||||
constructor() {
|
||||
super("w:headerReference");
|
||||
this.root.push(
|
||||
new HeaderReferenceAttributes({
|
||||
type: "default",
|
||||
id: `rId${3}`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IPageMarginAttributes {
|
||||
top?: number;
|
||||
right?: number;
|
||||
bottom?: number;
|
||||
left?: number;
|
||||
header?: number;
|
||||
footer?: number;
|
||||
gutter?: number;
|
||||
}
|
||||
|
||||
export class PageMarginAttributes extends XmlAttributeComponent<IPageMarginAttributes> {
|
||||
protected xmlKeys = {
|
||||
top: "w:top",
|
||||
right: "w:right",
|
||||
bottom: "w:bottom",
|
||||
left: "w:left",
|
||||
header: "w:header",
|
||||
footer: "w:footer",
|
||||
gutter: "w:gutter",
|
||||
};
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { PageMarginAttributes } from "./page-margin-attributes";
|
||||
|
||||
export class PageMargin extends XmlComponent {
|
||||
constructor(top: number, right: number, bottom: number, left: number, header: number, footer: number, gutter: number) {
|
||||
super("w:pgMar");
|
||||
this.root.push(
|
||||
new PageMarginAttributes({
|
||||
top: top,
|
||||
right: right,
|
||||
bottom: bottom,
|
||||
left: left,
|
||||
header: header,
|
||||
footer: footer,
|
||||
gutter: gutter,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IPageSizeAttributes {
|
||||
width?: number;
|
||||
height?: number;
|
||||
orientation?: string;
|
||||
}
|
||||
|
||||
export class PageSizeAttributes extends XmlAttributeComponent<IPageSizeAttributes> {
|
||||
protected xmlKeys = {
|
||||
width: "w:w",
|
||||
height: "w:h",
|
||||
orientation: "w:orient",
|
||||
};
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../../../export/formatter";
|
||||
import { PageSize } from "./page-size";
|
||||
|
||||
describe("PageSize", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("should create page size with portrait", () => {
|
||||
const properties = new PageSize(100, 200, "portrait");
|
||||
const tree = new Formatter().format(properties);
|
||||
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
|
||||
expect(tree["w:pgSz"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:pgSz"][0]).to.deep.equal({ _attr: { "w:h": 200, "w:w": 100, "w:orient": "portrait" } });
|
||||
});
|
||||
|
||||
it("should create page size with horizontal and invert the lengths", () => {
|
||||
const properties = new PageSize(100, 200, "landscape");
|
||||
const tree = new Formatter().format(properties);
|
||||
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
|
||||
expect(tree["w:pgSz"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:pgSz"][0]).to.deep.equal({ _attr: { "w:h": 100, "w:w": 200, "w:orient": "landscape" } });
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,18 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { PageSizeAttributes } from "./page-size-attributes";
|
||||
|
||||
export class PageSize extends XmlComponent {
|
||||
constructor(width: number, height: number, orientation: string) {
|
||||
super("w:pgSz");
|
||||
|
||||
const flip = orientation === "landscape";
|
||||
|
||||
this.root.push(
|
||||
new PageSizeAttributes({
|
||||
width: flip ? height : width,
|
||||
height: flip ? width : height,
|
||||
orientation: orientation,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../../export/formatter";
|
||||
import { SectionProperties } from "./section-properties";
|
||||
|
||||
describe("SectionProperties", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("should create section properties with options", () => {
|
||||
const properties = new SectionProperties({
|
||||
width: 11906,
|
||||
height: 16838,
|
||||
top: 1440,
|
||||
right: 1440,
|
||||
bottom: 1440,
|
||||
left: 1440,
|
||||
header: 708,
|
||||
footer: 708,
|
||||
gutter: 0,
|
||||
space: 708,
|
||||
linePitch: 360,
|
||||
});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create section properties with no options", () => {
|
||||
const properties = new SectionProperties();
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create section properties with changed options", () => {
|
||||
const properties = new SectionProperties({
|
||||
top: 0,
|
||||
});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
"w:top": 0,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create section properties with changed options", () => {
|
||||
const properties = new SectionProperties({
|
||||
bottom: 0,
|
||||
});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:bottom": 0,
|
||||
"w:footer": 708,
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create section properties with changed options", () => {
|
||||
const properties = new SectionProperties({
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
|
||||
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 0, "w:w": 0, "w:orient": "portrait" } }] });
|
||||
expect(tree["w:sectPr"][1]).to.deep.equal({
|
||||
"w:pgMar": [
|
||||
{
|
||||
_attr: {
|
||||
"w:bottom": 1440,
|
||||
"w:footer": 708,
|
||||
"w:top": 1440,
|
||||
"w:right": 1440,
|
||||
"w:left": 1440,
|
||||
"w:header": 708,
|
||||
"w:gutter": 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,57 @@
|
||||
// http://officeopenxml.com/WPsection.php
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
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 } from "./footer-reference/footer-reference";
|
||||
import { HeaderReference } from "./header-reference/header-reference";
|
||||
import { PageMargin } from "./page-margin/page-margin";
|
||||
import { IPageMarginAttributes } from "./page-margin/page-margin-attributes";
|
||||
import { PageSize } from "./page-size/page-size";
|
||||
import { IPageSizeAttributes } from "./page-size/page-size-attributes";
|
||||
|
||||
export type SectionPropertiesOptions = IPageSizeAttributes & IPageMarginAttributes & IColumnsAttributes & IDocGridAttributesProperties;
|
||||
|
||||
export class SectionProperties extends XmlComponent {
|
||||
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,
|
||||
space: 708,
|
||||
linePitch: 360,
|
||||
orientation: "portrait",
|
||||
};
|
||||
|
||||
const mergedOptions = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
};
|
||||
|
||||
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,
|
||||
),
|
||||
);
|
||||
this.root.push(new Columns(mergedOptions.space));
|
||||
this.root.push(new DocumentGrid(mergedOptions.linePitch));
|
||||
this.root.push(new HeaderReference());
|
||||
this.root.push(new FooterReference());
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { XmlAttributeComponent } from "../xml-components";
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IDocumentAttributesProperties {
|
||||
wpc?: string;
|
83
src/file/document/document.spec.ts
Normal file
83
src/file/document/document.spec.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { assert, expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../export/formatter";
|
||||
import { Paragraph } from "../paragraph";
|
||||
import { Table } from "../table";
|
||||
import { Document } from "./document";
|
||||
|
||||
describe("Document", () => {
|
||||
let document: Document;
|
||||
|
||||
beforeEach(() => {
|
||||
document = new Document();
|
||||
});
|
||||
|
||||
describe("#constructor()", () => {
|
||||
it("should create valid JSON", () => {
|
||||
const stringifiedJson = JSON.stringify(document);
|
||||
|
||||
try {
|
||||
JSON.parse(stringifiedJson);
|
||||
} catch (e) {
|
||||
assert.isTrue(false);
|
||||
}
|
||||
assert.isTrue(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createParagraph", () => {
|
||||
it("should create a new paragraph and append it to body", () => {
|
||||
const para = document.createParagraph();
|
||||
expect(para).to.be.an.instanceof(Paragraph);
|
||||
const body = new Formatter().format(document)["w:document"][1]["w:body"];
|
||||
expect(body)
|
||||
.to.be.an("array")
|
||||
.which.has.length.at.least(1);
|
||||
expect(body[1]).to.have.property("w:p");
|
||||
});
|
||||
|
||||
it("should use the text given to create a run in the paragraph", () => {
|
||||
const para = document.createParagraph("sample paragraph text");
|
||||
expect(para).to.be.an.instanceof(Paragraph);
|
||||
const body = new Formatter().format(document)["w:document"][1]["w:body"];
|
||||
expect(body)
|
||||
.to.be.an("array")
|
||||
.which.has.length.at.least(1);
|
||||
expect(body[1])
|
||||
.to.have.property("w:p")
|
||||
.which.includes({
|
||||
"w:r": [{ "w:rPr": [] }, { "w:t": [{ _attr: { "xml:space": "preserve" } }, "sample paragraph text"] }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createTable", () => {
|
||||
it("should create a new table and append it to body", () => {
|
||||
const table = document.createTable(2, 3);
|
||||
expect(table).to.be.an.instanceof(Table);
|
||||
const body = new Formatter().format(document)["w:document"][1]["w:body"];
|
||||
expect(body)
|
||||
.to.be.an("array")
|
||||
.which.has.length.at.least(1);
|
||||
expect(body[1]).to.have.property("w:tbl");
|
||||
});
|
||||
|
||||
it("should create a table with the correct dimensions", () => {
|
||||
document.createTable(2, 3);
|
||||
const body = new Formatter().format(document)["w:document"][1]["w:body"];
|
||||
expect(body)
|
||||
.to.be.an("array")
|
||||
.which.has.length.at.least(1);
|
||||
expect(body[1])
|
||||
.to.have.property("w:tbl")
|
||||
.which.includes({
|
||||
"w:tblGrid": [
|
||||
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
|
||||
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
|
||||
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
|
||||
],
|
||||
});
|
||||
expect(body[1]["w:tbl"].filter((x) => x["w:tr"])).to.have.length(2);
|
||||
});
|
||||
});
|
||||
});
|
73
src/file/document/document.ts
Normal file
73
src/file/document/document.ts
Normal file
@ -0,0 +1,73 @@
|
||||
// http://officeopenxml.com/WPdocument.php
|
||||
import { IMediaData } from "file/media";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { Paragraph, PictureRun } from "../paragraph";
|
||||
import { Table } from "../table";
|
||||
import { Body } from "./body";
|
||||
import { SectionPropertiesOptions } from "./body/section-properties/section-properties";
|
||||
import { DocumentAttributes } from "./document-attributes";
|
||||
|
||||
export class Document extends XmlComponent {
|
||||
private readonly body: Body;
|
||||
|
||||
constructor(sectionPropertiesOptions?: SectionPropertiesOptions) {
|
||||
super("w:document");
|
||||
this.root.push(
|
||||
new DocumentAttributes({
|
||||
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
||||
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
||||
o: "urn:schemas-microsoft-com:office:office",
|
||||
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||
m: "http://schemas.openxmlformats.org/officeDocument/2006/math",
|
||||
v: "urn:schemas-microsoft-com:vml",
|
||||
wp14: "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
|
||||
wp: "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
w10: "urn:schemas-microsoft-com:office:word",
|
||||
w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||
w14: "http://schemas.microsoft.com/office/word/2010/wordml",
|
||||
w15: "http://schemas.microsoft.com/office/word/2012/wordml",
|
||||
wpg: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
|
||||
wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
|
||||
wne: "http://schemas.microsoft.com/office/word/2006/wordml",
|
||||
wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
||||
Ignorable: "w14 w15 wp14",
|
||||
}),
|
||||
);
|
||||
this.body = new Body(sectionPropertiesOptions);
|
||||
this.root.push(this.body);
|
||||
}
|
||||
|
||||
public addParagraph(paragraph: Paragraph): void {
|
||||
this.body.push(paragraph);
|
||||
}
|
||||
|
||||
public createParagraph(text?: string): Paragraph {
|
||||
const para = new Paragraph(text);
|
||||
this.addParagraph(para);
|
||||
return para;
|
||||
}
|
||||
|
||||
public addTable(table: Table): void {
|
||||
this.body.push(table);
|
||||
}
|
||||
|
||||
public createTable(rows: number, cols: number): Table {
|
||||
const table = new Table(rows, cols);
|
||||
this.addTable(table);
|
||||
return table;
|
||||
}
|
||||
|
||||
public addDrawing(imageData: IMediaData): void {
|
||||
const paragraph = new Paragraph();
|
||||
const run = new PictureRun(imageData);
|
||||
paragraph.addRun(run);
|
||||
|
||||
this.body.push(paragraph);
|
||||
}
|
||||
|
||||
public createDrawing(imageData: IMediaData): void {
|
||||
this.addDrawing(imageData);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
1
src/file/document/index.ts
Normal file
1
src/file/document/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./document";
|
@ -1,18 +1,29 @@
|
||||
import { assert } from "chai";
|
||||
import * as fs from "fs";
|
||||
import { Drawing } from "../../../../docx/run/run-components/drawing";
|
||||
import { Utility } from "../../../utility";
|
||||
|
||||
import { Utility } from "../../tests/utility";
|
||||
import { Drawing } from "./";
|
||||
|
||||
describe("Drawing", () => {
|
||||
let currentBreak: Drawing;
|
||||
|
||||
beforeEach(() => {
|
||||
const path = "./demo/penguins.jpg";
|
||||
const path = "./demo/images/image1.jpeg";
|
||||
currentBreak = new Drawing({
|
||||
fileName: "test.jpg",
|
||||
referenceId: 1,
|
||||
stream: fs.createReadStream(path),
|
||||
path: path,
|
||||
dimensions: {
|
||||
pixels: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
},
|
||||
emus: {
|
||||
x: 100 * 9525,
|
||||
y: 100 * 9525,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -1,16 +1,15 @@
|
||||
import { IData } from "../../../../media/data";
|
||||
import { XmlComponent } from "../../../xml-components";
|
||||
import { IMediaData } from "file/media";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { Inline } from "./inline";
|
||||
|
||||
export class Drawing extends XmlComponent {
|
||||
|
||||
constructor(imageData: IData) {
|
||||
constructor(imageData: IMediaData) {
|
||||
super("w:drawing");
|
||||
|
||||
if (imageData === undefined) {
|
||||
throw new Error("imageData cannot be undefined");
|
||||
}
|
||||
|
||||
this.root.push(new Inline(imageData.referenceId));
|
||||
this.root.push(new Inline(imageData.referenceId, imageData.dimensions));
|
||||
}
|
||||
}
|
1
src/file/drawing/index.ts
Normal file
1
src/file/drawing/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { Drawing } from "./drawing";
|
@ -0,0 +1,15 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IDocPropertiesAttributes {
|
||||
id?: number;
|
||||
name?: string;
|
||||
descr?: string;
|
||||
}
|
||||
|
||||
export class DocPropertiesAttributes extends XmlAttributeComponent<IDocPropertiesAttributes> {
|
||||
protected xmlKeys = {
|
||||
id: "id",
|
||||
name: "name",
|
||||
descr: "descr",
|
||||
};
|
||||
}
|
16
src/file/drawing/inline/doc-properties/doc-properties.ts
Normal file
16
src/file/drawing/inline/doc-properties/doc-properties.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { DocPropertiesAttributes } from "./doc-properties-attributes";
|
||||
|
||||
export class DocProperties extends XmlComponent {
|
||||
constructor() {
|
||||
super("wp:docPr");
|
||||
|
||||
this.root.push(
|
||||
new DocPropertiesAttributes({
|
||||
id: 0,
|
||||
name: "",
|
||||
descr: "",
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IEffectExtentAttributes {
|
||||
b?: number;
|
||||
l?: number;
|
||||
r?: number;
|
||||
t?: number;
|
||||
}
|
||||
|
||||
export class EffectExtentAttributes extends XmlAttributeComponent<IEffectExtentAttributes> {
|
||||
protected xmlKeys = {
|
||||
b: "b",
|
||||
l: "l",
|
||||
r: "r",
|
||||
t: "t",
|
||||
};
|
||||
}
|
17
src/file/drawing/inline/effect-extent/effect-extent.ts
Normal file
17
src/file/drawing/inline/effect-extent/effect-extent.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { EffectExtentAttributes } from "./effect-extent-attributes";
|
||||
|
||||
export class EffectExtent extends XmlComponent {
|
||||
constructor() {
|
||||
super("wp:effectExtent");
|
||||
|
||||
this.root.push(
|
||||
new EffectExtentAttributes({
|
||||
b: 0,
|
||||
l: 0,
|
||||
r: 0,
|
||||
t: 0,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
13
src/file/drawing/inline/extent/extent-attributes.ts
Normal file
13
src/file/drawing/inline/extent/extent-attributes.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IExtentAttributes {
|
||||
cx?: number;
|
||||
cy?: number;
|
||||
}
|
||||
|
||||
export class ExtentAttributes extends XmlAttributeComponent<IExtentAttributes> {
|
||||
protected xmlKeys = {
|
||||
cx: "cx",
|
||||
cy: "cy",
|
||||
};
|
||||
}
|
15
src/file/drawing/inline/extent/extent.ts
Normal file
15
src/file/drawing/inline/extent/extent.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { ExtentAttributes } from "./extent-attributes";
|
||||
|
||||
export class Extent extends XmlComponent {
|
||||
constructor(x: number, y: number) {
|
||||
super("wp:extent");
|
||||
|
||||
this.root.push(
|
||||
new ExtentAttributes({
|
||||
cx: x,
|
||||
cy: y,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IGraphicFrameLockAttributes {
|
||||
xmlns?: string;
|
||||
noChangeAspect?: number;
|
||||
}
|
||||
|
||||
export class GraphicFrameLockAttributes extends XmlAttributeComponent<IGraphicFrameLockAttributes> {
|
||||
protected xmlKeys = {
|
||||
xmlns: "xmlns:a",
|
||||
noChangeAspect: "noChangeAspect",
|
||||
};
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { GraphicFrameLockAttributes } from "./graphic-frame-lock-attributes";
|
||||
|
||||
export class GraphicFrameLocks extends XmlComponent {
|
||||
constructor() {
|
||||
super("a:graphicFrameLocks");
|
||||
|
||||
this.root.push(
|
||||
new GraphicFrameLockAttributes({
|
||||
xmlns: "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||
noChangeAspect: 1,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { GraphicFrameLocks } from "./graphic-frame-locks/graphic-frame-locks";
|
||||
|
||||
export class GraphicFrameProperties extends XmlComponent {
|
||||
constructor() {
|
||||
super("wp:cNvGraphicFramePr");
|
||||
|
||||
this.root.push(new GraphicFrameLocks());
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IGraphicDataAttributes {
|
||||
uri?: string;
|
||||
}
|
||||
|
||||
export class GraphicDataAttributes extends XmlAttributeComponent<IGraphicDataAttributes> {
|
||||
protected xmlKeys = {
|
||||
uri: "uri",
|
||||
};
|
||||
}
|
17
src/file/drawing/inline/graphic/graphic-data/graphic-data.ts
Normal file
17
src/file/drawing/inline/graphic/graphic-data/graphic-data.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { GraphicDataAttributes } from "./graphic-data-attribute";
|
||||
import { Pic } from "./pic";
|
||||
|
||||
export class GraphicData extends XmlComponent {
|
||||
constructor(referenceId: number, x: number, y: number) {
|
||||
super("a:graphicData");
|
||||
|
||||
this.root.push(
|
||||
new GraphicDataAttributes({
|
||||
uri: "http://schemas.openxmlformats.org/drawingml/2006/picture",
|
||||
}),
|
||||
);
|
||||
|
||||
this.root.push(new Pic(referenceId, x, y));
|
||||
}
|
||||
}
|
1
src/file/drawing/inline/graphic/graphic-data/index.ts
Normal file
1
src/file/drawing/inline/graphic/graphic-data/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./graphic-data";
|
@ -1,10 +1,9 @@
|
||||
import { XmlComponent } from "../../../../../../../../xml-components";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { Blip } from "./blip";
|
||||
import { SourceRectangle } from "./source-rectangle";
|
||||
import { Stretch } from "./stretch";
|
||||
|
||||
export class BlipFill extends XmlComponent {
|
||||
|
||||
constructor(referenceId: number) {
|
||||
super("pic:blipFill");
|
||||
this.root.push(new Blip(referenceId));
|
@ -1,21 +1,25 @@
|
||||
import { XmlAttributeComponent, XmlComponent } from "../../../../../../../../xml-components";
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
interface IBlipProperties {
|
||||
embed: string;
|
||||
cstate: string;
|
||||
}
|
||||
|
||||
class BlipAttributes extends XmlAttributeComponent<IBlipProperties> {
|
||||
protected xmlKeys = {
|
||||
embed: "r:embed",
|
||||
cstate: "cstate",
|
||||
};
|
||||
}
|
||||
|
||||
export class Blip extends XmlComponent {
|
||||
|
||||
constructor(referenceId: number) {
|
||||
super("a:blip");
|
||||
this.root.push(new BlipAttributes({
|
||||
embed: `rId${referenceId}`,
|
||||
}));
|
||||
this.root.push(
|
||||
new BlipAttributes({
|
||||
embed: `rId${referenceId}`,
|
||||
cstate: "none",
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
import { XmlComponent } from "../../../../../../../../xml-components";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
export class SourceRectangle extends XmlComponent {
|
||||
|
||||
constructor() {
|
||||
super("a:srcRect");
|
||||
}
|
@ -1,14 +1,12 @@
|
||||
import { XmlComponent } from "../../../../../../../../xml-components";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
class FillRectangle extends XmlComponent {
|
||||
|
||||
constructor() {
|
||||
super("a:fillRect");
|
||||
}
|
||||
}
|
||||
|
||||
export class Stretch extends XmlComponent {
|
||||
|
||||
constructor() {
|
||||
super("a:stretch");
|
||||
this.root.push(new FillRectangle());
|
@ -0,0 +1 @@
|
||||
export * from "./pic";
|
@ -0,0 +1,10 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { PicLocks } from "./pic-locks/pic-locks";
|
||||
|
||||
export class ChildNonVisualProperties extends XmlComponent {
|
||||
constructor() {
|
||||
super("pic:cNvPicPr");
|
||||
|
||||
this.root.push(new PicLocks());
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IPicLocksAttributes {
|
||||
noChangeAspect?: number;
|
||||
noChangeArrowheads?: number;
|
||||
}
|
||||
|
||||
export class PicLocksAttributes extends XmlAttributeComponent<IPicLocksAttributes> {
|
||||
protected xmlKeys = {
|
||||
noChangeAspect: "noChangeAspect",
|
||||
noChangeArrowheads: "noChangeArrowheads",
|
||||
};
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { PicLocksAttributes } from "./pic-locks-attributes";
|
||||
|
||||
export class PicLocks extends XmlComponent {
|
||||
constructor() {
|
||||
super("a:picLocks");
|
||||
this.root.push(
|
||||
new PicLocksAttributes({
|
||||
noChangeAspect: 1,
|
||||
noChangeArrowheads: 1,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { ChildNonVisualProperties } from "./child-non-visual-pic-properties/child-non-visual-pic-properties";
|
||||
import { NonVisualProperties } from "./non-visual-properties/non-visual-properties";
|
||||
|
||||
export class NonVisualPicProperties extends XmlComponent {
|
||||
constructor() {
|
||||
super("pic:nvPicPr");
|
||||
|
||||
this.root.push(new NonVisualProperties());
|
||||
this.root.push(new ChildNonVisualProperties());
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface INonVisualPropertiesAttributes {
|
||||
id?: number;
|
||||
name?: string;
|
||||
descr?: string;
|
||||
}
|
||||
|
||||
export class NonVisualPropertiesAttributes extends XmlAttributeComponent<INonVisualPropertiesAttributes> {
|
||||
protected xmlKeys = {
|
||||
id: "id",
|
||||
name: "name",
|
||||
descr: "desc",
|
||||
};
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { NonVisualPropertiesAttributes } from "./non-visual-properties-attributes";
|
||||
|
||||
export class NonVisualProperties extends XmlComponent {
|
||||
constructor() {
|
||||
super("pic:cNvPr");
|
||||
|
||||
this.root.push(
|
||||
new NonVisualPropertiesAttributes({
|
||||
id: 0,
|
||||
name: "",
|
||||
descr: "",
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IPicAttributes {
|
||||
xmlns?: string;
|
||||
}
|
||||
|
||||
export class PicAttributes extends XmlAttributeComponent<IPicAttributes> {
|
||||
protected xmlKeys = {
|
||||
xmlns: "xmlns:pic",
|
||||
};
|
||||
}
|
21
src/file/drawing/inline/graphic/graphic-data/pic/pic.ts
Normal file
21
src/file/drawing/inline/graphic/graphic-data/pic/pic.ts
Normal file
@ -0,0 +1,21 @@
|
||||
// http://officeopenxml.com/drwPic.php
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { BlipFill } from "./blip/blip-fill";
|
||||
import { NonVisualPicProperties } from "./non-visual-pic-properties/non-visual-pic-properties";
|
||||
import { PicAttributes } from "./pic-attributes";
|
||||
import { ShapeProperties } from "./shape-properties/shape-properties";
|
||||
|
||||
export class Pic extends XmlComponent {
|
||||
constructor(referenceId: number, x: number, y: number) {
|
||||
super("pic:pic");
|
||||
|
||||
this.root.push(
|
||||
new PicAttributes({
|
||||
xmlns: "http://schemas.openxmlformats.org/drawingml/2006/picture",
|
||||
}),
|
||||
);
|
||||
this.root.push(new NonVisualPicProperties());
|
||||
this.root.push(new BlipFill(referenceId));
|
||||
this.root.push(new ShapeProperties(x, y));
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IExtentsAttributes {
|
||||
cx?: number;
|
||||
cy?: number;
|
||||
}
|
||||
|
||||
export class ExtentsAttributes extends XmlAttributeComponent<IExtentsAttributes> {
|
||||
protected xmlKeys = {
|
||||
cx: "cx",
|
||||
cy: "cy",
|
||||
};
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
// http://officeopenxml.com/drwSp-size.php
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { ExtentsAttributes } from "./extents-attributes";
|
||||
|
||||
export class Extents extends XmlComponent {
|
||||
constructor(x: number, y: number) {
|
||||
super("a:ext");
|
||||
|
||||
this.root.push(
|
||||
new ExtentsAttributes({
|
||||
cx: x,
|
||||
cy: y,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
// http://officeopenxml.com/drwSp-size.php
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { Extents } from "./extents/extents";
|
||||
import { Offset } from "./offset/off";
|
||||
|
||||
export class Form extends XmlComponent {
|
||||
constructor(x: number, y: number) {
|
||||
super("a:xfrm");
|
||||
|
||||
this.root.push(new Extents(x, y));
|
||||
this.root.push(new Offset());
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export * from "./form";
|
@ -0,0 +1,13 @@
|
||||
import { XmlAttributeComponent } from "file/xml-components";
|
||||
|
||||
export interface IOffsetAttributes {
|
||||
x?: number;
|
||||
y?: number;
|
||||
}
|
||||
|
||||
export class OffsetAttributes extends XmlAttributeComponent<IOffsetAttributes> {
|
||||
protected xmlKeys = {
|
||||
x: "x",
|
||||
y: "y",
|
||||
};
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
// http://officeopenxml.com/drwSp-size.php
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { OffsetAttributes } from "./off-attributes";
|
||||
|
||||
export class Offset extends XmlComponent {
|
||||
constructor() {
|
||||
super("a:off");
|
||||
|
||||
this.root.push(
|
||||
new OffsetAttributes({
|
||||
x: 0,
|
||||
y: 0,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
export class NoFill extends XmlComponent {
|
||||
constructor() {
|
||||
super("a:noFill");
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
export class NoFill extends XmlComponent {
|
||||
constructor() {
|
||||
super("a:noFill");
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
// http://officeopenxml.com/drwSp-outline.php
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { NoFill } from "./no-fill";
|
||||
|
||||
export class Outline extends XmlComponent {
|
||||
constructor() {
|
||||
super("a:ln");
|
||||
|
||||
this.root.push(new NoFill());
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user