Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions demo/97-endnotes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Endnotes

import * as fs from "fs";
import { Document, EndnoteReferenceRun, Packer, Paragraph, TextRun } from "docx";

const doc = new Document({
endnotes: {
1: { children: [new Paragraph("This is the first endnote with some detailed explanation.")] },
2: { children: [new Paragraph("Second endnote"), new Paragraph("With multiple paragraphs for more complex content.")] },
3: { children: [new Paragraph("Third endnote referencing important source material.")] },
4: { children: [new Paragraph("Fourth endnote from a different section.")] },
},
sections: [
{
properties: {
page: {
margin: {
top: 1440, // 1 inch
right: 1440,
bottom: 1440,
left: 1440,
},
},
},
children: [
new Paragraph({
children: [
new TextRun({
text: "Endnotes Demo Document",
bold: true,
size: 28,
}),
],
spacing: { after: 400 },
}),
new Paragraph({
children: [
new TextRun("This document demonstrates endnotes functionality. "),
new TextRun("Here is some text with an endnote reference"),
new EndnoteReferenceRun(1),
new TextRun(". This allows for detailed citations and references "),
new EndnoteReferenceRun(2),
new TextRun(" without cluttering the main text flow."),
],
spacing: { after: 200 },
}),
new Paragraph({
children: [
new TextRun("Endnotes appear at the end of the document, "),
new TextRun("unlike footnotes which appear at the bottom of each page"),
new EndnoteReferenceRun(3),
new TextRun(". This makes them ideal for academic papers and formal documents."),
],
spacing: { after: 200 },
}),
],
},
{
children: [
new Paragraph({
children: [
new TextRun({
text: "Second Section",
bold: true,
size: 24,
}),
],
spacing: { after: 200 },
}),
new Paragraph({
children: [
new TextRun("This is content from a different section "),
new TextRun("with its own endnote reference"),
new EndnoteReferenceRun(4),
new TextRun(". Endnotes from all sections appear together at the document end."),
],
}),
],
},
],
});

Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});
63 changes: 58 additions & 5 deletions docs/usage/footnotes.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Footnotes
# Footnotes and Endnotes

!> Footnotes requires an understanding of [Sections](usage/sections.md).
!> Footnotes and endnotes require an understanding of [Sections](usage/sections.md).

Use footnotes and endnotes to explain, comment on, or provide references to something in a document. Usually, footnotes appear at the bottom of the page.
Use footnotes and endnotes to explain, comment on, or provide references to something in a document. Footnotes appear at the bottom of the page, while endnotes appear at the end of the document.

## Example
## Footnotes Example

```ts
const doc = new Document({
Expand Down Expand Up @@ -33,10 +33,63 @@ const doc = new Document({
});
```

## Endnotes Example

```ts
const doc = new Document({
endnotes: {
1: { children: [new Paragraph("This is the first endnote with some detailed explanation.")] },
2: { children: [new Paragraph("Second endnote"), new Paragraph("With multiple paragraphs for more complex content.")] },
3: { children: [new Paragraph("Third endnote referencing important source material.")] },
},
sections: [
{
children: [
new Paragraph({
children: [
new TextRun("This document demonstrates endnotes functionality. "),
new TextRun("Here is some text with an endnote reference"),
new EndnoteReferenceRun(1),
new TextRun(". This allows for detailed citations and references "),
new EndnoteReferenceRun(2),
new TextRun(" without cluttering the main text flow."),
],
}),
new Paragraph({
children: [
new TextRun("Endnotes appear at the end of the document, "),
new TextRun("unlike footnotes which appear at the bottom of each page"),
new EndnoteReferenceRun(3),
new TextRun(". This makes them ideal for academic papers and formal documents."),
],
}),
],
},
],
});
```

## Usage

Footnotes requires an entry into the `footnotes` array in the `Document` constructor, and a `FootnoteReferenceRun` in the `Paragraph` constructor.
### Footnotes

Footnotes require an entry in the `footnotes` object in the `Document` constructor, and a `FootnoteReferenceRun` in the `Paragraph` constructor.

`footnotes` is an object of number to `Footnote` objects. The number is the reference number, and the `Footnote` object is the content of the footnote. The `Footnote` object has a `children` property, which is an array of `Paragraph` objects.

`FootnoteReferenceRun` is a `Run` object, which are added to `Paragraph`s. It takes a number as a parameter, which is the reference number of the footnote.

### Endnotes

Endnotes require an entry in the `endnotes` object in the `Document` constructor, and an `EndnoteReferenceRun` in the `Paragraph` constructor.

`endnotes` is an object of number to `Endnote` objects. The number is the reference number, and the `Endnote` object is the content of the endnote. The `Endnote` object has a `children` property, which is an array of `Paragraph` objects.

`EndnoteReferenceRun` is a `Run` object, which are added to `Paragraph`s. It takes a number as a parameter, which is the reference number of the endnote.

### Key Differences

- **Footnotes** appear at the bottom of each page where they are referenced
- **Endnotes** appear at the end of the entire document
- Both footnote and endnote references automatically appear as superscript in the document
- Endnotes are ideal for academic papers, research documents, and lengthy citations that would otherwise clutter the page
34 changes: 34 additions & 0 deletions src/export/packer/next-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type IXmlifyedFileMapping = {
readonly AppProperties: IXmlifyedFile;
readonly FootNotes: IXmlifyedFile;
readonly FootNotesRelationships: IXmlifyedFile;
readonly Endnotes: IXmlifyedFile;
readonly EndnotesRelationships: IXmlifyedFile;
readonly Settings: IXmlifyedFile;
readonly Comments?: IXmlifyedFile;
readonly CommentsRelationships?: IXmlifyedFile;
Expand Down Expand Up @@ -454,6 +456,38 @@ export class Compiler {
),
path: "word/_rels/footnotes.xml.rels",
},
Endnotes: {
data: xml(
this.formatter.format(file.Endnotes.View, {
viewWrapper: file.Endnotes,
file,
stack: [],
}),
{
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
),
path: "word/endnotes.xml",
},
EndnotesRelationships: {
data: xml(
this.formatter.format(file.Endnotes.Relationships, {
viewWrapper: file.Endnotes,
file,
stack: [],
}),
{
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
),
path: "word/_rels/endnotes.xml.rels",
},
Settings: {
data: xml(
this.formatter.format(file.Settings, {
Expand Down
1 change: 1 addition & 0 deletions src/file/content-types/content-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class ContentTypes extends XmlComponent {
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"));
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml", "/word/endnotes.xml"));
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", "/word/settings.xml"));
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml", "/word/comments.xml"));
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml", "/word/fontTable.xml"));
Expand Down
8 changes: 8 additions & 0 deletions src/file/core-properties/properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ export type IPropertiesOptions = {
}
>
>;
readonly endnotes?: Readonly<
Record<
string,
{
readonly children: readonly Paragraph[];
}
>
>;
readonly background?: IDocumentBackgroundOptions;
readonly features?: {
readonly trackRevisions?: boolean;
Expand Down
3 changes: 2 additions & 1 deletion src/file/document-wrapper.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Document, IDocumentOptions } from "./document";
import { Endnotes } from "./endnotes";
import { Footer } from "./footer/footer";
import { FootNotes } from "./footnotes";
import { Header } from "./header/header";
import { Relationships } from "./relationships";
import { XmlComponent } from "./xml-components";

export type IViewWrapper = {
readonly View: Document | Footer | Header | FootNotes | XmlComponent;
readonly View: Document | Footer | Header | FootNotes | Endnotes | XmlComponent;
readonly Relationships: Relationships;
};

Expand Down
21 changes: 21 additions & 0 deletions src/file/endnotes-wrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { IViewWrapper } from "./document-wrapper";
import { Endnotes } from "./endnotes/endnotes";
import { Relationships } from "./relationships";

export class EndnotesWrapper implements IViewWrapper {
private readonly endnotes: Endnotes;
private readonly relationships: Relationships;

public constructor() {
this.endnotes = new Endnotes();
this.relationships = new Relationships();
}

public get View(): Endnotes {
return this.endnotes;
}

public get Relationships(): Relationships {
return this.relationships;
}
}
11 changes: 11 additions & 0 deletions src/file/endnotes/endnote/endnote-attributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { XmlAttributeComponent } from "@file/xml-components";

export class EndnoteAttributes extends XmlAttributeComponent<{
readonly type?: string;
readonly id: number;
}> {
protected readonly xmlKeys = {
type: "w:type",
id: "w:id",
};
}
Loading