diff --git a/demo/demo13.js b/demo/demo13.js new file mode 100644 index 0000000000..b68348b1c8 --- /dev/null +++ b/demo/demo13.js @@ -0,0 +1,16 @@ +const docx = require('../build'); + +var doc = new docx.Document(); + +var paragraph = new docx.Paragraph("Hello World"); +var institutionText = new docx.TextRun("University College London").bold(); +var dateText = new docx.TextRun("5th Dec 2015").tab().bold(); +paragraph.addRun(institutionText); +paragraph.addRun(dateText); + +doc.addParagraph(paragraph); + +var exporter = new docx.LocalPacker(doc); +exporter.packPdf('My Document'); + +console.log('Document created successfully at project root!'); diff --git a/package.json b/package.json index 74e591431c..99104a157d 100644 --- a/package.json +++ b/package.json @@ -43,9 +43,11 @@ "@types/archiver": "^2.1.0", "@types/express": "^4.0.35", "@types/image-size": "0.0.29", + "@types/jszip": "^3.1.3", "@types/request-promise": "^4.1.41", "archiver": "^2.1.1", "image-size": "^0.6.2", + "jszip": "^3.1.5", "request": "^2.83.0", "request-promise": "^4.2.2", "xml": "^1.0.1" diff --git a/src/export/packer/local.ts b/src/export/packer/local.ts index 5d0c05fe81..1ebf8787ec 100644 --- a/src/export/packer/local.ts +++ b/src/export/packer/local.ts @@ -3,12 +3,11 @@ import * as os from "os"; import * as path from "path"; import { File } from "../../file"; -import { Compiler } from "./compiler"; +import { Compiler } from "./next-compiler"; import { IPacker } from "./packer"; import { PdfConvertWrapper } from "./pdf-convert-wrapper"; export class LocalPacker implements IPacker { - private stream: fs.WriteStream; private readonly pdfConverter: PdfConvertWrapper; private readonly packer: Compiler; @@ -20,8 +19,8 @@ export class LocalPacker implements IPacker { public async pack(filePath: string): Promise { filePath = filePath.replace(/.docx$/, ""); - this.stream = fs.createWriteStream(`${filePath}.docx`); - await this.packer.compile(this.stream); + const zipData = await this.packer.compile().generateAsync({ type: "base64" }) as string; + await this.writeToFile(`${filePath}.docx`, zipData); } public async packPdf(filePath: string): Promise { @@ -29,19 +28,27 @@ export class LocalPacker implements IPacker { const fileName = path.basename(filePath, path.extname(filePath)); const tempPath = path.join(os.tmpdir(), `${fileName}.docx`); - this.stream = fs.createWriteStream(tempPath); - await this.packer.compile(this.stream); + + const zipData = await this.packer.compile().generateAsync({ type: "base64" }) as string; + await this.writeToFile(tempPath, zipData); + const text = await this.pdfConverter.convert(tempPath); // const writeFile = util.promisify(fs.writeFile); --use this in future, in 3 years time. Only in node 8 // return writeFile(`${filePath}.pdf`, text); - return new Promise((resolve, reject) => { - fs.writeFile(`${filePath}.pdf`, text, (err) => { - if (err) { - reject(err); - return; - } + + await this.writeToFile(`${filePath}.pdf`, text); + } + + private writeToFile(filePath: string, data: string): Promise { + const file = fs.createWriteStream(filePath); + + return new Promise((resolve, reject) => { + file.write(data, "base64"); + file.end(); + file.on("finish", () => { resolve(); }); + file.on("error", reject); }); } } diff --git a/src/export/packer/next-compiler.ts b/src/export/packer/next-compiler.ts new file mode 100644 index 0000000000..baecd72ae7 --- /dev/null +++ b/src/export/packer/next-compiler.ts @@ -0,0 +1,117 @@ +import * as JSZip from "jszip"; +import * as xml from "xml"; + +import { File } from "file"; +import { Formatter } from "../formatter"; + +interface IXmlifyedFile { + data: string; + path: string; +} + +interface IXmlifyedFileMapping { + Document: IXmlifyedFile; + Styles: IXmlifyedFile; + Properties: IXmlifyedFile; + Numbering: IXmlifyedFile; + Relationships: IXmlifyedFile; + FileRelationships: IXmlifyedFile; + Header: IXmlifyedFile; + Footer: IXmlifyedFile; + HeaderRelationships: IXmlifyedFile; + FooterRelationships: IXmlifyedFile; + ContentTypes: IXmlifyedFile; + AppProperties: IXmlifyedFile; +} + +export class Compiler { + private formatter: Formatter; + + constructor(private file: File) { + this.formatter = new Formatter(); + } + + public compile(): JSZip { + const zip = new JSZip(); + + const xmlifiedFileMapping = this.xmlifyFile(this.file); + + for (const key in xmlifiedFileMapping) { + if (!xmlifiedFileMapping[key]) { + continue; + } + + const xmlifiedFile = xmlifiedFileMapping[key]; + + zip.file(xmlifiedFile.path, xmlifiedFile.data); + } + + // for (const data of file.Media.array) { + // this.archive.append(data.stream, { + // name: `word/media/${data.fileName}`, + // }); + + // zip.file(`word/media/${data.fileName}`, ) + // } + + return zip; + } + + private xmlifyFile(file: File): IXmlifyedFileMapping { + return { + Document: { + data: xml(this.formatter.format(file.Document), true), + path: "word/document.xml", + }, + Styles: { + data: xml(this.formatter.format(file.Styles)), + path: "word/styles.xml", + }, + Properties: { + data: xml(this.formatter.format(file.CoreProperties), { + declaration: { + standalone: "yes", + encoding: "UTF-8", + }, + }), + path: "docProps/core.xml", + }, + Numbering: { + data: xml(this.formatter.format(file.Numbering)), + path: "word/numbering.xml", + }, + Relationships: { + data: xml(this.formatter.format(file.DocumentRelationships)), + path: "word/_rels/document.xml.rels", + }, + FileRelationships: { + data: xml(this.formatter.format(file.FileRelationships)), + path: "_rels/.rels", + }, + Header: { + data: xml(this.formatter.format(file.Header.Header)), + path: "word/header1.xml", + }, + Footer: { + data: xml(this.formatter.format(file.Footer.Footer)), + path: "word/footer1.xml", + }, + HeaderRelationships: { + data: xml(this.formatter.format(file.Header.Relationships)), + path: "word/_rels/header1.xml.rels", + }, + FooterRelationships: { + data: xml(this.formatter.format(file.Footer.Relationships)), + path: "word/_rels/footer1.xml.rels", + }, + ContentTypes: { + data: xml(this.formatter.format(file.ContentTypes)), + path: "[Content_Types].xml", + }, + AppProperties: { + data: xml(this.formatter.format(file.AppProperties)), + path: "docProps/app.xml", + }, + }; + } +}