diff --git a/demo/demo17.js b/demo/demo17.js new file mode 100644 index 0000000000..266e556b7d --- /dev/null +++ b/demo/demo17.js @@ -0,0 +1,17 @@ +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/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/export/packer/compiler.ts b/src/export/packer/compiler.ts index 456976d9d6..99821442fa 100644 --- a/src/export/packer/compiler.ts +++ b/src/export/packer/compiler.ts @@ -35,6 +35,7 @@ export class Compiler { 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", @@ -80,6 +81,10 @@ export class Compiler { }); } + this.archive.append(xmlFootnotes, { + name: "word/footnotes.xml", + }); + this.archive.append(xmlRelationships, { name: "word/_rels/document.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: { diff --git a/src/file/content-types/content-types.ts b/src/file/content-types/content-types.ts index 7ac3a6d649..8735d3d9e2 100644 --- a/src/file/content-types/content-types.ts +++ b/src/file/content-types/content-types.ts @@ -29,6 +29,7 @@ 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")); } public addFooter(index: number): void { diff --git a/src/file/file.ts b/src/file/file.ts index da22c96ca8..ae9cf9deac 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -6,6 +6,7 @@ import { Document } from "./document"; import { FooterReferenceType, HeaderReference, HeaderReferenceType } from "./document/body/section-properties"; import { SectionPropertiesOptions } from "./document/body/section-properties/section-properties"; import { FooterWrapper } from "./footer-wrapper"; +import { FootNotes } from "./footnotes"; import { HeaderWrapper } from "./header-wrapper"; import { Media } from "./media"; import { Numbering } from "./numbering"; @@ -26,6 +27,7 @@ export class File { private readonly fileRelationships: Relationships; private readonly headerWrapper: HeaderWrapper[] = []; private readonly footerWrapper: FooterWrapper[] = []; + private readonly footNotes: FootNotes; private readonly contentTypes: ContentTypes; private readonly appProperties: AppProperties; @@ -63,6 +65,12 @@ export class File { "numbering.xml", ); this.contentTypes = new ContentTypes(); + + this.docRelationships.createRelationship( + this.nextId++, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes", + "footnotes.xml", + ); this.media = new Media(); const header = this.createHeader(); @@ -86,6 +94,7 @@ export class File { ); this.appProperties = new AppProperties(); + this.footNotes = new FootNotes(); if (!sectionPropertiesOptions) { sectionPropertiesOptions = { footerType: FooterReferenceType.DEFAULT, @@ -152,6 +161,10 @@ export class File { this.document.Body.addSection(sectionPropertiesOptions); } + public createFootnote(paragraph: Paragraph): void { + this.footNotes.createFootNote(paragraph); + } + /** * Creates new header. */ @@ -262,4 +275,8 @@ export class File { public get AppProperties(): AppProperties { return this.appProperties; } + + public get FootNotes(): FootNotes { + return this.footNotes; + } } 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.spec.ts b/src/file/footnotes/footnote/footnote.spec.ts new file mode 100644 index 0000000000..caeb8cc694 --- /dev/null +++ b/src/file/footnotes/footnote/footnote.spec.ts @@ -0,0 +1,25 @@ +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 } }); + }); + }); +}); diff --git a/src/file/footnotes/footnote/footnote.ts b/src/file/footnotes/footnote/footnote.ts new file mode 100644 index 0000000000..6760e9bb03 --- /dev/null +++ b/src/file/footnotes/footnote/footnote.ts @@ -0,0 +1,26 @@ +import { XmlComponent } from "file/xml-components"; +import { Paragraph } from "../../paragraph"; +import { FootnoteAttributes } from "./footnote-attributes"; +import { FootnoteRefRun } from "./run/footnote-ref-run"; + +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({ + type: type, + id: id, + }), + ); + } + + public addParagraph(paragraph: Paragraph): void { + paragraph.addRunToFront(new FootnoteRefRun()); + this.root.push(paragraph); + } +} 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/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/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"); + } +} 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 new file mode 100644 index 0000000000..bb6ac388c5 --- /dev/null +++ b/src/file/footnotes/footnotes.ts @@ -0,0 +1,70 @@ +import { XmlComponent } from "file/xml-components"; +import { Paragraph } from "../paragraph"; +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"; + +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", + 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, FootnoteType.SEPERATOR); + begin.addParagraph( + 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()), + ); + this.root.push(spacing); + } + + public createFootNote(paragraph: Paragraph): void { + const footnote = new Footnote(this.counter); + footnote.addParagraph(paragraph); + this.root.push(footnote); + + this.counter++; + } +} 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"; 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", }; } diff --git a/src/file/paragraph/paragraph.ts b/src/file/paragraph/paragraph.ts index 970c4bfae9..187814762e 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,14 @@ 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; + } + + public addRunToFront(run: Run): Paragraph { + this.root.splice(1, 0, run); + 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"); 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"; 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); + } +}