diff --git a/.vscode/settings.json b/.vscode/settings.json index 034279a342..f144f59202 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,16 +1,9 @@ { - "cSpell.words": [ - "clippy", - "docx", - "dolan", - "miu", - "officegen", - "typedoc" - ], + "cSpell.words": ["clippy", "docx", "dolan", "miu", "officegen", "typedoc"], "prettier.trailingComma": "all", "prettier.printWidth": 140, - "editor.formatOnSave": true, + "editor.formatOnSave": false, "prettier.tabWidth": 4, "prettier.arrowParens": "always", - "prettier.bracketSpacing": true, + "prettier.bracketSpacing": true } diff --git a/demo/demo8.js b/demo/demo8.js new file mode 100644 index 0000000000..4bde11e1fc --- /dev/null +++ b/demo/demo8.js @@ -0,0 +1,12 @@ +const docx = require('../build'); + +var doc = new docx.File(); + +doc.createParagraph("Hello World"); + +doc.Header.createParagraph("Header text"); + +var exporter = new docx.LocalPacker(doc); +exporter.pack('My Document'); + +console.log('Document created succesfully at project root!'); diff --git a/src/export/packer/compiler.ts b/src/export/packer/compiler.ts index 33ac1fa604..76bdcc2dad 100644 --- a/src/export/packer/compiler.ts +++ b/src/export/packer/compiler.ts @@ -42,6 +42,7 @@ export class Compiler { }); const xmlNumbering = xml(this.formatter.format(this.file.Numbering)); const xmlRelationships = xml(this.formatter.format(this.file.Relationships)); + const xmlHeader = xml(this.formatter.format(this.file.Header)); this.archive.append(xmlDocument, { name: "word/document.xml", @@ -59,6 +60,10 @@ export class Compiler { name: "word/numbering.xml", }); + this.archive.append(xmlHeader, { + name: "word/header1.xml", + }); + this.archive.append(xmlRelationships, { name: "word/_rels/document.xml.rels", }); diff --git a/src/export/packer/express.ts b/src/export/packer/express.ts index e6ee3693ac..4f99298424 100644 --- a/src/export/packer/express.ts +++ b/src/export/packer/express.ts @@ -5,10 +5,9 @@ import { Compiler } from "./compiler"; import { IPacker } from "./packer"; export class ExpressPacker implements IPacker { - private res: express.Response; - private packer: Compiler; + private readonly packer: Compiler; - constructor(file: File, res: express.Response) { + constructor(file: File, private readonly res: express.Response) { this.packer = new Compiler(file); this.res = res; diff --git a/src/export/packer/local.ts b/src/export/packer/local.ts index 2e33f74dfc..5d0c05fe81 100644 --- a/src/export/packer/local.ts +++ b/src/export/packer/local.ts @@ -9,8 +9,8 @@ import { PdfConvertWrapper } from "./pdf-convert-wrapper"; export class LocalPacker implements IPacker { private stream: fs.WriteStream; - private pdfConverter: PdfConvertWrapper; - private packer: Compiler; + private readonly pdfConverter: PdfConvertWrapper; + private readonly packer: Compiler; constructor(file: File) { this.pdfConverter = new PdfConvertWrapper(); 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 new file mode 100644 index 0000000000..b0407c4a0a --- /dev/null +++ b/src/file/document/body/section-properties/header-reference/header-reference-attributes.ts @@ -0,0 +1,13 @@ +import { XmlAttributeComponent } from "file/xml-components"; + +export interface IHeaderReferenceAttributes { + type: string; + id: string; +} + +export class HeaderReferenceAttributes extends XmlAttributeComponent { + protected xmlKeys = { + type: "w:type", + id: "r:id", + }; +} 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 new file mode 100644 index 0000000000..3809047bef --- /dev/null +++ b/src/file/document/body/section-properties/header-reference/header-reference.ts @@ -0,0 +1,14 @@ +import { XmlComponent } from "file/xml-components"; +import { HeaderReferenceAttributes } from "./header-reference-attributes"; + +export class HeaderReference extends XmlComponent { + constructor() { + super("w:headerReference"); + this.root.push( + new HeaderReferenceAttributes({ + type: "default", + id: `rId${3}`, + }), + ); + } +} diff --git a/src/file/document/body/section-properties/section-properties.ts b/src/file/document/body/section-properties/section-properties.ts index 097fff225f..a7aae06f10 100644 --- a/src/file/document/body/section-properties/section-properties.ts +++ b/src/file/document/body/section-properties/section-properties.ts @@ -1,13 +1,14 @@ // http://officeopenxml.com/WPsection.php -import { IColumnsAttributes } from "file/document/body/section-properties/columns/columns-attributes"; -import { IPageMarginAttributes } from "file/document/body/section-properties/page-margin/page-margin-attributes"; -import { IPageSizeAttributes } from "file/document/body/section-properties/page-size/page-size-attributes"; import { XmlComponent } from "file/xml-components"; 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 { HeaderReference } 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"; export type SectionPropertiesOptions = IPageSizeAttributes & IPageMarginAttributes & IColumnsAttributes & IDocGridAttributesProperties; @@ -49,5 +50,6 @@ export class SectionProperties extends XmlComponent { ); this.root.push(new Columns(mergedOptions.space)); this.root.push(new DocumentGrid(mergedOptions.linePitch)); + this.root.push(new HeaderReference()); } } diff --git a/src/file/document/document.ts b/src/file/document/document.ts index f29b2795b4..f4d580b9ca 100644 --- a/src/file/document/document.ts +++ b/src/file/document/document.ts @@ -8,7 +8,7 @@ import { SectionPropertiesOptions } from "./body/section-properties/section-prop import { DocumentAttributes } from "./document-attributes"; export class Document extends XmlComponent { - private body: Body; + private readonly body: Body; constructor(sectionPropertiesOptions?: SectionPropertiesOptions) { super("w:document"); diff --git a/src/file/file.ts b/src/file/file.ts index 03265a083e..9f6f3016a8 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -1,21 +1,23 @@ -import { Relationships } from "file/relationships"; import { Document } from "./document"; import { SectionPropertiesOptions } from "./document/body/section-properties/section-properties"; +import { Header } from "./header/header"; import { Media } from "./media"; import { Numbering } from "./numbering"; import { Paragraph } from "./paragraph"; import { IPropertiesOptions, Properties } from "./properties"; +import { Relationships } from "./relationships"; import { Styles } from "./styles"; import { DefaultStylesFactory } from "./styles/factory"; import { Table } from "./table"; export class File { - private document: Document; - private styles: Styles; - private properties: Properties; - private numbering: Numbering; - private media: Media; - private relationships: Relationships; + private readonly document: Document; + private readonly styles: Styles; + private readonly properties: Properties; + private readonly numbering: Numbering; + private readonly media: Media; + private readonly relationships: Relationships; + private readonly header: Header; constructor(options?: IPropertiesOptions, sectionPropertiesOptions?: SectionPropertiesOptions) { this.document = new Document(sectionPropertiesOptions); @@ -32,8 +34,9 @@ export class File { this.properties = new Properties(options); this.numbering = new Numbering(); - this.media = new Media(); this.relationships = new Relationships(); + this.media = new Media(); + this.header = new Header(); } public addParagraph(paragraph: Paragraph): void { @@ -41,7 +44,7 @@ export class File { } public createParagraph(text?: string): Paragraph { - return this.document.createParagraph(); + return this.document.createParagraph(text); } public addTable(table: Table): void { @@ -53,7 +56,7 @@ export class File { } public createImage(image: string): void { - const mediaData = this.media.addMedia(image); + const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount); this.relationships.createRelationship( mediaData.referenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", @@ -85,4 +88,8 @@ export class File { public get Relationships(): Relationships { return this.relationships; } + + public get Header(): Header { + return this.header; + } } diff --git a/src/file/header/header-attributes.ts b/src/file/header/header-attributes.ts new file mode 100644 index 0000000000..e893306192 --- /dev/null +++ b/src/file/header/header-attributes.ts @@ -0,0 +1,31 @@ +import { XmlAttributeComponent } from "file/xml-components"; + +export interface IHeaderAttributesProperties { + o?: string; + r?: string; + v?: string; + w?: string; + w10?: string; + wp?: string; + wps?: string; + wpg?: string; + mc?: string; + wp14?: string; + w14?: string; +} + +export class HeaderAttributes extends XmlAttributeComponent { + protected xmlKeys = { + o: "xmlns:o", + r: "xmlns:r", + v: "xmlns:v", + w: "xmlns:w", + w10: "xmlns:w10", + wp: "xmlns:wp", + wps: "xmlns:wps", + wpg: "xmlns:wpg", + mc: "xmlns:mc", + wp14: "xmlns:wp14", + w14: "xmlns:w14", + }; +} diff --git a/src/file/header/header.ts b/src/file/header/header.ts new file mode 100644 index 0000000000..8e2c37fe2d --- /dev/null +++ b/src/file/header/header.ts @@ -0,0 +1,61 @@ +// http://officeopenxml.com/WPheaders.php +import { IMediaData } from "file/media"; +import { XmlComponent } from "file/xml-components"; +import { Paragraph, PictureRun } from "../paragraph"; +import { Table } from "../table"; +import { HeaderAttributes } from "./header-attributes"; + +export class Header extends XmlComponent { + constructor() { + super("w:hdr"); + this.root.push( + new HeaderAttributes({ + o: "urn:schemas-microsoft-com:office:office", + r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships", + v: "urn:schemas-microsoft-com:vml", + w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main", + w10: "urn:schemas-microsoft-com:office:word", + wp: "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", + wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape", + wpg: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup", + mc: "http://schemas.openxmlformats.org/markup-compatibility/2006", + wp14: "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", + w14: "http://schemas.microsoft.com/office/word/2010/wordml", + }), + ); + } + + public addParagraph(paragraph: Paragraph): void { + this.root.push(paragraph); + } + + public createParagraph(text?: string): Paragraph { + const para = new Paragraph(text); + this.addParagraph(para); + return para; + } + + public addTable(table: Table): void { + this.root.push(table); + } + + public createTable(rows: number, cols: number): Table { + const table = new Table(rows, cols); + this.addTable(table); + return table; + } + + public addDrawing(imageData: IMediaData): void { + const paragraph = new Paragraph(); + const run = new PictureRun(imageData); + paragraph.addRun(run); + + this.root.push(paragraph); + } + + public createDrawing(imageData: IMediaData): void { + this.addDrawing(imageData); + + return; + } +} diff --git a/src/file/media/media.ts b/src/file/media/media.ts index 854d19194a..a244807599 100644 --- a/src/file/media/media.ts +++ b/src/file/media/media.ts @@ -5,7 +5,7 @@ import * as path from "path"; import { IMediaData } from "./data"; export class Media { - private map: Map; + private readonly map: Map; constructor() { this.map = new Map(); @@ -21,12 +21,12 @@ export class Media { return data; } - public addMedia(filePath: string): IMediaData { + public addMedia(filePath: string, relationshipsCount: number): IMediaData { const key = path.basename(filePath); const dimensions = sizeOf(filePath); const imageData = { - referenceId: this.map.size + 3, + referenceId: this.map.size + relationshipsCount, stream: fs.createReadStream(filePath), path: filePath, fileName: key, diff --git a/src/file/numbering/level.ts b/src/file/numbering/level.ts index ab8c3ccb53..bb50bfb2e2 100644 --- a/src/file/numbering/level.ts +++ b/src/file/numbering/level.ts @@ -61,8 +61,8 @@ class LevelJc extends XmlComponent { } export class LevelBase extends XmlComponent { - private paragraphProperties: ParagraphProperties; - private runProperties: RunProperties; + private readonly paragraphProperties: ParagraphProperties; + private readonly runProperties: RunProperties; constructor(level: number, start?: number, numberFormat?: string, levelText?: string, lvlJc?: string) { super("w:lvl"); diff --git a/src/file/numbering/num.ts b/src/file/numbering/num.ts index e6b4628ac4..cd3034d920 100644 --- a/src/file/numbering/num.ts +++ b/src/file/numbering/num.ts @@ -46,16 +46,14 @@ class LevelOverrideAttributes extends XmlAttributeComponent<{ ilvl: number }> { } export class LevelOverride extends XmlComponent { - private levelNum: number; private lvl?: LevelForOverride; - constructor(levelNum: number, start?: number) { + constructor(private readonly levelNum: number, start?: number) { super("w:lvlOverride"); this.root.push(new LevelOverrideAttributes({ ilvl: levelNum })); if (start !== undefined) { this.root.push(new StartOverride(start)); } - this.levelNum = levelNum; } get level(): LevelForOverride { diff --git a/src/file/relationships/relationship/relationship.ts b/src/file/relationships/relationship/relationship.ts index e40d43bc5b..e3ac807946 100644 --- a/src/file/relationships/relationship/relationship.ts +++ b/src/file/relationships/relationship/relationship.ts @@ -8,7 +8,8 @@ export type RelationshipType = | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" - | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering"; + | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" + | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"; export class Relationship extends XmlComponent { constructor(id: string, type: RelationshipType, target: string) { diff --git a/src/file/relationships/relationships.ts b/src/file/relationships/relationships.ts index f338bdd736..c665be063e 100644 --- a/src/file/relationships/relationships.ts +++ b/src/file/relationships/relationships.ts @@ -13,6 +13,7 @@ export class Relationships extends XmlComponent { this.createRelationship(1, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles", "styles.xml"); this.createRelationship(2, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering", "numbering.xml"); + this.createRelationship(3, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header", "header1.xml"); } public addRelationship(relationship: Relationship): void { @@ -25,4 +26,8 @@ export class Relationships extends XmlComponent { return relationship; } + + public get RelationshipCount(): number { + return this.root.length - 1; + } } diff --git a/src/file/styles/defaults/index.ts b/src/file/styles/defaults/index.ts index 811eb0aa1d..2b6c49e299 100644 --- a/src/file/styles/defaults/index.ts +++ b/src/file/styles/defaults/index.ts @@ -3,8 +3,8 @@ import { ParagraphPropertiesDefaults } from "./paragraph-properties"; import { RunPropertiesDefaults } from "./run-properties"; export class DocumentDefaults extends XmlComponent { - private runPropertiesDefaults: RunPropertiesDefaults; - private paragraphPropertiesDefaults: ParagraphPropertiesDefaults; + private readonly runPropertiesDefaults: RunPropertiesDefaults; + private readonly paragraphPropertiesDefaults: ParagraphPropertiesDefaults; constructor() { super("w:docDefaults"); diff --git a/src/file/styles/defaults/run-properties.ts b/src/file/styles/defaults/run-properties.ts index 242aa3e58d..c00b6ccc79 100644 --- a/src/file/styles/defaults/run-properties.ts +++ b/src/file/styles/defaults/run-properties.ts @@ -4,7 +4,7 @@ import { RunProperties } from "../../paragraph/run/properties"; import { RunFonts } from "../../paragraph/run/run-fonts"; export class RunPropertiesDefaults extends XmlComponent { - private properties: RunProperties; + private readonly properties: RunProperties; constructor() { super("w:rPrDefault"); diff --git a/src/file/styles/style/index.ts b/src/file/styles/style/index.ts index 50265f182a..7436fbb22a 100644 --- a/src/file/styles/style/index.ts +++ b/src/file/styles/style/index.ts @@ -36,8 +36,8 @@ export class Style extends XmlComponent { } export class ParagraphStyle extends Style { - private paragraphProperties: paragraph.ParagraphProperties; - private runProperties: RunProperties; + private readonly paragraphProperties: paragraph.ParagraphProperties; + private readonly runProperties: RunProperties; constructor(styleId: string, name?: string) { super({ type: "paragraph", styleId: styleId }, name); diff --git a/src/file/table/table.ts b/src/file/table/table.ts index 43a41cd78e..f11b6959c7 100644 --- a/src/file/table/table.ts +++ b/src/file/table/table.ts @@ -5,9 +5,9 @@ import { TableGrid } from "./grid"; import { TableProperties, WidthTypes } from "./properties"; export class Table extends XmlComponent { - private properties: TableProperties; - private rows: TableRow[]; - private grid: TableGrid; + private readonly properties: TableProperties; + private readonly rows: TableRow[]; + private readonly grid: TableGrid; constructor(rows: number, cols: number) { super("w:tbl"); @@ -63,14 +63,12 @@ export class Table extends XmlComponent { } export class TableRow extends XmlComponent { - private properties: TableRowProperties; - private cells: TableCell[]; + private readonly properties: TableRowProperties; - constructor(cells: TableCell[]) { + constructor(private readonly cells: TableCell[]) { super("w:tr"); this.properties = new TableRowProperties(); this.root.push(this.properties); - this.cells = cells; cells.forEach((c) => this.root.push(c)); } @@ -86,7 +84,7 @@ export class TableRowProperties extends XmlComponent { } export class TableCell extends XmlComponent { - private properties: TableCellProperties; + private readonly properties: TableCellProperties; constructor() { super("w:tc"); diff --git a/template/[Content_Types].xml b/template/[Content_Types].xml index 996f6a2bea..978bf4e3fe 100644 --- a/template/[Content_Types].xml +++ b/template/[Content_Types].xml @@ -8,6 +8,7 @@ +