table of contents with all the options
This commit is contained in:
@ -1 +1,2 @@
|
|||||||
export * from "./table-of-contents";
|
export * from "./table-of-contents";
|
||||||
|
export * from "./table-of-contents-properties";
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export class TableOfContentsProperties {
|
|
||||||
public headingRange = "1-6";
|
|
||||||
}
|
|
@ -1,61 +1,68 @@
|
|||||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
import { TableOfContentsProperties } from "./table-of-contents-properties";
|
||||||
|
|
||||||
class TextAttributes extends XmlAttributeComponent<{ space: "default" | "preserve" }> {
|
class TextAttributes extends XmlAttributeComponent<{ space: "default" | "preserve" }> {
|
||||||
protected xmlKeys = { space: "xml:space" };
|
protected xmlKeys = { space: "xml:space" };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Options according to this docs:
|
|
||||||
* http://officeopenxml.com/WPtableOfContents.php
|
|
||||||
*/
|
|
||||||
|
|
||||||
export class StyleLevel {
|
|
||||||
public styleName: string;
|
|
||||||
public level: number;
|
|
||||||
}
|
|
||||||
export class TableOfContentsInstructionProperties {
|
|
||||||
// \b option
|
|
||||||
public entriesFromSession: string;
|
|
||||||
// \h option
|
|
||||||
public hiperlink: true;
|
|
||||||
// \n option
|
|
||||||
public entryLevelsRange: string;
|
|
||||||
// \o option
|
|
||||||
public headerRange = "1-6";
|
|
||||||
// \t option
|
|
||||||
public styles: StyleLevel[];
|
|
||||||
// \z option
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TableOfContentsInstruction extends XmlComponent {
|
export class TableOfContentsInstruction extends XmlComponent {
|
||||||
private readonly properties: TableOfContentsInstructionProperties;
|
private readonly properties: TableOfContentsProperties;
|
||||||
|
|
||||||
constructor(properties?: TableOfContentsInstructionProperties) {
|
constructor(properties?: TableOfContentsProperties) {
|
||||||
super("w:instrText");
|
super("w:instrText");
|
||||||
this.properties = properties || new TableOfContentsInstructionProperties();
|
this.properties = properties || new TableOfContentsProperties();
|
||||||
|
|
||||||
this.root.push(new TextAttributes({ space: "preserve" }));
|
this.root.push(new TextAttributes({ space: "preserve" }));
|
||||||
let instruction = "TOC";
|
let instruction = "TOC";
|
||||||
if (this.properties.entriesFromSession) {
|
if (this.properties.captionLabel) {
|
||||||
instruction = `${instruction} \\b "${this.properties.entriesFromSession}"`;
|
instruction = `${instruction} \\a "${this.properties.captionLabel}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.entriesFromBookmark) {
|
||||||
|
instruction = `${instruction} \\b "${this.properties.entriesFromBookmark}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.captionLabelIncludingNumbers) {
|
||||||
|
instruction = `${instruction} \\c "${this.properties.captionLabelIncludingNumbers}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.sequenceAndPageNumbersSeparator) {
|
||||||
|
instruction = `${instruction} \\d "${this.properties.sequenceAndPageNumbersSeparator}"`;
|
||||||
|
}
|
||||||
|
if (this.properties.tcFieldIdentifier) {
|
||||||
|
instruction = `${instruction} \\f "${this.properties.tcFieldIdentifier}"`;
|
||||||
}
|
}
|
||||||
if (this.properties.hiperlink) {
|
if (this.properties.hiperlink) {
|
||||||
instruction = `${instruction} \\h`;
|
instruction = `${instruction} \\h`;
|
||||||
}
|
}
|
||||||
if (this.properties.entryLevelsRange) {
|
if (this.properties.tcFieldLevelRange) {
|
||||||
instruction = `${instruction} \\n "${this.properties.entryLevelsRange}"`;
|
instruction = `${instruction} \\l "${this.properties.tcFieldLevelRange}`;
|
||||||
}
|
}
|
||||||
if (this.properties.headerRange) {
|
if (this.properties.pageNumbersEntryLevelsRange) {
|
||||||
instruction = `${instruction} \\o "${this.properties.headerRange}"`;
|
instruction = `${instruction} \\n "${this.properties.pageNumbersEntryLevelsRange}`;
|
||||||
}
|
}
|
||||||
if (this.properties.styles && this.properties.styles.length) {
|
if (this.properties.headingStyleRange) {
|
||||||
const styles = this.properties.styles.map((sl) => `${sl.styleName}, ${sl.level}`).join(", ");
|
instruction = `${instruction} \\o "${this.properties.headingStyleRange}`;
|
||||||
|
}
|
||||||
|
if (this.properties.entryAndPageNumberSeparator) {
|
||||||
|
instruction = `${instruction} \\p "${this.properties.entryAndPageNumberSeparator}`;
|
||||||
|
}
|
||||||
|
if (this.properties.seqFieldIdentifierForPrefix) {
|
||||||
|
instruction = `${instruction} \\s "${this.properties.seqFieldIdentifierForPrefix}`;
|
||||||
|
}
|
||||||
|
if (this.properties.stylesWithLevels && this.properties.stylesWithLevels.length) {
|
||||||
|
const styles = this.properties.stylesWithLevels.map((sl) => `${sl.styleName},${sl.level}`).join(",");
|
||||||
instruction = `${instruction} \\t "${styles}"`;
|
instruction = `${instruction} \\t "${styles}"`;
|
||||||
}
|
}
|
||||||
|
if (this.properties.useAppliedParagraphOutlineLevel) {
|
||||||
|
instruction = `${instruction} \\u`;
|
||||||
|
}
|
||||||
|
if (this.properties.preserveTabInEntries) {
|
||||||
|
instruction = `${instruction} \\w`;
|
||||||
|
}
|
||||||
|
if (this.properties.preserveNewLineInEntries) {
|
||||||
|
instruction = `${instruction} \\x`;
|
||||||
|
}
|
||||||
|
if (this.properties.hideTabAndPageNumbersInWebView) {
|
||||||
|
instruction = `${instruction} \\z`;
|
||||||
|
}
|
||||||
this.root.push(instruction);
|
this.root.push(instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getHeaderRange(): string {
|
|
||||||
return this.properties.headerRange;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
122
src/file/table-of-contents/table-of-contents-properties.ts
Normal file
122
src/file/table-of-contents/table-of-contents-properties.ts
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
export class StyleLevel {
|
||||||
|
public styleName: string;
|
||||||
|
public level: number;
|
||||||
|
|
||||||
|
constructor(styleName: string, level: number) {
|
||||||
|
this.styleName = styleName;
|
||||||
|
this.level = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options according to this docs:
|
||||||
|
* https://www.ecma-international.org/publications/standards/Ecma-376.htm
|
||||||
|
* Part 1 - Page 1251
|
||||||
|
*
|
||||||
|
* Short Guide:
|
||||||
|
* http://officeopenxml.com/WPtableOfContents.php
|
||||||
|
*/
|
||||||
|
export class TableOfContentsProperties {
|
||||||
|
/**
|
||||||
|
* \a option - Includes captioned items, but omits caption labels and numbers.
|
||||||
|
* The identifier designated by text in this switch's field-argument corresponds to the caption label.
|
||||||
|
* Use captionLabelIncludingNumbers (\c) to build a table of captions with labels and numbers.
|
||||||
|
*/
|
||||||
|
public captionLabel: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \b option - Includes entries only from the portion of the document marked by
|
||||||
|
* the bookmark named by text in this switch's field-argument.
|
||||||
|
*/
|
||||||
|
public entriesFromBookmark: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \c option - Includes figures, tables, charts, and other items that are numbered
|
||||||
|
* by a SEQ field (§17.16.5.56). The sequence identifier designated by text in this switch's
|
||||||
|
* field-argument, which corresponds to the caption label, shall match the identifier in the
|
||||||
|
* corresponding SEQ field.
|
||||||
|
*/
|
||||||
|
public captionLabelIncludingNumbers: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \d option - When used with \s, the text in this switch's field-argument defines
|
||||||
|
* the separator between sequence and page numbers. The default separator is a hyphen (-).
|
||||||
|
*/
|
||||||
|
public sequenceAndPageNumbersSeparator: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \f option - Includes only those TC fields whose identifier exactly matches the
|
||||||
|
* text in this switch's field-argument (which is typically a letter).
|
||||||
|
*/
|
||||||
|
public tcFieldIdentifier: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \h option - Makes the table of contents entries hyperlinks.
|
||||||
|
*/
|
||||||
|
public hiperlink: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \l option - Includes TC fields that assign entries to one of the levels specified
|
||||||
|
* by text in this switch's field-argument as a range having the form startLevel-endLevel,
|
||||||
|
* where startLevel and endLevel are integers, and startLevel has a value equal-to or less-than endLevel.
|
||||||
|
* TC fields that assign entries to lower levels are skipped.
|
||||||
|
*/
|
||||||
|
public tcFieldLevelRange: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \n option - Without field-argument, omits page numbers from the table of contents.
|
||||||
|
* Page numbers are omitted from all levels unless a range of entry levels is specified by
|
||||||
|
* text in this switch's field-argument. A range is specified as for \l.
|
||||||
|
*/
|
||||||
|
public pageNumbersEntryLevelsRange: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \o option - Uses paragraphs formatted with all or the specified range of builtin
|
||||||
|
* heading styles. Headings in a style range are specified by text in this switch's
|
||||||
|
* field-argument using the notation specified as for \l, where each integer corresponds
|
||||||
|
* to the style with a style ID of HeadingX (e.g. 1 corresponds to Heading1).
|
||||||
|
* If no heading range is specified, all heading levels used in the document are listed.
|
||||||
|
*/
|
||||||
|
public headingStyleRange: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \p option - Text in this switch's field-argument specifies a sequence of characters
|
||||||
|
* that separate an entry and its page number. The default is a tab with leader dots.
|
||||||
|
*/
|
||||||
|
public entryAndPageNumberSeparator: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \s option - For entries numbered with a SEQ field (§17.16.5.56), adds a prefix to the page number.
|
||||||
|
* The prefix depends on the type of entry. text in this switch's field-argument shall match the
|
||||||
|
* identifier in the SEQ field.
|
||||||
|
*/
|
||||||
|
public seqFieldIdentifierForPrefix: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \t field-argument Uses paragraphs formatted with styles other than the built-in heading styles.
|
||||||
|
* Text in this switch's field-argument specifies those styles as a set of comma-separated doublets,
|
||||||
|
* with each doublet being a comma-separated set of style name and table of content level.
|
||||||
|
* \t can be combined with \o.
|
||||||
|
*/
|
||||||
|
public stylesWithLevels: StyleLevel[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \u Uses the applied paragraph outline level.
|
||||||
|
*/
|
||||||
|
public useAppliedParagraphOutlineLevel = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \w Preserves tab entries within table entries.
|
||||||
|
*/
|
||||||
|
public preserveTabInEntries = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \x Preserves newline characters within table entries.
|
||||||
|
*/
|
||||||
|
public preserveNewLineInEntries = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \z Hides tab leader and page numbers in web page view (§17.18.102).
|
||||||
|
*/
|
||||||
|
public hideTabAndPageNumbersInWebView = false;
|
||||||
|
}
|
@ -1,7 +1,46 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
|
||||||
import { Formatter } from "../../export/formatter";
|
import { Formatter } from "../../export/formatter";
|
||||||
import { TableOfContents } from "./";
|
import { StyleLevel, TableOfContents, TableOfContentsProperties } from "./";
|
||||||
|
|
||||||
|
describe("Table of Contents", () => {
|
||||||
|
describe("#constructor", () => {
|
||||||
|
it("should construct a TOC without options", () => {
|
||||||
|
const toc = new TableOfContents();
|
||||||
|
const tree = new Formatter().format(toc);
|
||||||
|
expect(tree).to.be.deep.equal(DEFAULT_TOC);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should construct a TOC with all the options and alias", () => {
|
||||||
|
const props = new TableOfContentsProperties();
|
||||||
|
|
||||||
|
props.captionLabel = "A";
|
||||||
|
props.entriesFromBookmark = "B";
|
||||||
|
props.captionLabelIncludingNumbers = "C";
|
||||||
|
props.sequenceAndPageNumbersSeparator = "D";
|
||||||
|
props.tcFieldIdentifier = "F";
|
||||||
|
props.hiperlink = true;
|
||||||
|
props.tcFieldLevelRange = "L";
|
||||||
|
props.pageNumbersEntryLevelsRange = "N";
|
||||||
|
props.headingStyleRange = "O";
|
||||||
|
props.entryAndPageNumberSeparator = "P";
|
||||||
|
props.seqFieldIdentifierForPrefix = "S";
|
||||||
|
|
||||||
|
const styles = new Array<StyleLevel>();
|
||||||
|
styles.push(new StyleLevel("SL", 1));
|
||||||
|
styles.push(new StyleLevel("SL", 2));
|
||||||
|
props.stylesWithLevels = styles;
|
||||||
|
props.useAppliedParagraphOutlineLevel = true;
|
||||||
|
props.preserveTabInEntries = true;
|
||||||
|
props.preserveNewLineInEntries = true;
|
||||||
|
props.hideTabAndPageNumbersInWebView = true;
|
||||||
|
|
||||||
|
const toc = new TableOfContents("Summary", props);
|
||||||
|
const tree = new Formatter().format(toc);
|
||||||
|
expect(tree).to.be.deep.equal(COMPLETE_TOC);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const DEFAULT_TOC = {
|
const DEFAULT_TOC = {
|
||||||
"w:sdt": [
|
"w:sdt": [
|
||||||
@ -47,7 +86,7 @@ const DEFAULT_TOC = {
|
|||||||
"xml:space": "preserve",
|
"xml:space": "preserve",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'TOC \\o "1-6"',
|
"TOC",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -91,13 +130,90 @@ const DEFAULT_TOC = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("Table of Contents", () => {
|
const COMPLETE_TOC = {
|
||||||
describe("#constructor", () => {
|
"w:sdt": [
|
||||||
it("should construct a TOC with default options", () => {
|
{
|
||||||
const toc = new TableOfContents();
|
"w:sdtPr": [
|
||||||
const tree = new Formatter().format(toc);
|
{
|
||||||
// console.log(JSON.stringify(tree, null, 2));
|
"w:alias": [
|
||||||
expect(tree).to.be.deep.equal(DEFAULT_TOC);
|
{
|
||||||
});
|
_attr: {
|
||||||
});
|
"w:val": "Summary",
|
||||||
});
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:sdtContent": [
|
||||||
|
{
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:pPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:fldChar": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:fldCharType": "begin",
|
||||||
|
"w:dirty": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:instrText": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"xml:space": "preserve",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'TOC \\a "A" \\b "B" \\c "C" \\d "D" \\f "F" \\h \\l "L \\n "N \\o "O \\p "P \\s "S \\t "SL,1,SL,2" \\u \\w \\x \\z',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:fldChar": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:fldCharType": "separate",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:pPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:rPr": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:fldChar": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:fldCharType": "end",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
@ -6,18 +6,19 @@ import { XmlComponent } from "file/xml-components";
|
|||||||
import { SdtContent } from "./sdt-content";
|
import { SdtContent } from "./sdt-content";
|
||||||
import { SdtProperties } from "./sdt-properties";
|
import { SdtProperties } from "./sdt-properties";
|
||||||
import { TableOfContentsInstruction } from "./table-of-contents-instruction";
|
import { TableOfContentsInstruction } from "./table-of-contents-instruction";
|
||||||
|
import { TableOfContentsProperties } from "./table-of-contents-properties";
|
||||||
|
|
||||||
export class TableOfContents extends XmlComponent {
|
export class TableOfContents extends XmlComponent {
|
||||||
constructor() {
|
constructor(alias: string = "Table of Contents", properties?: TableOfContentsProperties) {
|
||||||
super("w:sdt");
|
super("w:sdt");
|
||||||
this.root.push(new SdtProperties("Table of Contents"));
|
this.root.push(new SdtProperties(alias));
|
||||||
|
|
||||||
const content = new SdtContent();
|
const content = new SdtContent();
|
||||||
|
|
||||||
const beginParagraph = new Paragraph();
|
const beginParagraph = new Paragraph();
|
||||||
const beginRun = new Run();
|
const beginRun = new Run();
|
||||||
beginRun.addChildElement(new Begin(true));
|
beginRun.addChildElement(new Begin(true));
|
||||||
beginRun.addChildElement(new TableOfContentsInstruction());
|
beginRun.addChildElement(new TableOfContentsInstruction(properties));
|
||||||
beginRun.addChildElement(new Separate());
|
beginRun.addChildElement(new Separate());
|
||||||
beginParagraph.addRun(beginRun);
|
beginParagraph.addRun(beginRun);
|
||||||
content.addChildElement(beginParagraph);
|
content.addChildElement(beginParagraph);
|
||||||
|
Reference in New Issue
Block a user