Merge branch 'master' into feat/math
# Conflicts: # src/file/paragraph/index.ts # src/file/paragraph/paragraph.ts
This commit is contained in:
@ -1,7 +1,8 @@
|
||||
import { assert, expect } from "chai";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
import * as file from "file";
|
||||
import { Paragraph, TextRun } from "file";
|
||||
import { CoreProperties } from "file/core-properties";
|
||||
import { Attributes } from "file/xml-components";
|
||||
|
||||
@ -14,28 +15,65 @@ describe("Formatter", () => {
|
||||
|
||||
describe("#format()", () => {
|
||||
it("should format simple paragraph", () => {
|
||||
const paragraph = new file.Paragraph("");
|
||||
const paragraph = new Paragraph("");
|
||||
const newJson = formatter.format(paragraph);
|
||||
assert.isDefined(newJson["w:p"]);
|
||||
});
|
||||
|
||||
it("should remove xmlKeys", () => {
|
||||
const paragraph = new file.Paragraph("");
|
||||
const paragraph = new Paragraph("");
|
||||
const newJson = formatter.format(paragraph);
|
||||
const stringifiedJson = JSON.stringify(newJson);
|
||||
assert(stringifiedJson.indexOf("xmlKeys") < 0);
|
||||
});
|
||||
|
||||
it("should format simple paragraph with bold text", () => {
|
||||
const paragraph = new file.Paragraph("");
|
||||
paragraph.addRun(
|
||||
new file.TextRun({
|
||||
text: "test",
|
||||
bold: true,
|
||||
}),
|
||||
);
|
||||
const newJson = formatter.format(paragraph);
|
||||
assert.isDefined(newJson["w:p"][1]["w:r"][0]["w:rPr"][0]["w:b"]._attr["w:val"]);
|
||||
const paragraph = new Paragraph({
|
||||
children: [
|
||||
new TextRun({
|
||||
text: "test",
|
||||
bold: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const tree = formatter.format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
{
|
||||
"w:r": [
|
||||
{
|
||||
"w:rPr": [
|
||||
{
|
||||
"w:b": {
|
||||
_attr: {
|
||||
"w:val": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:bCs": {
|
||||
_attr: {
|
||||
"w:val": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:t": [
|
||||
{
|
||||
_attr: {
|
||||
"xml:space": "preserve",
|
||||
},
|
||||
},
|
||||
"test",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should format attributes (rsidSect)", () => {
|
||||
@ -63,7 +101,7 @@ describe("Formatter", () => {
|
||||
});
|
||||
|
||||
it("should should change 'p' tag into 'w:p' tag", () => {
|
||||
const paragraph = new file.Paragraph("");
|
||||
const paragraph = new Paragraph("");
|
||||
const newJson = formatter.format(paragraph);
|
||||
assert.isDefined(newJson["w:p"]);
|
||||
});
|
||||
@ -76,5 +114,13 @@ describe("Formatter", () => {
|
||||
const newJson = formatter.format(properties);
|
||||
assert.isDefined(newJson["cp:coreProperties"]);
|
||||
});
|
||||
|
||||
it("should call the prep method only once", () => {
|
||||
const paragraph = new Paragraph("");
|
||||
const spy = sinon.spy(paragraph, "prepForXml");
|
||||
|
||||
formatter.format(paragraph);
|
||||
expect(spy.calledOnce).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -5,7 +5,7 @@ export class ImageReplacer {
|
||||
let currentXmlData = xmlData;
|
||||
|
||||
mediaData.forEach((image, i) => {
|
||||
currentXmlData = currentXmlData.replace(`{${image.fileName}}`, (offset + i).toString());
|
||||
currentXmlData = currentXmlData.replace(new RegExp(`{${image.fileName}}`, "g"), (offset + i).toString());
|
||||
});
|
||||
|
||||
return currentXmlData;
|
||||
|
@ -1,7 +1,8 @@
|
||||
/* tslint:disable:typedef space-before-function-paren */
|
||||
import { expect } from "chai";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import { File, Footer, Header } from "file";
|
||||
import { File, Footer, Header, Paragraph } from "file";
|
||||
|
||||
import { Compiler } from "./next-compiler";
|
||||
|
||||
@ -72,5 +73,22 @@ describe("Compiler", () => {
|
||||
expect(fileNames).to.include("word/footer2.xml");
|
||||
expect(fileNames).to.include("word/_rels/footer2.xml.rels");
|
||||
});
|
||||
|
||||
it("should call the format method X times equalling X files to be formatted", () => {
|
||||
// This test is required because before, there was a case where Document was formatted twice, which was inefficient
|
||||
// This also caused issues such as running prepForXml multiple times as format() was ran multiple times.
|
||||
const paragraph = new Paragraph("");
|
||||
const doc = new File();
|
||||
|
||||
doc.addSection({
|
||||
properties: {},
|
||||
children: [paragraph],
|
||||
});
|
||||
// tslint:disable-next-line: no-string-literal
|
||||
const spy = sinon.spy(compiler["formatter"], "format");
|
||||
|
||||
compiler.compile(file);
|
||||
expect(spy.callCount).to.equal(10);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -68,13 +68,13 @@ export class Compiler {
|
||||
file.verifyUpdateFields();
|
||||
const documentRelationshipCount = file.DocumentRelationships.RelationshipCount + 1;
|
||||
|
||||
const documentXmlData = xml(this.formatter.format(file.Document), prettify);
|
||||
const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media);
|
||||
|
||||
return {
|
||||
Relationships: {
|
||||
data: (() => {
|
||||
const xmlData = xml(this.formatter.format(file.Document), prettify);
|
||||
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
|
||||
|
||||
mediaDatas.forEach((mediaData, i) => {
|
||||
documentMediaDatas.forEach((mediaData, i) => {
|
||||
file.DocumentRelationships.createRelationship(
|
||||
documentRelationshipCount + i,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
@ -88,9 +88,7 @@ export class Compiler {
|
||||
},
|
||||
Document: {
|
||||
data: (() => {
|
||||
const tempXmlData = xml(this.formatter.format(file.Document), prettify);
|
||||
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
|
||||
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, documentRelationshipCount);
|
||||
const xmlData = this.imageReplacer.replace(documentXmlData, documentMediaDatas, documentRelationshipCount);
|
||||
|
||||
return xmlData;
|
||||
})(),
|
||||
|
@ -4,33 +4,33 @@ import { Compiler } from "./next-compiler";
|
||||
export class Packer {
|
||||
public static async toBuffer(file: File, prettify?: boolean): Promise<Buffer> {
|
||||
const zip = this.compiler.compile(file, prettify);
|
||||
const zipData = (await zip.generateAsync({
|
||||
const zipData = await zip.generateAsync({
|
||||
type: "nodebuffer",
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
compression: "DEFLATE",
|
||||
})) as Buffer;
|
||||
});
|
||||
|
||||
return zipData;
|
||||
}
|
||||
|
||||
public static async toBase64String(file: File, prettify?: boolean): Promise<string> {
|
||||
const zip = this.compiler.compile(file, prettify);
|
||||
const zipData = (await zip.generateAsync({
|
||||
const zipData = await zip.generateAsync({
|
||||
type: "base64",
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
compression: "DEFLATE",
|
||||
})) as string;
|
||||
});
|
||||
|
||||
return zipData;
|
||||
}
|
||||
|
||||
public static async toBlob(file: File, prettify?: boolean): Promise<Blob> {
|
||||
const zip = this.compiler.compile(file, prettify);
|
||||
const zipData = (await zip.generateAsync({
|
||||
const zipData = await zip.generateAsync({
|
||||
type: "blob",
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
compression: "DEFLATE",
|
||||
})) as Blob;
|
||||
});
|
||||
|
||||
return zipData;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { DocumentAttributes } from "../document/document-attributes";
|
||||
import { IStylesOptions } from "../styles";
|
||||
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
|
||||
|
||||
export interface IPropertiesOptions {
|
||||
@ -11,6 +13,7 @@ export interface IPropertiesOptions {
|
||||
readonly lastModifiedBy?: string;
|
||||
readonly revision?: string;
|
||||
readonly externalStyles?: string;
|
||||
readonly styles?: IStylesOptions;
|
||||
}
|
||||
|
||||
export class CoreProperties extends XmlComponent {
|
||||
|
@ -0,0 +1,42 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
import { FooterReference } from "./footer-reference";
|
||||
import { FooterReferenceType } from "./footer-reference-attributes";
|
||||
|
||||
describe("footerReference", () => {
|
||||
it("should create", () => {
|
||||
const footer = new FooterReference({
|
||||
footerType: FooterReferenceType.DEFAULT,
|
||||
footerId: 1,
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(footer);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:footerReference": {
|
||||
_attr: {
|
||||
"r:id": "rId1",
|
||||
"w:type": "default",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should create without a footer type", () => {
|
||||
const footer = new FooterReference({
|
||||
footerId: 1,
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(footer);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:footerReference": {
|
||||
_attr: {
|
||||
"r:id": "rId1",
|
||||
"w:type": "default",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,42 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
import { HeaderReference } from "./header-reference";
|
||||
import { HeaderReferenceType } from "./header-reference-attributes";
|
||||
|
||||
describe("HeaderReference", () => {
|
||||
it("should create", () => {
|
||||
const footer = new HeaderReference({
|
||||
headerType: HeaderReferenceType.DEFAULT,
|
||||
headerId: 1,
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(footer);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:headerReference": {
|
||||
_attr: {
|
||||
"r:id": "rId1",
|
||||
"w:type": "default",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should create without a header type", () => {
|
||||
const footer = new HeaderReference({
|
||||
headerId: 1,
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(footer);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:headerReference": {
|
||||
_attr: {
|
||||
"r:id": "rId1",
|
||||
"w:type": "default",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
@ -38,6 +38,7 @@ describe("SectionProperties", () => {
|
||||
},
|
||||
pageNumberStart: 10,
|
||||
pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT,
|
||||
titlePage: true,
|
||||
});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||
|
@ -6,7 +6,7 @@ import { Formatter } from "export/formatter";
|
||||
import { File } from "./file";
|
||||
import { Footer, Header } from "./header";
|
||||
import { Paragraph } from "./paragraph";
|
||||
import { Table } from "./table";
|
||||
import { Table, TableCell, TableRow } from "./table";
|
||||
import { TableOfContents } from "./table-of-contents";
|
||||
|
||||
describe("File", () => {
|
||||
@ -108,8 +108,15 @@ describe("File", () => {
|
||||
file.addSection({
|
||||
children: [
|
||||
new Table({
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
@ -46,8 +46,6 @@ export interface ISectionOptions {
|
||||
export class File {
|
||||
// tslint:disable-next-line:readonly-keyword
|
||||
private currentRelationshipId: number = 1;
|
||||
// tslint:disable-next-line:readonly-keyword
|
||||
private styles: Styles;
|
||||
|
||||
private readonly document: Document;
|
||||
private readonly headers: IDocumentHeader[] = [];
|
||||
@ -61,6 +59,7 @@ export class File {
|
||||
private readonly settings: Settings;
|
||||
private readonly contentTypes: ContentTypes;
|
||||
private readonly appProperties: AppProperties;
|
||||
private readonly styles: Styles;
|
||||
|
||||
constructor(
|
||||
options: IPropertiesOptions = {
|
||||
@ -97,9 +96,16 @@ export class File {
|
||||
} else if (options.externalStyles) {
|
||||
const stylesFactory = new ExternalStylesFactory();
|
||||
this.styles = stylesFactory.newInstance(options.externalStyles);
|
||||
} else if (options.styles) {
|
||||
const stylesFactory = new DefaultStylesFactory();
|
||||
const defaultStyles = stylesFactory.newInstance();
|
||||
this.styles = new Styles({
|
||||
...defaultStyles,
|
||||
...options.styles,
|
||||
});
|
||||
} else {
|
||||
const stylesFactory = new DefaultStylesFactory();
|
||||
this.styles = stylesFactory.newInstance();
|
||||
this.styles = new Styles(stylesFactory.newInstance());
|
||||
}
|
||||
|
||||
this.addDefaultRelationships();
|
||||
@ -277,10 +283,6 @@ export class File {
|
||||
return this.styles;
|
||||
}
|
||||
|
||||
public set Styles(styles: Styles) {
|
||||
this.styles = styles;
|
||||
}
|
||||
|
||||
public get CoreProperties(): CoreProperties {
|
||||
return this.coreProperties;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import * as sinon from "sinon";
|
||||
import { FooterWrapper } from "./footer-wrapper";
|
||||
import { Media } from "./media";
|
||||
import { Paragraph } from "./paragraph";
|
||||
import { Table } from "./table";
|
||||
import { Table, TableCell, TableRow } from "./table";
|
||||
|
||||
describe("FooterWrapper", () => {
|
||||
describe("#add", () => {
|
||||
@ -21,22 +21,20 @@ describe("FooterWrapper", () => {
|
||||
const spy = sinon.spy(file.Footer, "add");
|
||||
file.add(
|
||||
new Table({
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
|
||||
it("should call the underlying footer's addImage", () => {
|
||||
const file = new FooterWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Footer, "add");
|
||||
// tslint:disable-next-line:no-any
|
||||
file.addImage({} as any);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addChildElement", () => {
|
||||
|
@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { FooterReferenceType } from "./document";
|
||||
import { Footer } from "./footer/footer";
|
||||
import { Image, Media } from "./media";
|
||||
import { Media } from "./media";
|
||||
import { Paragraph } from "./paragraph";
|
||||
import { Relationships } from "./relationships";
|
||||
import { Table } from "./table";
|
||||
@ -25,11 +25,6 @@ export class FooterWrapper {
|
||||
this.footer.add(item);
|
||||
}
|
||||
|
||||
public addImage(image: Image): FooterWrapper {
|
||||
this.footer.add(image.Paragraph);
|
||||
return this;
|
||||
}
|
||||
|
||||
public addChildElement(childElement: XmlComponent): void {
|
||||
this.footer.addChildElement(childElement);
|
||||
}
|
||||
|
47
src/file/footer/footer.spec.ts
Normal file
47
src/file/footer/footer.spec.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { Paragraph } from "../paragraph";
|
||||
import { Footer } from "./footer";
|
||||
|
||||
describe("Footer", () => {
|
||||
it("should create", () => {
|
||||
const footer = new Footer(1);
|
||||
|
||||
const tree = new Formatter().format(footer);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:ftr": {
|
||||
_attr: {
|
||||
"xmlns:m": "http://schemas.openxmlformats.org/officeDocument/2006/math",
|
||||
"xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
||||
"xmlns:o": "urn:schemas-microsoft-com:office:office",
|
||||
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||
"xmlns:v": "urn:schemas-microsoft-com:vml",
|
||||
"xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||
"xmlns:w10": "urn:schemas-microsoft-com:office:word",
|
||||
"xmlns:w14": "http://schemas.microsoft.com/office/word/2010/wordml",
|
||||
"xmlns:w15": "http://schemas.microsoft.com/office/word/2012/wordml",
|
||||
"xmlns:wne": "http://schemas.microsoft.com/office/word/2006/wordml",
|
||||
"xmlns:wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"xmlns:wp14": "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
|
||||
"xmlns:wpc": "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
||||
"xmlns:wpg": "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
|
||||
"xmlns:wpi": "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
|
||||
"xmlns:wps": "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should create with initContent", () => {
|
||||
const header = new Footer(1, new Paragraph({}));
|
||||
|
||||
const tree = new Formatter().format(header);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:ftr": {},
|
||||
});
|
||||
});
|
||||
});
|
@ -44,7 +44,8 @@ export class FootNotes extends XmlComponent {
|
||||
line: 240,
|
||||
lineRule: "auto",
|
||||
},
|
||||
}).addRun(new SeperatorRun()),
|
||||
children: [new SeperatorRun()],
|
||||
}),
|
||||
);
|
||||
this.root.push(begin);
|
||||
|
||||
@ -56,7 +57,8 @@ export class FootNotes extends XmlComponent {
|
||||
line: 240,
|
||||
lineRule: "auto",
|
||||
},
|
||||
}).addRun(new ContinuationSeperatorRun()),
|
||||
children: [new ContinuationSeperatorRun()],
|
||||
}),
|
||||
);
|
||||
this.root.push(spacing);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import * as sinon from "sinon";
|
||||
import { HeaderWrapper } from "./header-wrapper";
|
||||
import { Media } from "./media";
|
||||
import { Paragraph } from "./paragraph";
|
||||
import { Table } from "./table";
|
||||
import { Table, TableCell, TableRow } from "./table";
|
||||
|
||||
describe("HeaderWrapper", () => {
|
||||
describe("#add", () => {
|
||||
@ -21,8 +21,15 @@ describe("HeaderWrapper", () => {
|
||||
const spy = sinon.spy(wrapper.Header, "add");
|
||||
wrapper.add(
|
||||
new Table({
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
@ -30,17 +37,6 @@ describe("HeaderWrapper", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addImage", () => {
|
||||
it("should call the underlying header's addImage", () => {
|
||||
const file = new HeaderWrapper(new Media(), 1);
|
||||
const spy = sinon.spy(file.Header, "add");
|
||||
// tslint:disable-next-line:no-any
|
||||
file.addImage({} as any);
|
||||
|
||||
expect(spy.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addChildElement", () => {
|
||||
it("should call the underlying header's addChildElement", () => {
|
||||
const file = new HeaderWrapper(new Media(), 1);
|
||||
|
@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { HeaderReferenceType } from "./document";
|
||||
import { Header } from "./header/header";
|
||||
import { Image, Media } from "./media";
|
||||
import { Media } from "./media";
|
||||
import { Paragraph } from "./paragraph";
|
||||
import { Relationships } from "./relationships";
|
||||
import { Table } from "./table";
|
||||
@ -27,11 +27,6 @@ export class HeaderWrapper {
|
||||
return this;
|
||||
}
|
||||
|
||||
public addImage(image: Image): HeaderWrapper {
|
||||
this.header.add(image.Paragraph);
|
||||
return this;
|
||||
}
|
||||
|
||||
public addChildElement(childElement: XmlComponent | string): void {
|
||||
this.header.addChildElement(childElement);
|
||||
}
|
||||
|
58
src/file/header/header.spec.ts
Normal file
58
src/file/header/header.spec.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { Paragraph } from "../paragraph";
|
||||
import { Header } from "./header";
|
||||
|
||||
describe("Header", () => {
|
||||
it("should create", () => {
|
||||
const header = new Header(1);
|
||||
|
||||
const tree = new Formatter().format(header);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:hdr": {
|
||||
_attr: {
|
||||
"xmlns:cx": "http://schemas.microsoft.com/office/drawing/2014/chartex",
|
||||
"xmlns:cx1": "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex",
|
||||
"xmlns:cx2": "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
|
||||
"xmlns:cx3": "http://schemas.microsoft.com/office/drawing/2016/5/9/chartex",
|
||||
"xmlns:cx4": "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex",
|
||||
"xmlns:cx5": "http://schemas.microsoft.com/office/drawing/2016/5/11/chartex",
|
||||
"xmlns:cx6": "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex",
|
||||
"xmlns:cx7": "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex",
|
||||
"xmlns:cx8": "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex",
|
||||
"xmlns:m": "http://schemas.openxmlformats.org/officeDocument/2006/math",
|
||||
"xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
||||
"xmlns:o": "urn:schemas-microsoft-com:office:office",
|
||||
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||
"xmlns:v": "urn:schemas-microsoft-com:vml",
|
||||
"xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||
"xmlns:w10": "urn:schemas-microsoft-com:office:word",
|
||||
"xmlns:w14": "http://schemas.microsoft.com/office/word/2010/wordml",
|
||||
"xmlns:w15": "http://schemas.microsoft.com/office/word/2012/wordml",
|
||||
"xmlns:w16cid": "http://schemas.microsoft.com/office/word/2016/wordml/cid",
|
||||
"xmlns:w16se": "http://schemas.microsoft.com/office/word/2015/wordml/symex",
|
||||
"xmlns:wne": "http://schemas.microsoft.com/office/word/2006/wordml",
|
||||
"xmlns:wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"xmlns:wp14": "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
|
||||
"xmlns:wpc": "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
||||
"xmlns:wpg": "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
|
||||
"xmlns:wpi": "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
|
||||
"xmlns:wps": "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should create with initContent", () => {
|
||||
const header = new Header(1, new Paragraph({}));
|
||||
|
||||
const tree = new Formatter().format(header);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:hdr": {},
|
||||
});
|
||||
});
|
||||
});
|
@ -1,13 +0,0 @@
|
||||
import { ImageParagraph, PictureRun } from "../paragraph";
|
||||
|
||||
export class Image {
|
||||
constructor(private readonly paragraph: ImageParagraph) {}
|
||||
|
||||
public get Paragraph(): ImageParagraph {
|
||||
return this.paragraph;
|
||||
}
|
||||
|
||||
public get Run(): PictureRun {
|
||||
return this.paragraph.Run;
|
||||
}
|
||||
}
|
@ -1,3 +1,2 @@
|
||||
export * from "./media";
|
||||
export * from "./data";
|
||||
export * from "./image";
|
||||
|
@ -80,6 +80,16 @@ describe("Media", () => {
|
||||
const image = new Media().addMedia("");
|
||||
expect(image.stream).to.be.an.instanceof(Uint8Array);
|
||||
});
|
||||
|
||||
it("should use data as is if its not a string", () => {
|
||||
// tslint:disable-next-line
|
||||
((process as any).atob as any) = () => "atob result";
|
||||
// tslint:disable-next-line:no-any
|
||||
(Media as any).generateId = () => "test";
|
||||
|
||||
const image = new Media().addMedia(new Buffer(""));
|
||||
expect(image.stream).to.be.an.instanceof(Uint8Array);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getMedia", () => {
|
||||
|
@ -7,14 +7,15 @@ import {
|
||||
ISpacingProperties,
|
||||
KeepLines,
|
||||
KeepNext,
|
||||
LeftTabStop,
|
||||
MaxRightTabStop,
|
||||
Spacing,
|
||||
TabStop,
|
||||
TabStopType,
|
||||
ThematicBreak,
|
||||
} from "../paragraph/formatting";
|
||||
import { ParagraphProperties } from "../paragraph/properties";
|
||||
import * as formatting from "../paragraph/run/formatting";
|
||||
import { RunProperties } from "../paragraph/run/properties";
|
||||
import { UnderlineType } from "../paragraph/run/underline";
|
||||
|
||||
interface ILevelAttributesProperties {
|
||||
readonly ilvl?: number;
|
||||
@ -184,7 +185,7 @@ export class LevelBase extends XmlComponent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public underline(underlineType?: string, color?: string): Level {
|
||||
public underline(underlineType?: UnderlineType, color?: string): Level {
|
||||
this.addRunProperty(new formatting.Underline(underlineType, color));
|
||||
return this;
|
||||
}
|
||||
@ -235,13 +236,12 @@ export class LevelBase extends XmlComponent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public maxRightTabStop(): Level {
|
||||
this.addParagraphProperty(new MaxRightTabStop());
|
||||
return this;
|
||||
public rightTabStop(position: number): Level {
|
||||
return this.addParagraphProperty(new TabStop(TabStopType.RIGHT, position));
|
||||
}
|
||||
|
||||
public leftTabStop(position: number): Level {
|
||||
this.addParagraphProperty(new LeftTabStop(position));
|
||||
this.addParagraphProperty(new TabStop(TabStopType.LEFT, position));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,8 @@ import { Num } from "./num";
|
||||
import { Numbering } from "./numbering";
|
||||
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
import { TabStopPosition } from "../paragraph";
|
||||
import { UnderlineType } from "../paragraph/run/underline";
|
||||
|
||||
describe("Numbering", () => {
|
||||
let numbering: Numbering;
|
||||
@ -202,7 +204,7 @@ describe("AbstractNumbering", () => {
|
||||
|
||||
it("#maxRightTabStop", () => {
|
||||
const abstractNumbering = new AbstractNumbering(1);
|
||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").maxRightTabStop();
|
||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").rightTabStop(TabStopPosition.MAX);
|
||||
const tree = new Formatter().format(level);
|
||||
expect(tree["w:lvl"]).to.include({
|
||||
"w:pPr": [
|
||||
@ -355,7 +357,7 @@ describe("AbstractNumbering", () => {
|
||||
|
||||
it("should set the style if given", () => {
|
||||
const abstractNumbering = new AbstractNumbering(1);
|
||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline("double");
|
||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline(UnderlineType.DOUBLE);
|
||||
const tree = new Formatter().format(level);
|
||||
expect(tree["w:lvl"]).to.include({
|
||||
"w:rPr": [{ "w:u": { _attr: { "w:val": "double" } } }],
|
||||
@ -364,7 +366,7 @@ describe("AbstractNumbering", () => {
|
||||
|
||||
it("should set the style and color if given", () => {
|
||||
const abstractNumbering = new AbstractNumbering(1);
|
||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline("double", "005599");
|
||||
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline(UnderlineType.DOUBLE, "005599");
|
||||
const tree = new Formatter().format(level);
|
||||
expect(tree["w:lvl"]).to.include({
|
||||
"w:rPr": [{ "w:u": { _attr: { "w:val": "double", "w:color": "005599" } } }],
|
||||
|
@ -1,11 +1,87 @@
|
||||
import { assert, expect } from "chai";
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { ThematicBreak } from "./border";
|
||||
import { Border, ThematicBreak } from "./border";
|
||||
|
||||
describe("Border", () => {
|
||||
// TODO: Need tests here
|
||||
describe("#constructor", () => {
|
||||
it("should create", () => {
|
||||
const border = new Border({
|
||||
top: {
|
||||
color: "red",
|
||||
space: 1,
|
||||
value: "test",
|
||||
size: 2,
|
||||
},
|
||||
bottom: {
|
||||
color: "red",
|
||||
space: 3,
|
||||
value: "test",
|
||||
size: 4,
|
||||
},
|
||||
left: {
|
||||
color: "red",
|
||||
space: 5,
|
||||
value: "test",
|
||||
size: 6,
|
||||
},
|
||||
right: {
|
||||
color: "red",
|
||||
space: 7,
|
||||
value: "test",
|
||||
size: 8,
|
||||
},
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(border);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:pBdr": [
|
||||
{
|
||||
"w:top": {
|
||||
_attr: {
|
||||
"w:color": "red",
|
||||
"w:space": 1,
|
||||
"w:sz": 2,
|
||||
"w:val": "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:bottom": {
|
||||
_attr: {
|
||||
"w:color": "red",
|
||||
"w:space": 3,
|
||||
"w:sz": 4,
|
||||
"w:val": "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:left": {
|
||||
_attr: {
|
||||
"w:color": "red",
|
||||
"w:space": 5,
|
||||
"w:sz": 6,
|
||||
"w:val": "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:right": {
|
||||
_attr: {
|
||||
"w:color": "red",
|
||||
"w:space": 7,
|
||||
"w:sz": 8,
|
||||
"w:val": "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("ThematicBreak", () => {
|
||||
@ -16,17 +92,6 @@ describe("ThematicBreak", () => {
|
||||
});
|
||||
|
||||
describe("#constructor()", () => {
|
||||
it("should create valid JSON", () => {
|
||||
const stringifiedJson = JSON.stringify(thematicBreak);
|
||||
|
||||
try {
|
||||
JSON.parse(stringifiedJson);
|
||||
} catch (e) {
|
||||
assert.isTrue(false);
|
||||
}
|
||||
assert.isTrue(true);
|
||||
});
|
||||
|
||||
it("should create a Thematic Break with correct border properties", () => {
|
||||
const tree = new Formatter().format(thematicBreak);
|
||||
expect(tree).to.deep.equal({
|
||||
|
@ -2,13 +2,13 @@ import { assert } from "chai";
|
||||
|
||||
import { Utility } from "tests/utility";
|
||||
|
||||
import { LeaderType, LeftTabStop, MaxRightTabStop, RightTabStop } from "./tab-stop";
|
||||
import { LeaderType, TabStop, TabStopType } from "./tab-stop";
|
||||
|
||||
describe("LeftTabStop", () => {
|
||||
let tabStop: LeftTabStop;
|
||||
let tabStop: TabStop;
|
||||
|
||||
beforeEach(() => {
|
||||
tabStop = new LeftTabStop(100);
|
||||
tabStop = new TabStop(TabStopType.LEFT, 100);
|
||||
});
|
||||
|
||||
describe("#constructor()", () => {
|
||||
@ -29,10 +29,10 @@ describe("LeftTabStop", () => {
|
||||
});
|
||||
|
||||
describe("RightTabStop", () => {
|
||||
let tabStop: RightTabStop;
|
||||
let tabStop: TabStop;
|
||||
|
||||
beforeEach(() => {
|
||||
tabStop = new RightTabStop(100, LeaderType.DOT);
|
||||
tabStop = new TabStop(TabStopType.RIGHT, 100, LeaderType.DOT);
|
||||
});
|
||||
|
||||
describe("#constructor()", () => {
|
||||
@ -52,28 +52,3 @@ describe("RightTabStop", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2,13 +2,13 @@
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
export class TabStop extends XmlComponent {
|
||||
constructor(tab: TabStopItem) {
|
||||
constructor(type: TabStopType, position: number, leader?: LeaderType) {
|
||||
super("w:tabs");
|
||||
this.root.push(tab);
|
||||
this.root.push(new TabStopItem(type, position, leader));
|
||||
}
|
||||
}
|
||||
|
||||
export enum TabValue {
|
||||
export enum TabStopType {
|
||||
LEFT = "left",
|
||||
RIGHT = "right",
|
||||
CENTER = "center",
|
||||
@ -28,8 +28,12 @@ export enum LeaderType {
|
||||
UNDERSCORE = "underscore",
|
||||
}
|
||||
|
||||
export enum TabStopPosition {
|
||||
MAX = 9026,
|
||||
}
|
||||
|
||||
export class TabAttributes extends XmlAttributeComponent<{
|
||||
readonly val: TabValue;
|
||||
readonly val: TabStopType;
|
||||
readonly pos: string | number;
|
||||
readonly leader?: LeaderType;
|
||||
}> {
|
||||
@ -37,7 +41,7 @@ export class TabAttributes extends XmlAttributeComponent<{
|
||||
}
|
||||
|
||||
export class TabStopItem extends XmlComponent {
|
||||
constructor(value: TabValue, position: string | number, leader?: LeaderType) {
|
||||
constructor(value: TabStopType, position: string | number, leader?: LeaderType) {
|
||||
super("w:tab");
|
||||
this.root.push(
|
||||
new TabAttributes({
|
||||
@ -48,27 +52,3 @@ export class TabStopItem extends XmlComponent {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class MaxRightTabStop extends TabStop {
|
||||
constructor(leader?: LeaderType) {
|
||||
super(new TabStopItem(TabValue.RIGHT, 9026, leader));
|
||||
}
|
||||
}
|
||||
|
||||
export class LeftTabStop extends TabStop {
|
||||
constructor(position: number, leader?: LeaderType) {
|
||||
super(new TabStopItem(TabValue.LEFT, position, leader));
|
||||
}
|
||||
}
|
||||
|
||||
export class RightTabStop extends TabStop {
|
||||
constructor(position: number, leader?: LeaderType) {
|
||||
super(new TabStopItem(TabValue.RIGHT, position, leader));
|
||||
}
|
||||
}
|
||||
|
||||
export class CenterTabStop extends TabStop {
|
||||
constructor(position: number, leader?: LeaderType) {
|
||||
super(new TabStopItem(TabValue.CENTER, position, leader));
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +0,0 @@
|
||||
// tslint:disable:object-literal-key-quotes
|
||||
import { assert } from "chai";
|
||||
|
||||
import { ImageParagraph } from "./image";
|
||||
|
||||
describe("Image", () => {
|
||||
let image: ImageParagraph;
|
||||
|
||||
beforeEach(() => {
|
||||
image = new ImageParagraph({
|
||||
stream: new Buffer(""),
|
||||
path: "",
|
||||
fileName: "test.png",
|
||||
dimensions: {
|
||||
pixels: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
},
|
||||
emus: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe("#constructor()", () => {
|
||||
it("should create valid JSON", () => {
|
||||
const stringifiedJson = JSON.stringify(image);
|
||||
|
||||
try {
|
||||
JSON.parse(stringifiedJson);
|
||||
} catch (e) {
|
||||
assert.isTrue(false);
|
||||
}
|
||||
assert.isTrue(true);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,18 +0,0 @@
|
||||
import { IDrawingOptions } from "../drawing";
|
||||
import { IMediaData } from "../media";
|
||||
import { Paragraph } from "./paragraph";
|
||||
import { PictureRun } from "./run";
|
||||
|
||||
export class ImageParagraph extends Paragraph {
|
||||
private readonly pictureRun: PictureRun;
|
||||
|
||||
constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) {
|
||||
super({});
|
||||
this.pictureRun = new PictureRun(imageData, drawingOptions);
|
||||
this.root.push(this.pictureRun);
|
||||
}
|
||||
|
||||
public get Run(): PictureRun {
|
||||
return this.pictureRun;
|
||||
}
|
||||
}
|
@ -3,5 +3,4 @@ export * from "./paragraph";
|
||||
export * from "./properties";
|
||||
export * from "./run";
|
||||
export * from "./links";
|
||||
export * from "./image";
|
||||
export * from "./math";
|
||||
|
@ -4,7 +4,7 @@ import { Formatter } from "export/formatter";
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
|
||||
import { Numbering } from "../numbering";
|
||||
import { AlignmentType, HeadingLevel, LeaderType } from "./formatting";
|
||||
import { AlignmentType, HeadingLevel, LeaderType, PageBreak, TabStopPosition, TabStopType } from "./formatting";
|
||||
import { Paragraph } from "./paragraph";
|
||||
|
||||
describe("Paragraph", () => {
|
||||
@ -254,11 +254,14 @@ describe("Paragraph", () => {
|
||||
});
|
||||
|
||||
describe("#maxRightTabStop()", () => {
|
||||
it("should add maxRightTabStop to JSON", () => {
|
||||
it("should add right tab stop to JSON", () => {
|
||||
const paragraph = new Paragraph({
|
||||
tabStop: {
|
||||
maxRight: {},
|
||||
},
|
||||
tabStops: [
|
||||
{
|
||||
type: TabStopType.RIGHT,
|
||||
position: TabStopPosition.MAX,
|
||||
},
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
@ -287,12 +290,13 @@ describe("Paragraph", () => {
|
||||
describe("#leftTabStop()", () => {
|
||||
it("should add leftTabStop to JSON", () => {
|
||||
const paragraph = new Paragraph({
|
||||
tabStop: {
|
||||
left: {
|
||||
tabStops: [
|
||||
{
|
||||
type: TabStopType.LEFT,
|
||||
position: 100,
|
||||
leader: LeaderType.HYPHEN,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
@ -322,12 +326,13 @@ describe("Paragraph", () => {
|
||||
describe("#rightTabStop()", () => {
|
||||
it("should add rightTabStop to JSON", () => {
|
||||
const paragraph = new Paragraph({
|
||||
tabStop: {
|
||||
right: {
|
||||
tabStops: [
|
||||
{
|
||||
type: TabStopType.RIGHT,
|
||||
position: 100,
|
||||
leader: LeaderType.DOT,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
@ -357,12 +362,13 @@ describe("Paragraph", () => {
|
||||
describe("#centerTabStop()", () => {
|
||||
it("should add centerTabStop to JSON", () => {
|
||||
const paragraph = new Paragraph({
|
||||
tabStop: {
|
||||
center: {
|
||||
tabStops: [
|
||||
{
|
||||
type: TabStopType.CENTER,
|
||||
position: 100,
|
||||
leader: LeaderType.MIDDLE_DOT,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
@ -492,8 +498,9 @@ describe("Paragraph", () => {
|
||||
|
||||
describe("#pageBreak()", () => {
|
||||
it("should add page break to JSON", () => {
|
||||
const paragraph = new Paragraph({});
|
||||
paragraph.pageBreak();
|
||||
const paragraph = new Paragraph({
|
||||
children: [new PageBreak()],
|
||||
});
|
||||
const tree = new Formatter().format(paragraph);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:p": [
|
||||
|
@ -1,6 +1,5 @@
|
||||
// http://officeopenxml.com/WPparagraph.php
|
||||
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
||||
import { Image } from "file/media";
|
||||
import { Num } from "file/numbering/num";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
@ -12,17 +11,12 @@ import { KeepLines, KeepNext } from "./formatting/keep";
|
||||
import { PageBreak, PageBreakBefore } from "./formatting/page-break";
|
||||
import { ContextualSpacing, ISpacingProperties, Spacing } from "./formatting/spacing";
|
||||
import { HeadingLevel, Style } from "./formatting/style";
|
||||
import { CenterTabStop, LeaderType, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop";
|
||||
import { LeaderType, TabStop, TabStopPosition, TabStopType } from "./formatting/tab-stop";
|
||||
import { NumberProperties } from "./formatting/unordered-list";
|
||||
import { Bookmark, Hyperlink, OutlineLevel } from "./links";
|
||||
import { Math } from "./math";
|
||||
import { ParagraphProperties } from "./properties";
|
||||
import { PictureRun, Run, SequentialIdentifier, TextRun } from "./run";
|
||||
|
||||
interface ITabStopOptions {
|
||||
readonly position: number;
|
||||
readonly leader?: LeaderType;
|
||||
}
|
||||
import { PictureRun, Run, SequentialIdentifier, SymbolRun, TextRun } from "./run";
|
||||
|
||||
export interface IParagraphOptions {
|
||||
readonly text?: string;
|
||||
@ -38,14 +32,11 @@ export interface IParagraphOptions {
|
||||
readonly indent?: IIndentAttributesProperties;
|
||||
readonly keepLines?: boolean;
|
||||
readonly keepNext?: boolean;
|
||||
readonly tabStop?: {
|
||||
readonly left?: ITabStopOptions;
|
||||
readonly right?: ITabStopOptions;
|
||||
readonly maxRight?: {
|
||||
readonly leader?: LeaderType;
|
||||
};
|
||||
readonly center?: ITabStopOptions;
|
||||
};
|
||||
readonly tabStops?: Array<{
|
||||
readonly position: number | TabStopPosition;
|
||||
readonly type: TabStopType;
|
||||
readonly leader?: LeaderType;
|
||||
}>;
|
||||
readonly style?: string;
|
||||
readonly bullet?: {
|
||||
readonly level: number;
|
||||
@ -55,7 +46,7 @@ export interface IParagraphOptions {
|
||||
readonly level: number;
|
||||
readonly custom?: boolean;
|
||||
};
|
||||
readonly children?: Array<TextRun | PictureRun | Hyperlink | Math>;
|
||||
readonly children?: Array<TextRun | PictureRun | Hyperlink | SymbolRun | Bookmark | PageBreak | SequentialIdentifier | Math>;
|
||||
}
|
||||
|
||||
export class Paragraph extends XmlComponent {
|
||||
@ -132,21 +123,9 @@ export class Paragraph extends XmlComponent {
|
||||
this.properties.push(new KeepNext());
|
||||
}
|
||||
|
||||
if (options.tabStop) {
|
||||
if (options.tabStop.left) {
|
||||
this.properties.push(new LeftTabStop(options.tabStop.left.position, options.tabStop.left.leader));
|
||||
}
|
||||
|
||||
if (options.tabStop.right) {
|
||||
this.properties.push(new RightTabStop(options.tabStop.right.position, options.tabStop.right.leader));
|
||||
}
|
||||
|
||||
if (options.tabStop.maxRight) {
|
||||
this.properties.push(new MaxRightTabStop(options.tabStop.maxRight.leader));
|
||||
}
|
||||
|
||||
if (options.tabStop.center) {
|
||||
this.properties.push(new CenterTabStop(options.tabStop.center.position, options.tabStop.center.leader));
|
||||
if (options.tabStops) {
|
||||
for (const tabStop of options.tabStops) {
|
||||
this.properties.push(new TabStop(tabStop.type, tabStop.position, tabStop.leader));
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,41 +147,18 @@ export class Paragraph extends XmlComponent {
|
||||
|
||||
if (options.children) {
|
||||
for (const child of options.children) {
|
||||
if (child instanceof Bookmark) {
|
||||
this.root.push(child.start);
|
||||
this.root.push(child.text);
|
||||
this.root.push(child.end);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.root.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public addRun(run: Run): Paragraph {
|
||||
this.root.push(run);
|
||||
return this;
|
||||
}
|
||||
|
||||
public addHyperLink(hyperlink: Hyperlink): Paragraph {
|
||||
this.root.push(hyperlink);
|
||||
return this;
|
||||
}
|
||||
|
||||
public addBookmark(bookmark: Bookmark): Paragraph {
|
||||
// Bookmarks by spec have three components, a start, text, and end
|
||||
this.root.push(bookmark.start);
|
||||
this.root.push(bookmark.text);
|
||||
this.root.push(bookmark.end);
|
||||
return this;
|
||||
}
|
||||
|
||||
public addImage(image: Image): PictureRun {
|
||||
const run = image.Run;
|
||||
this.addRun(run);
|
||||
|
||||
return run;
|
||||
}
|
||||
|
||||
public pageBreak(): Paragraph {
|
||||
this.root.push(new PageBreak());
|
||||
return this;
|
||||
}
|
||||
|
||||
public referenceFootnote(id: number): Paragraph {
|
||||
this.root.push(new FootnoteReferenceRun(id));
|
||||
return this;
|
||||
@ -212,9 +168,4 @@ export class Paragraph extends XmlComponent {
|
||||
this.root.splice(1, 0, run);
|
||||
return this;
|
||||
}
|
||||
|
||||
public addSequentialIdentifier(identifier: string): Paragraph {
|
||||
this.root.push(new SequentialIdentifier(identifier));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
export * from "./run";
|
||||
export * from "./text-run";
|
||||
export * from "./symbol-run";
|
||||
export * from "./picture-run";
|
||||
export * from "./run-fonts";
|
||||
export * from "./sequential-identifier";
|
||||
export * from "./underline";
|
||||
|
@ -2,7 +2,7 @@ import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { NumberOfPages, Page } from "./page-number";
|
||||
import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number";
|
||||
|
||||
describe("Page", () => {
|
||||
describe("#constructor()", () => {
|
||||
@ -21,3 +21,12 @@ describe("NumberOfPages", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("NumberOfPagesSection", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("uses the font name for both ascii and hAnsi", () => {
|
||||
const tree = new Formatter().format(new NumberOfPagesSection());
|
||||
expect(tree).to.deep.equal({ "w:instrText": [{ _attr: { "xml:space": "preserve" } }, "SECTIONPAGES"] });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -20,3 +20,11 @@ export class NumberOfPages extends XmlComponent {
|
||||
this.root.push("NUMPAGES");
|
||||
}
|
||||
}
|
||||
|
||||
export class NumberOfPagesSection extends XmlComponent {
|
||||
constructor() {
|
||||
super("w:instrText");
|
||||
this.root.push(new TextAttributes({ space: SpaceType.PRESERVE }));
|
||||
this.root.push("SECTIONPAGES");
|
||||
}
|
||||
}
|
||||
|
28
src/file/paragraph/run/run-components/symbol.spec.ts
Normal file
28
src/file/paragraph/run/run-components/symbol.spec.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { Symbol } from "./symbol";
|
||||
|
||||
describe("Symbol", () => {
|
||||
describe("#constructor", () => {
|
||||
// Note: if no character is given, the output is a MS Windows logo
|
||||
it("creates an empty symbol run if no character is given", () => {
|
||||
const s = new Symbol();
|
||||
const f = new Formatter().format(s);
|
||||
expect(f).to.deep.equal({ "w:sym": { _attr: { "w:char": "", "w:font": "Wingdings" } } });
|
||||
});
|
||||
|
||||
it("creates the provided symbol with default font", () => {
|
||||
const s = new Symbol("F071");
|
||||
const f = new Formatter().format(s);
|
||||
expect(f).to.deep.equal({ "w:sym": { _attr: { "w:char": "F071", "w:font": "Wingdings" } } });
|
||||
});
|
||||
|
||||
it("creates the provided symbol with the provided font", () => {
|
||||
const s = new Symbol("F071", "Arial");
|
||||
const f = new Formatter().format(s);
|
||||
expect(f).to.deep.equal({ "w:sym": { _attr: { "w:char": "F071", "w:font": "Arial" } } });
|
||||
});
|
||||
});
|
||||
});
|
20
src/file/paragraph/run/run-components/symbol.ts
Normal file
20
src/file/paragraph/run/run-components/symbol.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
interface ISymbolAttributesProperties {
|
||||
readonly char: string;
|
||||
readonly symbolfont?: string;
|
||||
}
|
||||
|
||||
class SymbolAttributes extends XmlAttributeComponent<ISymbolAttributesProperties> {
|
||||
protected readonly xmlKeys = {
|
||||
char: "w:char",
|
||||
symbolfont: "w:font",
|
||||
};
|
||||
}
|
||||
|
||||
export class Symbol extends XmlComponent {
|
||||
constructor(char: string = "", symbolfont: string = "Wingdings") {
|
||||
super("w:sym");
|
||||
this.root.push(new SymbolAttributes({ char: char, symbolfont: symbolfont }));
|
||||
}
|
||||
}
|
@ -284,6 +284,22 @@ describe("Run", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#numberOfTotalPagesSection", () => {
|
||||
it("should set the run to the RTL mode", () => {
|
||||
const run = new Run({});
|
||||
run.numberOfTotalPagesSection();
|
||||
const tree = new Formatter().format(run);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:r": [
|
||||
{ "w:fldChar": { _attr: { "w:fldCharType": "begin" } } },
|
||||
{ "w:instrText": [{ _attr: { "xml:space": "preserve" } }, "SECTIONPAGES"] },
|
||||
{ "w:fldChar": { _attr: { "w:fldCharType": "separate" } } },
|
||||
{ "w:fldChar": { _attr: { "w:fldCharType": "end" } } },
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#pageNumber", () => {
|
||||
it("should set the run to the RTL mode", () => {
|
||||
const run = new Run({});
|
||||
|
@ -2,6 +2,7 @@
|
||||
import { ShadingType } from "file/table";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { FieldInstruction } from "file/table-of-contents/field-instruction";
|
||||
import { Break } from "./break";
|
||||
import { Caps, SmallCaps } from "./caps";
|
||||
import { Begin, End, Separate } from "./field";
|
||||
@ -21,7 +22,7 @@ import {
|
||||
SizeComplexScript,
|
||||
Strike,
|
||||
} from "./formatting";
|
||||
import { NumberOfPages, Page } from "./page-number";
|
||||
import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number";
|
||||
import { RunProperties } from "./properties";
|
||||
import { RunFonts } from "./run-fonts";
|
||||
import { SubScript, SuperScript } from "./script";
|
||||
@ -56,6 +57,7 @@ export interface IRunOptions {
|
||||
readonly fill: string;
|
||||
readonly color: string;
|
||||
};
|
||||
readonly children?: Array<Begin | FieldInstruction | Separate | End>;
|
||||
}
|
||||
|
||||
export class Run extends XmlComponent {
|
||||
@ -134,6 +136,12 @@ export class Run extends XmlComponent {
|
||||
this.properties.push(new Shading(options.shading.type, options.shading.fill, options.shading.color));
|
||||
this.properties.push(new ShadowComplexScript(options.shading.type, options.shading.fill, options.shading.color));
|
||||
}
|
||||
|
||||
if (options.children) {
|
||||
for (const child of options.children) {
|
||||
this.root.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public break(): Run {
|
||||
@ -161,4 +169,12 @@ export class Run extends XmlComponent {
|
||||
this.root.push(new End());
|
||||
return this;
|
||||
}
|
||||
|
||||
public numberOfTotalPagesSection(): Run {
|
||||
this.root.push(new Begin());
|
||||
this.root.push(new NumberOfPagesSection());
|
||||
this.root.push(new Separate());
|
||||
this.root.push(new End());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
76
src/file/paragraph/run/symbol-run.spec.ts
Normal file
76
src/file/paragraph/run/symbol-run.spec.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { UnderlineType } from "./underline";
|
||||
|
||||
import { SymbolRun } from "./symbol-run";
|
||||
|
||||
describe("SymbolRun", () => {
|
||||
let run: SymbolRun;
|
||||
|
||||
describe("#constructor()", () => {
|
||||
it("should create symbol run from text input", () => {
|
||||
run = new SymbolRun("F071");
|
||||
const f = new Formatter().format(run);
|
||||
expect(f).to.deep.equal({
|
||||
"w:r": [{ "w:sym": { _attr: { "w:char": "F071", "w:font": "Wingdings" } } }],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create symbol run from object input with just 'char' specified", () => {
|
||||
run = new SymbolRun({ char: "F071" });
|
||||
const f = new Formatter().format(run);
|
||||
expect(f).to.deep.equal({
|
||||
"w:r": [{ "w:sym": { _attr: { "w:char": "F071", "w:font": "Wingdings" } } }],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create symbol run from object input with just 'char' specified", () => {
|
||||
run = new SymbolRun({ char: "F071", symbolfont: "Arial" });
|
||||
const f = new Formatter().format(run);
|
||||
expect(f).to.deep.equal({
|
||||
"w:r": [{ "w:sym": { _attr: { "w:char": "F071", "w:font": "Arial" } } }],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add other standard run properties", () => {
|
||||
run = new SymbolRun({
|
||||
char: "F071",
|
||||
symbolfont: "Arial",
|
||||
italics: true,
|
||||
bold: true,
|
||||
underline: {
|
||||
color: "red",
|
||||
type: UnderlineType.DOUBLE,
|
||||
},
|
||||
color: "green",
|
||||
size: 40,
|
||||
highlight: "yellow",
|
||||
});
|
||||
|
||||
const f = new Formatter().format(run);
|
||||
expect(f).to.deep.equal({
|
||||
"w:r": [
|
||||
{
|
||||
"w:rPr": [
|
||||
{ "w:b": { _attr: { "w:val": true } } },
|
||||
{ "w:bCs": { _attr: { "w:val": true } } },
|
||||
{ "w:i": { _attr: { "w:val": true } } },
|
||||
{ "w:iCs": { _attr: { "w:val": true } } },
|
||||
{ "w:u": { _attr: { "w:val": "double", "w:color": "red" } } },
|
||||
{ "w:color": { _attr: { "w:val": "green" } } },
|
||||
{ "w:sz": { _attr: { "w:val": 40 } } },
|
||||
{ "w:szCs": { _attr: { "w:val": 40 } } },
|
||||
{ "w:highlight": { _attr: { "w:val": "yellow" } } },
|
||||
{ "w:highlightCs": { _attr: { "w:val": "yellow" } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:sym": { _attr: { "w:char": "F071", "w:font": "Arial" } },
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
20
src/file/paragraph/run/symbol-run.ts
Normal file
20
src/file/paragraph/run/symbol-run.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { IRunOptions, Run } from "./run";
|
||||
import { Symbol } from "./run-components/symbol";
|
||||
|
||||
export interface ISymbolRunOptions extends IRunOptions {
|
||||
readonly char: string;
|
||||
readonly symbolfont?: string;
|
||||
}
|
||||
|
||||
export class SymbolRun extends Run {
|
||||
constructor(options: ISymbolRunOptions | string) {
|
||||
if (typeof options === "string") {
|
||||
super({});
|
||||
this.root.push(new Symbol(options));
|
||||
return;
|
||||
}
|
||||
|
||||
super(options);
|
||||
this.root.push(new Symbol(options.char, options.symbolfont));
|
||||
}
|
||||
}
|
@ -16,4 +16,23 @@ describe("TextRun", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#referenceFootnote()", () => {
|
||||
it("should add a valid footnote reference", () => {
|
||||
run = new TextRun("test");
|
||||
run.referenceFootnote(1);
|
||||
const tree = new Formatter().format(run);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:r": [
|
||||
{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "test"] },
|
||||
{
|
||||
"w:r": [
|
||||
{ "w:rPr": [{ "w:rStyle": { _attr: { "w:val": "FootnoteReference" } } }] },
|
||||
{ "w:footnoteReference": { _attr: { "w:id": 1 } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
||||
import { IRunOptions, Run } from "./run";
|
||||
import { Text } from "./run-components/text";
|
||||
|
||||
@ -16,4 +17,9 @@ export class TextRun extends Run {
|
||||
super(options);
|
||||
this.root.push(new Text(options.text));
|
||||
}
|
||||
|
||||
public referenceFootnote(id: number): TextRun {
|
||||
this.root.push(new FootnoteReferenceRun(id));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ describe("Underline", () => {
|
||||
});
|
||||
|
||||
it("should use the given style type and color", () => {
|
||||
const underline = new u.Underline("double", "FF00CC");
|
||||
const underline = new u.Underline(u.UnderlineType.DOUBLE, "FF00CC");
|
||||
const tree = new Formatter().format(underline);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:u": { _attr: { "w:val": "double", "w:color": "FF00CC" } },
|
||||
|
@ -33,7 +33,7 @@ export abstract class BaseUnderline extends XmlComponent {
|
||||
}
|
||||
|
||||
export class Underline extends BaseUnderline {
|
||||
constructor(underlineType: string = "single", color?: string) {
|
||||
constructor(underlineType: UnderlineType = UnderlineType.SINGLE, color?: string) {
|
||||
super(underlineType, color);
|
||||
}
|
||||
}
|
||||
|
@ -38,11 +38,13 @@ export class ExternalStylesFactory {
|
||||
throw new Error("can not find styles element");
|
||||
}
|
||||
|
||||
const importedStyle = new Styles(new ImportedRootElementAttributes(stylesXmlElement.attributes));
|
||||
const stylesElements = stylesXmlElement.elements || [];
|
||||
for (const childElm of stylesElements) {
|
||||
importedStyle.push(convertToXmlComponent(childElm) as ImportedXmlComponent);
|
||||
}
|
||||
|
||||
const importedStyle = new Styles({
|
||||
initialStyles: new ImportedRootElementAttributes(stylesXmlElement.attributes),
|
||||
importedStyles: stylesElements.map((childElm) => convertToXmlComponent(childElm) as ImportedXmlComponent),
|
||||
});
|
||||
|
||||
return importedStyle;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { DocumentAttributes } from "../document/document-attributes";
|
||||
import { Color, Italics, Size } from "../paragraph/run/formatting";
|
||||
import { Styles } from "./";
|
||||
import { IStylesOptions } from "./styles";
|
||||
|
||||
import { DocumentDefaults } from "./defaults";
|
||||
import {
|
||||
FootnoteReferenceStyle,
|
||||
FootnoteText,
|
||||
@ -18,7 +18,7 @@ import {
|
||||
} from "./style";
|
||||
|
||||
export class DefaultStylesFactory {
|
||||
public newInstance(): Styles {
|
||||
public newInstance(): IStylesOptions {
|
||||
const documentAttributes = new DocumentAttributes({
|
||||
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
||||
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||
@ -27,57 +27,55 @@ export class DefaultStylesFactory {
|
||||
w15: "http://schemas.microsoft.com/office/word/2012/wordml",
|
||||
Ignorable: "w14 w15",
|
||||
});
|
||||
const styles = new Styles(documentAttributes);
|
||||
styles.createDocumentDefaults();
|
||||
|
||||
const titleStyle = new TitleStyle();
|
||||
titleStyle.addRunProperty(new Size(56));
|
||||
styles.push(titleStyle);
|
||||
|
||||
const heading1Style = new Heading1Style();
|
||||
heading1Style.addRunProperty(new Color("2E74B5"));
|
||||
heading1Style.addRunProperty(new Size(32));
|
||||
styles.push(heading1Style);
|
||||
|
||||
const heading2Style = new Heading2Style();
|
||||
heading2Style.addRunProperty(new Color("2E74B5"));
|
||||
heading2Style.addRunProperty(new Size(26));
|
||||
styles.push(heading2Style);
|
||||
|
||||
const heading3Style = new Heading3Style();
|
||||
heading3Style.addRunProperty(new Color("1F4D78"));
|
||||
heading3Style.addRunProperty(new Size(24));
|
||||
styles.push(heading3Style);
|
||||
|
||||
const heading4Style = new Heading4Style();
|
||||
heading4Style.addRunProperty(new Color("2E74B5"));
|
||||
heading4Style.addRunProperty(new Italics());
|
||||
styles.push(heading4Style);
|
||||
|
||||
const heading5Style = new Heading5Style();
|
||||
heading5Style.addRunProperty(new Color("2E74B5"));
|
||||
styles.push(heading5Style);
|
||||
|
||||
const heading6Style = new Heading6Style();
|
||||
heading6Style.addRunProperty(new Color("1F4D78"));
|
||||
styles.push(heading6Style);
|
||||
|
||||
const listParagraph = new ListParagraph();
|
||||
// listParagraph.addParagraphProperty();
|
||||
styles.push(listParagraph);
|
||||
|
||||
const hyperLinkStyle = new HyperlinkStyle();
|
||||
styles.push(hyperLinkStyle);
|
||||
|
||||
const footnoteReferenceStyle = new FootnoteReferenceStyle();
|
||||
styles.push(footnoteReferenceStyle);
|
||||
|
||||
const footnoteTextStyle = new FootnoteText();
|
||||
styles.push(footnoteTextStyle);
|
||||
|
||||
const footnoteTextCharStyle = new FootnoteTextChar();
|
||||
styles.push(footnoteTextCharStyle);
|
||||
|
||||
return styles;
|
||||
return {
|
||||
initialStyles: documentAttributes,
|
||||
importedStyles: [
|
||||
new DocumentDefaults(),
|
||||
new TitleStyle({
|
||||
run: {
|
||||
size: 56,
|
||||
},
|
||||
}),
|
||||
new Heading1Style({
|
||||
run: {
|
||||
color: "2E74B5",
|
||||
size: 32,
|
||||
},
|
||||
}),
|
||||
new Heading2Style({
|
||||
run: {
|
||||
color: "2E74B5",
|
||||
size: 26,
|
||||
},
|
||||
}),
|
||||
new Heading3Style({
|
||||
run: {
|
||||
color: "1F4D78",
|
||||
size: 24,
|
||||
},
|
||||
}),
|
||||
new Heading4Style({
|
||||
run: {
|
||||
color: "2E74B5",
|
||||
italics: true,
|
||||
},
|
||||
}),
|
||||
new Heading5Style({
|
||||
run: {
|
||||
color: "2E74B5",
|
||||
},
|
||||
}),
|
||||
new Heading6Style({
|
||||
run: {
|
||||
color: "1F4D78",
|
||||
},
|
||||
}),
|
||||
new ListParagraph({}),
|
||||
new HyperlinkStyle({}),
|
||||
new FootnoteReferenceStyle({}),
|
||||
new FootnoteText({}),
|
||||
new FootnoteTextChar({}),
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,16 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
import { UnderlineType } from "file/paragraph/run/underline";
|
||||
import { ShadingType } from "file/table";
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
|
||||
import { CharacterStyle } from "./character-style";
|
||||
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
|
||||
describe("CharacterStyle", () => {
|
||||
describe("#constructor", () => {
|
||||
it("should set the style type to character and use the given style id", () => {
|
||||
const style = new CharacterStyle("myStyleId");
|
||||
const style = new CharacterStyle({ id: "myStyleId" });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -17,7 +18,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -29,7 +30,10 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
|
||||
it("should set the name of the style, if given", () => {
|
||||
const style = new CharacterStyle("myStyleId", "Style Name");
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
name: "Style Name",
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -38,7 +42,222 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:unhideWhenUsed": EMPTY_OBJECT,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add smallCaps", () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
smallCaps: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:smallCaps": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:unhideWhenUsed": EMPTY_OBJECT,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add allCaps", () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
allCaps: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:caps": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:unhideWhenUsed": EMPTY_OBJECT,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add strike", () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
strike: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:strike": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:unhideWhenUsed": EMPTY_OBJECT,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add double strike", () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
doubleStrike: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:dstrike": { _attr: { "w:val": true } } }],
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:unhideWhenUsed": EMPTY_OBJECT,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add sub script", () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
subScript: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [
|
||||
{
|
||||
"w:vertAlign": {
|
||||
_attr: {
|
||||
"w:val": "subscript",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:unhideWhenUsed": EMPTY_OBJECT,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add font", () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
font: "test font",
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [
|
||||
{
|
||||
"w:rFonts": {
|
||||
_attr: {
|
||||
"w:ascii": "test font",
|
||||
"w:cs": "test font",
|
||||
"w:eastAsia": "test font",
|
||||
"w:hAnsi": "test font",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:unhideWhenUsed": EMPTY_OBJECT,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add character spacing", () => {
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
characterSpacing: 100,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "myStyleId" } },
|
||||
{
|
||||
"w:rPr": [{ "w:spacing": { _attr: { "w:val": 100 } } }],
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -52,7 +271,7 @@ describe("CharacterStyle", () => {
|
||||
|
||||
describe("formatting methods: style attributes", () => {
|
||||
it("#basedOn", () => {
|
||||
const style = new CharacterStyle("myStyleId").basedOn("otherId");
|
||||
const style = new CharacterStyle({ id: "myStyleId", basedOn: "otherId" });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -60,7 +279,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -75,7 +294,12 @@ describe("CharacterStyle", () => {
|
||||
|
||||
describe("formatting methods: run properties", () => {
|
||||
it("#size", () => {
|
||||
const style = new CharacterStyle("myStyleId").size(24);
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
size: 24,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -86,7 +310,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -99,7 +323,12 @@ describe("CharacterStyle", () => {
|
||||
|
||||
describe("#underline", () => {
|
||||
it("should set underline to 'single' if no arguments are given", () => {
|
||||
const style = new CharacterStyle("myStyleId").underline();
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
underline: {},
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -110,7 +339,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -122,7 +351,14 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
|
||||
it("should set the style if given", () => {
|
||||
const style = new CharacterStyle("myStyleId").underline("double");
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
underline: {
|
||||
type: UnderlineType.DOUBLE,
|
||||
},
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -133,7 +369,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -145,7 +381,15 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
|
||||
it("should set the style and color if given", () => {
|
||||
const style = new CharacterStyle("myStyleId").underline("double", "005599");
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
underline: {
|
||||
type: UnderlineType.DOUBLE,
|
||||
color: "005599",
|
||||
},
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -156,7 +400,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -169,7 +413,12 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
|
||||
it("#superScript", () => {
|
||||
const style = new CharacterStyle("myStyleId").superScript();
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
superScript: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -188,7 +437,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -200,7 +449,12 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
|
||||
it("#color", () => {
|
||||
const style = new CharacterStyle("myStyleId").color("123456");
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
color: "123456",
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -211,7 +465,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -223,7 +477,12 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
|
||||
it("#bold", () => {
|
||||
const style = new CharacterStyle("myStyleId").bold();
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
bold: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -234,7 +493,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -246,7 +505,12 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
|
||||
it("#italics", () => {
|
||||
const style = new CharacterStyle("myStyleId").italics();
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
italics: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -257,7 +521,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -269,7 +533,7 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
|
||||
it("#link", () => {
|
||||
const style = new CharacterStyle("myStyleId").link("MyLink");
|
||||
const style = new CharacterStyle({ id: "myStyleId", link: "MyLink" });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -277,7 +541,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -290,7 +554,7 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
|
||||
it("#semiHidden", () => {
|
||||
const style = new CharacterStyle("myStyleId").semiHidden();
|
||||
const style = new CharacterStyle({ id: "myStyleId", semiHidden: true });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -298,7 +562,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -309,7 +573,12 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
|
||||
it("#highlight", () => {
|
||||
const style = new CharacterStyle("myStyleId").highlight("005599");
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
highlight: "005599",
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -320,7 +589,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -332,7 +601,16 @@ describe("CharacterStyle", () => {
|
||||
});
|
||||
|
||||
it("#shadow", () => {
|
||||
const style = new CharacterStyle("myStyleId").shadow("pct10", "00FFFF", "FF0000");
|
||||
const style = new CharacterStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
shadow: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -343,7 +621,7 @@ describe("CharacterStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,69 +1,128 @@
|
||||
import * as formatting from "file/paragraph/run/formatting";
|
||||
import { RunProperties } from "file/paragraph/run/properties";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { UnderlineType } from "file/paragraph/run/underline";
|
||||
|
||||
import { BasedOn, Link, SemiHidden, UiPriority, UnhideWhenUsed } from "./components";
|
||||
import { Style } from "./style";
|
||||
|
||||
export interface IBaseCharacterStyleOptions {
|
||||
readonly basedOn?: string;
|
||||
readonly link?: string;
|
||||
readonly semiHidden?: boolean;
|
||||
readonly run?: {
|
||||
readonly size?: number;
|
||||
readonly bold?: boolean;
|
||||
readonly italics?: boolean;
|
||||
readonly smallCaps?: boolean;
|
||||
readonly allCaps?: boolean;
|
||||
readonly strike?: boolean;
|
||||
readonly doubleStrike?: boolean;
|
||||
readonly subScript?: boolean;
|
||||
readonly superScript?: boolean;
|
||||
readonly underline?: {
|
||||
readonly type?: UnderlineType;
|
||||
readonly color?: string;
|
||||
};
|
||||
readonly color?: string;
|
||||
readonly font?: string;
|
||||
readonly characterSpacing?: number;
|
||||
readonly highlight?: string;
|
||||
readonly shadow?: {
|
||||
readonly type: string;
|
||||
readonly fill: string;
|
||||
readonly color: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface ICharacterStyleOptions extends IBaseCharacterStyleOptions {
|
||||
readonly id: string;
|
||||
readonly name?: string;
|
||||
}
|
||||
|
||||
export class CharacterStyle extends Style {
|
||||
private readonly runProperties: RunProperties;
|
||||
|
||||
constructor(styleId: string, name?: string) {
|
||||
super({ type: "character", styleId: styleId }, name);
|
||||
constructor(options: ICharacterStyleOptions) {
|
||||
super({ type: "character", styleId: options.id }, options.name);
|
||||
this.runProperties = new RunProperties();
|
||||
this.root.push(this.runProperties);
|
||||
this.root.push(new UiPriority("99"));
|
||||
this.root.push(new UiPriority(99));
|
||||
this.root.push(new UnhideWhenUsed());
|
||||
}
|
||||
|
||||
public basedOn(parentId: string): CharacterStyle {
|
||||
this.root.push(new BasedOn(parentId));
|
||||
return this;
|
||||
}
|
||||
if (options.basedOn) {
|
||||
this.root.push(new BasedOn(options.basedOn));
|
||||
}
|
||||
|
||||
public addRunProperty(property: XmlComponent): CharacterStyle {
|
||||
this.runProperties.push(property);
|
||||
return this;
|
||||
}
|
||||
if (options.link) {
|
||||
this.root.push(new Link(options.link));
|
||||
}
|
||||
|
||||
public color(color: string): CharacterStyle {
|
||||
return this.addRunProperty(new formatting.Color(color));
|
||||
}
|
||||
if (options.semiHidden) {
|
||||
this.root.push(new SemiHidden());
|
||||
}
|
||||
|
||||
public bold(): CharacterStyle {
|
||||
return this.addRunProperty(new formatting.Bold());
|
||||
}
|
||||
if (options.run) {
|
||||
if (options.run.size) {
|
||||
this.runProperties.push(new formatting.Size(options.run.size));
|
||||
this.runProperties.push(new formatting.SizeComplexScript(options.run.size));
|
||||
}
|
||||
|
||||
public italics(): CharacterStyle {
|
||||
return this.addRunProperty(new formatting.Italics());
|
||||
}
|
||||
if (options.run.bold) {
|
||||
this.runProperties.push(new formatting.Bold());
|
||||
}
|
||||
|
||||
public underline(underlineType?: string, color?: string): CharacterStyle {
|
||||
return this.addRunProperty(new formatting.Underline(underlineType, color));
|
||||
}
|
||||
if (options.run.italics) {
|
||||
this.runProperties.push(new formatting.Italics());
|
||||
}
|
||||
|
||||
public superScript(): CharacterStyle {
|
||||
return this.addRunProperty(new formatting.SuperScript());
|
||||
}
|
||||
if (options.run.smallCaps) {
|
||||
this.runProperties.push(new formatting.SmallCaps());
|
||||
}
|
||||
|
||||
public size(twips: number): CharacterStyle {
|
||||
return this.addRunProperty(new formatting.Size(twips)).addRunProperty(new formatting.SizeComplexScript(twips));
|
||||
}
|
||||
if (options.run.allCaps) {
|
||||
this.runProperties.push(new formatting.Caps());
|
||||
}
|
||||
|
||||
public link(link: string): CharacterStyle {
|
||||
this.root.push(new Link(link));
|
||||
return this;
|
||||
}
|
||||
if (options.run.strike) {
|
||||
this.runProperties.push(new formatting.Strike());
|
||||
}
|
||||
|
||||
public semiHidden(): CharacterStyle {
|
||||
this.root.push(new SemiHidden());
|
||||
return this;
|
||||
}
|
||||
if (options.run.doubleStrike) {
|
||||
this.runProperties.push(new formatting.DoubleStrike());
|
||||
}
|
||||
|
||||
public highlight(color: string): CharacterStyle {
|
||||
return this.addRunProperty(new formatting.Highlight(color));
|
||||
}
|
||||
if (options.run.subScript) {
|
||||
this.runProperties.push(new formatting.SubScript());
|
||||
}
|
||||
|
||||
public shadow(value: string, fill: string, color: string): CharacterStyle {
|
||||
return this.addRunProperty(new formatting.Shading(value, fill, color));
|
||||
if (options.run.superScript) {
|
||||
this.runProperties.push(new formatting.SuperScript());
|
||||
}
|
||||
|
||||
if (options.run.underline) {
|
||||
this.runProperties.push(new formatting.Underline(options.run.underline.type, options.run.underline.color));
|
||||
}
|
||||
|
||||
if (options.run.color) {
|
||||
this.runProperties.push(new formatting.Color(options.run.color));
|
||||
}
|
||||
|
||||
if (options.run.font) {
|
||||
this.runProperties.push(new formatting.RunFonts(options.run.font));
|
||||
}
|
||||
|
||||
if (options.run.characterSpacing) {
|
||||
this.runProperties.push(new formatting.CharacterSpacing(options.run.characterSpacing));
|
||||
}
|
||||
|
||||
if (options.run.highlight) {
|
||||
this.runProperties.push(new formatting.Highlight(options.run.highlight));
|
||||
}
|
||||
|
||||
if (options.run.shadow) {
|
||||
this.runProperties.push(new formatting.Shading(options.run.shadow.type, options.run.shadow.fill, options.run.shadow.color));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,9 +30,9 @@ describe("Style components", () => {
|
||||
});
|
||||
|
||||
it("UiPriority#constructor", () => {
|
||||
const style = new components.UiPriority("123");
|
||||
const style = new components.UiPriority(123);
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({ "w:uiPriority": { _attr: { "w:val": "123" } } });
|
||||
expect(tree).to.deep.equal({ "w:uiPriority": { _attr: { "w:val": 123 } } });
|
||||
});
|
||||
|
||||
it("UnhideWhenUsed#constructor", () => {
|
||||
|
@ -1,7 +1,8 @@
|
||||
// http://officeopenxml.com/WPstyleGenProps.php
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
interface IComponentAttributes {
|
||||
readonly val: string;
|
||||
readonly val: string | number;
|
||||
}
|
||||
|
||||
class ComponentAttributes extends XmlAttributeComponent<IComponentAttributes> {
|
||||
@ -37,7 +38,7 @@ export class Link extends XmlComponent {
|
||||
}
|
||||
|
||||
export class UiPriority extends XmlComponent {
|
||||
constructor(value: string) {
|
||||
constructor(value: number) {
|
||||
super("w:uiPriority");
|
||||
// TODO: this value should be a ST_DecimalNumber
|
||||
this.root.push(new ComponentAttributes({ val: value }));
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { expect } from "chai";
|
||||
import { Formatter } from "export/formatter";
|
||||
import * as defaultStyels from "./default-styles";
|
||||
import * as defaultStyles from "./default-styles";
|
||||
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
|
||||
describe("Default Styles", () => {
|
||||
it("HeadingStyle#constructor", () => {
|
||||
const style = new defaultStyels.HeadingStyle("Heading1", "Heading 1");
|
||||
const style = new defaultStyles.HeadingStyle({
|
||||
id: "Heading1",
|
||||
name: "Heading 1",
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -20,7 +23,7 @@ describe("Default Styles", () => {
|
||||
});
|
||||
|
||||
it("TitleStyle#constructor", () => {
|
||||
const style = new defaultStyels.TitleStyle();
|
||||
const style = new defaultStyles.TitleStyle({});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -34,7 +37,7 @@ describe("Default Styles", () => {
|
||||
});
|
||||
|
||||
it("Heading1Style#constructor", () => {
|
||||
const style = new defaultStyels.Heading1Style();
|
||||
const style = new defaultStyles.Heading1Style({});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -48,7 +51,7 @@ describe("Default Styles", () => {
|
||||
});
|
||||
|
||||
it("Heading2Style#constructor", () => {
|
||||
const style = new defaultStyels.Heading2Style();
|
||||
const style = new defaultStyles.Heading2Style({});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -62,7 +65,7 @@ describe("Default Styles", () => {
|
||||
});
|
||||
|
||||
it("Heading3Style#constructor", () => {
|
||||
const style = new defaultStyels.Heading3Style();
|
||||
const style = new defaultStyles.Heading3Style({});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -76,7 +79,7 @@ describe("Default Styles", () => {
|
||||
});
|
||||
|
||||
it("Heading4Style#constructor", () => {
|
||||
const style = new defaultStyels.Heading4Style();
|
||||
const style = new defaultStyles.Heading4Style({});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -90,7 +93,7 @@ describe("Default Styles", () => {
|
||||
});
|
||||
|
||||
it("Heading5Style#constructor", () => {
|
||||
const style = new defaultStyels.Heading5Style();
|
||||
const style = new defaultStyles.Heading5Style({});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -104,7 +107,7 @@ describe("Default Styles", () => {
|
||||
});
|
||||
|
||||
it("Heading6Style#constructor", () => {
|
||||
const style = new defaultStyels.Heading6Style();
|
||||
const style = new defaultStyles.Heading6Style({});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -118,7 +121,7 @@ describe("Default Styles", () => {
|
||||
});
|
||||
|
||||
it("ListParagraph#constructor", () => {
|
||||
const style = new defaultStyels.ListParagraph();
|
||||
const style = new defaultStyles.ListParagraph({});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -131,7 +134,7 @@ describe("Default Styles", () => {
|
||||
});
|
||||
|
||||
it("FootnoteText#constructor", () => {
|
||||
const style = new defaultStyels.FootnoteText();
|
||||
const style = new defaultStyles.FootnoteText({});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -171,14 +174,14 @@ describe("Default Styles", () => {
|
||||
{ "w:basedOn": { _attr: { "w:val": "Normal" } } },
|
||||
{ "w:link": { _attr: { "w:val": "FootnoteTextChar" } } },
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
},
|
||||
},
|
||||
"w:semiHidden": EMPTY_OBJECT,
|
||||
},
|
||||
{
|
||||
"w:semiHidden": EMPTY_OBJECT,
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:unhideWhenUsed": EMPTY_OBJECT,
|
||||
@ -188,7 +191,7 @@ describe("Default Styles", () => {
|
||||
});
|
||||
|
||||
it("FootnoteReferenceStyle#constructor", () => {
|
||||
const style = new defaultStyels.FootnoteReferenceStyle();
|
||||
const style = new defaultStyles.FootnoteReferenceStyle({});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -208,7 +211,7 @@ describe("Default Styles", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -225,7 +228,7 @@ describe("Default Styles", () => {
|
||||
});
|
||||
|
||||
it("FootnoteTextChar#constructor", () => {
|
||||
const style = new defaultStyels.FootnoteTextChar();
|
||||
const style = new defaultStyles.FootnoteTextChar({});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -252,7 +255,7 @@ describe("Default Styles", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -269,19 +272,28 @@ describe("Default Styles", () => {
|
||||
});
|
||||
|
||||
it("HyperlinkStyle#constructor", () => {
|
||||
const style = new defaultStyels.HyperlinkStyle();
|
||||
const style = new defaultStyles.HyperlinkStyle({});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
{ _attr: { "w:type": "character", "w:styleId": "Hyperlink" } },
|
||||
{ "w:name": { _attr: { "w:val": "Hyperlink" } } },
|
||||
{
|
||||
"w:rPr": [{ "w:color": { _attr: { "w:val": "0563C1" } } }, { "w:u": { _attr: { "w:val": "single" } } }],
|
||||
"w:rPr": [
|
||||
{ "w:u": { _attr: { "w:val": "single" } } },
|
||||
{
|
||||
"w:color": {
|
||||
_attr: {
|
||||
"w:val": "0563C1",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,106 +1,170 @@
|
||||
import { CharacterStyle } from "./character-style";
|
||||
import { ParagraphStyle } from "./paragraph-style";
|
||||
import { UnderlineType } from "file/paragraph/run/underline";
|
||||
|
||||
import { CharacterStyle, IBaseCharacterStyleOptions } from "./character-style";
|
||||
import { IBaseParagraphStyleOptions, IParagraphStyleOptions, ParagraphStyle } from "./paragraph-style";
|
||||
|
||||
export class HeadingStyle extends ParagraphStyle {
|
||||
constructor(styleId: string, name: string) {
|
||||
super(styleId, name);
|
||||
this.basedOn("Normal");
|
||||
this.next("Normal");
|
||||
this.quickFormat();
|
||||
constructor(options: IParagraphStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
basedOn: "Normal",
|
||||
next: "Normal",
|
||||
quickFormat: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class TitleStyle extends HeadingStyle {
|
||||
constructor() {
|
||||
super("Title", "Title");
|
||||
constructor(options: IBaseParagraphStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
id: "Title",
|
||||
name: "Title",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class Heading1Style extends HeadingStyle {
|
||||
constructor() {
|
||||
super("Heading1", "Heading 1");
|
||||
constructor(options: IBaseParagraphStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
id: "Heading1",
|
||||
name: "Heading 1",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class Heading2Style extends HeadingStyle {
|
||||
constructor() {
|
||||
super("Heading2", "Heading 2");
|
||||
constructor(options: IBaseParagraphStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
id: "Heading2",
|
||||
name: "Heading 2",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class Heading3Style extends HeadingStyle {
|
||||
constructor() {
|
||||
super("Heading3", "Heading 3");
|
||||
constructor(options: IBaseParagraphStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
id: "Heading3",
|
||||
name: "Heading 3",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class Heading4Style extends HeadingStyle {
|
||||
constructor() {
|
||||
super("Heading4", "Heading 4");
|
||||
constructor(options: IBaseParagraphStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
id: "Heading4",
|
||||
name: "Heading 4",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class Heading5Style extends HeadingStyle {
|
||||
constructor() {
|
||||
super("Heading5", "Heading 5");
|
||||
constructor(options: IBaseParagraphStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
id: "Heading5",
|
||||
name: "Heading 5",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class Heading6Style extends HeadingStyle {
|
||||
constructor() {
|
||||
super("Heading6", "Heading 6");
|
||||
constructor(options: IBaseParagraphStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
id: "Heading6",
|
||||
name: "Heading 6",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ListParagraph extends ParagraphStyle {
|
||||
constructor() {
|
||||
super("ListParagraph", "List Paragraph");
|
||||
this.basedOn("Normal");
|
||||
this.quickFormat();
|
||||
constructor(options: IBaseParagraphStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
id: "ListParagraph",
|
||||
name: "List Paragraph",
|
||||
basedOn: "Normal",
|
||||
quickFormat: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class FootnoteText extends ParagraphStyle {
|
||||
constructor() {
|
||||
super("FootnoteText", "footnote text");
|
||||
this.basedOn("Normal")
|
||||
.link("FootnoteTextChar")
|
||||
.uiPriority("99")
|
||||
.semiHidden()
|
||||
.unhideWhenUsed()
|
||||
.spacing({
|
||||
after: 0,
|
||||
line: 240,
|
||||
lineRule: "auto",
|
||||
})
|
||||
.size(20);
|
||||
constructor(options: IBaseParagraphStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
id: "FootnoteText",
|
||||
name: "footnote text",
|
||||
link: "FootnoteTextChar",
|
||||
basedOn: "Normal",
|
||||
uiPriority: 99,
|
||||
semiHidden: true,
|
||||
unhideWhenUsed: true,
|
||||
paragraph: {
|
||||
spacing: {
|
||||
after: 0,
|
||||
line: 240,
|
||||
lineRule: "auto",
|
||||
},
|
||||
},
|
||||
run: {
|
||||
size: 20,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class FootnoteReferenceStyle extends CharacterStyle {
|
||||
constructor() {
|
||||
super("FootnoteReference", "footnote reference");
|
||||
this.basedOn("DefaultParagraphFont")
|
||||
.semiHidden()
|
||||
.superScript();
|
||||
constructor(options: IBaseCharacterStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
id: "FootnoteReference",
|
||||
name: "footnote reference",
|
||||
basedOn: "DefaultParagraphFont",
|
||||
semiHidden: true,
|
||||
run: {
|
||||
superScript: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class FootnoteTextChar extends CharacterStyle {
|
||||
constructor() {
|
||||
super("FootnoteTextChar", "Footnote Text Char");
|
||||
this.basedOn("DefaultParagraphFont")
|
||||
.link("FootnoteText")
|
||||
.semiHidden()
|
||||
.size(20);
|
||||
constructor(options: IBaseCharacterStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
id: "FootnoteTextChar",
|
||||
name: "Footnote Text Char",
|
||||
basedOn: "DefaultParagraphFont",
|
||||
link: "FootnoteText",
|
||||
semiHidden: true,
|
||||
run: {
|
||||
size: 20,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class HyperlinkStyle extends CharacterStyle {
|
||||
constructor() {
|
||||
super("Hyperlink", "Hyperlink");
|
||||
this.basedOn("DefaultParagraphFont")
|
||||
.color("0563C1")
|
||||
.underline("single");
|
||||
constructor(options: IBaseCharacterStyleOptions) {
|
||||
super({
|
||||
...options,
|
||||
id: "Hyperlink",
|
||||
name: "Hyperlink",
|
||||
basedOn: "DefaultParagraphFont",
|
||||
run: {
|
||||
color: "0563C1",
|
||||
underline: {
|
||||
type: UnderlineType.SINGLE,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
import { AlignmentType, TabStopPosition } from "file/paragraph";
|
||||
import { UnderlineType } from "file/paragraph/run/underline";
|
||||
import { ShadingType } from "file/table";
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
|
||||
import { ParagraphStyle } from "./paragraph-style";
|
||||
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
|
||||
describe("ParagraphStyle", () => {
|
||||
describe("#constructor", () => {
|
||||
it("should set the style type to paragraph and use the given style id", () => {
|
||||
const style = new ParagraphStyle("myStyleId");
|
||||
const style = new ParagraphStyle({ id: "myStyleId" });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": { _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } },
|
||||
@ -17,7 +19,10 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("should set the name of the style, if given", () => {
|
||||
const style = new ParagraphStyle("myStyleId", "Style Name");
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
name: "Style Name",
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -30,7 +35,7 @@ describe("ParagraphStyle", () => {
|
||||
|
||||
describe("formatting methods: style attributes", () => {
|
||||
it("#basedOn", () => {
|
||||
const style = new ParagraphStyle("myStyleId").basedOn("otherId");
|
||||
const style = new ParagraphStyle({ id: "myStyleId", basedOn: "otherId" });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -41,7 +46,7 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#quickFormat", () => {
|
||||
const style = new ParagraphStyle("myStyleId").quickFormat();
|
||||
const style = new ParagraphStyle({ id: "myStyleId", quickFormat: true });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:qFormat": EMPTY_OBJECT }],
|
||||
@ -49,7 +54,7 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#next", () => {
|
||||
const style = new ParagraphStyle("myStyleId").next("otherId");
|
||||
const style = new ParagraphStyle({ id: "myStyleId", next: "otherId" });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -62,7 +67,12 @@ describe("ParagraphStyle", () => {
|
||||
|
||||
describe("formatting methods: paragraph properties", () => {
|
||||
it("#indent", () => {
|
||||
const style = new ParagraphStyle("myStyleId").indent({ left: 720 });
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
paragraph: {
|
||||
indent: { left: 720 },
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -75,7 +85,7 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#spacing", () => {
|
||||
const style = new ParagraphStyle("myStyleId").spacing({ before: 50, after: 150 });
|
||||
const style = new ParagraphStyle({ id: "myStyleId", paragraph: { spacing: { before: 50, after: 150 } } });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -88,7 +98,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#center", () => {
|
||||
const style = new ParagraphStyle("myStyleId").center();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
paragraph: {
|
||||
alignment: AlignmentType.CENTER,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -101,7 +116,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#character spacing", () => {
|
||||
const style = new ParagraphStyle("myStyleId").characterSpacing(24);
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
characterSpacing: 24,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -114,7 +134,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#left", () => {
|
||||
const style = new ParagraphStyle("myStyleId").left();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
paragraph: {
|
||||
alignment: AlignmentType.LEFT,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -127,7 +152,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#right", () => {
|
||||
const style = new ParagraphStyle("myStyleId").right();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
paragraph: {
|
||||
alignment: AlignmentType.RIGHT,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -140,7 +170,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#justified", () => {
|
||||
const style = new ParagraphStyle("myStyleId").justified();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
paragraph: {
|
||||
alignment: AlignmentType.JUSTIFIED,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -153,7 +188,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#thematicBreak", () => {
|
||||
const style = new ParagraphStyle("myStyleId").thematicBreak();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
paragraph: {
|
||||
thematicBreak: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -181,7 +221,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#leftTabStop", () => {
|
||||
const style = new ParagraphStyle("myStyleId").leftTabStop(1200);
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
paragraph: {
|
||||
leftTabStop: 1200,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -198,7 +243,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#maxRightTabStop", () => {
|
||||
const style = new ParagraphStyle("myStyleId").maxRightTabStop();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
paragraph: {
|
||||
rightTabStop: TabStopPosition.MAX,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -215,7 +265,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#keepLines", () => {
|
||||
const style = new ParagraphStyle("myStyleId").keepLines();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
paragraph: {
|
||||
keepLines: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:pPr": [{ "w:keepLines": EMPTY_OBJECT }] }],
|
||||
@ -223,7 +278,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#keepNext", () => {
|
||||
const style = new ParagraphStyle("myStyleId").keepNext();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
paragraph: {
|
||||
keepNext: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:pPr": [{ "w:keepNext": EMPTY_OBJECT }] }],
|
||||
@ -231,7 +291,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#outlineLevel", () => {
|
||||
const style = new ParagraphStyle("myStyleId").outlineLevel(1);
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
paragraph: {
|
||||
outlineLevel: 1,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -244,7 +309,12 @@ describe("ParagraphStyle", () => {
|
||||
|
||||
describe("formatting methods: run properties", () => {
|
||||
it("#size", () => {
|
||||
const style = new ParagraphStyle("myStyleId").size(24);
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
size: 24,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -257,7 +327,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#smallCaps", () => {
|
||||
const style = new ParagraphStyle("myStyleId").smallCaps();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
smallCaps: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -270,7 +345,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#allCaps", () => {
|
||||
const style = new ParagraphStyle("myStyleId").allCaps();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
allCaps: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -283,7 +363,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#strike", () => {
|
||||
const style = new ParagraphStyle("myStyleId").strike();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
strike: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -296,7 +381,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#doubleStrike", () => {
|
||||
const style = new ParagraphStyle("myStyleId").doubleStrike();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
doubleStrike: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -309,7 +399,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#subScript", () => {
|
||||
const style = new ParagraphStyle("myStyleId").subScript();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
subScript: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -322,7 +417,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#superScript", () => {
|
||||
const style = new ParagraphStyle("myStyleId").superScript();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
superScript: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -335,7 +435,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#font", () => {
|
||||
const style = new ParagraphStyle("myStyleId").font("Times");
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
font: "Times",
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -350,7 +455,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#bold", () => {
|
||||
const style = new ParagraphStyle("myStyleId").bold();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
bold: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -363,7 +473,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#italics", () => {
|
||||
const style = new ParagraphStyle("myStyleId").italics();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
italics: true,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -376,7 +491,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#highlight", () => {
|
||||
const style = new ParagraphStyle("myStyleId").highlight("005599");
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
highlight: "005599",
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -389,7 +509,16 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#shadow", () => {
|
||||
const style = new ParagraphStyle("myStyleId").shadow("pct10", "00FFFF", "FF0000");
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
shadow: {
|
||||
type: ShadingType.PERCENT_10,
|
||||
fill: "00FFFF",
|
||||
color: "FF0000",
|
||||
},
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -403,7 +532,12 @@ describe("ParagraphStyle", () => {
|
||||
|
||||
describe("#underline", () => {
|
||||
it("should set underline to 'single' if no arguments are given", () => {
|
||||
const style = new ParagraphStyle("myStyleId").underline();
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
underline: {},
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -416,7 +550,14 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("should set the style if given", () => {
|
||||
const style = new ParagraphStyle("myStyleId").underline("double");
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
underline: {
|
||||
type: UnderlineType.DOUBLE,
|
||||
},
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -429,7 +570,15 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("should set the style and color if given", () => {
|
||||
const style = new ParagraphStyle("myStyleId").underline("double", "005599");
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
underline: {
|
||||
type: UnderlineType.DOUBLE,
|
||||
color: "005599",
|
||||
},
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -443,7 +592,12 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#color", () => {
|
||||
const style = new ParagraphStyle("myStyleId").color("123456");
|
||||
const style = new ParagraphStyle({
|
||||
id: "myStyleId",
|
||||
run: {
|
||||
color: "123456",
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -456,7 +610,7 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#link", () => {
|
||||
const style = new ParagraphStyle("myStyleId").link("MyLink");
|
||||
const style = new ParagraphStyle({ id: "myStyleId", link: "MyLink" });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:link": { _attr: { "w:val": "MyLink" } } }],
|
||||
@ -464,7 +618,7 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#semiHidden", () => {
|
||||
const style = new ParagraphStyle("myStyleId").semiHidden();
|
||||
const style = new ParagraphStyle({ id: "myStyleId", semiHidden: true });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:semiHidden": EMPTY_OBJECT }],
|
||||
@ -472,7 +626,7 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#uiPriority", () => {
|
||||
const style = new ParagraphStyle("myStyleId").uiPriority("99");
|
||||
const style = new ParagraphStyle({ id: "myStyleId", uiPriority: 99 });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [
|
||||
@ -480,7 +634,7 @@ describe("ParagraphStyle", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -489,7 +643,7 @@ describe("ParagraphStyle", () => {
|
||||
});
|
||||
|
||||
it("#unhideWhenUsed", () => {
|
||||
const style = new ParagraphStyle("myStyleId").unhideWhenUsed();
|
||||
const style = new ParagraphStyle({ id: "myStyleId", unhideWhenUsed: true });
|
||||
const tree = new Formatter().format(style);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:style": [{ _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, { "w:unhideWhenUsed": EMPTY_OBJECT }],
|
||||
|
@ -5,188 +5,207 @@ import {
|
||||
ISpacingProperties,
|
||||
KeepLines,
|
||||
KeepNext,
|
||||
LeftTabStop,
|
||||
MaxRightTabStop,
|
||||
OutlineLevel,
|
||||
ParagraphProperties,
|
||||
Spacing,
|
||||
ThematicBreak,
|
||||
} from "file/paragraph";
|
||||
import { IIndentAttributesProperties, TabStop, TabStopType } from "file/paragraph/formatting";
|
||||
import * as formatting from "file/paragraph/run/formatting";
|
||||
import { RunProperties } from "file/paragraph/run/properties";
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
import { UnderlineType } from "file/paragraph/run/underline";
|
||||
import { ShadingType } from "file/table";
|
||||
|
||||
import { BasedOn, Link, Next, QuickFormat, SemiHidden, UiPriority, UnhideWhenUsed } from "./components";
|
||||
import { Style } from "./style";
|
||||
|
||||
export interface IBaseParagraphStyleOptions {
|
||||
readonly basedOn?: string;
|
||||
readonly next?: string;
|
||||
readonly quickFormat?: boolean;
|
||||
readonly link?: string;
|
||||
readonly semiHidden?: boolean;
|
||||
readonly uiPriority?: number;
|
||||
readonly unhideWhenUsed?: boolean;
|
||||
readonly run?: {
|
||||
readonly size?: number;
|
||||
readonly bold?: boolean;
|
||||
readonly italics?: boolean;
|
||||
readonly smallCaps?: boolean;
|
||||
readonly allCaps?: boolean;
|
||||
readonly strike?: boolean;
|
||||
readonly doubleStrike?: boolean;
|
||||
readonly subScript?: boolean;
|
||||
readonly superScript?: boolean;
|
||||
readonly underline?: {
|
||||
readonly type?: UnderlineType;
|
||||
readonly color?: string;
|
||||
};
|
||||
readonly color?: string;
|
||||
readonly font?: string;
|
||||
readonly characterSpacing?: number;
|
||||
readonly highlight?: string;
|
||||
readonly shadow?: {
|
||||
readonly type: ShadingType;
|
||||
readonly fill: string;
|
||||
readonly color: string;
|
||||
};
|
||||
};
|
||||
readonly paragraph?: {
|
||||
readonly alignment?: AlignmentType;
|
||||
readonly thematicBreak?: boolean;
|
||||
readonly rightTabStop?: number;
|
||||
readonly leftTabStop?: number;
|
||||
readonly indent?: IIndentAttributesProperties;
|
||||
readonly spacing?: ISpacingProperties;
|
||||
readonly keepNext?: boolean;
|
||||
readonly keepLines?: boolean;
|
||||
readonly outlineLevel?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IParagraphStyleOptions extends IBaseParagraphStyleOptions {
|
||||
readonly id: string;
|
||||
readonly name?: string;
|
||||
}
|
||||
export class ParagraphStyle extends Style {
|
||||
private readonly paragraphProperties: ParagraphProperties;
|
||||
private readonly runProperties: RunProperties;
|
||||
|
||||
constructor(styleId: string, name?: string) {
|
||||
super({ type: "paragraph", styleId: styleId }, name);
|
||||
constructor(options: IParagraphStyleOptions) {
|
||||
super({ type: "paragraph", styleId: options.id }, options.name);
|
||||
this.paragraphProperties = new ParagraphProperties({});
|
||||
this.runProperties = new RunProperties();
|
||||
this.root.push(this.paragraphProperties);
|
||||
this.root.push(this.runProperties);
|
||||
}
|
||||
|
||||
public addParagraphProperty(property: XmlComponent): ParagraphStyle {
|
||||
this.paragraphProperties.push(property);
|
||||
return this;
|
||||
}
|
||||
if (options.basedOn) {
|
||||
this.root.push(new BasedOn(options.basedOn));
|
||||
}
|
||||
|
||||
public outlineLevel(level: number): ParagraphStyle {
|
||||
this.paragraphProperties.push(new OutlineLevel(level));
|
||||
return this;
|
||||
}
|
||||
if (options.next) {
|
||||
this.root.push(new Next(options.next));
|
||||
}
|
||||
|
||||
public addRunProperty(property: XmlComponent): ParagraphStyle {
|
||||
this.runProperties.push(property);
|
||||
return this;
|
||||
}
|
||||
if (options.quickFormat) {
|
||||
this.root.push(new QuickFormat());
|
||||
}
|
||||
|
||||
public basedOn(parentId: string): ParagraphStyle {
|
||||
this.root.push(new BasedOn(parentId));
|
||||
return this;
|
||||
}
|
||||
if (options.link) {
|
||||
this.root.push(new Link(options.link));
|
||||
}
|
||||
|
||||
public quickFormat(): ParagraphStyle {
|
||||
this.root.push(new QuickFormat());
|
||||
return this;
|
||||
}
|
||||
if (options.semiHidden) {
|
||||
this.root.push(new SemiHidden());
|
||||
}
|
||||
|
||||
public next(nextId: string): ParagraphStyle {
|
||||
this.root.push(new Next(nextId));
|
||||
return this;
|
||||
}
|
||||
if (options.uiPriority) {
|
||||
this.root.push(new UiPriority(options.uiPriority));
|
||||
}
|
||||
|
||||
// ---------- Run formatting ---------------------- //
|
||||
if (options.unhideWhenUsed) {
|
||||
this.root.push(new UnhideWhenUsed());
|
||||
}
|
||||
|
||||
public size(twips: number): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.Size(twips)).addRunProperty(new formatting.SizeComplexScript(twips));
|
||||
}
|
||||
if (options.run) {
|
||||
if (options.run.size) {
|
||||
this.runProperties.push(new formatting.Size(options.run.size));
|
||||
this.runProperties.push(new formatting.SizeComplexScript(options.run.size));
|
||||
}
|
||||
|
||||
public bold(): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.Bold());
|
||||
}
|
||||
if (options.run.bold) {
|
||||
this.runProperties.push(new formatting.Bold());
|
||||
}
|
||||
|
||||
public italics(): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.Italics());
|
||||
}
|
||||
if (options.run.italics) {
|
||||
this.runProperties.push(new formatting.Italics());
|
||||
}
|
||||
|
||||
public smallCaps(): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.SmallCaps());
|
||||
}
|
||||
if (options.run.smallCaps) {
|
||||
this.runProperties.push(new formatting.SmallCaps());
|
||||
}
|
||||
|
||||
public allCaps(): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.Caps());
|
||||
}
|
||||
if (options.run.allCaps) {
|
||||
this.runProperties.push(new formatting.Caps());
|
||||
}
|
||||
|
||||
public strike(): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.Strike());
|
||||
}
|
||||
if (options.run.strike) {
|
||||
this.runProperties.push(new formatting.Strike());
|
||||
}
|
||||
|
||||
public doubleStrike(): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.DoubleStrike());
|
||||
}
|
||||
if (options.run.doubleStrike) {
|
||||
this.runProperties.push(new formatting.DoubleStrike());
|
||||
}
|
||||
|
||||
public subScript(): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.SubScript());
|
||||
}
|
||||
if (options.run.subScript) {
|
||||
this.runProperties.push(new formatting.SubScript());
|
||||
}
|
||||
|
||||
public superScript(): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.SuperScript());
|
||||
}
|
||||
if (options.run.superScript) {
|
||||
this.runProperties.push(new formatting.SuperScript());
|
||||
}
|
||||
|
||||
public underline(underlineType?: string, color?: string): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.Underline(underlineType, color));
|
||||
}
|
||||
if (options.run.underline) {
|
||||
this.runProperties.push(new formatting.Underline(options.run.underline.type, options.run.underline.color));
|
||||
}
|
||||
|
||||
public color(color: string): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.Color(color));
|
||||
}
|
||||
if (options.run.color) {
|
||||
this.runProperties.push(new formatting.Color(options.run.color));
|
||||
}
|
||||
|
||||
public font(fontName: string): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.RunFonts(fontName));
|
||||
}
|
||||
if (options.run.font) {
|
||||
this.runProperties.push(new formatting.RunFonts(options.run.font));
|
||||
}
|
||||
|
||||
public characterSpacing(value: number): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.CharacterSpacing(value));
|
||||
}
|
||||
if (options.run.characterSpacing) {
|
||||
this.runProperties.push(new formatting.CharacterSpacing(options.run.characterSpacing));
|
||||
}
|
||||
|
||||
public highlight(color: string): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.Highlight(color));
|
||||
}
|
||||
if (options.run.highlight) {
|
||||
this.runProperties.push(new formatting.Highlight(options.run.highlight));
|
||||
}
|
||||
|
||||
public shadow(value: string, fill: string, color: string): ParagraphStyle {
|
||||
return this.addRunProperty(new formatting.Shading(value, fill, color));
|
||||
}
|
||||
if (options.run.shadow) {
|
||||
this.runProperties.push(new formatting.Shading(options.run.shadow.type, options.run.shadow.fill, options.run.shadow.color));
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------- Paragraph formatting ------------------------ //
|
||||
if (options.paragraph) {
|
||||
if (options.paragraph.alignment) {
|
||||
this.paragraphProperties.push(new Alignment(options.paragraph.alignment));
|
||||
}
|
||||
|
||||
public center(): ParagraphStyle {
|
||||
return this.addParagraphProperty(new Alignment(AlignmentType.CENTER));
|
||||
}
|
||||
if (options.paragraph.thematicBreak) {
|
||||
this.paragraphProperties.push(new ThematicBreak());
|
||||
}
|
||||
|
||||
public left(): ParagraphStyle {
|
||||
return this.addParagraphProperty(new Alignment(AlignmentType.LEFT));
|
||||
}
|
||||
if (options.paragraph.rightTabStop) {
|
||||
this.paragraphProperties.push(new TabStop(TabStopType.RIGHT, options.paragraph.rightTabStop));
|
||||
}
|
||||
|
||||
public right(): ParagraphStyle {
|
||||
return this.addParagraphProperty(new Alignment(AlignmentType.RIGHT));
|
||||
}
|
||||
if (options.paragraph.leftTabStop) {
|
||||
this.paragraphProperties.push(new TabStop(TabStopType.LEFT, options.paragraph.leftTabStop));
|
||||
}
|
||||
|
||||
public justified(): ParagraphStyle {
|
||||
return this.addParagraphProperty(new Alignment(AlignmentType.BOTH));
|
||||
}
|
||||
if (options.paragraph.indent) {
|
||||
this.paragraphProperties.push(new Indent(options.paragraph.indent));
|
||||
}
|
||||
|
||||
public thematicBreak(): ParagraphStyle {
|
||||
return this.addParagraphProperty(new ThematicBreak());
|
||||
}
|
||||
if (options.paragraph.spacing) {
|
||||
this.paragraphProperties.push(new Spacing(options.paragraph.spacing));
|
||||
}
|
||||
|
||||
public maxRightTabStop(): ParagraphStyle {
|
||||
return this.addParagraphProperty(new MaxRightTabStop());
|
||||
}
|
||||
if (options.paragraph.keepNext) {
|
||||
this.paragraphProperties.push(new KeepNext());
|
||||
}
|
||||
|
||||
public leftTabStop(position: number): ParagraphStyle {
|
||||
return this.addParagraphProperty(new LeftTabStop(position));
|
||||
}
|
||||
if (options.paragraph.keepLines) {
|
||||
this.paragraphProperties.push(new KeepLines());
|
||||
}
|
||||
|
||||
public indent(attrs: object): ParagraphStyle {
|
||||
return this.addParagraphProperty(new Indent(attrs));
|
||||
}
|
||||
|
||||
public spacing(params: ISpacingProperties): ParagraphStyle {
|
||||
return this.addParagraphProperty(new Spacing(params));
|
||||
}
|
||||
|
||||
public keepNext(): ParagraphStyle {
|
||||
return this.addParagraphProperty(new KeepNext());
|
||||
}
|
||||
|
||||
public keepLines(): ParagraphStyle {
|
||||
return this.addParagraphProperty(new KeepLines());
|
||||
}
|
||||
|
||||
/*-------------- Style Properties -----------------*/
|
||||
|
||||
public link(link: string): ParagraphStyle {
|
||||
this.root.push(new Link(link));
|
||||
return this;
|
||||
}
|
||||
|
||||
public semiHidden(): ParagraphStyle {
|
||||
this.root.push(new SemiHidden());
|
||||
return this;
|
||||
}
|
||||
|
||||
public uiPriority(priority: string): ParagraphStyle {
|
||||
this.root.push(new UiPriority(priority));
|
||||
return this;
|
||||
}
|
||||
|
||||
public unhideWhenUsed(): ParagraphStyle {
|
||||
this.root.push(new UnhideWhenUsed());
|
||||
return this;
|
||||
if (options.paragraph.outlineLevel) {
|
||||
this.paragraphProperties.push(new OutlineLevel(options.paragraph.outlineLevel));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,4 @@ export class Style extends XmlComponent {
|
||||
this.root.push(new Name(name));
|
||||
}
|
||||
}
|
||||
|
||||
public push(styleSegment: XmlComponent): void {
|
||||
this.root.push(styleSegment);
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,20 @@
|
||||
import { assert, expect } from "chai";
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { CharacterStyle, ParagraphStyle } from "./style";
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
|
||||
import { Styles } from "./styles";
|
||||
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
|
||||
describe("Styles", () => {
|
||||
let styles: Styles;
|
||||
|
||||
beforeEach(() => {
|
||||
styles = new Styles();
|
||||
});
|
||||
|
||||
describe("#constructor()", () => {
|
||||
it("should create styles with correct rootKey", () => {
|
||||
const newJson = JSON.parse(JSON.stringify(styles));
|
||||
assert.equal(newJson.rootKey, "w:styles");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createParagraphStyle", () => {
|
||||
it("should create a new paragraph style and push it onto this collection", () => {
|
||||
const pStyle = styles.createParagraphStyle("pStyleId");
|
||||
expect(pStyle).to.instanceOf(ParagraphStyle);
|
||||
const styles = new Styles({
|
||||
paragraphStyles: [
|
||||
{
|
||||
id: "pStyleId",
|
||||
},
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr);
|
||||
expect(tree).to.deep.equal([
|
||||
{
|
||||
@ -35,8 +24,14 @@ describe("Styles", () => {
|
||||
});
|
||||
|
||||
it("should set the paragraph name if given", () => {
|
||||
const pStyle = styles.createParagraphStyle("pStyleId", "Paragraph Style");
|
||||
expect(pStyle).to.instanceOf(ParagraphStyle);
|
||||
const styles = new Styles({
|
||||
paragraphStyles: [
|
||||
{
|
||||
id: "pStyleId",
|
||||
name: "Paragraph Style",
|
||||
},
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr);
|
||||
expect(tree).to.deep.equal([
|
||||
{
|
||||
@ -51,8 +46,13 @@ describe("Styles", () => {
|
||||
|
||||
describe("#createCharacterStyle", () => {
|
||||
it("should create a new character style and push it onto this collection", () => {
|
||||
const cStyle = styles.createCharacterStyle("pStyleId");
|
||||
expect(cStyle).to.instanceOf(CharacterStyle);
|
||||
const styles = new Styles({
|
||||
characterStyles: [
|
||||
{
|
||||
id: "pStyleId",
|
||||
},
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr);
|
||||
expect(tree).to.deep.equal([
|
||||
{
|
||||
@ -61,7 +61,7 @@ describe("Styles", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -74,8 +74,14 @@ describe("Styles", () => {
|
||||
});
|
||||
|
||||
it("should set the character name if given", () => {
|
||||
const cStyle = styles.createCharacterStyle("pStyleId", "Character Style");
|
||||
expect(cStyle).to.instanceOf(CharacterStyle);
|
||||
const styles = new Styles({
|
||||
characterStyles: [
|
||||
{
|
||||
id: "pStyleId",
|
||||
name: "Character Style",
|
||||
},
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr);
|
||||
expect(tree).to.deep.equal([
|
||||
{
|
||||
@ -85,7 +91,7 @@ describe("Styles", () => {
|
||||
{
|
||||
"w:uiPriority": {
|
||||
_attr: {
|
||||
"w:val": "99",
|
||||
"w:val": 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,36 +1,41 @@
|
||||
import { BaseXmlComponent, XmlComponent } from "file/xml-components";
|
||||
import { DocumentDefaults } from "./defaults";
|
||||
import { BaseXmlComponent, ImportedXmlComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
import { CharacterStyle, ParagraphStyle } from "./style";
|
||||
import { ICharacterStyleOptions } from "./style/character-style";
|
||||
import { IParagraphStyleOptions } from "./style/paragraph-style";
|
||||
export * from "./border";
|
||||
|
||||
export interface IStylesOptions {
|
||||
readonly initialStyles?: BaseXmlComponent;
|
||||
readonly paragraphStyles?: IParagraphStyleOptions[];
|
||||
readonly characterStyles?: ICharacterStyleOptions[];
|
||||
readonly importedStyles?: Array<XmlComponent | ParagraphStyle | CharacterStyle | ImportedXmlComponent>;
|
||||
}
|
||||
|
||||
export class Styles extends XmlComponent {
|
||||
constructor(initialStyles?: BaseXmlComponent) {
|
||||
constructor(options: IStylesOptions) {
|
||||
super("w:styles");
|
||||
if (initialStyles) {
|
||||
this.root.push(initialStyles);
|
||||
|
||||
if (options.initialStyles) {
|
||||
this.root.push(options.initialStyles);
|
||||
}
|
||||
|
||||
if (options.importedStyles) {
|
||||
for (const style of options.importedStyles) {
|
||||
this.root.push(style);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.paragraphStyles) {
|
||||
for (const style of options.paragraphStyles) {
|
||||
this.root.push(new ParagraphStyle(style));
|
||||
}
|
||||
}
|
||||
|
||||
if (options.characterStyles) {
|
||||
for (const style of options.characterStyles) {
|
||||
this.root.push(new CharacterStyle(style));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public push(style: XmlComponent): Styles {
|
||||
this.root.push(style);
|
||||
return this;
|
||||
}
|
||||
|
||||
public createDocumentDefaults(): DocumentDefaults {
|
||||
const defaults = new DocumentDefaults();
|
||||
this.push(defaults);
|
||||
return defaults;
|
||||
}
|
||||
|
||||
public createParagraphStyle(styleId: string, name?: string): ParagraphStyle {
|
||||
const paragraphStyle = new ParagraphStyle(styleId, name);
|
||||
this.push(paragraphStyle);
|
||||
return paragraphStyle;
|
||||
}
|
||||
|
||||
public createCharacterStyle(styleId: string, name?: string): CharacterStyle {
|
||||
const characterStyle = new CharacterStyle(styleId, name);
|
||||
this.push(characterStyle);
|
||||
return characterStyle;
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ export class FieldInstruction extends XmlComponent {
|
||||
instruction = `${instruction} \\s "${this.properties.seqFieldIdentifierForPrefix}"`;
|
||||
}
|
||||
if (this.properties.stylesWithLevels && this.properties.stylesWithLevels.length) {
|
||||
const styles = this.properties.stylesWithLevels.map((sl) => `${sl.styleName};${sl.level}`).join(";");
|
||||
const styles = this.properties.stylesWithLevels.map((sl) => `${sl.styleName},${sl.level}`).join(",");
|
||||
instruction = `${instruction} \\t "${styles}"`;
|
||||
}
|
||||
if (this.properties.useAppliedParagraphOutlineLevel) {
|
||||
|
@ -146,7 +146,7 @@ const COMPLETE_TOC = {
|
||||
"xml:space": "preserve",
|
||||
},
|
||||
},
|
||||
'TOC \\a "A" \\b "B" \\c "C" \\d "D" \\f "F" \\h \\l "L" \\n "N" \\o "O" \\p "P" \\s "S" \\t "SL;1;SL;2" \\u \\w \\x \\z',
|
||||
'TOC \\a "A" \\b "B" \\c "C" \\d "D" \\f "F" \\h \\l "L" \\n "N" \\o "O" \\p "P" \\s "S" \\t "SL,1,SL,2" \\u \\w \\x \\z',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -16,18 +16,24 @@ export class TableOfContents extends XmlComponent {
|
||||
|
||||
const content = new StructuredDocumentTagContent();
|
||||
|
||||
const beginParagraph = new Paragraph({});
|
||||
const beginRun = new Run({});
|
||||
beginRun.addChildElement(new Begin(true));
|
||||
beginRun.addChildElement(new FieldInstruction(properties));
|
||||
beginRun.addChildElement(new Separate());
|
||||
beginParagraph.addRun(beginRun);
|
||||
const beginParagraph = new Paragraph({
|
||||
children: [
|
||||
new Run({
|
||||
children: [new Begin(true), new FieldInstruction(properties), new Separate()],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
content.addChildElement(beginParagraph);
|
||||
|
||||
const endParagraph = new Paragraph({});
|
||||
const endRun = new Run({});
|
||||
endRun.addChildElement(new End());
|
||||
endParagraph.addRun(endRun);
|
||||
const endParagraph = new Paragraph({
|
||||
children: [
|
||||
new Run({
|
||||
children: [new End()],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
content.addChildElement(endParagraph);
|
||||
|
||||
this.root.push(content);
|
||||
|
@ -2,9 +2,11 @@
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
export class TableGrid extends XmlComponent {
|
||||
constructor(cols: number[]) {
|
||||
constructor(widths: number[]) {
|
||||
super("w:tblGrid");
|
||||
cols.forEach((col) => this.root.push(new GridCol(col)));
|
||||
for (const width of widths) {
|
||||
this.root.push(new GridCol(width));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ export class GridSpan extends XmlComponent {
|
||||
/**
|
||||
* Vertical merge types.
|
||||
*/
|
||||
export enum VMergeType {
|
||||
export enum VerticalMergeType {
|
||||
/**
|
||||
* Cell that is merged with upper one.
|
||||
*/
|
||||
@ -114,19 +114,19 @@ export enum VMergeType {
|
||||
RESTART = "restart",
|
||||
}
|
||||
|
||||
class VMergeAttributes extends XmlAttributeComponent<{ readonly val: VMergeType }> {
|
||||
class VerticalMergeAttributes extends XmlAttributeComponent<{ readonly val: VerticalMergeType }> {
|
||||
protected readonly xmlKeys = { val: "w:val" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertical merge element. Should be used in a table cell.
|
||||
*/
|
||||
export class VMerge extends XmlComponent {
|
||||
constructor(value: VMergeType) {
|
||||
export class VerticalMerge extends XmlComponent {
|
||||
constructor(value: VerticalMergeType) {
|
||||
super("w:vMerge");
|
||||
|
||||
this.root.push(
|
||||
new VMergeAttributes({
|
||||
new VerticalMergeAttributes({
|
||||
val: value,
|
||||
}),
|
||||
);
|
||||
|
@ -3,7 +3,7 @@ import { expect } from "chai";
|
||||
import { Formatter } from "export/formatter";
|
||||
import { BorderStyle } from "file/styles";
|
||||
|
||||
import { VerticalAlign, VMergeType, WidthType } from "./table-cell-components";
|
||||
import { VerticalAlign, VerticalMergeType, WidthType } from "./table-cell-components";
|
||||
import { TableCellProperties } from "./table-cell-properties";
|
||||
|
||||
describe("TableCellProperties", () => {
|
||||
@ -30,7 +30,7 @@ describe("TableCellProperties", () => {
|
||||
describe("#addVerticalMerge", () => {
|
||||
it("adds vertical merge", () => {
|
||||
const properties = new TableCellProperties();
|
||||
properties.addVerticalMerge(VMergeType.CONTINUE);
|
||||
properties.addVerticalMerge(VerticalMergeType.CONTINUE);
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(tree).to.deep.equal({ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] });
|
||||
});
|
||||
@ -73,6 +73,54 @@ describe("TableCellProperties", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addMargins", () => {
|
||||
it("sets shading", () => {
|
||||
const properties = new TableCellProperties();
|
||||
properties.addMargins({});
|
||||
const tree = new Formatter().format(properties);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tcPr": [
|
||||
{
|
||||
"w:tcMar": [
|
||||
{
|
||||
"w:top": {
|
||||
_attr: {
|
||||
"w:type": "dxa",
|
||||
"w:w": 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:bottom": {
|
||||
_attr: {
|
||||
"w:type": "dxa",
|
||||
"w:w": 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:end": {
|
||||
_attr: {
|
||||
"w:type": "dxa",
|
||||
"w:w": 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:start": {
|
||||
_attr: {
|
||||
"w:type": "dxa",
|
||||
"w:w": 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#Borders", () => {
|
||||
it("should return the TableCellBorders if Border has borders", () => {
|
||||
const properties = new TableCellProperties();
|
||||
|
@ -2,7 +2,16 @@ import { IgnoreIfEmptyXmlComponent } from "file/xml-components";
|
||||
|
||||
import { ITableShadingAttributesProperties, TableShading } from "../shading";
|
||||
import { ITableCellMarginOptions, TableCellMargin } from "./cell-margin/table-cell-margins";
|
||||
import { GridSpan, TableCellBorders, TableCellWidth, VAlign, VerticalAlign, VMerge, VMergeType, WidthType } from "./table-cell-components";
|
||||
import {
|
||||
GridSpan,
|
||||
TableCellBorders,
|
||||
TableCellWidth,
|
||||
VAlign,
|
||||
VerticalAlign,
|
||||
VerticalMerge,
|
||||
VerticalMergeType,
|
||||
WidthType,
|
||||
} from "./table-cell-components";
|
||||
|
||||
export class TableCellProperties extends IgnoreIfEmptyXmlComponent {
|
||||
private readonly cellBorder: TableCellBorders;
|
||||
@ -23,8 +32,8 @@ export class TableCellProperties extends IgnoreIfEmptyXmlComponent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public addVerticalMerge(type: VMergeType): TableCellProperties {
|
||||
this.root.push(new VMerge(type));
|
||||
public addVerticalMerge(type: VerticalMergeType): TableCellProperties {
|
||||
this.root.push(new VerticalMerge(type));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ import { expect } from "chai";
|
||||
import { Formatter } from "export/formatter";
|
||||
import { BorderStyle } from "file/styles";
|
||||
|
||||
import { TableCellBorders, TableCellWidth, WidthType } from "./table-cell-components";
|
||||
import { ShadingType } from "../shading";
|
||||
import { TableCell } from "./table-cell";
|
||||
import { TableCellBorders, TableCellWidth, VerticalAlign, VerticalMergeType, WidthType } from "./table-cell-components";
|
||||
|
||||
describe("TableCellBorders", () => {
|
||||
describe("#prepForXml", () => {
|
||||
@ -222,3 +224,359 @@ describe("TableCellWidth", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("TableCell", () => {
|
||||
describe("#constructor", () => {
|
||||
it("should create", () => {
|
||||
const cell = new TableCell({
|
||||
children: [],
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(cell);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tc": [
|
||||
{
|
||||
"w:p": {},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create with vertical align", () => {
|
||||
const cell = new TableCell({
|
||||
children: [],
|
||||
verticalAlign: VerticalAlign.CENTER,
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(cell);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tc": [
|
||||
{
|
||||
"w:tcPr": [
|
||||
{
|
||||
"w:vAlign": {
|
||||
_attr: {
|
||||
"w:val": "center",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:p": {},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create with vertical merge", () => {
|
||||
const cell = new TableCell({
|
||||
children: [],
|
||||
verticalMerge: VerticalMergeType.RESTART,
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(cell);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tc": [
|
||||
{
|
||||
"w:tcPr": [
|
||||
{
|
||||
"w:vMerge": {
|
||||
_attr: {
|
||||
"w:val": "restart",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:p": {},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create with margins", () => {
|
||||
const cell = new TableCell({
|
||||
children: [],
|
||||
margins: {
|
||||
top: 1,
|
||||
left: 1,
|
||||
bottom: 1,
|
||||
right: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(cell);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tc": [
|
||||
{
|
||||
"w:tcPr": [
|
||||
{
|
||||
"w:tcMar": [
|
||||
{
|
||||
"w:top": {
|
||||
_attr: {
|
||||
"w:type": "dxa",
|
||||
"w:w": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:bottom": {
|
||||
_attr: {
|
||||
"w:type": "dxa",
|
||||
"w:w": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:end": {
|
||||
_attr: {
|
||||
"w:type": "dxa",
|
||||
"w:w": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:start": {
|
||||
_attr: {
|
||||
"w:type": "dxa",
|
||||
"w:w": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:p": {},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create with shading", () => {
|
||||
const cell = new TableCell({
|
||||
children: [],
|
||||
shading: {
|
||||
fill: "red",
|
||||
color: "blue",
|
||||
val: ShadingType.PERCENT_10,
|
||||
},
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(cell);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tc": [
|
||||
{
|
||||
"w:tcPr": [
|
||||
{
|
||||
"w:shd": {
|
||||
_attr: {
|
||||
"w:color": "blue",
|
||||
"w:fill": "red",
|
||||
"w:val": "pct10",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:p": {},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create with width", () => {
|
||||
const cell = new TableCell({
|
||||
children: [],
|
||||
width: { size: 100, type: WidthType.DXA },
|
||||
});
|
||||
const tree = new Formatter().format(cell);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tc": [
|
||||
{
|
||||
"w:tcPr": [
|
||||
{
|
||||
"w:tcW": {
|
||||
_attr: {
|
||||
"w:type": "dxa",
|
||||
"w:w": 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:p": {},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create with column span", () => {
|
||||
const cell = new TableCell({
|
||||
children: [],
|
||||
columnSpan: 2,
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(cell);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tc": [
|
||||
{
|
||||
"w:tcPr": [
|
||||
{
|
||||
"w:gridSpan": {
|
||||
_attr: {
|
||||
"w:val": 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:p": {},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe("rowSpan", () => {
|
||||
it("should not create with row span if its less than 1", () => {
|
||||
const cell = new TableCell({
|
||||
children: [],
|
||||
rowSpan: 0,
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(cell);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tc": [
|
||||
{
|
||||
"w:p": {},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create with row span if its greater than 1", () => {
|
||||
const cell = new TableCell({
|
||||
children: [],
|
||||
rowSpan: 2,
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(cell);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tc": [
|
||||
{
|
||||
"w:tcPr": [
|
||||
{
|
||||
"w:vMerge": {
|
||||
_attr: {
|
||||
"w:val": "restart",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:p": {},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create with borders", () => {
|
||||
const cell = new TableCell({
|
||||
children: [],
|
||||
borders: {
|
||||
top: {
|
||||
style: BorderStyle.DASH_DOT_STROKED,
|
||||
size: 3,
|
||||
color: "red",
|
||||
},
|
||||
bottom: {
|
||||
style: BorderStyle.DOUBLE,
|
||||
size: 3,
|
||||
color: "blue",
|
||||
},
|
||||
left: {
|
||||
style: BorderStyle.DASH_DOT_STROKED,
|
||||
size: 3,
|
||||
color: "green",
|
||||
},
|
||||
right: {
|
||||
style: BorderStyle.DASH_DOT_STROKED,
|
||||
size: 3,
|
||||
color: "#ff8000",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const tree = new Formatter().format(cell);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tc": [
|
||||
{
|
||||
"w:tcPr": [
|
||||
{
|
||||
"w:tcBorders": [
|
||||
{
|
||||
"w:top": {
|
||||
_attr: {
|
||||
"w:color": "red",
|
||||
"w:sz": 3,
|
||||
"w:val": "dashDotStroked",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:bottom": {
|
||||
_attr: {
|
||||
"w:color": "blue",
|
||||
"w:sz": 3,
|
||||
"w:val": "double",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:left": {
|
||||
_attr: {
|
||||
"w:color": "green",
|
||||
"w:sz": 3,
|
||||
"w:val": "dashDotStroked",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:right": {
|
||||
_attr: {
|
||||
"w:color": "#ff8000",
|
||||
"w:sz": 3,
|
||||
"w:val": "dashDotStroked",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:p": {},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,77 +1,120 @@
|
||||
// http://officeopenxml.com/WPtableGrid.php
|
||||
import { Paragraph } from "file/paragraph";
|
||||
import { BorderStyle } from "file/styles";
|
||||
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
||||
|
||||
import { ITableShadingAttributesProperties } from "../shading";
|
||||
import { Table } from "../table";
|
||||
import { ITableCellMarginOptions } from "./cell-margin/table-cell-margins";
|
||||
import { TableCellBorders, VerticalAlign, VMergeType } from "./table-cell-components";
|
||||
import { VerticalAlign, VerticalMergeType, WidthType } from "./table-cell-components";
|
||||
import { TableCellProperties } from "./table-cell-properties";
|
||||
|
||||
export interface ITableCellOptions {
|
||||
readonly shading?: ITableShadingAttributesProperties;
|
||||
readonly margins?: ITableCellMarginOptions;
|
||||
readonly verticalAlign?: VerticalAlign;
|
||||
readonly verticalMerge?: VerticalMergeType;
|
||||
readonly width?: {
|
||||
readonly size: number | string;
|
||||
readonly type?: WidthType;
|
||||
};
|
||||
readonly columnSpan?: number;
|
||||
readonly rowSpan?: number;
|
||||
readonly borders?: {
|
||||
readonly top?: {
|
||||
readonly style: BorderStyle;
|
||||
readonly size: number;
|
||||
readonly color: string;
|
||||
};
|
||||
readonly bottom?: {
|
||||
readonly style: BorderStyle;
|
||||
readonly size: number;
|
||||
readonly color: string;
|
||||
};
|
||||
readonly left?: {
|
||||
readonly style: BorderStyle;
|
||||
readonly size: number;
|
||||
readonly color: string;
|
||||
};
|
||||
readonly right?: {
|
||||
readonly style: BorderStyle;
|
||||
readonly size: number;
|
||||
readonly color: string;
|
||||
};
|
||||
};
|
||||
readonly children: Array<Paragraph | Table>;
|
||||
}
|
||||
|
||||
export class TableCell extends XmlComponent {
|
||||
private readonly properties: TableCellProperties;
|
||||
|
||||
constructor() {
|
||||
constructor(readonly options: ITableCellOptions) {
|
||||
super("w:tc");
|
||||
|
||||
this.properties = new TableCellProperties();
|
||||
this.root.push(this.properties);
|
||||
}
|
||||
|
||||
public add(item: Paragraph | Table): TableCell {
|
||||
this.root.push(item);
|
||||
for (const child of options.children) {
|
||||
this.root.push(child);
|
||||
}
|
||||
|
||||
return this;
|
||||
if (options.verticalAlign) {
|
||||
this.properties.setVerticalAlign(options.verticalAlign);
|
||||
}
|
||||
|
||||
if (options.verticalMerge) {
|
||||
this.properties.addVerticalMerge(options.verticalMerge);
|
||||
}
|
||||
|
||||
if (options.margins) {
|
||||
this.properties.addMargins(options.margins);
|
||||
}
|
||||
|
||||
if (options.shading) {
|
||||
this.properties.setShading(options.shading);
|
||||
}
|
||||
|
||||
if (options.columnSpan) {
|
||||
this.properties.addGridSpan(options.columnSpan);
|
||||
}
|
||||
|
||||
if (options.rowSpan && options.rowSpan > 1) {
|
||||
this.properties.addVerticalMerge(VerticalMergeType.RESTART);
|
||||
}
|
||||
|
||||
if (options.width) {
|
||||
this.properties.setWidth(options.width.size, options.width.type);
|
||||
}
|
||||
|
||||
if (options.borders) {
|
||||
if (options.borders.top) {
|
||||
this.properties.Borders.addTopBorder(options.borders.top.style, options.borders.top.size, options.borders.top.color);
|
||||
}
|
||||
if (options.borders.bottom) {
|
||||
this.properties.Borders.addBottomBorder(
|
||||
options.borders.bottom.style,
|
||||
options.borders.bottom.size,
|
||||
options.borders.bottom.color,
|
||||
);
|
||||
}
|
||||
if (options.borders.left) {
|
||||
this.properties.Borders.addLeftBorder(options.borders.left.style, options.borders.left.size, options.borders.left.color);
|
||||
}
|
||||
if (options.borders.right) {
|
||||
this.properties.Borders.addRightBorder(
|
||||
options.borders.right.style,
|
||||
options.borders.right.size,
|
||||
options.borders.right.color,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public prepForXml(): IXmlableObject | undefined {
|
||||
// Cells must end with a paragraph
|
||||
if (!(this.root[this.root.length - 1] instanceof Paragraph)) {
|
||||
const para = new Paragraph({});
|
||||
this.add(para);
|
||||
this.root.push(new Paragraph({}));
|
||||
}
|
||||
return super.prepForXml();
|
||||
}
|
||||
|
||||
public setVerticalAlign(type: VerticalAlign): TableCell {
|
||||
this.properties.setVerticalAlign(type);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public addGridSpan(cellSpan: number): TableCell {
|
||||
this.properties.addGridSpan(cellSpan);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public addVerticalMerge(type: VMergeType): TableCell {
|
||||
this.properties.addVerticalMerge(type);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public setMargins(margins: ITableCellMarginOptions): TableCell {
|
||||
this.properties.addMargins(margins);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public setShading(attrs: ITableShadingAttributesProperties): TableCell {
|
||||
this.properties.setShading(attrs);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public get Borders(): TableCellBorders {
|
||||
return this.properties.Borders;
|
||||
}
|
||||
|
||||
public get Properties(): TableCellProperties {
|
||||
return this.properties;
|
||||
}
|
||||
}
|
||||
|
@ -1,56 +0,0 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { TableCell } from "./table-cell";
|
||||
import { TableColumn } from "./table-column";
|
||||
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
|
||||
describe("TableColumn", () => {
|
||||
let cells: TableCell[];
|
||||
beforeEach(() => {
|
||||
cells = [new TableCell(), new TableCell(), new TableCell()];
|
||||
});
|
||||
|
||||
describe("#getCell", () => {
|
||||
it("should get the correct cell", () => {
|
||||
const tableColumn = new TableColumn(cells);
|
||||
const cell = tableColumn.getCell(0);
|
||||
|
||||
expect(cell).to.deep.equal(cells[0]);
|
||||
|
||||
const cell2 = tableColumn.getCell(1);
|
||||
|
||||
expect(cell2).to.deep.equal(cells[1]);
|
||||
});
|
||||
|
||||
it("should throw an error if index is out of bounds", () => {
|
||||
const tableColumn = new TableColumn(cells);
|
||||
|
||||
expect(() => tableColumn.getCell(9)).to.throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe("#mergeCells", () => {
|
||||
it("should add vMerge to correct cells", () => {
|
||||
const tableColumn = new TableColumn(cells);
|
||||
tableColumn.mergeCells(0, 2);
|
||||
|
||||
const tree = new Formatter().format(cells[0]);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "restart" } } }] }, { "w:p": EMPTY_OBJECT }],
|
||||
});
|
||||
|
||||
const tree2 = new Formatter().format(cells[1]);
|
||||
expect(tree2).to.deep.equal({
|
||||
"w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }, { "w:p": EMPTY_OBJECT }],
|
||||
});
|
||||
|
||||
const tree3 = new Formatter().format(cells[2]);
|
||||
expect(tree3).to.deep.equal({
|
||||
"w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }, { "w:p": EMPTY_OBJECT }],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,25 +0,0 @@
|
||||
import { TableCell, VMergeType } from "./table-cell";
|
||||
|
||||
export class TableColumn {
|
||||
constructor(private readonly cells: TableCell[]) {}
|
||||
|
||||
public getCell(index: number): TableCell {
|
||||
const cell = this.cells[index];
|
||||
|
||||
if (!cell) {
|
||||
throw Error("Index out of bounds when trying to get cell on column");
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
public mergeCells(startIndex: number, endIndex: number): TableCell {
|
||||
this.cells[startIndex].addVerticalMerge(VMergeType.RESTART);
|
||||
|
||||
for (let i = startIndex + 1; i <= endIndex; i++) {
|
||||
this.cells[i].addVerticalMerge(VMergeType.CONTINUE);
|
||||
}
|
||||
|
||||
return this.cells[startIndex];
|
||||
}
|
||||
}
|
@ -14,38 +14,66 @@ describe("TableCellMargin", () => {
|
||||
});
|
||||
|
||||
describe("#addTopMargin", () => {
|
||||
it("adds a table cell top margin", () => {
|
||||
it("should add a table cell top margin", () => {
|
||||
const cellMargin = new TableCellMargin();
|
||||
cellMargin.addTopMargin(1234, WidthType.DXA);
|
||||
const tree = new Formatter().format(cellMargin);
|
||||
expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:top": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] });
|
||||
});
|
||||
|
||||
it("should add a table cell top margin using default width type", () => {
|
||||
const cellMargin = new TableCellMargin();
|
||||
cellMargin.addTopMargin(1234);
|
||||
const tree = new Formatter().format(cellMargin);
|
||||
expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:top": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] });
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addLeftMargin", () => {
|
||||
it("adds a table cell left margin", () => {
|
||||
it("should add a table cell left margin", () => {
|
||||
const cellMargin = new TableCellMargin();
|
||||
cellMargin.addLeftMargin(1234, WidthType.DXA);
|
||||
const tree = new Formatter().format(cellMargin);
|
||||
expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:left": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] });
|
||||
});
|
||||
|
||||
it("should add a table cell left margin using default width type", () => {
|
||||
const cellMargin = new TableCellMargin();
|
||||
cellMargin.addLeftMargin(1234);
|
||||
const tree = new Formatter().format(cellMargin);
|
||||
expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:left": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] });
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addBottomMargin", () => {
|
||||
it("adds a table cell bottom margin", () => {
|
||||
it("should add a table cell bottom margin", () => {
|
||||
const cellMargin = new TableCellMargin();
|
||||
cellMargin.addBottomMargin(1234, WidthType.DXA);
|
||||
const tree = new Formatter().format(cellMargin);
|
||||
expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:bottom": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] });
|
||||
});
|
||||
|
||||
it("should add a table cell bottom margin using default width type", () => {
|
||||
const cellMargin = new TableCellMargin();
|
||||
cellMargin.addBottomMargin(1234);
|
||||
const tree = new Formatter().format(cellMargin);
|
||||
expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:bottom": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] });
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addRightMargin", () => {
|
||||
it("adds a table cell right margin", () => {
|
||||
it("should add a table cell right margin", () => {
|
||||
const cellMargin = new TableCellMargin();
|
||||
cellMargin.addRightMargin(1234, WidthType.DXA);
|
||||
const tree = new Formatter().format(cellMargin);
|
||||
expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:right": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] });
|
||||
});
|
||||
|
||||
it("should add a table cell right margin using default width type", () => {
|
||||
const cellMargin = new TableCellMargin();
|
||||
cellMargin.addRightMargin(1234);
|
||||
const tree = new Formatter().format(cellMargin);
|
||||
expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:right": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2,6 +2,7 @@ import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { ShadingType } from "../shading";
|
||||
import { WidthType } from "../table-cell";
|
||||
import { TableLayoutType } from "./table-layout";
|
||||
import { TableProperties } from "./table-properties";
|
||||
@ -66,4 +67,29 @@ describe("TableProperties", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#setShading", () => {
|
||||
it("sets the shading of the table", () => {
|
||||
const tp = new TableProperties();
|
||||
tp.setShading({
|
||||
fill: "b79c2f",
|
||||
val: ShadingType.REVERSE_DIAGONAL_STRIPE,
|
||||
color: "auto",
|
||||
});
|
||||
const tree = new Formatter().format(tp);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tblPr": [
|
||||
{
|
||||
"w:shd": {
|
||||
_attr: {
|
||||
"w:color": "auto",
|
||||
"w:fill": "b79c2f",
|
||||
"w:val": "reverseDiagStripe",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2,6 +2,7 @@ import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { Paragraph } from "file/paragraph";
|
||||
import { HeightRule } from "file/table/table-row/table-row-height";
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
import { TableCell } from "../table-cell";
|
||||
@ -10,7 +11,9 @@ import { TableRow } from "./table-row";
|
||||
describe("TableRow", () => {
|
||||
describe("#constructor", () => {
|
||||
it("should create with no cells", () => {
|
||||
const tableRow = new TableRow([]);
|
||||
const tableRow = new TableRow({
|
||||
children: [],
|
||||
});
|
||||
const tree = new Formatter().format(tableRow);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tr": EMPTY_OBJECT,
|
||||
@ -18,7 +21,13 @@ describe("TableRow", () => {
|
||||
});
|
||||
|
||||
it("should create with one cell", () => {
|
||||
const tableRow = new TableRow([new TableCell()]);
|
||||
const tableRow = new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [],
|
||||
}),
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(tableRow);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tr": [
|
||||
@ -32,46 +41,61 @@ describe("TableRow", () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getCell", () => {
|
||||
it("should get the cell", () => {
|
||||
const cell = new TableCell();
|
||||
const tableRow = new TableRow([cell]);
|
||||
|
||||
expect(tableRow.getCell(0)).to.equal(cell);
|
||||
it("should create with cant split", () => {
|
||||
const tableRow = new TableRow({
|
||||
children: [],
|
||||
cantSplit: true,
|
||||
});
|
||||
const tree = new Formatter().format(tableRow);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tr": [
|
||||
{
|
||||
"w:trPr": [
|
||||
{
|
||||
"w:cantSplit": {
|
||||
_attr: {
|
||||
"w:val": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw an error if index is out of bounds", () => {
|
||||
const cell = new TableCell();
|
||||
const tableRow = new TableRow([cell]);
|
||||
|
||||
expect(() => tableRow.getCell(1)).to.throw();
|
||||
it("should create with table header", () => {
|
||||
const tableRow = new TableRow({
|
||||
children: [],
|
||||
tableHeader: true,
|
||||
});
|
||||
const tree = new Formatter().format(tableRow);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tr": [
|
||||
{
|
||||
"w:trPr": [
|
||||
{
|
||||
"w:tblHeader": {
|
||||
_attr: {
|
||||
"w:val": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addGridSpan", () => {
|
||||
it("should merge the cell", () => {
|
||||
const tableRow = new TableRow([new TableCell(), new TableCell()]);
|
||||
|
||||
tableRow.addGridSpan(0, 2);
|
||||
expect(() => tableRow.getCell(1)).to.throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe("#mergeCells", () => {
|
||||
it("should merge the cell", () => {
|
||||
const tableRow = new TableRow([new TableCell(), new TableCell()]);
|
||||
|
||||
tableRow.mergeCells(0, 1);
|
||||
expect(() => tableRow.getCell(1)).to.throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe("#setHeight", () => {
|
||||
it("should set row height", () => {
|
||||
const tableRow = new TableRow([]);
|
||||
tableRow.setHeight(100, HeightRule.EXACT);
|
||||
const tableRow = new TableRow({
|
||||
children: [],
|
||||
height: {
|
||||
height: 100,
|
||||
rule: HeightRule.EXACT,
|
||||
},
|
||||
});
|
||||
const tree = new Formatter().format(tableRow);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tr": [
|
||||
@ -91,4 +115,71 @@ describe("TableRow", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addCellToIndex", () => {
|
||||
it("should add cell to correct index with no initial properties", () => {
|
||||
const tableRow = new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("test")],
|
||||
}),
|
||||
],
|
||||
tableHeader: true,
|
||||
});
|
||||
|
||||
tableRow.addCellToIndex(
|
||||
new TableCell({
|
||||
children: [],
|
||||
}),
|
||||
0,
|
||||
);
|
||||
|
||||
const tree = new Formatter().format(tableRow);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tr": [
|
||||
{
|
||||
"w:trPr": [
|
||||
{
|
||||
"w:tblHeader": {
|
||||
_attr: {
|
||||
"w:val": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:tc": [
|
||||
{
|
||||
"w:p": {},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:tc": [
|
||||
{
|
||||
"w:p": [
|
||||
{
|
||||
"w:r": [
|
||||
{
|
||||
"w:t": [
|
||||
{
|
||||
_attr: {
|
||||
"xml:space": "preserve",
|
||||
},
|
||||
},
|
||||
"test",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -3,56 +3,51 @@ import { XmlComponent } from "file/xml-components";
|
||||
import { TableCell } from "../table-cell";
|
||||
import { TableRowProperties } from "./table-row-properties";
|
||||
|
||||
export interface ITableRowOptions {
|
||||
readonly cantSplit?: boolean;
|
||||
readonly tableHeader?: boolean;
|
||||
readonly height?: {
|
||||
readonly height: number;
|
||||
readonly rule: HeightRule;
|
||||
};
|
||||
readonly children: TableCell[];
|
||||
}
|
||||
|
||||
export class TableRow extends XmlComponent {
|
||||
private readonly properties: TableRowProperties;
|
||||
|
||||
constructor(private readonly cells: TableCell[]) {
|
||||
constructor(private readonly options: ITableRowOptions) {
|
||||
super("w:tr");
|
||||
this.properties = new TableRowProperties();
|
||||
this.root.push(this.properties);
|
||||
cells.forEach((c) => this.root.push(c));
|
||||
}
|
||||
|
||||
public getCell(index: number): TableCell {
|
||||
const cell = this.cells[index];
|
||||
|
||||
if (!cell) {
|
||||
throw Error("Index out of bounds when trying to get cell on row");
|
||||
for (const child of options.children) {
|
||||
this.root.push(child);
|
||||
}
|
||||
|
||||
return cell;
|
||||
if (options.cantSplit) {
|
||||
this.properties.setCantSplit();
|
||||
}
|
||||
|
||||
if (options.tableHeader) {
|
||||
this.properties.setTableHeader();
|
||||
}
|
||||
|
||||
if (options.height) {
|
||||
this.properties.setHeight(options.height.height, options.height.rule);
|
||||
}
|
||||
}
|
||||
|
||||
public addGridSpan(index: number, cellSpan: number): TableCell {
|
||||
const remainCell = this.cells[index];
|
||||
remainCell.addGridSpan(cellSpan);
|
||||
this.cells.splice(index + 1, cellSpan - 1);
|
||||
this.root.splice(index + 2, cellSpan - 1);
|
||||
|
||||
return remainCell;
|
||||
public get CellCount(): number {
|
||||
return this.options.children.length;
|
||||
}
|
||||
|
||||
public mergeCells(startIndex: number, endIndex: number): TableCell {
|
||||
const cellSpan = endIndex - startIndex + 1;
|
||||
|
||||
return this.addGridSpan(startIndex, cellSpan);
|
||||
public get Children(): TableCell[] {
|
||||
return this.options.children;
|
||||
}
|
||||
|
||||
public setCantSplit(): TableRow {
|
||||
this.properties.setCantSplit();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public setTableHeader(): TableRow {
|
||||
this.properties.setTableHeader();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public setHeight(height: number, rule: HeightRule): TableRow {
|
||||
this.properties.setHeight(height, rule);
|
||||
|
||||
return this;
|
||||
public addCellToIndex(cell: TableCell, index: number): void {
|
||||
// Offset because properties is also in root.
|
||||
this.root.splice(index + 1, 0, cell);
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,9 @@ import { Table } from "./table";
|
||||
// import { WidthType } from "./table-cell";
|
||||
import { RelativeHorizontalPosition, RelativeVerticalPosition, TableAnchorType } from "./table-properties";
|
||||
|
||||
import { EMPTY_OBJECT } from "file/xml-components";
|
||||
import { TableCell, WidthType } from "./table-cell";
|
||||
import { TableLayoutType } from "./table-properties/table-layout";
|
||||
import { TableRow } from "./table-row";
|
||||
|
||||
const DEFAULT_TABLE_PROPERTIES = {
|
||||
"w:tblCellMar": [
|
||||
@ -118,11 +119,62 @@ describe("Table", () => {
|
||||
describe("#constructor", () => {
|
||||
it("creates a table with the correct number of rows and columns", () => {
|
||||
const table = new Table({
|
||||
rows: 3,
|
||||
columns: 2,
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(table);
|
||||
const cell = { "w:tc": [{ "w:p": EMPTY_OBJECT }] };
|
||||
const cell = {
|
||||
"w:tc": [
|
||||
{
|
||||
"w:p": [
|
||||
{
|
||||
"w:r": [
|
||||
{
|
||||
"w:t": [
|
||||
{
|
||||
_attr: {
|
||||
"xml:space": "preserve",
|
||||
},
|
||||
},
|
||||
"hello",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tbl": [
|
||||
{ "w:tblPr": [DEFAULT_TABLE_PROPERTIES, BORDERS, WIDTHS] },
|
||||
@ -138,8 +190,15 @@ describe("Table", () => {
|
||||
|
||||
it("sets the table to fixed width layout", () => {
|
||||
const table = new Table({
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
layout: TableLayoutType.FIXED,
|
||||
});
|
||||
const tree = new Formatter().format(table);
|
||||
@ -151,130 +210,60 @@ describe("Table", () => {
|
||||
"w:tblPr": [DEFAULT_TABLE_PROPERTIES, BORDERS, WIDTHS, { "w:tblLayout": { _attr: { "w:type": "fixed" } } }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getRow and Row#getCell", () => {
|
||||
const table = new Table({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
});
|
||||
|
||||
it("should return the correct row", () => {
|
||||
table
|
||||
.getRow(0)
|
||||
.getCell(0)
|
||||
.add(new Paragraph("A1"));
|
||||
table
|
||||
.getRow(0)
|
||||
.getCell(1)
|
||||
.add(new Paragraph("B1"));
|
||||
table
|
||||
.getRow(1)
|
||||
.getCell(0)
|
||||
.add(new Paragraph("A2"));
|
||||
table
|
||||
.getRow(1)
|
||||
.getCell(1)
|
||||
.add(new Paragraph("B2"));
|
||||
const tree = new Formatter().format(table);
|
||||
const cell = (c) => ({
|
||||
"w:tc": [
|
||||
{
|
||||
"w:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, c] }] }],
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tbl": [
|
||||
{ "w:tblPr": [DEFAULT_TABLE_PROPERTIES, BORDERS, WIDTHS] },
|
||||
{
|
||||
"w:tblGrid": [{ "w:gridCol": { _attr: { "w:w": 100 } } }, { "w:gridCol": { _attr: { "w:w": 100 } } }],
|
||||
},
|
||||
{ "w:tr": [cell("A1"), cell("B1")] },
|
||||
{ "w:tr": [cell("A2"), cell("B2")] },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("throws an exception if index is out of bounds", () => {
|
||||
expect(() => table.getCell(9, 9)).to.throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getColumn", () => {
|
||||
const table = new Table({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
});
|
||||
|
||||
it("should get correct cell", () => {
|
||||
const column = table.getColumn(0);
|
||||
|
||||
expect(column.getCell(0)).to.equal(table.getCell(0, 0));
|
||||
expect(column.getCell(1)).to.equal(table.getCell(1, 0));
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getCell", () => {
|
||||
it("should returns the correct cell", () => {
|
||||
it("should set the table to provided width", () => {
|
||||
const table = new Table({
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
});
|
||||
table.getCell(0, 0).add(new Paragraph("A1"));
|
||||
table.getCell(0, 1).add(new Paragraph("B1"));
|
||||
table.getCell(1, 0).add(new Paragraph("A2"));
|
||||
table.getCell(1, 1).add(new Paragraph("B2"));
|
||||
const tree = new Formatter().format(table);
|
||||
const cell = (c) => ({
|
||||
"w:tc": [
|
||||
{
|
||||
"w:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, c] }] }],
|
||||
},
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
width: {
|
||||
size: 100,
|
||||
type: WidthType.PERCENTAGE,
|
||||
},
|
||||
layout: TableLayoutType.FIXED,
|
||||
});
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tbl": [
|
||||
{ "w:tblPr": [DEFAULT_TABLE_PROPERTIES, BORDERS, WIDTHS] },
|
||||
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": [
|
||||
DEFAULT_TABLE_PROPERTIES,
|
||||
BORDERS,
|
||||
{
|
||||
"w:tblGrid": [{ "w:gridCol": { _attr: { "w:w": 100 } } }, { "w:gridCol": { _attr: { "w:w": 100 } } }],
|
||||
"w:tblW": {
|
||||
_attr: {
|
||||
"w:type": "pct",
|
||||
"w:w": "100%",
|
||||
},
|
||||
},
|
||||
},
|
||||
{ "w:tr": [cell("A1"), cell("B1")] },
|
||||
{ "w:tr": [cell("A2"), cell("B2")] },
|
||||
{ "w:tblLayout": { _attr: { "w:type": "fixed" } } },
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// describe("#setWidth", () => {
|
||||
// it("should set the preferred width on the table", () => {
|
||||
// const table = new Table({rows: 1,columns: 1,}).setWidth(1000, WidthType.PERCENTAGE);
|
||||
// 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": [DEFAULT_TABLE_PROPERTIES, { "w:tblW": { _attr: { "w:type": "pct", "w:w": "1000%" } } }],
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("sets the preferred width on the table with a default of AUTO", () => {
|
||||
// const table = new Table({rows: 1,columns: 1,}).setWidth(1000);
|
||||
// const tree = new Formatter().format(table);
|
||||
|
||||
// expect(tree["w:tbl"][0]).to.deep.equal({
|
||||
// "w:tblPr": [DEFAULT_TABLE_PROPERTIES, { "w:tblW": { _attr: { "w:type": "auto", "w:w": 1000 } } }],
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
describe("Cell", () => {
|
||||
describe("#prepForXml", () => {
|
||||
it("inserts a paragraph at the end of the cell if it is empty", () => {
|
||||
const table = new Table({
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(table);
|
||||
expect(tree)
|
||||
@ -282,72 +271,119 @@ describe("Table", () => {
|
||||
.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:p": EMPTY_OBJECT }],
|
||||
});
|
||||
});
|
||||
|
||||
it("inserts a paragraph at the end of the cell even if it has a child table", () => {
|
||||
const parentTable = new Table({
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
});
|
||||
parentTable.getCell(0, 0).add(
|
||||
new Table({
|
||||
rows: 1,
|
||||
columns: 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": EMPTY_OBJECT,
|
||||
});
|
||||
});
|
||||
|
||||
it("does not insert a paragraph if it already ends with one", () => {
|
||||
const parentTable = new Table({
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
});
|
||||
parentTable.getCell(0, 0).add(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:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "Hello"] }] }],
|
||||
"w:p": [
|
||||
{
|
||||
"w:r": [
|
||||
{
|
||||
"w:t": [
|
||||
{
|
||||
_attr: {
|
||||
"xml:space": "preserve",
|
||||
},
|
||||
},
|
||||
"hello",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
// it("inserts a paragraph at the end of the cell even if it has a child table", () => {
|
||||
// const table = new Table({
|
||||
// rows: [
|
||||
// new TableRow({
|
||||
// children: [
|
||||
// new TableCell({
|
||||
// children: [new Paragraph("hello")],
|
||||
// }),
|
||||
// ],
|
||||
// }),
|
||||
// ],
|
||||
// });
|
||||
// table.getCell(0, 0).add(
|
||||
// new Table({
|
||||
// rows: [
|
||||
// new TableRow({
|
||||
// children: [
|
||||
// new TableCell({
|
||||
// children: [new Paragraph("hello")],
|
||||
// }),
|
||||
// ],
|
||||
// }),
|
||||
// ],
|
||||
// }),
|
||||
// );
|
||||
// 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);
|
||||
// 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": EMPTY_OBJECT,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("does not insert a paragraph if it already ends with one", () => {
|
||||
// const table = new Table({
|
||||
// rows: [
|
||||
// new TableRow({
|
||||
// children: [
|
||||
// new TableCell({
|
||||
// children: [new Paragraph("hello")],
|
||||
// }),
|
||||
// ],
|
||||
// }),
|
||||
// ],
|
||||
// });
|
||||
// table.getCell(0, 0).add(new Paragraph("Hello"));
|
||||
// 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:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "Hello"] }] }],
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
// });
|
||||
});
|
||||
});
|
||||
|
||||
describe("#float", () => {
|
||||
it("sets the table float properties", () => {
|
||||
const table = new Table({
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
children: [new Paragraph("hello")],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
float: {
|
||||
horizontalAnchor: TableAnchorType.MARGIN,
|
||||
verticalAnchor: TableAnchorType.PAGE,
|
||||
|
@ -1,12 +1,11 @@
|
||||
// http://officeopenxml.com/WPtableGrid.php
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import { TableGrid } from "./grid";
|
||||
import { TableCell, WidthType } from "./table-cell";
|
||||
import { TableColumn } from "./table-column";
|
||||
import { TableCell, VerticalMergeType, WidthType } from "./table-cell";
|
||||
import { ITableFloatOptions, TableProperties } from "./table-properties";
|
||||
import { TableLayoutType } from "./table-properties/table-layout";
|
||||
import { TableRow } from "./table-row";
|
||||
|
||||
/*
|
||||
0-width columns don't get rendered correctly, so we need
|
||||
to give them some value. A reasonable default would be
|
||||
@ -18,10 +17,11 @@ import { TableRow } from "./table-row";
|
||||
algorithm will expand columns to fit its content
|
||||
*/
|
||||
export interface ITableOptions {
|
||||
readonly rows: number;
|
||||
readonly columns: number;
|
||||
readonly width?: number;
|
||||
readonly widthUnitType?: WidthType;
|
||||
readonly rows: TableRow[];
|
||||
readonly width?: {
|
||||
readonly size: number;
|
||||
readonly type?: WidthType;
|
||||
};
|
||||
readonly columnWidths?: number[];
|
||||
readonly margins?: {
|
||||
readonly marginUnitType?: WidthType;
|
||||
@ -36,14 +36,11 @@ export interface ITableOptions {
|
||||
|
||||
export class Table extends XmlComponent {
|
||||
private readonly properties: TableProperties;
|
||||
private readonly rows: TableRow[];
|
||||
|
||||
constructor({
|
||||
rows,
|
||||
columns,
|
||||
width = 100,
|
||||
widthUnitType = WidthType.AUTO,
|
||||
columnWidths = Array<number>(columns).fill(100),
|
||||
width,
|
||||
columnWidths = Array<number>(Math.max(...rows.map((row) => row.CellCount))).fill(100),
|
||||
margins: { marginUnitType, top, bottom, right, left } = { marginUnitType: WidthType.AUTO, top: 0, bottom: 0, right: 0, left: 0 },
|
||||
float,
|
||||
layout,
|
||||
@ -52,26 +49,45 @@ export class Table extends XmlComponent {
|
||||
this.properties = new TableProperties();
|
||||
this.root.push(this.properties);
|
||||
this.properties.setBorder();
|
||||
this.properties.setWidth(width, widthUnitType);
|
||||
|
||||
if (width) {
|
||||
this.properties.setWidth(width.size, width.type);
|
||||
} else {
|
||||
this.properties.setWidth(100);
|
||||
}
|
||||
|
||||
this.properties.CellMargin.addBottomMargin(bottom || 0, marginUnitType);
|
||||
this.properties.CellMargin.addTopMargin(top || 0, marginUnitType);
|
||||
this.properties.CellMargin.addLeftMargin(left || 0, marginUnitType);
|
||||
this.properties.CellMargin.addRightMargin(right || 0, marginUnitType);
|
||||
const grid = new TableGrid(columnWidths);
|
||||
|
||||
this.root.push(grid);
|
||||
this.root.push(new TableGrid(columnWidths));
|
||||
|
||||
this.rows = Array(rows)
|
||||
.fill(0)
|
||||
.map(() => {
|
||||
const cells = Array(columns)
|
||||
.fill(0)
|
||||
.map(() => new TableCell());
|
||||
const row = new TableRow(cells);
|
||||
return row;
|
||||
for (const row of rows) {
|
||||
this.root.push(row);
|
||||
}
|
||||
|
||||
for (const row of rows) {
|
||||
row.Children.forEach((cell, cellIndex) => {
|
||||
const column = rows.map((r) => r.Children[cellIndex]);
|
||||
// Row Span has to be added in this method and not the constructor because it needs to know information about the column which happens after Table Cell construction
|
||||
// Row Span of 1 will crash word as it will add RESTART and not a corresponding CONTINUE
|
||||
if (cell.options.rowSpan && cell.options.rowSpan > 1) {
|
||||
const thisCellsColumnIndex = column.indexOf(cell);
|
||||
const endColumnIndex = thisCellsColumnIndex + (cell.options.rowSpan - 1);
|
||||
|
||||
for (let i = thisCellsColumnIndex + 1; i <= endColumnIndex; i++) {
|
||||
rows[i].addCellToIndex(
|
||||
new TableCell({
|
||||
children: [],
|
||||
verticalMerge: VerticalMergeType.CONTINUE,
|
||||
}),
|
||||
i,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.rows.forEach((x) => this.root.push(x));
|
||||
}
|
||||
|
||||
if (float) {
|
||||
this.properties.setTableFloatProperties(float);
|
||||
@ -81,24 +97,4 @@ export class Table extends XmlComponent {
|
||||
this.properties.setLayout(layout);
|
||||
}
|
||||
}
|
||||
|
||||
public getRow(index: number): TableRow {
|
||||
const row = this.rows[index];
|
||||
|
||||
if (!row) {
|
||||
throw Error("Index out of bounds when trying to get row on table");
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
public getColumn(index: number): TableColumn {
|
||||
// This is a convinence method for people who like to work with columns
|
||||
const cells = this.rows.map((row) => row.getCell(index));
|
||||
return new TableColumn(cells);
|
||||
}
|
||||
|
||||
public getCell(row: number, col: number): TableCell {
|
||||
return this.getRow(row).getCell(col);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user