Compare commits

...

34 Commits
2.1.3 ... 3.1.0

Author SHA1 Message Date
cf1689a3c2 Version bump 2017-12-06 01:39:40 +00:00
66d0bab0a5 Add packPdf reference 2017-12-06 01:39:24 +00:00
5889f20f1e Simplify tests and made compile async 2017-12-06 01:32:57 +00:00
ebec10e312 Refactor 2017-12-06 01:03:14 +00:00
ae8a0c7fd0 Edit Tslint 2017-12-05 23:43:19 +00:00
bb49200fad Clean up code using request promise 2017-12-05 02:24:49 +00:00
a6bba0bc6c Enable pdf export 2017-12-05 00:16:21 +00:00
fb08f79344 Add initial conversion 2017-11-10 14:27:57 +00:00
741c74825e Class definition for Pdf converter wrapper 2017-09-30 18:23:02 +01:00
28539cd47b Initial scaffold of pdf convert 2017-09-30 18:15:33 +01:00
8ca7c5a343 Add test 2017-09-30 18:00:06 +01:00
32b56e7071 Move sub components of Paragraph into own folder 2017-09-30 01:52:37 +01:00
7dad717952 Add documentation links 2017-09-22 14:46:19 +01:00
34e928755f Add documentation links 2017-09-21 14:56:46 +01:00
51e0f311fe Refactor numbering into its own file 2017-09-20 13:37:39 +01:00
cc9dff6b94 Version bump 2017-09-19 16:02:32 +01:00
101cc0fdea Add docx keyword to package 2017-09-19 16:02:06 +01:00
d408262fa8 Remove unused class 2017-09-19 15:58:37 +01:00
ed53c30f42 Remove template file and placed in the Wiki
https://github.com/dolanmiu/docx/wiki/Contributing-Guidelines
2017-09-19 15:56:52 +01:00
ebbf6a99c1 Make exporting more consistent 2017-09-19 15:51:55 +01:00
b98c103e45 Export table into own file 2017-09-19 15:49:27 +01:00
518fec0595 Export like standard style 2017-09-19 15:47:29 +01:00
659936f3f0 Refactor xml component into own class 2017-09-19 15:46:20 +01:00
b0febf5054 Refactor run into own file 2017-09-19 15:42:40 +01:00
56b951a2b1 Refactor body into own file 2017-09-19 15:39:14 +01:00
a3a9958a69 Refactor document into own file 2017-09-19 15:36:41 +01:00
4f36bbf426 Refactor paragraph into its own file
Create barrel
2017-09-19 15:21:07 +01:00
357bc7f377 Add documentation reference 2017-09-19 15:17:58 +01:00
da8405b5b9 Add Right and Center tab stops 2017-09-19 12:51:37 +01:00
492face7ab 3.0.0 2017-09-17 00:20:18 +01:00
b6351f0260 Merge branch 'jacwright-indent-bc' 2017-09-17 00:09:57 +01:00
3a7f9053b9 Allow indent to use other options than left and hanging
This is a breaking change. Existing code using indent will break when padding in the number for left.
2017-09-16 17:05:40 -06:00
19b122684c Add lint to Travis
Fix linting
2017-09-17 00:01:09 +01:00
72e89cfc3c Move tests to respective folders as .spec
This is to keep to standards
2017-09-17 00:00:41 +01:00
77 changed files with 990 additions and 840 deletions

View File

@ -4,6 +4,7 @@ node_js:
install:
- npm install
script:
- npm run lint
- npm test
after_failure:
- "cat /home/travis/builds/dolanmiu/docx/npm-debug.log"

View File

@ -57,6 +57,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 +68,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 +90,3 @@ 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

View File

@ -1,6 +1,6 @@
{
"name": "docx",
"version": "2.1.3",
"version": "3.1.0",
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
"main": "build/index.js",
"scripts": {
@ -22,6 +22,7 @@
"url": "git+https://github.com/dolanmiu/docx.git"
},
"keywords": [
"docx",
"office",
"word",
"generate",
@ -36,7 +37,11 @@
"dependencies": {
"@types/archiver": "^1.3.4",
"@types/express": "^4.0.35",
"@types/request": "^2.0.3",
"@types/request-promise": "^4.1.39",
"archiver": "^1.3.0",
"request": "^2.83.0",
"request-promise": "^4.2.2",
"xml": "^1.0.1"
},
"author": "Dolan Miu",

View File

@ -1,11 +1,12 @@
import { assert } from "chai";
import { Body } from "../../../docx/document/body";
import { Columns } from "../../../docx/document/body/columns";
import { DocumentGrid } from "../../../docx/document/body/doc-grid";
import { PageMargin } from "../../../docx/document/body/page-margin";
import { PageSize } from "../../../docx/document/body/page-size";
import { SectionProperties } from "../../../docx/document/body/section-properties";
import { Utility } from "../../utility";
import { Utility } from "../../../tests/utility";
import { Body } from "./";
import { Columns } from "./columns";
import { DocumentGrid } from "./doc-grid";
import { PageMargin } from "./page-margin";
import { PageSize } from "./page-size";
import { SectionProperties } from "./section-properties";
describe("Body", () => {
let body: Body;

View File

@ -0,0 +1,12 @@
import { XmlComponent } from "../../xml-components";
export class Body extends XmlComponent {
constructor() {
super("w:body");
}
public push(component: XmlComponent): void {
this.root.push(component);
}
}

View File

@ -1,12 +1 @@
import { XmlComponent } from "../../xml-components";
export class Body extends XmlComponent {
constructor() {
super("w:body");
}
public push(component: XmlComponent): void {
this.root.push(component);
}
}
export * from "./body";

View File

@ -1,6 +1,7 @@
import { assert, expect } from "chai";
import * as docx from "../../../docx";
import { Formatter } from "../../../export/formatter";
import * as docx from "../../";
import { Formatter } from "../../export/formatter";
describe("Document", () => {
let document: docx.Document;

View File

@ -0,0 +1,56 @@
// http://officeopenxml.com/WPdocument.php
import { Paragraph } from "../paragraph";
import { Table } from "../table";
import { XmlComponent } from "../xml-components";
import { Body } from "./body";
import { DocumentAttributes } from "./document-attributes";
export class Document extends XmlComponent {
private body: Body;
constructor() {
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();
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;
}
}

View File

@ -1,55 +1 @@
import { Paragraph } from "../paragraph";
import { Table } from "../table";
import { XmlComponent } from "../xml-components";
import { Body } from "./body";
import { DocumentAttributes } from "./document-attributes";
export class Document extends XmlComponent {
private body: Body;
constructor() {
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();
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;
}
}
export * from "./document";

View File

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

View File

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

View File

@ -1,4 +1,5 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
// http://officeopenxml.com/WPalignment.php
import { XmlAttributeComponent, XmlComponent } from "../../xml-components";
export type AlignmentOptions = "left" | "center" | "right" | "both";

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import { ThematicBreak } from "../../../docx/paragraph/border";
import { Utility } from "../../utility";
import { Utility } from "../../../tests/utility";
import { ThematicBreak } from "./border";
describe("Border", () => {
// TODO: Need tests here
@ -14,6 +15,18 @@ describe("ThematicBreak", () => {
});
describe("#constructor()", () => {
it("should create valid JSON", () => {
const stringifiedJson = JSON.stringify(thematicBreak);
let newJson;
try {
newJson = JSON.parse(stringifiedJson);
} catch (e) {
assert.isTrue(false);
}
assert.isTrue(true);
});
it("should create a Thematic Break with correct border properties", () => {
const newJson = Utility.jsonify(thematicBreak);
const attributes = {

View File

@ -1,4 +1,5 @@
import { Attributes, XmlComponent } from "../xml-components";
// http://officeopenxml.com/WPborders.php
import { Attributes, XmlComponent } from "../../xml-components";
class Border extends XmlComponent {

View File

@ -0,0 +1,28 @@
// http://officeopenxml.com/WPindentation.php
import { XmlAttributeComponent, XmlComponent } from "../../xml-components";
interface IIndentAttributesProperties {
left?: number;
hanging?: number;
firstLine?: number;
start?: number;
end?: number;
}
class IndentAttributes extends XmlAttributeComponent<IIndentAttributesProperties> {
protected xmlKeys = {
left: "w:left",
hanging: "w:hanging",
firstLine: "w:firstLine",
start: "w:start",
end: "w:end",
};
}
export class Indent extends XmlComponent {
constructor(attrs: object) {
super("w:ind");
this.root.push(new IndentAttributes(attrs));
}
}

View File

@ -0,0 +1,9 @@
export * from "./alignment";
export * from "./border";
export * from "./indent";
export * from "./keep";
export * from "./page-break";
export * from "./spacing";
export * from "./style";
export * from "./tab-stop";
export * from "./unordered-list";

View File

@ -1,4 +1,4 @@
import { XmlComponent } from "../xml-components";
import { XmlComponent } from "../../xml-components";
export class KeepLines extends XmlComponent {
constructor() {

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import { PageBreak } from "../../../docx/paragraph/page-break";
import { Utility } from "../../utility";
import { Utility } from "../../../tests/utility";
import { PageBreak } from "./page-break";
describe("PageBreak", () => {
let pageBreak: PageBreak;

View File

@ -1,5 +1,6 @@
import { Run } from "../run";
import { Attributes, XmlComponent } from "../xml-components";
// http://officeopenxml.com/WPtextSpecialContent-break.php
import { Run } from "../../run";
import { Attributes, XmlComponent } from "../../xml-components";
class Break extends XmlComponent {

View File

@ -1,6 +1,7 @@
import { expect } from "chai";
import { Spacing } from "../../../docx/paragraph/spacing";
import { Formatter } from "../../../export/formatter";
import { Spacing } from "./spacing";
describe("Spacing", () => {
describe("#constructor", () => {

View File

@ -1,4 +1,5 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
// http://officeopenxml.com/WPspacing.php
import { XmlAttributeComponent, XmlComponent } from "../../xml-components";
export interface ISpacingProperties {
after?: number;

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import { Style } from "../../../docx/paragraph/style";
import { Utility } from "../../utility";
import { Utility } from "../../../tests/utility";
import { Style } from "./style";
describe("ParagraphStyle", () => {
let style: Style;

View File

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

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import { LeftTabStop, MaxRightTabStop } from "../../../docx/paragraph/tab-stop";
import { Utility } from "../../utility";
import { Utility } from "../../../tests/utility";
import { LeftTabStop, MaxRightTabStop } from "./tab-stop";
describe("LeftTabStop", () => {
let tabStop: LeftTabStop;

View File

@ -1,4 +1,5 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
// http://officeopenxml.com/WPtab.php
import { XmlAttributeComponent, XmlComponent } from "../../xml-components";
export class TabStop extends XmlComponent {
@ -8,15 +9,15 @@ export class TabStop extends XmlComponent {
}
}
export type TabOptions = "left" | "right";
export type TabValue = "left" | "right" | "center" | "bar" | "clear" | "decimal" | "end" | "num" | "start";
export class TabAttributes extends XmlAttributeComponent<{val: TabOptions, pos: string | number}> {
export class TabAttributes extends XmlAttributeComponent<{val: TabValue, pos: string | number}> {
protected xmlKeys = {val: "w:val", pos: "w:pos"};
}
export class Tab extends XmlComponent {
constructor(value: TabOptions, position: string | number) {
constructor(value: TabValue, position: string | number) {
super("w:tab");
this.root.push(new TabAttributes({
val: value,
@ -36,3 +37,15 @@ export class LeftTabStop extends TabStop {
super(new Tab("left", position));
}
}
export class RightTabStop extends TabStop {
constructor(position: number) {
super(new Tab("right", position));
}
}
export class CenterTabStop extends TabStop {
constructor(position: number) {
super(new Tab("center", position));
}
}

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import { NumberProperties } from "../../../docx/paragraph/unordered-list";
import { Utility } from "../../utility";
import { Utility } from "../../../tests/utility";
import { NumberProperties } from "./unordered-list";
describe("NumberProperties", () => {
let numberProperties: NumberProperties;

View File

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

View File

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

View File

@ -1,156 +1,3 @@
import { IData } from "../../media/data";
import { Num } from "../../numbering/num";
import { Run } from "../run";
import { PictureRun } from "../run/picture-run";
import { TextRun } from "../run/text-run";
import { XmlComponent } from "../xml-components";
import { Alignment } from "./alignment";
import { ThematicBreak } from "./border";
import { Indent } from "./indent";
import { KeepLines, KeepNext } from "./keep";
import { PageBreak } from "./page-break";
import { ParagraphProperties } from "./properties";
import { ISpacingProperties, Spacing } from "./spacing";
import { Style } from "./style";
import { LeftTabStop, MaxRightTabStop } from "./tab-stop";
import { NumberProperties } from "./unordered-list";
export * from "./formatting";
export class Paragraph extends XmlComponent {
private properties: ParagraphProperties;
constructor(text?: string) {
super("w:p");
this.properties = new ParagraphProperties();
this.root.push(this.properties);
if (text !== undefined) {
this.root.push(new TextRun(text));
}
}
public addRun(run: Run): Paragraph {
this.root.push(run);
return this;
}
public createTextRun(text: string): TextRun {
const run = new TextRun(text);
this.addRun(run);
return run;
}
public createPictureRun(imageData: IData): PictureRun {
const run = new PictureRun(imageData);
this.addRun(run);
return run;
}
public heading1(): Paragraph {
this.properties.push(new Style("Heading1"));
return this;
}
public heading2(): Paragraph {
this.properties.push(new Style("Heading2"));
return this;
}
public heading3(): Paragraph {
this.properties.push(new Style("Heading3"));
return this;
}
public heading4(): Paragraph {
this.properties.push(new Style("Heading4"));
return this;
}
public heading5(): Paragraph {
this.properties.push(new Style("Heading5"));
return this;
}
public title(): Paragraph {
this.properties.push(new Style("Title"));
return this;
}
public center(): Paragraph {
this.properties.push(new Alignment("center"));
return this;
}
public left(): Paragraph {
this.properties.push(new Alignment("left"));
return this;
}
public right(): Paragraph {
this.properties.push(new Alignment("right"));
return this;
}
public justified(): Paragraph {
this.properties.push(new Alignment("both"));
return this;
}
public thematicBreak(): Paragraph {
this.properties.push(new ThematicBreak());
return this;
}
public pageBreak(): Paragraph {
this.root.push(new PageBreak());
return this;
}
public maxRightTabStop(): Paragraph {
this.properties.push(new MaxRightTabStop());
return this;
}
public leftTabStop(position: number): Paragraph {
this.properties.push(new LeftTabStop(position));
return this;
}
public bullet(): Paragraph {
this.properties.push(new Style("ListParagraph"));
this.properties.push(new NumberProperties(1, 0));
return this;
}
public setNumbering(numbering: Num, indentLevel: number): Paragraph {
this.properties.push(new Style("ListParagraph"));
this.properties.push(new NumberProperties(numbering.id, indentLevel));
return this;
}
public style(styleId: string): Paragraph {
this.properties.push(new Style(styleId));
return this;
}
public indent(start: number, hanging?: number): Paragraph {
this.properties.push(new Indent(start, hanging));
return this;
}
public spacing(params: ISpacingProperties): Paragraph {
this.properties.push(new Spacing(params));
return this;
}
public keepNext(): Paragraph {
this.properties.push(new KeepNext());
return this;
}
public keepLines(): Paragraph {
this.properties.push(new KeepLines());
return this;
}
}
export * from "./paragraph";
export * from "./properties";

View File

@ -1,7 +1,8 @@
import { assert, expect } from "chai";
import * as docx from "../../../docx";
import { Formatter } from "../../../export/formatter";
import { Numbering } from "../../../numbering";
import * as docx from "../../docx";
import { Formatter } from "../../export/formatter";
import { Numbering } from "../../numbering";
describe("Paragraph", () => {
let paragraph: docx.Paragraph;
@ -145,7 +146,7 @@ describe("Paragraph", () => {
expect(tree).to.deep.equal({
"w:p": [{
"w:pPr": [],
},{
}, {
"w:r": [
{"w:rPr": []},
{"w:br": [{_attr: {"w:type": "page"}}]},
@ -240,7 +241,7 @@ describe("Paragraph", () => {
describe("#indent", () => {
it("should set the paragraph indent to the given values", () => {
paragraph.indent(720);
paragraph.indent({ left: 720 });
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [

View File

@ -0,0 +1,165 @@
// http://officeopenxml.com/WPparagraph.php
import { IData } from "../../media/data";
import { Num } from "../../numbering/num";
import { Run } from "../run";
import { PictureRun } from "../run/picture-run";
import { TextRun } from "../run/text-run";
import { XmlComponent } from "../xml-components";
import { Alignment } from "./formatting/alignment";
import { ThematicBreak } from "./formatting/border";
import { Indent } from "./formatting/indent";
import { KeepLines, KeepNext } from "./formatting/keep";
import { PageBreak } from "./formatting/page-break";
import { ISpacingProperties, Spacing } from "./formatting/spacing";
import { Style } from "./formatting/style";
import { CenterTabStop, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop";
import { NumberProperties } from "./formatting/unordered-list";
import { ParagraphProperties } from "./properties";
export class Paragraph extends XmlComponent {
private properties: ParagraphProperties;
constructor(text?: string) {
super("w:p");
this.properties = new ParagraphProperties();
this.root.push(this.properties);
if (text !== undefined) {
this.root.push(new TextRun(text));
}
}
public addRun(run: Run): Paragraph {
this.root.push(run);
return this;
}
public createTextRun(text: string): TextRun {
const run = new TextRun(text);
this.addRun(run);
return run;
}
public createPictureRun(imageData: IData): PictureRun {
const run = new PictureRun(imageData);
this.addRun(run);
return run;
}
public heading1(): Paragraph {
this.properties.push(new Style("Heading1"));
return this;
}
public heading2(): Paragraph {
this.properties.push(new Style("Heading2"));
return this;
}
public heading3(): Paragraph {
this.properties.push(new Style("Heading3"));
return this;
}
public heading4(): Paragraph {
this.properties.push(new Style("Heading4"));
return this;
}
public heading5(): Paragraph {
this.properties.push(new Style("Heading5"));
return this;
}
public title(): Paragraph {
this.properties.push(new Style("Title"));
return this;
}
public center(): Paragraph {
this.properties.push(new Alignment("center"));
return this;
}
public left(): Paragraph {
this.properties.push(new Alignment("left"));
return this;
}
public right(): Paragraph {
this.properties.push(new Alignment("right"));
return this;
}
public justified(): Paragraph {
this.properties.push(new Alignment("both"));
return this;
}
public thematicBreak(): Paragraph {
this.properties.push(new ThematicBreak());
return this;
}
public pageBreak(): Paragraph {
this.root.push(new PageBreak());
return this;
}
public maxRightTabStop(): Paragraph {
this.properties.push(new MaxRightTabStop());
return this;
}
public leftTabStop(position: number): Paragraph {
this.properties.push(new LeftTabStop(position));
return this;
}
public rightTabStop(position: number): Paragraph {
this.properties.push(new RightTabStop(position));
return this;
}
public centerTabStop(position: number): Paragraph {
this.properties.push(new CenterTabStop(position));
return this;
}
public bullet(): Paragraph {
this.properties.push(new Style("ListParagraph"));
this.properties.push(new NumberProperties(1, 0));
return this;
}
public setNumbering(numbering: Num, indentLevel: number): Paragraph {
this.properties.push(new Style("ListParagraph"));
this.properties.push(new NumberProperties(numbering.id, indentLevel));
return this;
}
public style(styleId: string): Paragraph {
this.properties.push(new Style(styleId));
return this;
}
public indent(attrs: object): Paragraph {
this.properties.push(new Indent(attrs));
return this;
}
public spacing(params: ISpacingProperties): Paragraph {
this.properties.push(new Spacing(params));
return this;
}
public keepNext(): Paragraph {
this.properties.push(new KeepNext());
return this;
}
public keepLines(): Paragraph {
this.properties.push(new KeepLines());
return this;
}
}

View File

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

View File

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

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import { Break } from "../../../docx/run/break";
import { Utility } from "../../utility";
import { Utility } from "../../tests/utility";
import { Break } from "./break";
describe("Break", () => {
let currentBreak: Break;

View File

@ -1,3 +1,4 @@
// http://officeopenxml.com/WPtextSpecialContent-break.php
import { XmlComponent } from "../xml-components";
export class Break extends XmlComponent {

View File

@ -1,96 +1,3 @@
import { Break } from "./break";
import { Caps, SmallCaps } from "./caps";
import { Bold, Color, DoubleStrike, Italics, Size, Strike } from "./formatting";
import { RunProperties } from "./properties";
import { RunFonts } from "./run-fonts";
import { SubScript, SuperScript } from "./script";
import { Style } from "./style";
import { Tab } from "./tab";
import { Underline } from "./underline";
import { XmlComponent } from "../xml-components";
export class Run extends XmlComponent {
private properties: RunProperties;
constructor() {
super("w:r");
this.properties = new RunProperties();
this.root.push(this.properties);
}
public bold(): Run {
this.properties.push(new Bold());
return this;
}
public italic(): Run {
this.properties.push(new Italics());
return this;
}
public underline(underlineType?: string, color?: string): Run {
this.properties.push(new Underline(underlineType, color));
return this;
}
public color(color: string): Run {
this.properties.push(new Color(color));
return this;
}
public size(size: number): Run {
this.properties.push(new Size(size));
return this;
}
public break(): Run {
this.root.splice(1, 0, new Break());
return this;
}
public tab(): Run {
this.root.splice(1, 0, new Tab());
return this;
}
public smallCaps(): Run {
this.properties.push(new SmallCaps());
return this;
}
public allCaps(): Run {
this.properties.push(new Caps());
return this;
}
public strike(): Run {
this.properties.push(new Strike());
return this;
}
public doubleStrike(): Run {
this.properties.push(new DoubleStrike());
return this;
}
public subScript(): Run {
this.properties.push(new SubScript());
return this;
}
public superScript(): Run {
this.properties.push(new SuperScript());
return this;
}
public font(fontName: string): Run {
this.properties.push(new RunFonts(fontName));
return this;
}
public style(styleId: string): Run {
this.properties.push(new Style(styleId));
return this;
}
}
export * from "./run";
export * from "./text-run";
export * from "./picture-run";

View File

@ -1,7 +1,8 @@
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;

View File

@ -1,6 +1,7 @@
import { expect } from "chai";
import { Text } from "../../../../docx/run/run-components/text";
import { Formatter } from "../../../../export/formatter";
import { Formatter } from "../../../export/formatter";
import { Text } from "./text";
describe("Text", () => {
describe("#constructor", () => {

View File

@ -1,6 +1,7 @@
import { expect } from "chai";
import { RunFonts } from "../../../docx/run/run-fonts";
import { Formatter } from "../../../export/formatter";
import { Formatter } from "../../export/formatter";
import { RunFonts } from "./run-fonts";
describe("RunFonts", () => {

View File

@ -1,7 +1,8 @@
import { assert, expect } from "chai";
import { Run } from "../../../docx/run";
import { Formatter } from "../../../export/formatter";
import { Utility } from "../../utility";
import { Formatter } from "../../export/formatter";
import { Utility } from "../../tests/utility";
import { Run } from "./";
describe("Run", () => {
let run: Run;

97
ts/docx/run/run.ts Normal file
View File

@ -0,0 +1,97 @@
// http://officeopenxml.com/WPtext.php
import { Break } from "./break";
import { Caps, SmallCaps } from "./caps";
import { Bold, Color, DoubleStrike, Italics, Size, Strike } from "./formatting";
import { RunProperties } from "./properties";
import { RunFonts } from "./run-fonts";
import { SubScript, SuperScript } from "./script";
import { Style } from "./style";
import { Tab } from "./tab";
import { Underline } from "./underline";
import { XmlComponent } from "../xml-components";
export class Run extends XmlComponent {
private properties: RunProperties;
constructor() {
super("w:r");
this.properties = new RunProperties();
this.root.push(this.properties);
}
public bold(): Run {
this.properties.push(new Bold());
return this;
}
public italic(): Run {
this.properties.push(new Italics());
return this;
}
public underline(underlineType?: string, color?: string): Run {
this.properties.push(new Underline(underlineType, color));
return this;
}
public color(color: string): Run {
this.properties.push(new Color(color));
return this;
}
public size(size: number): Run {
this.properties.push(new Size(size));
return this;
}
public break(): Run {
this.root.splice(1, 0, new Break());
return this;
}
public tab(): Run {
this.root.splice(1, 0, new Tab());
return this;
}
public smallCaps(): Run {
this.properties.push(new SmallCaps());
return this;
}
public allCaps(): Run {
this.properties.push(new Caps());
return this;
}
public strike(): Run {
this.properties.push(new Strike());
return this;
}
public doubleStrike(): Run {
this.properties.push(new DoubleStrike());
return this;
}
public subScript(): Run {
this.properties.push(new SubScript());
return this;
}
public superScript(): Run {
this.properties.push(new SuperScript());
return this;
}
public font(fontName: string): Run {
this.properties.push(new RunFonts(fontName));
return this;
}
public style(styleId: string): Run {
this.properties.push(new Style(styleId));
return this;
}
}

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import { SubScript, SuperScript } from "../../../docx/run/script";
import { Utility } from "../../utility";
import { Utility } from "../../tests/utility";
import { SubScript, SuperScript } from "./script";
describe("SubScript", () => {
let subScript: SubScript;

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import { DoubleStrike, Strike } from "../../../docx/run/formatting";
import { Utility } from "../../utility";
import { Utility } from "../../tests/utility";
import { DoubleStrike, Strike } from "./formatting";
describe("Strike", () => {
let strike: Strike;

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import { Tab } from "../../../docx/run/tab";
import { Utility } from "../../utility";
import { Utility } from "../../tests/utility";
import { Tab } from "./tab";
describe("Tab", () => {
let tab: Tab;

View File

@ -1,6 +1,7 @@
import { expect } from "chai";
import { TextRun } from "../../../docx/run/text-run";
import { Formatter } from "../../../export/formatter";
import { Formatter } from "../../export/formatter";
import { TextRun } from "./text-run";
describe("TextRun", () => {
let run: TextRun;

View File

@ -1,7 +1,8 @@
import { assert, expect } from "chai";
import * as u from "../../../docx/run/underline";
import { Formatter } from "../../../export/formatter";
import { Utility } from "../../utility";
import { Formatter } from "../../export/formatter";
import { Utility } from "../../tests/utility";
import * as u from "./underline";
describe("Underline", () => {

View File

@ -1,6 +1,7 @@
import { expect } from "chai";
import { GridCol, TableGrid } from "../../../docx/table/grid";
import { Formatter } from "../../../export/formatter";
import { Formatter } from "../../export/formatter";
import { GridCol, TableGrid } from "./grid";
describe("GridCol", () => {
describe("#constructor", () => {

View File

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

View File

@ -1,6 +1,7 @@
import { expect } from "chai";
import { TableProperties } from "../../../docx/table/properties";
import { Formatter } from "../../../export/formatter";
import { Formatter } from "../../export/formatter";
import { TableProperties } from "./properties";
describe("TableProperties", () => {
describe("#constructor", () => {

View File

@ -1,8 +1,9 @@
/* tslint:disable:no-unused-expression */
import { expect } from "chai";
import { Paragraph } from "../../../docx/paragraph";
import { Table } from "../../../docx/table";
import { Formatter } from "../../../export/formatter";
import { Formatter } from "../../export/formatter";
import { Paragraph } from "../paragraph";
import { Table } from "./";
describe("Table", () => {
describe("#constructor", () => {

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

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

View File

@ -1,5 +1,6 @@
import { assert } from "chai";
import { Attributes } from "../../../docx/xml-components";
import { Attributes } from "./";
describe("Attribute", () => {
describe("#constructor()", () => {

View File

@ -1,27 +1,3 @@
import { BaseXmlComponent } from "./base";
import { IXmlableObject } from "./xmlable-object";
export { BaseXmlComponent };
export abstract class XmlComponent extends BaseXmlComponent {
protected root: Array<BaseXmlComponent | string>;
constructor(rootKey: string) {
super(rootKey);
this.root = new Array<BaseXmlComponent>();
}
public prepForXml(): IXmlableObject {
const children = this.root.map((comp) => {
if (comp instanceof BaseXmlComponent) {
return comp.prepForXml();
}
return comp;
}).filter((comp) => comp); // Exclude null, undefined, and empty strings
return {
[this.rootKey]: children,
};
}
}
export * from "./xml-component";
export * from "./attributes";
export * from "./default-attributes";

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import { XmlComponent } from "../../../docx/xml-components";
import { Utility } from "../../utility";
import { Utility } from "../../tests/utility";
import { XmlComponent } from "./";
class TestComponent extends XmlComponent {

View File

@ -0,0 +1,24 @@
import { BaseXmlComponent } from "./base";
import { IXmlableObject } from "./xmlable-object";
export { BaseXmlComponent };
export abstract class XmlComponent extends BaseXmlComponent {
protected root: Array<BaseXmlComponent | string>;
constructor(rootKey: string) {
super(rootKey);
this.root = new Array<BaseXmlComponent>();
}
public prepForXml(): IXmlableObject {
const children = this.root.map((comp) => {
if (comp instanceof BaseXmlComponent) {
return comp.prepForXml();
}
return comp;
}).filter((comp) => comp); // Exclude null, undefined, and empty strings
return {
[this.rootKey]: children,
};
}
}

View File

@ -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 * as docx from "../docx";
import { Attributes } from "../docx/xml-components";
import { Formatter } from "../export/formatter";
import { Properties } from "../properties";
import { Utility } from "../tests/utility";
describe("Formatter", () => {
let formatter: Formatter;

View File

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

View File

@ -0,0 +1,99 @@
import * as archiver from "archiver";
import * as express from "express";
import * as fs from "fs";
import * as path from "path";
import * as xml from "xml";
import { Document } from "../../docx";
import { Media } from "../../media";
import { Numbering } from "../../numbering";
import { Properties } from "../../properties";
import { Styles } from "../../styles";
import { DefaultStylesFactory } from "../../styles/factory";
import { Formatter } from "../formatter";
const TEMPLATE_PATH = path.resolve(__dirname, "../../../template");
export class Compiler {
protected archive: archiver.Archiver;
private formatter: Formatter;
private style: Styles;
constructor(
protected document: Document,
style?: Styles,
private properties: Properties = new Properties({
creator: "Un-named",
revision: "1",
lastModifiedBy: "Un-named",
}),
private numbering: Numbering = new Numbering(),
private media: Media = new Media(),
) {
this.formatter = new Formatter();
this.archive = archiver.create("zip", {});
if (style) {
this.style = style;
} else {
const stylesFactory = new DefaultStylesFactory();
this.style = stylesFactory.newInstance();
}
this.archive.on("error", (err) => {
throw err;
});
}
public async compile(output: fs.WriteStream | express.Response): Promise<void> {
this.archive.pipe(output);
this.archive.glob("**", {
cwd: TEMPLATE_PATH,
});
this.archive.glob("**/.rels", {
cwd: TEMPLATE_PATH,
});
const xmlDocument = xml(this.formatter.format(this.document));
const xmlStyles = xml(this.formatter.format(this.style));
const xmlProperties = xml(this.formatter.format(this.properties), {
declaration: {
standalone: "yes",
encoding: "UTF-8",
},
});
const xmlNumbering = xml(this.formatter.format(this.numbering));
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(xmlNumbering, {
name: "word/numbering.xml",
});
for (const data of this.media.array) {
this.archive.append(data.stream, {
name: `media/${data.fileName}`,
});
}
this.archive.finalize();
return new Promise<void>((resolve) => {
output.on("close", () => {
resolve();
});
});
}
}

View File

@ -1,16 +1,20 @@
import * as express from "express";
import { Document } from "../../docx/document";
import { Media } from "../../media";
import { Numbering } from "../../numbering";
import { Properties } from "../../properties";
import { Styles } from "../../styles";
import { Packer } from "./packer";
import { Compiler } from "./compiler";
import { IPacker } from "./packer";
export class ExpressPacker extends Packer {
export class ExpressPacker implements IPacker {
private res: express.Response;
private packer: Compiler;
constructor(document: Document, res: express.Response, styles?: Styles, properties?: Properties, numbering?: Numbering, media?: Media) {
super(document, styles, properties, numbering, media);
this.packer = new Compiler(document, styles, properties, numbering, media);
this.res = res;
this.res.on("close", () => {
@ -18,8 +22,10 @@ export class ExpressPacker extends Packer {
});
}
public pack(name: string): void {
public async pack(name: string): Promise<void> {
name = name.replace(/.docx$/, "");
this.res.attachment(`${name}.docx`);
super.compile(this.res);
await this.packer.compile(this.res);
}
}

View File

@ -1,4 +1,4 @@
import { assert } from "chai";
/* tslint:disable:typedef space-before-function-paren */
import * as fs from "fs";
import { Document } from "../../docx/document";
@ -7,7 +7,7 @@ import { LocalPacker } from "../../export/packer/local";
import { Properties } from "../../properties";
import { DefaultStylesFactory } from "../../styles/factory";
describe("Packer", () => {
describe("LocalPacker", () => {
let packer: LocalPacker;
let stylesFactory: DefaultStylesFactory;
@ -29,27 +29,19 @@ describe("Packer", () => {
});
describe("#pack()", () => {
/* tslint:disable */
it("should create a standard docx file", function (done) {
/* tslint:enable */
it("should create a standard docx file", async function () {
this.timeout(99999999);
packer.pack("build-tests/tests/test.docx");
const int = setInterval(() => {
const stats = fs.statSync("build-tests/tests/test.docx");
if (stats.size > 2000) {
clearInterval(int);
clearTimeout(out);
done();
}
}, 1000);
const out = setTimeout(() => {
clearInterval(int);
try {
assert(false, "did not create a file within the alloted time");
} catch (e) {
done(e);
}
}, 2000);
await packer.pack("build-tests/tests/test");
fs.statSync("build-tests/tests/test.docx");
});
});
describe("#packPdf", () => {
it("should create a standard PDF file", async function () {
this.timeout(99999999);
await packer.packPdf("build-tests/tests/pdf-test");
fs.statSync("build-tests/tests/pdf-test.pdf");
});
});
});

View File

@ -1,21 +1,52 @@
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import { Document } from "../../docx/document";
import { Media } from "../../media";
import { Numbering } from "../../numbering";
import { Properties } from "../../properties";
import { Styles } from "../../styles";
import { Packer } from "./packer";
import { Compiler } from "./compiler";
import { IPacker } from "./packer";
import { PdfConvertWrapper } from "./pdf-convert-wrapper";
export class LocalPacker extends Packer {
export class LocalPacker implements IPacker {
private stream: fs.WriteStream;
private pdfConverter: PdfConvertWrapper;
private packer: Compiler;
constructor(document: Document, styles?: Styles, properties?: Properties, numbering?: Numbering, media?: Media) {
super(document, styles, properties, numbering, media);
this.pdfConverter = new PdfConvertWrapper();
this.packer = new Compiler(document, styles, properties, numbering, media);
}
public pack(path: string): void {
path = path.replace(/.docx$/, "");
this.stream = fs.createWriteStream(`${path}.docx`);
super.compile(this.stream);
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();
});
});
}
}

View File

@ -1,91 +1,3 @@
import * as archiver from "archiver";
import * as express from "express";
import * as fs from "fs";
import * as path from "path";
import * as xml from "xml";
import { Document } from "../../docx";
import { Media } from "../../media";
import { Numbering } from "../../numbering";
import { Properties } from "../../properties";
import { Styles } from "../../styles";
import { DefaultStylesFactory } from "../../styles/factory";
import { Formatter } from "../formatter";
const TEMPLATE_PATH = path.resolve(__dirname, "../../../template");
export abstract class Packer {
protected archive: archiver.Archiver;
private formatter: Formatter;
private style: Styles;
constructor(
protected document: Document,
style?: Styles,
private properties: Properties = new Properties({
creator: "Un-named",
revision: "1",
lastModifiedBy: "Un-named",
}),
private numbering: Numbering = new Numbering(),
private media: Media = new Media(),
) {
this.formatter = new Formatter();
this.archive = archiver.create("zip", {});
if (style) {
this.style = style;
} else {
const stylesFactory = new DefaultStylesFactory();
this.style = stylesFactory.newInstance();
}
this.archive.on("error", (err) => {
throw err;
});
}
protected compile(output: fs.WriteStream | express.Response): void {
this.archive.pipe(output);
this.archive.glob("**", {
cwd: TEMPLATE_PATH,
});
this.archive.glob("**/.rels", {
cwd: TEMPLATE_PATH,
});
const xmlDocument = xml(this.formatter.format(this.document));
const xmlStyles = xml(this.formatter.format(this.style));
const xmlProperties = xml(this.formatter.format(this.properties), {
declaration: {
standalone: "yes",
encoding: "UTF-8",
},
});
const xmlNumbering = xml(this.formatter.format(this.numbering));
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(xmlNumbering, {
name: "word/numbering.xml",
});
for (const data of this.media.array) {
this.archive.append(data.stream, {
name: `media/${data.fileName}`,
});
}
this.archive.finalize();
}
export interface IPacker {
pack(path: string): void;
}

View File

@ -0,0 +1,34 @@
/* 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": "",
},
});
}
}

View File

@ -1,87 +1 @@
import { DocumentAttributes } from "../docx/document/document-attributes";
import { Indent } from "../docx/paragraph/indent";
import { RunFonts } from "../docx/run/run-fonts";
import { XmlComponent } from "../docx/xml-components";
import { AbstractNumbering } from "./abstract-numbering";
import { Num } from "./num";
export class Numbering extends XmlComponent {
private nextId: number;
constructor() {
super("w:numbering");
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.nextId = 0;
const abstractNumbering = this.createAbstractNumbering();
abstractNumbering.createLevel(0, "bullet", "•", "left")
.addParagraphProperty(new Indent(720, 360))
.addRunProperty(new RunFonts("Symbol", "default"));
abstractNumbering.createLevel(1, "bullet", "o", "left")
.addParagraphProperty(new Indent(1440, 360))
.addRunProperty(new RunFonts("Courier New", "default"));
abstractNumbering.createLevel(2, "bullet", "•", "left")
.addParagraphProperty(new Indent(2160, 360))
.addRunProperty(new RunFonts("Wingdings", "default"));
abstractNumbering.createLevel(3, "bullet", "•", "left")
.addParagraphProperty(new Indent(2880, 360))
.addRunProperty(new RunFonts("Symbol", "default"));
abstractNumbering.createLevel(4, "bullet", "o", "left")
.addParagraphProperty(new Indent(3600, 360))
.addRunProperty(new RunFonts("Courier New", "default"));
abstractNumbering.createLevel(5, "bullet", "•", "left")
.addParagraphProperty(new Indent(4320, 360))
.addRunProperty(new RunFonts("Wingdings", "default"));
abstractNumbering.createLevel(6, "bullet", "•", "left")
.addParagraphProperty(new Indent(5040, 360))
.addRunProperty(new RunFonts("Symbol", "default"));
abstractNumbering.createLevel(7, "bullet", "o", "left")
.addParagraphProperty(new Indent(5760, 360))
.addRunProperty(new RunFonts("Courier New", "default"));
abstractNumbering.createLevel(8, "bullet", "•", "left")
.addParagraphProperty(new Indent(6480, 360))
.addRunProperty(new RunFonts("Wingdings", "default"));
this.createConcreteNumbering(abstractNumbering);
}
public createAbstractNumbering(): AbstractNumbering {
const num = new AbstractNumbering(this.nextId++);
this.root.push(num);
return num;
}
public createConcreteNumbering(abstractNumbering: AbstractNumbering): Num {
const num = new Num(this.nextId++, abstractNumbering.id);
this.root.push(num);
return num;
}
}
export * from "./numbering";

View File

@ -196,8 +196,8 @@ export class LevelBase extends XmlComponent {
return this;
}
public indent(left: number, hanging?: number): Level {
this.addParagraphProperty(new paragraph.Indent(left, hanging));
public indent(attrs: object): Level {
this.addParagraphProperty(new paragraph.Indent(attrs));
return this;
}

View File

@ -1,9 +1,9 @@
import { expect } from "chai";
import { Formatter } from "../export/formatter";
import { Numbering } from "../numbering";
import { AbstractNumbering } from "../numbering/abstract-numbering";
import { LevelForOverride } from "../numbering/level";
import { Num } from "../numbering/num";
import { Numbering } from "./";
import { AbstractNumbering } from "./abstract-numbering";
import { LevelForOverride } from "./level";
import { Num } from "./num";
describe("Numbering", () => {
@ -111,7 +111,7 @@ describe("AbstractNumbering", () => {
it("#indent", () => {
const abstractNumbering = new AbstractNumbering(1);
const level = abstractNumbering.createLevel(0, "lowerLetter", "%0.")
.indent(720);
.indent({ left: 720 });
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:pPr": [{"w:ind": [{_attr: {"w:left": 720}}]}],

87
ts/numbering/numbering.ts Normal file
View File

@ -0,0 +1,87 @@
import { DocumentAttributes } from "../docx/document/document-attributes";
import { Indent } from "../docx/paragraph/formatting";
import { RunFonts } from "../docx/run/run-fonts";
import { XmlComponent } from "../docx/xml-components";
import { AbstractNumbering } from "./abstract-numbering";
import { Num } from "./num";
export class Numbering extends XmlComponent {
private nextId: number;
constructor() {
super("w:numbering");
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.nextId = 0;
const abstractNumbering = this.createAbstractNumbering();
abstractNumbering.createLevel(0, "bullet", "•", "left")
.addParagraphProperty(new Indent({ left: 720, hanging: 360 }))
.addRunProperty(new RunFonts("Symbol", "default"));
abstractNumbering.createLevel(1, "bullet", "o", "left")
.addParagraphProperty(new Indent({ left: 1440, hanging: 360 }))
.addRunProperty(new RunFonts("Courier New", "default"));
abstractNumbering.createLevel(2, "bullet", "•", "left")
.addParagraphProperty(new Indent({ left: 2160, hanging: 360 }))
.addRunProperty(new RunFonts("Wingdings", "default"));
abstractNumbering.createLevel(3, "bullet", "•", "left")
.addParagraphProperty(new Indent({ left: 2880, hanging: 360 }))
.addRunProperty(new RunFonts("Symbol", "default"));
abstractNumbering.createLevel(4, "bullet", "o", "left")
.addParagraphProperty(new Indent({ left: 3600, hanging: 360 }))
.addRunProperty(new RunFonts("Courier New", "default"));
abstractNumbering.createLevel(5, "bullet", "•", "left")
.addParagraphProperty(new Indent({ left: 4320, hanging: 360 }))
.addRunProperty(new RunFonts("Wingdings", "default"));
abstractNumbering.createLevel(6, "bullet", "•", "left")
.addParagraphProperty(new Indent({ left: 5040, hanging: 360 }))
.addRunProperty(new RunFonts("Symbol", "default"));
abstractNumbering.createLevel(7, "bullet", "o", "left")
.addParagraphProperty(new Indent({ left: 5760, hanging: 360 }))
.addRunProperty(new RunFonts("Courier New", "default"));
abstractNumbering.createLevel(8, "bullet", "•", "left")
.addParagraphProperty(new Indent({ left: 6480, hanging: 360 }))
.addRunProperty(new RunFonts("Wingdings", "default"));
this.createConcreteNumbering(abstractNumbering);
}
public createAbstractNumbering(): AbstractNumbering {
const num = new AbstractNumbering(this.nextId++);
this.root.push(num);
return num;
}
public createConcreteNumbering(abstractNumbering: AbstractNumbering): Num {
const num = new Num(this.nextId++, abstractNumbering.id);
this.root.push(num);
return num;
}
}

View File

@ -1,7 +1,7 @@
import { expect } from "chai";
import { Formatter } from "../export/formatter";
import { Properties } from "../properties";
import { Properties } from "./";
describe("Properties", () => {

View File

@ -1,7 +1,7 @@
import { RunProperties } from "../../docx/run/properties";
import { XmlComponent } from "../../docx/xml-components";
import { RunFonts } from "../../docx/run/run-fonts";
import { Size } from "../../docx/run/formatting";
import { RunProperties } from "../../docx/run/properties";
import { RunFonts } from "../../docx/run/run-fonts";
import { XmlComponent } from "../../docx/xml-components";
export class RunPropertiesDefaults extends XmlComponent {
private properties: RunProperties;

View File

@ -1,7 +1,7 @@
import { Color, Italics, Size } from "../docx/run/formatting";
import { Styles } from "./";
import { DocumentDefaults } from "./defaults";
// import { DocumentDefaults } from "./defaults";
import {
Heading1Style, Heading2Style, Heading3Style, Heading4Style, Heading5Style, Heading6Style,
ListParagraph, TitleStyle,

View File

@ -1,7 +1,7 @@
import { DocumentAttributes } from "../docx/document/document-attributes";
import { XmlComponent } from "../docx/xml-components";
import { ParagraphStyle } from "./style";
import { DocumentDefaults } from "./defaults";
import { ParagraphStyle } from "./style";
export class Styles extends XmlComponent {

View File

@ -1,4 +1,4 @@
import * as paragraph from "../../docx/paragraph/formatting";
import * as paragraph from "../../docx/paragraph";
import * as formatting from "../../docx/run/formatting";
import { RunProperties } from "../../docx/run/properties";
import { XmlAttributeComponent, XmlComponent } from "../../docx/xml-components";
@ -171,8 +171,8 @@ export class ParagraphStyle extends Style {
return this;
}
public indent(left: number, hanging?: number): ParagraphStyle {
this.addParagraphProperty(new paragraph.Indent(left, hanging));
public indent(attrs: object): ParagraphStyle {
this.addParagraphProperty(new paragraph.Indent(attrs));
return this;
}

View File

@ -1,8 +1,8 @@
import { assert, expect } from "chai";
import { Formatter } from "../export/formatter";
import { Styles } from "../styles";
import { ParagraphStyle, Style } from "../styles/style";
import * as components from "../styles/style/components";
import { Styles } from "./";
import { ParagraphStyle, Style } from "./style";
import * as components from "./style/components";
describe("Styles", () => {
let styles: Styles;
@ -185,7 +185,7 @@ describe("ParagraphStyle", () => {
describe("formatting methods: paragraph properties", () => {
it("#indent", () => {
const style = new ParagraphStyle("myStyleId")
.indent(720);
.indent({ left: 720 });
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [

View File

@ -8,6 +8,11 @@
"outDir": "../build-tests",
"sourceRoot": "./",
"rootDir": "./",
"module": "commonjs"
}
"module": "commonjs",
"noUnusedLocals": true
},
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@ -1,17 +0,0 @@
/* tslint:disable */
import { assert } from "chai";
import { Utility } from "./utility";
/* tslint:enable */
describe("", () => {
beforeEach(() => {
// TODO
});
describe("#methodName()", () => {
it("should ", () => {
// TODO
});
});
});

View File

@ -9,9 +9,12 @@
"sourceRoot": "./",
"rootDir": "./",
"module": "commonjs",
"declaration": true
"declaration": true,
"noUnusedLocals": true
},
"exclude": [
"tests"
"tests",
"**/*.spec.ts",
"**/_*"
]
}

View File

@ -33,6 +33,7 @@
],
"no-unused-variable": [
true
]
],
"no-implicit-dependencies": false
}
}