From b5b96506aed1eaecbb96323e290c01c38a5fa7ed Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sun, 3 Jun 2018 02:11:21 +0100 Subject: [PATCH 01/15] Add footnotes scaffolding --- src/file/file.ts | 7 +++++++ src/file/footnotes/footnotes.ts | 9 +++++++++ src/file/footnotes/index.ts | 1 + 3 files changed, 17 insertions(+) create mode 100644 src/file/footnotes/footnotes.ts create mode 100644 src/file/footnotes/index.ts diff --git a/src/file/file.ts b/src/file/file.ts index 7c5d81e44b..b207065e8f 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -5,6 +5,7 @@ import { CoreProperties, IPropertiesOptions } from "./core-properties"; import { Document } from "./document"; import { SectionPropertiesOptions } from "./document/body/section-properties/section-properties"; import { FooterWrapper } from "./footer-wrapper"; +import { FootNotes } from "./footnotes"; import { FirstPageHeaderWrapper, HeaderWrapper } from "./header-wrapper"; import { Media } from "./media"; import { Numbering } from "./numbering"; @@ -24,6 +25,7 @@ export class File { private readonly docRelationships: Relationships; private readonly fileRelationships: Relationships; private readonly headerWrapper: HeaderWrapper; + private readonly footNotes: FootNotes; private readonly firstPageHeaderWrapper: FirstPageHeaderWrapper; @@ -104,6 +106,7 @@ export class File { "docProps/app.xml", ); this.appProperties = new AppProperties(); + this.footNotes = new FootNotes(); } public addParagraph(paragraph: Paragraph): void { @@ -201,4 +204,8 @@ export class File { public get AppProperties(): AppProperties { return this.appProperties; } + + public get FootNotes(): FootNotes { + return this.footNotes; + } } diff --git a/src/file/footnotes/footnotes.ts b/src/file/footnotes/footnotes.ts new file mode 100644 index 0000000000..c33cf714c7 --- /dev/null +++ b/src/file/footnotes/footnotes.ts @@ -0,0 +1,9 @@ +export class FootNotes { + public createFootNote(): void { + // TODO + } + + public getFootNote(): void { + // TODO + } +} diff --git a/src/file/footnotes/index.ts b/src/file/footnotes/index.ts new file mode 100644 index 0000000000..91f3a9948b --- /dev/null +++ b/src/file/footnotes/index.ts @@ -0,0 +1 @@ +export * from "./footnotes"; From 8cb8d3514acb86ffc139a6ecbbcf615aa7d7bbe0 Mon Sep 17 00:00:00 2001 From: Dolan Date: Mon, 11 Jun 2018 00:45:21 +0100 Subject: [PATCH 02/15] Add footnotes class --- .../footnotes/footnote/footnote-attributes.ts | 13 +++++ src/file/footnotes/footnote/footnote.ts | 19 +++++++ src/file/footnotes/footnotes-attributes.ts | 43 +++++++++++++++ src/file/footnotes/footnotes.ts | 52 ++++++++++++++++++- src/file/paragraph/formatting/spacing.ts | 2 + 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/file/footnotes/footnote/footnote-attributes.ts create mode 100644 src/file/footnotes/footnote/footnote.ts create mode 100644 src/file/footnotes/footnotes-attributes.ts diff --git a/src/file/footnotes/footnote/footnote-attributes.ts b/src/file/footnotes/footnote/footnote-attributes.ts new file mode 100644 index 0000000000..b6d96f89d8 --- /dev/null +++ b/src/file/footnotes/footnote/footnote-attributes.ts @@ -0,0 +1,13 @@ +import { XmlAttributeComponent } from "file/xml-components"; + +export interface IFootnoteAttributesProperties { + type?: string; + id: number; +} + +export class FootnoteAttributes extends XmlAttributeComponent { + protected xmlKeys = { + type: "w:type", + id: "w:id", + }; +} diff --git a/src/file/footnotes/footnote/footnote.ts b/src/file/footnotes/footnote/footnote.ts new file mode 100644 index 0000000000..a827737de0 --- /dev/null +++ b/src/file/footnotes/footnote/footnote.ts @@ -0,0 +1,19 @@ +import { XmlComponent } from "file/xml-components"; +import { Paragraph } from "../../paragraph"; +import { FootnoteAttributes } from "./footnote-attributes"; + +export class FootNote extends XmlComponent { + constructor(id: number, type?: string) { + super("w:footnote"); + this.root.push( + new FootnoteAttributes({ + type: type, + id: id, + }), + ); + } + + public addParagraph(paragraph: Paragraph): void { + this.root.push(paragraph); + } +} diff --git a/src/file/footnotes/footnotes-attributes.ts b/src/file/footnotes/footnotes-attributes.ts new file mode 100644 index 0000000000..f09216c9ac --- /dev/null +++ b/src/file/footnotes/footnotes-attributes.ts @@ -0,0 +1,43 @@ +import { XmlAttributeComponent } from "file/xml-components"; + +export interface IFootnotesAttributesProperties { + wpc?: string; + mc?: string; + o?: string; + r?: string; + m?: string; + v?: string; + wp14?: string; + wp?: string; + w10?: string; + w?: string; + w14?: string; + w15?: string; + wpg?: string; + wpi?: string; + wne?: string; + wps?: string; + Ignorable?: string; +} + +export class FootnotesAttributes extends XmlAttributeComponent { + protected xmlKeys = { + wpc: "xmlns:wpc", + mc: "xmlns:mc", + o: "xmlns:o", + r: "xmlns:r", + m: "xmlns:m", + v: "xmlns:v", + wp14: "xmlns:wp14", + wp: "xmlns:wp", + w10: "xmlns:w10", + w: "xmlns:w", + w14: "xmlns:w14", + w15: "xmlns:w15", + wpg: "xmlns:wpg", + wpi: "xmlns:wpi", + wne: "xmlns:wne", + wps: "xmlns:wps", + Ignorable: "mc:Ignorable", + }; +} diff --git a/src/file/footnotes/footnotes.ts b/src/file/footnotes/footnotes.ts index c33cf714c7..d46526d2ab 100644 --- a/src/file/footnotes/footnotes.ts +++ b/src/file/footnotes/footnotes.ts @@ -1,4 +1,54 @@ -export class FootNotes { +import { XmlComponent } from "file/xml-components"; +import { FootNote } from "./footnote/footnote"; +import { FootnotesAttributes } from "./footnotes-attributes"; +import { Paragraph } from "../paragraph"; + +export class FootNotes extends XmlComponent { + constructor() { + super("w:footnotes"); + this.root.push( + new FootnotesAttributes({ + wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas", + mc: "http://schemas.openxmlformats.org/markup-compatibility/2006", + o: "urn:schemas-microsoft-com:office:office", + r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships", + m: "http://schemas.openxmlformats.org/officeDocument/2006/math", + v: "urn:schemas-microsoft-com:vml", + wp14: "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", + wp: "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", + w10: "urn:schemas-microsoft-com:office:word", + w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main", + w14: "http://schemas.microsoft.com/office/word/2010/wordml", + w15: "http://schemas.microsoft.com/office/word/2012/wordml", + wpg: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup", + wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk", + wne: "http://schemas.microsoft.com/office/word/2006/wordml", + wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape", + Ignorable: "w14 w15 wp14", + }), + ); + + const begin = new FootNote(-1); + begin.addParagraph( + new Paragraph().spacing({ + after: 0, + line: 240, + lineRule: "auto", + }), + ); + this.root.push(begin); + + const spacing = new FootNote(0); + spacing.addParagraph( + new Paragraph().spacing({ + after: 0, + line: 240, + lineRule: "auto", + }), + ); + this.root.push(spacing); + } + public createFootNote(): void { // TODO } diff --git a/src/file/paragraph/formatting/spacing.ts b/src/file/paragraph/formatting/spacing.ts index fceab8af8c..292864bf53 100644 --- a/src/file/paragraph/formatting/spacing.ts +++ b/src/file/paragraph/formatting/spacing.ts @@ -5,6 +5,7 @@ export interface ISpacingProperties { after?: number; before?: number; line?: number; + lineRule?: string; } class SpacingAttributes extends XmlAttributeComponent { @@ -12,6 +13,7 @@ class SpacingAttributes extends XmlAttributeComponent { after: "w:after", before: "w:before", line: "w:line", + lineRule: "w:lineRule", }; } From 6085f69c2229a59593baaea0d5122f333dec7642 Mon Sep 17 00:00:00 2001 From: Dolan Date: Mon, 11 Jun 2018 00:46:51 +0100 Subject: [PATCH 03/15] Sorting --- src/file/footnotes/footnotes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/file/footnotes/footnotes.ts b/src/file/footnotes/footnotes.ts index d46526d2ab..d53ef4f02d 100644 --- a/src/file/footnotes/footnotes.ts +++ b/src/file/footnotes/footnotes.ts @@ -1,7 +1,7 @@ import { XmlComponent } from "file/xml-components"; +import { Paragraph } from "../paragraph"; import { FootNote } from "./footnote/footnote"; import { FootnotesAttributes } from "./footnotes-attributes"; -import { Paragraph } from "../paragraph"; export class FootNotes extends XmlComponent { constructor() { From c19a2e71b59093c6c69fb525ab2b32371fd4cc0b Mon Sep 17 00:00:00 2001 From: Dolan Date: Mon, 11 Jun 2018 00:48:50 +0100 Subject: [PATCH 04/15] Add footnotes as part of export --- src/export/packer/compiler.ts | 5 +++++ src/file/file.ts | 6 ++++++ src/file/relationships/relationship/relationship.ts | 3 ++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/export/packer/compiler.ts b/src/export/packer/compiler.ts index 6ee1665074..c912054510 100644 --- a/src/export/packer/compiler.ts +++ b/src/export/packer/compiler.ts @@ -40,6 +40,7 @@ export class Compiler { const xmlFooterRelationships = xml(this.formatter.format(this.file.Footer.Relationships)); const xmlContentTypes = xml(this.formatter.format(this.file.ContentTypes)); const xmlAppProperties = xml(this.formatter.format(this.file.AppProperties)); + const xmlFootnotes = xml(this.formatter.format(this.file.FootNotes)); this.archive.append(xmlDocument, { name: "word/document.xml", @@ -73,6 +74,10 @@ export class Compiler { name: "word/footer1.xml", }); + this.archive.append(xmlFootnotes, { + name: "word/footnotes.xml", + }); + this.archive.append(xmlRelationships, { name: "word/_rels/document.xml.rels", }); diff --git a/src/file/file.ts b/src/file/file.ts index b207065e8f..627b5e79be 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -82,6 +82,12 @@ export class File { "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer", "footer1.xml", ); + + this.docRelationships.createRelationship( + 5, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes", + "footnotes.xml", + ); this.media = new Media(); this.headerWrapper = new HeaderWrapper(this.media); diff --git a/src/file/relationships/relationship/relationship.ts b/src/file/relationships/relationship/relationship.ts index 3986451257..92553a38f3 100644 --- a/src/file/relationships/relationship/relationship.ts +++ b/src/file/relationships/relationship/relationship.ts @@ -14,7 +14,8 @@ export type RelationshipType = | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" | "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" - | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"; + | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" + | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes"; export type TargetModeType = "External"; From 369ec9c30b9e19304edb613ff3ed6df5d5bd06bb Mon Sep 17 00:00:00 2001 From: Dolan Date: Mon, 11 Jun 2018 00:50:48 +0100 Subject: [PATCH 05/15] Set correct ID --- src/file/file.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/file/file.ts b/src/file/file.ts index 627b5e79be..5ee6c175b3 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -84,7 +84,7 @@ export class File { ); this.docRelationships.createRelationship( - 5, + 6, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes", "footnotes.xml", ); From 99290d646e7730335bd283f97d45ce1a49d03af0 Mon Sep 17 00:00:00 2001 From: Dolan Date: Mon, 25 Jun 2018 19:49:46 +0100 Subject: [PATCH 06/15] Add footnote support to API --- demo/demo16.js | 14 ++++++++++++++ src/file/content-types/content-types.ts | 1 + src/file/file.ts | 4 ++++ src/file/footnotes/footnotes.ts | 20 ++++++++++---------- src/file/paragraph/paragraph.ts | 8 +++++++- src/file/paragraph/run/run.ts | 2 +- 6 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 demo/demo16.js diff --git a/demo/demo16.js b/demo/demo16.js new file mode 100644 index 0000000000..ea6308eb46 --- /dev/null +++ b/demo/demo16.js @@ -0,0 +1,14 @@ +const docx = require('../build'); + +var doc = new docx.Document(); + +var paragraph = new docx.Paragraph("Hello World").referenceFootnote(1); + +doc.addParagraph(paragraph); + +doc.createFootnote(new docx.Paragraph("Test")); + +var exporter = new docx.LocalPacker(doc); +exporter.pack('My Document'); + +console.log('Document created successfully at project root!'); diff --git a/src/file/content-types/content-types.ts b/src/file/content-types/content-types.ts index 9bb3837cb2..cbb078d058 100644 --- a/src/file/content-types/content-types.ts +++ b/src/file/content-types/content-types.ts @@ -32,5 +32,6 @@ export class ContentTypes extends XmlComponent { this.root.push(new Override("application/vnd.openxmlformats-package.core-properties+xml", "/docProps/core.xml")); this.root.push(new Override("application/vnd.openxmlformats-officedocument.extended-properties+xml", "/docProps/app.xml")); this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", "/word/numbering.xml")); + this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", "/word/footnotes.xml")); } } diff --git a/src/file/file.ts b/src/file/file.ts index 5ee6c175b3..e3250d93a6 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -163,6 +163,10 @@ export class File { return hyperlink; } + public createFootnote(paragraph: Paragraph): void { + this.footNotes.createFootNote(paragraph); + } + public get Document(): Document { return this.document; } diff --git a/src/file/footnotes/footnotes.ts b/src/file/footnotes/footnotes.ts index d53ef4f02d..67f8e36022 100644 --- a/src/file/footnotes/footnotes.ts +++ b/src/file/footnotes/footnotes.ts @@ -1,6 +1,8 @@ import { XmlComponent } from "file/xml-components"; import { Paragraph } from "../paragraph"; import { FootNote } from "./footnote/footnote"; +import { ContinuationSeperatorRun } from "./footnote/run/continuation-seperator-run"; +import { SeperatorRun } from "./footnote/run/seperator-run"; import { FootnotesAttributes } from "./footnotes-attributes"; export class FootNotes extends XmlComponent { @@ -28,32 +30,30 @@ export class FootNotes extends XmlComponent { }), ); - const begin = new FootNote(-1); + const begin = new FootNote(-1, "separator"); begin.addParagraph( new Paragraph().spacing({ after: 0, line: 240, lineRule: "auto", - }), + }).addRun(new SeperatorRun()), ); this.root.push(begin); - const spacing = new FootNote(0); + const spacing = new FootNote(0, "continuationSeparator"); spacing.addParagraph( new Paragraph().spacing({ after: 0, line: 240, lineRule: "auto", - }), + }).addRun(new ContinuationSeperatorRun()), ); this.root.push(spacing); } - public createFootNote(): void { - // TODO - } - - public getFootNote(): void { - // TODO + public createFootNote(paragraph: Paragraph): void { + const footnote = new FootNote(1); + footnote.addParagraph(paragraph); + this.root.push(footnote); } } diff --git a/src/file/paragraph/paragraph.ts b/src/file/paragraph/paragraph.ts index 970c4bfae9..9a6e68065a 100644 --- a/src/file/paragraph/paragraph.ts +++ b/src/file/paragraph/paragraph.ts @@ -1,8 +1,8 @@ // http://officeopenxml.com/WPparagraph.php +import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run"; import { IMediaData } from "file/media"; import { Num } from "file/numbering/num"; import { XmlComponent } from "file/xml-components"; -import { PictureRun, Run, TextRun } from "./run"; import { Alignment } from "./formatting/alignment"; import { ThematicBreak } from "./formatting/border"; @@ -15,6 +15,7 @@ import { CenterTabStop, LeftTabStop, MaxRightTabStop, RightTabStop } from "./for import { NumberProperties } from "./formatting/unordered-list"; import { Hyperlink } from "./links"; import { ParagraphProperties } from "./properties"; +import { PictureRun, Run, TextRun } from "./run"; export class Paragraph extends XmlComponent { private properties: ParagraphProperties; @@ -181,4 +182,9 @@ export class Paragraph extends XmlComponent { this.properties.push(new KeepLines()); return this; } + + public referenceFootnote(id: number): Paragraph { + this.root.push(new FootnoteReferenceRun(id)); + return this; + } } diff --git a/src/file/paragraph/run/run.ts b/src/file/paragraph/run/run.ts index b913e49df4..c258c52702 100644 --- a/src/file/paragraph/run/run.ts +++ b/src/file/paragraph/run/run.ts @@ -13,7 +13,7 @@ import { Underline } from "./underline"; import { XmlComponent } from "file/xml-components"; export class Run extends XmlComponent { - private properties: RunProperties; + protected properties: RunProperties; constructor() { super("w:r"); From 044442b0d73619d592d2a6472d16597c23b2cf0f Mon Sep 17 00:00:00 2001 From: Dolan Date: Mon, 25 Jun 2018 19:50:19 +0100 Subject: [PATCH 07/15] Add footnote classes --- .../run/continuation-seperator-run.ts | 10 ++++++ .../footnote/run/continuation-seperator.ts | 7 ++++ .../footnotes/footnote/run/reference-run.ts | 35 +++++++++++++++++++ .../footnotes/footnote/run/seperator-run.ts | 10 ++++++ src/file/footnotes/footnote/run/seperator.ts | 7 ++++ 5 files changed, 69 insertions(+) create mode 100644 src/file/footnotes/footnote/run/continuation-seperator-run.ts create mode 100644 src/file/footnotes/footnote/run/continuation-seperator.ts create mode 100644 src/file/footnotes/footnote/run/reference-run.ts create mode 100644 src/file/footnotes/footnote/run/seperator-run.ts create mode 100644 src/file/footnotes/footnote/run/seperator.ts diff --git a/src/file/footnotes/footnote/run/continuation-seperator-run.ts b/src/file/footnotes/footnote/run/continuation-seperator-run.ts new file mode 100644 index 0000000000..4cf3ad2d21 --- /dev/null +++ b/src/file/footnotes/footnote/run/continuation-seperator-run.ts @@ -0,0 +1,10 @@ +import { Run } from "file/paragraph"; +import { ContinuationSeperator } from "./continuation-seperator"; + +export class ContinuationSeperatorRun extends Run { + constructor() { + super(); + + this.root.push(new ContinuationSeperator()); + } +} diff --git a/src/file/footnotes/footnote/run/continuation-seperator.ts b/src/file/footnotes/footnote/run/continuation-seperator.ts new file mode 100644 index 0000000000..6e9cc87c77 --- /dev/null +++ b/src/file/footnotes/footnote/run/continuation-seperator.ts @@ -0,0 +1,7 @@ +import { XmlComponent } from "file/xml-components"; + +export class ContinuationSeperator extends XmlComponent { + constructor() { + super("w:continuationSeparator"); + } +} diff --git a/src/file/footnotes/footnote/run/reference-run.ts b/src/file/footnotes/footnote/run/reference-run.ts new file mode 100644 index 0000000000..ef40d37608 --- /dev/null +++ b/src/file/footnotes/footnote/run/reference-run.ts @@ -0,0 +1,35 @@ +import { Run } from "file/paragraph/run"; +import { Style } from "file/paragraph/run/style"; +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +export interface IFootNoteReferenceRunAttributesProperties { + id: number; +} + +export class FootNoteReferenceRunAttributes extends XmlAttributeComponent { + protected xmlKeys = { + id: "w:id", + }; +} + +export class FootnoteReference extends XmlComponent { + constructor(id: number) { + super("w:footnoteReference"); + + this.root.push( + new FootNoteReferenceRunAttributes({ + id: id, + }), + ); + } +} + +export class FootnoteReferenceRun extends Run { + constructor(id: number) { + super(); + + this.properties.push(new Style("FootnoteReference")); + + this.root.push(new FootnoteReference(id)); + } +} diff --git a/src/file/footnotes/footnote/run/seperator-run.ts b/src/file/footnotes/footnote/run/seperator-run.ts new file mode 100644 index 0000000000..17cc69d8ec --- /dev/null +++ b/src/file/footnotes/footnote/run/seperator-run.ts @@ -0,0 +1,10 @@ +import { Run } from "file/paragraph"; +import { Seperator } from "./seperator"; + +export class SeperatorRun extends Run { + constructor() { + super(); + + this.root.push(new Seperator()); + } +} diff --git a/src/file/footnotes/footnote/run/seperator.ts b/src/file/footnotes/footnote/run/seperator.ts new file mode 100644 index 0000000000..e6038e33ad --- /dev/null +++ b/src/file/footnotes/footnote/run/seperator.ts @@ -0,0 +1,7 @@ +import { XmlComponent } from "file/xml-components"; + +export class Seperator extends XmlComponent { + constructor() { + super("w:separator"); + } +} From 802e461792b87522ffa0bf84438a5acf601fb979 Mon Sep 17 00:00:00 2001 From: Dolan Date: Wed, 27 Jun 2018 23:36:04 +0100 Subject: [PATCH 08/15] Footnotes demo --- demo/demo17.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 demo/demo17.js diff --git a/demo/demo17.js b/demo/demo17.js new file mode 100644 index 0000000000..ea6308eb46 --- /dev/null +++ b/demo/demo17.js @@ -0,0 +1,14 @@ +const docx = require('../build'); + +var doc = new docx.Document(); + +var paragraph = new docx.Paragraph("Hello World").referenceFootnote(1); + +doc.addParagraph(paragraph); + +doc.createFootnote(new docx.Paragraph("Test")); + +var exporter = new docx.LocalPacker(doc); +exporter.pack('My Document'); + +console.log('Document created successfully at project root!'); From 5dc82d817638ed75128da3271f0d8d18d903d5ad Mon Sep 17 00:00:00 2001 From: Dolan Date: Thu, 28 Jun 2018 03:01:25 +0100 Subject: [PATCH 09/15] Add styles to footnote --- src/file/footnotes/footnote/footnote.ts | 2 + .../footnote/run/footnote-ref-run.ts | 11 ++++ .../footnotes/footnote/run/footnote-ref.ts | 7 +++ src/file/paragraph/paragraph.ts | 5 ++ src/file/styles/factory.ts | 13 +++++ src/file/styles/style/components.ts | 12 +++- src/file/styles/style/index.ts | 55 ++++++++++++++++++- 7 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 src/file/footnotes/footnote/run/footnote-ref-run.ts create mode 100644 src/file/footnotes/footnote/run/footnote-ref.ts diff --git a/src/file/footnotes/footnote/footnote.ts b/src/file/footnotes/footnote/footnote.ts index a827737de0..3a655e7c0c 100644 --- a/src/file/footnotes/footnote/footnote.ts +++ b/src/file/footnotes/footnote/footnote.ts @@ -1,6 +1,7 @@ import { XmlComponent } from "file/xml-components"; import { Paragraph } from "../../paragraph"; import { FootnoteAttributes } from "./footnote-attributes"; +import { FootnoteRefRun } from "./run/footnote-ref-run"; export class FootNote extends XmlComponent { constructor(id: number, type?: string) { @@ -14,6 +15,7 @@ export class FootNote extends XmlComponent { } public addParagraph(paragraph: Paragraph): void { + paragraph.addRunToFront(new FootnoteRefRun()); this.root.push(paragraph); } } diff --git a/src/file/footnotes/footnote/run/footnote-ref-run.ts b/src/file/footnotes/footnote/run/footnote-ref-run.ts new file mode 100644 index 0000000000..65ef0b697a --- /dev/null +++ b/src/file/footnotes/footnote/run/footnote-ref-run.ts @@ -0,0 +1,11 @@ +import { Run } from "file/paragraph"; +import { FootnoteRef } from "./footnote-ref"; + +export class FootnoteRefRun extends Run { + constructor() { + super(); + + this.style("FootnoteReference"); + this.root.push(new FootnoteRef()); + } +} diff --git a/src/file/footnotes/footnote/run/footnote-ref.ts b/src/file/footnotes/footnote/run/footnote-ref.ts new file mode 100644 index 0000000000..4a7d11fd7f --- /dev/null +++ b/src/file/footnotes/footnote/run/footnote-ref.ts @@ -0,0 +1,7 @@ +import { XmlComponent } from "file/xml-components"; + +export class FootnoteRef extends XmlComponent { + constructor() { + super("w:footnoteRef"); + } +} diff --git a/src/file/paragraph/paragraph.ts b/src/file/paragraph/paragraph.ts index 9a6e68065a..187814762e 100644 --- a/src/file/paragraph/paragraph.ts +++ b/src/file/paragraph/paragraph.ts @@ -187,4 +187,9 @@ export class Paragraph extends XmlComponent { this.root.push(new FootnoteReferenceRun(id)); return this; } + + public addRunToFront(run: Run): Paragraph { + this.root.splice(1, 0, run); + return this; + } } diff --git a/src/file/styles/factory.ts b/src/file/styles/factory.ts index f00038fac0..800d2ca9ce 100644 --- a/src/file/styles/factory.ts +++ b/src/file/styles/factory.ts @@ -3,6 +3,9 @@ import { Color, Italics, Size } from "../paragraph/run/formatting"; import { Styles } from "./"; import { + FootnoteReferenceStyle, + FootnoteText, + FootnoteTextChar, Heading1Style, Heading2Style, Heading3Style, @@ -65,6 +68,16 @@ export class DefaultStylesFactory { const hyperLinkStyle = new HyperlinkStyle(); styles.push(hyperLinkStyle); + + const footnoteReferenceStyle = new FootnoteReferenceStyle(); + styles.push(footnoteReferenceStyle); + + const footnoteTextStyle = new FootnoteText(); + styles.push(footnoteTextStyle); + + const footnoteTextCharStyle = new FootnoteTextChar(); + styles.push(footnoteTextCharStyle); + return styles; } } diff --git a/src/file/styles/style/components.ts b/src/file/styles/style/components.ts index 2a50ef5d57..5535036ac9 100644 --- a/src/file/styles/style/components.ts +++ b/src/file/styles/style/components.ts @@ -44,7 +44,11 @@ export class UiPriority extends XmlComponent { } } -export class UnhideWhenUsed extends XmlComponent {} +export class UnhideWhenUsed extends XmlComponent { + constructor() { + super("w:unhideWhenUsed"); + } +} export class QuickFormat extends XmlComponent { constructor() { @@ -56,4 +60,8 @@ export class TableProperties extends XmlComponent {} export class RsId extends XmlComponent {} -export class SemiHidden extends XmlComponent {} +export class SemiHidden extends XmlComponent { + constructor() { + super("w:semiHidden"); + } +} diff --git a/src/file/styles/style/index.ts b/src/file/styles/style/index.ts index 85640a2f29..a3b25b7520 100644 --- a/src/file/styles/style/index.ts +++ b/src/file/styles/style/index.ts @@ -3,7 +3,7 @@ import * as paragraph from "../../paragraph"; import * as formatting from "../../paragraph/run/formatting"; import { RunProperties } from "../../paragraph/run/properties"; -import { BasedOn, Name, Next, QuickFormat, UiPriority, UnhideWhenUsed } from "./components"; +import { BasedOn, Link, Name, Next, QuickFormat, SemiHidden, UiPriority, UnhideWhenUsed } from "./components"; export interface IStyleAttributes { type?: string; @@ -258,7 +258,7 @@ export class CharacterStyle extends Style { this.runProperties = new RunProperties(); this.root.push(this.runProperties); this.root.push(new UiPriority("99")); - this.root.push(new UnhideWhenUsed("")); + this.root.push(new UnhideWhenUsed()); } public basedOn(parentId: string): CharacterStyle { @@ -279,6 +279,11 @@ export class CharacterStyle extends Style { this.addRunProperty(new formatting.Underline(underlineType, color)); return this; } + + public size(twips: number): CharacterStyle { + this.addRunProperty(new formatting.Size(twips)); + return this; + } } export class HyperlinkStyle extends CharacterStyle { @@ -289,3 +294,49 @@ export class HyperlinkStyle extends CharacterStyle { .underline("single"); } } + +export class FootnoteReferenceStyle extends Style { + private readonly runProperties: RunProperties; + + constructor() { + super({ type: "character", styleId: "FootnoteReference" }); + this.root.push(new Name("footnote reference")); + this.root.push(new BasedOn("DefaultParagraphFont")); + this.root.push(new UiPriority("99")); + this.root.push(new SemiHidden()); + this.root.push(new UnhideWhenUsed()); + + this.runProperties = new RunProperties(); + this.runProperties.addChildElement(new formatting.SuperScript()); + this.root.push(this.runProperties); + } +} + +export class FootnoteText extends ParagraphStyle { + constructor() { + super("FootnoteText"); + this.root.push(new Name("footnote text")); + this.root.push(new BasedOn("Normal")); + this.root.push(new Link("FootnoteTextChar")); + this.root.push(new UiPriority("99")); + this.root.push(new SemiHidden()); + this.root.push(new UnhideWhenUsed()); + this.spacing({ + after: 0, + line: 240, + lineRule: "auto", + }); + this.size(20); + } +} + +export class FootnoteTextChar extends CharacterStyle { + constructor() { + super("FootnoteTextChar", "Footnote Text Char"); + this.basedOn("DefaultParagraphFont"); + this.root.push(new Link("FootnoteText")); + this.root.push(new UiPriority("99")); + this.root.push(new SemiHidden()); + this.size(20); + } +} From 9f0e55b7b885d9ffde08fcffe0425df43f37d205 Mon Sep 17 00:00:00 2001 From: Dolan Date: Fri, 29 Jun 2018 19:49:19 +0100 Subject: [PATCH 10/15] Add footnotes spec file --- src/file/footnotes/footnote/footnote.spec.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/file/footnotes/footnote/footnote.spec.ts diff --git a/src/file/footnotes/footnote/footnote.spec.ts b/src/file/footnotes/footnote/footnote.spec.ts new file mode 100644 index 0000000000..e69de29bb2 From 6c85ad3188aad0a88e195b9f44cc2d750e0a51c5 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sat, 30 Jun 2018 23:39:45 +0100 Subject: [PATCH 11/15] Fix tests --- src/export/packer/compiler.spec.ts | 5 +++-- src/file/content-types/content-types.spec.ts | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/export/packer/compiler.spec.ts b/src/export/packer/compiler.spec.ts index 0bf691ebcb..78d2339b11 100644 --- a/src/export/packer/compiler.spec.ts +++ b/src/export/packer/compiler.spec.ts @@ -26,7 +26,7 @@ describe("Compiler", () => { const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name); expect(fileNames).is.an.instanceof(Array); - expect(fileNames).has.length(12); + expect(fileNames).has.length(13); expect(fileNames).to.include("word/document.xml"); expect(fileNames).to.include("word/styles.xml"); expect(fileNames).to.include("docProps/core.xml"); @@ -35,6 +35,7 @@ describe("Compiler", () => { expect(fileNames).to.include("word/header1.xml"); expect(fileNames).to.include("word/_rels/header1.xml.rels"); expect(fileNames).to.include("word/footer1.xml"); + expect(fileNames).to.include("word/footnotes.xml"); expect(fileNames).to.include("word/_rels/footer1.xml.rels"); expect(fileNames).to.include("word/_rels/document.xml.rels"); expect(fileNames).to.include("[Content_Types].xml"); @@ -56,7 +57,7 @@ describe("Compiler", () => { const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name); expect(fileNames).is.an.instanceof(Array); - expect(fileNames).has.length(20); + expect(fileNames).has.length(21); expect(fileNames).to.include("word/header1.xml"); expect(fileNames).to.include("word/_rels/header1.xml.rels"); diff --git a/src/file/content-types/content-types.spec.ts b/src/file/content-types/content-types.spec.ts index 8a755d3924..14665df4ee 100644 --- a/src/file/content-types/content-types.spec.ts +++ b/src/file/content-types/content-types.spec.ts @@ -83,7 +83,7 @@ describe("ContentTypes", () => { contentTypes.addFooter(102); const tree = new Formatter().format(contentTypes); - expect(tree["Types"][13]).to.deep.equal({ + expect(tree["Types"][14]).to.deep.equal({ Override: [ { _attr: { @@ -94,7 +94,7 @@ describe("ContentTypes", () => { ], }); - expect(tree["Types"][14]).to.deep.equal({ + expect(tree["Types"][15]).to.deep.equal({ Override: [ { _attr: { @@ -113,7 +113,7 @@ describe("ContentTypes", () => { contentTypes.addHeader(202); const tree = new Formatter().format(contentTypes); - expect(tree["Types"][13]).to.deep.equal({ + expect(tree["Types"][14]).to.deep.equal({ Override: [ { _attr: { @@ -124,7 +124,7 @@ describe("ContentTypes", () => { ], }); - expect(tree["Types"][14]).to.deep.equal({ + expect(tree["Types"][15]).to.deep.equal({ Override: [ { _attr: { From 34a92ab4488a35b028fcb9eb9d1f7fe89aa621f4 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sat, 30 Jun 2018 23:41:57 +0100 Subject: [PATCH 12/15] Use enums instead of strings --- src/file/footnotes/footnote/footnote.ts | 9 +++++++-- src/file/footnotes/footnotes.ts | 8 ++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/file/footnotes/footnote/footnote.ts b/src/file/footnotes/footnote/footnote.ts index 3a655e7c0c..6760e9bb03 100644 --- a/src/file/footnotes/footnote/footnote.ts +++ b/src/file/footnotes/footnote/footnote.ts @@ -3,8 +3,13 @@ import { Paragraph } from "../../paragraph"; import { FootnoteAttributes } from "./footnote-attributes"; import { FootnoteRefRun } from "./run/footnote-ref-run"; -export class FootNote extends XmlComponent { - constructor(id: number, type?: string) { +export enum FootnoteType { + SEPERATOR = "separator", + CONTINUATION_SEPERATOR = "continuationSeparator", +} + +export class Footnote extends XmlComponent { + constructor(id: number, type?: FootnoteType) { super("w:footnote"); this.root.push( new FootnoteAttributes({ diff --git a/src/file/footnotes/footnotes.ts b/src/file/footnotes/footnotes.ts index 67f8e36022..fea6141842 100644 --- a/src/file/footnotes/footnotes.ts +++ b/src/file/footnotes/footnotes.ts @@ -1,6 +1,6 @@ import { XmlComponent } from "file/xml-components"; import { Paragraph } from "../paragraph"; -import { FootNote } from "./footnote/footnote"; +import { Footnote, FootnoteType } from "./footnote/footnote"; import { ContinuationSeperatorRun } from "./footnote/run/continuation-seperator-run"; import { SeperatorRun } from "./footnote/run/seperator-run"; import { FootnotesAttributes } from "./footnotes-attributes"; @@ -30,7 +30,7 @@ export class FootNotes extends XmlComponent { }), ); - const begin = new FootNote(-1, "separator"); + const begin = new Footnote(-1, FootnoteType.SEPERATOR); begin.addParagraph( new Paragraph().spacing({ after: 0, @@ -40,7 +40,7 @@ export class FootNotes extends XmlComponent { ); this.root.push(begin); - const spacing = new FootNote(0, "continuationSeparator"); + const spacing = new Footnote(0, FootnoteType.CONTINUATION_SEPERATOR); spacing.addParagraph( new Paragraph().spacing({ after: 0, @@ -52,7 +52,7 @@ export class FootNotes extends XmlComponent { } public createFootNote(paragraph: Paragraph): void { - const footnote = new FootNote(1); + const footnote = new Footnote(1); footnote.addParagraph(paragraph); this.root.push(footnote); } From e5fae3af64e566cfa847d795245df5aef6a8f489 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sat, 30 Jun 2018 23:42:48 +0100 Subject: [PATCH 13/15] Add footnote test --- src/file/footnotes/footnote/footnote.spec.ts | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/file/footnotes/footnote/footnote.spec.ts b/src/file/footnotes/footnote/footnote.spec.ts index e69de29bb2..065634a898 100644 --- a/src/file/footnotes/footnote/footnote.spec.ts +++ b/src/file/footnotes/footnote/footnote.spec.ts @@ -0,0 +1,26 @@ +import { expect } from "chai"; +import { Formatter } from "../../../export/formatter"; +import { Footnote, FootnoteType } from "./footnote"; + +describe("Footnote", () => { + + describe("#constructor", () => { + it("should create a footnote with a footnote type", () => { + const footnote = new Footnote(1, FootnoteType.SEPERATOR); + const tree = new Formatter().format(footnote); + + expect(Object.keys(tree)).to.deep.equal(["w:footnote"]); + expect(tree["w:footnote"]).to.be.an.instanceof(Array); + expect(tree["w:footnote"][0]).to.deep.equal({ _attr: { "w:type": "separator", "w:id": 1 } }); + }); + + it("should create a footnote without a footnote type", () => { + const footnote = new Footnote(1); + const tree = new Formatter().format(footnote); + + expect(Object.keys(tree)).to.deep.equal(["w:footnote"]); + expect(tree["w:footnote"]).to.be.an.instanceof(Array); + expect(tree["w:footnote"][0]).to.deep.equal({ _attr: { "w:id": 1 } }); + }); + }); +}); From b8934c68f23b6e6ad56c81a7bb79ba79f30f6a9c Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sat, 30 Jun 2018 23:50:02 +0100 Subject: [PATCH 14/15] Add counter to footnotes --- src/file/footnotes/footnote/footnote.spec.ts | 1 - src/file/footnotes/footnotes.ts | 33 +++++++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/file/footnotes/footnote/footnote.spec.ts b/src/file/footnotes/footnote/footnote.spec.ts index 065634a898..caeb8cc694 100644 --- a/src/file/footnotes/footnote/footnote.spec.ts +++ b/src/file/footnotes/footnote/footnote.spec.ts @@ -3,7 +3,6 @@ import { Formatter } from "../../../export/formatter"; import { Footnote, FootnoteType } from "./footnote"; describe("Footnote", () => { - describe("#constructor", () => { it("should create a footnote with a footnote type", () => { const footnote = new Footnote(1, FootnoteType.SEPERATOR); diff --git a/src/file/footnotes/footnotes.ts b/src/file/footnotes/footnotes.ts index fea6141842..bb6ac388c5 100644 --- a/src/file/footnotes/footnotes.ts +++ b/src/file/footnotes/footnotes.ts @@ -6,8 +6,13 @@ import { SeperatorRun } from "./footnote/run/seperator-run"; import { FootnotesAttributes } from "./footnotes-attributes"; export class FootNotes extends XmlComponent { + private counter: number; + constructor() { super("w:footnotes"); + + this.counter = 1; + this.root.push( new FootnotesAttributes({ wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas", @@ -32,28 +37,34 @@ export class FootNotes extends XmlComponent { const begin = new Footnote(-1, FootnoteType.SEPERATOR); begin.addParagraph( - new Paragraph().spacing({ - after: 0, - line: 240, - lineRule: "auto", - }).addRun(new SeperatorRun()), + new Paragraph() + .spacing({ + after: 0, + line: 240, + lineRule: "auto", + }) + .addRun(new SeperatorRun()), ); this.root.push(begin); const spacing = new Footnote(0, FootnoteType.CONTINUATION_SEPERATOR); spacing.addParagraph( - new Paragraph().spacing({ - after: 0, - line: 240, - lineRule: "auto", - }).addRun(new ContinuationSeperatorRun()), + new Paragraph() + .spacing({ + after: 0, + line: 240, + lineRule: "auto", + }) + .addRun(new ContinuationSeperatorRun()), ); this.root.push(spacing); } public createFootNote(paragraph: Paragraph): void { - const footnote = new Footnote(1); + const footnote = new Footnote(this.counter); footnote.addParagraph(paragraph); this.root.push(footnote); + + this.counter++; } } From ef16dac4f4dfc59426d836c3673ca7bc3c13d9a6 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sat, 30 Jun 2018 23:50:11 +0100 Subject: [PATCH 15/15] Improve demo --- demo/demo17.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/demo/demo17.js b/demo/demo17.js index ea6308eb46..266e556b7d 100644 --- a/demo/demo17.js +++ b/demo/demo17.js @@ -3,10 +3,13 @@ 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');