diff --git a/.travis.yml b/.travis.yml index 986233743d..373070eeb8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,18 +8,30 @@ script: - npm test - npm run style - npm run build - - node ./demo/demo1.js - - node ./demo/demo2.js - - node ./demo/demo3.js - - node ./demo/demo4.js - - node ./demo/demo5.js - - node ./demo/demo6.js - - node ./demo/demo7.js - - node ./demo/demo8.js - - node ./demo/demo9.js - - node ./demo/demo10.js - - node ./demo/demo11.js - - node ./demo/demo12.js + - npm run ts-node -- ./demo/demo1.ts + - npm run ts-node -- ./demo/demo2.ts + - npm run ts-node -- ./demo/demo3.ts + - npm run ts-node -- ./demo/demo4.ts + - npm run ts-node -- ./demo/demo5.ts + - npm run ts-node -- ./demo/demo6.ts + - npm run ts-node -- ./demo/demo7.ts + - npm run ts-node -- ./demo/demo8.ts + - npm run ts-node -- ./demo/demo9.ts + - npm run ts-node -- ./demo/demo10.ts + - npm run ts-node -- ./demo/demo11.ts + - npm run ts-node -- ./demo/demo12.ts + - npm run ts-node -- ./demo/demo13.ts + - npm run ts-node -- ./demo/demo14.ts + - npm run ts-node -- ./demo/demo15.ts + - npm run ts-node -- ./demo/demo16.ts + - npm run ts-node -- ./demo/demo17.ts + - npm run ts-node -- ./demo/demo18.ts + - npm run ts-node -- ./demo/demo19.ts + - npm run ts-node -- ./demo/demo20.ts + - npm run ts-node -- ./demo/demo21.ts + - npm run ts-node -- ./demo/demo22.ts + - npm run ts-node -- ./demo/demo23.ts + - npm run ts-node -- ./demo/demo24.ts after_failure: - "cat /home/travis/builds/dolanmiu/docx/npm-debug.log" after_success: diff --git a/README.md b/README.md index 5415a4976e..7e25666ee0 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

- Generate .docx files with JS/TS very easily, written in TS. + Easily generate .docx files with JS/TS. Works for Node and on the Browser.

--- @@ -14,18 +14,13 @@ [![Dependency Status][daviddm-image]][daviddm-url] [![Known Vulnerabilities][snky-image]][snky-url] [![Chat on Gitter][gitter-image]][gitter-url] -[![code style: prettier][prettier-image]][prettier-url] [![PRs Welcome][pr-image]][pr-url] -# docx +

+ drawing +

-## Install - -```sh -$ npm install --save docx -``` - -## Demo +# Demo Press `endpoint` on the `RunKit` website: @@ -41,32 +36,18 @@ Press `endpoint` on the `RunKit` website: * https://runkit.com/dolanmiu/docx-demo8/1.0.1 - Header and Footer * https://runkit.com/dolanmiu/docx-demo10 - **My CV generated with docx** -#### Run demos locally: +# How to use & Documentation -```sh -$ npm run demo -``` +Please refer to the [documentation at https://docx.js.org/](https://docx.js.org/) for details on how to use this library, examples and much more! -This command will run the demo selector app in the `demo` folder. It will prompt you to select a demo number, which will run a demo from that folder. +# Examples -## How to - -Please refer to [https://docx.js.org/](https://docx.js.org/) for details on how to use this library, examples and much more! - -## Examples - -Check the examples section in the [documentation](https://docx.js.org/#/examples) and the [demo folder](https://github.com/dolanmiu/docx/tree/master/demo) for examples. +Check the `examples` section in the [documentation](https://docx.js.org/#/examples) and the [demo folder](https://github.com/dolanmiu/docx/tree/master/demo) for examples. # Contributing Read the contribution guidelines [here](https://docx.js.org/#/contribution-guidelines). -# Honoured Mentions - -[@felipeochoa](https://github.com/felipeochoa) - -[@h4buli](https://github.com/h4buli) - --- Made with 💖 @@ -83,7 +64,5 @@ Made with 💖 [snky-url]: https://snyk.io/test/github/dolanmiu/docx [gitter-image]: https://badges.gitter.im/dolanmiu/docx.svg [gitter-url]: https://gitter.im/docx-lib/Lobby -[prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg -[prettier-url]: https://github.com/prettier/prettier [pr-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg [pr-url]: http://makeapullrequest.com diff --git a/demo/browser-demo.html b/demo/browser-demo.html new file mode 100644 index 0000000000..fff5dc7989 --- /dev/null +++ b/demo/browser-demo.html @@ -0,0 +1,39 @@ + + + + + + + + + + +

DOCX browser Word document generation

+ + + + + + + + diff --git a/demo/demo1.js b/demo/demo1.js deleted file mode 100644 index 53e0e58834..0000000000 --- a/demo/demo1.js +++ /dev/null @@ -1,16 +0,0 @@ -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.pack('My Document'); - -console.log('Document created successfully at project root!'); diff --git a/demo/demo1.ts b/demo/demo1.ts new file mode 100644 index 0000000000..ba3a0bcc2f --- /dev/null +++ b/demo/demo1.ts @@ -0,0 +1,20 @@ +// Simple example to add text to a document +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph, TextRun } from "../build"; + +const doc = new Document(); + +const paragraph = new Paragraph("Hello World"); +const institutionText = new TextRun("Foo Bar").bold(); +const dateText = new TextRun("Github is the best").tab().bold(); +paragraph.addRun(institutionText); +paragraph.addRun(dateText); + +doc.addParagraph(paragraph); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo10.js b/demo/demo10.ts similarity index 73% rename from demo/demo10.js rename to demo/demo10.ts index 2b986dbf65..84713e777f 100644 --- a/demo/demo10.js +++ b/demo/demo10.ts @@ -1,8 +1,13 @@ -const docx = require("../build"); +// Add images to header and footer +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph, TextRun } from "../build"; + +// tslint:disable:no-shadowed-variable const PHONE_NUMBER = "07534563401"; const PROFILE_URL = "https://www.linkedin.com/in/dolan1"; -const EMAIL = "docx@docx.com"; +const EMAIL = "docx@com"; const experiences = [ { @@ -122,13 +127,15 @@ const achievements = [ ]; class DocumentCreator { - create(data) { - const experiences = data[0]; - const educations = data[1]; - const skills = data[2]; - const achivements = data[3]; - const document = new docx.Document(); - document.addParagraph(new docx.Paragraph("Dolan Miu").title()); + public create(data: object[]): Document { + // tslint:disable-next-line:no-any + const experiences = data[0] as any[]; + // tslint:disable-next-line:no-any + const educations = data[1] as any[]; + const skills = data[2] as object[]; + const achivements = data[3] as object[]; + const document = new Document(); + document.addParagraph(new Paragraph("Dolan Miu").title()); document.addParagraph(this.createContactInfo(PHONE_NUMBER, PROFILE_URL, EMAIL)); document.addParagraph(this.createHeading("Education")); @@ -181,23 +188,23 @@ class DocumentCreator { document.addParagraph(this.createHeading("References")); document.addParagraph( - new docx.Paragraph( + new Paragraph( "Dr. Dean Mohamedally Director of Postgraduate Studies Department of Computer Science, University College London Malet Place, Bloomsbury, London WC1E d.mohamedally@ucl.ac.uk", ), ); - document.addParagraph(new docx.Paragraph("More references upon request")); + document.addParagraph(new Paragraph("More references upon request")); document.addParagraph( - new docx.Paragraph( + new Paragraph( "This CV was generated in real-time based on my Linked-In profile from my personal website www.dolan.bio.", ).center(), ); return document; } - createContactInfo(phoneNumber, profileUrl, email) { - const paragraph = new docx.Paragraph().center(); - const contactInfo = new docx.TextRun(`Mobile: ${phoneNumber} | LinkedIn: ${profileUrl} | Email: ${email}`); - const address = new docx.TextRun("Address: 58 Elm Avenue, Kent ME4 6ER, UK").break(); + public createContactInfo(phoneNumber: string, profileUrl: string, email: string): Paragraph { + const paragraph = new Paragraph().center(); + const contactInfo = new TextRun(`Mobile: ${phoneNumber} | LinkedIn: ${profileUrl} | Email: ${email}`); + const address = new TextRun("Address: 58 Elm Avenue, Kent ME4 6ER, UK").break(); paragraph.addRun(contactInfo); paragraph.addRun(address); @@ -205,18 +212,18 @@ class DocumentCreator { return paragraph; } - createHeading(text) { - return new docx.Paragraph(text).heading1().thematicBreak(); + public createHeading(text: string): Paragraph { + return new Paragraph(text).heading1().thematicBreak(); } - createSubHeading(text) { - return new docx.Paragraph(text).heading2(); + public createSubHeading(text: string): Paragraph { + return new Paragraph(text).heading2(); } - createInstitutionHeader(institutionName, dateText) { - const paragraph = new docx.Paragraph().maxRightTabStop(); - const institution = new docx.TextRun(institutionName).bold(); - const date = new docx.TextRun(dateText).tab().bold(); + public createInstitutionHeader(institutionName: string, dateText: string): Paragraph { + const paragraph = new Paragraph().maxRightTabStop(); + const institution = new TextRun(institutionName).bold(); + const date = new TextRun(dateText).tab().bold(); paragraph.addRun(institution); paragraph.addRun(date); @@ -224,57 +231,61 @@ class DocumentCreator { return paragraph; } - createRoleText(roleText) { - const paragraph = new docx.Paragraph(); - const role = new docx.TextRun(roleText).italic(); + public createRoleText(roleText: string): Paragraph { + const paragraph = new Paragraph(); + const role = new TextRun(roleText).italic(); paragraph.addRun(role); return paragraph; } - createBullet(text) { - return new docx.Paragraph(text).bullet(); + public createBullet(text: string): Paragraph { + return new Paragraph(text).bullet(); } - createSkillList(skills) { - const paragraph = new docx.Paragraph(); + // tslint:disable-next-line:no-any + public createSkillList(skills: any[]): Paragraph { + const paragraph = new Paragraph(); const skillConcat = skills.map((skill) => skill.name).join(", ") + "."; - paragraph.addRun(new docx.TextRun(skillConcat)); + paragraph.addRun(new TextRun(skillConcat)); return paragraph; } - createAchivementsList(achivements) { - const arr = []; + // tslint:disable-next-line:no-any + public createAchivementsList(achivements: any[]): Paragraph[] { + const arr: Paragraph[] = []; for (const achievement of achivements) { - arr.push(new docx.Paragraph(achievement.name).bullet()); + const paragraph = new Paragraph(achievement.name).bullet(); + arr.push(paragraph); } return arr; } - createInterests(interests) { - const paragraph = new docx.Paragraph(); + public createInterests(interests: string): Paragraph { + const paragraph = new Paragraph(); - paragraph.addRun(new docx.TextRun(interests)); + paragraph.addRun(new TextRun(interests)); return paragraph; } - splitParagraphIntoBullets(text) { + public splitParagraphIntoBullets(text: string): string[] { return text.split("\n\n"); } - createPositionDateText(startDate, endDate, isCurrent) { + // tslint:disable-next-line:no-any + public createPositionDateText(startDate: any, endDate: any, isCurrent: boolean): string { const startDateText = this.getMonthFromInt(startDate.month) + ". " + startDate.year; const endDateText = isCurrent ? "Present" : `${this.getMonthFromInt(endDate.month)}. ${endDate.year}`; return `${startDateText} - ${endDateText}`; } - getMonthFromInt(value) { + public getMonthFromInt(value: number): string { switch (value) { case 1: return "Jan"; @@ -300,6 +311,8 @@ class DocumentCreator { return "Nov"; case 12: return "Dec"; + default: + return "N/A"; } } } @@ -308,7 +321,8 @@ const documentCreator = new DocumentCreator(); const doc = documentCreator.create([experiences, education, skills, achievements]); -var exporter = new docx.LocalPacker(doc); -exporter.pack("Dolan Miu CV"); +const packer = new Packer(); -console.log("Document created successfully at project root!"); +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo11.js b/demo/demo11.ts similarity index 72% rename from demo/demo11.js rename to demo/demo11.ts index 2c6305106e..76ec915060 100644 --- a/demo/demo11.js +++ b/demo/demo11.ts @@ -1,6 +1,9 @@ -const docx = require("../build"); +// Setting styles with JavaScript configuration +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph, Table } from "../build"; -const doc = new docx.Document(undefined, { +const doc = new Document(undefined, { top: 700, right: 700, bottom: 700, @@ -15,7 +18,7 @@ doc.Styles.createParagraphStyle("Heading1", "Heading 1") .size(52) .center() .bold() - .color(000000) + .color("000000") .spacing({ line: 340 }) .underline("single", "000000"); @@ -51,7 +54,7 @@ doc.Styles.createParagraphStyle("normalPara", "Normal Para") .font("Calibri") .quickFormat() .leftTabStop(453.543307087) - .maxRightTabStop(453.543307087) + .maxRightTabStop() .size(26) .spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 }); @@ -69,7 +72,7 @@ doc.Styles.createParagraphStyle("aside", "Aside") .next("Normal") .color("999999") .italics() - .indent(720) + .indent({ left: 720 }) .spacing({ line: 276 }); doc.Styles.createParagraphStyle("wellSpaced", "Well Spaced") @@ -80,7 +83,7 @@ doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph") .quickFormat() .basedOn("Normal"); -doc.createImage("./demo/images/pizza.gif"); +doc.createImage(fs.readFileSync("./demo/images/pizza.gif")); doc .createParagraph("HEADING") .heading1() @@ -104,29 +107,35 @@ doc.createParagraph("Sir,").style("normalPara"); doc.createParagraph("BRIEF DESCRIPTION").style("normalPara"); -var table = new docx.Table(4, 4); -var contentParagraph = table +const table = new Table(4, 4); +table .getRow(0) .getCell(0) - .addContent(new docx.Paragraph("Pole No.")); -table.properties.width = 10000; + .addContent(new Paragraph("Pole No.")); +// table.Properties.width = 10000; doc.addTable(table); -var arrboth = [{ - image: "./demo/images/pizza.gif", - comment: "Test" -}, { - image: "./demo/images/pizza.gif", - comment: "Test 2" -}]; +const arrboth = [ + { + image: "./demo/images/pizza.gif", + comment: "Test", + }, + { + image: "./demo/images/pizza.gif", + comment: "Test 2", + }, +]; -arrboth.forEach(function(item) { +arrboth.forEach((item) => { const para = doc.createParagraph(); - para.createTextRun(doc.createImage(item.image)); - para.properties.width = 60; - para.properties.height = 90; + para.addImage(doc.createImage(fs.readFileSync(item.image))); + // para.Properties.width = 60; + // para.Properties.height = 90; doc.createParagraph(item.comment).style("normalPara2"); }); -var exporter = new docx.LocalPacker(doc); -exporter.pack("My Document"); +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo12.js b/demo/demo12.js deleted file mode 100644 index 7fd5eb5ffc..0000000000 --- a/demo/demo12.js +++ /dev/null @@ -1,21 +0,0 @@ -const docx = require("../build"); - -var doc = new docx.Document(); - -var paragraph = new docx.Paragraph("Hello World"); -doc.addParagraph(paragraph); - -const image = doc.createImage("./demo/images/pizza.gif"); -const image2 = doc.createImage("./demo/images/pizza.gif"); -const image3 = doc.createImage("./demo/images/pizza.gif"); -const image4 = doc.createImage("./demo/images/pizza.gif"); - -image.scale(0.5); -image2.scale(1) -image3.scale(2.5); -image4.scale(4); - -var exporter = new docx.LocalPacker(doc); -exporter.pack("My Document"); - -console.log("Document created successfully at project root!"); diff --git a/demo/demo12.ts b/demo/demo12.ts new file mode 100644 index 0000000000..a3479c3cbf --- /dev/null +++ b/demo/demo12.ts @@ -0,0 +1,25 @@ +// Scaling images +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph } from "../build"; + +const doc = new Document(); + +const paragraph = new Paragraph("Hello World"); +doc.addParagraph(paragraph); + +const image = doc.createImage(fs.readFileSync("./demo/images/pizza.gif")); +const image2 = doc.createImage(fs.readFileSync("./demo/images/pizza.gif")); +const image3 = doc.createImage(fs.readFileSync("./demo/images/pizza.gif")); +const image4 = doc.createImage(fs.readFileSync("./demo/images/pizza.gif")); + +image.scale(0.5); +image2.scale(1); +image3.scale(2.5); +image4.scale(4); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo13.js b/demo/demo13.js deleted file mode 100644 index f141547d7a..0000000000 --- a/demo/demo13.js +++ /dev/null @@ -1,26 +0,0 @@ -// This example shows 3 styles -const fs = require('fs'); -const docx = require('../build'); - -const styles = fs.readFileSync('./demo/assets/custom-styles.xml', 'utf-8'); -const doc = new docx.Document({ - title: 'Title', - externalStyles: styles -}); - -doc.createParagraph('Cool Heading Text').heading1(); - -let paragraph = new docx.Paragraph('This is a custom named style from the template "MyFancyStyle"'); -paragraph.style('MyFancyStyle'); -doc.addParagraph(paragraph); - -doc.createParagraph('Some normal text') - -doc.createParagraph('MyFancyStyle again').style('MyFancyStyle'); -paragraph.style('MyFancyStyle'); -doc.addParagraph(paragraph); - -var exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); - -console.log('Document created successfully at project root!'); diff --git a/demo/demo13.ts b/demo/demo13.ts new file mode 100644 index 0000000000..b80b9fdc22 --- /dev/null +++ b/demo/demo13.ts @@ -0,0 +1,28 @@ +// This example shows 3 styles using XML styles +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph } from "../build"; + +const styles = fs.readFileSync("./demo/assets/custom-styles.xml", "utf-8"); +const doc = new Document({ + title: "Title", + externalStyles: styles, +}); + +doc.createParagraph("Cool Heading Text").heading1(); + +const paragraph = new Paragraph('This is a custom named style from the template "MyFancyStyle"'); +paragraph.style("MyFancyStyle"); +doc.addParagraph(paragraph); + +doc.createParagraph("Some normal text"); + +doc.createParagraph("MyFancyStyle again").style("MyFancyStyle"); +paragraph.style("MyFancyStyle"); +doc.addParagraph(paragraph); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo14.js b/demo/demo14.js deleted file mode 100644 index b2262f106a..0000000000 --- a/demo/demo14.js +++ /dev/null @@ -1,24 +0,0 @@ -const docx = require('../build'); - -var doc = new docx.Document(); - -doc.createParagraph("First Page").pageBreak() -doc.createParagraph("Second Page"); - -var pageNumber = new docx.TextRun().pageNumber() - -var pageoneheader = new docx.Paragraph("First Page Header ").right(); - -pageoneheader.addRun(pageNumber); -var firstPageHeader = doc.createFirstPageHeader(); -firstPageHeader.addParagraph(pageoneheader); - -var pagetwoheader = new docx.Paragraph("My Title ").right(); - -pagetwoheader.addRun(pageNumber) -doc.Header.addParagraph(pagetwoheader) - -var exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); - -console.log('Document created successfully at project root!'); diff --git a/demo/demo14.ts b/demo/demo14.ts new file mode 100644 index 0000000000..94d45a66a6 --- /dev/null +++ b/demo/demo14.ts @@ -0,0 +1,28 @@ +// Page numbers +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph, TextRun } from "../build"; + +const doc = new Document(); + +doc.createParagraph("First Page").pageBreak(); +doc.createParagraph("Second Page"); + +const pageNumber = new TextRun("Page ").pageNumber(); + +const pageoneheader = new Paragraph("First Page Header ").right(); + +pageoneheader.addRun(pageNumber); +const firstPageHeader = doc.createFirstPageHeader(); +firstPageHeader.addParagraph(pageoneheader); + +const pagetwoheader = new Paragraph("My Title ").right(); + +pagetwoheader.addRun(pageNumber); +doc.Header.addParagraph(pagetwoheader); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo15.js b/demo/demo15.js deleted file mode 100644 index 61d9351817..0000000000 --- a/demo/demo15.js +++ /dev/null @@ -1,14 +0,0 @@ -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").pageBreakBefore(); - -doc.addParagraph(paragraph); -doc.addParagraph(paragraph2); - -var exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); - -console.log('Document created successfully at project root!'); diff --git a/demo/demo15.ts b/demo/demo15.ts new file mode 100644 index 0000000000..5da6c826b5 --- /dev/null +++ b/demo/demo15.ts @@ -0,0 +1,18 @@ +// Page break before example +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph } from "../build"; + +const doc = new Document(); + +const paragraph = new Paragraph("Hello World"); +const paragraph2 = new Paragraph("Hello World on another page").pageBreakBefore(); + +doc.addParagraph(paragraph); +doc.addParagraph(paragraph2); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo16.js b/demo/demo16.js deleted file mode 100644 index ce0b162dc1..0000000000 --- a/demo/demo16.js +++ /dev/null @@ -1,36 +0,0 @@ -const docx = require("../build"); - -var doc = new docx.Document(); - -var paragraph = new docx.Paragraph("Hello World").pageBreak(); - -doc.addParagraph(paragraph); - -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, -}); - -doc.createParagraph("hello"); - -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!"); diff --git a/demo/demo16.ts b/demo/demo16.ts new file mode 100644 index 0000000000..4b92dc0d7e --- /dev/null +++ b/demo/demo16.ts @@ -0,0 +1,40 @@ +// Multiple sections and headers +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, PageNumberFormat, PageOrientation, Paragraph } from "../build"; + +const doc = new Document(); + +const paragraph = new Paragraph("Hello World").pageBreak(); + +doc.addParagraph(paragraph); + +const header = doc.createHeader(); +header.createParagraph("Header on another page"); +const footer = doc.createFooter(); +footer.createParagraph("Footer on another page"); + +doc.addSection({ + headerId: header.Header.ReferenceId, + footerId: footer.Footer.ReferenceId, + pageNumberStart: 1, + pageNumberFormatType: PageNumberFormat.DECIMAL, +}); + +doc.createParagraph("hello"); + +doc.addSection({ + headerId: header.Header.ReferenceId, + footerId: footer.Footer.ReferenceId, + pageNumberStart: 1, + pageNumberFormatType: PageNumberFormat.DECIMAL, + orientation: PageOrientation.LANDSCAPE, +}); + +doc.createParagraph("hello in landscape"); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo17.js b/demo/demo17.js deleted file mode 100644 index 266e556b7d..0000000000 --- a/demo/demo17.js +++ /dev/null @@ -1,17 +0,0 @@ -const docx = require('../build'); - -var doc = new docx.Document(); - -var paragraph = new docx.Paragraph("Hello World").referenceFootnote(1); -var paragraph2 = new docx.Paragraph("Hello World").referenceFootnote(2); - -doc.addParagraph(paragraph); -doc.addParagraph(paragraph2); - -doc.createFootnote(new docx.Paragraph("Test")); -doc.createFootnote(new docx.Paragraph("My amazing reference")); - -var exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); - -console.log('Document created successfully at project root!'); diff --git a/demo/demo17.ts b/demo/demo17.ts new file mode 100644 index 0000000000..c48a2e67e8 --- /dev/null +++ b/demo/demo17.ts @@ -0,0 +1,21 @@ +// Footnotes +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph } from "../build"; + +const doc = new Document(); + +const paragraph = new Paragraph("Hello World").referenceFootnote(1); +const paragraph2 = new Paragraph("Hello World").referenceFootnote(2); + +doc.addParagraph(paragraph); +doc.addParagraph(paragraph2); + +doc.createFootnote(new Paragraph("Test")); +doc.createFootnote(new Paragraph("My amazing reference")); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo18.js b/demo/demo18.ts similarity index 87% rename from demo/demo18.js rename to demo/demo18.ts index 9035d19e7a..4a61488c1f 100644 --- a/demo/demo18.js +++ b/demo/demo18.ts @@ -1,15 +1,17 @@ // Insert image from a buffer -const docx = require('../build'); +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer } from "../build"; -var doc = new docx.Document(); +const doc = new Document(); +const imageBase64Data = `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`; -const imageBase64Data = `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` +// doc.createImage(Buffer.from(imageBase64Data, 'base64')); +doc.createImage(Buffer.from(imageBase64Data, "base64"), 100, 100); -// doc.createImageFromBuffer(Buffer.from(imageBase64Data, 'base64')); -doc.createImageFromBuffer(Buffer.from(imageBase64Data, 'base64'), 100, 100); +const packer = new Packer(); -var exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); - -console.log('Document created successfully at project root!'); +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo19.js b/demo/demo19.js deleted file mode 100644 index 3aafebc142..0000000000 --- a/demo/demo19.js +++ /dev/null @@ -1,20 +0,0 @@ -const fs = require("fs"); -const docx = require("../build"); - -var doc = new docx.Document(); - -var paragraph = new docx.Paragraph("Hello World"); -var institutionText = new docx.TextRun("Foo").bold(); -var dateText = new docx.TextRun("Bar").tab().bold(); -paragraph.addRun(institutionText); -paragraph.addRun(dateText); - -doc.addParagraph(paragraph); - -var exporter = new docx.BufferPacker(doc); -exporter.pack("My Document").then((buffer) => { - // At this point, you can do anything with the buffer, including casting it to a string etc. - console.log(buffer); - fs.writeFileSync('My Document.docx', buffer); - console.log("Document created successfully at project root!"); -}); diff --git a/demo/demo19.ts b/demo/demo19.ts new file mode 100644 index 0000000000..52b5fd6694 --- /dev/null +++ b/demo/demo19.ts @@ -0,0 +1,20 @@ +// Export to base64 string - Useful in a browser environment. +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph, TextRun } from "../build"; + +const doc = new Document(); + +const paragraph = new Paragraph("Hello World"); +const institutionText = new TextRun("Foo").bold(); +const dateText = new TextRun("Bar").tab().bold(); +paragraph.addRun(institutionText); +paragraph.addRun(dateText); + +doc.addParagraph(paragraph); + +const packer = new Packer(); + +packer.toBase64String(doc).then((str) => { + fs.writeFileSync("My Document.docx", str); +}); diff --git a/demo/demo2.js b/demo/demo2.js deleted file mode 100644 index 841e31b6bf..0000000000 --- a/demo/demo2.js +++ /dev/null @@ -1,74 +0,0 @@ -const docx = require('../build'); - -const doc = new docx.Document({ - creator: 'Clippy', - title: 'Sample Document', - description: 'A brief example of using docx', -}); - -doc.Styles.createParagraphStyle('Heading1', 'Heading 1') - .basedOn("Normal") - .next("Normal") - .quickFormat() - .size(28) - .bold() - .italics() - .spacing({after: 120}); - -doc.Styles.createParagraphStyle('Heading2', 'Heading 2') - .basedOn("Normal") - .next("Normal") - .quickFormat() - .size(26) - .bold() - .underline('double', 'FF0000') - .spacing({before: 240, after: 120}); - -doc.Styles.createParagraphStyle('aside', 'Aside') - .basedOn('Normal') - .next('Normal') - .color('999999') - .italics() - .indent(720) - .spacing({line: 276}); - -doc.Styles.createParagraphStyle('wellSpaced', 'Well Spaced') - .basedOn('Normal') - .spacing({line: 276, before: 20 * 72 * .1, after: 20 * 72 * .05}); - -doc.Styles.createParagraphStyle('ListParagraph', 'List Paragraph') - .quickFormat() - .basedOn('Normal'); - - -const numberedAbstract = doc.Numbering.createAbstractNumbering(); -numberedAbstract.createLevel(0, "lowerLetter", "%1)", "left"); - -doc.createParagraph('Test heading1, bold and italicized').heading1(); -doc.createParagraph('Some simple content'); -doc.createParagraph('Test heading2 with double red underline').heading2(); - -const letterNumbering = doc.Numbering.createConcreteNumbering(numberedAbstract); -const letterNumbering5 = doc.Numbering.createConcreteNumbering(numberedAbstract); -letterNumbering5.overrideLevel(0, 5); - -doc.createParagraph('Option1').setNumbering(letterNumbering, 0); -doc.createParagraph('Option5 -- override 2 to 5').setNumbering(letterNumbering5, 0); -doc.createParagraph('Option3').setNumbering(letterNumbering, 0); - -doc.createParagraph() - .createTextRun('Some monospaced content') - .font('Monospace'); - -doc.createParagraph('An aside, in light gray italics and indented').style('aside'); -doc.createParagraph('This is normal, but well-spaced text').style('wellSpaced'); -const para = doc.createParagraph(); -para.createTextRun('This is a bold run,').bold(); -para.createTextRun(' switching to normal '); -para.createTextRun('and then underlined ').underline(); -para.createTextRun('and back to normal.'); - -const exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); - -console.log('Document created successfully at project root!'); diff --git a/demo/demo2.ts b/demo/demo2.ts new file mode 100644 index 0000000000..f32779fa06 --- /dev/null +++ b/demo/demo2.ts @@ -0,0 +1,78 @@ +// Example on how to customise the look at feel using Styles +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer } from "../build"; + +const doc = new Document({ + creator: "Clippy", + title: "Sample Document", + description: "A brief example of using docx", +}); + +doc.Styles.createParagraphStyle("Heading1", "Heading 1") + .basedOn("Normal") + .next("Normal") + .quickFormat() + .size(28) + .bold() + .italics() + .spacing({ after: 120 }); + +doc.Styles.createParagraphStyle("Heading2", "Heading 2") + .basedOn("Normal") + .next("Normal") + .quickFormat() + .size(26) + .bold() + .underline("double", "FF0000") + .spacing({ before: 240, after: 120 }); + +doc.Styles.createParagraphStyle("aside", "Aside") + .basedOn("Normal") + .next("Normal") + .color("999999") + .italics() + .indent({ left: 720 }) + .spacing({ line: 276 }); + +doc.Styles.createParagraphStyle("wellSpaced", "Well Spaced") + .basedOn("Normal") + .spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 }); + +doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph") + .quickFormat() + .basedOn("Normal"); + +const numberedAbstract = doc.Numbering.createAbstractNumbering(); +numberedAbstract.createLevel(0, "lowerLetter", "%1)", "left"); + +doc.createParagraph("Test heading1, bold and italicized").heading1(); +doc.createParagraph("Some simple content"); +doc.createParagraph("Test heading2 with double red underline").heading2(); + +const letterNumbering = doc.Numbering.createConcreteNumbering(numberedAbstract); +const letterNumbering5 = doc.Numbering.createConcreteNumbering(numberedAbstract); +letterNumbering5.overrideLevel(0, 5); + +doc.createParagraph("Option1").setNumbering(letterNumbering, 0); +doc.createParagraph("Option5 -- override 2 to 5").setNumbering(letterNumbering5, 0); +doc.createParagraph("Option3").setNumbering(letterNumbering, 0); + +doc + .createParagraph() + .createTextRun("Some monospaced content") + .font("Monospace"); + +doc.createParagraph("An aside, in light gray italics and indented").style("aside"); +doc.createParagraph("This is normal, but well-spaced text").style("wellSpaced"); +const para = doc.createParagraph(); +para.createTextRun("This is a bold run,").bold(); +para.createTextRun(" switching to normal "); +para.createTextRun("and then underlined ").underline(); +para.createTextRun("and back to normal."); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo20.js b/demo/demo20.js deleted file mode 100644 index 12ffb58549..0000000000 --- a/demo/demo20.js +++ /dev/null @@ -1,17 +0,0 @@ -const docx = require("../build"); - -var doc = new docx.Document(); - -const table = doc.createTable(4, 4); -table - .getCell(2, 2) - .addContent(new docx.Paragraph("Hello")) - .cellProperties.borders.addTopBorder(docx.BorderStyle.DASH_DOT_STROKED, 3, "red") - .addBottomBorder(docx.BorderStyle.DOUBLE, 3, "blue") - .addStartBorder(docx.BorderStyle.DOT_DOT_DASH, 3, "green") - .addEndBorder(docx.BorderStyle.DOT_DOT_DASH, 3, "#ff8000"); - -var exporter = new docx.LocalPacker(doc); -exporter.pack("My Document"); - -console.log("Document created successfully at project root!"); diff --git a/demo/demo20.ts b/demo/demo20.ts new file mode 100644 index 0000000000..f3f43edb26 --- /dev/null +++ b/demo/demo20.ts @@ -0,0 +1,21 @@ +// Add custom borders to table cell +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { BorderStyle, Document, Packer, Paragraph } from "../build"; + +const doc = new Document(); + +const table = doc.createTable(4, 4); +table + .getCell(2, 2) + .addContent(new Paragraph("Hello")) + .CellProperties.Borders.addTopBorder(BorderStyle.DASH_DOT_STROKED, 3, "red") + .addBottomBorder(BorderStyle.DOUBLE, 3, "blue") + .addStartBorder(BorderStyle.DOT_DOT_DASH, 3, "green") + .addEndBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000"); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo21.js b/demo/demo21.js deleted file mode 100644 index fd74e61b31..0000000000 --- a/demo/demo21.js +++ /dev/null @@ -1,31 +0,0 @@ -/** This demo shows how to create bookmarks then link to them with internal hyperlinks */ - -const docx = require("../build"); - -const loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mi velit, convallis convallis scelerisque nec, faucibus nec leo. Phasellus at posuere mauris, tempus dignissim velit. Integer et tortor dolor. Duis auctor efficitur mattis. Vivamus ut metus accumsan tellus auctor sollicitudin venenatis et nibh. Cras quis massa ac metus fringilla venenatis. Proin rutrum mauris purus, ut suscipit magna consectetur id. Integer consectetur sollicitudin ante, vitae faucibus neque efficitur in. Praesent ultricies nibh lectus. Mauris pharetra id odio eget iaculis. Duis dictum, risus id pellentesque rutrum, lorem quam malesuada massa, quis ullamcorper turpis urna a diam. Cras vulputate metus vel massa porta ullamcorper. Etiam porta condimentum nulla nec tristique. Sed nulla urna, pharetra non tortor sed, sollicitudin molestie diam. Maecenas enim leo, feugiat eget vehicula id, sollicitudin vitae ante."; - -const doc = new docx.Document({ - creator: 'Clippy', - title: 'Sample Document', - description: 'A brief example of using docx with bookmarks and internal hyperlinks', -}); - -const anchorId = "anchorID"; - -// First create the bookmark -const bookmark = doc.createBookmark(anchorId, "Lorem Ipsum"); -// That has header styling -doc.createParagraph().addBookmark(bookmark).heading1(); -doc.createParagraph("\n"); - -doc.createParagraph(loremIpsum); -doc.createParagraph().pageBreak(); - -// Now the link back up to the bookmark -const hyperlink = doc.createInternalHyperLink(anchorId, `Click me!`); -doc.createParagraph().addHyperLink(hyperlink); - -var exporter = new docx.LocalPacker(doc); -exporter.pack("My Document"); - -console.log("Document created successfully at project root!"); diff --git a/demo/demo21.ts b/demo/demo21.ts new file mode 100644 index 0000000000..b02da56065 --- /dev/null +++ b/demo/demo21.ts @@ -0,0 +1,37 @@ +// This demo shows how to create bookmarks then link to them with internal hyperlinks +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer } from "../build"; + +const loremIpsum = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mi velit, convallis convallis scelerisque nec, faucibus nec leo. Phasellus at posuere mauris, tempus dignissim velit. Integer et tortor dolor. Duis auctor efficitur mattis. Vivamus ut metus accumsan tellus auctor sollicitudin venenatis et nibh. Cras quis massa ac metus fringilla venenatis. Proin rutrum mauris purus, ut suscipit magna consectetur id. Integer consectetur sollicitudin ante, vitae faucibus neque efficitur in. Praesent ultricies nibh lectus. Mauris pharetra id odio eget iaculis. Duis dictum, risus id pellentesque rutrum, lorem quam malesuada massa, quis ullamcorper turpis urna a diam. Cras vulputate metus vel massa porta ullamcorper. Etiam porta condimentum nulla nec tristique. Sed nulla urna, pharetra non tortor sed, sollicitudin molestie diam. Maecenas enim leo, feugiat eget vehicula id, sollicitudin vitae ante."; + +const doc = new Document({ + creator: "Clippy", + title: "Sample Document", + description: "A brief example of using docx with bookmarks and internal hyperlinks", +}); + +const anchorId = "anchorID"; + +// First create the bookmark +const bookmark = doc.createBookmark(anchorId, "Lorem Ipsum"); +// That has header styling +doc + .createParagraph() + .addBookmark(bookmark) + .heading1(); +doc.createParagraph("\n"); + +doc.createParagraph(loremIpsum); +doc.createParagraph().pageBreak(); + +// Now the link back up to the bookmark +const hyperlink = doc.createInternalHyperLink(anchorId, `Click me!`); +doc.createParagraph().addHyperLink(hyperlink); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo22.js b/demo/demo22.js deleted file mode 100644 index ce42f22ccd..0000000000 --- a/demo/demo22.js +++ /dev/null @@ -1,14 +0,0 @@ -const docx = require('../build'); - -var doc = new docx.Document(); - -var textRun = new docx.TextRun("שלום עולם").rtl(); -var paragraph = new docx.Paragraph().bidi(); -paragraph.addRun(textRun); - -doc.addParagraph(paragraph); - -var exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); - -console.log('Document created successfully at project root!'); diff --git a/demo/demo22.ts b/demo/demo22.ts new file mode 100644 index 0000000000..c5051ab019 --- /dev/null +++ b/demo/demo22.ts @@ -0,0 +1,27 @@ +// This demo shows right to left for special languages +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph, TextRun } from "../build"; + +const doc = new Document(); + +const paragraph1 = new Paragraph().bidirectional(); +const textRun1 = new TextRun("שלום עולם").rightToLeft(); +paragraph1.addRun(textRun1); +doc.addParagraph(paragraph1); + +const paragraph2 = new Paragraph().bidirectional(); +const textRun2 = new TextRun("שלום עולם").bold().rightToLeft(); +paragraph2.addRun(textRun2); +doc.addParagraph(paragraph2); + +const paragraph3 = new Paragraph().bidirectional(); +const textRun3 = new TextRun("שלום עולם").italic().rightToLeft(); +paragraph3.addRun(textRun3); +doc.addParagraph(paragraph3); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo23.js b/demo/demo23.ts similarity index 76% rename from demo/demo23.js rename to demo/demo23.ts index 906f141e90..47ee26a6f9 100644 --- a/demo/demo23.js +++ b/demo/demo23.ts @@ -1,28 +1,34 @@ // This demo adds an image to the Media cache, and then insert to the document afterwards -const docx = require("../build"); +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Media, Packer, Paragraph } from "../build"; -var doc = new docx.Document(); +const doc = new Document(); -var paragraph = new docx.Paragraph("Hello World"); +const paragraph = new Paragraph("Hello World"); doc.addParagraph(paragraph); -const image = docx.Media.addImage(doc, "./demo/images/image1.jpeg"); -const image2 = docx.Media.addImage(doc, "./demo/images/dog.png"); -const image3 = docx.Media.addImage(doc, "./demo/images/cat.jpg"); -const image4 = docx.Media.addImage(doc, "./demo/images/parrots.bmp"); -const image5 = docx.Media.addImage(doc, "./demo/images/pizza.gif"); +const image = Media.addImage(doc, "./demo/images/image1.jpeg"); +const image2 = Media.addImage(doc, "./demo/images/dog.png"); +const image3 = Media.addImage(doc, "./demo/images/cat.jpg"); +const image4 = Media.addImage(doc, "./demo/images/parrots.bmp"); +const image5 = Media.addImage(doc, "./demo/images/pizza.gif"); -const imageBase64Data = `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` -const image6 = docx.Media.addImageFromBuffer(doc, Buffer.from(imageBase64Data, 'base64'), 100, 100); +const imageBase64Data = `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`; +const image6 = Media.addImage(doc, Buffer.from(imageBase64Data, "base64"), 100, 100); -doc.insertImage(image); -doc.insertImage(image2); -doc.insertImage(image3); -doc.insertImage(image4); -doc.insertImage(image5); -doc.insertImage(image6); +// I am adding an image to the paragraph rather than the document to make the image inline +paragraph.addImage(image5); -var exporter = new docx.LocalPacker(doc); -exporter.pack("My Document"); +doc.addImage(image); +doc.addImage(image2); +doc.addImage(image3); +doc.addImage(image4); +doc.addImage(image5); +doc.addImage(image6); -console.log("Document created successfully at project root!"); +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo24.js b/demo/demo24.js deleted file mode 100644 index a95189a6ed..0000000000 --- a/demo/demo24.js +++ /dev/null @@ -1,15 +0,0 @@ -// Add image to table cell -const docx = require('../build'); - -var doc = new docx.Document(); - -const table = doc.createTable(4, 4); -table.getCell(2, 2).addContent(new docx.Paragraph('Hello')); - -const image = docx.Media.addImage(doc, "./demo/images/image1.jpeg"); -table.getCell(1, 1).addContent(image); - -var exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); - -console.log('Document created successfully at project root!'); diff --git a/demo/demo24.ts b/demo/demo24.ts new file mode 100644 index 0000000000..23828aec65 --- /dev/null +++ b/demo/demo24.ts @@ -0,0 +1,18 @@ +// Add image to table cell +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Media, Packer, Paragraph } from "../build"; + +const doc = new Document(); + +const table = doc.createTable(4, 4); +table.getCell(2, 2).addContent(new Paragraph("Hello")); + +const image = Media.addImage(doc, "./demo/images/image1.jpeg"); +table.getCell(1, 1).addContent(image.Paragraph); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo25.js b/demo/demo25.js deleted file mode 100644 index 85dd29febe..0000000000 --- a/demo/demo25.js +++ /dev/null @@ -1,15 +0,0 @@ -const fs = require("fs"); -const docx = require("../build"); - -var doc = new docx.Document(); - -var paragraph = new docx.Paragraph("Hello World"); -var institutionText = new docx.TextRun("Foo").bold(); -var dateText = new docx.TextRun("Bar").tab().bold(); -paragraph.addRun(institutionText); -paragraph.addRun(dateText); - -doc.addParagraph(paragraph); - -var exporter = new docx.LocalPacker(doc); -exporter.packPdf("My Document"); diff --git a/demo/demo26.ts b/demo/demo26.ts new file mode 100644 index 0000000000..9fa560d260 --- /dev/null +++ b/demo/demo26.ts @@ -0,0 +1,22 @@ +// Creates two paragraphs, one with a border and one without +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph } from "../build"; + +const doc = new Document(); + +const paragraph = new Paragraph("No border!"); + +doc.addParagraph(paragraph); + +const borderParagraph = new Paragraph("I have borders on my top and bottom sides!").createBorder(); +borderParagraph.Borders.addTopBorder(); +borderParagraph.Borders.addBottomBorder(); + +doc.addParagraph(borderParagraph); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo3.js b/demo/demo3.js deleted file mode 100644 index accd597905..0000000000 --- a/demo/demo3.js +++ /dev/null @@ -1,45 +0,0 @@ -const docx = require('../build'); - -var doc = new docx.Document(); - -const numbering = new docx.Numbering(); - -const abstractNum = numbering.createAbstractNumbering(); -abstractNum.createLevel(0, "upperRoman", "%1", "start") - .addParagraphProperty(new docx.Indent(720, 260)); -abstractNum.createLevel(1, "decimal", "%2.", "start") - .addParagraphProperty(new docx.Indent(1440, 980)); -abstractNum.createLevel(2, "lowerLetter", "%3)", "start") - .addParagraphProperty(new docx.Indent(2160, 1700)); - -const concrete = numbering.createConcreteNumbering(abstractNum); - -var topLevelP = new docx.Paragraph("Hey you"); -var subP = new docx.Paragraph("What's up fam"); -var secondSubP = new docx.Paragraph("Hello World 2"); -var subSubP = new docx.Paragraph("Yeah boi"); - -topLevelP.setNumbering(concrete, 0); -subP.setNumbering(concrete, 1); -secondSubP.setNumbering(concrete, 1); -subSubP.setNumbering(concrete, 2); - -doc.addParagraph(topLevelP); -doc.addParagraph(subP); -doc.addParagraph(secondSubP); -doc.addParagraph(subSubP); - -var bullet1 = new docx.Paragraph("Hey you").bullet(); -var bullet2 = new docx.Paragraph("What's up fam").bullet(1); -var bullet3 = new docx.Paragraph("Hello World 2").bullet(2); -var bullet4 = new docx.Paragraph("Yeah boi").bullet(3); - -doc.addParagraph(bullet1); -doc.addParagraph(bullet2); -doc.addParagraph(bullet3); -doc.addParagraph(bullet4); - -var exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); - -console.log('Document created successfully at project root!'); diff --git a/demo/demo3.ts b/demo/demo3.ts new file mode 100644 index 0000000000..ac3680ef32 --- /dev/null +++ b/demo/demo3.ts @@ -0,0 +1,46 @@ +// Numbering and bullet points example +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Indent, Numbering, Packer, Paragraph } from "../build"; + +const doc = new Document(); + +const numbering = new Numbering(); + +const abstractNum = numbering.createAbstractNumbering(); +abstractNum.createLevel(0, "upperRoman", "%1", "start").addParagraphProperty(new Indent({ left: 720, hanging: 260 })); +abstractNum.createLevel(1, "decimal", "%2.", "start").addParagraphProperty(new Indent({ left: 1440, hanging: 980 })); +abstractNum.createLevel(2, "lowerLetter", "%3)", "start").addParagraphProperty(new Indent({ left: 14402160, hanging: 1700 })); + +const concrete = numbering.createConcreteNumbering(abstractNum); + +const topLevelP = new Paragraph("Hey you"); +const subP = new Paragraph("What's up fam"); +const secondSubP = new Paragraph("Hello World 2"); +const subSubP = new Paragraph("Yeah boi"); + +topLevelP.setNumbering(concrete, 0); +subP.setNumbering(concrete, 1); +secondSubP.setNumbering(concrete, 1); +subSubP.setNumbering(concrete, 2); + +doc.addParagraph(topLevelP); +doc.addParagraph(subP); +doc.addParagraph(secondSubP); +doc.addParagraph(subSubP); + +const bullet1 = new Paragraph("Hey you").bullet(); +const bullet2 = new Paragraph("What's up fam").bullet(1); +const bullet3 = new Paragraph("Hello World 2").bullet(2); +const bullet4 = new Paragraph("Yeah boi").bullet(3); + +doc.addParagraph(bullet1); +doc.addParagraph(bullet2); +doc.addParagraph(bullet3); +doc.addParagraph(bullet4); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo4.js b/demo/demo4.js deleted file mode 100644 index 6ded74254c..0000000000 --- a/demo/demo4.js +++ /dev/null @@ -1,12 +0,0 @@ -const docx = require('../build'); - -var doc = new docx.Document(); - -const table = doc.createTable(4, 4); -table.getCell(2, 2).addContent(new docx.Paragraph('Hello')); - - -var exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); - -console.log('Document created successfully at project root!'); diff --git a/demo/demo4.ts b/demo/demo4.ts new file mode 100644 index 0000000000..eb6f5b8c09 --- /dev/null +++ b/demo/demo4.ts @@ -0,0 +1,15 @@ +// Example of how you would create a table and add data to it +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph } from "../build"; + +const doc = new Document(); + +const table = doc.createTable(4, 4); +table.getCell(2, 2).addContent(new Paragraph("Hello")); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo5.js b/demo/demo5.js deleted file mode 100644 index 89638ad49b..0000000000 --- a/demo/demo5.js +++ /dev/null @@ -1,17 +0,0 @@ -const docx = require("../build"); - -var doc = new docx.Document(); - -var paragraph = new docx.Paragraph("Hello World"); -doc.addParagraph(paragraph); - -doc.createImage("./demo/images/image1.jpeg"); -doc.createImage("./demo/images/dog.png"); -doc.createImage("./demo/images/cat.jpg"); -doc.createImage("./demo/images/parrots.bmp"); -doc.createImage("./demo/images/pizza.gif"); - -var exporter = new docx.LocalPacker(doc); -exporter.pack("My Document"); - -console.log("Document created successfully at project root!"); diff --git a/demo/demo5.ts b/demo/demo5.ts new file mode 100644 index 0000000000..e56cdfc01e --- /dev/null +++ b/demo/demo5.ts @@ -0,0 +1,21 @@ +// Example of how to add images to the document - You can use Buffers, UInt8Arrays or Base64 strings +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph } from "../build"; + +const doc = new Document(); + +const paragraph = new Paragraph("Hello World"); +doc.addParagraph(paragraph); + +doc.createImage(fs.readFileSync("./demo/images/image1.jpeg")); +doc.createImage(fs.readFileSync("./demo/images/dog.png").toString("base64")); +doc.createImage(fs.readFileSync("./demo/images/cat.jpg")); +doc.createImage(fs.readFileSync("./demo/images/parrots.bmp")); +doc.createImage(fs.readFileSync("./demo/images/pizza.gif")); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo6.js b/demo/demo6.js deleted file mode 100644 index 939f590d05..0000000000 --- a/demo/demo6.js +++ /dev/null @@ -1,25 +0,0 @@ -const docx = require("../build"); - -var doc = new docx.Document(undefined, { - top: 0, - right: 0, - bottom: 0, - left: 0, -}); - -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); - -doc.createParagraph("Hello World").heading1(); -doc.createParagraph("University College London"); -doc.createParagraph("5th Dec 2015"); - -var exporter = new docx.LocalPacker(doc); -exporter.pack("My Document"); - -console.log("Document created successfully at project root!"); diff --git a/demo/demo6.ts b/demo/demo6.ts new file mode 100644 index 0000000000..5c9ff2798f --- /dev/null +++ b/demo/demo6.ts @@ -0,0 +1,29 @@ +// Example of how to change page borders +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph, TextRun } from "../build"; + +const doc = new Document(undefined, { + top: 0, + right: 0, + bottom: 0, + left: 0, +}); + +const paragraph = new Paragraph("Hello World"); +const institutionText = new TextRun("Foo bar").bold(); +const dateText = new TextRun("Github is the best").tab().bold(); +paragraph.addRun(institutionText); +paragraph.addRun(dateText); + +doc.addParagraph(paragraph); + +doc.createParagraph("Hello World").heading1(); +doc.createParagraph("Foo bar"); +doc.createParagraph("Github is the best"); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo7.js b/demo/demo7.js deleted file mode 100644 index f60dcd1911..0000000000 --- a/demo/demo7.js +++ /dev/null @@ -1,14 +0,0 @@ -const docx = require("../build"); - -var doc = new docx.Document(undefined, { - orientation: "landscape", -}); - -var paragraph = new docx.Paragraph("Hello World"); - -doc.addParagraph(paragraph); - -var exporter = new docx.LocalPacker(doc); -exporter.pack("My Document"); - -console.log("Document created successfully at project root!"); diff --git a/demo/demo7.ts b/demo/demo7.ts new file mode 100644 index 0000000000..fe9fccf928 --- /dev/null +++ b/demo/demo7.ts @@ -0,0 +1,18 @@ +// Example of how to set the document to landscape +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, PageOrientation, Paragraph } from "../build"; + +const doc = new Document(undefined, { + orientation: PageOrientation.LANDSCAPE, +}); + +const paragraph = new Paragraph("Hello World"); + +doc.addParagraph(paragraph); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo8.js b/demo/demo8.js deleted file mode 100644 index 03ba944473..0000000000 --- a/demo/demo8.js +++ /dev/null @@ -1,13 +0,0 @@ -const docx = require('../build'); - -var doc = new docx.Document(); - -doc.createParagraph("Hello World"); - -doc.Header.createParagraph("Header text"); -doc.Footer.createParagraph("Footer text"); - -var exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); - -console.log('Document created successfully at project root!'); \ No newline at end of file diff --git a/demo/demo8.ts b/demo/demo8.ts new file mode 100644 index 0000000000..c4fb96a78c --- /dev/null +++ b/demo/demo8.ts @@ -0,0 +1,17 @@ +// Add text to header and footer +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer } from "../build"; + +const doc = new Document(); + +doc.createParagraph("Hello World"); + +doc.Header.createParagraph("Header text"); +doc.Footer.createParagraph("Footer text"); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/demo9.js b/demo/demo9.js deleted file mode 100644 index c93d8fa63c..0000000000 --- a/demo/demo9.js +++ /dev/null @@ -1,13 +0,0 @@ -const docx = require('../build'); - -var doc = new docx.Document(); - -doc.createParagraph("Hello World"); - -doc.Header.createImage("./demo/images/pizza.gif"); -doc.Footer.createImage("./demo/images/pizza.gif"); - -var exporter = new docx.LocalPacker(doc); -exporter.pack('My Document'); - -console.log('Document created successfully at project root!'); diff --git a/demo/demo9.ts b/demo/demo9.ts new file mode 100644 index 0000000000..87fe9b5192 --- /dev/null +++ b/demo/demo9.ts @@ -0,0 +1,17 @@ +// Add images to header and footer +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer } from "../build"; + +const doc = new Document(); + +doc.createParagraph("Hello World"); + +doc.Header.createImage(fs.readFileSync("./demo/images/pizza.gif")); +doc.Footer.createImage(fs.readFileSync("./demo/images/pizza.gif")); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/index.js b/demo/index.js deleted file mode 100644 index 8ae9aac536..0000000000 --- a/demo/index.js +++ /dev/null @@ -1,29 +0,0 @@ -var prompt = require('prompt'); -var shelljs = require('shelljs'); -var fs = require('fs'); - -console.log('What demo do you wish to run? (Enter a number)'); - -var schema = { - properties: { - number: { - pattern: /^[0-9]+$/, - message: 'Please enter a number.', - required: true - } - } -}; - -prompt.start(); - -prompt.get(schema, function (err, result) { - var demoNumber = result.number; - var filePath = `./demo/demo${demoNumber}.js`; - - if (!fs.existsSync(filePath)) { - console.error(`demo${demoNumber} does not exist: ${filePath}`); - return; - } - console.log(`Running demo ${demoNumber}`); - shelljs.exec(`node ${filePath}`); -}); diff --git a/demo/index.ts b/demo/index.ts new file mode 100644 index 0000000000..50b65572f2 --- /dev/null +++ b/demo/index.ts @@ -0,0 +1,34 @@ +// tslint:disable:no-console +import * as fs from "fs"; +import * as prompt from "prompt"; +import * as shelljs from "shelljs"; + +console.log("What demo do you wish to run? (Enter a number)"); + +const schema = { + properties: { + number: { + pattern: /^[0-9]+$/, + message: "Please enter a number.", + required: true, + }, + }, +}; + +prompt.start(); + +prompt.get(schema, (_, result) => { + const demoNumber = result.number; + const filePath = `./demo/demo${demoNumber}.ts`; + + if (!fs.existsSync(filePath)) { + console.error(`demo${demoNumber} does not exist: ${filePath}`); + return; + } + console.log(`Running demo ${demoNumber}`); + if (shelljs.exec(`npm run ts-node -- ${filePath}`).code === 0) { + console.log("Document created successfully"); + } else { + console.error("Something went wrong with the demo"); + } +}); diff --git a/docs/README.md b/docs/README.md index 2ea6ac25e3..f3134b8fa3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,7 +3,7 @@

- Easily generate .docx files with JS/TS. :100: + Easily generate .docx files with JS/TS. Works for Node and on the Browser. :100:

--- @@ -19,11 +19,11 @@ npm install --save docx Then you can `require` or `import` as usual: ```js -let docx = require('docx'); +let docx = require("docx"); ``` ```js -import * as docx from 'docx' +import * as docx from "docx"; ``` ## Basic Usage @@ -48,6 +48,12 @@ exporter.pack("My First Document"); // Done! A file called 'My First Document.docx' will be in your file system if you used LocalPacker ``` +## Honoured Mentions + +[@felipeochoa](https://github.com/felipeochoa) + +[@h4buli](https://github.com/h4buli) + --- Made with 💖 diff --git a/docs/contribution-guidelines.md b/docs/contribution-guidelines.md index 28cfbad954..5d1442fe0c 100644 --- a/docs/contribution-guidelines.md +++ b/docs/contribution-guidelines.md @@ -12,6 +12,51 @@ * Follow the `TSLint` rules +## Add vs Create + +This is just a guideline, and the rules can sometimes be broken. + +* Use `create` if the method `new`'s up an element inside: + + ```js + public createParagraph() { + const paragraph = new Paragraph(); + this.root.push(paragraph); + } + ``` + +* Use `add` if you add the element into the method as a parameter: + + ```js + public addParagraph(paragraph: Paragraph) { + this.root.push(paragraph); + } + ``` + +## Getters and Setters + +Getters and Setters are done with a capital letter like so: + +```js +public get Level() { + +} +``` + +There is no performance advantage by doing this. It means we don't need to prefix all private variables with the ugly `_`: + +**Do not:** + +```js +private get _level: string; +``` + +**Do** + +```js +private get level: string; +``` + ## Testing Please write a test of every file you make and suffix it with `.spec.ts`. diff --git a/docs/examples.md b/docs/examples.md index c2450496ca..05dbfb2de5 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -2,13 +2,21 @@ > All examples can run independently and can be found in the `/demo` folder of the project +All the examples below can be ran locally, to do so, run the following command: + +```sh +npm run demo +``` + +This command will run the `demo selector app` in the `/demo` folder. It will prompt you to select a demo number, which will run a demo from that folder. + ## Simple A simple hello world of the `docx` library: -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo1.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo1.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo1.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo1.ts_ ## Styles @@ -16,41 +24,41 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo1.js_ This example shows how to customise the look and feel of a document using JS configuration -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo2.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo2.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo2.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo2.ts_ ### Styling with XML This example shows how to customise the look and feel of a document using XML configuration -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo13.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo13.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo13.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts_ ## Numbering This example shows many levels of numbering -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo3.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo3.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo3.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo3.ts_ ## Table Example of simple table -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_ ### Styling table borders Styling the borders of a table -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_ ## Images @@ -58,73 +66,73 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.js_ Importing Images from file system path -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_ ### Add images to header and footer Example showing how to add image to headers and footers -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_ ### Scaling images Example showing how to scale images -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo12.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo12.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo12.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo12.ts_ ### Add Image to media before adding to document This is the best way to add an image to a document because you can add the same image in two locations without increasing document size by re-using the same image -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo23.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo23.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo23.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo23.ts_ ### Add image to table As before, to add an image to a table, you would need to add it to the `Media` object first -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_ ### Images using Base64 URI If you want to use a Base64 image instead -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo18.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo18.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo18.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo18.ts_ ## Margins Example showing how to set custom margains -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo6.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo6.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo6.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo6.ts_ ## Orientation Example showing how to set the document to `landscape` or `portrait` -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo7.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo7.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo7.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo7.ts_ ## Headers & Footers Example showing how to add headers and footers -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo8.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo8.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo8.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo8.ts_ ## Multiple headers and footers @@ -136,67 +144,60 @@ Check out `Sections` for this feature Example showing how to page break -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo14.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo14.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo14.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo14.ts_ ### Page break before Example showing how to page break before like in Word -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo15.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo15.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo15.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo15.ts_ ## Sections Example of how sections work. Sections allow multiple headers and footers, and `landscape`/`portrait` inside the same document -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo16.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo16.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo16.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo16.ts_ ## Footnotes Example of how to add footnotes. Good for references -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo17.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo17.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo17.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo17.ts_ ## Packers -## Buffer Packer +## Buffer output Example showing how to use the Buffer packer and then write that buffer to the file system -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo19.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo19.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo19.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo19.ts_ -## PDF Packing - -Example of how to use the `LocalPacker` to create a PDF document - -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo25.js ":include") - -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo25.js_ ## Bookmarks Example showing how to make bookmarks to make internal hyperlinks within the document -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo21.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo21.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo21.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo21.ts_ ## Bidirectional text Example showing how to use bidirectional text for certain languages such as Hebrew -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo22.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo22.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo22.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo22.ts_ ## Showcase @@ -204,14 +205,14 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo22.js_ Example showing how to add headers and footers -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo10.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo10.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo10.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo10.ts_ ### Style and Images This example shows how to customise the look and feel of a document and add images -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo11.js ":include") +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo11.ts ":include") -_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo11.js_ +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo11.ts_ diff --git a/docs/usage/headers-and-footers.md b/docs/usage/headers-and-footers.md index 72aa424d33..1489576dda 100644 --- a/docs/usage/headers-and-footers.md +++ b/docs/usage/headers-and-footers.md @@ -19,8 +19,8 @@ doc.Footer.createParagraph("Footer text"); Even add images: ```js -doc.Header.createImage([PATH_TO_YOUR_IMAGE]); -doc.Footer.createImage([PATH_TO_YOUR_IMAGE]); +doc.Header.createImage([BUFFER_OF_YOUR_IMAGE]); +doc.Footer.createImage([BUFFER_OF_YOUR_IMAGE]); ``` Refer to `demo8.js` for more information @@ -37,8 +37,8 @@ Also all the supported section properties are implemented according to: http://o // Add new section with another header and footer doc.addSection({ - headerId: header.Header.referenceId, - footerId: footer.Footer.referenceId, + headerId: header.Header.ReferenceId, + footerId: footer.Footer.ReferenceId, pageNumberStart: 1, pageNumberFormatType: docx.PageNumberFormat.DECIMAL, }); diff --git a/docs/usage/images.md b/docs/usage/images.md index 2cc0339507..a89fa893cd 100644 --- a/docs/usage/images.md +++ b/docs/usage/images.md @@ -7,7 +7,7 @@ Adding images is very simple Simply call the `createImage` method: ```js -const image = doc.createImage([PATH_TO_YOUR_IMAGE]); +const image = doc.createImage([BUFFER_OF_YOUR_IMAGE]); ``` `docx` supports `jpeg`, `jpg`, `bmp`, `gif` and `png` @@ -43,7 +43,7 @@ interface DrawingOptions { can be passed when creating `PictureRun()` for example: ```js -const imageData = document.createImageData(filename, buffer, 903, 1149); +const imageData = document.createImage(buffer, 903, 1149); new docx.PictureRun(imageData, { position: docx.PlacementPosition.FLOATING, diff --git a/docs/usage/packers.md b/docs/usage/packers.md index 1994781639..11f107390d 100644 --- a/docs/usage/packers.md +++ b/docs/usage/packers.md @@ -2,7 +2,48 @@ > Packers are the way in which `docx` turns your code into `.docx` format. It is completely decoupled from the `docx.Document`. -## File System Packer +## Version 4 + +Packers in `version 4` and above are now one single `Packer`. It works in both a node and browser environment (Angular etc). Now, the packer returns a `Buffer`, `Blob` or `base64 string`. It is up to you to take that and persist it with node's `fs`, send it down as a downloadable file, or anything else you wish. As of version 4, this library will not have options to export to PDF. + +### Export as Buffer + +This will return a NodeJS `Buffer`. If this is used in the browser, it will return a `UInt8Array` instead. + +```js +const packer = new docx.Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); +``` + +### Export as a `base64` string + +```js +const packer = new docx.Packer(); + +packer.toBase64String(doc).then((string) => { + console.log(string); +}); +``` + +### Export as Blob + +This is useful if you want to send it as an downloadable in a browser environment. + +```js +const packer = new docx.Packer(); + +packer.toBlob(doc).then((blob) => { + // saveAs from FileSaver will download the file + saveAs(blob, "example.docx"); +}); +``` + +## Version 3 and below + +### File System Packer ```js const docx = require("docx"); @@ -13,7 +54,7 @@ exporter.pack("My Document"); // Word Document is in file system ``` -## Buffer Packer +### Buffer Packer ```js const docx = require("docx"); @@ -23,7 +64,7 @@ const exporter = new docx.BufferPacker(doc); const buffer = exporter.pack(); ``` -## Stream Packer +### Stream Packer Creates a `node` `Readable` stream @@ -32,26 +73,37 @@ const docx = require("docx"); const doc = new docx.Document(); const exporter = new docx.StreamPacker(doc); -const buffer = exporter.pack(); +const stream = exporter.pack(); ``` -## Express Packer +### Express Packer + +The old express packer is now deprecated and may disappear soon, so you should upgrade. + +The reason for this is because it means this project needs to know about and use `express`, which for a Word document generator, does not sound right. Seperation of concerns. + +It will still be usable (for now), but it is ill advised. I used the express exporter in my [website](http://www.dolan.bio). -Pass in the necessary parameters: +The recommended way is to use the `StreamPacker` and handle the `express` magic outside of the library: ```js const docx = require("docx"); const doc = new docx.Document(); -const exporter = new docx.ExpressPacker(doc, res); -exporter.pack("My Document"); +const exporter = new docx.StreamPacker(doc); + +const stream = exporter.pack(); + +// Express' response object +res.attachment("yourfile.xlsx"); +stream.pipe(res); ``` where `res` is the response object obtained through the Express router. It is that simple. The file will begin downloading in the browser. -## PDF Exporting +### PDF Exporting You can export your word document as a PDF file like so: @@ -63,7 +115,3 @@ exporter.packPdf("My Document"); const exporter = new docx.ExpressPacker(doc, res); exporter.packPdf("My Document"); ``` - -## Browser based docx exporting - -It is on the bucket list. It has been requested by a few, and work is already on it diff --git a/package.json b/package.json index ebfee8c979..330ad72bac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "docx", - "version": "3.6.0", + "version": "4.0.0", "description": "Generate .docx documents with JavaScript (formerly Office-Clippy)", "main": "build/index.js", "scripts": { @@ -12,11 +12,13 @@ "build": "npm run webpack && npm run fix-types", "tsc": "rimraf ./build && tsc -p .", "webpack": "rimraf ./build && webpack", - "demo": "npm run build && node ./demo", - "typedoc": "typedoc --out docs/api/ src/ --module commonjs --target ES6 --disableOutputCheck --excludePrivate --externalPattern \"**/*.spec.ts\"", + "build.web": "webpack --config webpack.web.config.js", + "demo": "npm run build && npm run ts-node ./demo", + "typedoc": "typedoc src/index.ts", "style": "prettier -l \"src/**/*.ts\"", "style.fix": "prettier \"src/**/*.ts\" --write", - "fix-types": "node types-absolute-fixer.js" + "fix-types": "node types-absolute-fixer.js", + "ts-node": "ts-node" }, "pre-commit": [ "style", @@ -45,16 +47,11 @@ ], "types": "./build/index.d.ts", "dependencies": { - "@types/archiver": "^2.1.0", - "@types/bluebird": "3.5.20", - "@types/express": "^4.0.35", "@types/image-size": "0.0.29", - "@types/request-promise": "^4.1.42", - "archiver": "^2.1.1", + "@types/jszip": "^3.1.3", "fast-xml-parser": "^3.3.6", "image-size": "^0.6.2", - "request": "^2.83.0", - "request-promise": "^4.2.2", + "jszip": "^3.1.5", "xml": "^1.0.1" }, "author": "Dolan Miu", @@ -71,7 +68,7 @@ "chai": "^3.5.0", "glob": "^7.1.2", "jszip": "^3.1.5", - "mocha": "^3.2.0", + "mocha": "^5.2.0", "mocha-webpack": "^1.0.1", "pre-commit": "^1.2.2", "prettier": "^1.12.1", @@ -80,7 +77,8 @@ "rimraf": "^2.5.2", "shelljs": "^0.7.7", "sinon": "^5.0.7", - "tslint": "^5.1.0", + "ts-node": "^7.0.1", + "tslint": "^5.11.0", "typedoc": "^0.11.1", "typescript": "2.9.2", "webpack": "^3.10.0" diff --git a/src/export/index.ts b/src/export/index.ts index f6a47e4826..042cb4366d 100644 --- a/src/export/index.ts +++ b/src/export/index.ts @@ -1,5 +1 @@ -export * from "./packer/local"; -export * from "./packer/express"; export * from "./packer/packer"; -export * from "./packer/stream"; -export * from "./packer/buffer"; diff --git a/src/export/packer/buffer-stream.ts b/src/export/packer/buffer-stream.ts deleted file mode 100644 index d1b9b0daa1..0000000000 --- a/src/export/packer/buffer-stream.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Writable } from "stream"; - -export class BufferStream extends Writable { - private data: Buffer[]; - - constructor() { - super(); - - this.data = []; - } - - // tslint:disable-next-line:no-any - public _write(chunk: any, encoding: string, next: (err?: Error) => void): void { - this.data.push(Buffer.from(chunk)); - next(); - } - - // tslint:disable-next-line:ban-types - public end(cb?: Function): void { - super.end(cb); - - this.emit("close"); - } - - public get Buffer(): Buffer { - return Buffer.concat(this.data); - } -} diff --git a/src/export/packer/buffer.ts b/src/export/packer/buffer.ts deleted file mode 100644 index 9c46c4e166..0000000000 --- a/src/export/packer/buffer.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { File } from "../../file"; -import { BufferStream } from "./buffer-stream"; -import { Compiler } from "./compiler"; -import { IPacker } from "./packer"; - -export class BufferPacker implements IPacker { - private readonly packer: Compiler; - - constructor(file: File) { - this.packer = new Compiler(file); - } - - public async pack(): Promise { - const stream = new BufferStream(); - - await this.packer.compile(stream); - - return stream.Buffer; - } -} diff --git a/src/export/packer/compiler.ts b/src/export/packer/compiler.ts deleted file mode 100644 index d439e0718c..0000000000 --- a/src/export/packer/compiler.ts +++ /dev/null @@ -1,114 +0,0 @@ -import * as archiver from "archiver"; -import * as express from "express"; -import { Writable } from "stream"; -import * as xml from "xml"; - -import { File } from "file"; -import { Formatter } from "../formatter"; - -export class Compiler { - protected archive: archiver.Archiver; - private formatter: Formatter; - - constructor(private file: File) { - this.formatter = new Formatter(); - this.archive = archiver.create("zip", {}); - - this.archive.on("error", (err) => { - throw err; - }); - } - - public async compile(output: Writable | express.Response): Promise { - this.archive.pipe(output); - - const xmlDocument = xml(this.formatter.format(this.file.Document)); - const xmlStyles = xml(this.formatter.format(this.file.Styles)); - const xmlProperties = xml(this.formatter.format(this.file.CoreProperties), { - declaration: { - standalone: "yes", - encoding: "UTF-8", - }, - }); - 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 xmlContentTypes = xml(this.formatter.format(this.file.ContentTypes)); - const xmlAppProperties = xml(this.formatter.format(this.file.AppProperties)); - const xmlFootnotes = xml(this.formatter.format(this.file.FootNotes)); - - this.archive.append(xmlDocument, { - name: "word/document.xml", - }); - - this.archive.append(xmlStyles, { - name: "word/styles.xml", - }); - - this.archive.append(xmlProperties, { - name: "docProps/core.xml", - }); - - this.archive.append(xmlAppProperties, { - name: "docProps/app.xml", - }); - - this.archive.append(xmlNumbering, { - name: "word/numbering.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(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(xmlFootnotes, { - name: "word/footnotes.xml", - }); - - this.archive.append(xmlRelationships, { - name: "word/_rels/document.xml.rels", - }); - - this.archive.append(xmlContentTypes, { - name: "[Content_Types].xml", - }); - - this.archive.append(xmlFileRelationships, { - name: "_rels/.rels", - }); - - for (const data of this.file.Media.array) { - this.archive.append(data.stream, { - name: `word/media/${data.fileName}`, - }); - } - - this.archive.finalize(); - - return new Promise((resolve) => { - output.on("close", () => { - resolve(); - }); - }); - } -} diff --git a/src/export/packer/express.spec.ts b/src/export/packer/express.spec.ts deleted file mode 100644 index 416d206815..0000000000 --- a/src/export/packer/express.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -// tslint:disable:typedef space-before-function-paren -// tslint:disable:no-empty -// tslint:disable:no-any -import { assert } from "chai"; -import { stub } from "sinon"; - -import { ExpressPacker } from "../../export/packer/express"; -import { File, Paragraph } from "../../file"; - -describe("LocalPacker", () => { - let packer: ExpressPacker; - - beforeEach(() => { - const file = new File({ - creator: "Dolan Miu", - revision: "1", - lastModifiedBy: "Dolan Miu", - }); - const paragraph = new Paragraph("test text"); - const heading = new Paragraph("Hello world").heading1(); - file.addParagraph(new Paragraph("title").title()); - file.addParagraph(heading); - file.addParagraph(new Paragraph("heading 2").heading2()); - file.addParagraph(paragraph); - - const expressResMock = { - on: () => {}, - attachment: () => {}, - }; - - packer = new ExpressPacker(file, expressResMock as any); - }); - - describe("#pack()", () => { - it("should handle exception if it throws any", () => { - const compiler = stub((packer as any).packer, "compile"); - compiler.throwsException(); - return packer.pack("build/tests/test").catch((error) => { - assert.isDefined(error); - }); - }); - }); -}); diff --git a/src/export/packer/express.ts b/src/export/packer/express.ts deleted file mode 100644 index 4f99298424..0000000000 --- a/src/export/packer/express.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as express from "express"; - -import { File } from "file"; -import { Compiler } from "./compiler"; -import { IPacker } from "./packer"; - -export class ExpressPacker implements IPacker { - private readonly packer: Compiler; - - constructor(file: File, private readonly res: express.Response) { - this.packer = new Compiler(file); - - this.res = res; - - this.res.on("close", () => { - return res - .status(200) - .send("OK") - .end(); - }); - } - - public async pack(name: string): Promise { - name = name.replace(/.docx$/, ""); - - this.res.attachment(`${name}.docx`); - await this.packer.compile(this.res); - } -} diff --git a/src/export/packer/local.spec.ts b/src/export/packer/local.spec.ts deleted file mode 100644 index e942fb70a9..0000000000 --- a/src/export/packer/local.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* tslint:disable:typedef space-before-function-paren */ -import { assert } from "chai"; -import * as fs from "fs"; -import { stub } from "sinon"; - -import { LocalPacker } from "../../export/packer/local"; -import { File, Paragraph } from "../../file"; - -describe("LocalPacker", () => { - let packer: LocalPacker; - - beforeEach(() => { - const file = new File({ - creator: "Dolan Miu", - revision: "1", - lastModifiedBy: "Dolan Miu", - }); - const paragraph = new Paragraph("test text"); - const heading = new Paragraph("Hello world").heading1(); - file.addParagraph(new Paragraph("title").title()); - file.addParagraph(heading); - file.addParagraph(new Paragraph("heading 2").heading2()); - file.addParagraph(paragraph); - - packer = new LocalPacker(file); - }); - - describe("#pack()", () => { - it("should create a standard docx file", async function() { - this.timeout(99999999); - await packer.pack("build/tests/test"); - fs.statSync("build/tests/test.docx"); - }); - - it("should handle exception if it throws any", () => { - // tslint:disable-next-line:no-any - const compiler = stub((packer as any).packer, "compile"); - compiler.throwsException(); - return packer.pack("build/tests/test").catch((error) => { - assert.isDefined(error); - }); - }); - }); - - describe("#packPdf", () => { - it("should create a standard PDF file", async function() { - this.timeout(99999999); - - // tslint:disable-next-line:no-any - const pdfConverterConvert = stub((packer as any).pdfConverter, "convert"); - pdfConverterConvert.returns("Test PDF Contents"); - - await packer.packPdf("build/tests/pdf-test"); - fs.statSync("build/tests/pdf-test.pdf"); - }); - - it("should handle exception if it throws any", () => { - // tslint:disable-next-line:no-any - const compiler = stub((packer as any).packer, "compile"); - compiler.throwsException(); - return packer.packPdf("build/tests/pdf-test").catch((error) => { - assert.isDefined(error); - }); - }); - }); -}); diff --git a/src/export/packer/local.ts b/src/export/packer/local.ts deleted file mode 100644 index 5d0c05fe81..0000000000 --- a/src/export/packer/local.ts +++ /dev/null @@ -1,47 +0,0 @@ -import * as fs from "fs"; -import * as os from "os"; -import * as path from "path"; - -import { File } from "../../file"; -import { Compiler } from "./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; - - constructor(file: File) { - this.pdfConverter = new PdfConvertWrapper(); - this.packer = new Compiler(file); - } - - public async pack(filePath: string): Promise { - filePath = filePath.replace(/.docx$/, ""); - - this.stream = fs.createWriteStream(`${filePath}.docx`); - await this.packer.compile(this.stream); - } - - public async packPdf(filePath: string): Promise { - filePath = filePath.replace(/.pdf$/, ""); - - 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 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; - } - resolve(); - }); - }); - } -} diff --git a/src/export/packer/compiler.spec.ts b/src/export/packer/next-compiler.spec.ts similarity index 78% rename from src/export/packer/compiler.spec.ts rename to src/export/packer/next-compiler.spec.ts index 78d2339b11..fc02d4a814 100644 --- a/src/export/packer/compiler.spec.ts +++ b/src/export/packer/next-compiler.spec.ts @@ -1,10 +1,7 @@ /* tslint:disable:typedef space-before-function-paren */ -import * as fs from "fs"; -import * as JSZip from "jszip"; - import { expect } from "chai"; import { File } from "../../file"; -import { Compiler } from "./compiler"; +import { Compiler } from "./next-compiler"; describe("Compiler", () => { let compiler: Compiler; @@ -12,21 +9,17 @@ describe("Compiler", () => { beforeEach(() => { file = new File(); - compiler = new Compiler(file); + compiler = new Compiler(); }); 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 zipFile = await compiler.compile(file); const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name); expect(fileNames).is.an.instanceof(Array); - expect(fileNames).has.length(13); + expect(fileNames).has.length(17); expect(fileNames).to.include("word/document.xml"); expect(fileNames).to.include("word/styles.xml"); expect(fileNames).to.include("docProps/core.xml"); @@ -49,15 +42,12 @@ describe("Compiler", () => { 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 zipFile = await compiler.compile(file); const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name); expect(fileNames).is.an.instanceof(Array); - expect(fileNames).has.length(21); + expect(fileNames).has.length(25); expect(fileNames).to.include("word/header1.xml"); expect(fileNames).to.include("word/_rels/header1.xml.rels"); diff --git a/src/export/packer/next-compiler.ts b/src/export/packer/next-compiler.ts new file mode 100644 index 0000000000..f06c09e196 --- /dev/null +++ b/src/export/packer/next-compiler.ts @@ -0,0 +1,125 @@ +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; + Headers: IXmlifyedFile[]; + Footers: IXmlifyedFile[]; + HeaderRelationships: IXmlifyedFile[]; + FooterRelationships: IXmlifyedFile[]; + ContentTypes: IXmlifyedFile; + AppProperties: IXmlifyedFile; + FootNotes: IXmlifyedFile; +} + +export class Compiler { + private readonly formatter: Formatter; + + constructor() { + this.formatter = new Formatter(); + } + + public async compile(file: File): Promise { + const zip = new JSZip(); + + const xmlifiedFileMapping = this.xmlifyFile(file); + + for (const key in xmlifiedFileMapping) { + if (!xmlifiedFileMapping[key]) { + continue; + } + + const obj = xmlifiedFileMapping[key] as IXmlifyedFile | IXmlifyedFile[]; + + if (Array.isArray(obj)) { + for (const subFile of obj) { + zip.file(subFile.path, subFile.data); + } + } else { + zip.file(obj.path, obj.data); + } + } + + for (const data of file.Media.Array) { + const mediaData = data.stream; + zip.file(`word/media/${data.fileName}`, mediaData); + } + + 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", + }, + Headers: file.Headers.map((headerWrapper, index) => ({ + data: xml(this.formatter.format(headerWrapper.Header)), + path: `word/header${index + 1}.xml`, + })), + Footers: file.Footers.map((footerWrapper, index) => ({ + data: xml(this.formatter.format(footerWrapper.Footer)), + path: `word/footer${index + 1}.xml`, + })), + HeaderRelationships: file.Headers.map((headerWrapper, index) => ({ + data: xml(this.formatter.format(headerWrapper.Relationships)), + path: `word/_rels/header${index + 1}.xml.rels`, + })), + FooterRelationships: file.Footers.map((footerWrapper, index) => ({ + data: xml(this.formatter.format(footerWrapper.Relationships)), + path: `word/_rels/footer${index + 1}.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", + }, + FootNotes: { + data: xml(this.formatter.format(file.FootNotes)), + path: "word/footnotes.xml", + }, + }; + } +} diff --git a/src/export/packer/buffer.spec.ts b/src/export/packer/packer.spec.ts similarity index 72% rename from src/export/packer/buffer.spec.ts rename to src/export/packer/packer.spec.ts index 95eccc5ecc..497e3e46b7 100644 --- a/src/export/packer/buffer.spec.ts +++ b/src/export/packer/packer.spec.ts @@ -2,41 +2,45 @@ import { assert } from "chai"; import { stub } from "sinon"; -import { BufferPacker } from "../../export/packer/buffer"; import { File, Paragraph } from "../../file"; +import { Packer } from "./packer"; -describe("BufferPacker", () => { - let packer: BufferPacker; +describe("Packer", () => { + let packer: Packer; + let file: File; beforeEach(() => { - const file = new File({ + file = new File({ creator: "Dolan Miu", revision: "1", lastModifiedBy: "Dolan Miu", }); const paragraph = new Paragraph("test text"); const heading = new Paragraph("Hello world").heading1(); + file.addParagraph(new Paragraph("title").title()); file.addParagraph(heading); file.addParagraph(new Paragraph("heading 2").heading2()); file.addParagraph(paragraph); - packer = new BufferPacker(file); + packer = new Packer(); }); - describe("#pack()", () => { + describe("#toBuffer()", () => { it("should create a standard docx file", async function() { this.timeout(99999999); - const buffer = await packer.pack(); + const buffer = await packer.toBuffer(file); + assert.isDefined(buffer); assert.isTrue(buffer.byteLength > 0); }); it("should handle exception if it throws any", () => { // tslint:disable-next-line:no-any - const compiler = stub((packer as any).packer, "compile"); + const compiler = stub((packer as any).compiler, "compile"); + compiler.throwsException(); - return packer.pack().catch((error) => { + return packer.toBuffer(file).catch((error) => { assert.isDefined(error); }); }); diff --git a/src/export/packer/packer.ts b/src/export/packer/packer.ts index 64fa05a9aa..e85b42c8ea 100644 --- a/src/export/packer/packer.ts +++ b/src/export/packer/packer.ts @@ -1,6 +1,31 @@ -export interface IPacker { - pack(path: string): void; -} +import { File } from "file"; +import { Compiler } from "./next-compiler"; -// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432 -export const WORKAROUND = ""; +export class Packer { + private readonly compiler: Compiler; + + constructor() { + this.compiler = new Compiler(); + } + + public async toBuffer(file: File): Promise { + const zip = await this.compiler.compile(file); + const zipData = (await zip.generateAsync({ type: "nodebuffer" })) as Buffer; + + return zipData; + } + + public async toBase64String(file: File): Promise { + const zip = await this.compiler.compile(file); + const zipData = (await zip.generateAsync({ type: "base64" })) as string; + + return zipData; + } + + public async toBlob(file: File): Promise { + const zip = await this.compiler.compile(file); + const zipData = (await zip.generateAsync({ type: "blob" })) as Blob; + + return zipData; + } +} diff --git a/src/export/packer/pdf-convert-wrapper.ts b/src/export/packer/pdf-convert-wrapper.ts deleted file mode 100644 index e1229cf768..0000000000 --- a/src/export/packer/pdf-convert-wrapper.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as fs from "fs"; -import * as request from "request-promise"; - -export interface IConvertOutput { - data: string; -} - -export class PdfConvertWrapper { - public convert(filePath: string): request.RequestPromise { - return request.post({ - url: "http://mirror1.convertonlinefree.com", - // tslint:disable-next-line:no-null-keyword - encoding: null, - headers: { - "User-Agent": - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.94 Safari/537.36", - }, - formData: { - __EVENTTARGET: "", - __EVENTARGUMENT: "", - __VIEWSTATE: "", - ctl00$MainContent$fu: { - value: fs.readFileSync(filePath), - options: { - filename: "output.docx", - contentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - }, - }, - ctl00$MainContent$btnConvert: "Convert", - ctl00$MainContent$fuZip: "", - }, - }); - } -} diff --git a/src/export/packer/stream.ts b/src/export/packer/stream.ts deleted file mode 100644 index 1182cd5dcb..0000000000 --- a/src/export/packer/stream.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Readable, Transform } from "stream"; -import { File } from "../../file"; -import { Compiler } from "./compiler"; -import { IPacker } from "./packer"; - -class Pipe extends Transform { - public _transform(chunk: Buffer | string, encoding: string, callback: () => void): void { - this.push(chunk, encoding); - callback(); - } -} - -export class StreamPacker implements IPacker { - private readonly compiler: Compiler; - - constructor(file: File) { - this.compiler = new Compiler(file); - } - - public pack(): Readable { - const pipe = new Pipe(); - this.compiler.compile(pipe); - return pipe; - } -} diff --git a/src/file/content-types/content-types.spec.ts b/src/file/content-types/content-types.spec.ts index 14665df4ee..7e9b62c88f 100644 --- a/src/file/content-types/content-types.spec.ts +++ b/src/file/content-types/content-types.spec.ts @@ -1,6 +1,9 @@ +// tslint:disable:no-string-literal + import { expect } from "chai"; import { Formatter } from "../../export/formatter"; import { ContentTypes } from "./content-types"; + describe("ContentTypes", () => { let contentTypes: ContentTypes; diff --git a/src/file/document/body/body.ts b/src/file/document/body/body.ts index 5acc9f9c2b..e274fa359f 100644 --- a/src/file/document/body/body.ts +++ b/src/file/document/body/body.ts @@ -1,11 +1,11 @@ import { IXmlableObject, XmlComponent } from "file/xml-components"; import { Paragraph, ParagraphProperties } from "../.."; -import { SectionProperties, SectionPropertiesOptions } from "./section-properties"; +import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties"; export class Body extends XmlComponent { - private defaultSection: SectionProperties; + private readonly defaultSection: SectionProperties; - private sections: SectionProperties[] = []; + private readonly sections: SectionProperties[] = []; constructor(sectionPropertiesOptions?: SectionPropertiesOptions) { super("w:body"); diff --git a/src/file/document/body/section-properties/index.ts b/src/file/document/body/section-properties/index.ts index f1b5eabb84..cab88ac7c7 100644 --- a/src/file/document/body/section-properties/index.ts +++ b/src/file/document/body/section-properties/index.ts @@ -3,3 +3,4 @@ export * from "./footer-reference"; export * from "./header-reference"; export * from "./page-size"; export * from "./page-number"; +export * from "./page-border"; diff --git a/src/file/document/body/section-properties/page-border/index.ts b/src/file/document/body/section-properties/page-border/index.ts new file mode 100644 index 0000000000..53d8b8ce4b --- /dev/null +++ b/src/file/document/body/section-properties/page-border/index.ts @@ -0,0 +1 @@ +export * from "./page-borders"; diff --git a/src/file/document/body/section-properties/page-border/page-borders.spec.ts b/src/file/document/body/section-properties/page-border/page-borders.spec.ts new file mode 100644 index 0000000000..66abb9cc66 --- /dev/null +++ b/src/file/document/body/section-properties/page-border/page-borders.spec.ts @@ -0,0 +1,91 @@ +import { expect } from "chai"; + +import { Formatter } from "../../../../../export/formatter"; +import { BorderStyle } from "../../../../styles"; +import { PageBorderDisplay, PageBorders, PageBorderZOrder } from "./page-borders"; + +describe("PageBorders", () => { + describe("#constructor()", () => { + it("should create empty element when no options are passed", () => { + const properties = new PageBorders(); + const tree = new Formatter().format(properties); + + expect(tree).to.equal(""); + }); + + it("should create page borders with some configuration", () => { + const properties = new PageBorders({ + pageBorders: { + display: PageBorderDisplay.FIRST_PAGE, + }, + }); + const tree = new Formatter().format(properties); + + expect(Object.keys(tree)).to.deep.equal(["w:pgBorders"]); + expect(tree["w:pgBorders"]).to.be.an.instanceof(Array); + expect(tree["w:pgBorders"][0]).to.deep.equal({ _attr: { "w:display": "firstPage" } }); + }); + + it("should create page borders with full configuration", () => { + const properties = new PageBorders({ + pageBorders: { + display: PageBorderDisplay.FIRST_PAGE, + zOrder: PageBorderZOrder.BACK, + }, + pageBorderTop: { + style: BorderStyle.DOUBLE_WAVE, + size: 10, + color: "001122", + }, + pageBorderRight: { + style: BorderStyle.DOUBLE, + size: 20, + color: "223344", + }, + pageBorderBottom: { + style: BorderStyle.SINGLE, + size: 30, + color: "556677", + }, + pageBorderLeft: { + style: BorderStyle.DOTTED, + size: 40, + color: "889900", + }, + }); + const tree = new Formatter().format(properties); + + expect(Object.keys(tree)).to.deep.equal(["w:pgBorders"]); + expect(tree["w:pgBorders"]).to.be.an.instanceof(Array); + expect(tree["w:pgBorders"][0]).to.deep.equal({ _attr: { "w:display": "firstPage", "w:zOrder": "back" } }); + expect(tree["w:pgBorders"][1]).to.deep.equal({ + "w:top": [ + { + _attr: { "w:color": "001122", "w:size": 10, "w:val": "doubleWave" }, + }, + ], + }); + expect(tree["w:pgBorders"][2]).to.deep.equal({ + "w:right": [ + { + _attr: { "w:color": "223344", "w:size": 20, "w:val": "double" }, + }, + ], + }); + expect(tree["w:pgBorders"][3]).to.deep.equal({ + "w:bottom": [ + { + _attr: { "w:color": "556677", "w:size": 30, "w:val": "single" }, + }, + ], + }); + expect(tree["w:pgBorders"][4]).to.deep.equal({ + "w:left": [ + { + _attr: { "w:color": "889900", "w:size": 40, "w:val": "dotted" }, + }, + ], + }); + }); + }); +}); diff --git a/src/file/document/body/section-properties/page-border/page-borders.ts b/src/file/document/body/section-properties/page-border/page-borders.ts new file mode 100644 index 0000000000..0af96cfd41 --- /dev/null +++ b/src/file/document/body/section-properties/page-border/page-borders.ts @@ -0,0 +1,104 @@ +// http://officeopenxml.com/WPsectionBorders.php +import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components"; +import { BorderStyle } from "../../../../styles"; + +export enum PageBorderDisplay { + ALL_PAGES = "allPages", + FIRST_PAGE = "firstPage", + NOT_FIRST_PAGE = "notFirstPage", +} + +export enum PageBorderOffsetFrom { + PAGE = "page", + TEXT = "text", +} + +export enum PageBorderZOrder { + BACK = "back", + FRONT = "front", +} + +export interface IPageBorderAttributes { + display?: PageBorderDisplay; + offsetFrom?: PageBorderOffsetFrom; + zOrder?: PageBorderZOrder; +} + +export interface IPageBorderConfiguration { + style?: BorderStyle; + size?: number; + color?: string; + space?: number; +} + +export interface IPageBordersOptions { + pageBorders?: IPageBorderAttributes; + pageBorderTop?: IPageBorderConfiguration; + pageBorderRight?: IPageBorderConfiguration; + pageBorderBottom?: IPageBorderConfiguration; + pageBorderLeft?: IPageBorderConfiguration; +} + +class PageBordeAttributes extends XmlAttributeComponent { + protected xmlKeys = { + style: "w:val", + size: "w:size", + color: "w:color", + space: "w:space", + }; +} + +class PageBorder extends XmlComponent { + constructor(key: string, options: IPageBorderConfiguration) { + super(key); + + this.root.push(new PageBordeAttributes(options)); + } +} + +class PageBordersAttributes extends XmlAttributeComponent { + protected xmlKeys = { + display: "w:display", + offsetFrom: "w:offsetFrom", + zOrder: "w:zOrder", + }; +} + +export class PageBorders extends XmlComponent { + constructor(options?: IPageBordersOptions) { + super("w:pgBorders"); + + if (!options) { + return; + } + + let pageBordersAttributes = {}; + + if (options.pageBorders) { + pageBordersAttributes = { + display: options.pageBorders.display, + offsetFrom: options.pageBorders.offsetFrom, + zOrder: options.pageBorders.zOrder, + }; + } + + this.root.push(new PageBordersAttributes(pageBordersAttributes)); + + if (options.pageBorderTop) { + this.root.push(new PageBorder("w:top", options.pageBorderTop)); + } + if (options.pageBorderRight) { + this.root.push(new PageBorder("w:right", options.pageBorderRight)); + } + if (options.pageBorderBottom) { + this.root.push(new PageBorder("w:bottom", options.pageBorderBottom)); + } + if (options.pageBorderLeft) { + this.root.push(new PageBorder("w:left", options.pageBorderLeft)); + } + } + + public prepForXml(): IXmlableObject { + return this.root.length > 0 ? super.prepForXml() : ""; + } +} 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 05a49d09d9..e80facb635 100644 --- a/src/file/document/body/section-properties/section-properties.spec.ts +++ b/src/file/document/body/section-properties/section-properties.spec.ts @@ -1,8 +1,8 @@ import { expect } from "chai"; import { Formatter } from "../../../../export/formatter"; +import { FooterReferenceType, PageBorderOffsetFrom, PageNumberFormat } from "./"; import { SectionProperties } from "./section-properties"; -import { FooterReferenceType, PageNumberFormat } from "."; describe("SectionProperties", () => { describe("#constructor()", () => { @@ -155,5 +155,18 @@ describe("SectionProperties", () => { ], }); }); + + it("should create section properties with page borders", () => { + const properties = new SectionProperties({ + pageBorders: { + offsetFrom: PageBorderOffsetFrom.PAGE, + }, + }); + const tree = new Formatter().format(properties); + expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]); + expect(tree["w:sectPr"][7]).to.deep.equal({ + "w:pgBorders": [{ _attr: { "w:offsetFrom": "page" } }], + }); + }); }); }); diff --git a/src/file/document/body/section-properties/section-properties.ts b/src/file/document/body/section-properties/section-properties.ts index c1a666b8ed..c23964cd14 100644 --- a/src/file/document/body/section-properties/section-properties.ts +++ b/src/file/document/body/section-properties/section-properties.ts @@ -1,6 +1,6 @@ // http://officeopenxml.com/WPsection.php import { XmlComponent } from "file/xml-components"; -import { FooterReferenceType, IPageNumberTypeAttributes, PageNumberFormat, PageNumberType } from "./"; +import { FooterReferenceType, IPageBordersOptions, IPageNumberTypeAttributes, PageBorders, PageNumberFormat, PageNumberType } from "./"; import { Columns } from "./columns/columns"; import { IColumnsAttributes } from "./columns/columns-attributes"; import { DocumentGrid } from "./doc-grid/doc-grid"; @@ -12,7 +12,6 @@ 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"; export type SectionPropertiesOptions = IPageSizeAttributes & IPageMarginAttributes & @@ -20,10 +19,12 @@ export type SectionPropertiesOptions = IPageSizeAttributes & IDocGridAttributesProperties & IHeaderOptions & IFooterOptions & - IPageNumberTypeAttributes; + IPageNumberTypeAttributes & + IPageBordersOptions; export class SectionProperties extends XmlComponent { - private options: SectionPropertiesOptions; + private readonly options: SectionPropertiesOptions; + constructor(options?: SectionPropertiesOptions) { super("w:sectPr"); @@ -46,6 +47,11 @@ export class SectionProperties extends XmlComponent { footerId: 0, pageNumberStart: undefined, pageNumberFormatType: PageNumberFormat.DECIMAL, + pageBorders: undefined, + pageBorderTop: undefined, + pageBorderRight: undefined, + pageBorderBottom: undefined, + pageBorderLeft: undefined, }; const mergedOptions = { @@ -83,6 +89,24 @@ export class SectionProperties extends XmlComponent { this.root.push(new PageNumberType(mergedOptions.pageNumberStart, mergedOptions.pageNumberFormatType)); + if ( + mergedOptions.pageBorders || + mergedOptions.pageBorderTop || + mergedOptions.pageBorderRight || + mergedOptions.pageBorderBottom || + mergedOptions.pageBorderLeft + ) { + this.root.push( + new PageBorders({ + pageBorders: mergedOptions.pageBorders, + pageBorderTop: mergedOptions.pageBorderTop, + pageBorderRight: mergedOptions.pageBorderRight, + pageBorderBottom: mergedOptions.pageBorderBottom, + pageBorderLeft: mergedOptions.pageBorderLeft, + }), + ); + } + this.options = mergedOptions; } diff --git a/src/file/document/document.ts b/src/file/document/document.ts index ad4a77bac5..72e48852d7 100644 --- a/src/file/document/document.ts +++ b/src/file/document/document.ts @@ -3,7 +3,7 @@ import { XmlComponent } from "file/xml-components"; import { Paragraph } from "../paragraph"; import { Table } from "../table"; import { Body } from "./body"; -import { SectionPropertiesOptions } from "./body/section-properties/section-properties"; +import { SectionPropertiesOptions } from "./body/section-properties"; import { DocumentAttributes } from "./document-attributes"; export class Document extends XmlComponent { diff --git a/src/file/drawing/drawing.spec.ts b/src/file/drawing/drawing.spec.ts index cf3f926315..87ea5e9e44 100644 --- a/src/file/drawing/drawing.spec.ts +++ b/src/file/drawing/drawing.spec.ts @@ -1,16 +1,17 @@ import { assert } from "chai"; -import * as fs from "fs"; import { Utility } from "../../tests/utility"; import { Drawing, IDrawingOptions, PlacementPosition } from "./"; +const imageBase64Data = `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`; + function createDrawing(drawingOptions?: IDrawingOptions): Drawing { const path = "./demo/images/image1.jpeg"; return new Drawing( { fileName: "test.jpg", referenceId: 1, - stream: fs.createReadStream(path), + stream: Buffer.from(imageBase64Data, "base64"), path: path, dimensions: { pixels: { diff --git a/src/file/drawing/drawing.ts b/src/file/drawing/drawing.ts index 4c3db93b3a..d45a6ad3bb 100644 --- a/src/file/drawing/drawing.ts +++ b/src/file/drawing/drawing.ts @@ -28,7 +28,7 @@ const defaultDrawingOptions: IDrawingOptions = { }; export class Drawing extends XmlComponent { - private inline: Inline; + private readonly inline: Inline; constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) { super("w:drawing"); diff --git a/src/file/drawing/extent/extent.ts b/src/file/drawing/extent/extent.ts index 0e22348cf1..270a85a8a8 100644 --- a/src/file/drawing/extent/extent.ts +++ b/src/file/drawing/extent/extent.ts @@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components"; import { ExtentAttributes } from "./extent-attributes"; export class Extent extends XmlComponent { - private attributes: ExtentAttributes; + private readonly attributes: ExtentAttributes; constructor(x: number, y: number) { super("wp:extent"); diff --git a/src/file/drawing/floating/simple-pos.spec.ts b/src/file/drawing/floating/simple-pos.spec.ts index a86739b7b0..5eee49f4c1 100644 --- a/src/file/drawing/floating/simple-pos.spec.ts +++ b/src/file/drawing/floating/simple-pos.spec.ts @@ -1,7 +1,7 @@ import { assert } from "chai"; -import { SimplePos } from "./simple-pos"; import { Utility } from "../../../tests/utility"; +import { SimplePos } from "./simple-pos"; describe("SimplePos", () => { describe("#constructor()", () => { diff --git a/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts b/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts index ca3d4600be..78606fd399 100644 --- a/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts +++ b/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts @@ -3,7 +3,7 @@ import { GraphicDataAttributes } from "./graphic-data-attribute"; import { Pic } from "./pic"; export class GraphicData extends XmlComponent { - private pic: Pic; + private readonly pic: Pic; constructor(referenceId: number, x: number, y: number) { super("a:graphicData"); diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/pic.ts b/src/file/drawing/inline/graphic/graphic-data/pic/pic.ts index f8b555af00..14ccf63f02 100644 --- a/src/file/drawing/inline/graphic/graphic-data/pic/pic.ts +++ b/src/file/drawing/inline/graphic/graphic-data/pic/pic.ts @@ -6,7 +6,7 @@ import { PicAttributes } from "./pic-attributes"; import { ShapeProperties } from "./shape-properties/shape-properties"; export class Pic extends XmlComponent { - private shapeProperties: ShapeProperties; + private readonly shapeProperties: ShapeProperties; constructor(referenceId: number, x: number, y: number) { super("pic:pic"); diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/extents/extents.ts b/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/extents/extents.ts index 74aea18a48..55bf8bae0b 100644 --- a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/extents/extents.ts +++ b/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/extents/extents.ts @@ -3,7 +3,7 @@ import { XmlComponent } from "file/xml-components"; import { ExtentsAttributes } from "./extents-attributes"; export class Extents extends XmlComponent { - private attributes: ExtentsAttributes; + private readonly attributes: ExtentsAttributes; constructor(x: number, y: number) { super("a:ext"); diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/form.ts b/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/form.ts index 06523e0965..48c069f644 100644 --- a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/form.ts +++ b/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/form.ts @@ -4,7 +4,7 @@ import { Extents } from "./extents/extents"; import { Offset } from "./offset/off"; export class Form extends XmlComponent { - private extents: Extents; + private readonly extents: Extents; constructor(x: number, y: number) { super("a:xfrm"); diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/shape-properties.ts b/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/shape-properties.ts index 9c910f62e2..9dacae18d6 100644 --- a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/shape-properties.ts +++ b/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/shape-properties.ts @@ -7,7 +7,7 @@ import { PresetGeometry } from "./preset-geometry/preset-geometry"; import { ShapePropertiesAttributes } from "./shape-properties-attributes"; export class ShapeProperties extends XmlComponent { - private form: Form; + private readonly form: Form; constructor(x: number, y: number) { super("pic:spPr"); diff --git a/src/file/drawing/inline/graphic/graphic.ts b/src/file/drawing/inline/graphic/graphic.ts index ce61893e05..dba653d2ca 100644 --- a/src/file/drawing/inline/graphic/graphic.ts +++ b/src/file/drawing/inline/graphic/graphic.ts @@ -12,7 +12,7 @@ class GraphicAttributes extends XmlAttributeComponent { } export class Graphic extends XmlComponent { - private data: GraphicData; + private readonly data: GraphicData; constructor(referenceId: number, x: number, y: number) { super("a:graphic"); diff --git a/src/file/drawing/inline/inline.ts b/src/file/drawing/inline/inline.ts index 6e5be2ba13..f36dd19cf3 100644 --- a/src/file/drawing/inline/inline.ts +++ b/src/file/drawing/inline/inline.ts @@ -9,10 +9,10 @@ import { Graphic } from "./../inline/graphic"; import { InlineAttributes } from "./inline-attributes"; export class Inline extends XmlComponent { - private extent: Extent; - private graphic: Graphic; + private readonly extent: Extent; + private readonly graphic: Graphic; - constructor(referenceId: number, private dimensions: IMediaDataDimensions) { + constructor(referenceId: number, private readonly dimensions: IMediaDataDimensions) { super("wp:inline"); this.root.push( diff --git a/src/file/file.ts b/src/file/file.ts index f8d13ed2bf..472baf28b5 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -2,14 +2,13 @@ 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, HeaderReference, HeaderReferenceType } from "./document/body/section-properties"; -import { SectionPropertiesOptions } from "./document/body/section-properties/section-properties"; +import { FooterReferenceType, HeaderReference, HeaderReferenceType, SectionPropertiesOptions } from "./document/body/section-properties"; import { FooterWrapper } from "./footer-wrapper"; import { FootNotes } from "./footnotes"; import { HeaderWrapper } from "./header-wrapper"; -import { Media } from "./media"; +import { Image, Media } from "./media"; import { Numbering } from "./numbering"; -import { Bookmark, Hyperlink, Image, Paragraph } from "./paragraph"; +import { Bookmark, Hyperlink, Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Styles } from "./styles"; import { ExternalStylesFactory } from "./styles/external-styles-factory"; @@ -98,12 +97,12 @@ export class File { sectionPropertiesOptions = { footerType: FooterReferenceType.DEFAULT, headerType: HeaderReferenceType.DEFAULT, - headerId: header.Header.referenceId, - footerId: footer.Footer.referenceId, + headerId: header.Header.ReferenceId, + footerId: footer.Footer.ReferenceId, }; } else { - sectionPropertiesOptions.headerId = header.Header.referenceId; - sectionPropertiesOptions.footerId = footer.Footer.referenceId; + sectionPropertiesOptions.headerId = header.Header.ReferenceId; + sectionPropertiesOptions.footerId = footer.Footer.ReferenceId; } this.document = new Document(sectionPropertiesOptions); } @@ -124,21 +123,14 @@ export class File { return this.document.createTable(rows, cols); } - public createImage(filePath: string): Image { - const image = Media.addImage(this, filePath); - this.document.addParagraph(image); - - return image; - } - - public insertImage(image: Image): File { - this.document.addParagraph(image); + public addImage(image: Image): File { + this.document.addParagraph(image.Paragraph); return this; } - public createImageFromBuffer(buffer: Buffer, width?: number, height?: number): Image { - const image = Media.addImageFromBuffer(this, buffer, width, height); - this.document.addParagraph(image); + public createImage(buffer: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): Image { + const image = Media.addImage(this, buffer, width, height); + this.document.addParagraph(image.Paragraph); return image; } @@ -181,7 +173,7 @@ export class File { const header = new HeaderWrapper(this.media, this.currentRelationshipId++); this.headerWrapper.push(header); this.docRelationships.createRelationship( - header.Header.referenceId, + header.Header.ReferenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header", `header${this.headerWrapper.length}.xml`, ); @@ -193,7 +185,7 @@ export class File { const footer = new FooterWrapper(this.media, this.currentRelationshipId++); this.footerWrapper.push(footer); this.docRelationships.createRelationship( - footer.Footer.referenceId, + footer.Footer.ReferenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer", `footer${this.footerWrapper.length}.xml`, ); @@ -207,7 +199,7 @@ export class File { this.document.Body.DefaultSection.addChildElement( new HeaderReference({ headerType: HeaderReferenceType.FIRST, - headerId: headerWrapper.Header.referenceId, + headerId: headerWrapper.Header.ReferenceId, }), ); @@ -251,7 +243,7 @@ export class File { } public HeaderByRefNumber(refId: number): HeaderWrapper { - const entry = this.headerWrapper.find((h) => h.Header.referenceId === refId); + const entry = this.headerWrapper.find((h) => h.Header.ReferenceId === refId); if (entry) { return entry; } @@ -267,7 +259,7 @@ export class File { } public FooterByRefNumber(refId: number): FooterWrapper { - const entry = this.footerWrapper.find((h) => h.Footer.referenceId === refId); + const entry = this.footerWrapper.find((h) => h.Footer.ReferenceId === refId); if (entry) { return entry; } diff --git a/src/file/footer-wrapper.ts b/src/file/footer-wrapper.ts index 8d90a5a941..afd673c2b2 100644 --- a/src/file/footer-wrapper.ts +++ b/src/file/footer-wrapper.ts @@ -1,7 +1,7 @@ import { XmlComponent } from "file/xml-components"; import { Footer } from "./footer/footer"; -import { Media } from "./media"; -import { Image, Paragraph } from "./paragraph"; +import { Image, Media } from "./media"; +import { ImageParagraph, Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Table } from "./table"; @@ -36,18 +36,18 @@ export class FooterWrapper { this.footer.addChildElement(childElement); } - public createImage(image: string): void { - const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount); + public createImage(image: Buffer, width?: number, height?: number): void { + const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount, width, height); this.relationships.createRelationship( mediaData.referenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", `media/${mediaData.fileName}`, ); - this.insertImage(new Image(mediaData)); + this.addImage(new Image(new ImageParagraph(mediaData))); } - public insertImage(image: Image): FooterWrapper { - this.footer.addParagraph(image); + public addImage(image: Image): FooterWrapper { + this.footer.addParagraph(image.Paragraph); return this; } diff --git a/src/file/footer/footer.ts b/src/file/footer/footer.ts index 4237e350a4..532e662c25 100644 --- a/src/file/footer/footer.ts +++ b/src/file/footer/footer.ts @@ -5,7 +5,8 @@ import { Table } from "../table"; import { FooterAttributes } from "./footer-attributes"; export class Footer extends XmlComponent { - private refId: number; + private readonly refId: number; + constructor(referenceNumber: number) { super("w:ftr"); this.refId = referenceNumber; @@ -31,7 +32,7 @@ export class Footer extends XmlComponent { ); } - public get referenceId(): number { + public get ReferenceId(): number { return this.refId; } diff --git a/src/file/header-wrapper.ts b/src/file/header-wrapper.ts index b8183b92ba..79a216610a 100644 --- a/src/file/header-wrapper.ts +++ b/src/file/header-wrapper.ts @@ -1,7 +1,7 @@ import { XmlComponent } from "file/xml-components"; import { Header } from "./header/header"; -import { Media } from "./media"; -import { Image, Paragraph } from "./paragraph"; +import { Image, Media } from "./media"; +import { ImageParagraph, Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Table } from "./table"; @@ -36,18 +36,18 @@ export class HeaderWrapper { this.header.addChildElement(childElement); } - public createImage(image: string): void { - const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount); + public createImage(image: Buffer, width?: number, height?: number): void { + const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount, width, height); this.relationships.createRelationship( mediaData.referenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", `media/${mediaData.fileName}`, ); - this.insertImage(new Image(mediaData)); + this.addImage(new Image(new ImageParagraph(mediaData))); } - public insertImage(image: Image): HeaderWrapper { - this.header.addParagraph(image); + public addImage(image: Image): HeaderWrapper { + this.header.addParagraph(image.Paragraph); return this; } diff --git a/src/file/header/header.ts b/src/file/header/header.ts index 8260b332e3..be1708658f 100644 --- a/src/file/header/header.ts +++ b/src/file/header/header.ts @@ -5,9 +5,11 @@ import { Table } from "../table"; import { HeaderAttributes } from "./header-attributes"; export class Header extends XmlComponent { - private refId: number; + private readonly refId: number; + constructor(referenceNumber: number) { super("w:hdr"); + this.refId = referenceNumber; this.root.push( new HeaderAttributes({ @@ -31,7 +33,7 @@ export class Header extends XmlComponent { ); } - public get referenceId(): number { + public get ReferenceId(): number { return this.refId; } diff --git a/src/file/media/data.ts b/src/file/media/data.ts index cbc7d8c5bf..42e5874f99 100644 --- a/src/file/media/data.ts +++ b/src/file/media/data.ts @@ -1,5 +1,3 @@ -import * as fs from "fs"; - export interface IMediaDataDimensions { pixels: { x: number; @@ -13,11 +11,14 @@ export interface IMediaDataDimensions { export interface IMediaData { referenceId: number; - stream: fs.ReadStream | Buffer; + stream: Buffer | Uint8Array | ArrayBuffer; path?: string; fileName: string; dimensions: IMediaDataDimensions; } // Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432 +/** + * @ignore + */ export const WORKAROUND2 = ""; diff --git a/src/file/media/image.ts b/src/file/media/image.ts new file mode 100644 index 0000000000..b596f77d9d --- /dev/null +++ b/src/file/media/image.ts @@ -0,0 +1,17 @@ +import { ImageParagraph, PictureRun } from "../paragraph"; + +export class Image { + constructor(private readonly paragraph: ImageParagraph) {} + + public get Paragraph(): ImageParagraph { + return this.paragraph; + } + + public get Run(): PictureRun { + return this.paragraph.Run; + } + + public scale(factorX: number, factorY?: number): void { + this.paragraph.Run.scale(factorX, factorY); + } +} diff --git a/src/file/media/index.ts b/src/file/media/index.ts index 3575274e26..2ccc436e68 100644 --- a/src/file/media/index.ts +++ b/src/file/media/index.ts @@ -1,2 +1,3 @@ export * from "./media"; export * from "./data"; +export * from "./image"; diff --git a/src/file/media/media.ts b/src/file/media/media.ts index ed081cc5e1..284ca8806d 100644 --- a/src/file/media/media.ts +++ b/src/file/media/media.ts @@ -1,45 +1,31 @@ -import * as fs from "fs"; -import * as sizeOf from "image-size"; -import * as path from "path"; - +import { IDrawingOptions } from "../drawing"; import { File } from "../file"; -import { Image } from "../paragraph"; +import { ImageParagraph } from "../paragraph"; import { IMediaData } from "./data"; +import { Image } from "./image"; interface IHackedFile { currentRelationshipId: number; } export class Media { - public static addImage(file: File, filePath: string): Image { + public static addImage( + file: File, + buffer: Buffer | string | Uint8Array | ArrayBuffer, + width?: number, + height?: number, + drawingOptions?: IDrawingOptions, + ): Image { // Workaround to expose id without exposing to API const exposedFile = (file as {}) as IHackedFile; - const mediaData = file.Media.addMedia(filePath, exposedFile.currentRelationshipId++); - file.DocumentRelationships.createRelationship( - mediaData.referenceId, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - `media/${mediaData.fileName}`, - ); - return new Image(mediaData); - } - - public static addImageFromBuffer(file: File, buffer: Buffer, width?: number, height?: number): Image { - // Workaround to expose id without exposing to API - const exposedFile = (file as {}) as IHackedFile; - const mediaData = file.Media.addMediaFromBuffer( - `${Media.generateId()}.png`, - buffer, - exposedFile.currentRelationshipId++, - width, - height, - ); + const mediaData = file.Media.addMedia(buffer, exposedFile.currentRelationshipId++, width, height); file.DocumentRelationships.createRelationship( mediaData.referenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", `media/${mediaData.fileName}`, ); - return new Image(mediaData); + return new Image(new ImageParagraph(mediaData, drawingOptions)); } private static generateId(): string { @@ -70,34 +56,36 @@ export class Media { return data; } - public addMedia(filePath: string, referenceId: number): IMediaData { - const key = path.basename(filePath); - const dimensions = sizeOf(filePath); - return this.createMedia(key, referenceId, dimensions, fs.createReadStream(filePath), filePath); - } + public addMedia( + buffer: Buffer | string | Uint8Array | ArrayBuffer, + referenceId: number, + width: number = 100, + height: number = 100, + ): IMediaData { + const key = `${Media.generateId()}.png`; - public addMediaFromBuffer(fileName: string, buffer: Buffer, referenceId: number, width?: number, height?: number): IMediaData { - const key = fileName; - let dimensions; - if (width && height) { - dimensions = { + return this.createMedia( + key, + referenceId, + { width: width, height: height, - }; - } else { - dimensions = sizeOf(buffer); - } - - return this.createMedia(key, referenceId, dimensions, buffer); + }, + buffer, + ); } private createMedia( key: string, relationshipsCount: number, dimensions: { width: number; height: number }, - data: fs.ReadStream | Buffer, + data: Buffer | string | Uint8Array | ArrayBuffer, filePath?: string, ): IMediaData { + if (typeof data === "string") { + data = this.convertDataURIToBinary(data); + } + const imageData = { referenceId: this.map.size + relationshipsCount + 1, stream: data, @@ -105,12 +93,12 @@ export class Media { fileName: key, dimensions: { pixels: { - x: dimensions.width, - y: dimensions.height, + x: Math.round(dimensions.width), + y: Math.round(dimensions.height), }, emus: { - x: dimensions.width * 9525, - y: dimensions.height * 9525, + x: Math.round(dimensions.width * 9525), + y: Math.round(dimensions.height * 9525), }, }, }; @@ -120,7 +108,7 @@ export class Media { return imageData; } - public get array(): IMediaData[] { + public get Array(): IMediaData[] { const array = new Array(); this.map.forEach((data) => { @@ -129,4 +117,23 @@ export class Media { return array; } + + private convertDataURIToBinary(dataURI: string): Uint8Array { + // https://gist.github.com/borismus/1032746 + // https://github.com/mafintosh/base64-to-uint8array + const BASE64_MARKER = ";base64,"; + + const base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length; + + if (typeof atob === "function") { + return new Uint8Array( + atob(dataURI.substring(base64Index)) + .split("") + .map((c) => c.charCodeAt(0)), + ); + } else { + const b = require("buf" + "fer"); + return new b.Buffer(dataURI, "base64"); + } + } } diff --git a/src/file/numbering/index.ts b/src/file/numbering/index.ts index a861d336ee..d83cffc61c 100644 --- a/src/file/numbering/index.ts +++ b/src/file/numbering/index.ts @@ -1,2 +1,3 @@ export * from "./numbering"; export * from "./abstract-numbering"; +export * from "./level"; diff --git a/src/file/numbering/level.ts b/src/file/numbering/level.ts index bb50bfb2e2..8bdc7ff8d7 100644 --- a/src/file/numbering/level.ts +++ b/src/file/numbering/level.ts @@ -60,6 +60,23 @@ class LevelJc extends XmlComponent { } } +export enum LevelSuffix { + NOTHING = "nothing", + SPACE = "space", + TAB = "tab", +} + +class Suffix extends XmlComponent { + constructor(value: LevelSuffix) { + super("w:suff"); + this.root.push( + new Attributes({ + val: value, + }), + ); + } +} + export class LevelBase extends XmlComponent { private readonly paragraphProperties: ParagraphProperties; private readonly runProperties: RunProperties; @@ -93,6 +110,11 @@ export class LevelBase extends XmlComponent { this.root.push(this.runProperties); } + public setSuffix(value: LevelSuffix): LevelBase { + this.root.push(new Suffix(value)); + return this; + } + public addParagraphProperty(property: XmlComponent): Level { this.paragraphProperties.push(property); return this; diff --git a/src/file/numbering/num.ts b/src/file/numbering/num.ts index c4caaf8881..e4600339cf 100644 --- a/src/file/numbering/num.ts +++ b/src/file/numbering/num.ts @@ -56,7 +56,7 @@ export class LevelOverride extends XmlComponent { } } - public get level(): LevelForOverride { + public get Level(): LevelForOverride { let lvl: LevelForOverride; if (!this.lvl) { lvl = new LevelForOverride(this.levelNum); diff --git a/src/file/numbering/numbering.spec.ts b/src/file/numbering/numbering.spec.ts index 17b46eda30..05dc4a391a 100644 --- a/src/file/numbering/numbering.spec.ts +++ b/src/file/numbering/numbering.spec.ts @@ -387,9 +387,9 @@ describe("concrete numbering", () => { }); }); - it("sets the lvl element if overrideLevel.level is accessed", () => { + it("sets the lvl element if overrideLevel.Level is accessed", () => { const ol = concreteNumbering.overrideLevel(1); - expect(ol.level).to.be.instanceof(LevelForOverride); + expect(ol.Level).to.be.instanceof(LevelForOverride); const tree = new Formatter().format(concreteNumbering); expect(tree["w:num"]).to.include({ "w:lvlOverride": [ diff --git a/src/file/numbering/numbering.ts b/src/file/numbering/numbering.ts index 0a886e5598..a256500c45 100644 --- a/src/file/numbering/numbering.ts +++ b/src/file/numbering/numbering.ts @@ -7,8 +7,8 @@ import { Num } from "./num"; export class Numbering extends XmlComponent { private nextId: number; - private abstractNumbering: XmlComponent[] = []; - private concreteNumbering: XmlComponent[] = []; + private readonly abstractNumbering: XmlComponent[] = []; + private readonly concreteNumbering: XmlComponent[] = []; constructor() { super("w:numbering"); diff --git a/src/file/paragraph/formatting/bidi.ts b/src/file/paragraph/formatting/bidirectional.ts similarity index 67% rename from src/file/paragraph/formatting/bidi.ts rename to src/file/paragraph/formatting/bidirectional.ts index 4a8bbdade9..4083247e78 100644 --- a/src/file/paragraph/formatting/bidi.ts +++ b/src/file/paragraph/formatting/bidirectional.ts @@ -1,6 +1,6 @@ import { XmlComponent } from "file/xml-components"; -export class Bidi extends XmlComponent { +export class Bidirectional extends XmlComponent { constructor() { super("w:bidi"); } diff --git a/src/file/paragraph/formatting/border.ts b/src/file/paragraph/formatting/border.ts index 9ceb0c1336..f37854a3c7 100644 --- a/src/file/paragraph/formatting/border.ts +++ b/src/file/paragraph/formatting/border.ts @@ -1,23 +1,63 @@ // http://officeopenxml.com/WPborders.php import { Attributes, XmlComponent } from "file/xml-components"; -class Border extends XmlComponent { +class BorderProperty extends XmlComponent { + public setProperties(color: string, space: string, value: string, size: string): XmlComponent { + const attrs = new Attributes({ + color: color, + space: space, + val: value, + sz: size, + }); + this.root.push(attrs); + + return this; + } +} + +export class Border extends XmlComponent { constructor() { - super("w:bottom"); - this.root.push( - new Attributes({ - color: "auto", - space: "1", - val: "single", - sz: "6", - }), - ); + super("w:pBdr"); + } + + public addTopBorder(color: string = "auto", space: string = "1", value: string = "single", size: string = "6"): XmlComponent { + const top = new BorderProperty("w:top"); + top.setProperties(color, space, value, size); + this.root.push(top); + + return this; + } + + public addBottomBorder(color: string = "auto", space: string = "1", value: string = "single", size: string = "6"): XmlComponent { + const bottom = new BorderProperty("w:bottom"); + bottom.setProperties(color, space, value, size); + this.root.push(bottom); + + return this; + } + + public addLeftBorder(color: string = "auto", space: string = "1", value: string = "single", size: string = "6"): XmlComponent { + const left = new BorderProperty("w:left"); + left.setProperties(color, space, value, size); + this.root.push(left); + + return this; + } + + public addRightBorder(color: string = "auto", space: string = "1", value: string = "single", size: string = "6"): XmlComponent { + const right = new BorderProperty("w:right"); + right.setProperties(color, space, value, size); + this.root.push(right); + + return this; } } export class ThematicBreak extends XmlComponent { constructor() { super("w:pBdr"); - this.root.push(new Border()); + const bottom = new BorderProperty("w:bottom"); + bottom.setProperties("auto", "1", "single", "6"); + this.root.push(bottom); } } diff --git a/src/file/paragraph/formatting/indent.ts b/src/file/paragraph/formatting/indent.ts index 0b42246689..cd7528d2b8 100644 --- a/src/file/paragraph/formatting/indent.ts +++ b/src/file/paragraph/formatting/indent.ts @@ -1,7 +1,7 @@ // http://officeopenxml.com/WPindentation.php import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; -interface IIndentAttributesProperties { +export interface IIndentAttributesProperties { left?: number; hanging?: number; firstLine?: number; @@ -20,7 +20,7 @@ class IndentAttributes extends XmlAttributeComponent { - let image: Image; + let image: ImageParagraph; beforeEach(() => { - image = new Image({ + image = new ImageParagraph({ referenceId: 0, stream: new Buffer(""), path: "", diff --git a/src/file/paragraph/image.ts b/src/file/paragraph/image.ts index 214378853c..634f76e517 100644 --- a/src/file/paragraph/image.ts +++ b/src/file/paragraph/image.ts @@ -3,7 +3,7 @@ import { IMediaData } from "../media"; import { Paragraph } from "./paragraph"; import { PictureRun } from "./run"; -export class Image extends Paragraph { +export class ImageParagraph extends Paragraph { private readonly pictureRun: PictureRun; constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) { @@ -15,4 +15,8 @@ export class Image extends Paragraph { public scale(factorX: number, factorY?: number): void { this.pictureRun.scale(factorX, factorY); } + + public get Run(): PictureRun { + return this.pictureRun; + } } diff --git a/src/file/paragraph/paragraph.spec.ts b/src/file/paragraph/paragraph.spec.ts index a2176f3ecf..2413769b1e 100644 --- a/src/file/paragraph/paragraph.spec.ts +++ b/src/file/paragraph/paragraph.spec.ts @@ -144,6 +144,51 @@ describe("Paragraph", () => { }); }); + describe("#paragraphBorders()", () => { + it("should add a left and right border to a paragraph", () => { + paragraph.createBorder(); + paragraph.Borders.addLeftBorder(); + paragraph.Borders.addRightBorder(); + const tree = new Formatter().format(paragraph); + expect(tree).to.deep.equal({ + "w:p": [ + { + "w:pPr": [ + { + "w:pBdr": [ + { + "w:left": [ + { + _attr: { + "w:color": "auto", + "w:space": "1", + "w:sz": "6", + "w:val": "single", + }, + }, + ], + }, + { + "w:right": [ + { + _attr: { + "w:color": "auto", + "w:space": "1", + "w:sz": "6", + "w:val": "single", + }, + }, + ], + }, + ], + }, + ], + }, + ], + }); + }); + }); + describe("#pageBreak()", () => { it("should add page break to JSON", () => { paragraph.pageBreak(); @@ -339,9 +384,9 @@ describe("Paragraph", () => { }); }); - describe("#bidi", () => { + describe("#bidirectional", () => { it("set paragraph right to left layout", () => { - paragraph.bidi(); + paragraph.bidirectional(); const tree = new Formatter().format(paragraph); expect(tree).to.deep.equal({ "w:p": [{ "w:pPr": [{ "w:bidi": [] }] }], diff --git a/src/file/paragraph/paragraph.ts b/src/file/paragraph/paragraph.ts index 3695b70bd5..42b34e0c0d 100644 --- a/src/file/paragraph/paragraph.ts +++ b/src/file/paragraph/paragraph.ts @@ -1,13 +1,13 @@ // http://officeopenxml.com/WPparagraph.php import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run"; -import { IMediaData } from "file/media"; +import { Image } from "file/media"; import { Num } from "file/numbering/num"; import { XmlComponent } from "file/xml-components"; import { Alignment } from "./formatting/alignment"; -import { Bidi } from "./formatting/bidi"; -import { ThematicBreak } from "./formatting/border"; -import { Indent } from "./formatting/indent"; +import { Bidirectional } from "./formatting/bidirectional"; +import { Border, ThematicBreak } from "./formatting/border"; +import { IIndentAttributesProperties, Indent } from "./formatting/indent"; import { KeepLines, KeepNext } from "./formatting/keep"; import { PageBreak, PageBreakBefore } from "./formatting/page-break"; import { ISpacingProperties, Spacing } from "./formatting/spacing"; @@ -19,7 +19,7 @@ import { ParagraphProperties } from "./properties"; import { PictureRun, Run, TextRun } from "./run"; export class Paragraph extends XmlComponent { - private properties: ParagraphProperties; + private readonly properties: ParagraphProperties; constructor(text?: string) { super("w:p"); @@ -30,6 +30,15 @@ export class Paragraph extends XmlComponent { } } + public get Borders(): Border { + return this.properties.paragraphBorder; + } + + public createBorder(): Paragraph { + this.properties.createBorder(); + return this; + } + public addRun(run: Run): Paragraph { this.root.push(run); return this; @@ -54,9 +63,10 @@ export class Paragraph extends XmlComponent { return run; } - public createPictureRun(imageData: IMediaData): PictureRun { - const run = new PictureRun(imageData); + public addImage(image: Image): PictureRun { + const run = image.Run; this.addRun(run); + return run; } @@ -187,7 +197,7 @@ export class Paragraph extends XmlComponent { return this; } - public indent(attrs: object): Paragraph { + public indent(attrs: IIndentAttributesProperties): Paragraph { this.properties.push(new Indent(attrs)); return this; } @@ -217,8 +227,12 @@ export class Paragraph extends XmlComponent { return this; } - public bidi(): Paragraph { - this.properties.push(new Bidi()); + public bidirectional(): Paragraph { + this.properties.push(new Bidirectional()); return this; } + + public get Properties(): ParagraphProperties { + return this.properties; + } } diff --git a/src/file/paragraph/paragraph.ts.orig b/src/file/paragraph/paragraph.ts.orig new file mode 100644 index 0000000000..7ffbeca43d --- /dev/null +++ b/src/file/paragraph/paragraph.ts.orig @@ -0,0 +1,243 @@ +// http://officeopenxml.com/WPparagraph.php +import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run"; +import { Image } from "file/media"; +import { Num } from "file/numbering/num"; +import { XmlComponent } from "file/xml-components"; + +import { Alignment } from "./formatting/alignment"; +import { Bidirectional } from "./formatting/bidirectional"; +<<<<<<< HEAD +import { Border, ThematicBreak } from "./formatting/border"; +import { Indent } from "./formatting/indent"; +======= +import { ThematicBreak } from "./formatting/border"; +import { IIndentAttributesProperties, Indent } from "./formatting/indent"; +>>>>>>> a53818754a1c76b9930ee2ecc642570170fa3c06 +import { KeepLines, KeepNext } from "./formatting/keep"; +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"; +import { NumberProperties } from "./formatting/unordered-list"; +import { Bookmark, Hyperlink } from "./links"; +import { ParagraphProperties } from "./properties"; +import { PictureRun, Run, TextRun } from "./run"; + +export class Paragraph extends XmlComponent { + private readonly properties: ParagraphProperties; + + constructor(text?: string) { + super("w:p"); + this.properties = new ParagraphProperties(); + this.root.push(this.properties); + if (text !== undefined) { + this.root.push(new TextRun(text)); + } + } + + public get Borders(): Border { + return this.properties.paragraphBorder; + } + + public createBorder(): Paragraph { + this.properties.createBorder(); + return this; + } + + public addRun(run: Run): Paragraph { + this.root.push(run); + return this; + } + + public addHyperLink(hyperlink: Hyperlink): Paragraph { + this.root.push(hyperlink); + return this; + } + + public addBookmark(bookmark: Bookmark): Paragraph { + // Bookmarks by spec have three components, a start, text, and end + this.root.push(bookmark.start); + this.root.push(bookmark.text); + this.root.push(bookmark.end); + return this; + } + + public createTextRun(text: string): TextRun { + const run = new TextRun(text); + this.addRun(run); + return run; + } + + public addImage(image: Image): PictureRun { + const run = image.Run; + this.addRun(run); + + return run; + } + + public heading1(): Paragraph { + this.properties.push(new Style("Heading1")); + return this; + } + + public heading2(): Paragraph { + this.properties.push(new Style("Heading2")); + return this; + } + + public heading3(): Paragraph { + this.properties.push(new Style("Heading3")); + return this; + } + + public heading4(): Paragraph { + this.properties.push(new Style("Heading4")); + return this; + } + + public heading5(): Paragraph { + this.properties.push(new Style("Heading5")); + return this; + } + + public heading6(): Paragraph { + this.properties.push(new Style("Heading6")); + return this; + } + + public title(): Paragraph { + this.properties.push(new Style("Title")); + return this; + } + + public center(): Paragraph { + this.properties.push(new Alignment("center")); + return this; + } + + public left(): Paragraph { + this.properties.push(new Alignment("left")); + return this; + } + + public right(): Paragraph { + this.properties.push(new Alignment("right")); + return this; + } + + public start(): Paragraph { + this.properties.push(new Alignment("start")); + return this; + } + + public end(): Paragraph { + this.properties.push(new Alignment("end")); + return this; + } + + public distribute(): Paragraph { + this.properties.push(new Alignment("distribute")); + return this; + } + + public justified(): Paragraph { + this.properties.push(new Alignment("both")); + return this; + } + + public thematicBreak(): Paragraph { + this.properties.push(new ThematicBreak()); + return this; + } + + public pageBreak(): Paragraph { + this.root.push(new PageBreak()); + return this; + } + + public pageBreakBefore(): Paragraph { + this.properties.push(new PageBreakBefore()); + return this; + } + + public maxRightTabStop(): Paragraph { + this.properties.push(new MaxRightTabStop()); + return this; + } + + public leftTabStop(position: number): Paragraph { + this.properties.push(new LeftTabStop(position)); + return this; + } + + public rightTabStop(position: number): Paragraph { + this.properties.push(new RightTabStop(position)); + return this; + } + + public centerTabStop(position: number): Paragraph { + this.properties.push(new CenterTabStop(position)); + return this; + } + + public bullet(indentLevel: number = 0): Paragraph { + this.properties.push(new Style("ListParagraph")); + this.properties.push(new NumberProperties(1, indentLevel)); + return this; + } + + public setNumbering(numbering: Num, indentLevel: number): Paragraph { + this.properties.push(new Style("ListParagraph")); + this.properties.push(new NumberProperties(numbering.id, indentLevel)); + return this; + } + + public setCustomNumbering(numberId: number, indentLevel: number): Paragraph { + this.properties.push(new NumberProperties(numberId, indentLevel)); + return this; + } + + public style(styleId: string): Paragraph { + this.properties.push(new Style(styleId)); + return this; + } + + public indent(attrs: IIndentAttributesProperties): Paragraph { + this.properties.push(new Indent(attrs)); + return this; + } + + public spacing(params: ISpacingProperties): Paragraph { + this.properties.push(new Spacing(params)); + return this; + } + + public keepNext(): Paragraph { + this.properties.push(new KeepNext()); + return this; + } + + public keepLines(): Paragraph { + this.properties.push(new KeepLines()); + return this; + } + + public referenceFootnote(id: number): Paragraph { + this.root.push(new FootnoteReferenceRun(id)); + return this; + } + + public addRunToFront(run: Run): Paragraph { + this.root.splice(1, 0, run); + return this; + } + + public bidirectional(): Paragraph { + this.properties.push(new Bidirectional()); + return this; + } + + public get Properties(): ParagraphProperties { + return this.properties; + } +} diff --git a/src/file/paragraph/properties.ts b/src/file/paragraph/properties.ts index 93fba4ce31..5f0c651246 100644 --- a/src/file/paragraph/properties.ts +++ b/src/file/paragraph/properties.ts @@ -1,9 +1,17 @@ // http://officeopenxml.com/WPparagraphProperties.php import { XmlComponent } from "file/xml-components"; +import { Border } from "./formatting/border"; export class ParagraphProperties extends XmlComponent { + public paragraphBorder: Border; + constructor() { super("w:pPr"); + this.paragraphBorder = new Border(); + } + + public createBorder(): void { + this.push(this.paragraphBorder); } public push(item: XmlComponent): void { diff --git a/src/file/paragraph/run/formatting.ts b/src/file/paragraph/run/formatting.ts index a59879917c..8b1be933ec 100644 --- a/src/file/paragraph/run/formatting.ts +++ b/src/file/paragraph/run/formatting.ts @@ -14,7 +14,7 @@ export class Bold extends XmlComponent { } } -export class BoldCs extends XmlComponent { +export class BoldComplexScript extends XmlComponent { constructor() { super("w:bCs"); this.root.push( @@ -36,7 +36,7 @@ export class Italics extends XmlComponent { } } -export class ItalicsCs extends XmlComponent { +export class ItalicsComplexScript extends XmlComponent { constructor() { super("w:iCs"); this.root.push( @@ -146,7 +146,7 @@ export class Size extends XmlComponent { } } -export class SizeCs extends XmlComponent { +export class SizeComplexScript extends XmlComponent { constructor(size: number) { super("w:szCs"); this.root.push( @@ -157,7 +157,7 @@ export class SizeCs extends XmlComponent { } } -export class RTL extends XmlComponent { +export class RightToLeft extends XmlComponent { constructor() { super("w:rtl"); this.root.push( diff --git a/src/file/paragraph/run/picture-run.ts b/src/file/paragraph/run/picture-run.ts index 6a07b3bd9e..d51d0fc8f9 100644 --- a/src/file/paragraph/run/picture-run.ts +++ b/src/file/paragraph/run/picture-run.ts @@ -4,7 +4,7 @@ import { IMediaData } from "../../media/data"; import { Run } from "../run"; export class PictureRun extends Run { - private drawing: Drawing; + private readonly drawing: Drawing; constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) { super(); diff --git a/src/file/paragraph/run/run.spec.ts b/src/file/paragraph/run/run.spec.ts index 52443bc0d2..cdd925d9d5 100644 --- a/src/file/paragraph/run/run.spec.ts +++ b/src/file/paragraph/run/run.spec.ts @@ -147,7 +147,7 @@ describe("Run", () => { describe("#rtl", () => { it("should set the run to the RTL mode", () => { - run.rtl(); + run.rightToLeft(); const tree = new Formatter().format(run); expect(tree).to.deep.equal({ "w:r": [{ "w:rPr": [{ "w:rtl": [{ _attr: { "w:val": true } }] }] }], diff --git a/src/file/paragraph/run/run.ts b/src/file/paragraph/run/run.ts index b08627aadb..70b344842f 100644 --- a/src/file/paragraph/run/run.ts +++ b/src/file/paragraph/run/run.ts @@ -1,7 +1,18 @@ // http://officeopenxml.com/WPtext.php import { Break } from "./break"; import { Caps, SmallCaps } from "./caps"; -import { Bold, BoldCs, Color, DoubleStrike, Italics, ItalicsCs, RTL, Size, SizeCs, Strike } from "./formatting"; +import { + Bold, + BoldComplexScript, + Color, + DoubleStrike, + Italics, + ItalicsComplexScript, + RightToLeft, + Size, + SizeComplexScript, + Strike, +} from "./formatting"; import { Begin, End, Page, Separate } from "./page-number"; import { RunProperties } from "./properties"; import { RunFonts } from "./run-fonts"; @@ -23,13 +34,13 @@ export class Run extends XmlComponent { public bold(): Run { this.properties.push(new Bold()); - this.properties.push(new BoldCs()); + this.properties.push(new BoldComplexScript()); return this; } public italic(): Run { this.properties.push(new Italics()); - this.properties.push(new ItalicsCs()); + this.properties.push(new ItalicsComplexScript()); return this; } @@ -45,12 +56,12 @@ export class Run extends XmlComponent { public size(size: number): Run { this.properties.push(new Size(size)); - this.properties.push(new SizeCs(size)); + this.properties.push(new SizeComplexScript(size)); return this; } - public rtl(): Run { - this.properties.push(new RTL()); + public rightToLeft(): Run { + this.properties.push(new RightToLeft()); return this; } diff --git a/src/file/styles/border/border-style.ts b/src/file/styles/border/border-style.ts new file mode 100644 index 0000000000..00f4117d31 --- /dev/null +++ b/src/file/styles/border/border-style.ts @@ -0,0 +1,29 @@ +export enum BorderStyle { + SINGLE = "single", + DASH_DOT_STROKED = "dashDotStroked", + DASHED = "dashed", + DASH_SMALL_GAP = "dashSmallGap", + DOT_DASH = "dotDash", + DOT_DOT_DASH = "dotDotDash", + DOTTED = "dotted", + DOUBLE = "double", + DOUBLE_WAVE = "doubleWave", + INSET = "inset", + NIL = "nil", + NONE = "none", + OUTSET = "outset", + THICK = "thick", + THICK_THIN_LARGE_GAP = "thickThinLargeGap", + THICK_THIN_MEDIUM_GAP = "thickThinMediumGap", + THICK_THIN_SMALL_GAP = "thickThinSmallGap", + THIN_THICK_LARGE_GAP = "thinThickLargeGap", + THIN_THICK_MEDIUM_GAP = "thinThickMediumGap", + THIN_THICK_SMALL_GAP = "thinThickSmallGap", + THIN_THICK_THIN_LARGE_GAP = "thinThickThinLargeGap", + THIN_THICK_THIN_MEDIUM_GAP = "thinThickThinMediumGap", + THIN_THICK_THIN_SMALL_GAP = "thinThickThinSmallGap", + THREE_D_EMBOSS = "threeDEmboss", + THREE_D_ENGRAVE = "threeDEngrave", + TRIPLE = "triple", + WAVE = "wave", +} diff --git a/src/file/styles/border/index.ts b/src/file/styles/border/index.ts new file mode 100644 index 0000000000..e62e5f7dcc --- /dev/null +++ b/src/file/styles/border/index.ts @@ -0,0 +1 @@ +export * from "./border-style"; diff --git a/src/file/styles/defaults/run-properties.ts b/src/file/styles/defaults/run-properties.ts index 1f4018dee2..9f30986b84 100644 --- a/src/file/styles/defaults/run-properties.ts +++ b/src/file/styles/defaults/run-properties.ts @@ -1,5 +1,5 @@ import { XmlComponent } from "file/xml-components"; -import { Size, SizeCs } from "../../paragraph/run/formatting"; +import { Size, SizeComplexScript } from "../../paragraph/run/formatting"; import { RunProperties } from "../../paragraph/run/properties"; import { RunFonts } from "../../paragraph/run/run-fonts"; @@ -14,7 +14,7 @@ export class RunPropertiesDefaults extends XmlComponent { public size(size: number): RunPropertiesDefaults { this.properties.push(new Size(size)); - this.properties.push(new SizeCs(size)); + this.properties.push(new SizeComplexScript(size)); return this; } diff --git a/src/file/styles/external-styles-factory.spec.ts b/src/file/styles/external-styles-factory.spec.ts index 861a6f05d8..295c9644f6 100644 --- a/src/file/styles/external-styles-factory.spec.ts +++ b/src/file/styles/external-styles-factory.spec.ts @@ -10,8 +10,19 @@ describe("External styles factory", () => { + + + + + + + + + + + - + @@ -37,6 +48,7 @@ describe("External styles factory", () => { describe("#parse", () => { it("should parse w:styles attributes", () => { + // tslint:disable-next-line:no-any const importedStyle = new ExternalStylesFactory().newInstance(externalStyles) as any; expect(importedStyle.rootKey).to.equal("w:styles"); @@ -47,12 +59,69 @@ describe("External styles factory", () => { }); it("should parse other child elements of w:styles", () => { + // tslint:disable-next-line:no-any const importedStyle = new ExternalStylesFactory().newInstance(externalStyles) as any; expect(importedStyle.root.length).to.equal(5); expect(importedStyle.root[1]).to.eql({ deleted: false, - root: [], + root: [ + { + deleted: false, + root: [ + { + deleted: false, + root: [ + { + _attr: { + "w:ascii": "Arial", + "w:cstheme": "minorHAnsi", + "w:eastAsiaTheme": "minorHAnsi", + "w:hAnsi": "Arial", + }, + deleted: false, + root: [], + rootKey: "w:rFonts", + }, + { + _attr: { + "w:bidi": "ar-SA", + "w:eastAsia": "en-US", + "w:val": "en-US", + }, + deleted: false, + root: [], + rootKey: "w:lang", + }, + ], + rootKey: "w:rPr", + }, + ], + rootKey: "w:rPrDefault", + }, + { + deleted: false, + root: [ + { + deleted: false, + root: [ + { + _attr: { + "w:after": "160", + "w:line": "259", + "w:lineRule": "auto", + }, + deleted: false, + root: [], + rootKey: "w:spacing", + }, + ], + rootKey: "w:pPr", + }, + ], + rootKey: "w:pPrDefault", + }, + ], rootKey: "w:docDefaults", }); expect(importedStyle.root[2]).to.eql({ @@ -67,6 +136,7 @@ describe("External styles factory", () => { }); it("should parse styles elements", () => { + // tslint:disable-next-line:no-any const importedStyle = new ExternalStylesFactory().newInstance(externalStyles) as any; expect(importedStyle.root.length).to.equal(5); diff --git a/src/file/styles/external-styles-factory.ts b/src/file/styles/external-styles-factory.ts index 2ebd6323bd..301211bdc7 100644 --- a/src/file/styles/external-styles-factory.ts +++ b/src/file/styles/external-styles-factory.ts @@ -1,5 +1,5 @@ import * as fastXmlParser from "fast-xml-parser"; -import { convertToXmlComponent, ImportedRootElementAttributes, ImportedXmlComponent, parseOptions } from "file/xml-components"; +import { convertToXmlComponent, ImportedRootElementAttributes, parseOptions } from "file/xml-components"; import { Styles } from "./"; export class ExternalStylesFactory { @@ -34,7 +34,12 @@ export class ExternalStylesFactory { Object.keys(xmlStyles) .filter((element) => element !== "_attr" && element !== "w:style") .forEach((element) => { - importedStyle.push(new ImportedXmlComponent(element, xmlStyles[element]._attr)); + const converted = convertToXmlComponent(element, xmlStyles[element]); + if (Array.isArray(converted)) { + converted.forEach((c) => importedStyle.push(c)); + } else { + importedStyle.push(converted); + } }); // convert the styles one by one diff --git a/src/file/styles/index.ts b/src/file/styles/index.ts index 30f81c0012..d6cdbfc6f9 100644 --- a/src/file/styles/index.ts +++ b/src/file/styles/index.ts @@ -1,6 +1,7 @@ import { BaseXmlComponent, XmlComponent } from "file/xml-components"; import { DocumentDefaults } from "./defaults"; import { ParagraphStyle } from "./style"; +export * from "./border"; export class Styles extends XmlComponent { constructor(initialStyles?: BaseXmlComponent) { diff --git a/src/file/styles/style/index.ts b/src/file/styles/style/index.ts index 21e60b3d43..655cc28d2a 100644 --- a/src/file/styles/style/index.ts +++ b/src/file/styles/style/index.ts @@ -74,7 +74,7 @@ export class ParagraphStyle extends Style { public size(twips: number): ParagraphStyle { this.addRunProperty(new formatting.Size(twips)); - this.addRunProperty(new formatting.SizeCs(twips)); + this.addRunProperty(new formatting.SizeComplexScript(twips)); return this; } @@ -283,7 +283,7 @@ export class CharacterStyle extends Style { public size(twips: number): CharacterStyle { this.addRunProperty(new formatting.Size(twips)); - this.addRunProperty(new formatting.SizeCs(twips)); + this.addRunProperty(new formatting.SizeComplexScript(twips)); return this; } } diff --git a/src/file/table/properties.spec.ts b/src/file/table/properties.spec.ts index b6336c1411..a26e3fd352 100644 --- a/src/file/table/properties.spec.ts +++ b/src/file/table/properties.spec.ts @@ -2,6 +2,7 @@ import { expect } from "chai"; import { Formatter } from "../../export/formatter"; import { TableProperties } from "./properties"; +import { WidthType } from "./table-cell"; describe("TableProperties", () => { describe("#constructor", () => { @@ -14,7 +15,7 @@ describe("TableProperties", () => { describe("#setWidth", () => { it("adds a table width property", () => { - const tp = new TableProperties().setWidth("dxa", 1234); + const tp = new TableProperties().setWidth(WidthType.DXA, 1234); const tree = new Formatter().format(tp); expect(tree).to.deep.equal({ "w:tblPr": [{ "w:tblW": [{ _attr: { "w:type": "dxa", "w:w": 1234 } }] }], @@ -22,13 +23,24 @@ describe("TableProperties", () => { }); }); - describe("#fixedWidthLayout", () => { + describe("#setFixedWidthLayout", () => { it("sets the table to fixed width layout", () => { - const tp = new TableProperties().fixedWidthLayout(); + const tp = new TableProperties().setFixedWidthLayout(); const tree = new Formatter().format(tp); expect(tree).to.deep.equal({ "w:tblPr": [{ "w:tblLayout": [{ _attr: { "w:type": "fixed" } }] }], }); }); }); + + describe("#cellMargin", () => { + it("adds a table cell top margin", () => { + const tp = new TableProperties(); + tp.CellMargin.addTopMargin(1234, WidthType.DXA); + const tree = new Formatter().format(tp); + expect(tree).to.deep.equal({ + "w:tblPr": [{ "w:tblCellMar": [{ "w:top": [{ _attr: { "w:sz": "dxa", "w:w": 1234 } }] }] }], + }); + }); + }); }); diff --git a/src/file/table/properties.ts b/src/file/table/properties.ts index 1478399dae..7c5b0bad76 100644 --- a/src/file/table/properties.ts +++ b/src/file/table/properties.ts @@ -1,18 +1,23 @@ import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; - -export type WidthTypes = "dxa" | "pct" | "nil" | "auto"; +import { WidthType } from "./table-cell"; +import { TableCellMargin } from "./table-cell-margin"; export class TableProperties extends XmlComponent { + private readonly cellMargain: TableCellMargin; + constructor() { super("w:tblPr"); + + this.cellMargain = new TableCellMargin(); + this.root.push(this.cellMargain); } - public setWidth(type: WidthTypes, w: number | string): TableProperties { + public setWidth(type: WidthType, w: number | string): TableProperties { this.root.push(new PreferredTableWidth(type, w)); return this; } - public fixedWidthLayout(): TableProperties { + public setFixedWidthLayout(): TableProperties { this.root.push(new TableLayout("fixed")); return this; } @@ -21,10 +26,14 @@ export class TableProperties extends XmlComponent { this.root.push(new TableBorders()); return this; } + + public get CellMargin(): TableCellMargin { + return this.cellMargain; + } } interface ITableWidth { - type: WidthTypes; + type: WidthType; w: number | string; } @@ -33,7 +42,7 @@ class TableWidthAttributes extends XmlAttributeComponent { } class PreferredTableWidth extends XmlComponent { - constructor(type: WidthTypes, w: number | string) { + constructor(type: WidthType, w: number | string) { super("w:tblW"); this.root.push(new TableWidthAttributes({ type, w })); } diff --git a/src/file/table/table-cell-margin.ts b/src/file/table/table-cell-margin.ts new file mode 100644 index 0000000000..198dc89cbd --- /dev/null +++ b/src/file/table/table-cell-margin.ts @@ -0,0 +1,55 @@ +import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components"; +import { WidthType } from "./table-cell"; + +class TableCellMarginAttributes extends XmlAttributeComponent<{ type: WidthType; value: number }> { + protected xmlKeys = { value: "w:w", type: "w:sz" }; +} + +class BaseTableCellMargin extends XmlComponent { + public setProperties(value: number, type: WidthType = WidthType.DXA): void { + this.root.push( + new TableCellMarginAttributes({ + type: type, + value: value, + }), + ); + } +} + +export class TableCellMargin extends XmlComponent { + constructor() { + super("w:tblCellMar"); + } + + public prepForXml(): IXmlableObject { + return this.root.length > 0 ? super.prepForXml() : ""; + } + + public addTopMargin(value: number, type: WidthType = WidthType.DXA): void { + const top = new BaseTableCellMargin("w:top"); + + top.setProperties(value, type); + this.root.push(top); + } + + public addLeftMargin(value: number, type: WidthType = WidthType.DXA): void { + const left = new BaseTableCellMargin("w:left"); + + left.setProperties(value, type); + this.root.push(left); + } + + public addBottomMargin(value: number, type: WidthType = WidthType.DXA): void { + const bottom = new BaseTableCellMargin("w:bottom"); + + bottom.setProperties(value, type); + this.root.push(bottom); + } + + public addRightMargin(value: number, type: WidthType = WidthType.DXA): void { + const right = new BaseTableCellMargin("w:right"); + + right.setProperties(value, type); + this.root.push(right); + } +} diff --git a/src/file/table/table-cell.spec.ts b/src/file/table/table-cell.spec.ts index 01c81848bb..7a3072f186 100644 --- a/src/file/table/table-cell.spec.ts +++ b/src/file/table/table-cell.spec.ts @@ -1,7 +1,8 @@ import { expect } from "chai"; -import { TableCellBorders, BorderStyle, TableCellWidth, WidthType } from "./table-cell"; import { Formatter } from "../../export/formatter"; +import { BorderStyle } from "../styles"; +import { TableCellBorders, TableCellWidth, WidthType } from "./table-cell"; describe("TableCellBorders", () => { describe("#prepForXml", () => { diff --git a/src/file/table/table-cell.ts b/src/file/table/table-cell.ts index 954786aa12..19e3ccc428 100644 --- a/src/file/table/table-cell.ts +++ b/src/file/table/table-cell.ts @@ -1,34 +1,5 @@ import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components"; - -export enum BorderStyle { - SINGLE = "single", - DASH_DOT_STROKED = "dashDotStroked", - DASHED = "dashed", - DASH_SMALL_GAP = "dashSmallGap", - DOT_DASH = "dotDash", - DOT_DOT_DASH = "dotDotDash", - DOTTED = "dotted", - DOUBLE = "double", - DOUBLE_WAVE = "doubleWave", - INSET = "inset", - NIL = "nil", - NONE = "none", - OUTSET = "outset", - THICK = "thick", - THICK_THIN_LARGE_GAP = "thickThinLargeGap", - THICK_THIN_MEDIUM_GAP = "thickThinMediumGap", - THICK_THIN_SMALL_GAP = "thickThinSmallGap", - THIN_THICK_LARGE_GAP = "thinThickLargeGap", - THIN_THICK_MEDIUM_GAP = "thinThickMediumGap", - THIN_THICK_SMALL_GAP = "thinThickSmallGap", - THIN_THICK_THIN_LARGE_GAP = "thinThickThinLargeGap", - THIN_THICK_THIN_MEDIUM_GAP = "thinThickThinMediumGap", - THIN_THICK_THIN_SMALL_GAP = "thinThickThinSmallGap", - THREE_D_EMBOSS = "threeDEmboss", - THREE_D_ENGRAVE = "threeDEngrave", - TRIPLE = "triple", - WAVE = "wave", -} +import { BorderStyle } from "../styles"; interface ICellBorder { style: BorderStyle; diff --git a/src/file/table/table.spec.ts b/src/file/table/table.spec.ts index 84cbe99590..65871655b4 100644 --- a/src/file/table/table.spec.ts +++ b/src/file/table/table.spec.ts @@ -4,6 +4,7 @@ import { expect } from "chai"; import { Formatter } from "../../export/formatter"; import { Paragraph } from "../paragraph"; import { Table } from "./"; +import { WidthType } from "./table-cell"; const DEFAULT_TABLE_PROPERTIES = { "w:tblBorders": [ @@ -174,7 +175,7 @@ describe("Table", () => { describe("#setWidth", () => { it("sets the preferred width on the table", () => { - const table = new Table(2, 2).setWidth("pct", 1000); + const table = new Table(2, 2).setWidth(WidthType.PERCENTAGE, 1000); const tree = new Formatter().format(table); expect(tree) .to.have.property("w:tbl") @@ -186,9 +187,9 @@ describe("Table", () => { }); }); - describe("#fixedWidthLayout", () => { + describe("#setFixedWidthLayout", () => { it("sets the table to fixed width layout", () => { - const table = new Table(2, 2).fixedWidthLayout(); + const table = new Table(2, 2).setFixedWidthLayout(); const tree = new Formatter().format(table); expect(tree) .to.have.property("w:tbl") diff --git a/src/file/table/table.ts b/src/file/table/table.ts index 68f083ce6a..0406bdf549 100644 --- a/src/file/table/table.ts +++ b/src/file/table/table.ts @@ -12,7 +12,7 @@ import { import { IXmlableObject, XmlComponent } from "file/xml-components"; import { Paragraph } from "../paragraph"; import { TableGrid } from "./grid"; -import { TableProperties, WidthTypes } from "./properties"; +import { TableProperties } from "./properties"; export class Table extends XmlComponent { private readonly properties: TableProperties; @@ -67,15 +67,19 @@ export class Table extends XmlComponent { return this.getRow(row).getCell(col); } - public setWidth(type: WidthTypes, width: number | string): Table { + public setWidth(type: WidthType, width: number | string): Table { this.properties.setWidth(type, width); return this; } - public fixedWidthLayout(): Table { - this.properties.fixedWidthLayout(); + public setFixedWidthLayout(): Table { + this.properties.setFixedWidthLayout(); return this; } + + public get Properties(): TableProperties { + return this.properties; + } } export class TableRow extends XmlComponent { @@ -94,7 +98,7 @@ export class TableRow extends XmlComponent { public addGridSpan(ix: number, cellSpan: number): TableCell { const remainCell = this.cells[ix]; - remainCell.cellProperties.addGridSpan(cellSpan); + remainCell.CellProperties.addGridSpan(cellSpan); this.cells.splice(ix + 1, cellSpan - 1); this.root.splice(ix + 2, cellSpan - 1); @@ -138,20 +142,21 @@ export class TableCell extends XmlComponent { return para; } - public get cellProperties(): TableCellProperties { + public get CellProperties(): TableCellProperties { return this.properties; } } export class TableCellProperties extends XmlComponent { - private cellBorder: TableCellBorders; + private readonly cellBorder: TableCellBorders; + constructor() { super("w:tcPr"); this.cellBorder = new TableCellBorders(); this.root.push(this.cellBorder); } - public get borders(): TableCellBorders { + public get Borders(): TableCellBorders { return this.cellBorder; } @@ -167,8 +172,8 @@ export class TableCellProperties extends XmlComponent { return this; } - public setVerticalAlign(vAlignType: VerticalAlign): TableCellProperties { - this.root.push(new VAlign(vAlignType)); + public setVerticalAlign(type: VerticalAlign): TableCellProperties { + this.root.push(new VAlign(type)); return this; } diff --git a/src/file/xml-components/base.ts b/src/file/xml-components/base.ts index 0dd5333765..a5f24f3824 100644 --- a/src/file/xml-components/base.ts +++ b/src/file/xml-components/base.ts @@ -10,7 +10,7 @@ export abstract class BaseXmlComponent { public abstract prepForXml(): IXmlableObject; - public get isDeleted(): boolean { + public get IsDeleted(): boolean { return this.deleted; } } diff --git a/src/file/xml-components/imported-xml-component.spec.ts b/src/file/xml-components/imported-xml-component.spec.ts index e941d3f0ec..beed8cf09a 100644 --- a/src/file/xml-components/imported-xml-component.spec.ts +++ b/src/file/xml-components/imported-xml-component.spec.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { ImportedXmlComponent, convertToXmlComponent } from "./"; +import { convertToXmlComponent, ImportedXmlComponent } from "./"; const xmlString = ` @@ -11,10 +11,11 @@ const xmlString = ` Text 2 - + `; +// tslint:disable:object-literal-key-quotes const importedXmlElement = { "w:p": { _attr: { "w:one": "value 1", "w:two": "value 2" }, @@ -22,6 +23,7 @@ const importedXmlElement = { "w:r": [{ _attr: { active: "true" }, "w:t": "Text 1" }, { _attr: { active: "true" }, "w:t": "Text 2" }], }, }; +// tslint:enable:object-literal-key-quotes const convertedXmlElement = { deleted: false, diff --git a/src/file/xml-components/xml-component.ts b/src/file/xml-components/xml-component.ts index fa7709a043..0ed604e9ee 100644 --- a/src/file/xml-components/xml-component.ts +++ b/src/file/xml-components/xml-component.ts @@ -14,7 +14,7 @@ export abstract class XmlComponent extends BaseXmlComponent { const children = this.root .filter((c) => { if (c instanceof BaseXmlComponent) { - return !c.isDeleted; + return !c.IsDeleted; } return true; }) diff --git a/src/file/xml-components/xmlable-object.ts b/src/file/xml-components/xmlable-object.ts index 9fa0d5b600..255df14fc2 100644 --- a/src/file/xml-components/xmlable-object.ts +++ b/src/file/xml-components/xmlable-object.ts @@ -3,4 +3,7 @@ export interface IXmlableObject extends Object { } // Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432 -export const WORKAROUND3 = "workaround"; +/** + * @ignore + */ +export const WORKAROUND3 = ""; diff --git a/tsconfig.json b/tsconfig.json index 2d46f20143..3e9cc23b58 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "module": "commonjs", "declaration": true, "noUnusedLocals": true, + "noUnusedParameters": true, "baseUrl": "./src", "paths" : { "/*": [ @@ -20,7 +21,25 @@ "exclude": [ "node_modules", "tests", - "**/*.spec.ts", - "**/_*" - ] + "**/_*", + "demo" + ], + "typedocOptions": { + "mode": "file", + "out": "docs/api", + "exclude": "test", + "theme": "default", + "ignoreCompilerErrors": true, + "excludePrivate": true, + "excludeProtected": true, + "excludeNotExported": true, + "excludeExternals": false, + "target": "ES6", + "moduleResolution": "node", + "preserveConstEnums": true, + "stripInternal": true, + "suppressExcessPropertyErrors": true, + "suppressImplicitAnyIndexErrors": true, + "module": "commonjs" + } } diff --git a/tslint.json b/tslint.json index 5bbe52b1bc..497035eccc 100644 --- a/tslint.json +++ b/tslint.json @@ -32,11 +32,10 @@ "max-classes-per-file": [ false ], - "no-unused-variable": [ - true - ], "no-implicit-dependencies": false, "no-submodule-imports": false, - "no-null-keyword": true + "no-null-keyword": true, + "return-undefined": true, + "prefer-readonly": true } } diff --git a/webpack.config.js b/webpack.config.js index a8000404c1..01ecff1540 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,17 +1,17 @@ -const path = require('path'); +const path = require("path"); module.exports = { - entry: './src/index.ts', + entry: "./src/index.ts", output: { - path: path.resolve('build'), - filename: 'index.js', - libraryTarget: 'umd' + path: path.resolve("build"), + filename: "index.js", + libraryTarget: "umd", }, resolve: { - extensions: ['.tsx', '.ts', '.js'], - modules: [path.resolve('./src'), "node_modules"] + extensions: [".tsx", ".ts", ".js"], + modules: [path.resolve("./src"), "node_modules"], }, module: { @@ -19,13 +19,10 @@ module.exports = { { test: /\.ts$/, loaders: ["awesome-typescript-loader"], - } + }, ], }, - target: 'node', - - node: { - __dirname: true - } + // Because docx is now targetting web + // target: 'node', };