diff --git a/.github/workflows/demos.yml b/.github/workflows/demos.yml index 8f9675f05e..5de2594afa 100644 --- a/.github/workflows/demos.yml +++ b/.github/workflows/demos.yml @@ -716,3 +716,93 @@ jobs: with: xml-file: build/extracted-doc/word/document.xml xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd + - name: Run Demo + run: npm run ts-node -- ./demo/75-tab-stops.ts + - name: Extract Word Document + run: npm run extract + - name: Validate XML + uses: ChristophWurst/xmllint-action@v1 + with: + xml-file: build/extracted-doc/word/document.xml + xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd + - name: Run Demo + run: npm run ts-node -- ./demo/76-compatibility.ts + - name: Extract Word Document + run: npm run extract + - name: Validate XML + uses: ChristophWurst/xmllint-action@v1 + with: + xml-file: build/extracted-doc/word/document.xml + xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd + - name: Run Demo + run: npm run ts-node -- ./demo/77-side-by-side-tables.ts + - name: Extract Word Document + run: npm run extract + - name: Validate XML + uses: ChristophWurst/xmllint-action@v1 + with: + xml-file: build/extracted-doc/word/document.xml + xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd + - name: Run Demo + run: npm run ts-node -- ./demo/78-thai-distributed.ts + - name: Extract Word Document + run: npm run extract + - name: Validate XML + uses: ChristophWurst/xmllint-action@v1 + with: + xml-file: build/extracted-doc/word/document.xml + xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd + - name: Run Demo + run: npm run ts-node -- ./demo/79-table-from-data-source.ts + - name: Extract Word Document + run: npm run extract + - name: Validate XML + uses: ChristophWurst/xmllint-action@v1 + with: + xml-file: build/extracted-doc/word/document.xml + xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd + - name: Run Demo + run: npm run ts-node -- ./demo/80-thai-distributed.ts + - name: Extract Word Document + run: npm run extract + - name: Validate XML + uses: ChristophWurst/xmllint-action@v1 + with: + xml-file: build/extracted-doc/word/document.xml + xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd + - name: Run Demo + run: npm run ts-node -- ./demo/81-continuous-header.ts + - name: Extract Word Document + run: npm run extract + - name: Validate XML + uses: ChristophWurst/xmllint-action@v1 + with: + xml-file: build/extracted-doc/word/document.xml + xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd + - name: Run Demo + run: npm run ts-node -- ./demo/82-new-headers-new-section.ts + - name: Extract Word Document + run: npm run extract + - name: Validate XML + uses: ChristophWurst/xmllint-action@v1 + with: + xml-file: build/extracted-doc/word/document.xml + xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd + - name: Run Demo + run: npm run ts-node -- ./demo/83-setting-languages.ts + - name: Extract Word Document + run: npm run extract + - name: Validate XML + uses: ChristophWurst/xmllint-action@v1 + with: + xml-file: build/extracted-doc/word/document.xml + xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd + - name: Run Demo + run: npm run ts-node -- ./demo/84-positional-tabs.ts + - name: Extract Word Document + run: npm run extract + - name: Validate XML + uses: ChristophWurst/xmllint-action@v1 + with: + xml-file: build/extracted-doc/word/document.xml + xml-schema-file: ooxml-schemas/microsoft/wml-2010.xsd diff --git a/demo/75-tab-stops.ts b/demo/75-tab-stops.ts index a2257419d8..a9ea5deed1 100644 --- a/demo/75-tab-stops.ts +++ b/demo/75-tab-stops.ts @@ -1,7 +1,7 @@ -// Exporting the document as a stream +// Example of using tab stops // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, HeadingLevel, Packer, Paragraph, TabStopPosition, TabStopType, TextRun } from "../build"; +import { Document, HeadingLevel, Packer, Paragraph, TabStopPosition, TabStopType, TextRun, Tab } from "../build"; const columnWidth = TabStopPosition.MAX / 4; const receiptTabStops = [ @@ -30,7 +30,7 @@ const doc = new Document({ tabStops: twoTabStops, children: [ new TextRun({ - text: "To Bob.\tBy Alice.", + children: ["To Bob.", new Tab(), "By Alice."], bold: true, }), ], diff --git a/demo/84-positional-tabs.ts b/demo/84-positional-tabs.ts new file mode 100644 index 0000000000..1d61b93de7 --- /dev/null +++ b/demo/84-positional-tabs.ts @@ -0,0 +1,60 @@ +// Simple example apply positional tabs to a document +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { + Document, + Packer, + Paragraph, + PositionalTab, + Tab, + TextRun, + PositionalTabAlignment, + PositionalTabRelativeTo, + PositionalTabLeader, +} from "../build"; + +const doc = new Document({ + sections: [ + { + properties: {}, + children: [ + new Paragraph({ + children: [ + new TextRun("Full name"), + new TextRun({ + children: [ + new PositionalTab({ + alignment: PositionalTabAlignment.RIGHT, + relativeTo: PositionalTabRelativeTo.MARGIN, + leader: PositionalTabLeader.DOT, + }), + "John Doe", + ], + bold: true, + }), + ], + }), + new Paragraph({ + children: [ + new TextRun("Hello World"), + new TextRun({ + children: [ + new PositionalTab({ + alignment: PositionalTabAlignment.CENTER, + relativeTo: PositionalTabRelativeTo.INDENT, + leader: PositionalTabLeader.HYPHEN, + }), + "Foo bar", + ], + bold: true, + }), + ], + }), + ], + }, + ], +}); + +Packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/docs/_sidebar.md b/docs/_sidebar.md index cebe089854..8805f6a417 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -20,7 +20,7 @@ - [Hyperlinks](usage/hyperlinks.md) - [Numbering](usage/numbering.md) - [Tables](usage/tables.md) - - [Tab Stops](usage/tab-stops.md) + - [Tabs](usage/tabs.md) - [Table of Contents](usage/table-of-contents.md) - [Page Numbers](usage/page-numbers.md) - [Change Tracking](usage/change-tracking.md) diff --git a/docs/usage/tab-stops.md b/docs/usage/tab-stops.md deleted file mode 100644 index 5c859a1bac..0000000000 --- a/docs/usage/tab-stops.md +++ /dev/null @@ -1,121 +0,0 @@ -# Tab Stops - -> Tab stops are useful, if you are unclear of what they are, [here is a link explaining](https://en.wikipedia.org/wiki/Tab_stop). It enables side by side text which is nicely laid out without the need for tables, or constantly pressing space bar. - -!> **Note**: The unit of measurement for a tab stop is in [DXA](https://stackoverflow.com/questions/14360183/default-wordml-unit-measurement-pixel-or-point-or-inches) - -![Word 2013 Tabs](https://support.content.office.net/en-us/media/d75ca75d-9fe9-4d46-9a8b-4534c13acbc5.png "Word 2013 Tab Stops") - -Simply declare the tab stops on the paragraph, as shown below. Use the tab character `\t` to indicate the tab position within the `text` property of a `TextRun`. Adding multiple `tabStops` will mean you can add additional `\t` characters until the desired `tabStop` is selected. Example is shown below. - -## Example - -```ts -const paragraph = new Paragraph({ - children: [new TextRun({ text: "Hey everyone", bold: true}), new TextRun("\t11th November 1999")], - tabStops: [ - { - type: TabStopType.RIGHT, - position: TabStopPosition.MAX, - }, - ], -}); -``` - -The example above will create a left aligned text, and a right aligned text on the same line. The laymans approach to this problem would be to either use text boxes or tables. Not ideal! - -```ts -const paragraph = new Paragraph({ - children: [new TextRun("\t\tSecond tab stop here I come!")], - tabStops: [ - { - type: TabStopType.RIGHT, - position: TabStopPosition.MAX, - }, - { - type: TabStopType.LEFT, - position: 1000, - }, - ], -}); -``` - -The above shows the use of two tab stops, and how to select/use it. - -You can add multiple tab stops of the same `type` too. - -```ts -const paragraph = new Paragraph({ - children: [new TextRun("Multiple \ttab \tstops!")], - tabStops: [ - { - type: TabStopType.RIGHT, - position: TabStopPosition.MAX, - }, - { - type: TabStopType.RIGHT, - position: 1000, - }, - ], -}); -``` - -## Left Tab Stop - -```ts -const paragraph = new Paragraph({ - tabStops: [ - { - type: TabStopType.LEFT, - position: 2268, - }, - ], -}); -``` - -2268 is the distance from the left side. - -## Center Tab Stop - -```ts -const paragraph = new Paragraph({ - tabStops: [ - { - type: TabStopType.CENTER, - position: 2268, - }, - ], -}); -``` - -2268 is the distance from the center. - -## Right Tab Stop - -```ts -const paragraph = new Paragraph({ - tabStops: [ - { - type: TabStopType.RIGHT, - position: 2268, - }, - ], -}); -``` - -2268 is the distance from the left side. - -## Max Right Tab Stop - -```ts -const paragraph = new Paragraph({ - tabStops: [ - { - type: TabStopType.RIGHT, - position: TabStopPosition.MAX, - }, - ], -}); -``` - -This will create a tab stop on the very edge of the right hand side. Handy for right aligning and left aligning text on the same line. diff --git a/docs/usage/tabs.md b/docs/usage/tabs.md new file mode 100644 index 0000000000..f2ab2f2841 --- /dev/null +++ b/docs/usage/tabs.md @@ -0,0 +1,184 @@ +# Tabs and Tab Stops + +## Tab Stops + +> Tab stops are useful, if you are unclear of what they are, [here is a link explaining](https://en.wikipedia.org/wiki/Tab_stop). It enables side by side text which is nicely laid out without the need for tables, or constantly pressing space bar. + +!> **Note**: The unit of measurement for a tab stop is in [DXA](https://stackoverflow.com/questions/14360183/default-wordml-unit-measurement-pixel-or-point-or-inches) + +![Word 2013 Tabs](https://support.content.office.net/en-us/media/d75ca75d-9fe9-4d46-9a8b-4534c13acbc5.png "Word 2013 Tab Stops") + +Simply declare the tab stops on the paragraph, as shown below. Use the tab character `\t` or add the `new Tab()` child to indicate the tab position within the `text` property of a `TextRun`. Adding multiple `tabStops` will mean you can add additional `\t` characters until the desired `tabStop` is selected. Example is shown below. + +### Example + +```ts +const paragraph = new Paragraph({ + children: [ + new TextRun({ text: "Hey everyone", bold: true }), + new TextRun("\t11th November 1999"), + new TextRun({ + children: [new Tab(), "11th November 1999"], + }), + ], + tabStops: [ + { + type: TabStopType.RIGHT, + position: TabStopPosition.MAX, + }, + ], +}); +``` + +The example above will create a left aligned text, and a right aligned text on the same line. The laymans approach to this problem would be to either use text boxes or tables. Not ideal! + +```ts +const paragraph = new Paragraph({ + children: [new TextRun("\t\tSecond tab stop here I come!")], + tabStops: [ + { + type: TabStopType.RIGHT, + position: TabStopPosition.MAX, + }, + { + type: TabStopType.LEFT, + position: 1000, + }, + ], +}); +``` + +The above shows the use of two tab stops, and how to select/use it. + +You can add multiple tab stops of the same `type` too. + +```ts +const paragraph = new Paragraph({ + children: [new TextRun("Multiple \ttab \tstops!")], + tabStops: [ + { + type: TabStopType.RIGHT, + position: TabStopPosition.MAX, + }, + { + type: TabStopType.RIGHT, + position: 1000, + }, + ], +}); + +const paragraph = new Paragraph({ + children: [ + new TextRun({ + children: ["Multiple ", new Tab(), "tab ", new Tab(), "stops!"], + }), + ], + tabStops: [ + { + type: TabStopType.RIGHT, + position: TabStopPosition.MAX, + }, + { + type: TabStopType.RIGHT, + position: 1000, + }, + ], +}); +``` + +### Left Tab Stop + +```ts +const paragraph = new Paragraph({ + tabStops: [ + { + type: TabStopType.LEFT, + position: 2268, + }, + ], +}); +``` + +2268 is the distance from the left side. + +### Center Tab Stop + +```ts +const paragraph = new Paragraph({ + tabStops: [ + { + type: TabStopType.CENTER, + position: 2268, + }, + ], +}); +``` + +2268 is the distance from the center. + +### Right Tab Stop + +```ts +const paragraph = new Paragraph({ + tabStops: [ + { + type: TabStopType.RIGHT, + position: 2268, + }, + ], +}); +``` + +2268 is the distance from the left side. + +### Max Right Tab Stop + +```ts +const paragraph = new Paragraph({ + tabStops: [ + { + type: TabStopType.RIGHT, + position: TabStopPosition.MAX, + }, + ], +}); +``` + +This will create a tab stop on the very edge of the right hand side. Handy for right aligning and left aligning text on the same line. + +## Positional Tabs + +> Positional tab allow you to create a tab stop that is relative to the margin, or the page. This is useful if you want to create a table of contents, or a table of figures. + +They are easier to use than the normal tab stops, as you can use the `PositionalTab` class to create a tab stop, and then add the text to the `TextRun` children. Useful for most cases. + +![Word Positional Tabs](https://user-images.githubusercontent.com/26860966/209019464-d4b50236-c324-4cdb-8139-b9d172b1b993.png "Word Positional Tabs") + +### Example + +```ts +new Paragraph({ + children: [ + new TextRun("Full name"), + new TextRun({ + children: [ + new PositionalTab({ + alignment: PositionalTabAlignment.RIGHT, + relativeTo: PositionalTabRelativeTo.MARGIN, + leader: PositionalTabLeader.DOT, + }), + "John Doe", + ], + bold: true, + }), + ], +}), +``` + +### Options + +| Option | Type | Description | Possible Values | +| ---------- | ------------------------- | ------------------------------------- | ------------------------------------------------------------- | +| alignment | `PositionalTabAlignment` | The alignment of the tab stop | `LEFT`, `RIGHT`, `CENTER` | +| relativeTo | `PositionalTabRelativeTo` | The relative position of the tab stop | `MARGIN`, `INDENT` | +| leader | `PositionalTabLeader` | The leader of the tab stop | `NONE`, `DOT`, `HYPHEN`, `UNDERSCORE`, `MIDDLE_DOT`, `EQUALS` | diff --git a/src/file/paragraph/run/empty-children.spec.ts b/src/file/paragraph/run/empty-children.spec.ts new file mode 100644 index 0000000000..af9ccb4e35 --- /dev/null +++ b/src/file/paragraph/run/empty-children.spec.ts @@ -0,0 +1,229 @@ +import { expect } from "chai"; + +import { Formatter } from "@export/formatter"; +import { + AnnotationReference, + CarriageReturn, + ContinuationSeparator, + DayLong, + DayShort, + EndnoteReference, + FootnoteReferenceElement, + LastRenderedPageBreak, + MonthLong, + MonthShort, + NoBreakHyphen, + PageNumberElement, + Separator, + SoftHyphen, + Tab, + YearLong, + YearShort, +} from "./empty-children"; + +// +// +// +// +// +// +// +// +// +// +// +// +// +// ... +// +// +// +// ... +// + +describe("NoBreakHyphen", () => { + describe("#constructor()", () => { + it("should create a NoBreakHyphen with correct root key", () => { + const tree = new Formatter().format(new NoBreakHyphen()); + expect(tree).to.deep.equal({ + "w:noBreakHyphen": {}, + }); + }); + }); +}); + +describe("SoftHyphen", () => { + describe("#constructor()", () => { + it("should create a SoftHyphen with correct root key", () => { + const tree = new Formatter().format(new SoftHyphen()); + expect(tree).to.deep.equal({ + "w:softHyphen": {}, + }); + }); + }); +}); + +describe("DayShort", () => { + describe("#constructor()", () => { + it("should create a DayShort with correct root key", () => { + const tree = new Formatter().format(new DayShort()); + expect(tree).to.deep.equal({ + "w:dayShort": {}, + }); + }); + }); +}); + +describe("MonthShort", () => { + describe("#constructor()", () => { + it("should create a MonthShort with correct root key", () => { + const tree = new Formatter().format(new MonthShort()); + expect(tree).to.deep.equal({ + "w:monthShort": {}, + }); + }); + }); +}); + +describe("YearShort", () => { + describe("#constructor()", () => { + it("should create a YearShort with correct root key", () => { + const tree = new Formatter().format(new YearShort()); + expect(tree).to.deep.equal({ + "w:yearShort": {}, + }); + }); + }); +}); + +describe("DayLong", () => { + describe("#constructor()", () => { + it("should create a DayLong with correct root key", () => { + const tree = new Formatter().format(new DayLong()); + expect(tree).to.deep.equal({ + "w:dayLong": {}, + }); + }); + }); +}); + +describe("MonthLong", () => { + describe("#constructor()", () => { + it("should create a MonthLong with correct root key", () => { + const tree = new Formatter().format(new MonthLong()); + expect(tree).to.deep.equal({ + "w:monthLong": {}, + }); + }); + }); +}); + +describe("YearLong", () => { + describe("#constructor()", () => { + it("should create a YearLong with correct root key", () => { + const tree = new Formatter().format(new YearLong()); + expect(tree).to.deep.equal({ + "w:yearLong": {}, + }); + }); + }); +}); + +describe("AnnotationReference", () => { + describe("#constructor()", () => { + it("should create a AnnotationReference with correct root key", () => { + const tree = new Formatter().format(new AnnotationReference()); + expect(tree).to.deep.equal({ + "w:annotationRef": {}, + }); + }); + }); +}); + +describe("FootnoteReferenceElement", () => { + describe("#constructor()", () => { + it("should create a FootnoteReferenceElement with correct root key", () => { + const tree = new Formatter().format(new FootnoteReferenceElement()); + expect(tree).to.deep.equal({ + "w:footnoteRef": {}, + }); + }); + }); +}); + +describe("EndnoteReference", () => { + describe("#constructor()", () => { + it("should create a EndnoteReference with correct root key", () => { + const tree = new Formatter().format(new EndnoteReference()); + expect(tree).to.deep.equal({ + "w:endnoteRef": {}, + }); + }); + }); +}); + +describe("Separator", () => { + describe("#constructor()", () => { + it("should create a Separator with correct root key", () => { + const tree = new Formatter().format(new Separator()); + expect(tree).to.deep.equal({ + "w:separator": {}, + }); + }); + }); +}); + +describe("ContinuationSeparator", () => { + describe("#constructor()", () => { + it("should create a ContinuationSeparator with correct root key", () => { + const tree = new Formatter().format(new ContinuationSeparator()); + expect(tree).to.deep.equal({ + "w:continuationSeparator": {}, + }); + }); + }); +}); + +describe("PageNumberElement", () => { + describe("#constructor()", () => { + it("should create a PageNumberElement with correct root key", () => { + const tree = new Formatter().format(new PageNumberElement()); + expect(tree).to.deep.equal({ + "w:pgNum": {}, + }); + }); + }); +}); + +describe("CarriageReturn", () => { + describe("#constructor()", () => { + it("should create a CarriageReturn with correct root key", () => { + const tree = new Formatter().format(new CarriageReturn()); + expect(tree).to.deep.equal({ + "w:cr": {}, + }); + }); + }); +}); + +describe("Tab", () => { + describe("#constructor()", () => { + it("should create a Tab with correct root key", () => { + const tree = new Formatter().format(new Tab()); + expect(tree).to.deep.equal({ + "w:tab": {}, + }); + }); + }); +}); + +describe("LastRenderedPageBreak", () => { + describe("#constructor()", () => { + it("should create a LastRenderedPageBreak with correct root key", () => { + const tree = new Formatter().format(new LastRenderedPageBreak()); + expect(tree).to.deep.equal({ + "w:lastRenderedPageBreak": {}, + }); + }); + }); +}); diff --git a/src/file/paragraph/run/empty-children.ts b/src/file/paragraph/run/empty-children.ts new file mode 100644 index 0000000000..b72ba255d6 --- /dev/null +++ b/src/file/paragraph/run/empty-children.ts @@ -0,0 +1,127 @@ +import { EmptyElement } from "@file/xml-components"; + +// +// ... +// +// +// +// +// +// +// +// +// +// +// +// +// +// ... +// +// +// +// ... +// +// +// + +export class NoBreakHyphen extends EmptyElement { + public constructor() { + super("w:noBreakHyphen"); + } +} + +export class SoftHyphen extends EmptyElement { + public constructor() { + super("w:softHyphen"); + } +} + +export class DayShort extends EmptyElement { + public constructor() { + super("w:dayShort"); + } +} + +export class MonthShort extends EmptyElement { + public constructor() { + super("w:monthShort"); + } +} + +export class YearShort extends EmptyElement { + public constructor() { + super("w:yearShort"); + } +} + +export class DayLong extends EmptyElement { + public constructor() { + super("w:dayLong"); + } +} + +export class MonthLong extends EmptyElement { + public constructor() { + super("w:monthLong"); + } +} + +export class YearLong extends EmptyElement { + public constructor() { + super("w:yearLong"); + } +} + +export class AnnotationReference extends EmptyElement { + public constructor() { + super("w:annotationRef"); + } +} + +export class FootnoteReferenceElement extends EmptyElement { + public constructor() { + super("w:footnoteRef"); + } +} + +export class EndnoteReference extends EmptyElement { + public constructor() { + super("w:endnoteRef"); + } +} + +export class Separator extends EmptyElement { + public constructor() { + super("w:separator"); + } +} + +export class ContinuationSeparator extends EmptyElement { + public constructor() { + super("w:continuationSeparator"); + } +} + +export class PageNumberElement extends EmptyElement { + public constructor() { + super("w:pgNum"); + } +} + +export class CarriageReturn extends EmptyElement { + public constructor() { + super("w:cr"); + } +} + +export class Tab extends EmptyElement { + public constructor() { + super("w:tab"); + } +} + +export class LastRenderedPageBreak extends EmptyElement { + public constructor() { + super("w:lastRenderedPageBreak"); + } +} diff --git a/src/file/paragraph/run/index.ts b/src/file/paragraph/run/index.ts index 34819e2c02..f459256c59 100644 --- a/src/file/paragraph/run/index.ts +++ b/src/file/paragraph/run/index.ts @@ -7,6 +7,7 @@ export * from "./run-fonts"; export * from "./sequential-identifier"; export * from "./underline"; export * from "./emphasis-mark"; -export * from "./tab"; export * from "./simple-field"; export * from "./comment-run"; +export * from "./empty-children"; +export * from "./positional-tab"; diff --git a/src/file/paragraph/run/positional-tab.spec.ts b/src/file/paragraph/run/positional-tab.spec.ts new file mode 100644 index 0000000000..56fe20c273 --- /dev/null +++ b/src/file/paragraph/run/positional-tab.spec.ts @@ -0,0 +1,27 @@ +import { expect } from "chai"; + +import { Formatter } from "@export/formatter"; + +import { PositionalTab, PositionalTabAlignment, PositionalTabLeader, PositionalTabRelativeTo } from "./positional-tab"; + +describe("PositionalTab", () => { + it("should create a PositionalTab with correct root key", () => { + const tree = new Formatter().format( + new PositionalTab({ + alignment: PositionalTabAlignment.CENTER, + relativeTo: PositionalTabRelativeTo.MARGIN, + leader: PositionalTabLeader.DOT, + }), + ); + + expect(tree).to.deep.equal({ + "w:ptab": { + _attr: { + "w:alignment": "center", + "w:relativeTo": "margin", + "w:leader": "dot", + }, + }, + }); + }); +}); diff --git a/src/file/paragraph/run/positional-tab.ts b/src/file/paragraph/run/positional-tab.ts new file mode 100644 index 0000000000..e3b002ed81 --- /dev/null +++ b/src/file/paragraph/run/positional-tab.ts @@ -0,0 +1,80 @@ +import { NextAttributeComponent, XmlComponent } from "@file/xml-components"; + +// +// +// +// +// +// +// +export enum PositionalTabAlignment { + LEFT = "left", + CENTER = "center", + RIGHT = "right", +} + +// +// +// +// +// +// +export enum PositionalTabRelativeTo { + MARGIN = "margin", + INDENT = "indent", +} + +// +// +// +// +// +// +// +// +// +export enum PositionalTabLeader { + NONE = "none", + DOT = "dot", + HYPHEN = "hyphen", + UNDERSCORE = "underscore", + MIDDLE_DOT = "middleDot", +} + +export interface PositionalTabOptions { + readonly alignment: PositionalTabAlignment; + readonly relativeTo: PositionalTabRelativeTo; + readonly leader: PositionalTabLeader; +} + +// +// +// +// +// +export class PositionalTab extends XmlComponent { + public constructor(options: PositionalTabOptions) { + super("w:ptab"); + + this.root.push( + new NextAttributeComponent<{ + readonly alignment: PositionalTabAlignment; + readonly relativeTo: PositionalTabRelativeTo; + readonly leader: PositionalTabLeader; + }>({ + alignment: { + key: "w:alignment", + value: options.alignment, + }, + relativeTo: { + key: "w:relativeTo", + value: options.relativeTo, + }, + leader: { + key: "w:leader", + value: options.leader, + }, + }), + ); + } +} diff --git a/src/file/paragraph/run/run.ts b/src/file/paragraph/run/run.ts index 4fb047e0c7..023fff404e 100644 --- a/src/file/paragraph/run/run.ts +++ b/src/file/paragraph/run/run.ts @@ -9,10 +9,91 @@ import { Begin, End, Separate } from "./field"; import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number"; import { IRunPropertiesOptions, RunProperties } from "./properties"; import { Text } from "./run-components/text"; -import { Tab } from "./tab"; +import { + AnnotationReference, + CarriageReturn, + ContinuationSeparator, + DayLong, + DayShort, + EndnoteReference, + FootnoteReferenceElement, + LastRenderedPageBreak, + MonthLong, + MonthShort, + NoBreakHyphen, + PageNumberElement, + Separator, + SoftHyphen, + Tab, + YearLong, + YearShort, +} from "./empty-children"; +import { PositionalTab } from "./positional-tab"; export interface IRunOptions extends IRunPropertiesOptions { - readonly children?: readonly (Begin | FieldInstruction | Separate | End | PageNumber | FootnoteReferenceRun | Tab | string)[]; + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + readonly children?: readonly ( + | Begin + | FieldInstruction + | Separate + | End + | PageNumber + | FootnoteReferenceRun + | Break + | AnnotationReference + | CarriageReturn + | ContinuationSeparator + | DayLong + | DayShort + | EndnoteReference + | FootnoteReferenceElement + | LastRenderedPageBreak + | MonthLong + | MonthShort + | NoBreakHyphen + | PageNumberElement + | Separator + | SoftHyphen + | Tab + | YearLong + | YearShort + | PositionalTab + | string + )[]; readonly break?: number; readonly text?: string; } diff --git a/src/file/paragraph/run/tab.spec.ts b/src/file/paragraph/run/tab.spec.ts deleted file mode 100644 index 03381200a5..0000000000 --- a/src/file/paragraph/run/tab.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { expect } from "chai"; - -import { Formatter } from "@export/formatter"; - -import { Tab } from "./tab"; - -describe("Tab", () => { - let tab: Tab; - - beforeEach(() => { - tab = new Tab(); - }); - - describe("#constructor()", () => { - it("should create a Tab with correct root key", () => { - const tree = new Formatter().format(tab); - expect(tree).to.deep.equal({ - "w:tab": {}, - }); - }); - }); -}); diff --git a/src/file/paragraph/run/tab.ts b/src/file/paragraph/run/tab.ts deleted file mode 100644 index 061e3deb3a..0000000000 --- a/src/file/paragraph/run/tab.ts +++ /dev/null @@ -1,14 +0,0 @@ -// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_tab_topic_ID0EM6AO.html -import { XmlComponent } from "@file/xml-components"; - -// -// ... -// -// -// TODO: this is unused and undocumented currently. -// I think the intended use was for users to import, and insert as a child of `Run`. -export class Tab extends XmlComponent { - public constructor() { - super("w:tab"); - } -} diff --git a/src/file/xml-components/simple-elements.ts b/src/file/xml-components/simple-elements.ts index 8f4b3f5e90..b81901232c 100644 --- a/src/file/xml-components/simple-elements.ts +++ b/src/file/xml-components/simple-elements.ts @@ -35,6 +35,11 @@ export class HpsMeasureElement extends XmlComponent { // This represents element type CT_String, which indicate a string value. // +// +export class EmptyElement extends XmlComponent {} + +// This represents element type CT_Empty, which indicate aan empty element. +// // // //