diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000000..469d080845 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v8 \ No newline at end of file diff --git a/README.md b/README.md index 7e25666ee0..020cb78f96 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- clippy the assistant + clippy the assistant

@@ -17,11 +17,20 @@ [![PRs Welcome][pr-image]][pr-url]

- drawing + drawing

# Demo +## Browser + +Here are examples of `docx` being used with basic `HTML/JS` in a browser environment. + +* https://codepen.io/anon/pen/dqoVgQ +* https://jsfiddle.net/3xhezb5w/2 + +## Node + Press `endpoint` on the `RunKit` website: ![RunKit Instructions](https://user-images.githubusercontent.com/2917613/38582539-f84311b6-3d07-11e8-90db-5885ae02c3c4.png) @@ -33,9 +42,11 @@ Press `endpoint` on the `RunKit` website: * https://runkit.com/dolanmiu/docx-demo5 - Images * https://runkit.com/dolanmiu/docx-demo6 - Margins * https://runkit.com/dolanmiu/docx-demo7 - Landscape -* https://runkit.com/dolanmiu/docx-demo8/1.0.1 - Header and Footer +* https://runkit.com/dolanmiu/docx-demo8 - Header and Footer * https://runkit.com/dolanmiu/docx-demo10 - **My CV generated with docx** +More [here](https://docx.js.org/#/examples) and [here](https://github.com/dolanmiu/docx/tree/master/demo) + # How to use & Documentation 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! diff --git a/demo/demo27.ts b/demo/demo27.ts new file mode 100644 index 0000000000..e04fc07e02 --- /dev/null +++ b/demo/demo27.ts @@ -0,0 +1,34 @@ +import * as fs from "fs"; +import { Document, Packer } from "../build"; + +const doc = new Document(); +const myStyles = doc.Styles; + +// The first argument is an ID you use to apply the style to paragraphs +// The second argument is a human-friendly name to show in the UI +myStyles.createParagraphStyle("myWonkyStyle", "My Wonky Style") + .basedOn("Normal") + .next("Normal") + .color("990000") + .italics() + .indent({left: 720}) // 720 TWIP === 720 / 20 pt === .5 in + .spacing({line: 276}); // 276 / 240 = 1.15x line spacing + +myStyles.createParagraphStyle("Heading2", "Heading 2") + .basedOn("Normal") + .next("Normal") + .quickFormat() + .size(26) // 26 half-points === 13pt font + .bold() + .underline("double", "FF0000") + .spacing({before: 240, after: 120}); // TWIP for both + +doc.createParagraph("Hello").style("myWonkyStyle"); +doc.createParagraph("World").heading2(); // Uses the Heading2 style + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); + console.log("Document created successfully at project root!"); +}); diff --git a/docs/README.md b/docs/README.md index f3134b8fa3..4fc5757477 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,5 @@

- clippy the assistant + clippy the assistant

@@ -54,6 +54,10 @@ exporter.pack("My First Document"); [@h4buli](https://github.com/h4buli) +

+ clippy the assistant +

+ --- Made with 💖 diff --git a/logo/logo-small.gif b/logo/logo-small.gif new file mode 100644 index 0000000000..4b4a567c02 Binary files /dev/null and b/logo/logo-small.gif differ diff --git a/logo/logo-small.png b/logo/logo-small.png new file mode 100644 index 0000000000..966fd4b1e7 Binary files /dev/null and b/logo/logo-small.png differ diff --git a/logo/logo-small.psd b/logo/logo-small.psd new file mode 100644 index 0000000000..4c6b32123c Binary files /dev/null and b/logo/logo-small.psd differ diff --git a/logo/logo.psd b/logo/logo.psd new file mode 100644 index 0000000000..9763168e2c Binary files /dev/null and b/logo/logo.psd differ diff --git a/package.json b/package.json index 8508754bec..3bc82ca558 100644 --- a/package.json +++ b/package.json @@ -82,5 +82,8 @@ "typedoc": "^0.11.1", "typescript": "2.9.2", "webpack": "^3.10.0" + }, + "engines": { + "node": ">=8" } } diff --git a/src/file/base-file.ts b/src/file/base-file.ts index 48ed217b9c..1f1b65c85e 100644 --- a/src/file/base-file.ts +++ b/src/file/base-file.ts @@ -200,6 +200,10 @@ export abstract class BaseFile { return this.styles; } + public set Styles(styles: Styles) { + this.styles = styles; + } + public get CoreProperties(): CoreProperties { return this.coreProperties; } diff --git a/src/file/document/index.ts b/src/file/document/index.ts index 6b128299f6..3430666623 100644 --- a/src/file/document/index.ts +++ b/src/file/document/index.ts @@ -1,2 +1,3 @@ export * from "./document"; +export * from "./document-attributes"; export * from "./body"; diff --git a/src/file/file.ts.orig b/src/file/file.ts.orig new file mode 100644 index 0000000000..d7aa8213c5 --- /dev/null +++ b/src/file/file.ts.orig @@ -0,0 +1,304 @@ +import { BaseFile } from "./base-file"; +import { IPropertiesOptions } from "./core-properties"; +import { Document } from "./document"; +import { SectionPropertiesOptions } from "./document/body/section-properties"; + +export class File extends BaseFile { + constructor( + options: IPropertiesOptions = { + creator: "Un-named", + revision: "1", + lastModifiedBy: "Un-named", + }, + sectionPropertiesOptions: SectionPropertiesOptions = {}, + ) { + super(options); + + this.addDefaultRelationships(); + this.createHeader(); + this.createFooter(); + + sectionPropertiesOptions = { + ...sectionPropertiesOptions, + headers: this.headers.map((header) => ({ + headerId: header.header.Header.ReferenceId, + headerType: header.type, + })), + footers: this.footers.map((footer) => ({ + footerId: footer.footer.Footer.ReferenceId, + footerType: footer.type, + })), + }; + +<<<<<<< HEAD + this.document = new Document(sectionPropertiesOptions); + } +======= +export class File { + private readonly document: Document; + private styles: Styles; + private readonly coreProperties: CoreProperties; + private readonly numbering: Numbering; + private readonly media: Media; + private readonly docRelationships: Relationships; + private readonly fileRelationships: Relationships; + private readonly headerWrapper: HeaderWrapper[] = []; + private readonly footerWrapper: FooterWrapper[] = []; + private readonly footNotes: FootNotes; + + private readonly contentTypes: ContentTypes; + private readonly appProperties: AppProperties; + + private currentRelationshipId: number = 1; + + constructor(options?: IPropertiesOptions, sectionPropertiesOptions?: SectionPropertiesOptions) { + if (!options) { + options = { + creator: "Un-named", + revision: "1", + lastModifiedBy: "Un-named", + }; + } + + if (options.externalStyles) { + const stylesFactory = new ExternalStylesFactory(); + this.styles = stylesFactory.newInstance(options.externalStyles); + } else { + const stylesFactory = new DefaultStylesFactory(); + this.styles = stylesFactory.newInstance(); + } + + this.coreProperties = new CoreProperties(options); + this.numbering = new Numbering(); + this.docRelationships = new Relationships(); + this.docRelationships.createRelationship( + this.currentRelationshipId++, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles", + "styles.xml", + ); + this.docRelationships.createRelationship( + this.currentRelationshipId++, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering", + "numbering.xml", + ); + this.contentTypes = new ContentTypes(); + + this.docRelationships.createRelationship( + this.currentRelationshipId++, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes", + "footnotes.xml", + ); + this.media = new Media(); + + const header = this.createHeader(); + const footer = this.createFooter(); + + this.fileRelationships = new Relationships(); + this.fileRelationships.createRelationship( + 1, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", + "word/document.xml", + ); + this.fileRelationships.createRelationship( + 2, + "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties", + "docProps/core.xml", + ); + this.fileRelationships.createRelationship( + 3, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties", + "docProps/app.xml", + ); + this.appProperties = new AppProperties(); + + this.footNotes = new FootNotes(); + if (!sectionPropertiesOptions) { + sectionPropertiesOptions = { + footerType: FooterReferenceType.DEFAULT, + headerType: HeaderReferenceType.DEFAULT, + headerId: header.Header.ReferenceId, + footerId: footer.Footer.ReferenceId, + }; + } else { + sectionPropertiesOptions.headerId = header.Header.ReferenceId; + sectionPropertiesOptions.footerId = footer.Footer.ReferenceId; + } + this.document = new Document(sectionPropertiesOptions); + } + + public addParagraph(paragraph: Paragraph): void { + this.document.addParagraph(paragraph); + } + + public createParagraph(text?: string): Paragraph { + return this.document.createParagraph(text); + } + + public addTable(table: Table): void { + return this.document.addTable(table); + } + + public createTable(rows: number, cols: number): Table { + return this.document.createTable(rows, cols); + } + + public addImage(image: Image): File { + this.document.addParagraph(image.Paragraph); + return this; + } + + 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; + } + + public createHyperlink(link: string, text?: string): Hyperlink { + text = text === undefined ? link : text; + const hyperlink = new Hyperlink(text, this.docRelationships.RelationshipCount); + this.docRelationships.createRelationship( + hyperlink.linkId, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", + link, + "External", + ); + return hyperlink; + } + + public createInternalHyperLink(anchor: string, text?: string): Hyperlink { + text = text === undefined ? anchor : text; + const hyperlink = new Hyperlink(text, this.docRelationships.RelationshipCount, anchor); + // NOTE: unlike File#createHyperlink(), since the link is to an internal bookmark + // we don't need to create a new relationship. + return hyperlink; + } + + public createBookmark(name: string, text?: string): Bookmark { + text = text === undefined ? name : text; + const bookmark = new Bookmark(name, text, this.docRelationships.RelationshipCount); + return bookmark; + } + + public addSection(sectionPropertiesOptions: SectionPropertiesOptions): void { + this.document.Body.addSection(sectionPropertiesOptions); + } + + public createFootnote(paragraph: Paragraph): void { + this.footNotes.createFootNote(paragraph); + } + + public createHeader(): HeaderWrapper { + const header = new HeaderWrapper(this.media, this.currentRelationshipId++); + this.headerWrapper.push(header); + this.docRelationships.createRelationship( + header.Header.ReferenceId, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header", + `header${this.headerWrapper.length}.xml`, + ); + this.contentTypes.addHeader(this.headerWrapper.length); + return header; + } + + public createFooter(): FooterWrapper { + const footer = new FooterWrapper(this.media, this.currentRelationshipId++); + this.footerWrapper.push(footer); + this.docRelationships.createRelationship( + footer.Footer.ReferenceId, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer", + `footer${this.footerWrapper.length}.xml`, + ); + this.contentTypes.addFooter(this.footerWrapper.length); + return footer; + } + + public createFirstPageHeader(): HeaderWrapper { + const headerWrapper = this.createHeader(); + + this.document.Body.DefaultSection.addChildElement( + new HeaderReference({ + headerType: HeaderReferenceType.FIRST, + headerId: headerWrapper.Header.ReferenceId, + }), + ); + + return headerWrapper; + } + + public get Document(): Document { + return this.document; + } + + public get Styles(): Styles { + return this.styles; + } + + public set Styles(styles: Styles) { + this.styles = styles; + } + + public get CoreProperties(): CoreProperties { + return this.coreProperties; + } + + public get Numbering(): Numbering { + return this.numbering; + } + + public get Media(): Media { + return this.media; + } + + public get DocumentRelationships(): Relationships { + return this.docRelationships; + } + + public get FileRelationships(): Relationships { + return this.fileRelationships; + } + + public get Header(): HeaderWrapper { + return this.headerWrapper[0]; + } + + public get Headers(): HeaderWrapper[] { + return this.headerWrapper; + } + + public HeaderByRefNumber(refId: number): HeaderWrapper { + const entry = this.headerWrapper.find((h) => h.Header.ReferenceId === refId); + if (entry) { + return entry; + } + throw new Error(`There is no header with given reference id ${refId}`); + } + + public get Footer(): FooterWrapper { + return this.footerWrapper[0]; + } + + public get Footers(): FooterWrapper[] { + return this.footerWrapper; + } + + public FooterByRefNumber(refId: number): FooterWrapper { + const entry = this.footerWrapper.find((h) => h.Footer.ReferenceId === refId); + if (entry) { + return entry; + } + throw new Error(`There is no footer with given reference id ${refId}`); + } + + public get ContentTypes(): ContentTypes { + return this.contentTypes; + } + + public get AppProperties(): AppProperties { + return this.appProperties; + } + + public get FootNotes(): FootNotes { + return this.footNotes; + } +>>>>>>> 877aa325bb5e60d05846d4013e208a045004d4a1 +} diff --git a/src/file/table/properties.ts b/src/file/table/properties.ts index 7c5b0bad76..91ef393df3 100644 --- a/src/file/table/properties.ts +++ b/src/file/table/properties.ts @@ -3,13 +3,13 @@ import { WidthType } from "./table-cell"; import { TableCellMargin } from "./table-cell-margin"; export class TableProperties extends XmlComponent { - private readonly cellMargain: TableCellMargin; + private readonly cellMargin: TableCellMargin; constructor() { super("w:tblPr"); - this.cellMargain = new TableCellMargin(); - this.root.push(this.cellMargain); + this.cellMargin = new TableCellMargin(); + this.root.push(this.cellMargin); } public setWidth(type: WidthType, w: number | string): TableProperties { @@ -28,7 +28,7 @@ export class TableProperties extends XmlComponent { } public get CellMargin(): TableCellMargin { - return this.cellMargain; + return this.cellMargin; } }