diff --git a/ts/numbering/indent.ts b/ts/docx/paragraph/indent.ts similarity index 79% rename from ts/numbering/indent.ts rename to ts/docx/paragraph/indent.ts index b5bdac02b0..c98b3570a1 100644 --- a/ts/numbering/indent.ts +++ b/ts/docx/paragraph/indent.ts @@ -1,4 +1,4 @@ -import { XmlAttributeComponent, XmlComponent } from "../docx/xml-components"; +import { XmlAttributeComponent, XmlComponent } from "../xml-components"; interface IndentAttributesProperties { left: number; @@ -17,7 +17,7 @@ class IndentAttributes extends XmlAttributeComponent { export class Indent extends XmlComponent { - constructor(left: number, hanging: number) { + constructor(left: number, hanging?: number) { super("w:ind"); this.root.push(new IndentAttributes({ left: left, diff --git a/ts/docx/paragraph/index.ts b/ts/docx/paragraph/index.ts index 368cdc1f5a..c2b3c5ef37 100644 --- a/ts/docx/paragraph/index.ts +++ b/ts/docx/paragraph/index.ts @@ -1,9 +1,12 @@ import { Num } from "../../numbering/num"; import { TextRun } from "../run/text-run"; import { Attributes, XmlComponent } from "../xml-components"; + import { ThematicBreak } from "./border"; +import { Indent } from "./indent"; import { PageBreak } from "./page-break"; import { ParagraphProperties } from "./properties"; +import { ISpacingProperties, Spacing } from "./spacing"; import { Style } from "./style"; import { LeftTabStop, MaxRightTabStop } from "./tab-stop"; import { NumberProperties } from "./unordered-list"; @@ -116,4 +119,19 @@ export class Paragraph extends XmlComponent { this.properties.push(new NumberProperties(numbering.id, indentLevel)); return this; } + + public style(styleId: string): Paragraph { + this.properties.push(new Style(styleId)); + return this; + } + + public indent(start: number, hanging?: number): Paragraph { + this.properties.push(new Indent(start, hanging)); + return this; + } + + public spacing(params: ISpacingProperties): Paragraph { + this.properties.push(new Spacing(params)); + return this; + }; } diff --git a/ts/docx/paragraph/spacing.ts b/ts/docx/paragraph/spacing.ts new file mode 100644 index 0000000000..527d62c6d3 --- /dev/null +++ b/ts/docx/paragraph/spacing.ts @@ -0,0 +1,24 @@ +import { XmlAttributeComponent, XmlComponent } from "../xml-components"; + +export interface ISpacingProperties { + after?: number; + before?: number; + line?: number; +} + +class SpacingAttributes extends XmlAttributeComponent { + constructor(properties: ISpacingProperties) { + super({ + after: "w:after", + before: "w:before", + line: "w:line", + }, properties); + } +} + +export class Spacing extends XmlComponent { + constructor(opts: ISpacingProperties) { + super("w:spacing"); + this.root.push(new SpacingAttributes(opts)); + } +} diff --git a/ts/docx/run/formatting.ts b/ts/docx/run/formatting.ts index 2471b359cd..ccad4cc349 100644 --- a/ts/docx/run/formatting.ts +++ b/ts/docx/run/formatting.ts @@ -1,4 +1,5 @@ import { Attributes, XmlComponent } from "../xml-components"; +export { Underline } from './underline'; export class Bold extends XmlComponent { diff --git a/ts/docx/run/underline.ts b/ts/docx/run/underline.ts index cac9538a6b..1a6fda2334 100644 --- a/ts/docx/run/underline.ts +++ b/ts/docx/run/underline.ts @@ -13,8 +13,8 @@ abstract class BaseUnderline extends XmlComponent { export class Underline extends BaseUnderline { - constructor() { - super(""); + constructor(underlineType: string = "single", color?: string) { + super(underlineType, color); } } diff --git a/ts/index.ts b/ts/index.ts index ee4bf88498..5177ddce40 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -1,3 +1,4 @@ export * from "./docx"; export * from "./export"; export { Numbering } from "./numbering"; +export { Styles } from "./styles"; diff --git a/ts/numbering/index.ts b/ts/numbering/index.ts index 39bfda0867..828933ae90 100644 --- a/ts/numbering/index.ts +++ b/ts/numbering/index.ts @@ -1,9 +1,9 @@ import * as _ from "lodash"; import { DocumentAttributes } from "../docx/document/document-attributes"; +import { Indent } from "../docx/paragraph/indent"; import { RunFonts } from "../docx/run/run-fonts"; import { MultiPropertyXmlComponent } from "../docx/xml-components"; import { AbstractNumbering } from "./abstract-numbering"; -import { Indent } from "./indent"; import { Level } from "./level"; import { Num } from "./num"; diff --git a/ts/styles/defaults/index.ts b/ts/styles/defaults/index.ts index 7b74e1d311..65737fcc52 100644 --- a/ts/styles/defaults/index.ts +++ b/ts/styles/defaults/index.ts @@ -1,6 +1,6 @@ -import {XmlComponent} from "../../docx/xml-components"; -import {ParagraphPropertiesDefaults} from "./paragraph-properties"; -import {RunPropertiesDefaults} from "./run-properties"; +import { XmlComponent } from "../../docx/xml-components"; +import { ParagraphPropertiesDefaults } from "./paragraph-properties"; +import { RunPropertiesDefaults } from "./run-properties"; export class DocumentDefaults extends XmlComponent { @@ -15,10 +15,10 @@ export class DocumentDefaults extends XmlComponent { this.root.push(this.paragraphPropertiesDefaults); } - clearVariables(): void { + public clearVariables(): void { this.runPropertiesDefaults.clearVariables(); this.paragraphPropertiesDefaults.clearVariables(); delete this.runPropertiesDefaults; delete this.paragraphPropertiesDefaults; } -} \ No newline at end of file +} diff --git a/ts/styles/defaults/paragraph-properties.ts b/ts/styles/defaults/paragraph-properties.ts index 43d1d4c4cf..bac85aa796 100644 --- a/ts/styles/defaults/paragraph-properties.ts +++ b/ts/styles/defaults/paragraph-properties.ts @@ -1,5 +1,5 @@ -import {XmlComponent} from "../../docx/xml-components"; -import {ParagraphProperties} from "../../docx/paragraph/properties"; +import { ParagraphProperties } from "../../docx/paragraph/properties"; +import { XmlComponent } from "../../docx/xml-components"; export class ParagraphPropertiesDefaults extends XmlComponent { @@ -7,4 +7,4 @@ export class ParagraphPropertiesDefaults extends XmlComponent { super("w:pPrDefault"); this.root.push(new ParagraphProperties()); } -} \ No newline at end of file +} diff --git a/ts/styles/defaults/run-properties.ts b/ts/styles/defaults/run-properties.ts index e3c14a20a1..388d7a2e88 100644 --- a/ts/styles/defaults/run-properties.ts +++ b/ts/styles/defaults/run-properties.ts @@ -1,5 +1,5 @@ -import {XmlComponent} from "../../docx/xml-components"; -import {RunProperties} from "../../docx/run/properties"; +import { RunProperties } from "../../docx/run/properties"; +import { XmlComponent } from "../../docx/xml-components"; export class RunPropertiesDefaults extends XmlComponent { @@ -7,4 +7,4 @@ export class RunPropertiesDefaults extends XmlComponent { super("w:rPrDefault"); this.root.push(new RunProperties()); } -} \ No newline at end of file +} diff --git a/ts/styles/factory.ts b/ts/styles/factory.ts index 9907157dd3..d44e4c3b09 100644 --- a/ts/styles/factory.ts +++ b/ts/styles/factory.ts @@ -1,56 +1,60 @@ -import {Styles} from "./"; -import {DocumentDefaults} from "./defaults"; -import {ParagraphPropertiesDefaults} from "./defaults/paragraph-properties"; -import {RunPropertiesDefaults} from "./defaults/run-properties"; -import {Heading1Style, Heading2Style, TitleStyle, Heading3Style, Heading4Style, Heading5Style, Heading6Style, ListParagraph} from "./style"; -// import {StyleAttributes} from "./style/attributes"; -import {ParagraphProperties} from "../docx/paragraph/properties"; -import {RunProperties} from "../docx/run/properties"; -import {Color, Size, Italics} from "../docx/run/formatting"; +import { ParagraphProperties } from "../docx/paragraph/properties"; +import { Color, Italics, Size } from "../docx/run/formatting"; +import { RunProperties } from "../docx/run/properties"; + +import { Styles } from "./"; +import { DocumentDefaults } from "./defaults"; +import { ParagraphPropertiesDefaults } from "./defaults/paragraph-properties"; +import { RunPropertiesDefaults } from "./defaults/run-properties"; +import { + Heading1Style, Heading2Style, Heading3Style, Heading4Style, Heading5Style, Heading6Style, + ListParagraph, TitleStyle, +} from "./style"; +// import { StyleAttributes } from "./style/attributes"; export class DefaultStylesFactory { - newInstance(): Styles { - let styles = new Styles(); + public newInstance(): Styles { + const styles = new Styles(); styles.push(new DocumentDefaults()); - let titleStyle = new TitleStyle(); + const titleStyle = new TitleStyle(); titleStyle.addRunProperty(new Size(56)); styles.push(titleStyle); - let heading1Style = new Heading1Style(); + const heading1Style = new Heading1Style(); heading1Style.addRunProperty(new Color("2E74B5")); heading1Style.addRunProperty(new Size(32)); styles.push(heading1Style); - let heading2Style = new Heading2Style(); + const heading2Style = new Heading2Style(); heading2Style.addRunProperty(new Color("2E74B5")); heading2Style.addRunProperty(new Size(26)); styles.push(heading2Style); - let heading3Style = new Heading3Style(); + const heading3Style = new Heading3Style(); heading3Style.addRunProperty(new Color("1F4D78")); heading3Style.addRunProperty(new Size(24)); styles.push(heading3Style); - let heading4Style = new Heading4Style(); + const heading4Style = new Heading4Style(); heading4Style.addRunProperty(new Color("2E74B5")); heading4Style.addRunProperty(new Italics()); styles.push(heading4Style); - let heading5Style = new Heading5Style(); + const heading5Style = new Heading5Style(); heading5Style.addRunProperty(new Color("2E74B5")); styles.push(heading5Style); - let heading6Style = new Heading6Style(); + const heading6Style = new Heading6Style(); heading6Style.addRunProperty(new Color("1F4D78")); styles.push(heading6Style); - let listParagraph = new ListParagraph(); + const listParagraph = new ListParagraph(); // listParagraph.addParagraphProperty(); styles.push(listParagraph); // console.log(JSON.stringify(styles, null, " ")); return styles; } -} \ No newline at end of file +} diff --git a/ts/styles/index.ts b/ts/styles/index.ts index ebf66dd56b..519ad925e2 100644 --- a/ts/styles/index.ts +++ b/ts/styles/index.ts @@ -1,9 +1,10 @@ -import {XmlComponent} from "../docx/xml-components"; -import {DocumentAttributes} from "../docx/document/document-attributes"; -import {DocumentDefaults} from "./defaults"; -import {LatentStyles} from "./latent-styles"; -import {LatentStyleException} from "./latent-styles/exceptions"; -import {LatentStyleExceptionAttributes} from "./latent-styles/exceptions/attributes"; +import { DocumentAttributes } from "../docx/document/document-attributes"; +import { XmlComponent } from "../docx/xml-components"; +import { DocumentDefaults } from "./defaults"; +import { LatentStyles } from "./latent-styles"; +import { LatentStyleException } from "./latent-styles/exceptions"; +import { LatentStyleExceptionAttributes } from "./latent-styles/exceptions/attributes"; +import { ParagraphStyle } from "./style"; export class Styles extends XmlComponent { @@ -15,7 +16,7 @@ export class Styles extends XmlComponent { w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main", w14: "http://schemas.microsoft.com/office/word/2010/wordml", w15: "http://schemas.microsoft.com/office/word/2012/wordml", - Ignorable: "w14 w15" + Ignorable: "w14 w15", })); // let latentStyles = new LatentStyles(); // latentStyles.push(new LatentStyleException(new LatentStyleExceptionAttributes({ @@ -24,13 +25,20 @@ export class Styles extends XmlComponent { // this.root.push(latentStyles); } - push(style: XmlComponent): void { + public push(style: XmlComponent): Styles { this.root.push(style); + return this; } - clearVariables() { - this.root.forEach(element => { + public clearVariables(): void { + this.root.forEach((element) => { element.clearVariables(); }); } -} \ No newline at end of file + + public createParagraphStyle(styleId: string, name?: string): ParagraphStyle { + const para = new ParagraphStyle(styleId, name); + this.push(para); + return para; + } +} diff --git a/ts/styles/latent-styles/exceptions/attributes.ts b/ts/styles/latent-styles/exceptions/attributes.ts index 4c2eaa2786..359852bceb 100644 --- a/ts/styles/latent-styles/exceptions/attributes.ts +++ b/ts/styles/latent-styles/exceptions/attributes.ts @@ -1,6 +1,6 @@ import {XmlComponent} from "../../../docx/xml-components"; -interface LatentStyleExceptionAttributesProperties { +interface ILatentStyleExceptionAttributesProperties { name?: string; uiPriority?: string; qFormat?: string; @@ -9,23 +9,23 @@ interface LatentStyleExceptionAttributesProperties { } export class LatentStyleExceptionAttributes extends XmlComponent { - private _attr: Object; + private _attr: ILatentStyleExceptionAttributesProperties; - xmlKeys = { + private xmlKeys = { name: "w:name", uiPriority: "w:uiPriority", qFormat: "w:qFormat", semiHidden: "w:semiHidden", - unhideWhenUsed: "w:unhideWhenUsed" + unhideWhenUsed: "w:unhideWhenUsed", }; - constructor(properties?: LatentStyleExceptionAttributesProperties) { + constructor(properties?: ILatentStyleExceptionAttributesProperties) { super("_attr"); this._attr = properties; if (!properties) { this._attr = {}; } - this._attr["xmlKeys"] = this.xmlKeys; + //this._attr.xmlKeys = this.xmlKeys; } -} \ No newline at end of file +} diff --git a/ts/styles/latent-styles/exceptions/index.ts b/ts/styles/latent-styles/exceptions/index.ts index 685440b3ee..227ed93318 100644 --- a/ts/styles/latent-styles/exceptions/index.ts +++ b/ts/styles/latent-styles/exceptions/index.ts @@ -1,5 +1,5 @@ -import {XmlComponent} from "../../../docx/xml-components"; -import {LatentStyleExceptionAttributes} from "./attributes"; +import { XmlComponent } from "../../../docx/xml-components"; +import { LatentStyleExceptionAttributes } from "./attributes"; export class LatentStyleException extends XmlComponent { @@ -7,4 +7,4 @@ export class LatentStyleException extends XmlComponent { super("w:lsdException"); this.root.push(attributes); } -} \ No newline at end of file +} diff --git a/ts/styles/latent-styles/index.ts b/ts/styles/latent-styles/index.ts index fe75c11e37..583ab1e0e0 100644 --- a/ts/styles/latent-styles/index.ts +++ b/ts/styles/latent-styles/index.ts @@ -1,5 +1,5 @@ -import {XmlComponent} from "../../docx/xml-components"; -import {LatentStyleException} from "./exceptions"; +import { XmlComponent } from "../../docx/xml-components"; +import { LatentStyleException } from "./exceptions"; export class LatentStyles extends XmlComponent { @@ -7,7 +7,7 @@ export class LatentStyles extends XmlComponent { super("w:latentStyles"); } - push(latentException: LatentStyleException): void { + public push(latentException: LatentStyleException): void { this.root.push(latentException); } -} \ No newline at end of file +} diff --git a/ts/styles/style/attributes.ts b/ts/styles/style/attributes.ts deleted file mode 100644 index a227979cb4..0000000000 --- a/ts/styles/style/attributes.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {XmlComponent} from "../../docx/xml-components"; -import {XmlAttributeComponent} from "../../docx/xml-components"; - -interface StyleAttributesProperties { - type?: string; - styleId?: string; - default?: string; - customStyle?: string; - val?: string; -} - -export class StyleAttributes extends XmlAttributeComponent { - private _attr: Object; - - constructor(properties: StyleAttributesProperties) { - super({ - type: "w:type", - styleId: "w:styleId", - default: "w:default", - customStyle: "w:customStyle", - val: "w:val" - }, properties); - } -} \ No newline at end of file diff --git a/ts/styles/style/components.ts b/ts/styles/style/components.ts index f7853169d1..596a112852 100644 --- a/ts/styles/style/components.ts +++ b/ts/styles/style/components.ts @@ -1,13 +1,22 @@ -import {XmlComponent} from "../../docx/xml-components"; -import {StyleAttributes} from "./attributes"; +import { XmlAttributeComponent, XmlComponent } from "../../docx/xml-components"; + +interface IComponentAttributes { + val: string; +} + +class ComponentAttributes extends XmlAttributeComponent { + private _attr: IComponentAttributes; + + constructor(properties: IComponentAttributes) { + super({val: "w:val"}, properties); + } +} export class Name extends XmlComponent { constructor(value: string) { super("w:name"); - this.root.push(new StyleAttributes({ - val: value - })); + this.root.push(new ComponentAttributes({val: value})); } } @@ -15,9 +24,7 @@ export class BasedOn extends XmlComponent { constructor(value: string) { super("w:basedOn"); - this.root.push(new StyleAttributes({ - val: value - })); + this.root.push(new ComponentAttributes({val: value})); } } @@ -25,10 +32,7 @@ export class Next extends XmlComponent { constructor(value: string) { super("w:next"); - this.root.push(new StyleAttributes({ - styleId: "1", - val: value - })); + this.root.push(new ComponentAttributes({val: value})); } } @@ -36,9 +40,7 @@ export class Link extends XmlComponent { constructor(value: string) { super("w:link"); - this.root.push(new StyleAttributes({ - val: value - })); + this.root.push(new ComponentAttributes({val: value})); } } @@ -46,9 +48,8 @@ export class UiPriority extends XmlComponent { constructor(value: string) { super("w:uiPriority"); - this.root.push(new StyleAttributes({ - val: value - })); + // TODO: this value should be a ST_DecimalNumber + this.root.push(new ComponentAttributes({val: value})); } } @@ -73,4 +74,4 @@ export class RsId extends XmlComponent { export class SemiHidden extends XmlComponent { -} \ No newline at end of file +} diff --git a/ts/styles/style/index.ts b/ts/styles/style/index.ts index 412667ea81..840e55b317 100644 --- a/ts/styles/style/index.ts +++ b/ts/styles/style/index.ts @@ -1,17 +1,43 @@ -import {XmlComponent} from "../../docx/xml-components"; -import {StyleAttributes} from "./attributes"; -import {ParagraphProperties} from "../../docx/paragraph/properties"; -import {RunProperties} from "../../docx/run/properties"; -import {Name, BasedOn, Next, QuickFormat} from "./components"; +import { Indent } from "../../docx/paragraph/indent"; +import { ParagraphProperties } from "../../docx/paragraph/properties"; +import { ISpacingProperties, Spacing } from "../../docx/paragraph/spacing"; +import * as formatting from "../../docx/run/formatting"; +import { RunProperties } from "../../docx/run/properties"; +import { XmlAttributeComponent, XmlComponent } from "../../docx/xml-components"; + +import { BasedOn, Name, Next, QuickFormat } from "./components"; + +export interface IStyleAttributes { + type?: string; + styleId?: string; + default?: boolean; + customStyle?: string; +} + +class StyleAttributes extends XmlAttributeComponent { + private _attr: IStyleAttributes; + + constructor(properties: IStyleAttributes) { + super({ + type: "w:type", + styleId: "w:styleId", + default: "w:default", + customStyle: "w:customStyle", + }, properties); + } +} export class Style extends XmlComponent { - constructor(attributes: StyleAttributes) { + constructor(attributes: IStyleAttributes, name?: string) { super("w:style"); - this.root.push(attributes); + this.root.push(new StyleAttributes(attributes)); + if (name) { + this.root.push(new Name(name)); + } } - push(styleSegment: XmlComponent): void { + public push(styleSegment: XmlComponent): void { this.root.push(styleSegment); } } @@ -21,43 +47,87 @@ export class ParagraphStyle extends Style { private paragraphProperties: ParagraphProperties; private runProperties: RunProperties; - constructor(styleId: string) { - - let attributes = new StyleAttributes({ - type: "paragraph", - styleId: styleId - }); - super(attributes); + constructor(styleId: string, name?: string) { + super({type: "paragraph", styleId: styleId}, name); this.paragraphProperties = new ParagraphProperties(); this.runProperties = new RunProperties(); this.root.push(this.paragraphProperties); this.root.push(this.runProperties); } - clearVariables(): void { + public clearVariables(): void { this.paragraphProperties.clearVariables(); this.runProperties.clearVariables(); delete this.paragraphProperties; delete this.runProperties; } - addParagraphProperty(property: XmlComponent): void { + public addParagraphProperty(property: XmlComponent): void { this.paragraphProperties.push(property); } - addRunProperty(property: XmlComponent): void { + public addRunProperty(property: XmlComponent): void { this.runProperties.push(property); } + + public basedOn(parentId: string): ParagraphStyle { + this.root.push(new BasedOn(parentId)); + return this; + } + + public quickFormat(): ParagraphStyle { + this.root.push(new QuickFormat()); + return this; + } + + public next(nextId: string): ParagraphStyle { + this.root.push(new Next(nextId)); + return this; + } + + public size(twips: number): ParagraphStyle { + this.addRunProperty(new formatting.Size(twips)); + return this; + } + + public bold(): ParagraphStyle { + this.addRunProperty(new formatting.Bold()); + return this; + } + + public italics(): ParagraphStyle { + this.addRunProperty(new formatting.Italics()); + return this; + } + + public underline(underlineType?: string, color?: string): ParagraphStyle { + this.addRunProperty(new formatting.Underline(underlineType, color)); + return this; + } + + public color(color: string): ParagraphStyle { + this.addRunProperty(new formatting.Color(color)); + return this; + } + + public indent(left: number, hanging?: number): ParagraphStyle { + this.addParagraphProperty(new Indent(left, hanging)); + return this; + } + + public spacing(params: ISpacingProperties): ParagraphStyle { + this.addParagraphProperty(new Spacing(params)); + return this; + }; } export class HeadingStyle extends ParagraphStyle { constructor(styleId: string, name: string) { - super(styleId); - this.root.push(new Name(name)); - this.root.push(new BasedOn("Normal")); - this.root.push(new Next("Normal")); - this.root.push(new QuickFormat()); + super(styleId, name); + this.basedOn("Normal"); + this.next("Normal"); + this.quickFormat(); } } @@ -118,4 +188,4 @@ export class ListParagraph extends ParagraphStyle { this.root.push(new BasedOn("Normal")); this.root.push(new QuickFormat()); } -} \ No newline at end of file +} diff --git a/ts/tests/docx/paragraph/paragraphTests.ts b/ts/tests/docx/paragraph/paragraphTests.ts index 81fb18564d..fbab274f26 100644 --- a/ts/tests/docx/paragraph/paragraphTests.ts +++ b/ts/tests/docx/paragraph/paragraphTests.ts @@ -1,10 +1,11 @@ +import { assert, expect } from "chai"; + import * as docx from "../../../docx"; import { Formatter } from "../../../export/formatter"; import { Numbering } from "../../../numbering"; -import { assert, expect } from "chai"; -function jsonify(obj: Object) { - let stringifiedJson = JSON.stringify(obj); +function jsonify(obj: object) { + const stringifiedJson = JSON.stringify(obj); return JSON.parse(stringifiedJson); } @@ -18,7 +19,7 @@ describe("Paragraph", () => { describe("#constructor()", () => { it("should create valid JSON", () => { - let stringifiedJson = JSON.stringify(paragraph); + const stringifiedJson = JSON.stringify(paragraph); let newJson; try { @@ -30,8 +31,8 @@ describe("Paragraph", () => { }); it("should create have valid properties", () => { - let stringifiedJson = JSON.stringify(paragraph); - let newJson = JSON.parse(stringifiedJson); + const stringifiedJson = JSON.stringify(paragraph); + const newJson = JSON.parse(stringifiedJson); assert.equal(newJson.root[0].rootKey, "w:pPr"); }); }); @@ -39,7 +40,7 @@ describe("Paragraph", () => { describe("#heading1()", () => { it("should add heading style to JSON", () => { paragraph.heading1(); - let newJson = jsonify(paragraph); + const newJson = jsonify(paragraph); assert.equal(newJson.root[0].root[1].root[0].root.val, "Heading1"); }); }); @@ -47,7 +48,7 @@ describe("Paragraph", () => { describe("#heading2()", () => { it("should add heading style to JSON", () => { paragraph.heading2(); - let newJson = jsonify(paragraph); + const newJson = jsonify(paragraph); assert.equal(newJson.root[0].root[1].root[0].root.val, "Heading2"); }); @@ -56,7 +57,7 @@ describe("Paragraph", () => { describe("#heading3()", () => { it("should add heading style to JSON", () => { paragraph.heading3(); - let newJson = jsonify(paragraph); + const newJson = jsonify(paragraph); assert.equal(newJson.root[0].root[1].root[0].root.val, "Heading3"); }); @@ -65,7 +66,7 @@ describe("Paragraph", () => { describe("#title()", () => { it("should add title style to JSON", () => { paragraph.title(); - let newJson = jsonify(paragraph); + const newJson = jsonify(paragraph); assert.equal(newJson.root[0].root[1].root[0].root.val, "Title"); }); @@ -74,7 +75,7 @@ describe("Paragraph", () => { describe("#center()", () => { it("should add center alignment to JSON", () => { paragraph.center(); - let newJson = jsonify(paragraph); + const newJson = jsonify(paragraph); assert.equal(newJson.root[0].root[1].root[0].root.val, "center"); }); @@ -83,7 +84,7 @@ describe("Paragraph", () => { describe("#thematicBreak()", () => { it("should add thematic break to JSON", () => { paragraph.thematicBreak(); - let newJson = jsonify(paragraph); + const newJson = jsonify(paragraph); assert.equal(newJson.root[0].root[1].rootKey, "w:pBdr"); }); }); @@ -91,13 +92,13 @@ describe("Paragraph", () => { describe("#pageBreak()", () => { it("should add page break to JSON", () => { paragraph.pageBreak(); - let newJson = jsonify(paragraph); + const newJson = jsonify(paragraph); assert.equal(newJson.root[0].root[1].root[1].rootKey, "w:br"); }); it("should add page break with 'page' type", () => { paragraph.pageBreak(); - let newJson = jsonify(paragraph); + const newJson = jsonify(paragraph); assert.equal(newJson.root[0].root[1].root[1].root[0].root.type, "page"); }); }); @@ -105,13 +106,13 @@ describe("Paragraph", () => { describe("#bullet()", () => { it("should add list paragraph style to JSON", () => { paragraph.bullet(); - let newJson = jsonify(paragraph); + const newJson = jsonify(paragraph); assert.equal(newJson.root[0].root[1].root[0].root.val, "ListParagraph"); }); it("it should add numbered properties", () => { paragraph.bullet(); - let newJson = jsonify(paragraph); + const newJson = jsonify(paragraph); assert.isDefined(newJson.root[0].root[2]); }); }); @@ -124,7 +125,7 @@ describe("Paragraph", () => { const letterNumbering = numbering.createConcreteNumbering(numberedAbstract); paragraph.setNumbering(letterNumbering, 0); - let newJson = jsonify(paragraph); + const newJson = jsonify(paragraph); assert.equal(newJson.root[0].root[1].root[0].root.val, "ListParagraph"); }); @@ -136,23 +137,73 @@ describe("Paragraph", () => { paragraph.setNumbering(letterNumbering, 0); const tree = new Formatter().format(paragraph); - console.log(JSON.stringify(tree, null, 2)); expect(tree).to.deep.equal({ "w:p": [ { "w:pPr": [ - {"_attr": {}}, - {"w:pStyle": [{"_attr": {"w:val": "ListParagraph"}}]}, + {_attr: {}}, + {"w:pStyle": [{_attr: {"w:val": "ListParagraph"}}]}, { "w:numPr": [ - {"w:ilvl": [{"_attr": {"w:val": 0}}]}, - {"w:numId": [{"_attr": {"w:val": letterNumbering.id}}]} - ] + {"w:ilvl": [{_attr: {"w:val": 0}}]}, + {"w:numId": [{_attr: {"w:val": letterNumbering.id}}]}, + ], }, ], }, - ] - }) + ], + }); + }); + }); + + describe("#style", () => { + it("should set the paragraph style to the given styleId", () => { + paragraph.style("myFancyStyle"); + const tree = new Formatter().format(paragraph); + expect(tree).to.deep.equal({ + "w:p": [ + { + "w:pPr": [ + {_attr: {}}, + {"w:pStyle": [{_attr: {"w:val": "myFancyStyle"}}]}, + ], + }, + ], + }); + }); + }); + + describe("#indent", () => { + it("should set the paragraph indent to the given values", () => { + paragraph.indent(720); + const tree = new Formatter().format(paragraph); + expect(tree).to.deep.equal({ + "w:p": [ + { + "w:pPr": [ + {_attr: {}}, + {"w:ind": [{_attr: {"w:left": 720}}]}, + ], + }, + ], + }); + }); + }); + + describe("#spacing", () => { + it("should set the paragraph spacing to the given values", () => { + paragraph.spacing({before: 90, line: 50}); + const tree = new Formatter().format(paragraph); + expect(tree).to.deep.equal({ + "w:p": [ + { + "w:pPr": [ + {_attr: {}}, + {"w:spacing": [{_attr: {"w:before": 90, "w:line": 50}}]}, + ], + }, + ], + }); }); }); }); diff --git a/ts/tests/docx/paragraph/spacingTest.ts b/ts/tests/docx/paragraph/spacingTest.ts new file mode 100644 index 0000000000..e09e9e9042 --- /dev/null +++ b/ts/tests/docx/paragraph/spacingTest.ts @@ -0,0 +1,24 @@ +import { expect } from "chai"; + +import { Spacing } from "../../../docx/paragraph/spacing"; +import { Formatter } from "../../../export/formatter"; + +describe("Spacing", () => { + describe("#constructor", () => { + it("should set the properties given", () => { + const spacing = new Spacing({before: 100, after: 120, line: 150}); + const tree = new Formatter().format(spacing); + expect(tree).to.deep.equal({ + "w:spacing": [{_attr: {"w:after": 120, "w:before": 100, "w:line": 150}}], + }); + }); + + it("should only set the given properties", () => { + const spacing = new Spacing({before: 100}); + const tree = new Formatter().format(spacing); + expect(tree).to.deep.equal({ + "w:spacing": [{_attr: {"w:before": 100}}], + }); + }); + }); +}); diff --git a/ts/tests/docx/run/underlineTests.ts b/ts/tests/docx/run/underlineTests.ts index 4a6e931598..2fb34172f5 100644 --- a/ts/tests/docx/run/underlineTests.ts +++ b/ts/tests/docx/run/underlineTests.ts @@ -1,9 +1,11 @@ -import * as u from "../../../docx/run/underline"; -import { TextRun } from "../../../docx/run/text-run"; -import { assert } from "chai"; +import { assert, expect } from "chai"; -function jsonify(obj: Object) { - let stringifiedJson = JSON.stringify(obj); +import { TextRun } from "../../../docx/run/text-run"; +import * as u from "../../../docx/run/underline"; +import { Formatter } from "../../../export/formatter"; + +function jsonify(obj: object) { + const stringifiedJson = JSON.stringify(obj); return JSON.parse(stringifiedJson); } @@ -12,10 +14,26 @@ describe("Underline", () => { describe("#constructor()", () => { it("should create a new Underline object with u:u as the rootKey", () => { - let underline = new u.Underline(); - let newJson = jsonify(underline); + const underline = new u.Underline(); + const newJson = jsonify(underline); assert.equal(newJson.rootKey, "w:u"); }); + + it("should default to 'single' and no color", () => { + const underline = new u.Underline(); + const tree = new Formatter().format(underline); + expect(tree).to.deep.equal({ + "w:u": [{_attr: {"w:val": "single"}}], + }); + }); + + it("should use the given style type and color", () => { + const underline = new u.Underline("double", "FF00CC"); + const tree = new Formatter().format(underline); + expect(tree).to.deep.equal({ + "w:u": [{_attr: {"w:val": "double", "w:color": "FF00CC"}}], + }); + }); }); }); @@ -23,14 +41,14 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should have u:u as the rootKey", () => { - let underline = new u.DashDotDotHeavyUnderline(); - let newJson = jsonify(underline); + const underline = new u.DashDotDotHeavyUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.rootKey, "w:u"); }); it("should put value in attribute", () => { - let underline = new u.DashDotDotHeavyUnderline(); - let newJson = jsonify(underline); + const underline = new u.DashDotDotHeavyUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "dashDotDotHeavy"); }); }); @@ -40,8 +58,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.DashDotHeavyUnderline(); - let newJson = jsonify(underline); + const underline = new u.DashDotHeavyUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "dashDotHeavy"); }); }); @@ -51,8 +69,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.DashLongHeavyUnderline(); - let newJson = jsonify(underline); + const underline = new u.DashLongHeavyUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "dashLongHeavy"); }); }); @@ -62,8 +80,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.DashLongUnderline(); - let newJson = jsonify(underline); + const underline = new u.DashLongUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "dashLong"); }); }); @@ -73,8 +91,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.DashUnderline(); - let newJson = jsonify(underline); + const underline = new u.DashUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "dash"); }); }); @@ -84,8 +102,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.DotDashUnderline(); - let newJson = jsonify(underline); + const underline = new u.DotDashUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "dotDash"); }); }); @@ -95,8 +113,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.DotDotDashUnderline(); - let newJson = jsonify(underline); + const underline = new u.DotDotDashUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "dotDotDash"); }); }); @@ -106,8 +124,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.DottedHeavyUnderline(); - let newJson = jsonify(underline); + const underline = new u.DottedHeavyUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "dottedHeavy"); }); }); @@ -117,8 +135,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.DottedUnderline(); - let newJson = jsonify(underline); + const underline = new u.DottedUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "dotted"); }); }); @@ -128,8 +146,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.DoubleUnderline(); - let newJson = jsonify(underline); + const underline = new u.DoubleUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "double"); }); }); @@ -139,8 +157,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.SingleUnderline(); - let newJson = jsonify(underline); + const underline = new u.SingleUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "single"); }); }); @@ -150,8 +168,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.ThickUnderline(); - let newJson = jsonify(underline); + const underline = new u.ThickUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "thick"); }); }); @@ -161,8 +179,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.WaveUnderline(); - let newJson = jsonify(underline); + const underline = new u.WaveUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "wave"); }); }); @@ -172,8 +190,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.WavyDoubleUnderline(); - let newJson = jsonify(underline); + const underline = new u.WavyDoubleUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "wavyDouble"); }); }); @@ -183,8 +201,8 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.WavyHeavyUnderline(); - let newJson = jsonify(underline); + const underline = new u.WavyHeavyUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "wavyHeavy"); }); }); @@ -194,9 +212,9 @@ describe("DashDotDotHeavyUnderline", () => { describe("#constructor()", () => { it("should put value in attribute", () => { - let underline = new u.WordsUnderline(); - let newJson = jsonify(underline); + const underline = new u.WordsUnderline(); + const newJson = jsonify(underline); assert.equal(newJson.root[0].root.val, "words"); }); }); -}); \ No newline at end of file +}); diff --git a/ts/tests/stylesTest.ts b/ts/tests/stylesTest.ts index bc3015c009..0922627528 100644 --- a/ts/tests/stylesTest.ts +++ b/ts/tests/stylesTest.ts @@ -1,5 +1,8 @@ +import { assert, expect } from "chai"; +import { Formatter } from "../export/formatter"; import { Styles } from "../styles"; -import { assert } from "chai"; +import { ParagraphStyle, Style } from "../styles/style"; +import * as components from "../styles/style/components"; describe("Styles", () => { let styles: Styles; @@ -9,13 +12,316 @@ describe("Styles", () => { }); describe("#constructor()", () => { - it("should create styles with correct rootKey", () => { - let styles = new Styles(); - let stringifiedJson = JSON.stringify(styles); - let newJson = JSON.parse(stringifiedJson); - + const newJson = JSON.parse(JSON.stringify(styles)); assert.equal(newJson.rootKey, "w:styles"); }); }); -}); \ No newline at end of file + + describe("#createParagraphStyle", () => { + it("should create a new paragraph style and push it onto this collection", () => { + const ps = styles.createParagraphStyle("pStyleId"); + const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr); + expect(tree).to.deep.equal([{ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "pStyleId"}}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": []}, + ], + }]); + }); + + it("should set the paragraph name if given", () => { + const ps = styles.createParagraphStyle("pStyleId", "Paragraph Style"); + const tree = new Formatter().format(styles)["w:styles"].filter((x) => !x._attr); + expect(tree).to.deep.equal([{ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "pStyleId"}}, + {"w:name": [{_attr: {"w:val": "Paragraph Style"}}]}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": []}, + ], + }]); + }); + }); +}); + +describe("Style", () => { + describe("#constructor()", () => { + it("should set the given properties", () => { + const style = new Style({ + type: "paragraph", + styleId: "myStyleId", + default: true, + }); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId", "w:default": true}}, + ], + }); + }); + + it("should set the name of the style, if given", () => { + const style = new Style({ + type: "paragraph", + styleId: "myStyleId", + }, "Style Name"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:name": [{_attr: {"w:val": "Style Name"}}]}, + ], + }); + }); + }); +}); + +describe("Style components", () => { + it("Name#constructor", () => { + const style = new components.Name("Style Name"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({"w:name": [{_attr: {"w:val": "Style Name"}}]}); + }); + + it("BasedOn#constructor", () => { + const style = new components.BasedOn("otherId"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({"w:basedOn": [{_attr: {"w:val": "otherId"}}]}); + }); + + it("Next#constructor", () => { + const style = new components.Next("otherId"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({"w:next": [{_attr: {"w:val": "otherId"}}]}); + }); + + it("Link#constructor", () => { + const style = new components.Link("otherId"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({"w:link": [{_attr: {"w:val": "otherId"}}]}); + }); + + it("UiPriority#constructor", () => { + const style = new components.UiPriority("123"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({"w:uiPriority": [{_attr: {"w:val": "123"}}]}); + }); +}); + +describe("ParagraphStyle", () => { + describe("#constructor", () => { + it("should set the style type to paragraph and use the given style id", () => { + const style = new ParagraphStyle("myStyleId"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": []}, + ], + }); + }); + + it("should set the name of the style, if given", () => { + const style = new ParagraphStyle("myStyleId", "Style Name"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:name": [{_attr: {"w:val": "Style Name"}}]}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": []}, + ], + }); + }); + }); + + describe("formatting methods: style attributes", () => { + it("#basedOn", () => { + const style = new ParagraphStyle("myStyleId") + .basedOn("otherId"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": []}, + {"w:basedOn": [{_attr: {"w:val": "otherId"}}]}, + ], + }); + }); + + it("#quickFormat", () => { + const style = new ParagraphStyle("myStyleId") + .quickFormat(); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": []}, + {"w:qFormat": []}, + ], + }); + }); + + it("#next", () => { + const style = new ParagraphStyle("myStyleId") + .next("otherId"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": []}, + {"w:next": [{_attr: {"w:val": "otherId"}}]}, + ], + }); + }); + }); + + describe("formatting methods: paragraph properties", () => { + it("#indent", () => { + const style = new ParagraphStyle("myStyleId") + .indent(720); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [ + {_attr: {}}, + {"w:ind": [{_attr: {"w:left": 720}}]}, + ]}, + {"w:rPr": []}, + ], + }); + }); + + it("#spacing", () => { + const style = new ParagraphStyle("myStyleId") + .spacing({before: 50, after: 150}); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [ + {_attr: {}}, + {"w:spacing": [{_attr: {"w:before": 50, "w:after": 150}}]}, + ]}, + {"w:rPr": []}, + ], + }); + }); + }); + + describe("formatting methods: run properties", () => { + it("#size", () => { + const style = new ParagraphStyle("myStyleId") + .size(24); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": [ + {"w:sz": [{_attr: {"w:val": 24}}]}, + ]}, + ], + }); + }); + + it("#bold", () => { + const style = new ParagraphStyle("myStyleId") + .bold(); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": [ + {"w:b": [{_attr: {"w:val": true}}]}, + ]}, + ], + }); + }); + + it("#italics", () => { + const style = new ParagraphStyle("myStyleId") + .italics(); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": [ + {"w:i": [{_attr: {"w:val": true}}]}, + ]}, + ], + }); + }); + + describe("#underline", () => { + it("should set underline to 'single' if no arguments are given", () => { + const style = new ParagraphStyle("myStyleId") + .underline(); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": [ + {"w:u": [{_attr: {"w:val": "single"}}]}, + ]}, + ], + }); + }); + + it("should set the style if given", () => { + const style = new ParagraphStyle("myStyleId") + .underline("double"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": [ + {"w:u": [{_attr: {"w:val": "double"}}]}, + ]}, + ], + }); + }); + + it("should set the style and color if given", () => { + const style = new ParagraphStyle("myStyleId") + .underline("double", "005599"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": [ + {"w:u": [{_attr: {"w:val": "double", "w:color": "005599"}}]}, + ]}, + ], + }); + }); + }); + + it("#color", () => { + const style = new ParagraphStyle("myStyleId") + .color("123456"); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + {_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}}, + {"w:pPr": [{_attr: {}}]}, + {"w:rPr": [ + {"w:color": [{_attr: {"w:val": "123456"}}]}, + ]}, + ], + }); + }); + }); +});