From 15e8d7c053e6801e869650155895ecab0f4ce565 Mon Sep 17 00:00:00 2001 From: h4buli <34742290+h4buli@users.noreply.github.com> Date: Thu, 10 May 2018 20:24:06 +0200 Subject: [PATCH 01/23] paragraph: add support for page break before format property (#7) --- src/file/paragraph/formatting/page-break.spec.ts | 10 +++++++++- src/file/paragraph/formatting/page-break.ts | 9 +++++++++ src/file/paragraph/paragraph.spec.ts | 16 ++++++++++++++++ src/file/paragraph/paragraph.ts | 7 ++++++- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/file/paragraph/formatting/page-break.spec.ts b/src/file/paragraph/formatting/page-break.spec.ts index 675ac5d112..6ba877311f 100644 --- a/src/file/paragraph/formatting/page-break.spec.ts +++ b/src/file/paragraph/formatting/page-break.spec.ts @@ -1,7 +1,7 @@ import { assert } from "chai"; import { Utility } from "../../../tests/utility"; -import { PageBreak } from "./page-break"; +import { PageBreak, PageBreakBefore } from "./page-break"; describe("PageBreak", () => { let pageBreak: PageBreak; @@ -30,3 +30,11 @@ describe("PageBreak", () => { }); }); }); + +describe("PageBreakBefore", () => { + it("should create page break before", () => { + const pageBreakBefore = new PageBreakBefore(); + const newJson = Utility.jsonify(pageBreakBefore); + assert.equal(newJson.rootKey, "w:pageBreakBefore"); + }); +}); diff --git a/src/file/paragraph/formatting/page-break.ts b/src/file/paragraph/formatting/page-break.ts index 1b4e6642b5..13556d12e2 100644 --- a/src/file/paragraph/formatting/page-break.ts +++ b/src/file/paragraph/formatting/page-break.ts @@ -19,3 +19,12 @@ export class PageBreak extends Run { this.root.push(new Break()); } } + +/** + * Add page break before the paragraph if there is no one added before. + */ +export class PageBreakBefore extends XmlComponent { + constructor() { + super("w:pageBreakBefore"); + } +} diff --git a/src/file/paragraph/paragraph.spec.ts b/src/file/paragraph/paragraph.spec.ts index 54c5970679..0aa17b558a 100644 --- a/src/file/paragraph/paragraph.spec.ts +++ b/src/file/paragraph/paragraph.spec.ts @@ -161,6 +161,22 @@ describe("Paragraph", () => { }); }); + describe("#pageBreakBefore()", () => { + it("should add page break before to JSON", () => { + paragraph.pageBreakBefore(); + const tree = new Formatter().format(paragraph); + expect(tree).to.deep.equal({ + "w:p": [ + { + "w:pPr": [{ + "w:pageBreakBefore": [] + }], + } + ], + }); + }); + }); + describe("#bullet()", () => { it("should add list paragraph style to JSON", () => { paragraph.bullet(); diff --git a/src/file/paragraph/paragraph.ts b/src/file/paragraph/paragraph.ts index ac779de094..22839a491a 100644 --- a/src/file/paragraph/paragraph.ts +++ b/src/file/paragraph/paragraph.ts @@ -8,7 +8,7 @@ import { Alignment } from "./formatting/alignment"; import { ThematicBreak } from "./formatting/border"; import { Indent } from "./formatting/indent"; import { KeepLines, KeepNext } from "./formatting/keep"; -import { PageBreak } from "./formatting/page-break"; +import { PageBreak, PageBreakBefore } from "./formatting/page-break"; import { ISpacingProperties, Spacing } from "./formatting/spacing"; import { Style } from "./formatting/style"; import { CenterTabStop, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop"; @@ -104,6 +104,11 @@ export class Paragraph extends XmlComponent { return this; } + public pageBreakBefore(): Paragraph { + this.properties.push(new PageBreakBefore()); + return this; + } + public maxRightTabStop(): Paragraph { this.properties.push(new MaxRightTabStop()); return this; From cdb86b741c9633468dd89fe29f8a51e2d0e37bdb Mon Sep 17 00:00:00 2001 From: Igor Bulovski Date: Thu, 10 May 2018 20:25:14 +0200 Subject: [PATCH 02/23] Version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cee754834f..0c639332c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "docx-h4", - "version": "3.2.8", + "version": "3.2.9", "description": "Generate .docx documents with JavaScript (formerly Office-Clippy)", "main": "build/index.js", "scripts": { From dadd8c122f81880690088d0f17fe3dc86e5ff321 Mon Sep 17 00:00:00 2001 From: Igor Bulovski Date: Thu, 17 May 2018 15:32:15 +0200 Subject: [PATCH 03/23] fixed formatting styles --- src/file/index.ts | 2 +- src/file/media/media.ts | 8 ++++---- src/file/numbering/index.ts | 2 +- src/file/numbering/numbering.ts | 4 ++-- src/file/paragraph/paragraph.spec.ts | 10 ++++++---- src/file/styles/external-styles-factory.spec.ts | 1 - src/file/styles/external-styles-factory.ts | 4 +--- src/file/table/index.ts | 2 +- src/file/table/table-cell.spec.ts | 12 ++++++------ src/file/xml-components/index.ts | 2 +- src/file/xml-components/xml-component.spec.ts | 4 ++-- src/file/xml-components/xml-component.ts | 2 +- 12 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/file/index.ts b/src/file/index.ts index 3d86dafdb2..42a9ca3b76 100644 --- a/src/file/index.ts +++ b/src/file/index.ts @@ -5,4 +5,4 @@ export * from "./numbering"; export * from "./media"; export * from "./drawing"; export * from "./styles"; -export * from "./xml-components"; \ No newline at end of file +export * from "./xml-components"; diff --git a/src/file/media/media.ts b/src/file/media/media.ts index 7f5c960a55..69435eb21f 100644 --- a/src/file/media/media.ts +++ b/src/file/media/media.ts @@ -11,7 +11,7 @@ export class Media { this.map = new Map(); } - private createMedia(key: string, relationshipsCount, dimensions, data: fs.ReadStream | Buffer, filePath?: string, ) { + private createMedia(key: string, relationshipsCount, dimensions, data: fs.ReadStream | Buffer, filePath?: string) { const imageData = { referenceId: this.map.size + relationshipsCount + 1, stream: data, @@ -54,12 +54,12 @@ export class Media { if (width && height) { dimensions = { width: width, - height: height - } + height: height, + }; } else { dimensions = sizeOf(data); } - + return this.createMedia(key, relationshipsCount, dimensions, data); } diff --git a/src/file/numbering/index.ts b/src/file/numbering/index.ts index d7a38258d3..a861d336ee 100644 --- a/src/file/numbering/index.ts +++ b/src/file/numbering/index.ts @@ -1,2 +1,2 @@ export * from "./numbering"; -export * from "./abstract-numbering"; \ No newline at end of file +export * from "./abstract-numbering"; diff --git a/src/file/numbering/numbering.ts b/src/file/numbering/numbering.ts index 517766987a..a659643b38 100644 --- a/src/file/numbering/numbering.ts +++ b/src/file/numbering/numbering.ts @@ -49,8 +49,8 @@ export class Numbering extends XmlComponent { } public prepForXml(): IXmlableObject { - this.abstractNumbering.forEach(x => this.root.push(x)); - this.concreteNumbering.forEach(x => this.root.push(x)); + this.abstractNumbering.forEach((x) => this.root.push(x)); + this.concreteNumbering.forEach((x) => this.root.push(x)); return super.prepForXml(); } } diff --git a/src/file/paragraph/paragraph.spec.ts b/src/file/paragraph/paragraph.spec.ts index 0aa17b558a..863d0ceb3d 100644 --- a/src/file/paragraph/paragraph.spec.ts +++ b/src/file/paragraph/paragraph.spec.ts @@ -168,10 +168,12 @@ describe("Paragraph", () => { expect(tree).to.deep.equal({ "w:p": [ { - "w:pPr": [{ - "w:pageBreakBefore": [] - }], - } + "w:pPr": [ + { + "w:pageBreakBefore": [], + }, + ], + }, ], }); }); diff --git a/src/file/styles/external-styles-factory.spec.ts b/src/file/styles/external-styles-factory.spec.ts index 0957c25d9f..861a6f05d8 100644 --- a/src/file/styles/external-styles-factory.spec.ts +++ b/src/file/styles/external-styles-factory.spec.ts @@ -154,7 +154,6 @@ describe("External styles factory", () => { ], rootKey: "w:style", }); - }); }); }); diff --git a/src/file/styles/external-styles-factory.ts b/src/file/styles/external-styles-factory.ts index 703f017922..315eb7452c 100644 --- a/src/file/styles/external-styles-factory.ts +++ b/src/file/styles/external-styles-factory.ts @@ -44,9 +44,7 @@ export class ExternalStylesFactory { }); // convert the styles one by one - xmlStyles["w:style"] - .map((style) => this.convertElement("w:style", style)) - .forEach(importedStyle.push.bind(importedStyle)); + xmlStyles["w:style"].map((style) => this.convertElement("w:style", style)).forEach(importedStyle.push.bind(importedStyle)); return importedStyle; } diff --git a/src/file/table/index.ts b/src/file/table/index.ts index ef3d91a47b..dfba175857 100644 --- a/src/file/table/index.ts +++ b/src/file/table/index.ts @@ -1,2 +1,2 @@ export * from "./table"; -export * from './table-cell'; \ No newline at end of file +export * from "./table-cell"; diff --git a/src/file/table/table-cell.spec.ts b/src/file/table/table-cell.spec.ts index 596150d6d5..01c81848bb 100644 --- a/src/file/table/table-cell.spec.ts +++ b/src/file/table/table-cell.spec.ts @@ -169,13 +169,13 @@ describe("TableCellWidth", () => { expect(tree).to.deep.equal({ "w:tcW": [ { - "_attr": { + _attr: { "w:type": "dxa", - "w:w": 100 - } - } - ] + "w:w": 100, + }, + }, + ], }); }); }); -}); \ No newline at end of file +}); diff --git a/src/file/xml-components/index.ts b/src/file/xml-components/index.ts index 85e7e383f7..917933869e 100644 --- a/src/file/xml-components/index.ts +++ b/src/file/xml-components/index.ts @@ -1,5 +1,5 @@ export * from "./xml-component"; export * from "./attributes"; export * from "./default-attributes"; -export * from './imported-xml-component'; +export * from "./imported-xml-component"; export * from "./xmlable-object"; diff --git a/src/file/xml-components/xml-component.spec.ts b/src/file/xml-components/xml-component.spec.ts index 25bf442bc9..8b4f983388 100644 --- a/src/file/xml-components/xml-component.spec.ts +++ b/src/file/xml-components/xml-component.spec.ts @@ -24,9 +24,9 @@ describe("XmlComponent", () => { const child = new TestComponent("w:test1"); child.delete(); xmlComponent.addChildElement(child); - + const xml = xmlComponent.prepForXml(); - assert.equal(xml['w:test'].length, 0); + assert.equal(xml["w:test"].length, 0); }); }); }); diff --git a/src/file/xml-components/xml-component.ts b/src/file/xml-components/xml-component.ts index b41bb01267..bce354a422 100644 --- a/src/file/xml-components/xml-component.ts +++ b/src/file/xml-components/xml-component.ts @@ -12,7 +12,7 @@ export abstract class XmlComponent extends BaseXmlComponent { public prepForXml(): IXmlableObject { const children = this.root - .filter(c => { + .filter((c) => { if (c instanceof BaseXmlComponent) { return !c.isDeleted; } From 97e976a1842e4f2898d820b6b8af7b68a0edf19c Mon Sep 17 00:00:00 2001 From: h4buli <34742290+h4buli@users.noreply.github.com> Date: Mon, 28 May 2018 09:15:44 +0200 Subject: [PATCH 04/23] import: add support to import any xml tab element to XmlComponent (#8) - make method to import/convert to XmlComponent - expose method to add childElement to Header/Footer - add tests --- src/file/footer-wrapper.ts | 5 ++ src/file/header-wrapper.ts | 5 ++ src/file/styles/external-styles-factory.ts | 22 +------ .../imported-xml-component.spec.ts | 61 ++++++++++++++++- .../xml-components/imported-xml-component.ts | 66 +++++++++++++++++++ 5 files changed, 138 insertions(+), 21 deletions(-) diff --git a/src/file/footer-wrapper.ts b/src/file/footer-wrapper.ts index bb6efbe25a..47d0f73928 100644 --- a/src/file/footer-wrapper.ts +++ b/src/file/footer-wrapper.ts @@ -3,6 +3,7 @@ import { IMediaData, Media } from "./media"; import { Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Table } from "./table"; +import { XmlComponent } from "."; export class FooterWrapper { private readonly footer: Footer; @@ -35,6 +36,10 @@ export class FooterWrapper { this.footer.addDrawing(imageData); } + public addChildElement(childElement: XmlComponent | string) { + this.footer.addChildElement(childElement); + } + public createImage(image: string): void { const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount); this.relationships.createRelationship( diff --git a/src/file/header-wrapper.ts b/src/file/header-wrapper.ts index df15871bef..250919b489 100644 --- a/src/file/header-wrapper.ts +++ b/src/file/header-wrapper.ts @@ -3,6 +3,7 @@ import { IMediaData, Media } from "./media"; import { Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Table } from "./table"; +import { XmlComponent } from "."; export class HeaderWrapper { private readonly header: Header; @@ -35,6 +36,10 @@ export class HeaderWrapper { this.header.addDrawing(imageData); } + public addChildElement(childElement: XmlComponent | string) { + this.header.addChildElement(childElement); + } + public createImage(image: string): void { const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount); this.relationships.createRelationship( diff --git a/src/file/styles/external-styles-factory.ts b/src/file/styles/external-styles-factory.ts index 315eb7452c..9757faf8d9 100644 --- a/src/file/styles/external-styles-factory.ts +++ b/src/file/styles/external-styles-factory.ts @@ -1,12 +1,6 @@ import { Styles } from "./"; import * as fastXmlParser from "fast-xml-parser"; -import { ImportedXmlComponent, ImportedRootElementAttributes } from "./../../file/xml-components"; - -const parseOptions = { - ignoreAttributes: false, - attributeNamePrefix: "", - attrNodeName: "_attr", -}; +import { ImportedXmlComponent, ImportedRootElementAttributes, parseOptions, convertToXmlComponent } from "./../../file/xml-components"; export class ExternalStylesFactory { /** @@ -44,19 +38,7 @@ export class ExternalStylesFactory { }); // convert the styles one by one - xmlStyles["w:style"].map((style) => this.convertElement("w:style", style)).forEach(importedStyle.push.bind(importedStyle)); - + xmlStyles["w:style"].map((style) => convertToXmlComponent("w:style", style)).forEach(importedStyle.push.bind(importedStyle)); return importedStyle; } - - convertElement(elementName: string, element: any): ImportedXmlComponent { - const xmlElement = new ImportedXmlComponent(elementName, element._attr); - if (typeof element === "object") { - Object.keys(element) - .filter((key) => key !== "_attr") - .map((item) => this.convertElement(item, element[item])) - .forEach(xmlElement.push.bind(xmlElement)); - } - return xmlElement; - } } diff --git a/src/file/xml-components/imported-xml-component.spec.ts b/src/file/xml-components/imported-xml-component.spec.ts index d7b638ba9f..e941d3f0ec 100644 --- a/src/file/xml-components/imported-xml-component.spec.ts +++ b/src/file/xml-components/imported-xml-component.spec.ts @@ -1,5 +1,52 @@ import { expect } from "chai"; -import { ImportedXmlComponent } from "./"; +import { ImportedXmlComponent, convertToXmlComponent } from "./"; + +const xmlString = ` + + + some value + + + Text 1 + + + Text 2 + + + `; + +const importedXmlElement = { + "w:p": { + _attr: { "w:one": "value 1", "w:two": "value 2" }, + "w:rPr": { "w:noProof": "some value" }, + "w:r": [{ _attr: { active: "true" }, "w:t": "Text 1" }, { _attr: { active: "true" }, "w:t": "Text 2" }], + }, +}; + +const convertedXmlElement = { + deleted: false, + rootKey: "w:p", + root: [ + { + deleted: false, + rootKey: "w:rPr", + root: [{ deleted: false, rootKey: "w:noProof", root: ["some value"] }], + }, + { + deleted: false, + rootKey: "w:r", + root: [{ deleted: false, rootKey: "w:t", root: ["Text 1"] }], + _attr: { active: "true" }, + }, + { + deleted: false, + rootKey: "w:r", + root: [{ deleted: false, rootKey: "w:t", root: ["Text 2"] }], + _attr: { active: "true" }, + }, + ], + _attr: { "w:one": "value 1", "w:two": "value 2" }, +}; describe("ImportedXmlComponent", () => { let importedXmlComponent: ImportedXmlComponent; @@ -31,4 +78,16 @@ describe("ImportedXmlComponent", () => { }); }); }); + + it("should create XmlComponent from xml string", () => { + const converted = ImportedXmlComponent.fromXmlString(xmlString); + expect(converted).to.eql(convertedXmlElement); + }); + + describe("convertToXmlComponent", () => { + it("should convert to xml component", () => { + const converted = convertToXmlComponent("w:p", importedXmlElement["w:p"]); + expect(converted).to.eql(convertedXmlElement); + }); + }); }); diff --git a/src/file/xml-components/imported-xml-component.ts b/src/file/xml-components/imported-xml-component.ts index 51af733c2e..41ad025ab5 100644 --- a/src/file/xml-components/imported-xml-component.ts +++ b/src/file/xml-components/imported-xml-component.ts @@ -1,4 +1,53 @@ import { XmlComponent, IXmlableObject } from "."; +import * as fastXmlParser from "fast-xml-parser"; +import { flatMap } from "lodash"; + +export const parseOptions = { + ignoreAttributes: false, + attributeNamePrefix: "", + attrNodeName: "_attr", +}; + +/** + * Converts the given xml element (in json format) into XmlComponent. + * Note: If element is array, them it will return ImportedXmlComponent[]. Example for given: + * element = [ + * { w:t: "val 1"}, + * { w:t: "val 2"} + * ] + * will return + * [ + * ImportedXmlComponent { rootKey: "w:t", root: [ "val 1" ]}, + * ImportedXmlComponent { rootKey: "w:t", root: [ "val 2" ]} + * ] + * + * @param elementName name (rootKey) of the XmlComponent + * @param element the xml element in json presentation + */ +export function convertToXmlComponent(elementName: string, element: any): ImportedXmlComponent | ImportedXmlComponent[] { + const xmlElement = new ImportedXmlComponent(elementName, element._attr); + if (Array.isArray(element)) { + const out: any[] = []; + element.forEach((itemInArray) => { + out.push(convertToXmlComponent(elementName, itemInArray)); + }); + return flatMap(out); + } else if (typeof element === "object") { + Object.keys(element) + .filter((key) => key !== "_attr") + .map((item) => convertToXmlComponent(item, element[item])) + .forEach((converted) => { + if (Array.isArray(converted)) { + converted.forEach(xmlElement.push.bind(xmlElement)); + } else { + xmlElement.push(converted); + } + }); + } else if (element !== "") { + xmlElement.push(element); + } + return xmlElement; +} /** * Represents imported xml component from xml file. @@ -53,6 +102,23 @@ export class ImportedXmlComponent extends XmlComponent { push(xmlComponent: XmlComponent) { this.root.push(xmlComponent); } + + /** + * Converts the xml string to a XmlComponent tree. + * + * @param importedContent xml content of the imported component + */ + static fromXmlString(importedContent: string): ImportedXmlComponent { + const imported = fastXmlParser.parse(importedContent, parseOptions); + const elementName = Object.keys(imported)[0]; + + const converted = convertToXmlComponent(elementName, imported[elementName]); + + if (Array.isArray(converted) && converted.length > 1) { + throw new Error("Invalid conversion, input must be one element."); + } + return Array.isArray(converted) ? converted[0] : converted; + } } /** From 548fe3c864081a003b07b26f1df972f86cf8e89b Mon Sep 17 00:00:00 2001 From: Igor Bulovski Date: Mon, 28 May 2018 09:18:09 +0200 Subject: [PATCH 05/23] Version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0c639332c7..9795a35e25 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "docx-h4", - "version": "3.2.9", + "version": "3.2.10", "description": "Generate .docx documents with JavaScript (formerly Office-Clippy)", "main": "build/index.js", "scripts": { From ac40a40ec0c81183476c9436fb7f2f1e5ce7107c Mon Sep 17 00:00:00 2001 From: Igor Bulovski Date: Fri, 8 Jun 2018 07:50:23 +0200 Subject: [PATCH 06/23] refactor: move components from /drawing/inline to /drawing - they will be used for other positioning element (floating) --- .../doc-properties/doc-properties-attributes.ts | 0 .../{inline => }/doc-properties/doc-properties.ts | 0 .../effect-extent/effect-extent-attributes.ts | 0 .../{inline => }/effect-extent/effect-extent.ts | 0 .../drawing/{inline => }/extent/extent-attributes.ts | 0 src/file/drawing/{inline => }/extent/extent.ts | 0 .../graphic-frame-lock-attributes.ts | 0 .../graphic-frame-locks/graphic-frame-locks.ts | 0 .../graphic-frame/graphic-frame-properties.ts | 0 .../graphic/graphic-data/graphic-data-attribute.ts | 0 .../{inline => }/graphic/graphic-data/graphic-data.ts | 0 .../drawing/{inline => }/graphic/graphic-data/index.ts | 0 .../graphic/graphic-data/pic/blip/blip-fill.ts | 0 .../{inline => }/graphic/graphic-data/pic/blip/blip.ts | 0 .../graphic/graphic-data/pic/blip/source-rectangle.ts | 0 .../graphic/graphic-data/pic/blip/stretch.ts | 0 .../{inline => }/graphic/graphic-data/pic/index.ts | 0 .../child-non-visual-pic-properties.ts | 0 .../pic-locks/pic-locks-attributes.ts | 0 .../pic-locks/pic-locks.ts | 0 .../non-visual-pic-properties.ts | 0 .../non-visual-properties-attributes.ts | 0 .../non-visual-properties/non-visual-properties.ts | 0 .../graphic/graphic-data/pic/pic-attributes.ts | 0 .../{inline => }/graphic/graphic-data/pic/pic.ts | 0 .../form/extents/extents-attributes.ts | 0 .../pic/shape-properties/form/extents/extents.ts | 0 .../graphic-data/pic/shape-properties/form/form.ts | 0 .../graphic-data/pic/shape-properties/form/index.ts | 0 .../pic/shape-properties/form/offset/off-attributes.ts | 0 .../pic/shape-properties/form/offset/off.ts | 0 .../graphic-data/pic/shape-properties/no-fill.ts | 0 .../pic/shape-properties/outline/no-fill.ts | 0 .../pic/shape-properties/outline/outline.ts | 0 .../adjustment-values/adjustment-values.ts | 0 .../preset-geometry/preset-geometry-attributes.ts | 0 .../preset-geometry/preset-geometry.ts | 0 .../shape-properties/shape-properties-attributes.ts | 0 .../pic/shape-properties/shape-properties.ts | 0 src/file/drawing/{inline => }/graphic/index.ts | 0 src/file/drawing/inline/inline.ts | 10 +++++----- src/file/xml-components/imported-xml-component.ts | 4 ++-- 42 files changed, 7 insertions(+), 7 deletions(-) rename src/file/drawing/{inline => }/doc-properties/doc-properties-attributes.ts (100%) rename src/file/drawing/{inline => }/doc-properties/doc-properties.ts (100%) rename src/file/drawing/{inline => }/effect-extent/effect-extent-attributes.ts (100%) rename src/file/drawing/{inline => }/effect-extent/effect-extent.ts (100%) rename src/file/drawing/{inline => }/extent/extent-attributes.ts (100%) rename src/file/drawing/{inline => }/extent/extent.ts (100%) rename src/file/drawing/{inline => }/graphic-frame/graphic-frame-locks/graphic-frame-lock-attributes.ts (100%) rename src/file/drawing/{inline => }/graphic-frame/graphic-frame-locks/graphic-frame-locks.ts (100%) rename src/file/drawing/{inline => }/graphic-frame/graphic-frame-properties.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/graphic-data-attribute.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/graphic-data.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/index.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/blip/blip-fill.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/blip/blip.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/blip/source-rectangle.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/blip/stretch.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/index.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/child-non-visual-pic-properties.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/pic-locks/pic-locks-attributes.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/pic-locks/pic-locks.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-pic-properties.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-properties/non-visual-properties-attributes.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-properties/non-visual-properties.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/pic-attributes.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/pic.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/form/extents/extents-attributes.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/form/extents/extents.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/form/form.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/form/index.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/form/offset/off-attributes.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/form/offset/off.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/no-fill.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/outline/no-fill.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/outline/outline.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/preset-geometry/adjustment-values/adjustment-values.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/preset-geometry/preset-geometry-attributes.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/preset-geometry/preset-geometry.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/shape-properties-attributes.ts (100%) rename src/file/drawing/{inline => }/graphic/graphic-data/pic/shape-properties/shape-properties.ts (100%) rename src/file/drawing/{inline => }/graphic/index.ts (100%) diff --git a/src/file/drawing/inline/doc-properties/doc-properties-attributes.ts b/src/file/drawing/doc-properties/doc-properties-attributes.ts similarity index 100% rename from src/file/drawing/inline/doc-properties/doc-properties-attributes.ts rename to src/file/drawing/doc-properties/doc-properties-attributes.ts diff --git a/src/file/drawing/inline/doc-properties/doc-properties.ts b/src/file/drawing/doc-properties/doc-properties.ts similarity index 100% rename from src/file/drawing/inline/doc-properties/doc-properties.ts rename to src/file/drawing/doc-properties/doc-properties.ts diff --git a/src/file/drawing/inline/effect-extent/effect-extent-attributes.ts b/src/file/drawing/effect-extent/effect-extent-attributes.ts similarity index 100% rename from src/file/drawing/inline/effect-extent/effect-extent-attributes.ts rename to src/file/drawing/effect-extent/effect-extent-attributes.ts diff --git a/src/file/drawing/inline/effect-extent/effect-extent.ts b/src/file/drawing/effect-extent/effect-extent.ts similarity index 100% rename from src/file/drawing/inline/effect-extent/effect-extent.ts rename to src/file/drawing/effect-extent/effect-extent.ts diff --git a/src/file/drawing/inline/extent/extent-attributes.ts b/src/file/drawing/extent/extent-attributes.ts similarity index 100% rename from src/file/drawing/inline/extent/extent-attributes.ts rename to src/file/drawing/extent/extent-attributes.ts diff --git a/src/file/drawing/inline/extent/extent.ts b/src/file/drawing/extent/extent.ts similarity index 100% rename from src/file/drawing/inline/extent/extent.ts rename to src/file/drawing/extent/extent.ts diff --git a/src/file/drawing/inline/graphic-frame/graphic-frame-locks/graphic-frame-lock-attributes.ts b/src/file/drawing/graphic-frame/graphic-frame-locks/graphic-frame-lock-attributes.ts similarity index 100% rename from src/file/drawing/inline/graphic-frame/graphic-frame-locks/graphic-frame-lock-attributes.ts rename to src/file/drawing/graphic-frame/graphic-frame-locks/graphic-frame-lock-attributes.ts diff --git a/src/file/drawing/inline/graphic-frame/graphic-frame-locks/graphic-frame-locks.ts b/src/file/drawing/graphic-frame/graphic-frame-locks/graphic-frame-locks.ts similarity index 100% rename from src/file/drawing/inline/graphic-frame/graphic-frame-locks/graphic-frame-locks.ts rename to src/file/drawing/graphic-frame/graphic-frame-locks/graphic-frame-locks.ts diff --git a/src/file/drawing/inline/graphic-frame/graphic-frame-properties.ts b/src/file/drawing/graphic-frame/graphic-frame-properties.ts similarity index 100% rename from src/file/drawing/inline/graphic-frame/graphic-frame-properties.ts rename to src/file/drawing/graphic-frame/graphic-frame-properties.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/graphic-data-attribute.ts b/src/file/drawing/graphic/graphic-data/graphic-data-attribute.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/graphic-data-attribute.ts rename to src/file/drawing/graphic/graphic-data/graphic-data-attribute.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts b/src/file/drawing/graphic/graphic-data/graphic-data.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/graphic-data.ts rename to src/file/drawing/graphic/graphic-data/graphic-data.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/index.ts b/src/file/drawing/graphic/graphic-data/index.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/index.ts rename to src/file/drawing/graphic/graphic-data/index.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip-fill.ts b/src/file/drawing/graphic/graphic-data/pic/blip/blip-fill.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/blip/blip-fill.ts rename to src/file/drawing/graphic/graphic-data/pic/blip/blip-fill.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip.ts b/src/file/drawing/graphic/graphic-data/pic/blip/blip.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/blip/blip.ts rename to src/file/drawing/graphic/graphic-data/pic/blip/blip.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/blip/source-rectangle.ts b/src/file/drawing/graphic/graphic-data/pic/blip/source-rectangle.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/blip/source-rectangle.ts rename to src/file/drawing/graphic/graphic-data/pic/blip/source-rectangle.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/blip/stretch.ts b/src/file/drawing/graphic/graphic-data/pic/blip/stretch.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/blip/stretch.ts rename to src/file/drawing/graphic/graphic-data/pic/blip/stretch.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/index.ts b/src/file/drawing/graphic/graphic-data/pic/index.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/index.ts rename to src/file/drawing/graphic/graphic-data/pic/index.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/child-non-visual-pic-properties.ts b/src/file/drawing/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/child-non-visual-pic-properties.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/child-non-visual-pic-properties.ts rename to src/file/drawing/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/child-non-visual-pic-properties.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/pic-locks/pic-locks-attributes.ts b/src/file/drawing/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/pic-locks/pic-locks-attributes.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/pic-locks/pic-locks-attributes.ts rename to src/file/drawing/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/pic-locks/pic-locks-attributes.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/pic-locks/pic-locks.ts b/src/file/drawing/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/pic-locks/pic-locks.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/pic-locks/pic-locks.ts rename to src/file/drawing/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/pic-locks/pic-locks.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-pic-properties.ts b/src/file/drawing/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-pic-properties.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-pic-properties.ts rename to src/file/drawing/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-pic-properties.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-properties/non-visual-properties-attributes.ts b/src/file/drawing/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-properties/non-visual-properties-attributes.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-properties/non-visual-properties-attributes.ts rename to src/file/drawing/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-properties/non-visual-properties-attributes.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-properties/non-visual-properties.ts b/src/file/drawing/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-properties/non-visual-properties.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-properties/non-visual-properties.ts rename to src/file/drawing/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-properties/non-visual-properties.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/pic-attributes.ts b/src/file/drawing/graphic/graphic-data/pic/pic-attributes.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/pic-attributes.ts rename to src/file/drawing/graphic/graphic-data/pic/pic-attributes.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/pic.ts b/src/file/drawing/graphic/graphic-data/pic/pic.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/pic.ts rename to src/file/drawing/graphic/graphic-data/pic/pic.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/extents/extents-attributes.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/form/extents/extents-attributes.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/extents/extents-attributes.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/form/extents/extents-attributes.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/extents/extents.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/form/extents/extents.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/extents/extents.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/form/extents/extents.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/form.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/form/form.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/form.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/form/form.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/index.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/form/index.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/index.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/form/index.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/offset/off-attributes.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/form/offset/off-attributes.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/offset/off-attributes.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/form/offset/off-attributes.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/offset/off.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/form/offset/off.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/offset/off.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/form/offset/off.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/no-fill.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/no-fill.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/no-fill.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/no-fill.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/outline/no-fill.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/outline/no-fill.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/outline/no-fill.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/outline/no-fill.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/outline/outline.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/outline/outline.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/outline/outline.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/outline/outline.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/preset-geometry/adjustment-values/adjustment-values.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/preset-geometry/adjustment-values/adjustment-values.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/preset-geometry/adjustment-values/adjustment-values.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/preset-geometry/adjustment-values/adjustment-values.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/preset-geometry/preset-geometry-attributes.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/preset-geometry/preset-geometry-attributes.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/preset-geometry/preset-geometry-attributes.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/preset-geometry/preset-geometry-attributes.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/preset-geometry/preset-geometry.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/preset-geometry/preset-geometry.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/preset-geometry/preset-geometry.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/preset-geometry/preset-geometry.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/shape-properties-attributes.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/shape-properties-attributes.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/shape-properties-attributes.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/shape-properties-attributes.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/shape-properties.ts b/src/file/drawing/graphic/graphic-data/pic/shape-properties/shape-properties.ts similarity index 100% rename from src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/shape-properties.ts rename to src/file/drawing/graphic/graphic-data/pic/shape-properties/shape-properties.ts diff --git a/src/file/drawing/inline/graphic/index.ts b/src/file/drawing/graphic/index.ts similarity index 100% rename from src/file/drawing/inline/graphic/index.ts rename to src/file/drawing/graphic/index.ts diff --git a/src/file/drawing/inline/inline.ts b/src/file/drawing/inline/inline.ts index 6bde46d8d9..819bbe2e45 100644 --- a/src/file/drawing/inline/inline.ts +++ b/src/file/drawing/inline/inline.ts @@ -1,11 +1,11 @@ // http://officeopenxml.com/drwPicInline.php import { IMediaDataDimensions } from "file/media"; import { XmlComponent } from "file/xml-components"; -import { DocProperties } from "./doc-properties/doc-properties"; -import { EffectExtent } from "./effect-extent/effect-extent"; -import { Extent } from "./extent/extent"; -import { Graphic } from "./graphic"; -import { GraphicFrameProperties } from "./graphic-frame/graphic-frame-properties"; +import { DocProperties } from "./../doc-properties/doc-properties"; +import { EffectExtent } from "./../effect-extent/effect-extent"; +import { Extent } from "./../extent/extent"; +import { Graphic } from "./../graphic"; +import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties"; import { InlineAttributes } from "./inline-attributes"; export class Inline extends XmlComponent { diff --git a/src/file/xml-components/imported-xml-component.ts b/src/file/xml-components/imported-xml-component.ts index 41ad025ab5..b4cefc053c 100644 --- a/src/file/xml-components/imported-xml-component.ts +++ b/src/file/xml-components/imported-xml-component.ts @@ -111,10 +111,10 @@ export class ImportedXmlComponent extends XmlComponent { static fromXmlString(importedContent: string): ImportedXmlComponent { const imported = fastXmlParser.parse(importedContent, parseOptions); const elementName = Object.keys(imported)[0]; - + const converted = convertToXmlComponent(elementName, imported[elementName]); - if (Array.isArray(converted) && converted.length > 1) { + if (Array.isArray(converted) && converted.length > 1) { throw new Error("Invalid conversion, input must be one element."); } return Array.isArray(converted) ? converted[0] : converted; From 97b254ee7b8dd665b76cb438e6ae1591e5fee9f0 Mon Sep 17 00:00:00 2001 From: Igor Bulovski Date: Fri, 8 Jun 2018 16:03:04 +0200 Subject: [PATCH 07/23] add support for floating drawings - added need elements and test for them --- src/file/drawing/anchor/anchor-attributes.ts | 26 ++++ src/file/drawing/anchor/anchor.spec.ts | 118 ++++++++++++++++++ src/file/drawing/anchor/anchor.ts | 88 +++++++++++++ src/file/drawing/anchor/index.ts | 2 + src/file/drawing/drawing.spec.ts | 37 ++++-- src/file/drawing/drawing.ts | 38 +++++- src/file/drawing/floating/align.spec.ts | 15 +++ src/file/drawing/floating/align.ts | 10 ++ .../drawing/floating/floating-position.ts | 60 +++++++++ .../floating/horizontal-position.spec.ts | 41 ++++++ .../drawing/floating/horizontal-position.ts | 35 ++++++ src/file/drawing/floating/index.ts | 4 + .../drawing/floating/position-offset.spec.ts | 14 +++ src/file/drawing/floating/position-offset.ts | 9 ++ src/file/drawing/floating/simple-pos.spec.ts | 17 +++ src/file/drawing/floating/simple-pos.ts | 28 +++++ .../floating/vertical-position.spec.ts | 41 ++++++ .../drawing/floating/vertical-position.ts | 35 ++++++ src/file/drawing/index.ts | 4 +- src/file/drawing/inline/inline-attributes.ts | 8 +- src/file/drawing/text-wrap/index.ts | 5 + src/file/drawing/text-wrap/text-wrapping.ts | 22 ++++ src/file/drawing/text-wrap/wrap-none.ts | 8 ++ src/file/drawing/text-wrap/wrap-square.ts | 31 +++++ src/file/drawing/text-wrap/wrap-tight.ts | 33 +++++ .../drawing/text-wrap/wrap-top-and-bottom.ts | 33 +++++ src/file/paragraph/run/picture-run.ts | 5 +- 27 files changed, 746 insertions(+), 21 deletions(-) create mode 100644 src/file/drawing/anchor/anchor-attributes.ts create mode 100644 src/file/drawing/anchor/anchor.spec.ts create mode 100644 src/file/drawing/anchor/anchor.ts create mode 100644 src/file/drawing/anchor/index.ts create mode 100644 src/file/drawing/floating/align.spec.ts create mode 100644 src/file/drawing/floating/align.ts create mode 100644 src/file/drawing/floating/floating-position.ts create mode 100644 src/file/drawing/floating/horizontal-position.spec.ts create mode 100644 src/file/drawing/floating/horizontal-position.ts create mode 100644 src/file/drawing/floating/index.ts create mode 100644 src/file/drawing/floating/position-offset.spec.ts create mode 100644 src/file/drawing/floating/position-offset.ts create mode 100644 src/file/drawing/floating/simple-pos.spec.ts create mode 100644 src/file/drawing/floating/simple-pos.ts create mode 100644 src/file/drawing/floating/vertical-position.spec.ts create mode 100644 src/file/drawing/floating/vertical-position.ts create mode 100644 src/file/drawing/text-wrap/index.ts create mode 100644 src/file/drawing/text-wrap/text-wrapping.ts create mode 100644 src/file/drawing/text-wrap/wrap-none.ts create mode 100644 src/file/drawing/text-wrap/wrap-square.ts create mode 100644 src/file/drawing/text-wrap/wrap-tight.ts create mode 100644 src/file/drawing/text-wrap/wrap-top-and-bottom.ts diff --git a/src/file/drawing/anchor/anchor-attributes.ts b/src/file/drawing/anchor/anchor-attributes.ts new file mode 100644 index 0000000000..abe8aac5d7 --- /dev/null +++ b/src/file/drawing/anchor/anchor-attributes.ts @@ -0,0 +1,26 @@ +import { XmlAttributeComponent } from "file/xml-components"; +import { Distance } from "../drawing"; + +export interface IAnchorAttributes extends Distance { + allowOverlap?: "0" | "1"; + behindDoc?: "0" | "1"; + layoutInCell?: "0" | "1"; + locked?: "0" | "1"; + relativeHeight?: number; + simplePos?: "0" | "1"; +} + +export class AnchorAttributes extends XmlAttributeComponent { + protected xmlKeys = { + distT: "distT", + distB: "distB", + distL: "distL", + distR: "distR", + allowOverlap: "allowOverlap", + behindDoc: "behindDoc", + layoutInCell: "layoutInCell", + locked: "locked", + relativeHeight: "relativeHeight", + simplePos: "simplePos", + }; +} diff --git a/src/file/drawing/anchor/anchor.spec.ts b/src/file/drawing/anchor/anchor.spec.ts new file mode 100644 index 0000000000..a071377fab --- /dev/null +++ b/src/file/drawing/anchor/anchor.spec.ts @@ -0,0 +1,118 @@ +import { assert } from "chai"; + +import { Utility } from "../../../tests/utility"; +import { DrawingOptions, TextWrapStyle } from ".././"; +import { Anchor } from "./"; + +function createDrawing(drawingOptions: DrawingOptions) { + return new Anchor( + 1, + { + pixels: { + x: 100, + y: 100, + }, + emus: { + x: 100 * 9525, + y: 100 * 9525, + }, + }, + drawingOptions, + ); +} + +describe("Anchor", () => { + let anchor: Anchor; + + describe("#constructor()", () => { + it("should create a Drawing with correct root key", () => { + anchor = createDrawing({}); + const newJson = Utility.jsonify(anchor); + assert.equal(newJson.rootKey, "wp:anchor"); + assert.equal(newJson.root.length, 10); + }); + + it("should create a Drawing with all default options", () => { + anchor = createDrawing({}); + const newJson = Utility.jsonify(anchor); + assert.equal(newJson.root.length, 10); + + const anchorAttributes = newJson.root[0].root; + assert.include(anchorAttributes, { + distT: 0, + distB: 0, + distL: 0, + distR: 0, + simplePos: "0", + allowOverlap: "1", + behindDoc: "0", + locked: "0", + layoutInCell: "1", + relativeHeight: 952500, + }); + + // 1: simple pos + assert.equal(newJson.root[1].rootKey, "wp:simplePos"); + + // 2: horizontal position + const horizontalPosition = newJson.root[2]; + assert.equal(horizontalPosition.rootKey, "wp:positionH"); + assert.include(horizontalPosition.root[0].root, { + relativeFrom: "column", + }); + assert.equal(horizontalPosition.root[1].rootKey, "wp:posOffset"); + assert.include(horizontalPosition.root[1].root[0], 0); + + // 3: vertical position + const verticalPosition = newJson.root[3]; + assert.equal(verticalPosition.rootKey, "wp:positionV"); + assert.include(verticalPosition.root[0].root, { + relativeFrom: "paragraph", + }); + assert.equal(verticalPosition.root[1].rootKey, "wp:posOffset"); + assert.include(verticalPosition.root[1].root[0], 0); + + // 4: extent + const extent = newJson.root[4]; + assert.equal(extent.rootKey, "wp:extent"); + assert.include(extent.root[0].root, { + cx: 952500, + cy: 952500, + }); + + // 5: effect extent + const effectExtent = newJson.root[5]; + assert.equal(effectExtent.rootKey, "wp:effectExtent"); + + // 6 text wrap: none + const textWrap = newJson.root[6]; + assert.equal(textWrap.rootKey, "wp:wrapNone"); + + // 7: doc properties + const docProperties = newJson.root[7]; + assert.equal(docProperties.rootKey, "wp:docPr"); + + // 8: graphic frame properties + const graphicFrame = newJson.root[8]; + assert.equal(graphicFrame.rootKey, "wp:cNvGraphicFramePr"); + + // 9: graphic + const graphic = newJson.root[9]; + assert.equal(graphic.rootKey, "a:graphic"); + }); + + it("should create a Drawing with text wrapping", () => { + anchor = createDrawing({ + textWrapping: { + textWrapStyle: TextWrapStyle.SQUARE, + }, + }); + const newJson = Utility.jsonify(anchor); + assert.equal(newJson.root.length, 10); + + // 6 text wrap: square + const textWrap = newJson.root[6]; + assert.equal(textWrap.rootKey, "wp:wrapSquare"); + }); + }); +}); diff --git a/src/file/drawing/anchor/anchor.ts b/src/file/drawing/anchor/anchor.ts new file mode 100644 index 0000000000..ad7a266aa2 --- /dev/null +++ b/src/file/drawing/anchor/anchor.ts @@ -0,0 +1,88 @@ +// http://officeopenxml.com/drwPicFloating.php +import { IMediaDataDimensions } from "file/media"; +import { XmlComponent } from "file/xml-components"; +import { DocProperties } from "./../doc-properties/doc-properties"; +import { EffectExtent } from "./../effect-extent/effect-extent"; +import { Extent } from "./../extent/extent"; +import { Graphic } from "./../graphic"; +import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties"; +import { AnchorAttributes } from "./anchor-attributes"; +import { DrawingOptions } from "../drawing"; +import { + SimplePos, + HorizontalPosition, + VerticalPosition, + Floating, + VerticalPositionRelativeFrom, + HorizontalPositionRelativeFrom, +} from "../floating"; +import { WrapNone, TextWrapStyle, WrapSquare, WrapTight, WrapTopAndBottom } from "../text-wrap"; + +const defaultOptions: Floating = { + allowOverlap: true, + behindDocument: false, + lockAnchor: false, + layoutInCell: true, + verticalPosition: { + relative: VerticalPositionRelativeFrom.PARAGRAPH, + offset: 0, + }, + horizontalPosition: { + relative: HorizontalPositionRelativeFrom.COLUMN, + offset: 0, + }, +}; + +export class Anchor extends XmlComponent { + constructor(referenceId: number, dimensions: IMediaDataDimensions, drawingOptions: DrawingOptions) { + super("wp:anchor"); + + const floating = { + ...defaultOptions, + ...drawingOptions.floating, + }; + this.root.push( + new AnchorAttributes({ + distT: 0, + distB: 0, + distL: 0, + distR: 0, + simplePos: "0", // note: word doesn't fully support - so we use 0 + allowOverlap: floating.allowOverlap === true ? "1" : "0", + behindDoc: floating.behindDocument === true ? "1" : "0", + locked: floating.lockAnchor === true ? "1" : "0", + layoutInCell: floating.layoutInCell === true ? "1" : "0", + relativeHeight: dimensions.emus.y, + }), + ); + + this.root.push(new SimplePos()); + this.root.push(new HorizontalPosition(floating.horizontalPosition)); + this.root.push(new VerticalPosition(floating.verticalPosition)); + this.root.push(new Extent(dimensions.emus.x, dimensions.emus.y)); + this.root.push(new EffectExtent()); + + if (drawingOptions.textWrapping != null) { + switch (drawingOptions.textWrapping.textWrapStyle) { + case TextWrapStyle.SQUARE: + this.root.push(new WrapSquare(drawingOptions.textWrapping)); + break; + case TextWrapStyle.TIGHT: + this.root.push(new WrapTight(drawingOptions.textWrapping.distanceFromText)); + break; + case TextWrapStyle.TOP_AND_BOTTOM: + this.root.push(new WrapTopAndBottom(drawingOptions.textWrapping.distanceFromText)); + break; + case TextWrapStyle.NONE: + default: + this.root.push(new WrapNone()); + } + } else { + this.root.push(new WrapNone()); + } + + this.root.push(new DocProperties()); + this.root.push(new GraphicFrameProperties()); + this.root.push(new Graphic(referenceId, dimensions.emus.x, dimensions.emus.y)); + } +} diff --git a/src/file/drawing/anchor/index.ts b/src/file/drawing/anchor/index.ts new file mode 100644 index 0000000000..57faf47fc0 --- /dev/null +++ b/src/file/drawing/anchor/index.ts @@ -0,0 +1,2 @@ +export * from "./anchor"; +export * from "./anchor-attributes"; diff --git a/src/file/drawing/drawing.spec.ts b/src/file/drawing/drawing.spec.ts index 9b113da0bf..1e70aecda5 100644 --- a/src/file/drawing/drawing.spec.ts +++ b/src/file/drawing/drawing.spec.ts @@ -2,14 +2,12 @@ import { assert } from "chai"; import * as fs from "fs"; import { Utility } from "../../tests/utility"; -import { Drawing } from "./"; +import { Drawing, DrawingOptions, PlacementPosition } from "./"; -describe("Drawing", () => { - let currentBreak: Drawing; - - beforeEach(() => { - const path = "./demo/images/image1.jpeg"; - currentBreak = new Drawing({ +function createDrawing(drawingOptions?: DrawingOptions) { + const path = "./demo/images/image1.jpeg"; + return new Drawing( + { fileName: "test.jpg", referenceId: 1, stream: fs.createReadStream(path), @@ -24,14 +22,33 @@ describe("Drawing", () => { y: 100 * 9525, }, }, - }); - }); + }, + drawingOptions, + ); +} + +describe("Drawing", () => { + let currentBreak: Drawing; describe("#constructor()", () => { it("should create a Drawing with correct root key", () => { + currentBreak = createDrawing(); const newJson = Utility.jsonify(currentBreak); assert.equal(newJson.rootKey, "w:drawing"); - // console.log(JSON.stringify(newJson, null, 2)); + }); + + it("should create a drawing with inline element when there are no options passed", () => { + currentBreak = createDrawing(); + const newJson = Utility.jsonify(currentBreak); + assert.equal(newJson.root[0].rootKey, "wp:inline"); + }); + + it("should create a drawing with anchor element when there options are passed", () => { + currentBreak = createDrawing({ + position: PlacementPosition.FLOATING, + }); + const newJson = Utility.jsonify(currentBreak); + assert.equal(newJson.root[0].rootKey, "wp:anchor"); }); }); }); diff --git a/src/file/drawing/drawing.ts b/src/file/drawing/drawing.ts index e6dab2f4d1..55607fadbb 100644 --- a/src/file/drawing/drawing.ts +++ b/src/file/drawing/drawing.ts @@ -1,15 +1,49 @@ import { IMediaData } from "file/media"; import { XmlComponent } from "file/xml-components"; import { Inline } from "./inline"; +import { Anchor } from "./anchor"; +import { TextWrapping } from "./text-wrap"; +import { Floating } from "./floating"; + +export enum PlacementPosition { + INLINE, + FLOATING, +} + +export interface Distance { + distT?: number; + distB?: number; + distL?: number; + distR?: number; +} + +export interface DrawingOptions { + position?: PlacementPosition; + textWrapping?: TextWrapping; + floating?: Floating; +} + +const defaultDrawingOptions: DrawingOptions = { + position: PlacementPosition.INLINE, +}; export class Drawing extends XmlComponent { - constructor(imageData: IMediaData) { + constructor(imageData: IMediaData, drawingOptions?: DrawingOptions) { super("w:drawing"); if (imageData === undefined) { throw new Error("imageData cannot be undefined"); } - this.root.push(new Inline(imageData.referenceId, imageData.dimensions)); + const mergedOptions = { + ...defaultDrawingOptions, + ...drawingOptions, + }; + + if (mergedOptions.position === PlacementPosition.INLINE) { + this.root.push(new Inline(imageData.referenceId, imageData.dimensions)); + } else if (mergedOptions.position === PlacementPosition.FLOATING) { + this.root.push(new Anchor(imageData.referenceId, imageData.dimensions, mergedOptions)); + } } } diff --git a/src/file/drawing/floating/align.spec.ts b/src/file/drawing/floating/align.spec.ts new file mode 100644 index 0000000000..119366d30f --- /dev/null +++ b/src/file/drawing/floating/align.spec.ts @@ -0,0 +1,15 @@ +import { assert } from "chai"; + +import { Align } from "./align"; +import { Utility } from "../../../tests/utility"; +import { VerticalPositionAlign } from "."; + +describe("Align", () => { + describe("#constructor()", () => { + it("should create a element with correct root key", () => { + const newJson = Utility.jsonify(new Align(VerticalPositionAlign.CENTER)); + assert.equal(newJson.rootKey, "wp:align"); + assert.include(newJson.root[0], VerticalPositionAlign.CENTER); + }); + }); +}); diff --git a/src/file/drawing/floating/align.ts b/src/file/drawing/floating/align.ts new file mode 100644 index 0000000000..2ffa4ac52b --- /dev/null +++ b/src/file/drawing/floating/align.ts @@ -0,0 +1,10 @@ +// http://officeopenxml.com/drwPicFloating-position.php +import { XmlComponent } from "file/xml-components"; +import { HorizontalPositionAlign, VerticalPositionAlign } from "./floating-position"; + +export class Align extends XmlComponent { + constructor(value: HorizontalPositionAlign | VerticalPositionAlign) { + super("wp:align"); + this.root.push(value); + } +} diff --git a/src/file/drawing/floating/floating-position.ts b/src/file/drawing/floating/floating-position.ts new file mode 100644 index 0000000000..f9c4547776 --- /dev/null +++ b/src/file/drawing/floating/floating-position.ts @@ -0,0 +1,60 @@ +// http://officeopenxml.com/drwPicFloating-position.php + +export enum HorizontalPositionRelativeFrom { + CHARACTER = "character", + COLUMN = "column", + INSIDE_MARGIN = "insideMargin", + LEFT_MARGIN = "leftMargin", + MARGIN = "margin", + OUTSIDE_MARGIN = "outsideMargin", + PAGE = "page", + RIGHT_MARGIN = "rightMargin", +} + +export enum VerticalPositionRelativeFrom { + BOTTOM_MARGIN = "bottomMargin", + INSIDE_MARGIN = "insideMargin", + LINE = "line", + MARGIN = "margin", + OUTSIDE_MARGIN = "outsideMargin", + PAGE = "page", + PARAGRAPH = "paragraph", + TOP_MARGIN = "topMargin", +} + +export enum HorizontalPositionAlign { + CENTER = "center", + INSIDE = "inside", + LEFT = "left", + OUTSIDE = "outside", + RIGHT = "right", +} + +export enum VerticalPositionAlign { + BOTTOM = "bottom", + CENTER = "center", + INSIDE = "inside", + OUTSIDE = "outside", + TOP = "top", +} + +export interface HorizontalPositionOptions { + relative: HorizontalPositionRelativeFrom; + align?: HorizontalPositionAlign; + offset?: number; +} + +export interface VerticalPositionOptions { + relative: VerticalPositionRelativeFrom; + align?: VerticalPositionAlign; + offset?: number; +} + +export interface Floating { + horizontalPosition: HorizontalPositionOptions; + verticalPosition: VerticalPositionOptions; + allowOverlap?: boolean; + lockAnchor?: boolean; + behindDocument?: boolean; + layoutInCell?: boolean; +} diff --git a/src/file/drawing/floating/horizontal-position.spec.ts b/src/file/drawing/floating/horizontal-position.spec.ts new file mode 100644 index 0000000000..69415685e1 --- /dev/null +++ b/src/file/drawing/floating/horizontal-position.spec.ts @@ -0,0 +1,41 @@ +import { assert } from "chai"; + +import { HorizontalPosition } from "./horizontal-position"; +import { Utility } from "../../../tests/utility"; +import { HorizontalPositionRelativeFrom, HorizontalPositionAlign } from "."; + +describe("HorizontalPosition", () => { + describe("#constructor()", () => { + it("should create a element with position align", () => { + const newJson = Utility.jsonify( + new HorizontalPosition({ + relative: HorizontalPositionRelativeFrom.MARGIN, + align: HorizontalPositionAlign.CENTER, + }), + ); + assert.equal(newJson.rootKey, "wp:positionH"); + assert.include(newJson.root[0].root, { + relativeFrom: "margin", + }); + + assert.equal(newJson.root[1].rootKey, "wp:align"); + assert.include(newJson.root[1].root, "center"); + }); + + it("should create a element with offset", () => { + const newJson = Utility.jsonify( + new HorizontalPosition({ + relative: HorizontalPositionRelativeFrom.MARGIN, + offset: 40, + }), + ); + assert.equal(newJson.rootKey, "wp:positionH"); + assert.include(newJson.root[0].root, { + relativeFrom: "margin", + }); + + assert.equal(newJson.root[1].rootKey, "wp:posOffset"); + assert.include(newJson.root[1].root[0], 40); + }); + }); +}); diff --git a/src/file/drawing/floating/horizontal-position.ts b/src/file/drawing/floating/horizontal-position.ts new file mode 100644 index 0000000000..5b0a0e68ca --- /dev/null +++ b/src/file/drawing/floating/horizontal-position.ts @@ -0,0 +1,35 @@ +// http://officeopenxml.com/drwPicFloating-position.php +import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; +import { HorizontalPositionRelativeFrom, HorizontalPositionOptions } from "./floating-position"; +import { Align } from "./align"; +import { PositionOffset } from "./position-offset"; + +interface IHorizontalPositionAttributes { + relativeFrom: HorizontalPositionRelativeFrom; +} + +class HorizontalPositionAttributes extends XmlAttributeComponent { + protected xmlKeys = { + relativeFrom: "relativeFrom", + }; +} + +export class HorizontalPosition extends XmlComponent { + constructor(horizontalPosition: HorizontalPositionOptions) { + super("wp:positionH"); + + this.root.push( + new HorizontalPositionAttributes({ + relativeFrom: horizontalPosition.relative, + }), + ); + + if (horizontalPosition.align) { + this.root.push(new Align(horizontalPosition.align)); + } else if (horizontalPosition.offset !== undefined) { + this.root.push(new PositionOffset(horizontalPosition.offset)); + } else { + throw new Error("There is no configuration provided for floating position (Align or offset)"); + } + } +} diff --git a/src/file/drawing/floating/index.ts b/src/file/drawing/floating/index.ts new file mode 100644 index 0000000000..80061d16e1 --- /dev/null +++ b/src/file/drawing/floating/index.ts @@ -0,0 +1,4 @@ +export * from "./floating-position"; +export * from "./simple-pos"; +export * from "./horizontal-position"; +export * from "./vertical-position"; diff --git a/src/file/drawing/floating/position-offset.spec.ts b/src/file/drawing/floating/position-offset.spec.ts new file mode 100644 index 0000000000..e0360c1716 --- /dev/null +++ b/src/file/drawing/floating/position-offset.spec.ts @@ -0,0 +1,14 @@ +import { assert } from "chai"; + +import { PositionOffset } from "./position-offset"; +import { Utility } from "../../../tests/utility"; + +describe("PositionOffset", () => { + describe("#constructor()", () => { + it("should create a element with correct root key", () => { + const newJson = Utility.jsonify(new PositionOffset(50)); + assert.equal(newJson.rootKey, "wp:posOffset"); + assert.equal(newJson.root[0], 50); + }); + }); +}); diff --git a/src/file/drawing/floating/position-offset.ts b/src/file/drawing/floating/position-offset.ts new file mode 100644 index 0000000000..4d3aa96b07 --- /dev/null +++ b/src/file/drawing/floating/position-offset.ts @@ -0,0 +1,9 @@ +// http://officeopenxml.com/drwPicFloating-position.php +import { XmlComponent } from "file/xml-components"; + +export class PositionOffset extends XmlComponent { + constructor(offsetValue: number) { + super("wp:posOffset"); + this.root.push(offsetValue.toString()); + } +} diff --git a/src/file/drawing/floating/simple-pos.spec.ts b/src/file/drawing/floating/simple-pos.spec.ts new file mode 100644 index 0000000000..a86739b7b0 --- /dev/null +++ b/src/file/drawing/floating/simple-pos.spec.ts @@ -0,0 +1,17 @@ +import { assert } from "chai"; + +import { SimplePos } from "./simple-pos"; +import { Utility } from "../../../tests/utility"; + +describe("SimplePos", () => { + describe("#constructor()", () => { + it("should create a element with correct root key", () => { + const newJson = Utility.jsonify(new SimplePos()); + assert.equal(newJson.rootKey, "wp:simplePos"); + assert.include(newJson.root[0].root, { + x: 0, + y: 0, + }); + }); + }); +}); diff --git a/src/file/drawing/floating/simple-pos.ts b/src/file/drawing/floating/simple-pos.ts new file mode 100644 index 0000000000..b5e8df70d3 --- /dev/null +++ b/src/file/drawing/floating/simple-pos.ts @@ -0,0 +1,28 @@ +// http://officeopenxml.com/drwPicFloating-position.php +import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; + +interface ISimplePosAttributes { + x: number; + y: number; +} + +class SimplePosAttributes extends XmlAttributeComponent { + protected xmlKeys = { + x: "x", + y: "y", + }; +} + +export class SimplePos extends XmlComponent { + constructor() { + super("wp:simplePos"); + + // NOTE: It's not fully supported in Microsoft Word, but this element is needed anyway + this.root.push( + new SimplePosAttributes({ + x: 0, + y: 0, + }), + ); + } +} diff --git a/src/file/drawing/floating/vertical-position.spec.ts b/src/file/drawing/floating/vertical-position.spec.ts new file mode 100644 index 0000000000..13e1ae2a4c --- /dev/null +++ b/src/file/drawing/floating/vertical-position.spec.ts @@ -0,0 +1,41 @@ +import { assert } from "chai"; + +import { VerticalPosition } from "./vertical-position"; +import { Utility } from "../../../tests/utility"; +import { VerticalPositionRelativeFrom, VerticalPositionAlign } from "."; + +describe("VerticalPosition", () => { + describe("#constructor()", () => { + it("should create a element with position align", () => { + const newJson = Utility.jsonify( + new VerticalPosition({ + relative: VerticalPositionRelativeFrom.MARGIN, + align: VerticalPositionAlign.INSIDE, + }), + ); + assert.equal(newJson.rootKey, "wp:positionV"); + assert.include(newJson.root[0].root, { + relativeFrom: "margin", + }); + + assert.equal(newJson.root[1].rootKey, "wp:align"); + assert.include(newJson.root[1].root, "inside"); + }); + + it("should create a element with offset", () => { + const newJson = Utility.jsonify( + new VerticalPosition({ + relative: VerticalPositionRelativeFrom.MARGIN, + offset: 40, + }), + ); + assert.equal(newJson.rootKey, "wp:positionV"); + assert.include(newJson.root[0].root, { + relativeFrom: "margin", + }); + + assert.equal(newJson.root[1].rootKey, "wp:posOffset"); + assert.include(newJson.root[1].root[0], 40); + }); + }); +}); diff --git a/src/file/drawing/floating/vertical-position.ts b/src/file/drawing/floating/vertical-position.ts new file mode 100644 index 0000000000..4c6826221d --- /dev/null +++ b/src/file/drawing/floating/vertical-position.ts @@ -0,0 +1,35 @@ +// http://officeopenxml.com/drwPicFloating-position.php +import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; +import { VerticalPositionRelativeFrom, VerticalPositionOptions } from "./floating-position"; +import { Align } from "./align"; +import { PositionOffset } from "./position-offset"; + +interface IVerticalPositionAttributes { + relativeFrom: VerticalPositionRelativeFrom; +} + +class VerticalPositionAttributes extends XmlAttributeComponent { + protected xmlKeys = { + relativeFrom: "relativeFrom", + }; +} + +export class VerticalPosition extends XmlComponent { + constructor(verticalPosition: VerticalPositionOptions) { + super("wp:positionV"); + + this.root.push( + new VerticalPositionAttributes({ + relativeFrom: verticalPosition.relative, + }), + ); + + if (verticalPosition.align) { + this.root.push(new Align(verticalPosition.align)); + } else if (verticalPosition.offset !== undefined) { + this.root.push(new PositionOffset(verticalPosition.offset)); + } else { + throw new Error("There is no configuration provided for floating position (Align or offset)"); + } + } +} diff --git a/src/file/drawing/index.ts b/src/file/drawing/index.ts index ba96e11de9..8a1a62a201 100644 --- a/src/file/drawing/index.ts +++ b/src/file/drawing/index.ts @@ -1 +1,3 @@ -export { Drawing } from "./drawing"; +export * from "./drawing"; +export * from "./text-wrap"; +export * from "./floating"; diff --git a/src/file/drawing/inline/inline-attributes.ts b/src/file/drawing/inline/inline-attributes.ts index 1a4ef74e3c..c177fd9ea7 100644 --- a/src/file/drawing/inline/inline-attributes.ts +++ b/src/file/drawing/inline/inline-attributes.ts @@ -1,11 +1,7 @@ import { XmlAttributeComponent } from "file/xml-components"; +import { Distance } from "../drawing"; -export interface IInlineAttributes { - distT?: number; - distB?: number; - distL?: number; - distR?: number; -} +export interface IInlineAttributes extends Distance {} export class InlineAttributes extends XmlAttributeComponent { protected xmlKeys = { diff --git a/src/file/drawing/text-wrap/index.ts b/src/file/drawing/text-wrap/index.ts new file mode 100644 index 0000000000..ce8c0bbd13 --- /dev/null +++ b/src/file/drawing/text-wrap/index.ts @@ -0,0 +1,5 @@ +export * from "./text-wrapping"; +export * from "./wrap-none"; +export * from "./wrap-square"; +export * from "./wrap-tight"; +export * from "./wrap-top-and-bottom"; diff --git a/src/file/drawing/text-wrap/text-wrapping.ts b/src/file/drawing/text-wrap/text-wrapping.ts new file mode 100644 index 0000000000..4894ca76da --- /dev/null +++ b/src/file/drawing/text-wrap/text-wrapping.ts @@ -0,0 +1,22 @@ +// http://officeopenxml.com/drwPicFloating-textWrap.php +import { Distance } from "../drawing"; + +export enum TextWrapStyle { + NONE, + SQUARE, + TIGHT, + TOP_AND_BOTTOM, +} + +export enum WrapTextOption { + BOTH_SIDES = "bothSides", + LEFT = "left", + RIGHT = "right", + LARGEST = "largest", +} + +export interface TextWrapping { + textWrapStyle: TextWrapStyle; + wrapTextOption?: WrapTextOption; + distanceFromText?: Distance; +} diff --git a/src/file/drawing/text-wrap/wrap-none.ts b/src/file/drawing/text-wrap/wrap-none.ts new file mode 100644 index 0000000000..0ac4c632f0 --- /dev/null +++ b/src/file/drawing/text-wrap/wrap-none.ts @@ -0,0 +1,8 @@ +// http://officeopenxml.com/drwPicFloating-textWrap.php +import { XmlComponent } from "file/xml-components"; + +export class WrapNone extends XmlComponent { + constructor() { + super("wp:wrapNone"); + } +} diff --git a/src/file/drawing/text-wrap/wrap-square.ts b/src/file/drawing/text-wrap/wrap-square.ts new file mode 100644 index 0000000000..5f8b7aef57 --- /dev/null +++ b/src/file/drawing/text-wrap/wrap-square.ts @@ -0,0 +1,31 @@ +// http://officeopenxml.com/drwPicFloating-textWrap.php +import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; +import { TextWrapping, WrapTextOption } from "."; +import { Distance } from "../drawing"; + +interface IWrapSquareAttributes extends Distance { + wrapText?: WrapTextOption; +} + +class WrapSquareAttributes extends XmlAttributeComponent { + protected xmlKeys = { + distT: "distT", + distB: "distB", + distL: "distL", + distR: "distR", + wrapText: "wrapText", + }; +} + +export class WrapSquare extends XmlComponent { + constructor(textWrapping: TextWrapping) { + super("wp:wrapSquare"); + + this.root.push( + new WrapSquareAttributes({ + wrapText: textWrapping.wrapTextOption || WrapTextOption.BOTH_SIDES, + ...textWrapping.distanceFromText, + }), + ); + } +} diff --git a/src/file/drawing/text-wrap/wrap-tight.ts b/src/file/drawing/text-wrap/wrap-tight.ts new file mode 100644 index 0000000000..0703ebaaff --- /dev/null +++ b/src/file/drawing/text-wrap/wrap-tight.ts @@ -0,0 +1,33 @@ +// http://officeopenxml.com/drwPicFloating-textWrap.php +import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; +import { Distance } from "../drawing"; + +interface IWrapTightAttributes { + distT?: number; + distB?: number; +} + +class WrapTightAttributes extends XmlAttributeComponent { + protected xmlKeys = { + distT: "distT", + distB: "distB", + }; +} + +export class WrapTight extends XmlComponent { + constructor(distanceFromText?: Distance) { + super("wp:wrapTight"); + + distanceFromText = distanceFromText || { + distT: 0, + distB: 0, + }; + + this.root.push( + new WrapTightAttributes({ + distT: distanceFromText.distT, + distB: distanceFromText.distB, + }), + ); + } +} diff --git a/src/file/drawing/text-wrap/wrap-top-and-bottom.ts b/src/file/drawing/text-wrap/wrap-top-and-bottom.ts new file mode 100644 index 0000000000..a161058783 --- /dev/null +++ b/src/file/drawing/text-wrap/wrap-top-and-bottom.ts @@ -0,0 +1,33 @@ +// http://officeopenxml.com/drwPicFloating-textWrap.php +import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; +import { Distance } from "../drawing"; + +interface IWrapTopAndBottomAttributes { + distT?: number; + distB?: number; +} + +class WrapTopAndBottomAttributes extends XmlAttributeComponent { + protected xmlKeys = { + distT: "distT", + distB: "distB", + }; +} + +export class WrapTopAndBottom extends XmlComponent { + constructor(distanceFromText?: Distance) { + super("wp:wrapTopAndBottom"); + + distanceFromText = distanceFromText || { + distT: 0, + distB: 0, + }; + + this.root.push( + new WrapTopAndBottomAttributes({ + distT: distanceFromText.distT, + distB: distanceFromText.distB, + }), + ); + } +} diff --git a/src/file/paragraph/run/picture-run.ts b/src/file/paragraph/run/picture-run.ts index b949dc023e..fac9ae3354 100644 --- a/src/file/paragraph/run/picture-run.ts +++ b/src/file/paragraph/run/picture-run.ts @@ -1,15 +1,16 @@ import { Drawing } from "../../drawing"; import { IMediaData } from "../../media/data"; import { Run } from "../run"; +import { DrawingOptions } from "../../drawing/drawing"; export class PictureRun extends Run { - constructor(imageData: IMediaData) { + constructor(imageData: IMediaData, drawingOptions?: DrawingOptions) { super(); if (imageData === undefined) { throw new Error("imageData cannot be undefined"); } - this.root.push(new Drawing(imageData)); + this.root.push(new Drawing(imageData, drawingOptions)); } } From b8b5d1866248891130e4a8a246f6098760ac135e Mon Sep 17 00:00:00 2001 From: Igor Bulovski Date: Fri, 8 Jun 2018 22:21:10 +0200 Subject: [PATCH 08/23] version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9795a35e25..cfebd90317 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "docx-h4", - "version": "3.2.10", + "version": "3.2.11", "description": "Generate .docx documents with JavaScript (formerly Office-Clippy)", "main": "build/index.js", "scripts": { From deccfd7cc9f0b7001047413d5fb2f2bd28046b69 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sat, 9 Jun 2018 23:33:52 +0100 Subject: [PATCH 09/23] Add back scale functionality --- src/file/drawing/drawing.ts | 10 +++++++++- src/file/paragraph/run/picture-run.ts | 20 ++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/file/drawing/drawing.ts b/src/file/drawing/drawing.ts index 55607fadbb..4646d0cc87 100644 --- a/src/file/drawing/drawing.ts +++ b/src/file/drawing/drawing.ts @@ -28,6 +28,8 @@ const defaultDrawingOptions: DrawingOptions = { }; export class Drawing extends XmlComponent { + private inline: Inline; + constructor(imageData: IMediaData, drawingOptions?: DrawingOptions) { super("w:drawing"); @@ -40,10 +42,16 @@ export class Drawing extends XmlComponent { ...drawingOptions, }; + if (mergedOptions.position === PlacementPosition.INLINE) { - this.root.push(new Inline(imageData.referenceId, imageData.dimensions)); + this.inline = new Inline(imageData.referenceId, imageData.dimensions); + this.root.push(this.inline); } else if (mergedOptions.position === PlacementPosition.FLOATING) { this.root.push(new Anchor(imageData.referenceId, imageData.dimensions, mergedOptions)); } } + + public scale(factorX: number, factorY: number): void { + this.inline.scale(factorX, factorY); + } } diff --git a/src/file/paragraph/run/picture-run.ts b/src/file/paragraph/run/picture-run.ts index fac9ae3354..711aa36f05 100644 --- a/src/file/paragraph/run/picture-run.ts +++ b/src/file/paragraph/run/picture-run.ts @@ -1,9 +1,11 @@ import { Drawing } from "../../drawing"; +import { DrawingOptions } from "../../drawing/drawing"; import { IMediaData } from "../../media/data"; import { Run } from "../run"; -import { DrawingOptions } from "../../drawing/drawing"; export class PictureRun extends Run { + private drawing: Drawing; + constructor(imageData: IMediaData, drawingOptions?: DrawingOptions) { super(); @@ -11,6 +13,20 @@ export class PictureRun extends Run { throw new Error("imageData cannot be undefined"); } - this.root.push(new Drawing(imageData, drawingOptions)); + this.drawing = new Drawing(imageData, drawingOptions); + + this.root.push(this.drawing); + } + + public scale(factorX: number, factorY?: number): void { + if (!factorX) { + factorX = 1; + } + + if (!factorY) { + factorY = factorX; + } + + this.drawing.scale(factorX, factorY); } } From 80cdaaeb1c2c830481973e9f34f750b94d04bf29 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sat, 9 Jun 2018 23:34:54 +0100 Subject: [PATCH 10/23] Fix compile error --- src/file/media/media.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/file/media/media.ts b/src/file/media/media.ts index 4fc7cc2cd6..ee4ca9ae15 100644 --- a/src/file/media/media.ts +++ b/src/file/media/media.ts @@ -11,7 +11,6 @@ export class Media { this.map = new Map(); } - private createMedia(key: string, relationshipsCount, dimensions, data: fs.ReadStream | Buffer, filePath?: string) { public getMedia(key: string): IMediaData { const data = this.map.get(key); From 55005b57c63fd3872cbf0e986fed69b16a9782f1 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sat, 9 Jun 2018 23:49:01 +0100 Subject: [PATCH 11/23] Fix linting and style errors --- src/file/drawing/anchor/anchor-attributes.ts | 4 +-- src/file/drawing/anchor/anchor.spec.ts | 4 +-- src/file/drawing/anchor/anchor.ts | 26 +++++++++---------- src/file/drawing/drawing.spec.ts | 4 +-- src/file/drawing/drawing.ts | 19 +++++++------- src/file/drawing/floating/align.spec.ts | 4 +-- .../drawing/floating/floating-position.ts | 10 +++---- .../floating/horizontal-position.spec.ts | 4 +-- .../drawing/floating/horizontal-position.ts | 6 ++--- .../drawing/floating/position-offset.spec.ts | 2 +- src/file/drawing/floating/simple-pos.ts | 2 +- .../floating/vertical-position.spec.ts | 4 +-- .../drawing/floating/vertical-position.ts | 6 ++--- .../graphic-data/graphic-data-attribute.ts | 0 .../graphic/graphic-data/graphic-data.ts | 0 .../graphic/graphic-data/index.ts | 0 .../graphic-data/pic/blip/blip-fill.ts | 0 .../graphic/graphic-data/pic/blip/blip.ts | 0 .../graphic-data/pic/blip/source-rectangle.ts | 0 .../graphic/graphic-data/pic/blip/stretch.ts | 0 .../graphic/graphic-data/pic/index.ts | 0 .../child-non-visual-pic-properties.ts | 0 .../pic-locks/pic-locks-attributes.ts | 0 .../pic-locks/pic-locks.ts | 0 .../non-visual-pic-properties.ts | 0 .../non-visual-properties-attributes.ts | 0 .../non-visual-properties.ts | 0 .../graphic-data/pic/pic-attributes.ts | 0 .../graphic/graphic-data/pic/pic.ts | 0 .../form/extents/extents-attributes.ts | 0 .../shape-properties/form/extents/extents.ts | 0 .../pic/shape-properties/form/form.ts | 0 .../pic/shape-properties/form/index.ts | 0 .../form/offset/off-attributes.ts | 0 .../pic/shape-properties/form/offset/off.ts | 0 .../pic/shape-properties/no-fill.ts | 0 .../pic/shape-properties/outline/no-fill.ts | 0 .../pic/shape-properties/outline/outline.ts | 0 .../adjustment-values/adjustment-values.ts | 0 .../preset-geometry-attributes.ts | 0 .../preset-geometry/preset-geometry.ts | 0 .../shape-properties-attributes.ts | 0 .../pic/shape-properties/shape-properties.ts | 0 .../drawing/{ => inline}/graphic/index.ts | 0 src/file/drawing/inline/inline-attributes.ts | 5 ++-- src/file/drawing/inline/inline.ts | 2 +- src/file/drawing/text-wrap/text-wrapping.ts | 6 ++--- src/file/drawing/text-wrap/wrap-square.ts | 10 +++---- src/file/drawing/text-wrap/wrap-tight.ts | 6 ++--- .../drawing/text-wrap/wrap-top-and-bottom.ts | 6 ++--- src/file/footer-wrapper.ts | 4 +-- src/file/header-wrapper.ts | 4 +-- src/file/paragraph/run/picture-run.ts | 4 +-- src/file/styles/external-styles-factory.ts | 4 +-- .../xml-components/imported-xml-component.ts | 1 + 55 files changed, 74 insertions(+), 73 deletions(-) rename src/file/drawing/{ => inline}/graphic/graphic-data/graphic-data-attribute.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/graphic-data.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/index.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/blip/blip-fill.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/blip/blip.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/blip/source-rectangle.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/blip/stretch.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/index.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/child-non-visual-pic-properties.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/pic-locks/pic-locks-attributes.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/non-visual-pic-properties/child-non-visual-pic-properties/pic-locks/pic-locks.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-pic-properties.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-properties/non-visual-properties-attributes.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/non-visual-pic-properties/non-visual-properties/non-visual-properties.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/pic-attributes.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/pic.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/form/extents/extents-attributes.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/form/extents/extents.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/form/form.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/form/index.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/form/offset/off-attributes.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/form/offset/off.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/no-fill.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/outline/no-fill.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/outline/outline.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/preset-geometry/adjustment-values/adjustment-values.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/preset-geometry/preset-geometry-attributes.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/preset-geometry/preset-geometry.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/shape-properties-attributes.ts (100%) rename src/file/drawing/{ => inline}/graphic/graphic-data/pic/shape-properties/shape-properties.ts (100%) rename src/file/drawing/{ => inline}/graphic/index.ts (100%) diff --git a/src/file/drawing/anchor/anchor-attributes.ts b/src/file/drawing/anchor/anchor-attributes.ts index abe8aac5d7..cfd8ad3144 100644 --- a/src/file/drawing/anchor/anchor-attributes.ts +++ b/src/file/drawing/anchor/anchor-attributes.ts @@ -1,7 +1,7 @@ import { XmlAttributeComponent } from "file/xml-components"; -import { Distance } from "../drawing"; +import { IDistance } from "../drawing"; -export interface IAnchorAttributes extends Distance { +export interface IAnchorAttributes extends IDistance { allowOverlap?: "0" | "1"; behindDoc?: "0" | "1"; layoutInCell?: "0" | "1"; diff --git a/src/file/drawing/anchor/anchor.spec.ts b/src/file/drawing/anchor/anchor.spec.ts index a071377fab..58adefd174 100644 --- a/src/file/drawing/anchor/anchor.spec.ts +++ b/src/file/drawing/anchor/anchor.spec.ts @@ -1,10 +1,10 @@ import { assert } from "chai"; import { Utility } from "../../../tests/utility"; -import { DrawingOptions, TextWrapStyle } from ".././"; +import { IDrawingOptions, TextWrapStyle } from ".././"; import { Anchor } from "./"; -function createDrawing(drawingOptions: DrawingOptions) { +function createDrawing(drawingOptions: IDrawingOptions): Anchor { return new Anchor( 1, { diff --git a/src/file/drawing/anchor/anchor.ts b/src/file/drawing/anchor/anchor.ts index ad7a266aa2..7dbdfca0bc 100644 --- a/src/file/drawing/anchor/anchor.ts +++ b/src/file/drawing/anchor/anchor.ts @@ -1,24 +1,24 @@ // http://officeopenxml.com/drwPicFloating.php import { IMediaDataDimensions } from "file/media"; import { XmlComponent } from "file/xml-components"; +import { IDrawingOptions } from "../drawing"; +import { + HorizontalPosition, + HorizontalPositionRelativeFrom, + IFloating, + SimplePos, + VerticalPosition, + VerticalPositionRelativeFrom, +} from "../floating"; +import { Graphic } from "../inline/graphic"; +import { TextWrapStyle, WrapNone, WrapSquare, WrapTight, WrapTopAndBottom } from "../text-wrap"; import { DocProperties } from "./../doc-properties/doc-properties"; import { EffectExtent } from "./../effect-extent/effect-extent"; import { Extent } from "./../extent/extent"; -import { Graphic } from "./../graphic"; import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties"; import { AnchorAttributes } from "./anchor-attributes"; -import { DrawingOptions } from "../drawing"; -import { - SimplePos, - HorizontalPosition, - VerticalPosition, - Floating, - VerticalPositionRelativeFrom, - HorizontalPositionRelativeFrom, -} from "../floating"; -import { WrapNone, TextWrapStyle, WrapSquare, WrapTight, WrapTopAndBottom } from "../text-wrap"; -const defaultOptions: Floating = { +const defaultOptions: IFloating = { allowOverlap: true, behindDocument: false, lockAnchor: false, @@ -34,7 +34,7 @@ const defaultOptions: Floating = { }; export class Anchor extends XmlComponent { - constructor(referenceId: number, dimensions: IMediaDataDimensions, drawingOptions: DrawingOptions) { + constructor(referenceId: number, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) { super("wp:anchor"); const floating = { diff --git a/src/file/drawing/drawing.spec.ts b/src/file/drawing/drawing.spec.ts index 1e70aecda5..cf3f926315 100644 --- a/src/file/drawing/drawing.spec.ts +++ b/src/file/drawing/drawing.spec.ts @@ -2,9 +2,9 @@ import { assert } from "chai"; import * as fs from "fs"; import { Utility } from "../../tests/utility"; -import { Drawing, DrawingOptions, PlacementPosition } from "./"; +import { Drawing, IDrawingOptions, PlacementPosition } from "./"; -function createDrawing(drawingOptions?: DrawingOptions) { +function createDrawing(drawingOptions?: IDrawingOptions): Drawing { const path = "./demo/images/image1.jpeg"; return new Drawing( { diff --git a/src/file/drawing/drawing.ts b/src/file/drawing/drawing.ts index 4646d0cc87..4c3db93b3a 100644 --- a/src/file/drawing/drawing.ts +++ b/src/file/drawing/drawing.ts @@ -1,36 +1,36 @@ import { IMediaData } from "file/media"; import { XmlComponent } from "file/xml-components"; -import { Inline } from "./inline"; import { Anchor } from "./anchor"; -import { TextWrapping } from "./text-wrap"; -import { Floating } from "./floating"; +import { IFloating } from "./floating"; +import { Inline } from "./inline"; +import { ITextWrapping } from "./text-wrap"; export enum PlacementPosition { INLINE, FLOATING, } -export interface Distance { +export interface IDistance { distT?: number; distB?: number; distL?: number; distR?: number; } -export interface DrawingOptions { +export interface IDrawingOptions { position?: PlacementPosition; - textWrapping?: TextWrapping; - floating?: Floating; + textWrapping?: ITextWrapping; + floating?: IFloating; } -const defaultDrawingOptions: DrawingOptions = { +const defaultDrawingOptions: IDrawingOptions = { position: PlacementPosition.INLINE, }; export class Drawing extends XmlComponent { private inline: Inline; - constructor(imageData: IMediaData, drawingOptions?: DrawingOptions) { + constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) { super("w:drawing"); if (imageData === undefined) { @@ -42,7 +42,6 @@ export class Drawing extends XmlComponent { ...drawingOptions, }; - if (mergedOptions.position === PlacementPosition.INLINE) { this.inline = new Inline(imageData.referenceId, imageData.dimensions); this.root.push(this.inline); diff --git a/src/file/drawing/floating/align.spec.ts b/src/file/drawing/floating/align.spec.ts index 119366d30f..5ec77d6fd0 100644 --- a/src/file/drawing/floating/align.spec.ts +++ b/src/file/drawing/floating/align.spec.ts @@ -1,8 +1,8 @@ import { assert } from "chai"; -import { Align } from "./align"; -import { Utility } from "../../../tests/utility"; import { VerticalPositionAlign } from "."; +import { Utility } from "../../../tests/utility"; +import { Align } from "./align"; describe("Align", () => { describe("#constructor()", () => { diff --git a/src/file/drawing/floating/floating-position.ts b/src/file/drawing/floating/floating-position.ts index f9c4547776..7039846bc7 100644 --- a/src/file/drawing/floating/floating-position.ts +++ b/src/file/drawing/floating/floating-position.ts @@ -38,21 +38,21 @@ export enum VerticalPositionAlign { TOP = "top", } -export interface HorizontalPositionOptions { +export interface IHorizontalPositionOptions { relative: HorizontalPositionRelativeFrom; align?: HorizontalPositionAlign; offset?: number; } -export interface VerticalPositionOptions { +export interface IVerticalPositionOptions { relative: VerticalPositionRelativeFrom; align?: VerticalPositionAlign; offset?: number; } -export interface Floating { - horizontalPosition: HorizontalPositionOptions; - verticalPosition: VerticalPositionOptions; +export interface IFloating { + horizontalPosition: IHorizontalPositionOptions; + verticalPosition: IVerticalPositionOptions; allowOverlap?: boolean; lockAnchor?: boolean; behindDocument?: boolean; diff --git a/src/file/drawing/floating/horizontal-position.spec.ts b/src/file/drawing/floating/horizontal-position.spec.ts index 69415685e1..1b139b47be 100644 --- a/src/file/drawing/floating/horizontal-position.spec.ts +++ b/src/file/drawing/floating/horizontal-position.spec.ts @@ -1,8 +1,8 @@ import { assert } from "chai"; -import { HorizontalPosition } from "./horizontal-position"; +import { HorizontalPositionAlign, HorizontalPositionRelativeFrom } from "."; import { Utility } from "../../../tests/utility"; -import { HorizontalPositionRelativeFrom, HorizontalPositionAlign } from "."; +import { HorizontalPosition } from "./horizontal-position"; describe("HorizontalPosition", () => { describe("#constructor()", () => { diff --git a/src/file/drawing/floating/horizontal-position.ts b/src/file/drawing/floating/horizontal-position.ts index 5b0a0e68ca..f0725aa857 100644 --- a/src/file/drawing/floating/horizontal-position.ts +++ b/src/file/drawing/floating/horizontal-position.ts @@ -1,7 +1,7 @@ // http://officeopenxml.com/drwPicFloating-position.php -import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; -import { HorizontalPositionRelativeFrom, HorizontalPositionOptions } from "./floating-position"; +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; import { Align } from "./align"; +import { HorizontalPositionRelativeFrom, IHorizontalPositionOptions } from "./floating-position"; import { PositionOffset } from "./position-offset"; interface IHorizontalPositionAttributes { @@ -15,7 +15,7 @@ class HorizontalPositionAttributes extends XmlAttributeComponent { describe("#constructor()", () => { diff --git a/src/file/drawing/floating/simple-pos.ts b/src/file/drawing/floating/simple-pos.ts index b5e8df70d3..6330f6660a 100644 --- a/src/file/drawing/floating/simple-pos.ts +++ b/src/file/drawing/floating/simple-pos.ts @@ -1,5 +1,5 @@ // http://officeopenxml.com/drwPicFloating-position.php -import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; interface ISimplePosAttributes { x: number; diff --git a/src/file/drawing/floating/vertical-position.spec.ts b/src/file/drawing/floating/vertical-position.spec.ts index 13e1ae2a4c..a9d7ed65f8 100644 --- a/src/file/drawing/floating/vertical-position.spec.ts +++ b/src/file/drawing/floating/vertical-position.spec.ts @@ -1,8 +1,8 @@ import { assert } from "chai"; -import { VerticalPosition } from "./vertical-position"; +import { VerticalPositionAlign, VerticalPositionRelativeFrom } from "."; import { Utility } from "../../../tests/utility"; -import { VerticalPositionRelativeFrom, VerticalPositionAlign } from "."; +import { VerticalPosition } from "./vertical-position"; describe("VerticalPosition", () => { describe("#constructor()", () => { diff --git a/src/file/drawing/floating/vertical-position.ts b/src/file/drawing/floating/vertical-position.ts index 4c6826221d..10b6d6028f 100644 --- a/src/file/drawing/floating/vertical-position.ts +++ b/src/file/drawing/floating/vertical-position.ts @@ -1,7 +1,7 @@ // http://officeopenxml.com/drwPicFloating-position.php -import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; -import { VerticalPositionRelativeFrom, VerticalPositionOptions } from "./floating-position"; +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; import { Align } from "./align"; +import { IVerticalPositionOptions, VerticalPositionRelativeFrom } from "./floating-position"; import { PositionOffset } from "./position-offset"; interface IVerticalPositionAttributes { @@ -15,7 +15,7 @@ class VerticalPositionAttributes extends XmlAttributeComponent { protected xmlKeys = { diff --git a/src/file/drawing/inline/inline.ts b/src/file/drawing/inline/inline.ts index a40aed25f2..6e5be2ba13 100644 --- a/src/file/drawing/inline/inline.ts +++ b/src/file/drawing/inline/inline.ts @@ -4,8 +4,8 @@ import { XmlComponent } from "file/xml-components"; import { DocProperties } from "./../doc-properties/doc-properties"; import { EffectExtent } from "./../effect-extent/effect-extent"; import { Extent } from "./../extent/extent"; -import { Graphic } from "./../graphic"; import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties"; +import { Graphic } from "./../inline/graphic"; import { InlineAttributes } from "./inline-attributes"; export class Inline extends XmlComponent { diff --git a/src/file/drawing/text-wrap/text-wrapping.ts b/src/file/drawing/text-wrap/text-wrapping.ts index 4894ca76da..7fc14a52fd 100644 --- a/src/file/drawing/text-wrap/text-wrapping.ts +++ b/src/file/drawing/text-wrap/text-wrapping.ts @@ -1,5 +1,5 @@ // http://officeopenxml.com/drwPicFloating-textWrap.php -import { Distance } from "../drawing"; +import { IDistance } from "../drawing"; export enum TextWrapStyle { NONE, @@ -15,8 +15,8 @@ export enum WrapTextOption { LARGEST = "largest", } -export interface TextWrapping { +export interface ITextWrapping { textWrapStyle: TextWrapStyle; wrapTextOption?: WrapTextOption; - distanceFromText?: Distance; + distanceFromText?: IDistance; } diff --git a/src/file/drawing/text-wrap/wrap-square.ts b/src/file/drawing/text-wrap/wrap-square.ts index 5f8b7aef57..08ed108209 100644 --- a/src/file/drawing/text-wrap/wrap-square.ts +++ b/src/file/drawing/text-wrap/wrap-square.ts @@ -1,9 +1,9 @@ // http://officeopenxml.com/drwPicFloating-textWrap.php -import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; -import { TextWrapping, WrapTextOption } from "."; -import { Distance } from "../drawing"; +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; +import { ITextWrapping, WrapTextOption } from "."; +import { IDistance } from "../drawing"; -interface IWrapSquareAttributes extends Distance { +interface IWrapSquareAttributes extends IDistance { wrapText?: WrapTextOption; } @@ -18,7 +18,7 @@ class WrapSquareAttributes extends XmlAttributeComponent } export class WrapSquare extends XmlComponent { - constructor(textWrapping: TextWrapping) { + constructor(textWrapping: ITextWrapping) { super("wp:wrapSquare"); this.root.push( diff --git a/src/file/drawing/text-wrap/wrap-tight.ts b/src/file/drawing/text-wrap/wrap-tight.ts index 0703ebaaff..cda9a20194 100644 --- a/src/file/drawing/text-wrap/wrap-tight.ts +++ b/src/file/drawing/text-wrap/wrap-tight.ts @@ -1,6 +1,6 @@ // http://officeopenxml.com/drwPicFloating-textWrap.php -import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; -import { Distance } from "../drawing"; +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; +import { IDistance } from "../drawing"; interface IWrapTightAttributes { distT?: number; @@ -15,7 +15,7 @@ class WrapTightAttributes extends XmlAttributeComponent { } export class WrapTight extends XmlComponent { - constructor(distanceFromText?: Distance) { + constructor(distanceFromText?: IDistance) { super("wp:wrapTight"); distanceFromText = distanceFromText || { diff --git a/src/file/drawing/text-wrap/wrap-top-and-bottom.ts b/src/file/drawing/text-wrap/wrap-top-and-bottom.ts index a161058783..bf6a5c3cae 100644 --- a/src/file/drawing/text-wrap/wrap-top-and-bottom.ts +++ b/src/file/drawing/text-wrap/wrap-top-and-bottom.ts @@ -1,6 +1,6 @@ // http://officeopenxml.com/drwPicFloating-textWrap.php -import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; -import { Distance } from "../drawing"; +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; +import { IDistance } from "../drawing"; interface IWrapTopAndBottomAttributes { distT?: number; @@ -15,7 +15,7 @@ class WrapTopAndBottomAttributes extends XmlAttributeComponent Date: Thu, 21 Jun 2018 12:03:34 +0200 Subject: [PATCH 12/23] feature: add support to add mutiple sections to the document inc. headers and footers - write missing tests --- package.json | 1 + src/export/packer/compiler.spec.ts | 76 ++++++++++ src/export/packer/compiler.ts | 40 ++--- src/file/content-types/content-types.spec.ts | 139 ++++++++++++++++++ src/file/content-types/content-types.ts | 14 +- src/file/document/body/body.spec.ts | 57 +++---- src/file/document/body/body.ts | 49 +++++- src/file/document/body/index.ts | 1 + .../footer-reference-attributes.ts | 6 + .../footer-reference/footer-reference.ts | 14 +- .../footer-reference/index.ts | 2 + .../header-reference-attributes.ts | 6 + .../header-reference/header-reference.ts | 13 +- .../header-reference/index.ts | 2 + .../document/body/section-properties/index.ts | 5 + .../section-properties/page-number/index.ts | 1 + .../page-number/page-number.ts | 41 ++++++ .../section-properties/page-size/index.ts | 2 + .../page-size/page-size-attributes.ts | 7 +- .../page-size/page-size.spec.ts | 5 +- .../section-properties/page-size/page-size.ts | 6 +- .../section-properties.spec.ts | 17 +++ .../section-properties/section-properties.ts | 47 +++++- src/file/document/document.spec.ts | 15 +- src/file/document/document.ts | 4 + src/file/document/index.ts | 1 + src/file/file.ts | 105 +++++++++++-- src/file/footer-wrapper.ts | 4 +- src/file/footer/footer.ts | 8 +- src/file/header-wrapper.ts | 4 +- src/file/header/header.ts | 8 +- src/file/index.ts | 1 + src/file/media/media.ts | 12 +- 33 files changed, 611 insertions(+), 102 deletions(-) create mode 100644 src/export/packer/compiler.spec.ts create mode 100644 src/file/content-types/content-types.spec.ts create mode 100644 src/file/document/body/section-properties/footer-reference/index.ts create mode 100644 src/file/document/body/section-properties/header-reference/index.ts create mode 100644 src/file/document/body/section-properties/index.ts create mode 100644 src/file/document/body/section-properties/page-number/index.ts create mode 100644 src/file/document/body/section-properties/page-number/page-number.ts create mode 100644 src/file/document/body/section-properties/page-size/index.ts diff --git a/package.json b/package.json index cfebd90317..2a6ec009e3 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "awesome-typescript-loader": "^3.4.1", "chai": "^3.5.0", "glob": "^7.1.2", + "jszip": "^3.1.5", "mocha": "^3.2.0", "mocha-webpack": "^1.0.1", "prettier": "^1.10.2", diff --git a/src/export/packer/compiler.spec.ts b/src/export/packer/compiler.spec.ts new file mode 100644 index 0000000000..c87a39a0e8 --- /dev/null +++ b/src/export/packer/compiler.spec.ts @@ -0,0 +1,76 @@ +/* tslint:disable:typedef space-before-function-paren */ +import * as fs from "fs"; + +import { Compiler } from "./compiler"; +import { File } from "../../file"; +import { expect } from "chai"; + +import * as JSZip from "jszip"; + +describe("Compiler", () => { + let compiler: Compiler; + let file: File; + + beforeEach(() => { + file = new File(); + compiler = new Compiler(file); + }); + + describe("#compile()", () => { + it("should pack all the content", async function() { + this.timeout(99999999); + const fileName = "build/tests/test.docx"; + await compiler.compile(fs.createWriteStream(fileName)); + + const docxFile = fs.readFileSync(fileName); + const zipFile: JSZip = await JSZip.loadAsync(docxFile); + const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name); + + expect(fileNames).is.an.instanceof(Array); + expect(fileNames).has.length(12); + expect(fileNames).to.include("word/document.xml"); + expect(fileNames).to.include("word/styles.xml"); + expect(fileNames).to.include("docProps/core.xml"); + expect(fileNames).to.include("docProps/app.xml"); + expect(fileNames).to.include("word/numbering.xml"); + expect(fileNames).to.include("word/header1.xml"); + expect(fileNames).to.include("word/_rels/header1.xml.rels"); + expect(fileNames).to.include("word/footer1.xml"); + expect(fileNames).to.include("word/_rels/footer1.xml.rels"); + expect(fileNames).to.include("word/_rels/document.xml.rels"); + expect(fileNames).to.include("[Content_Types].xml"); + expect(fileNames).to.include("_rels/.rels"); + }); + + it("should pack all additional headers and footers", async function() { + file.createFooter(); + file.createFooter(); + file.createHeader(); + file.createHeader(); + + this.timeout(99999999); + const fileName = "build/tests/test2.docx"; + await compiler.compile(fs.createWriteStream(fileName)); + + const docxFile = fs.readFileSync(fileName); + const zipFile: JSZip = await JSZip.loadAsync(docxFile); + const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name); + + expect(fileNames).is.an.instanceof(Array); + expect(fileNames).has.length(20); + + expect(fileNames).to.include("word/header1.xml"); + expect(fileNames).to.include("word/_rels/header1.xml.rels"); + expect(fileNames).to.include("word/header2.xml"); + expect(fileNames).to.include("word/_rels/header2.xml.rels"); + expect(fileNames).to.include("word/header3.xml"); + expect(fileNames).to.include("word/_rels/header3.xml.rels"); + expect(fileNames).to.include("word/footer1.xml"); + expect(fileNames).to.include("word/_rels/footer1.xml.rels"); + expect(fileNames).to.include("word/footer2.xml"); + expect(fileNames).to.include("word/_rels/footer2.xml.rels"); + expect(fileNames).to.include("word/footer3.xml"); + expect(fileNames).to.include("word/_rels/footer3.xml.rels"); + }); + }); +}); diff --git a/src/export/packer/compiler.ts b/src/export/packer/compiler.ts index 44c7817249..456976d9d6 100644 --- a/src/export/packer/compiler.ts +++ b/src/export/packer/compiler.ts @@ -33,10 +33,6 @@ export class Compiler { const xmlNumbering = xml(this.formatter.format(this.file.Numbering)); const xmlRelationships = xml(this.formatter.format(this.file.DocumentRelationships)); const xmlFileRelationships = xml(this.formatter.format(this.file.FileRelationships)); - const xmlHeader = xml(this.formatter.format(this.file.Header.Header)); - const xmlFooter = xml(this.formatter.format(this.file.Footer.Footer)); - const xmlHeaderRelationships = xml(this.formatter.format(this.file.Header.Relationships)); - const xmlFooterRelationships = xml(this.formatter.format(this.file.Footer.Relationships)); const xmlContentTypes = xml(this.formatter.format(this.file.ContentTypes)); const xmlAppProperties = xml(this.formatter.format(this.file.AppProperties)); @@ -60,26 +56,34 @@ export class Compiler { name: "word/numbering.xml", }); - this.archive.append(xmlHeader, { - name: "word/header1.xml", - }); + // headers + for (let i = 0; i < this.file.Headers.length; i++) { + const element = this.file.Headers[i]; + this.archive.append(xml(this.formatter.format(element.Header)), { + name: `word/header${i + 1}.xml`, + }); - this.archive.append(xmlFooter, { - name: "word/footer1.xml", - }); + this.archive.append(xml(this.formatter.format(element.Relationships)), { + name: `word/_rels/header${i + 1}.xml.rels`, + }); + } + + // footers + for (let i = 0; i < this.file.Footers.length; i++) { + const element = this.file.Footers[i]; + this.archive.append(xml(this.formatter.format(element.Footer)), { + name: `word/footer${i + 1}.xml`, + }); + + this.archive.append(xml(this.formatter.format(element.Relationships)), { + name: `word/_rels/footer${i + 1}.xml.rels`, + }); + } this.archive.append(xmlRelationships, { name: "word/_rels/document.xml.rels", }); - this.archive.append(xmlHeaderRelationships, { - name: "word/_rels/header1.xml.rels", - }); - - this.archive.append(xmlFooterRelationships, { - name: "word/_rels/footer1.xml.rels", - }); - this.archive.append(xmlContentTypes, { name: "[Content_Types].xml", }); diff --git a/src/file/content-types/content-types.spec.ts b/src/file/content-types/content-types.spec.ts new file mode 100644 index 0000000000..8a755d3924 --- /dev/null +++ b/src/file/content-types/content-types.spec.ts @@ -0,0 +1,139 @@ +import { expect } from "chai"; +import { Formatter } from "../../export/formatter"; +import { ContentTypes } from "./content-types"; +describe("ContentTypes", () => { + let contentTypes: ContentTypes; + + beforeEach(() => { + contentTypes = new ContentTypes(); + }); + + describe("#constructor()", () => { + it("should create default content types", () => { + const tree = new Formatter().format(contentTypes); + + expect(tree["Types"]).to.be.an.instanceof(Array); + + expect(tree["Types"][0]).to.deep.equal({ _attr: { xmlns: "http://schemas.openxmlformats.org/package/2006/content-types" } }); + expect(tree["Types"][1]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/png", Extension: "png" } }] }); + expect(tree["Types"][2]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/jpeg", Extension: "jpeg" } }] }); + expect(tree["Types"][3]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/jpeg", Extension: "jpg" } }] }); + expect(tree["Types"][4]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/bmp", Extension: "bmp" } }] }); + expect(tree["Types"][5]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/gif", Extension: "gif" } }] }); + expect(tree["Types"][6]).to.deep.equal({ + Default: [{ _attr: { ContentType: "application/vnd.openxmlformats-package.relationships+xml", Extension: "rels" } }], + }); + expect(tree["Types"][7]).to.deep.equal({ Default: [{ _attr: { ContentType: "application/xml", Extension: "xml" } }] }); + expect(tree["Types"][8]).to.deep.equal({ + Override: [ + { + _attr: { + ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", + PartName: "/word/document.xml", + }, + }, + ], + }); + expect(tree["Types"][9]).to.deep.equal({ + Override: [ + { + _attr: { + ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", + PartName: "/word/styles.xml", + }, + }, + ], + }); + expect(tree["Types"][10]).to.deep.equal({ + Override: [ + { + _attr: { + ContentType: "application/vnd.openxmlformats-package.core-properties+xml", + PartName: "/docProps/core.xml", + }, + }, + ], + }); + expect(tree["Types"][11]).to.deep.equal({ + Override: [ + { + _attr: { + ContentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml", + PartName: "/docProps/app.xml", + }, + }, + ], + }); + expect(tree["Types"][12]).to.deep.equal({ + Override: [ + { + _attr: { + ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", + PartName: "/word/numbering.xml", + }, + }, + ], + }); + }); + }); + + describe("#addFooter()", () => { + it("should add footer", () => { + contentTypes.addFooter(101); + contentTypes.addFooter(102); + const tree = new Formatter().format(contentTypes); + + expect(tree["Types"][13]).to.deep.equal({ + Override: [ + { + _attr: { + ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", + PartName: "/word/footer101.xml", + }, + }, + ], + }); + + expect(tree["Types"][14]).to.deep.equal({ + Override: [ + { + _attr: { + ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", + PartName: "/word/footer102.xml", + }, + }, + ], + }); + }); + }); + + describe("#addHeader()", () => { + it("should add header", () => { + contentTypes.addHeader(201); + contentTypes.addHeader(202); + const tree = new Formatter().format(contentTypes); + + expect(tree["Types"][13]).to.deep.equal({ + Override: [ + { + _attr: { + ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", + PartName: "/word/header201.xml", + }, + }, + ], + }); + + expect(tree["Types"][14]).to.deep.equal({ + Override: [ + { + _attr: { + ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", + PartName: "/word/header202.xml", + }, + }, + ], + }); + }); + }); +}); diff --git a/src/file/content-types/content-types.ts b/src/file/content-types/content-types.ts index 4ce020a918..bfc7f6d8d0 100644 --- a/src/file/content-types/content-types.ts +++ b/src/file/content-types/content-types.ts @@ -24,11 +24,21 @@ export class ContentTypes extends XmlComponent { this.root.push( new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", "/word/document.xml"), ); - this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", "/word/header1.xml")); - this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", "/word/footer1.xml")); this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", "/word/styles.xml")); this.root.push(new Override("application/vnd.openxmlformats-package.core-properties+xml", "/docProps/core.xml")); this.root.push(new Override("application/vnd.openxmlformats-officedocument.extended-properties+xml", "/docProps/app.xml")); this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", "/word/numbering.xml")); } + + addFooter(index: number) { + this.root.push( + new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", `/word/footer${index}.xml`), + ); + } + + addHeader(index: number) { + this.root.push( + new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", `/word/header${index}.xml`), + ); + } } diff --git a/src/file/document/body/body.spec.ts b/src/file/document/body/body.spec.ts index c5498bc99a..31c1e49e32 100644 --- a/src/file/document/body/body.spec.ts +++ b/src/file/document/body/body.spec.ts @@ -1,39 +1,42 @@ -// import { assert } from "chai"; +import { expect } from "chai"; -// import { Utility } from "../../../tests/utility"; -// import { Body } from "./"; +import { Formatter } from "../../../export/formatter"; +import { Body } from "./body"; describe("Body", () => { - // let body: Body; + let body: Body; beforeEach(() => { - // body = new Body(); + body = new Body(); }); - // describe("#constructor()", () => { - // it("should create the Section Properties", () => { - // const newJson = Utility.jsonify(body); - // assert.equal(newJson.root[0].rootKey, "w:sectPr"); - // }); + describe("#constructor()", () => { + it("should create default section", () => { + const formatted = new Formatter().format(body)["w:body"][0]; + expect(formatted) + .to.have.property("w:sectPr") + .and.to.be.an.instanceof(Array); + expect(formatted["w:sectPr"]).to.have.length(7); + }); + }); - // it("should create the Page Size", () => { - // const newJson = Utility.jsonify(body); - // assert.equal(newJson.root[1].rootKey, "w:pgSz"); - // }); + describe("addSection", () => { + it("should add section with options", () => { + body.addSection({ + width: 10000, + height: 10000, + }); - // it("should create the Page Margin", () => { - // const newJson = Utility.jsonify(body); - // assert.equal(newJson.root[2].rootKey, "w:pgMar"); - // }); + const formatted = new Formatter().format(body)["w:body"]; + expect(formatted).to.be.an.instanceof(Array); + const defaultSectionPr = formatted[0]["w:p"][1]["w:pPr"][0]["w:sectPr"]; - // it("should create the Columns", () => { - // const newJson = Utility.jsonify(body); - // assert.equal(newJson.root[3].rootKey, "w:cols"); - // }); + // check that this is the default section and added first in paragraph + expect(defaultSectionPr[0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] }); - // it("should create the Document Grid", () => { - // const newJson = Utility.jsonify(body); - // assert.equal(newJson.root[4].rootKey, "w:docGrid"); - // }); - // }); + // check for new section (since it's the last one, it's direct child of body) + const newSection = formatted[1]["w:sectPr"]; + expect(newSection[0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 10000, "w:w": 10000, "w:orient": "portrait" } }] }); + }); + }); }); diff --git a/src/file/document/body/body.ts b/src/file/document/body/body.ts index fa30099f75..691dfd7518 100644 --- a/src/file/document/body/body.ts +++ b/src/file/document/body/body.ts @@ -1,14 +1,59 @@ -import { XmlComponent } from "file/xml-components"; +import { XmlComponent, IXmlableObject } from "file/xml-components"; import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties"; +import { Paragraph, ParagraphProperties } from "../.."; export class Body extends XmlComponent { + private defaultSection: SectionProperties; + + private sections: SectionProperties[] = []; + constructor(sectionPropertiesOptions?: SectionPropertiesOptions) { super("w:body"); - this.root.push(new SectionProperties(sectionPropertiesOptions)); + this.defaultSection = new SectionProperties(sectionPropertiesOptions); + this.sections.push(this.defaultSection); + } + + /** + * Adds new section properties. + * Note: Previous section is created in paragraph after the current element, and then new section will be added. + * The spec says: + * - section element should be in the last paragraph of the section + * - last section should be direct child of body + * @param section new section + */ + addSection(section: SectionPropertiesOptions | SectionProperties) { + const currentSection = this.sections.pop() as SectionProperties; + this.root.push(this.createSectionParagraph(currentSection)); + if (section instanceof SectionProperties) { + this.sections.push(section); + } else { + this.sections.push(new SectionProperties(section)); + } + } + public prepForXml(): IXmlableObject { + if (this.sections.length === 1) { + this.root.push(this.sections[0]); + } else if (this.sections.length > 1) { + throw new Error("Invalid usage of sections. At the end of the body element there must be ONE section."); + } + + return super.prepForXml(); } public push(component: XmlComponent): void { this.root.push(component); } + + get DefaultSection() { + return this.defaultSection; + } + + private createSectionParagraph(section: SectionProperties) { + const paragraph = new Paragraph(); + const properties = new ParagraphProperties(); + properties.addChildElement(section); + paragraph.addChildElement(properties); + return paragraph; + } } diff --git a/src/file/document/body/index.ts b/src/file/document/body/index.ts index 93f4529388..83678ef8ea 100644 --- a/src/file/document/body/index.ts +++ b/src/file/document/body/index.ts @@ -1 +1,2 @@ export * from "./body"; +export * from "./section-properties"; diff --git a/src/file/document/body/section-properties/footer-reference/footer-reference-attributes.ts b/src/file/document/body/section-properties/footer-reference/footer-reference-attributes.ts index 0097de0dbb..763053e36a 100644 --- a/src/file/document/body/section-properties/footer-reference/footer-reference-attributes.ts +++ b/src/file/document/body/section-properties/footer-reference/footer-reference-attributes.ts @@ -1,5 +1,11 @@ import { XmlAttributeComponent } from "file/xml-components"; +export enum FooterReferenceType { + DEFAULT = "default", + FIRST = "first", + EVEN = "even", +} + export interface IFooterReferenceAttributes { type: string; id: string; diff --git a/src/file/document/body/section-properties/footer-reference/footer-reference.ts b/src/file/document/body/section-properties/footer-reference/footer-reference.ts index 6b7012b6d3..3805676841 100644 --- a/src/file/document/body/section-properties/footer-reference/footer-reference.ts +++ b/src/file/document/body/section-properties/footer-reference/footer-reference.ts @@ -1,13 +1,19 @@ import { XmlComponent } from "file/xml-components"; -import { FooterReferenceAttributes } from "./footer-reference-attributes"; +import { FooterReferenceAttributes, FooterReferenceType } from "./footer-reference-attributes"; + +export interface FooterOptions { + footerType?: FooterReferenceType; + footerId?: number; +} export class FooterReference extends XmlComponent { - constructor() { + constructor(options: FooterOptions) { super("w:footerReference"); + this.root.push( new FooterReferenceAttributes({ - type: "default", - id: `rId${4}`, + type: options.footerType || FooterReferenceType.DEFAULT, + id: `rId${options.footerId}`, }), ); } diff --git a/src/file/document/body/section-properties/footer-reference/index.ts b/src/file/document/body/section-properties/footer-reference/index.ts new file mode 100644 index 0000000000..9673319fba --- /dev/null +++ b/src/file/document/body/section-properties/footer-reference/index.ts @@ -0,0 +1,2 @@ +export * from "./footer-reference"; +export * from "./footer-reference-attributes"; diff --git a/src/file/document/body/section-properties/header-reference/header-reference-attributes.ts b/src/file/document/body/section-properties/header-reference/header-reference-attributes.ts index b0407c4a0a..5569fa76b9 100644 --- a/src/file/document/body/section-properties/header-reference/header-reference-attributes.ts +++ b/src/file/document/body/section-properties/header-reference/header-reference-attributes.ts @@ -1,5 +1,11 @@ import { XmlAttributeComponent } from "file/xml-components"; +export enum HeaderReferenceType { + DEFAULT = "default", + FIRST = "first", + EVEN = "even", +} + export interface IHeaderReferenceAttributes { type: string; id: string; diff --git a/src/file/document/body/section-properties/header-reference/header-reference.ts b/src/file/document/body/section-properties/header-reference/header-reference.ts index 3809047bef..4065ed5253 100644 --- a/src/file/document/body/section-properties/header-reference/header-reference.ts +++ b/src/file/document/body/section-properties/header-reference/header-reference.ts @@ -1,13 +1,18 @@ import { XmlComponent } from "file/xml-components"; -import { HeaderReferenceAttributes } from "./header-reference-attributes"; +import { HeaderReferenceAttributes, HeaderReferenceType } from "./header-reference-attributes"; + +export interface HeaderOptions { + headerType?: HeaderReferenceType; + headerId?: number; +} export class HeaderReference extends XmlComponent { - constructor() { + constructor(options: HeaderOptions) { super("w:headerReference"); this.root.push( new HeaderReferenceAttributes({ - type: "default", - id: `rId${3}`, + type: options.headerType || HeaderReferenceType.DEFAULT, + id: `rId${options.headerId}`, }), ); } diff --git a/src/file/document/body/section-properties/header-reference/index.ts b/src/file/document/body/section-properties/header-reference/index.ts new file mode 100644 index 0000000000..80239ad98e --- /dev/null +++ b/src/file/document/body/section-properties/header-reference/index.ts @@ -0,0 +1,2 @@ +export * from "./header-reference"; +export * from "./header-reference-attributes"; diff --git a/src/file/document/body/section-properties/index.ts b/src/file/document/body/section-properties/index.ts new file mode 100644 index 0000000000..f1b5eabb84 --- /dev/null +++ b/src/file/document/body/section-properties/index.ts @@ -0,0 +1,5 @@ +export * from "./section-properties"; +export * from "./footer-reference"; +export * from "./header-reference"; +export * from "./page-size"; +export * from "./page-number"; diff --git a/src/file/document/body/section-properties/page-number/index.ts b/src/file/document/body/section-properties/page-number/index.ts new file mode 100644 index 0000000000..57e81d8724 --- /dev/null +++ b/src/file/document/body/section-properties/page-number/index.ts @@ -0,0 +1 @@ +export * from "./page-number"; diff --git a/src/file/document/body/section-properties/page-number/page-number.ts b/src/file/document/body/section-properties/page-number/page-number.ts new file mode 100644 index 0000000000..918b1e644c --- /dev/null +++ b/src/file/document/body/section-properties/page-number/page-number.ts @@ -0,0 +1,41 @@ +// http://officeopenxml.com/WPSectionPgNumType.php +import { XmlComponent, XmlAttributeComponent } from "file/xml-components"; + +export enum PageNumberFormat { + CARDINAL_TEXT = "cardinalText", + DECIMAL = "decimal", + DECIMAL_ENCLOSED_CIRCLE = "decimalEnclosedCircle", + DECIMAL_ENCLOSED_FULL_STOP = "decimalEnclosedFullstop", + DECIMAL_ENCLOSED_PAREN = "decimalEnclosedParen", + DECIMAL_ZERO = "decimalZero", + LOWER_LETTER = "lowerLetter", + LOWER_ROMAN = "lowerRoman", + NONE = "none", + ORDINAL_TEXT = "ordinalText", + UPPER_LETTER = "upperLetter", + UPPER_ROMAN = "upperRoman", +} + +export interface IPageNumberTypeAttributes { + pageNumberStart?: number; + pageNumberFormatType?: PageNumberFormat; +} + +export class PageNumberTypeAttributes extends XmlAttributeComponent { + protected xmlKeys = { + pageNumberStart: "w:start", + pageNumberFormatType: "w:fmt", + }; +} + +export class PageNumberType extends XmlComponent { + constructor(start?: number, numberFormat?: PageNumberFormat) { + super("w:pgNumType"); + this.root.push( + new PageNumberTypeAttributes({ + pageNumberStart: start, + pageNumberFormatType: numberFormat, + }), + ); + } +} diff --git a/src/file/document/body/section-properties/page-size/index.ts b/src/file/document/body/section-properties/page-size/index.ts new file mode 100644 index 0000000000..567f7c2d58 --- /dev/null +++ b/src/file/document/body/section-properties/page-size/index.ts @@ -0,0 +1,2 @@ +export * from "./page-size"; +export * from "./page-size-attributes"; diff --git a/src/file/document/body/section-properties/page-size/page-size-attributes.ts b/src/file/document/body/section-properties/page-size/page-size-attributes.ts index 5a3cd90907..4af206ac2d 100644 --- a/src/file/document/body/section-properties/page-size/page-size-attributes.ts +++ b/src/file/document/body/section-properties/page-size/page-size-attributes.ts @@ -1,9 +1,14 @@ import { XmlAttributeComponent } from "file/xml-components"; +export enum PageOrientation { + PORTRAIT = "portrait", + LANDSCAPE = "landscape", +} + export interface IPageSizeAttributes { width?: number; height?: number; - orientation?: string; + orientation?: PageOrientation; } export class PageSizeAttributes extends XmlAttributeComponent { diff --git a/src/file/document/body/section-properties/page-size/page-size.spec.ts b/src/file/document/body/section-properties/page-size/page-size.spec.ts index 4ebcf989c0..bd5b17aaa5 100644 --- a/src/file/document/body/section-properties/page-size/page-size.spec.ts +++ b/src/file/document/body/section-properties/page-size/page-size.spec.ts @@ -2,11 +2,12 @@ import { expect } from "chai"; import { Formatter } from "../../../../../export/formatter"; import { PageSize } from "./page-size"; +import { PageOrientation } from "./page-size-attributes"; describe("PageSize", () => { describe("#constructor()", () => { it("should create page size with portrait", () => { - const properties = new PageSize(100, 200, "portrait"); + const properties = new PageSize(100, 200, PageOrientation.PORTRAIT); const tree = new Formatter().format(properties); expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]); @@ -15,7 +16,7 @@ describe("PageSize", () => { }); it("should create page size with horizontal and invert the lengths", () => { - const properties = new PageSize(100, 200, "landscape"); + const properties = new PageSize(100, 200, PageOrientation.LANDSCAPE); const tree = new Formatter().format(properties); expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]); diff --git a/src/file/document/body/section-properties/page-size/page-size.ts b/src/file/document/body/section-properties/page-size/page-size.ts index ea93e1ebc0..bf5499d3f1 100644 --- a/src/file/document/body/section-properties/page-size/page-size.ts +++ b/src/file/document/body/section-properties/page-size/page-size.ts @@ -1,11 +1,11 @@ import { XmlComponent } from "file/xml-components"; -import { PageSizeAttributes } from "./page-size-attributes"; +import { PageSizeAttributes, PageOrientation } from "./page-size-attributes"; export class PageSize extends XmlComponent { - constructor(width: number, height: number, orientation: string) { + constructor(width: number, height: number, orientation: PageOrientation) { super("w:pgSz"); - const flip = orientation === "landscape"; + const flip = orientation === PageOrientation.LANDSCAPE; this.root.push( new PageSizeAttributes({ diff --git a/src/file/document/body/section-properties/section-properties.spec.ts b/src/file/document/body/section-properties/section-properties.spec.ts index 994db13891..05a49d09d9 100644 --- a/src/file/document/body/section-properties/section-properties.spec.ts +++ b/src/file/document/body/section-properties/section-properties.spec.ts @@ -2,6 +2,7 @@ import { expect } from "chai"; import { Formatter } from "../../../../export/formatter"; import { SectionProperties } from "./section-properties"; +import { FooterReferenceType, PageNumberFormat } from "."; describe("SectionProperties", () => { describe("#constructor()", () => { @@ -18,6 +19,11 @@ describe("SectionProperties", () => { gutter: 0, space: 708, linePitch: 360, + headerId: 100, + footerId: 200, + footerType: FooterReferenceType.EVEN, + pageNumberStart: 10, + pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT, }); const tree = new Formatter().format(properties); expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]); @@ -38,6 +44,12 @@ describe("SectionProperties", () => { }, ], }); + + expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] }); + expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] }); + expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": [{ _attr: { "r:id": "rId100", "w:type": "default" } }] }); + expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": [{ _attr: { "r:id": "rId200", "w:type": "even" } }] }); + expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "cardinalText", "w:start": 10 } }] }); }); it("should create section properties with no options", () => { @@ -61,6 +73,11 @@ describe("SectionProperties", () => { }, ], }); + expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] }); + expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] }); + expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": [{ _attr: { "r:id": "rId0", "w:type": "default" } }] }); + expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": [{ _attr: { "r:id": "rId0", "w:type": "default" } }] }); + expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] }); }); it("should create section properties with changed options", () => { diff --git a/src/file/document/body/section-properties/section-properties.ts b/src/file/document/body/section-properties/section-properties.ts index 27d3f0bd99..993812bd5e 100644 --- a/src/file/document/body/section-properties/section-properties.ts +++ b/src/file/document/body/section-properties/section-properties.ts @@ -4,16 +4,25 @@ import { Columns } from "./columns/columns"; import { IColumnsAttributes } from "./columns/columns-attributes"; import { DocumentGrid } from "./doc-grid/doc-grid"; import { IDocGridAttributesProperties } from "./doc-grid/doc-grid-attributes"; -import { FooterReference } from "./footer-reference/footer-reference"; -import { HeaderReference } from "./header-reference/header-reference"; +import { FooterReference, FooterOptions } from "./footer-reference/footer-reference"; +import { HeaderReference, HeaderOptions } from "./header-reference/header-reference"; import { PageMargin } from "./page-margin/page-margin"; import { IPageMarginAttributes } from "./page-margin/page-margin-attributes"; import { PageSize } from "./page-size/page-size"; -import { IPageSizeAttributes } from "./page-size/page-size-attributes"; +import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes"; +import { FooterReferenceType, IPageNumberTypeAttributes, PageNumberType, PageNumberFormat } from "."; +import { HeaderReferenceType } from "./header-reference/header-reference-attributes"; -export type SectionPropertiesOptions = IPageSizeAttributes & IPageMarginAttributes & IColumnsAttributes & IDocGridAttributesProperties; +export type SectionPropertiesOptions = IPageSizeAttributes & + IPageMarginAttributes & + IColumnsAttributes & + IDocGridAttributesProperties & + HeaderOptions & + FooterOptions & + IPageNumberTypeAttributes; export class SectionProperties extends XmlComponent { + private options: SectionPropertiesOptions; constructor(options?: SectionPropertiesOptions) { super("w:sectPr"); @@ -29,7 +38,13 @@ export class SectionProperties extends XmlComponent { gutter: 0, space: 708, linePitch: 360, - orientation: "portrait", + orientation: PageOrientation.PORTRAIT, + headerType: HeaderReferenceType.DEFAULT, + headerId: 0, + footerType: FooterReferenceType.DEFAULT, + footerId: 0, + pageNumberStart: undefined, + pageNumberFormatType: PageNumberFormat.DECIMAL, }; const mergedOptions = { @@ -51,7 +66,25 @@ export class SectionProperties extends XmlComponent { ); this.root.push(new Columns(mergedOptions.space)); this.root.push(new DocumentGrid(mergedOptions.linePitch)); - this.root.push(new HeaderReference()); - this.root.push(new FooterReference()); + this.root.push( + new HeaderReference({ + headerType: mergedOptions.headerType, + headerId: mergedOptions.headerId, + }), + ); + this.root.push( + new FooterReference({ + footerType: mergedOptions.footerType, + footerId: mergedOptions.footerId, + }), + ); + + this.root.push(new PageNumberType(mergedOptions.pageNumberStart, mergedOptions.pageNumberFormatType)); + + this.options = mergedOptions; + } + + get Options() { + return this.options; } } diff --git a/src/file/document/document.spec.ts b/src/file/document/document.spec.ts index 218ff55a98..e2e8781d7a 100644 --- a/src/file/document/document.spec.ts +++ b/src/file/document/document.spec.ts @@ -23,6 +23,11 @@ describe("Document", () => { } assert.isTrue(true); }); + + it("should create default section", () => { + const body = new Formatter().format(document)["w:document"][1]["w:body"]; + expect(body[0]).to.have.property("w:sectPr"); + }); }); describe("#createParagraph", () => { @@ -33,7 +38,7 @@ describe("Document", () => { expect(body) .to.be.an("array") .which.has.length.at.least(1); - expect(body[1]).to.have.property("w:p"); + expect(body[0]).to.have.property("w:p"); }); it("should use the text given to create a run in the paragraph", () => { @@ -43,7 +48,7 @@ describe("Document", () => { expect(body) .to.be.an("array") .which.has.length.at.least(1); - expect(body[1]) + expect(body[0]) .to.have.property("w:p") .which.includes({ "w:r": [{ "w:rPr": [] }, { "w:t": [{ _attr: { "xml:space": "preserve" } }, "sample paragraph text"] }], @@ -59,7 +64,7 @@ describe("Document", () => { expect(body) .to.be.an("array") .which.has.length.at.least(1); - expect(body[1]).to.have.property("w:tbl"); + expect(body[0]).to.have.property("w:tbl"); }); it("should create a table with the correct dimensions", () => { @@ -68,7 +73,7 @@ describe("Document", () => { expect(body) .to.be.an("array") .which.has.length.at.least(1); - expect(body[1]) + expect(body[0]) .to.have.property("w:tbl") .which.includes({ "w:tblGrid": [ @@ -77,7 +82,7 @@ describe("Document", () => { { "w:gridCol": [{ _attr: { "w:w": 1 } }] }, ], }); - expect(body[1]["w:tbl"].filter((x) => x["w:tr"])).to.have.length(2); + expect(body[0]["w:tbl"].filter((x) => x["w:tr"])).to.have.length(2); }); }); }); diff --git a/src/file/document/document.ts b/src/file/document/document.ts index f4d580b9ca..a71bcbffe9 100644 --- a/src/file/document/document.ts +++ b/src/file/document/document.ts @@ -70,4 +70,8 @@ export class Document extends XmlComponent { return; } + + get Body() { + return this.body; + } } diff --git a/src/file/document/index.ts b/src/file/document/index.ts index fe6d89c0eb..6b128299f6 100644 --- a/src/file/document/index.ts +++ b/src/file/document/index.ts @@ -1 +1,2 @@ export * from "./document"; +export * from "./body"; diff --git a/src/file/file.ts b/src/file/file.ts index dce3ef24b1..a42e542e33 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -14,6 +14,7 @@ import { DefaultStylesFactory } from "./styles/factory"; import { ExternalStylesFactory } from "./styles/external-styles-factory"; import { Table } from "./table"; import { IMediaData } from "index"; +import { FooterReferenceType, HeaderReferenceType } from "./document/body/section-properties"; export class File { private readonly document: Document; @@ -23,14 +24,14 @@ export class File { private readonly media: Media; private readonly docRelationships: Relationships; private readonly fileRelationships: Relationships; - private readonly headerWrapper: HeaderWrapper; - private readonly footerWrapper: FooterWrapper; + private readonly headerWrapper: HeaderWrapper[] = []; + private readonly footerWrapper: FooterWrapper[] = []; private readonly contentTypes: ContentTypes; private readonly appProperties: AppProperties; - constructor(options?: IPropertiesOptions, sectionPropertiesOptions?: SectionPropertiesOptions) { - this.document = new Document(sectionPropertiesOptions); + private nextId: number = 1; + constructor(options?: IPropertiesOptions, sectionPropertiesOptions?: SectionPropertiesOptions) { if (!options) { options = { creator: "Un-named", @@ -51,29 +52,36 @@ export class File { this.numbering = new Numbering(); this.docRelationships = new Relationships(); this.docRelationships.createRelationship( - 1, + this.nextId++, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles", "styles.xml", ); this.docRelationships.createRelationship( - 2, + this.nextId++, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering", "numbering.xml", ); + this.contentTypes = new ContentTypes(); + this.media = new Media(); + + const header = new HeaderWrapper(this.media, this.nextId++); + this.headerWrapper.push(header); this.docRelationships.createRelationship( - 3, + header.Header.referenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header", - "header1.xml", + `header1.xml`, ); + this.contentTypes.addHeader(this.headerWrapper.length); + + const footer = new FooterWrapper(this.media, this.nextId++); + this.footerWrapper.push(footer); this.docRelationships.createRelationship( - 4, + footer.Footer.referenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer", "footer1.xml", ); - this.media = new Media(); - this.headerWrapper = new HeaderWrapper(this.media); - this.footerWrapper = new FooterWrapper(this.media); - this.contentTypes = new ContentTypes(); + this.contentTypes.addFooter(this.footerWrapper.length); + this.fileRelationships = new Relationships(); this.fileRelationships.createRelationship( 1, @@ -91,6 +99,19 @@ export class File { "docProps/app.xml", ); this.appProperties = new AppProperties(); + + if (!sectionPropertiesOptions) { + sectionPropertiesOptions = { + footerType: FooterReferenceType.DEFAULT, + headerType: HeaderReferenceType.DEFAULT, + headerId: header.Header.referenceId, + footerId: footer.Footer.referenceId, + }; + } else { + sectionPropertiesOptions.headerId = header.Header.referenceId; + sectionPropertiesOptions.footerId = footer.Footer.referenceId; + } + this.document = new Document(sectionPropertiesOptions); } public addParagraph(paragraph: Paragraph): void { @@ -110,7 +131,7 @@ export class File { } public createImage(image: string): void { - const mediaData = this.media.addMedia(image, this.docRelationships.RelationshipCount); + const mediaData = this.media.addMedia(image, this.nextId++); this.docRelationships.createRelationship( mediaData.referenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", @@ -120,7 +141,7 @@ export class File { } public createImageData(imageName: string, data: Buffer, width?: number, height?: number): IMediaData { - const mediaData = this.media.addMediaWithData(imageName, data, this.docRelationships.RelationshipCount, width, height); + const mediaData = this.media.addMediaWithData(imageName, data, this.nextId++, width, height); this.docRelationships.createRelationship( mediaData.referenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", @@ -129,6 +150,40 @@ export class File { return mediaData; } + public addSection(sectionPropertiesOptions: SectionPropertiesOptions) { + this.document.Body.addSection(sectionPropertiesOptions); + } + + /** + * Creates new header. + */ + public createHeader(): HeaderWrapper { + const header = new HeaderWrapper(this.media, this.nextId++); + this.headerWrapper.push(header); + this.docRelationships.createRelationship( + header.Header.referenceId, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header", + `header${this.headerWrapper.length}.xml`, + ); + this.contentTypes.addHeader(this.headerWrapper.length); + return header; + } + + /** + * Creates new footer. + */ + public createFooter(): FooterWrapper { + const footer = new FooterWrapper(this.media, this.nextId++); + this.footerWrapper.push(footer); + this.docRelationships.createRelationship( + footer.Footer.referenceId, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer", + `footer${this.footerWrapper.length}.xml`, + ); + this.contentTypes.addFooter(this.footerWrapper.length); + return footer; + } + public get Document(): Document { return this.document; } @@ -158,13 +213,33 @@ export class File { } public get Header(): HeaderWrapper { + return this.headerWrapper[0]; + } + + public get Headers(): HeaderWrapper[] { return this.headerWrapper; } + public HeaderByRefNumber(refId: number): HeaderWrapper { + const entry = this.headerWrapper.find((h) => h.Header.referenceId === refId); + if (entry) return entry; + throw new Error(`There is no header with given reference id ${refId}`); + } + public get Footer(): FooterWrapper { + return this.footerWrapper[0]; + } + + public get Footers(): FooterWrapper[] { return this.footerWrapper; } + public FooterByRefNumber(refId: number): FooterWrapper { + const entry = this.footerWrapper.find((h) => h.Footer.referenceId === refId); + if (entry) return entry; + throw new Error(`There is no footer with given reference id ${refId}`); + } + public get ContentTypes(): ContentTypes { return this.contentTypes; } diff --git a/src/file/footer-wrapper.ts b/src/file/footer-wrapper.ts index 47d0f73928..8e0ab04496 100644 --- a/src/file/footer-wrapper.ts +++ b/src/file/footer-wrapper.ts @@ -9,8 +9,8 @@ export class FooterWrapper { private readonly footer: Footer; private readonly relationships: Relationships; - constructor(private readonly media: Media) { - this.footer = new Footer(); + constructor(private readonly media: Media, referenceId: number) { + this.footer = new Footer(referenceId); this.relationships = new Relationships(); } diff --git a/src/file/footer/footer.ts b/src/file/footer/footer.ts index 9a64909caa..34ee22a43e 100644 --- a/src/file/footer/footer.ts +++ b/src/file/footer/footer.ts @@ -6,8 +6,10 @@ import { Table } from "../table"; import { FooterAttributes } from "./footer-attributes"; export class Footer extends XmlComponent { - constructor() { + private refId: number; + constructor(referenceNumber: number) { super("w:ftr"); + this.refId = referenceNumber; this.root.push( new FooterAttributes({ wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas", @@ -30,6 +32,10 @@ export class Footer extends XmlComponent { ); } + get referenceId() { + return this.refId; + } + public addParagraph(paragraph: Paragraph): void { this.root.push(paragraph); } diff --git a/src/file/header-wrapper.ts b/src/file/header-wrapper.ts index 250919b489..5c1a389a24 100644 --- a/src/file/header-wrapper.ts +++ b/src/file/header-wrapper.ts @@ -9,8 +9,8 @@ export class HeaderWrapper { private readonly header: Header; private readonly relationships: Relationships; - constructor(private readonly media: Media) { - this.header = new Header(); + constructor(private readonly media: Media, referenceId: number) { + this.header = new Header(referenceId); this.relationships = new Relationships(); } diff --git a/src/file/header/header.ts b/src/file/header/header.ts index b4dd0918ef..f1a9f566c4 100644 --- a/src/file/header/header.ts +++ b/src/file/header/header.ts @@ -6,8 +6,10 @@ import { Table } from "../table"; import { HeaderAttributes } from "./header-attributes"; export class Header extends XmlComponent { - constructor() { + private refId: number; + constructor(referenceNumber: number) { super("w:hdr"); + this.refId = referenceNumber; this.root.push( new HeaderAttributes({ wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas", @@ -30,6 +32,10 @@ export class Header extends XmlComponent { ); } + get referenceId() { + return this.refId; + } + public addParagraph(paragraph: Paragraph): void { this.root.push(paragraph); } diff --git a/src/file/index.ts b/src/file/index.ts index 42a9ca3b76..d1e04ffcf7 100644 --- a/src/file/index.ts +++ b/src/file/index.ts @@ -4,5 +4,6 @@ export * from "./file"; export * from "./numbering"; export * from "./media"; export * from "./drawing"; +export * from "./document"; export * from "./styles"; export * from "./xml-components"; diff --git a/src/file/media/media.ts b/src/file/media/media.ts index 69435eb21f..7f384f9f98 100644 --- a/src/file/media/media.ts +++ b/src/file/media/media.ts @@ -11,9 +11,9 @@ export class Media { this.map = new Map(); } - private createMedia(key: string, relationshipsCount, dimensions, data: fs.ReadStream | Buffer, filePath?: string) { + private createMedia(key: string, referenceId, dimensions, data: fs.ReadStream | Buffer, filePath?: string) { const imageData = { - referenceId: this.map.size + relationshipsCount + 1, + referenceId: referenceId, stream: data, path: filePath, fileName: key, @@ -42,13 +42,13 @@ export class Media { return data; } - public addMedia(filePath: string, relationshipsCount: number): IMediaData { + public addMedia(filePath: string, referenceId: number): IMediaData { const key = path.basename(filePath); const dimensions = sizeOf(filePath); - return this.createMedia(key, relationshipsCount, dimensions, fs.createReadStream(filePath), filePath); + return this.createMedia(key, referenceId, dimensions, fs.createReadStream(filePath), filePath); } - public addMediaWithData(fileName: string, data: Buffer, relationshipsCount: number, width?, height?): IMediaData { + public addMediaWithData(fileName: string, data: Buffer, referenceId: number, width?, height?): IMediaData { const key = fileName; let dimensions; if (width && height) { @@ -60,7 +60,7 @@ export class Media { dimensions = sizeOf(data); } - return this.createMedia(key, relationshipsCount, dimensions, data); + return this.createMedia(key, referenceId, dimensions, data); } public get array(): IMediaData[] { From 026a221e5e8ce971d26dbfda41763ef126a9af4e Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Fri, 22 Jun 2018 23:01:34 +0100 Subject: [PATCH 13/23] Move header outside --- src/export/packer/compiler.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/export/packer/compiler.ts b/src/export/packer/compiler.ts index 2cb80a6f14..21a5d95e13 100644 --- a/src/export/packer/compiler.ts +++ b/src/export/packer/compiler.ts @@ -57,15 +57,15 @@ export class Compiler { name: "word/numbering.xml", }); + this.archive.append(xmlHeader2, { + name: "word/header2.xml", + }); + // headers for (let i = 0; i < this.file.Headers.length; i++) { const element = this.file.Headers[i]; this.archive.append(xml(this.formatter.format(element.Header)), { name: `word/header${i + 1}.xml`, - }); - - this.archive.append(xmlHeader2, { - name: "word/header2.xml", }); this.archive.append(xml(this.formatter.format(element.Relationships)), { From d63adc7a6b4372b2df7095fb7fe17fa6ae09658b Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Fri, 22 Jun 2018 23:04:03 +0100 Subject: [PATCH 14/23] Fix linting --- src/file/document/body/body.ts | 10 +++++----- .../footer-reference/footer-reference.ts | 4 ++-- .../header-reference/header-reference.ts | 4 ++-- .../body/section-properties/page-size/page-size.ts | 2 +- .../body/section-properties/section-properties.ts | 8 ++++---- src/file/document/document.ts | 2 +- src/file/footer/footer.ts | 2 +- src/file/header/header.ts | 2 +- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/file/document/body/body.ts b/src/file/document/body/body.ts index 691dfd7518..138a629409 100644 --- a/src/file/document/body/body.ts +++ b/src/file/document/body/body.ts @@ -1,6 +1,6 @@ -import { XmlComponent, IXmlableObject } from "file/xml-components"; -import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties"; +import { IXmlableObject, XmlComponent } from "file/xml-components"; import { Paragraph, ParagraphProperties } from "../.."; +import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties"; export class Body extends XmlComponent { private defaultSection: SectionProperties; @@ -22,7 +22,7 @@ export class Body extends XmlComponent { * - last section should be direct child of body * @param section new section */ - addSection(section: SectionPropertiesOptions | SectionProperties) { + public addSection(section: SectionPropertiesOptions | SectionProperties): void { const currentSection = this.sections.pop() as SectionProperties; this.root.push(this.createSectionParagraph(currentSection)); if (section instanceof SectionProperties) { @@ -45,11 +45,11 @@ export class Body extends XmlComponent { this.root.push(component); } - get DefaultSection() { + get DefaultSection(): SectionProperties { return this.defaultSection; } - private createSectionParagraph(section: SectionProperties) { + private createSectionParagraph(section: SectionProperties): Paragraph { const paragraph = new Paragraph(); const properties = new ParagraphProperties(); properties.addChildElement(section); diff --git a/src/file/document/body/section-properties/footer-reference/footer-reference.ts b/src/file/document/body/section-properties/footer-reference/footer-reference.ts index 3805676841..6c2786432d 100644 --- a/src/file/document/body/section-properties/footer-reference/footer-reference.ts +++ b/src/file/document/body/section-properties/footer-reference/footer-reference.ts @@ -1,13 +1,13 @@ import { XmlComponent } from "file/xml-components"; import { FooterReferenceAttributes, FooterReferenceType } from "./footer-reference-attributes"; -export interface FooterOptions { +export interface IFooterOptions { footerType?: FooterReferenceType; footerId?: number; } export class FooterReference extends XmlComponent { - constructor(options: FooterOptions) { + constructor(options: IFooterOptions) { super("w:footerReference"); this.root.push( diff --git a/src/file/document/body/section-properties/header-reference/header-reference.ts b/src/file/document/body/section-properties/header-reference/header-reference.ts index 4065ed5253..8464a33a92 100644 --- a/src/file/document/body/section-properties/header-reference/header-reference.ts +++ b/src/file/document/body/section-properties/header-reference/header-reference.ts @@ -1,13 +1,13 @@ import { XmlComponent } from "file/xml-components"; import { HeaderReferenceAttributes, HeaderReferenceType } from "./header-reference-attributes"; -export interface HeaderOptions { +export interface IHeaderOptions { headerType?: HeaderReferenceType; headerId?: number; } export class HeaderReference extends XmlComponent { - constructor(options: HeaderOptions) { + constructor(options: IHeaderOptions) { super("w:headerReference"); this.root.push( new HeaderReferenceAttributes({ diff --git a/src/file/document/body/section-properties/page-size/page-size.ts b/src/file/document/body/section-properties/page-size/page-size.ts index bf5499d3f1..6aa400bea2 100644 --- a/src/file/document/body/section-properties/page-size/page-size.ts +++ b/src/file/document/body/section-properties/page-size/page-size.ts @@ -1,5 +1,5 @@ import { XmlComponent } from "file/xml-components"; -import { PageSizeAttributes, PageOrientation } from "./page-size-attributes"; +import { PageOrientation, PageSizeAttributes } from "./page-size-attributes"; export class PageSize extends XmlComponent { constructor(width: number, height: number, orientation: PageOrientation) { diff --git a/src/file/document/body/section-properties/section-properties.ts b/src/file/document/body/section-properties/section-properties.ts index 557d33acb4..f448a1a2cf 100644 --- a/src/file/document/body/section-properties/section-properties.ts +++ b/src/file/document/body/section-properties/section-properties.ts @@ -5,8 +5,8 @@ import { Columns } from "./columns/columns"; import { IColumnsAttributes } from "./columns/columns-attributes"; import { DocumentGrid } from "./doc-grid/doc-grid"; import { IDocGridAttributesProperties } from "./doc-grid/doc-grid-attributes"; -import { FooterOptions, FooterReference } from "./footer-reference/footer-reference"; -import { HeaderOptions, HeaderReference } from "./header-reference/header-reference"; +import { FooterReference, IFooterOptions } from "./footer-reference/footer-reference"; +import { HeaderReference, IHeaderOptions } from "./header-reference/header-reference"; import { HeaderReferenceType } from "./header-reference/header-reference-attributes"; import { PageMargin } from "./page-margin/page-margin"; import { IPageMarginAttributes } from "./page-margin/page-margin-attributes"; @@ -18,8 +18,8 @@ export type SectionPropertiesOptions = IPageSizeAttributes & IPageMarginAttributes & IColumnsAttributes & IDocGridAttributesProperties & - HeaderOptions & - FooterOptions & + IHeaderOptions & + IFooterOptions & IPageNumberTypeAttributes; export class SectionProperties extends XmlComponent { diff --git a/src/file/document/document.ts b/src/file/document/document.ts index 5e0d3276e7..d8db84adf3 100644 --- a/src/file/document/document.ts +++ b/src/file/document/document.ts @@ -71,7 +71,7 @@ export class Document extends XmlComponent { return run; } - get Body() { + get Body(): Body { return this.body; } } diff --git a/src/file/footer/footer.ts b/src/file/footer/footer.ts index 34ee22a43e..cbdd4a544d 100644 --- a/src/file/footer/footer.ts +++ b/src/file/footer/footer.ts @@ -32,7 +32,7 @@ export class Footer extends XmlComponent { ); } - get referenceId() { + public get referenceId(): number { return this.refId; } diff --git a/src/file/header/header.ts b/src/file/header/header.ts index f1a9f566c4..b0ffaa8a63 100644 --- a/src/file/header/header.ts +++ b/src/file/header/header.ts @@ -32,7 +32,7 @@ export class Header extends XmlComponent { ); } - get referenceId() { + get referenceId(): number { return this.refId; } From 0b939b1cd6c0a03b369b98fabf597ee1e77ae371 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Fri, 22 Jun 2018 23:18:07 +0100 Subject: [PATCH 15/23] Organise imports --- src/export/packer/compiler.spec.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/export/packer/compiler.spec.ts b/src/export/packer/compiler.spec.ts index c87a39a0e8..0bf691ebcb 100644 --- a/src/export/packer/compiler.spec.ts +++ b/src/export/packer/compiler.spec.ts @@ -1,12 +1,11 @@ /* tslint:disable:typedef space-before-function-paren */ import * as fs from "fs"; - -import { Compiler } from "./compiler"; -import { File } from "../../file"; -import { expect } from "chai"; - import * as JSZip from "jszip"; +import { expect } from "chai"; +import { File } from "../../file"; +import { Compiler } from "./compiler"; + describe("Compiler", () => { let compiler: Compiler; let file: File; From 00955d2e4fe88b361b8982c46d87f12b9c2ee955 Mon Sep 17 00:00:00 2001 From: Dolan Date: Mon, 25 Jun 2018 19:53:40 +0100 Subject: [PATCH 16/23] Fix imports --- src/file/document/body/body.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/file/document/body/body.ts b/src/file/document/body/body.ts index 138a629409..30f606af1d 100644 --- a/src/file/document/body/body.ts +++ b/src/file/document/body/body.ts @@ -1,6 +1,6 @@ import { IXmlableObject, XmlComponent } from "file/xml-components"; import { Paragraph, ParagraphProperties } from "../.."; -import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties"; +import { SectionProperties, SectionPropertiesOptions } from "./section-properties"; export class Body extends XmlComponent { private defaultSection: SectionProperties; From 2a0b45dc20a6a23db47e200a3f2291b66e7d033f Mon Sep 17 00:00:00 2001 From: Dolan Date: Mon, 25 Jun 2018 21:53:01 +0100 Subject: [PATCH 17/23] make default settings in the addSection --- src/file/document/body/body.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/file/document/body/body.ts b/src/file/document/body/body.ts index 30f606af1d..1d7fcdac43 100644 --- a/src/file/document/body/body.ts +++ b/src/file/document/body/body.ts @@ -28,7 +28,11 @@ export class Body extends XmlComponent { if (section instanceof SectionProperties) { this.sections.push(section); } else { - this.sections.push(new SectionProperties(section)); + const params = { + ...this.defaultSection.Options, + ...section, + }; + this.sections.push(new SectionProperties(params)); } } public prepForXml(): IXmlableObject { From 84ebf8e6c30aff90b1bd990d707976b9ea21f76b Mon Sep 17 00:00:00 2001 From: Dolan Date: Mon, 25 Jun 2018 22:13:12 +0100 Subject: [PATCH 18/23] Remove old implementation of first page header --- src/export/packer/compiler.ts | 5 -- src/file/content-types/content-types.ts | 1 - .../section-properties/section-properties.ts | 21 ++++---- src/file/file.ts | 15 +----- src/file/header-wrapper.ts | 50 ------------------- 5 files changed, 12 insertions(+), 80 deletions(-) diff --git a/src/export/packer/compiler.ts b/src/export/packer/compiler.ts index 21a5d95e13..456976d9d6 100644 --- a/src/export/packer/compiler.ts +++ b/src/export/packer/compiler.ts @@ -33,7 +33,6 @@ export class Compiler { const xmlNumbering = xml(this.formatter.format(this.file.Numbering)); const xmlRelationships = xml(this.formatter.format(this.file.DocumentRelationships)); const xmlFileRelationships = xml(this.formatter.format(this.file.FileRelationships)); - const xmlHeader2 = xml(this.formatter.format(this.file.firstPageHeader.Header)); const xmlContentTypes = xml(this.formatter.format(this.file.ContentTypes)); const xmlAppProperties = xml(this.formatter.format(this.file.AppProperties)); @@ -57,10 +56,6 @@ export class Compiler { name: "word/numbering.xml", }); - this.archive.append(xmlHeader2, { - name: "word/header2.xml", - }); - // headers for (let i = 0; i < this.file.Headers.length; i++) { const element = this.file.Headers[i]; diff --git a/src/file/content-types/content-types.ts b/src/file/content-types/content-types.ts index d616cbc223..7ac3a6d649 100644 --- a/src/file/content-types/content-types.ts +++ b/src/file/content-types/content-types.ts @@ -25,7 +25,6 @@ export class ContentTypes extends XmlComponent { new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", "/word/document.xml"), ); - this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", "/word/header2.xml")); this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", "/word/styles.xml")); this.root.push(new Override("application/vnd.openxmlformats-package.core-properties+xml", "/docProps/core.xml")); this.root.push(new Override("application/vnd.openxmlformats-officedocument.extended-properties+xml", "/docProps/app.xml")); diff --git a/src/file/document/body/section-properties/section-properties.ts b/src/file/document/body/section-properties/section-properties.ts index f448a1a2cf..450f2e9c33 100644 --- a/src/file/document/body/section-properties/section-properties.ts +++ b/src/file/document/body/section-properties/section-properties.ts @@ -12,7 +12,7 @@ import { PageMargin } from "./page-margin/page-margin"; import { IPageMarginAttributes } from "./page-margin/page-margin-attributes"; import { PageSize } from "./page-size/page-size"; import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes"; -import { TitlePage } from "./title-page/title-page"; +// import { TitlePage } from "./title-page/title-page"; export type SectionPropertiesOptions = IPageSizeAttributes & IPageMarginAttributes & @@ -69,15 +69,16 @@ export class SectionProperties extends XmlComponent { this.root.push(new Columns(mergedOptions.space)); this.root.push(new DocumentGrid(mergedOptions.linePitch)); - if (mergedOptions.differentFirstPageHeader) { - this.root.push( - new HeaderReference({ - headerType: HeaderReferenceType.FIRST, - headerId: 5, - }), - ); - this.root.push(new TitlePage()); - } + // TODO + // if (mergedOptions.differentFirstPageHeader) { + // this.root.push( + // new HeaderReference({ + // headerType: HeaderReferenceType.FIRST, + // headerId: 5, + // }), + // ); + // this.root.push(new TitlePage()); + // } this.root.push( new HeaderReference({ diff --git a/src/file/file.ts b/src/file/file.ts index fa4fa15484..4efdba7dcb 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -6,7 +6,7 @@ import { Document } from "./document"; import { FooterReferenceType, HeaderReferenceType } from "./document/body/section-properties"; import { SectionPropertiesOptions } from "./document/body/section-properties/section-properties"; import { FooterWrapper } from "./footer-wrapper"; -import { FirstPageHeaderWrapper, HeaderWrapper } from "./header-wrapper"; +import { HeaderWrapper } from "./header-wrapper"; import { Media } from "./media"; import { Numbering } from "./numbering"; import { Hyperlink, Paragraph, PictureRun } from "./paragraph"; @@ -26,7 +26,6 @@ export class File { private readonly fileRelationships: Relationships; private readonly headerWrapper: HeaderWrapper[] = []; private readonly footerWrapper: FooterWrapper[] = []; - private readonly firstPageHeaderWrapper: FirstPageHeaderWrapper; private readonly contentTypes: ContentTypes; private readonly appProperties: AppProperties; @@ -78,12 +77,6 @@ export class File { const footer = new FooterWrapper(this.media, this.nextId++); this.footerWrapper.push(footer); - this.docRelationships.createRelationship( - 5, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header", - "header2.xml", - ); - this.docRelationships.createRelationship( footer.Footer.referenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer", @@ -91,8 +84,6 @@ export class File { ); this.contentTypes.addFooter(this.footerWrapper.length); - this.firstPageHeaderWrapper = new FirstPageHeaderWrapper(this.media, this.nextId++); - this.fileRelationships = new Relationships(); this.fileRelationships.createRelationship( 1, @@ -243,10 +234,6 @@ export class File { return this.headerWrapper; } - public get firstPageHeader(): FirstPageHeaderWrapper { - return this.firstPageHeaderWrapper; - } - public HeaderByRefNumber(refId: number): HeaderWrapper { const entry = this.headerWrapper.find((h) => h.Header.referenceId === refId); if (entry) { diff --git a/src/file/header-wrapper.ts b/src/file/header-wrapper.ts index 12ded301ab..c23f4db448 100644 --- a/src/file/header-wrapper.ts +++ b/src/file/header-wrapper.ts @@ -5,56 +5,6 @@ import { Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Table } from "./table"; -export class FirstPageHeaderWrapper { - private readonly header: Header; - private readonly relationships: Relationships; - - constructor(private readonly media: Media, referenceId: number) { - this.header = new Header(referenceId); - this.relationships = new Relationships(); - } - - public addParagraph(paragraph: Paragraph): void { - this.header.addParagraph(paragraph); - } - - public createParagraph(text?: string): Paragraph { - const para = new Paragraph(text); - this.addParagraph(para); - return para; - } - - public addTable(table: Table): void { - this.header.addTable(table); - } - - public createTable(rows: number, cols: number): Table { - return this.header.createTable(rows, cols); - } - - public addDrawing(imageData: IMediaData): void { - this.header.addDrawing(imageData); - } - - public createImage(image: string): void { - const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount); - this.relationships.createRelationship( - mediaData.referenceId, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - `media/${mediaData.fileName}`, - ); - this.addDrawing(mediaData); - } - - public get Header(): Header { - return this.header; - } - - public get Relationships(): Relationships { - return this.relationships; - } -} - export class HeaderWrapper { private readonly header: Header; private readonly relationships: Relationships; From cee091fa58aa7e08c8bdaed44ca680711958ca89 Mon Sep 17 00:00:00 2001 From: Dolan Date: Tue, 26 Jun 2018 00:03:28 +0100 Subject: [PATCH 19/23] Use method approach to different first page header --- demo/demo14.js | 5 +-- .../section-properties/section-properties.ts | 12 ------- src/file/file.ts | 33 ++++++++----------- 3 files changed, 17 insertions(+), 33 deletions(-) diff --git a/demo/demo14.js b/demo/demo14.js index 0e3859eadf..b2262f106a 100644 --- a/demo/demo14.js +++ b/demo/demo14.js @@ -1,6 +1,6 @@ const docx = require('../build'); -var doc = new docx.Document(undefined,{differentFirstPageHeader:true}); +var doc = new docx.Document(); doc.createParagraph("First Page").pageBreak() doc.createParagraph("Second Page"); @@ -10,7 +10,8 @@ var pageNumber = new docx.TextRun().pageNumber() var pageoneheader = new docx.Paragraph("First Page Header ").right(); pageoneheader.addRun(pageNumber); -doc.firstPageHeader.addParagraph(pageoneheader); +var firstPageHeader = doc.createFirstPageHeader(); +firstPageHeader.addParagraph(pageoneheader); var pagetwoheader = new docx.Paragraph("My Title ").right(); diff --git a/src/file/document/body/section-properties/section-properties.ts b/src/file/document/body/section-properties/section-properties.ts index 450f2e9c33..bb12cda3c3 100644 --- a/src/file/document/body/section-properties/section-properties.ts +++ b/src/file/document/body/section-properties/section-properties.ts @@ -40,7 +40,6 @@ export class SectionProperties extends XmlComponent { space: 708, linePitch: 360, orientation: PageOrientation.PORTRAIT, - differentFirstPageHeader: false, headerType: HeaderReferenceType.DEFAULT, headerId: 0, footerType: FooterReferenceType.DEFAULT, @@ -69,17 +68,6 @@ export class SectionProperties extends XmlComponent { this.root.push(new Columns(mergedOptions.space)); this.root.push(new DocumentGrid(mergedOptions.linePitch)); - // TODO - // if (mergedOptions.differentFirstPageHeader) { - // this.root.push( - // new HeaderReference({ - // headerType: HeaderReferenceType.FIRST, - // headerId: 5, - // }), - // ); - // this.root.push(new TitlePage()); - // } - this.root.push( new HeaderReference({ headerType: mergedOptions.headerType, diff --git a/src/file/file.ts b/src/file/file.ts index 4efdba7dcb..fb7c6c83a0 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -3,7 +3,7 @@ import { AppProperties } from "./app-properties/app-properties"; import { ContentTypes } from "./content-types/content-types"; import { CoreProperties, IPropertiesOptions } from "./core-properties"; import { Document } from "./document"; -import { FooterReferenceType, HeaderReferenceType } from "./document/body/section-properties"; +import { FooterReferenceType, HeaderReference, HeaderReferenceType } from "./document/body/section-properties"; import { SectionPropertiesOptions } from "./document/body/section-properties/section-properties"; import { FooterWrapper } from "./footer-wrapper"; import { HeaderWrapper } from "./header-wrapper"; @@ -65,24 +65,8 @@ export class File { this.contentTypes = new ContentTypes(); this.media = new Media(); - const header = new HeaderWrapper(this.media, this.nextId++); - this.headerWrapper.push(header); - this.docRelationships.createRelationship( - header.Header.referenceId, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header", - `header1.xml`, - ); - this.contentTypes.addHeader(this.headerWrapper.length); - - const footer = new FooterWrapper(this.media, this.nextId++); - this.footerWrapper.push(footer); - - this.docRelationships.createRelationship( - footer.Footer.referenceId, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer", - "footer1.xml", - ); - this.contentTypes.addFooter(this.footerWrapper.length); + const header = this.createHeader(); + const footer = this.createFooter(); this.fileRelationships = new Relationships(); this.fileRelationships.createRelationship( @@ -198,6 +182,17 @@ export class File { return footer; } + public createFirstPageHeader(): HeaderWrapper { + const headerWrapper = this.createHeader(); + + this.document.Body.DefaultSection.addChildElement(new HeaderReference({ + headerType: HeaderReferenceType.FIRST, + headerId: headerWrapper.Header.referenceId, + })); + + return headerWrapper; + } + public get Document(): Document { return this.document; } From f3a822b4b277c8ef374724c48d9ab5c70b88838d Mon Sep 17 00:00:00 2001 From: Dolan Date: Tue, 26 Jun 2018 00:03:37 +0100 Subject: [PATCH 20/23] Add section demo --- demo/demo16.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 demo/demo16.js diff --git a/demo/demo16.js b/demo/demo16.js new file mode 100644 index 0000000000..b5db3d03b6 --- /dev/null +++ b/demo/demo16.js @@ -0,0 +1,28 @@ +const docx = require('../build'); + +var doc = new docx.Document(); + +var paragraph = new docx.Paragraph("Hello World"); +var paragraph2 = new docx.Paragraph("Hello World on another page").pageBreak(); + +doc.addParagraph(paragraph); +doc.addParagraph(paragraph2); + +doc.createParagraph("hello").pageBreak(); + +var header = doc.createHeader(); +header.createParagraph("Header on another page"); +var footer = doc.createFooter(); +footer.createParagraph("Footer on another page"); + +doc.addSection({ + headerId: header.Header.referenceId, + footerId: footer.Footer.referenceId, + pageNumberStart: 1, + pageNumberFormatType: docx.PageNumberFormat.DECIMAL, +}); + +var exporter = new docx.LocalPacker(doc); +exporter.pack('My Document'); + +console.log('Document created successfully at project root!'); From b26a9f8dcf980c6124c229d1d68a65d074fee7f8 Mon Sep 17 00:00:00 2001 From: Dolan Date: Tue, 26 Jun 2018 00:10:08 +0100 Subject: [PATCH 21/23] Fix styles --- src/file/file.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/file/file.ts b/src/file/file.ts index fb7c6c83a0..da22c96ca8 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -185,10 +185,12 @@ export class File { public createFirstPageHeader(): HeaderWrapper { const headerWrapper = this.createHeader(); - this.document.Body.DefaultSection.addChildElement(new HeaderReference({ - headerType: HeaderReferenceType.FIRST, - headerId: headerWrapper.Header.referenceId, - })); + this.document.Body.DefaultSection.addChildElement( + new HeaderReference({ + headerType: HeaderReferenceType.FIRST, + headerId: headerWrapper.Header.referenceId, + }), + ); return headerWrapper; } From b3e15d6729689bebe7c7dffaddb03f69fdafda8b Mon Sep 17 00:00:00 2001 From: Dolan Date: Tue, 26 Jun 2018 23:34:39 +0100 Subject: [PATCH 22/23] Updated demo for sections --- demo/demo16.js | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/demo/demo16.js b/demo/demo16.js index b5db3d03b6..ce0b162dc1 100644 --- a/demo/demo16.js +++ b/demo/demo16.js @@ -1,14 +1,10 @@ -const docx = require('../build'); +const docx = require("../build"); var doc = new docx.Document(); -var paragraph = new docx.Paragraph("Hello World"); -var paragraph2 = new docx.Paragraph("Hello World on another page").pageBreak(); +var paragraph = new docx.Paragraph("Hello World").pageBreak(); doc.addParagraph(paragraph); -doc.addParagraph(paragraph2); - -doc.createParagraph("hello").pageBreak(); var header = doc.createHeader(); header.createParagraph("Header on another page"); @@ -16,13 +12,25 @@ var footer = doc.createFooter(); footer.createParagraph("Footer on another page"); doc.addSection({ - headerId: header.Header.referenceId, - footerId: footer.Footer.referenceId, - pageNumberStart: 1, - pageNumberFormatType: docx.PageNumberFormat.DECIMAL, + headerId: header.Header.referenceId, + footerId: footer.Footer.referenceId, + pageNumberStart: 1, + pageNumberFormatType: docx.PageNumberFormat.DECIMAL, }); -var exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); +doc.createParagraph("hello"); -console.log('Document created successfully at project root!'); +doc.addSection({ + headerId: header.Header.referenceId, + footerId: footer.Footer.referenceId, + pageNumberStart: 1, + pageNumberFormatType: docx.PageNumberFormat.DECIMAL, + orientation: docx.PageOrientation.LANDSCAPE, +}); + +doc.createParagraph("hello in landscape"); + +var exporter = new docx.LocalPacker(doc); +exporter.pack("My Document"); + +console.log("Document created successfully at project root!"); From 5f6d177df9a1bf204ff978fa6be4108cbff27668 Mon Sep 17 00:00:00 2001 From: Dolan Date: Wed, 27 Jun 2018 02:27:56 +0100 Subject: [PATCH 23/23] Update README.md --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6c4e820db4..60da1a934b 100644 --- a/README.md +++ b/README.md @@ -88,18 +88,22 @@ exporter.packPdf("My First Document"); ## Examples -Check [the Wiki](https://github.com/dolanmiu/docx/wiki/Examples) for examples. +Check [the Wiki](https://github.com/dolanmiu/docx/wiki/Examples) and the [demo folder](https://github.com/dolanmiu/docx/tree/master/demo) for examples. # Contributing Read the contribution guidelines [here](https://github.com/dolanmiu/docx/wiki/Contributing-Guidelines). +# Honoured Mentions + +[@felipeochoa](https://github.com/felipeochoa) + +[@h4buli](https://github.com/h4buli) + --- Made with 💖 -Huge thanks to [@felipeochoa](https://github.com/felipeochoa) for awesome contributions to this project - [npm-image]: https://badge.fury.io/js/docx.svg [npm-url]: https://npmjs.org/package/docx [downloads-image]: https://img.shields.io/npm/dm/docx.svg