#627 Add footnotes relationships
This commit is contained in:
@ -1,9 +1,24 @@
|
|||||||
// Example on how to add hyperlinks to websites
|
// Example on how to add hyperlinks to websites
|
||||||
// Import from 'docx' rather than '../build' if you install from npm
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { Document, ExternalHyperlink, Footer, Media, Packer, Paragraph, TextRun } from "../build";
|
import { Document, ExternalHyperlink, Footer, FootnoteReferenceRun, Media, Packer, Paragraph, TextRun } from "../build";
|
||||||
|
|
||||||
const doc = new Document({});
|
const doc = new Document({
|
||||||
|
footnotes: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new TextRun("Click here for the "),
|
||||||
|
new ExternalHyperlink({
|
||||||
|
child: new TextRun({
|
||||||
|
text: "Footnotes external hyperlink",
|
||||||
|
style: "Hyperlink",
|
||||||
|
}),
|
||||||
|
link: "http://www.example.com",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const image1 = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
const image1 = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||||
|
|
||||||
@ -54,6 +69,7 @@ doc.addSection({
|
|||||||
}),
|
}),
|
||||||
link: "http://www.example.com",
|
link: "http://www.example.com",
|
||||||
}),
|
}),
|
||||||
|
new FootnoteReferenceRun(1)
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
|
@ -22,13 +22,14 @@ describe("Compiler", () => {
|
|||||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||||
|
|
||||||
expect(fileNames).is.an.instanceof(Array);
|
expect(fileNames).is.an.instanceof(Array);
|
||||||
expect(fileNames).has.length(14);
|
expect(fileNames).has.length(15);
|
||||||
expect(fileNames).to.include("word/document.xml");
|
expect(fileNames).to.include("word/document.xml");
|
||||||
expect(fileNames).to.include("word/styles.xml");
|
expect(fileNames).to.include("word/styles.xml");
|
||||||
expect(fileNames).to.include("docProps/core.xml");
|
expect(fileNames).to.include("docProps/core.xml");
|
||||||
expect(fileNames).to.include("docProps/app.xml");
|
expect(fileNames).to.include("docProps/app.xml");
|
||||||
expect(fileNames).to.include("word/numbering.xml");
|
expect(fileNames).to.include("word/numbering.xml");
|
||||||
expect(fileNames).to.include("word/footnotes.xml");
|
expect(fileNames).to.include("word/footnotes.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/footnotes.xml.rels");
|
||||||
expect(fileNames).to.include("word/settings.xml");
|
expect(fileNames).to.include("word/settings.xml");
|
||||||
expect(fileNames).to.include("word/_rels/document.xml.rels");
|
expect(fileNames).to.include("word/_rels/document.xml.rels");
|
||||||
expect(fileNames).to.include("[Content_Types].xml");
|
expect(fileNames).to.include("[Content_Types].xml");
|
||||||
@ -62,7 +63,7 @@ describe("Compiler", () => {
|
|||||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||||
|
|
||||||
expect(fileNames).is.an.instanceof(Array);
|
expect(fileNames).is.an.instanceof(Array);
|
||||||
expect(fileNames).has.length(22);
|
expect(fileNames).has.length(23);
|
||||||
|
|
||||||
expect(fileNames).to.include("word/header1.xml");
|
expect(fileNames).to.include("word/header1.xml");
|
||||||
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
||||||
@ -88,7 +89,7 @@ describe("Compiler", () => {
|
|||||||
const spy = sinon.spy(compiler["formatter"], "format");
|
const spy = sinon.spy(compiler["formatter"], "format");
|
||||||
|
|
||||||
compiler.compile(file);
|
compiler.compile(file);
|
||||||
expect(spy.callCount).to.equal(10);
|
expect(spy.callCount).to.equal(11);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -25,6 +25,7 @@ interface IXmlifyedFileMapping {
|
|||||||
readonly ContentTypes: IXmlifyedFile;
|
readonly ContentTypes: IXmlifyedFile;
|
||||||
readonly AppProperties: IXmlifyedFile;
|
readonly AppProperties: IXmlifyedFile;
|
||||||
readonly FootNotes: IXmlifyedFile;
|
readonly FootNotes: IXmlifyedFile;
|
||||||
|
readonly FootNotesRelationships: IXmlifyedFile;
|
||||||
readonly Settings: IXmlifyedFile;
|
readonly Settings: IXmlifyedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,9 +185,13 @@ export class Compiler {
|
|||||||
path: "docProps/app.xml",
|
path: "docProps/app.xml",
|
||||||
},
|
},
|
||||||
FootNotes: {
|
FootNotes: {
|
||||||
data: xml(this.formatter.format(file.FootNotes, file.Document), prettify),
|
data: xml(this.formatter.format(file.FootNotes.View, file.FootNotes), prettify),
|
||||||
path: "word/footnotes.xml",
|
path: "word/footnotes.xml",
|
||||||
},
|
},
|
||||||
|
FootNotesRelationships: {
|
||||||
|
data: xml(this.formatter.format(file.FootNotes.Relationships, file.FootNotes), prettify),
|
||||||
|
path: "word/_rels/footnotes.xml.rels",
|
||||||
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
data: xml(this.formatter.format(file.Settings, file.Document), prettify),
|
data: xml(this.formatter.format(file.Settings, file.Document), prettify),
|
||||||
path: "word/settings.xml",
|
path: "word/settings.xml",
|
||||||
|
@ -1,50 +1,16 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import * as sinon from "sinon";
|
|
||||||
|
|
||||||
import { FooterWrapper } from "./footer-wrapper";
|
import { DocumentWrapper } from "./document-wrapper";
|
||||||
import { Media } from "./media";
|
|
||||||
import { Paragraph } from "./paragraph";
|
|
||||||
import { Table, TableCell, TableRow } from "./table";
|
|
||||||
|
|
||||||
describe("FooterWrapper", () => {
|
describe("DocumentWrapper", () => {
|
||||||
describe("#add", () => {
|
describe("#constructor", () => {
|
||||||
it("should call the underlying footer's addParagraph", () => {
|
it("should create", () => {
|
||||||
const file = new FooterWrapper(new Media(), 1);
|
const file = new DocumentWrapper({ background: {} });
|
||||||
const spy = sinon.spy(file.View, "add");
|
|
||||||
file.add(new Paragraph({}));
|
|
||||||
|
|
||||||
expect(spy.called).to.equal(true);
|
// tslint:disable-next-line: no-unused-expression
|
||||||
});
|
expect(file.View).to.be.ok;
|
||||||
|
// tslint:disable-next-line: no-unused-expression
|
||||||
it("should call the underlying footer's addParagraph", () => {
|
expect(file.Relationships).to.be.ok;
|
||||||
const file = new FooterWrapper(new Media(), 1);
|
|
||||||
const spy = sinon.spy(file.View, "add");
|
|
||||||
file.add(
|
|
||||||
new Table({
|
|
||||||
rows: [
|
|
||||||
new TableRow({
|
|
||||||
children: [
|
|
||||||
new TableCell({
|
|
||||||
children: [new Paragraph("hello")],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(spy.called).to.equal(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#addChildElement", () => {
|
|
||||||
it("should call the underlying footer's addChildElement", () => {
|
|
||||||
const file = new FooterWrapper(new Media(), 1);
|
|
||||||
const spy = sinon.spy(file.View, "addChildElement");
|
|
||||||
// tslint:disable-next-line:no-any
|
|
||||||
file.addChildElement({} as any);
|
|
||||||
|
|
||||||
expect(spy.called).to.equal(true);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Document, IDocumentOptions } from "./document";
|
import { Document, IDocumentOptions } from "./document";
|
||||||
import { Footer } from "./footer";
|
import { Footer } from "./footer";
|
||||||
|
import { FootNotes } from "./footnotes";
|
||||||
import { Header } from "./header/header";
|
import { Header } from "./header/header";
|
||||||
import { Relationships } from "./relationships";
|
import { Relationships } from "./relationships";
|
||||||
|
|
||||||
export interface IViewWrapper {
|
export interface IViewWrapper {
|
||||||
readonly View: Document | Footer | Header;
|
readonly View: Document | Footer | Header | FootNotes;
|
||||||
readonly Relationships: Relationships;
|
readonly Relationships: Relationships;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ describe("File", () => {
|
|||||||
footnotes: [new Paragraph("hello")],
|
footnotes: [new Paragraph("hello")],
|
||||||
});
|
});
|
||||||
|
|
||||||
const tree = new Formatter().format(wrapper.FootNotes);
|
const tree = new Formatter().format(wrapper.FootNotes.View);
|
||||||
|
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:footnotes": [
|
"w:footnotes": [
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
import { IPageMarginAttributes } from "./document/body/section-properties/page-margin/page-margin-attributes";
|
import { IPageMarginAttributes } from "./document/body/section-properties/page-margin/page-margin-attributes";
|
||||||
import { IFileProperties } from "./file-properties";
|
import { IFileProperties } from "./file-properties";
|
||||||
import { FooterWrapper, IDocumentFooter } from "./footer-wrapper";
|
import { FooterWrapper, IDocumentFooter } from "./footer-wrapper";
|
||||||
import { FootNotes } from "./footnotes";
|
import { FootnotesWrapper } from "./footnotes-wrapper";
|
||||||
import { Footer, Header } from "./header";
|
import { Footer, Header } from "./header";
|
||||||
import { HeaderWrapper, IDocumentHeader } from "./header-wrapper";
|
import { HeaderWrapper, IDocumentHeader } from "./header-wrapper";
|
||||||
import { Media } from "./media";
|
import { Media } from "./media";
|
||||||
@ -53,7 +53,7 @@ export class File {
|
|||||||
private readonly numbering: Numbering;
|
private readonly numbering: Numbering;
|
||||||
private readonly media: Media;
|
private readonly media: Media;
|
||||||
private readonly fileRelationships: Relationships;
|
private readonly fileRelationships: Relationships;
|
||||||
private readonly footNotes: FootNotes;
|
private readonly footnotesWrapper: FootnotesWrapper;
|
||||||
private readonly settings: Settings;
|
private readonly settings: Settings;
|
||||||
private readonly contentTypes: ContentTypes;
|
private readonly contentTypes: ContentTypes;
|
||||||
private readonly appProperties: AppProperties;
|
private readonly appProperties: AppProperties;
|
||||||
@ -76,10 +76,9 @@ export class File {
|
|||||||
config: [],
|
config: [],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// this.documentWrapper.Relationships = new Relationships();
|
|
||||||
this.fileRelationships = new Relationships();
|
this.fileRelationships = new Relationships();
|
||||||
this.appProperties = new AppProperties();
|
this.appProperties = new AppProperties();
|
||||||
this.footNotes = new FootNotes();
|
this.footnotesWrapper = new FootnotesWrapper();
|
||||||
this.contentTypes = new ContentTypes();
|
this.contentTypes = new ContentTypes();
|
||||||
this.documentWrapper = new DocumentWrapper({ background: options.background || {} });
|
this.documentWrapper = new DocumentWrapper({ background: options.background || {} });
|
||||||
this.settings = new Settings({
|
this.settings = new Settings({
|
||||||
@ -138,7 +137,7 @@ export class File {
|
|||||||
|
|
||||||
if (options.footnotes) {
|
if (options.footnotes) {
|
||||||
for (const paragraph of options.footnotes) {
|
for (const paragraph of options.footnotes) {
|
||||||
this.footNotes.createFootNote(paragraph);
|
this.footnotesWrapper.View.createFootNote(paragraph);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,8 +304,8 @@ export class File {
|
|||||||
return this.appProperties;
|
return this.appProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get FootNotes(): FootNotes {
|
public get FootNotes(): FootnotesWrapper {
|
||||||
return this.footNotes;
|
return this.footnotesWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get Settings(): Settings {
|
public get Settings(): Settings {
|
||||||
|
16
src/file/footnotes-wrapper.spec.ts
Normal file
16
src/file/footnotes-wrapper.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { FootnotesWrapper } from "./footnotes-wrapper";
|
||||||
|
|
||||||
|
describe("FootnotesWrapper", () => {
|
||||||
|
describe("#constructor", () => {
|
||||||
|
it("should create", () => {
|
||||||
|
const file = new FootnotesWrapper();
|
||||||
|
|
||||||
|
// tslint:disable-next-line: no-unused-expression
|
||||||
|
expect(file.View).to.be.ok;
|
||||||
|
// tslint:disable-next-line: no-unused-expression
|
||||||
|
expect(file.Relationships).to.be.ok;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
21
src/file/footnotes-wrapper.ts
Normal file
21
src/file/footnotes-wrapper.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { IViewWrapper } from "./document-wrapper";
|
||||||
|
import { FootNotes } from "./footnotes/footnotes";
|
||||||
|
import { Relationships } from "./relationships";
|
||||||
|
|
||||||
|
export class FootnotesWrapper implements IViewWrapper {
|
||||||
|
private readonly footnotess: FootNotes;
|
||||||
|
private readonly relationships: Relationships;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.footnotess = new FootNotes();
|
||||||
|
this.relationships = new Relationships();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get View(): FootNotes {
|
||||||
|
return this.footnotess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get Relationships(): Relationships {
|
||||||
|
return this.relationships;
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,10 @@ import { Footnote, FootnoteType } from "./footnote";
|
|||||||
describe("Footnote", () => {
|
describe("Footnote", () => {
|
||||||
describe("#constructor", () => {
|
describe("#constructor", () => {
|
||||||
it("should create a footnote with a footnote type", () => {
|
it("should create a footnote with a footnote type", () => {
|
||||||
const footnote = new Footnote(1, FootnoteType.SEPERATOR);
|
const footnote = new Footnote({
|
||||||
|
id: 1,
|
||||||
|
type: FootnoteType.SEPERATOR,
|
||||||
|
});
|
||||||
const tree = new Formatter().format(footnote);
|
const tree = new Formatter().format(footnote);
|
||||||
|
|
||||||
expect(Object.keys(tree)).to.deep.equal(["w:footnote"]);
|
expect(Object.keys(tree)).to.deep.equal(["w:footnote"]);
|
||||||
@ -15,7 +18,9 @@ describe("Footnote", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should create a footnote without a footnote type", () => {
|
it("should create a footnote without a footnote type", () => {
|
||||||
const footnote = new Footnote(1);
|
const footnote = new Footnote({
|
||||||
|
id: 1,
|
||||||
|
});
|
||||||
const tree = new Formatter().format(footnote);
|
const tree = new Formatter().format(footnote);
|
||||||
|
|
||||||
expect(Object.keys(tree)).to.deep.equal(["w:footnote"]);
|
expect(Object.keys(tree)).to.deep.equal(["w:footnote"]);
|
||||||
|
@ -9,13 +9,18 @@ export enum FootnoteType {
|
|||||||
CONTINUATION_SEPERATOR = "continuationSeparator",
|
CONTINUATION_SEPERATOR = "continuationSeparator",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IFootnoteOptions {
|
||||||
|
readonly id: number;
|
||||||
|
readonly type?: FootnoteType;
|
||||||
|
}
|
||||||
|
|
||||||
export class Footnote extends XmlComponent {
|
export class Footnote extends XmlComponent {
|
||||||
constructor(id: number, type?: FootnoteType) {
|
constructor(options: IFootnoteOptions) {
|
||||||
super("w:footnote");
|
super("w:footnote");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new FootnoteAttributes({
|
new FootnoteAttributes({
|
||||||
type: type,
|
type: options.type,
|
||||||
id: id,
|
id: options.id,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,10 @@ export class FootNotes extends XmlComponent {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const begin = new Footnote(-1, FootnoteType.SEPERATOR);
|
const begin = new Footnote({
|
||||||
|
id: -1,
|
||||||
|
type: FootnoteType.SEPERATOR,
|
||||||
|
});
|
||||||
begin.add(
|
begin.add(
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
spacing: {
|
spacing: {
|
||||||
@ -49,7 +52,10 @@ export class FootNotes extends XmlComponent {
|
|||||||
);
|
);
|
||||||
this.root.push(begin);
|
this.root.push(begin);
|
||||||
|
|
||||||
const spacing = new Footnote(0, FootnoteType.CONTINUATION_SEPERATOR);
|
const spacing = new Footnote({
|
||||||
|
id: 0,
|
||||||
|
type: FootnoteType.CONTINUATION_SEPERATOR,
|
||||||
|
});
|
||||||
spacing.add(
|
spacing.add(
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
spacing: {
|
spacing: {
|
||||||
@ -64,7 +70,7 @@ export class FootNotes extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public createFootNote(paragraph: Paragraph): void {
|
public createFootNote(paragraph: Paragraph): void {
|
||||||
const footnote = new Footnote(this.currentId);
|
const footnote = new Footnote({ id: this.currentId });
|
||||||
footnote.add(paragraph);
|
footnote.add(paragraph);
|
||||||
this.root.push(footnote);
|
this.root.push(footnote);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user