Move ts to src folder for standards

This commit is contained in:
Dolan
2017-12-15 00:01:59 +00:00
parent cf1689a3c2
commit 01950ed443
122 changed files with 5 additions and 5 deletions

View File

@ -0,0 +1,50 @@
import { assert } from "chai";
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;
beforeEach(() => {
body = new Body();
body.push(new SectionProperties());
body.push(new PageSize());
body.push(new PageMargin());
body.push(new Columns());
body.push(new DocumentGrid());
});
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");
});
});
});

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

@ -0,0 +1,11 @@
import { Attributes, XmlComponent } from "../../xml-components";
export class Columns extends XmlComponent {
constructor() {
super("w:cols");
this.root.push(new Attributes({
space: "708",
}));
}
}

View File

@ -0,0 +1,11 @@
import { Attributes, XmlComponent } from "../../xml-components";
export class DocumentGrid extends XmlComponent {
constructor() {
super("w:docGrid");
this.root.push(new Attributes({
linePitch: "360",
}));
}
}

View File

@ -0,0 +1 @@
export * from "./body";

View File

@ -0,0 +1,17 @@
import { Attributes, XmlComponent } from "../../xml-components";
export class PageMargin extends XmlComponent {
constructor() {
super("w:pgMar");
this.root.push(new Attributes({
top: "1440",
right: "1440",
bottom: "1440",
left: "1440",
header: "708",
footer: "708",
gutter: "0",
}));
}
}

View File

@ -0,0 +1,12 @@
import { Attributes, XmlComponent } from "../../xml-components";
export class PageSize extends XmlComponent {
constructor() {
super("w:pgSz");
this.root.push(new Attributes({
w: "11906",
h: "16838",
}));
}
}

View File

@ -0,0 +1,21 @@
import { Attributes, XmlComponent } from "../../xml-components";
import { Columns } from "./columns";
import { DocumentGrid } from "./doc-grid";
import { PageMargin } from "./page-margin";
import { PageSize } from "./page-size";
export class SectionProperties extends XmlComponent {
constructor() {
super("w:sectPr");
this.root.push(new Attributes({
rsidR: "00B64E8F",
rsidRPr: "00D842E4",
rsidSect: "000A6AD0",
}));
this.root.push(new PageSize());
this.root.push(new PageMargin());
this.root.push(new Columns());
this.root.push(new DocumentGrid());
}
}

View File

@ -0,0 +1,55 @@
import { XmlAttributeComponent } from "../xml-components";
export interface IDocumentAttributesProperties {
wpc?: string;
mc?: string;
o?: string;
r?: string;
m?: string;
v?: string;
wp14?: string;
wp?: string;
w10?: string;
w?: string;
w14?: string;
w15?: string;
wpg?: string;
wpi?: string;
wne?: string;
wps?: string;
Ignorable?: string;
cp?: string;
dc?: string;
dcterms?: string;
dcmitype?: string;
xsi?: string;
type?: string;
}
export class DocumentAttributes extends XmlAttributeComponent<IDocumentAttributesProperties> {
protected xmlKeys = {
wpc: "xmlns:wpc",
mc: "xmlns:mc",
o: "xmlns:o",
r: "xmlns:r",
m: "xmlns:m",
v: "xmlns:v",
wp14: "xmlns:wp14",
wp: "xmlns:wp",
w10: "xmlns:w10",
w: "xmlns:w",
w14: "xmlns:w14",
w15: "xmlns:w15",
wpg: "xmlns:wpg",
wpi: "xmlns:wpi",
wne: "xmlns:wne",
wps: "xmlns:wps",
Ignorable: "mc:Ignorable",
cp: "xmlns:cp",
dc: "xmlns:dc",
dcterms: "xmlns:dcterms",
dcmitype: "xmlns:dcmitype",
xsi: "xmlns:xsi",
type: "xsi:type",
};
}

View File

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

View File

@ -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

@ -0,0 +1 @@
export * from "./document";

4
src/docx/index.ts Normal file
View File

@ -0,0 +1,4 @@
export * from "./document";
export * from "./paragraph";
export * from "./run";
export { Table } from "./table";

View File

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

View File

@ -0,0 +1,41 @@
import { assert } from "chai";
import { Utility } from "../../../tests/utility";
import { ThematicBreak } from "./border";
describe("Border", () => {
// TODO: Need tests here
});
describe("ThematicBreak", () => {
let thematicBreak: ThematicBreak;
beforeEach(() => {
thematicBreak = new 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 = {
color: "auto",
space: "1",
val: "single",
sz: "6",
};
assert.equal(JSON.stringify(newJson.root[0].root[0].root), JSON.stringify(attributes));
});
});
});

View File

@ -0,0 +1,23 @@
// http://officeopenxml.com/WPborders.php
import { Attributes, XmlComponent } from "../../xml-components";
class Border extends XmlComponent {
constructor() {
super("w:bottom");
this.root.push(new Attributes({
color: "auto",
space: "1",
val: "single",
sz: "6",
}));
}
}
export class ThematicBreak extends XmlComponent {
constructor() {
super("w:pBdr");
this.root.push(new Border());
}
}

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

@ -0,0 +1,13 @@
import { XmlComponent } from "../../xml-components";
export class KeepLines extends XmlComponent {
constructor() {
super("w:keepLines");
}
}
export class KeepNext extends XmlComponent {
constructor() {
super("w:keepNext");
}
}

View File

@ -0,0 +1,32 @@
import { assert } from "chai";
import { Utility } from "../../../tests/utility";
import { PageBreak } from "./page-break";
describe("PageBreak", () => {
let pageBreak: PageBreak;
beforeEach(() => {
pageBreak = new PageBreak();
});
describe("#constructor()", () => {
it("should create a Page Break with correct attributes", () => {
const newJson = Utility.jsonify(pageBreak);
const attributes = {
type: "page",
};
assert.equal(JSON.stringify(newJson.root[1].root[0].root), JSON.stringify(attributes));
});
it("should create a Page Break with w:r", () => {
const newJson = Utility.jsonify(pageBreak);
assert.equal(newJson.rootKey, "w:r");
});
it("should create a Page Break with a Break inside", () => {
const newJson = Utility.jsonify(pageBreak);
assert.equal(newJson.root[1].rootKey, "w:br");
});
});
});

View File

@ -0,0 +1,21 @@
// http://officeopenxml.com/WPtextSpecialContent-break.php
import { Run } from "../../run";
import { Attributes, XmlComponent } from "../../xml-components";
class Break extends XmlComponent {
constructor() {
super("w:br");
this.root.push(new Attributes({
type: "page",
}));
}
}
export class PageBreak extends Run {
constructor() {
super();
this.root.push(new Break());
}
}

View File

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

View File

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

View File

@ -0,0 +1,22 @@
import { assert } from "chai";
import { Utility } from "../../../tests/utility";
import { Style } from "./style";
describe("ParagraphStyle", () => {
let style: Style;
describe("#constructor()", () => {
it("should create a style with given value", () => {
style = new Style("test");
const newJson = Utility.jsonify(style);
assert.equal(newJson.root[0].root.val, "test");
});
it("should create a style with blank val", () => {
style = new Style("");
const newJson = Utility.jsonify(style);
assert.equal(newJson.root[0].root.val, "");
});
});
});

View File

@ -0,0 +1,11 @@
import { Attributes, XmlComponent } from "../../xml-components";
export class Style extends XmlComponent {
constructor(type: string) {
super("w:pStyle");
this.root.push(new Attributes({
val: type,
}));
}
}

View File

@ -0,0 +1,57 @@
import { assert } from "chai";
import { Utility } from "../../../tests/utility";
import { LeftTabStop, MaxRightTabStop } from "./tab-stop";
describe("LeftTabStop", () => {
let tabStop: LeftTabStop;
beforeEach(() => {
tabStop = new LeftTabStop(100);
});
describe("#constructor()", () => {
it("should create a Tab Stop with correct attributes", () => {
const newJson = Utility.jsonify(tabStop);
const attributes = {
val: "left",
pos: 100,
};
assert.equal(JSON.stringify(newJson.root[0].root[0].root), JSON.stringify(attributes));
});
it("should create a Tab Stop with w:tab", () => {
const newJson = Utility.jsonify(tabStop);
assert.equal(newJson.root[0].rootKey, "w:tab");
});
});
});
describe("RightTabStop", () => {
// TODO
});
describe("MaxRightTabStop", () => {
let tabStop: MaxRightTabStop;
beforeEach(() => {
tabStop = new MaxRightTabStop();
});
describe("#constructor()", () => {
it("should create a Tab Stop with correct attributes", () => {
const newJson = Utility.jsonify(tabStop);
const attributes = {
val: "right",
pos: 9026,
};
assert.equal(JSON.stringify(newJson.root[0].root[0].root), JSON.stringify(attributes));
});
it("should create a Tab Stop with w:tab", () => {
const newJson = Utility.jsonify(tabStop);
assert.equal(newJson.root[0].rootKey, "w:tab");
});
});
});

View File

@ -0,0 +1,51 @@
// http://officeopenxml.com/WPtab.php
import { XmlAttributeComponent, XmlComponent } from "../../xml-components";
export class TabStop extends XmlComponent {
constructor(tab: Tab) {
super("w:tabs");
this.root.push(tab);
}
}
export type TabValue = "left" | "right" | "center" | "bar" | "clear" | "decimal" | "end" | "num" | "start";
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: TabValue, position: string | number) {
super("w:tab");
this.root.push(new TabAttributes({
val: value,
pos: position,
}));
}
}
export class MaxRightTabStop extends TabStop {
constructor() {
super(new Tab("right", 9026));
}
}
export class LeftTabStop extends TabStop {
constructor(position: number) {
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

@ -0,0 +1,31 @@
import { assert } from "chai";
import { Utility } from "../../../tests/utility";
import { NumberProperties } from "./unordered-list";
describe("NumberProperties", () => {
let numberProperties: NumberProperties;
beforeEach(() => {
numberProperties = new NumberProperties(5, 10);
});
describe("#constructor()", () => {
it("should create a Number Properties with correct root key", () => {
const newJson = Utility.jsonify(numberProperties);
assert.equal(newJson.rootKey, "w:numPr");
});
it("should create a Page Break with a Indent Level inside", () => {
const newJson = Utility.jsonify(numberProperties);
assert.equal(newJson.root[0].rootKey, "w:ilvl");
assert.equal(newJson.root[0].root[0].root.val, 10);
});
it("should create a Page Break with a Number Id inside", () => {
const newJson = Utility.jsonify(numberProperties);
assert.equal(newJson.root[1].rootKey, "w:numId");
assert.equal(newJson.root[1].root[0].root.val, 5);
});
});
});

View File

@ -0,0 +1,29 @@
import { Attributes, XmlComponent } from "../../xml-components";
export class NumberProperties extends XmlComponent {
constructor(numberId: number, indentLevel: number) {
super("w:numPr");
this.root.push(new IndentLevel(indentLevel));
this.root.push(new NumberId(numberId));
}
}
class IndentLevel extends XmlComponent {
constructor(level: number) {
super("w:ilvl");
this.root.push(new Attributes({
val: level,
}));
}
}
class NumberId extends XmlComponent {
constructor(id: number) {
super("w:numId");
this.root.push(new Attributes({
val: id,
}));
}
}

View File

@ -0,0 +1,3 @@
export * from "./formatting";
export * from "./paragraph";
export * from "./properties";

View File

@ -0,0 +1,293 @@
import { assert, expect } from "chai";
import * as docx from "../../docx";
import { Formatter } from "../../export/formatter";
import { Numbering } from "../../numbering";
describe("Paragraph", () => {
let paragraph: docx.Paragraph;
beforeEach(() => {
paragraph = new docx.Paragraph();
});
describe("#constructor()", () => {
it("should create valid JSON", () => {
const stringifiedJson = JSON.stringify(paragraph);
let newJson;
try {
newJson = JSON.parse(stringifiedJson);
} catch (e) {
assert.isTrue(false);
}
assert.isTrue(true);
});
it("should create have valid properties", () => {
const stringifiedJson = JSON.stringify(paragraph);
const newJson = JSON.parse(stringifiedJson);
assert.equal(newJson.root[0].rootKey, "w:pPr");
});
});
describe("#createTextRun", () => {
it("should add a new run to the paragraph and return it", () => {
const run = paragraph.createTextRun("this is a test run");
expect(run).to.be.instanceof(docx.TextRun);
const tree = new Formatter().format(paragraph)["w:p"];
expect(tree).to.be.an("array").which.includes({
"w:r": [
{"w:rPr": []},
{"w:t": [{_attr: {"xml:space": "preserve"}}, "this is a test run"]},
],
});
});
});
describe("#heading1()", () => {
it("should add heading style to JSON", () => {
paragraph.heading1();
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [{"w:pStyle": [{_attr: {"w:val": "Heading1"}}]}],
},
],
});
});
});
describe("#heading2()", () => {
it("should add heading style to JSON", () => {
paragraph.heading2();
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [{"w:pStyle": [{_attr: {"w:val": "Heading2"}}]}],
},
],
});
});
});
describe("#heading3()", () => {
it("should add heading style to JSON", () => {
paragraph.heading3();
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [{"w:pStyle": [{_attr: {"w:val": "Heading3"}}]}],
},
],
});
});
});
describe("#title()", () => {
it("should add title style to JSON", () => {
paragraph.title();
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [{"w:pStyle": [{_attr: {"w:val": "Title"}}]}],
},
],
});
});
});
describe("#center()", () => {
it("should add center alignment to JSON", () => {
paragraph.center();
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [{"w:jc": [{_attr: {"w:val": "center"}}]}],
},
],
});
});
});
describe("#thematicBreak()", () => {
it("should add thematic break to JSON", () => {
paragraph.thematicBreak();
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [{
"w:pPr": [{
"w:pBdr": [{
"w:bottom": [{
_attr: {
"w:val": "single",
"w:color": "auto",
"w:space": "1",
"w:sz": "6",
},
}],
}],
}],
}],
});
});
});
describe("#pageBreak()", () => {
it("should add page break to JSON", () => {
paragraph.pageBreak();
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [{
"w:pPr": [],
}, {
"w:r": [
{"w:rPr": []},
{"w:br": [{_attr: {"w:type": "page"}}]},
],
}],
});
});
});
describe("#bullet()", () => {
it("should add list paragraph style to JSON", () => {
paragraph.bullet();
const tree = new Formatter().format(paragraph);
expect(tree).to.have.property("w:p").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]).to.have.property("w:pPr").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]["w:pPr"][0]).to.deep.equal({
"w:pStyle": [{_attr: {"w:val": "ListParagraph"}}],
});
});
it("it should add numbered properties", () => {
paragraph.bullet();
const tree = new Formatter().format(paragraph);
expect(tree).to.have.property("w:p").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]).to.have.property("w:pPr").which.is.an("array").which.has.length.at.least(2);
expect(tree["w:p"][0]["w:pPr"][1]).to.deep.equal({
"w:numPr": [
{"w:ilvl": [{_attr: {"w:val": 0}}]},
{"w:numId": [{_attr: {"w:val": 1}}]},
],
});
});
});
describe("#setNumbering", () => {
it("should add list paragraph style to JSON", () => {
const numbering = new Numbering();
const numberedAbstract = numbering.createAbstractNumbering();
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "start");
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
paragraph.setNumbering(letterNumbering, 0);
const tree = new Formatter().format(paragraph);
expect(tree).to.have.property("w:p").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]).to.have.property("w:pPr").which.is.an("array").which.has.length.at.least(1);
expect(tree["w:p"][0]["w:pPr"][0]).to.deep.equal({
"w:pStyle": [{_attr: {"w:val": "ListParagraph"}}],
});
});
it("it should add numbered properties", () => {
const numbering = new Numbering();
const numberedAbstract = numbering.createAbstractNumbering();
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "start");
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
paragraph.setNumbering(letterNumbering, 0);
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [
{"w:pStyle": [{_attr: {"w:val": "ListParagraph"}}]},
{
"w:numPr": [
{"w:ilvl": [{_attr: {"w:val": 0}}]},
{"w:numId": [{_attr: {"w:val": letterNumbering.id}}]},
],
},
],
},
],
});
});
});
describe("#style", () => {
it("should set the paragraph style to the given styleId", () => {
paragraph.style("myFancyStyle");
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [
{"w:pStyle": [{_attr: {"w:val": "myFancyStyle"}}]},
],
},
],
});
});
});
describe("#indent", () => {
it("should set the paragraph indent to the given values", () => {
paragraph.indent({ left: 720 });
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [
{"w:ind": [{_attr: {"w:left": 720}}]},
],
},
],
});
});
});
describe("#spacing", () => {
it("should set the paragraph spacing to the given values", () => {
paragraph.spacing({before: 90, line: 50});
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [
{"w:spacing": [{_attr: {"w:before": 90, "w:line": 50}}]},
],
},
],
});
});
});
describe("#keepLines", () => {
it("should set the paragraph keepLines sub-component", () => {
paragraph.keepLines();
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [{"w:pPr": [{"w:keepLines": []}]}],
});
});
});
describe("#keepNext", () => {
it("should set the paragraph keepNext sub-component", () => {
paragraph.keepNext();
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [{"w:pPr": [{"w:keepNext": []}]}],
});
});
});
});

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

@ -0,0 +1,13 @@
// http://officeopenxml.com/WPparagraphProperties.php
import { XmlComponent } from "../xml-components";
export class ParagraphProperties extends XmlComponent {
constructor() {
super("w:pPr");
}
public push(item: XmlComponent): void {
this.root.push(item);
}
}

View File

@ -0,0 +1,19 @@
import { assert } from "chai";
import { Utility } from "../../tests/utility";
import { Break } from "./break";
describe("Break", () => {
let currentBreak: Break;
beforeEach(() => {
currentBreak = new Break();
});
describe("#constructor()", () => {
it("should create a Break with correct root key", () => {
const newJson = Utility.jsonify(currentBreak);
assert.equal(newJson.rootKey, "w:br");
});
});
});

9
src/docx/run/break.ts Normal file
View File

@ -0,0 +1,9 @@
// http://officeopenxml.com/WPtextSpecialContent-break.php
import { XmlComponent } from "../xml-components";
export class Break extends XmlComponent {
constructor() {
super("w:br");
}
}

15
src/docx/run/caps.ts Normal file
View File

@ -0,0 +1,15 @@
import { XmlComponent } from "../xml-components";
export class SmallCaps extends XmlComponent {
constructor() {
super("w:smallCaps");
}
}
export class Caps extends XmlComponent {
constructor() {
super("w:caps");
}
}

114
src/docx/run/formatting.ts Normal file
View File

@ -0,0 +1,114 @@
import { Attributes, XmlComponent } from "../xml-components";
export { Underline } from "./underline";
export { SubScript, SuperScript } from "./script";
export { RunFonts } from "./run-fonts";
export class Bold extends XmlComponent {
constructor() {
super("w:b");
this.root.push(new Attributes({
val: true,
}));
}
}
export class Italics extends XmlComponent {
constructor() {
super("w:i");
this.root.push(new Attributes({
val: true,
}));
}
}
export class Caps extends XmlComponent {
constructor() {
super("w:caps");
this.root.push(new Attributes({
val: true,
}));
}
}
export class Color extends XmlComponent {
constructor(color: string) {
super("w:color");
this.root.push(new Attributes({
val: color,
}));
}
}
export class DoubleStrike extends XmlComponent {
constructor() {
super("w:dstrike");
this.root.push(new Attributes({
val: true,
}));
}
}
export class Emboss extends XmlComponent {
constructor() {
super("w:emboss");
this.root.push(new Attributes({
val: true,
}));
}
}
export class Imprint extends XmlComponent {
constructor() {
super("w:imprint");
this.root.push(new Attributes({
val: true,
}));
}
}
export class Shadow extends XmlComponent {
constructor() {
super("w:shadow");
this.root.push(new Attributes({
val: true,
}));
}
}
export class SmallCaps extends XmlComponent {
constructor() {
super("w:smallCaps");
this.root.push(new Attributes({
val: true,
}));
}
}
export class Strike extends XmlComponent {
constructor() {
super("w:strike");
this.root.push(new Attributes({
val: true,
}));
}
}
export class Size extends XmlComponent {
constructor(size: number) {
super("w:sz");
this.root.push(new Attributes({
val: size,
}));
}
}

3
src/docx/run/index.ts Normal file
View File

@ -0,0 +1,3 @@
export * from "./run";
export * from "./text-run";
export * from "./picture-run";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,23 @@
import { expect } from "chai";
import { Formatter } from "../../../export/formatter";
import { Text } from "./text";
describe("Text", () => {
describe("#constructor", () => {
it("creates an empty text run if no text is given", () => {
const t = new Text("");
const f = new Formatter().format(t);
expect(f).to.deep.equal({"w:t": [{_attr: {"xml:space": "preserve"}}]});
});
it("adds the passed in text to the component", () => {
const t = new Text(" this is\n text");
const f = new Formatter().format(t);
expect(f).to.deep.equal({"w:t": [
{_attr: {"xml:space": "preserve"}},
" this is\n text",
]});
});
});
});

View File

@ -0,0 +1,15 @@
import { XmlAttributeComponent, XmlComponent } from "../../xml-components";
class TextAttributes extends XmlAttributeComponent<{space: "default" | "preserve"}> {
protected xmlKeys = {space: "xml:space"};
}
export class Text extends XmlComponent {
constructor(text: string) {
super("w:t");
this.root.push(new TextAttributes({space: "preserve"}));
if (text) {
this.root.push(text);
}
}
}

View File

@ -0,0 +1,23 @@
import { expect } from "chai";
import { Formatter } from "../../export/formatter";
import { RunFonts } from "./run-fonts";
describe("RunFonts", () => {
describe("#constructor()", () => {
it("uses the font name for both ascii and hAnsi", () => {
const tree = new Formatter().format(new RunFonts("Times"));
expect(tree).to.deep.equal({
"w:rFonts": [{_attr: {"w:ascii": "Times", "w:hAnsi": "Times"}}],
});
});
it("uses hint if given", () => {
const tree = new Formatter().format(new RunFonts("Times", "default"));
expect(tree).to.deep.equal({
"w:rFonts": [{_attr: {"w:ascii": "Times", "w:hAnsi": "Times", "w:hint": "default"}}],
});
});
});
});

27
src/docx/run/run-fonts.ts Normal file
View File

@ -0,0 +1,27 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
interface IRunFontAttributesProperties {
ascii: string;
hAnsi: string;
hint?: string;
}
class RunFontAttributes extends XmlAttributeComponent<IRunFontAttributesProperties> {
protected xmlKeys = {
ascii: "w:ascii",
hAnsi: "w:hAnsi",
hint: "w:hint",
};
}
export class RunFonts extends XmlComponent {
constructor(ascii: string, hint?: string) {
super("w:rFonts");
this.root.push(new RunFontAttributes({
ascii: ascii,
hAnsi: ascii,
hint: hint,
}));
}
}

157
src/docx/run/run.spec.ts Normal file
View File

@ -0,0 +1,157 @@
import { assert, expect } from "chai";
import { Formatter } from "../../export/formatter";
import { Utility } from "../../tests/utility";
import { Run } from "./";
describe("Run", () => {
let run: Run;
beforeEach(() => {
run = new Run();
});
describe("#bold()", () => {
it("it should add bold to the properties", () => {
run.bold();
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:b");
});
});
describe("#italic()", () => {
it("it should add italics to the properties", () => {
run.italic();
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:i");
});
});
describe("#underline()", () => {
it("it should add underline to the properties", () => {
run.underline();
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:u");
});
it("should default to 'single' and no color", () => {
run.underline();
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:u": [{_attr: {"w:val": "single"}}]}]},
],
});
});
it("should set the style type and color if given", () => {
run.underline("double", "990011");
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:u": [{_attr: {"w:val": "double", "w:color": "990011"}}]}]},
],
});
});
});
describe("#smallCaps()", () => {
it("it should add smallCaps to the properties", () => {
run.smallCaps();
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:smallCaps");
});
});
describe("#caps()", () => {
it("it should add caps to the properties", () => {
run.allCaps();
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:caps");
});
});
describe("#strike()", () => {
it("it should add strike to the properties", () => {
run.strike();
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:strike");
});
});
describe("#doubleStrike()", () => {
it("it should add caps to the properties", () => {
run.doubleStrike();
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:dstrike");
});
});
describe("#break()", () => {
it("it should add break to the run", () => {
run.break();
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[1].rootKey, "w:br");
});
});
describe("#tab()", () => {
it("it should add break to the run", () => {
run.tab();
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[1].rootKey, "w:tab");
});
});
describe("#font()", () => {
it("should allow chaining calls", () => {
expect(run.font("Times")).to.equal(run);
});
it("should set the font as named", () => {
run.font("Times");
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:rFonts": [{_attr: {"w:ascii": "Times", "w:hAnsi": "Times"}}]}]},
],
});
});
});
describe("#color", () => {
it("should set the run to the color given", () => {
run.color("001122");
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:color": [{_attr: {"w:val": "001122"}}]}]},
],
});
});
});
describe("#size", () => {
it("should set the run to the given size", () => {
run.size(24);
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:sz": [{_attr: {"w:val": 24}}]}]},
],
});
});
});
describe("#style", () => {
it("should set the style to the given styleId", () => {
run.style("myRunStyle");
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:rStyle": [{_attr: {"w:val": "myRunStyle"}}]}]},
],
});
});
});
});

97
src/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

@ -0,0 +1,50 @@
import { assert } from "chai";
import { Utility } from "../../tests/utility";
import { SubScript, SuperScript } from "./script";
describe("SubScript", () => {
let subScript: SubScript;
beforeEach(() => {
subScript = new SubScript();
});
describe("#constructor()", () => {
it("should create a Sub Script with correct attributes", () => {
const newJson = Utility.jsonify(subScript);
const attributes = {
val: "subscript",
};
assert.equal(JSON.stringify(newJson.root[0].root), JSON.stringify(attributes));
});
it("should create a Sub Script with correct root key", () => {
const newJson = Utility.jsonify(subScript);
assert.equal(newJson.rootKey, "w:vertAlign");
});
});
});
describe("SuperScript", () => {
let superScript: SuperScript;
beforeEach(() => {
superScript = new SuperScript();
});
describe("#constructor()", () => {
it("should create a Super Script with correct attributes", () => {
const newJson = Utility.jsonify(superScript);
const attributes = {
val: "superscript",
};
assert.equal(JSON.stringify(newJson.root[0].root), JSON.stringify(attributes));
});
it("should create a Super Script with correct root key", () => {
const newJson = Utility.jsonify(superScript);
assert.equal(newJson.rootKey, "w:vertAlign");
});
});
});

25
src/docx/run/script.ts Normal file
View File

@ -0,0 +1,25 @@
import { Attributes, XmlComponent } from "../xml-components";
export abstract class VerticalAlign extends XmlComponent {
constructor(type: string) {
super("w:vertAlign");
this.root.push(new Attributes({
val: type,
}));
}
}
export class SuperScript extends VerticalAlign {
constructor() {
super("superscript");
}
}
export class SubScript extends VerticalAlign {
constructor() {
super("subscript");
}
}

View File

@ -0,0 +1,34 @@
import { assert } from "chai";
import { Utility } from "../../tests/utility";
import { DoubleStrike, Strike } from "./formatting";
describe("Strike", () => {
let strike: Strike;
beforeEach(() => {
strike = new Strike();
});
describe("#constructor()", () => {
it("should create a Strike with correct root key", () => {
const newJson = Utility.jsonify(strike);
assert.equal(newJson.rootKey, "w:strike");
});
});
});
describe("DoubleStrike", () => {
let strike: DoubleStrike;
beforeEach(() => {
strike = new DoubleStrike();
});
describe("#constructor()", () => {
it("should create a Double Strike with correct root key", () => {
const newJson = Utility.jsonify(strike);
assert.equal(newJson.rootKey, "w:dstrike");
});
});
});

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

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

19
src/docx/run/tab.spec.ts Normal file
View File

@ -0,0 +1,19 @@
import { assert } from "chai";
import { Utility } from "../../tests/utility";
import { Tab } from "./tab";
describe("Tab", () => {
let tab: Tab;
beforeEach(() => {
tab = new Tab();
});
describe("#constructor()", () => {
it("should create a Tab with correct root key", () => {
const newJson = Utility.jsonify(tab);
assert.equal(newJson.rootKey, "w:tab");
});
});
});

8
src/docx/run/tab.ts Normal file
View File

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

View File

@ -0,0 +1,20 @@
import { expect } from "chai";
import { Formatter } from "../../export/formatter";
import { TextRun } from "./text-run";
describe("TextRun", () => {
let run: TextRun;
describe("#constructor()", () => {
it("should add text into run", () => {
run = new TextRun("test");
const f = new Formatter().format(run);
expect(f).to.deep.equal({"w:r": [
{"w:rPr": []},
{"w:t": [{_attr: {"xml:space": "preserve"}}, "test"]},
]});
});
});
});

10
src/docx/run/text-run.ts Normal file
View File

@ -0,0 +1,10 @@
import { Run } from "../run";
import { Text } from "./run-components/text";
export class TextRun extends Run {
constructor(text: string) {
super();
this.root.push(new Text(text));
}
}

View File

@ -0,0 +1,215 @@
import { assert, expect } from "chai";
import { Formatter } from "../../export/formatter";
import { Utility } from "../../tests/utility";
import * as u from "./underline";
describe("Underline", () => {
describe("#constructor()", () => {
it("should create a new Underline object with u:u as the rootKey", () => {
const underline = new u.Underline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.rootKey, "w:u");
});
it("should default to 'single' and no color", () => {
const underline = new u.Underline();
const tree = new Formatter().format(underline);
expect(tree).to.deep.equal({
"w:u": [{_attr: {"w:val": "single"}}],
});
});
it("should use the given style type and color", () => {
const underline = new u.Underline("double", "FF00CC");
const tree = new Formatter().format(underline);
expect(tree).to.deep.equal({
"w:u": [{_attr: {"w:val": "double", "w:color": "FF00CC"}}],
});
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should have u:u as the rootKey", () => {
const underline = new u.DashDotDotHeavyUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.rootKey, "w:u");
});
it("should put value in attribute", () => {
const underline = new u.DashDotDotHeavyUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dashDotDotHeavy");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.DashDotHeavyUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dashDotHeavy");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.DashLongHeavyUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dashLongHeavy");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.DashLongUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dashLong");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.DashUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dash");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.DotDashUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dotDash");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.DotDotDashUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dotDotDash");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.DottedHeavyUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dottedHeavy");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.DottedUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "dotted");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.DoubleUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "double");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.SingleUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "single");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.ThickUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "thick");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.WaveUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "wave");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.WavyDoubleUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "wavyDouble");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.WavyHeavyUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "wavyHeavy");
});
});
});
describe("DashDotDotHeavyUnderline", () => {
describe("#constructor()", () => {
it("should put value in attribute", () => {
const underline = new u.WordsUnderline();
const newJson = Utility.jsonify(underline);
assert.equal(newJson.root[0].root.val, "words");
});
});
});

131
src/docx/run/underline.ts Normal file
View File

@ -0,0 +1,131 @@
import { Attributes, XmlComponent } from "../xml-components";
export abstract class BaseUnderline extends XmlComponent {
constructor(underlineType: string, color?: string) {
super("w:u");
this.root.push(new Attributes({
val: underlineType,
color: color,
}));
}
}
export class Underline extends BaseUnderline {
constructor(underlineType: string = "single", color?: string) {
super(underlineType, color);
}
}
export class DashUnderline extends BaseUnderline {
constructor() {
super("dash");
}
}
export class DashDotDotHeavyUnderline extends BaseUnderline {
constructor() {
super("dashDotDotHeavy");
}
}
export class DashDotHeavyUnderline extends BaseUnderline {
constructor() {
super("dashDotHeavy");
}
}
export class DashLongUnderline extends BaseUnderline {
constructor() {
super("dashLong");
}
}
export class DashLongHeavyUnderline extends BaseUnderline {
constructor() {
super("dashLongHeavy");
}
}
export class DotDashUnderline extends BaseUnderline {
constructor() {
super("dotDash");
}
}
export class DotDotDashUnderline extends BaseUnderline {
constructor() {
super("dotDotDash");
}
}
export class DottedUnderline extends BaseUnderline {
constructor() {
super("dotted");
}
}
export class DottedHeavyUnderline extends BaseUnderline {
constructor() {
super("dottedHeavy");
}
}
export class DoubleUnderline extends BaseUnderline {
constructor() {
super("double");
}
}
export class SingleUnderline extends BaseUnderline {
constructor() {
super("single");
}
}
export class ThickUnderline extends BaseUnderline {
constructor() {
super("thick");
}
}
export class WaveUnderline extends BaseUnderline {
constructor() {
super("wave");
}
}
export class WavyDoubleUnderline extends BaseUnderline {
constructor() {
super("wavyDouble");
}
}
export class WavyHeavyUnderline extends BaseUnderline {
constructor() {
super("wavyHeavy");
}
}
export class WordsUnderline extends BaseUnderline {
constructor() {
super("words");
}
}

View File

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

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

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

1
src/docx/table/index.ts Normal file
View File

@ -0,0 +1 @@
export * from "./table";

View File

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

View File

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

View File

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

123
src/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

@ -0,0 +1,26 @@
import { assert } from "chai";
import { Attributes } from "./";
describe("Attribute", () => {
describe("#constructor()", () => {
it("should have val as defined with populated constructor", () => {
const newAttrs = new Attributes({
val: "test",
});
const stringifiedJson = JSON.stringify(newAttrs);
const newJson = JSON.parse(stringifiedJson);
assert.equal(newJson.root.val, "test");
});
it("should have space value as defined with populated constructor", () => {
const newAttrs = new Attributes({
space: "spaceTest",
});
const stringifiedJson = JSON.stringify(newAttrs);
const newJson = JSON.parse(stringifiedJson);
assert.equal(newJson.root.space, "spaceTest");
});
});
});

View File

@ -0,0 +1,47 @@
import { XmlAttributeComponent } from "./default-attributes";
export interface IAttributesProperties {
val?: string | number | boolean;
color?: string;
space?: string;
sz?: string;
type?: string;
rsidR?: string;
rsidRPr?: string;
rsidSect?: string;
w?: string;
h?: string;
top?: string;
right?: string;
bottom?: string;
left?: string;
header?: string;
footer?: string;
gutter?: string;
linePitch?: string;
pos?: string | number; // Little strange. Perhaps it is normal. Need to clarify in the spec.
}
export class Attributes extends XmlAttributeComponent<IAttributesProperties> {
protected xmlKeys = {
val: "w:val",
color: "w:color",
space: "w:space",
sz: "w:sz",
type: "w:type",
rsidR: "w:rsidR",
rsidRPr: "w:rsidRPr",
rsidSect: "w:rsidSect",
w: "w:w",
h: "w:h",
top: "w:top",
right: "w:right",
bottom: "w:bottom",
left: "w:left",
header: "w:header",
footer: "w:footer",
gutter: "w:gutter",
linePitch: "w:linePitch",
pos: "w:pos",
};
}

View File

@ -0,0 +1,11 @@
import { IXmlableObject } from "./xmlable-object";
export abstract class BaseXmlComponent {
protected rootKey: string;
constructor(rootKey: string) {
this.rootKey = rootKey;
}
public abstract prepForXml(): IXmlableObject;
}

View File

@ -0,0 +1,26 @@
import { BaseXmlComponent } from "./base";
import { IXmlableObject } from "./xmlable-object";
export type AttributeMap<T> = {[P in keyof T]: string};
export abstract class XmlAttributeComponent<T> extends BaseXmlComponent {
protected root: T;
protected xmlKeys: AttributeMap<T>;
constructor(properties: T) {
super("_attr");
this.root = properties;
}
public prepForXml(): IXmlableObject {
const attrs = {};
Object.keys(this.root).forEach((key) => {
const value = this.root[key];
if (value !== undefined) {
const newKey = this.xmlKeys[key];
attrs[newKey] = value;
}
});
return {_attr: attrs};
}
}

View File

@ -0,0 +1,3 @@
export * from "./xml-component";
export * from "./attributes";
export * from "./default-attributes";

View File

@ -0,0 +1,24 @@
import { assert } from "chai";
import { Utility } from "../../tests/utility";
import { XmlComponent } from "./";
class TestComponent extends XmlComponent {
}
describe("XmlComponent", () => {
let xmlComponent: TestComponent;
beforeEach(() => {
xmlComponent = new TestComponent("w:test");
});
describe("#constructor()", () => {
it("should create an Xml Component which has the correct rootKey", () => {
const newJson = Utility.jsonify(xmlComponent);
assert.equal(newJson.rootKey, "w:test");
});
});
});

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

@ -0,0 +1,3 @@
export interface IXmlableObject extends Object {
_attr?: { [key: string]: (string | number | boolean) };
}