#940 - Add positional tab feature

Also add extra run elements as per spec
This commit is contained in:
Dolan Miu
2022-12-24 19:32:44 +00:00
parent 49b4ca67e0
commit 11bebd42ac
15 changed files with 891 additions and 164 deletions

View File

@ -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

View File

@ -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,
}),
],

View File

@ -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);
});

View File

@ -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)

View File

@ -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.

184
docs/usage/tabs.md Normal file
View File

@ -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` |

View File

@ -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";
// <xsd:element name="noBreakHyphen" type="CT_Empty"/>
// <xsd:element name="softHyphen" type="CT_Empty" minOccurs="0" />
// <xsd:element name="dayShort" type="CT_Empty" minOccurs="0" />
// <xsd:element name="monthShort" type="CT_Empty" minOccurs="0" />
// <xsd:element name="yearShort" type="CT_Empty" minOccurs="0" />
// <xsd:element name="dayLong" type="CT_Empty" minOccurs="0" />
// <xsd:element name="monthLong" type="CT_Empty" minOccurs="0" />
// <xsd:element name="yearLong" type="CT_Empty" minOccurs="0" />
// <xsd:element name="annotationRef" type="CT_Empty" minOccurs="0" />
// <xsd:element name="footnoteRef" type="CT_Empty" minOccurs="0" />
// <xsd:element name="endnoteRef" type="CT_Empty" minOccurs="0" />
// <xsd:element name="separator" type="CT_Empty" minOccurs="0" />
// <xsd:element name="continuationSeparator" type="CT_Empty" minOccurs="0" />
// ...
// <xsd:element name="pgNum" type="CT_Empty" minOccurs="0" />
// <xsd:element name="cr" type="CT_Empty" minOccurs="0" />
// <xsd:element name="tab" type="CT_Empty" minOccurs="0" />
// ...
// <xsd:element name="lastRenderedPageBreak" type="CT_Empty" minOccurs="0" maxOccurs="1" />
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": {},
});
});
});
});

View File

@ -0,0 +1,127 @@
import { EmptyElement } from "@file/xml-components";
// <xsd:group name="EG_RunInnerContent">
// ...
// <xsd:element name="noBreakHyphen" type="CT_Empty"/>
// <xsd:element name="softHyphen" type="CT_Empty" minOccurs="0" />
// <xsd:element name="dayShort" type="CT_Empty" minOccurs="0" />
// <xsd:element name="monthShort" type="CT_Empty" minOccurs="0" />
// <xsd:element name="yearShort" type="CT_Empty" minOccurs="0" />
// <xsd:element name="dayLong" type="CT_Empty" minOccurs="0" />
// <xsd:element name="monthLong" type="CT_Empty" minOccurs="0" />
// <xsd:element name="yearLong" type="CT_Empty" minOccurs="0" />
// <xsd:element name="annotationRef" type="CT_Empty" minOccurs="0" />
// <xsd:element name="footnoteRef" type="CT_Empty" minOccurs="0" />
// <xsd:element name="endnoteRef" type="CT_Empty" minOccurs="0" />
// <xsd:element name="separator" type="CT_Empty" minOccurs="0" />
// <xsd:element name="continuationSeparator" type="CT_Empty" minOccurs="0" />
// ...
// <xsd:element name="pgNum" type="CT_Empty" minOccurs="0" />
// <xsd:element name="cr" type="CT_Empty" minOccurs="0" />
// <xsd:element name="tab" type="CT_Empty" minOccurs="0" />
// ...
// <xsd:element name="lastRenderedPageBreak" type="CT_Empty" minOccurs="0" maxOccurs="1" />
// </xsd:choice>
// </xsd:group>
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");
}
}

View File

@ -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";

View File

@ -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",
},
},
});
});
});

View File

@ -0,0 +1,80 @@
import { NextAttributeComponent, XmlComponent } from "@file/xml-components";
// <xsd:simpleType name="ST_PTabAlignment">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="left" />
// <xsd:enumeration value="center" />
// <xsd:enumeration value="right" />
// </xsd:restriction>
// </xsd:simpleType>
export enum PositionalTabAlignment {
LEFT = "left",
CENTER = "center",
RIGHT = "right",
}
// <xsd:simpleType name="ST_PTabRelativeTo">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="margin" />
// <xsd:enumeration value="indent" />
// </xsd:restriction>
// </xsd:simpleType>
export enum PositionalTabRelativeTo {
MARGIN = "margin",
INDENT = "indent",
}
// <xsd:simpleType name="ST_PTabLeader">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="none" />
// <xsd:enumeration value="dot" />
// <xsd:enumeration value="hyphen" />
// <xsd:enumeration value="underscore" />
// <xsd:enumeration value="middleDot" />
// </xsd:restriction>
// </xsd:simpleType>
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;
}
// <xsd:complexType name="CT_PTab">
// <xsd:attribute name="alignment" type="ST_PTabAlignment" use="required" />
// <xsd:attribute name="relativeTo" type="ST_PTabRelativeTo" use="required" />
// <xsd:attribute name="leader" type="ST_PTabLeader" use="required" />
// </xsd:complexType>
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,
},
}),
);
}
}

View File

@ -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)[];
// <xsd:choice>
// <xsd:element name="br" type="CT_Br" />
// <xsd:element name="t" type="CT_Text" />
// <xsd:element name="contentPart" type="CT_Rel" />
// <xsd:element name="delText" type="CT_Text" />
// <xsd:element name="instrText" type="CT_Text" />
// <xsd:element name="delInstrText" type="CT_Text" />
// <xsd:element name="noBreakHyphen" type="CT_Empty" />
// <xsd:element name="softHyphen" type="CT_Empty" minOccurs="0" />
// <xsd:element name="dayShort" type="CT_Empty" minOccurs="0" />
// <xsd:element name="monthShort" type="CT_Empty" minOccurs="0" />
// <xsd:element name="yearShort" type="CT_Empty" minOccurs="0" />
// <xsd:element name="dayLong" type="CT_Empty" minOccurs="0" />
// <xsd:element name="monthLong" type="CT_Empty" minOccurs="0" />
// <xsd:element name="yearLong" type="CT_Empty" minOccurs="0" />
// <xsd:element name="annotationRef" type="CT_Empty" minOccurs="0" />
// <xsd:element name="footnoteRef" type="CT_Empty" minOccurs="0" />
// <xsd:element name="endnoteRef" type="CT_Empty" minOccurs="0" />
// <xsd:element name="separator" type="CT_Empty" minOccurs="0" />
// <xsd:element name="continuationSeparator" type="CT_Empty" minOccurs="0" />
// <xsd:element name="sym" type="CT_Sym" minOccurs="0" />
// <xsd:element name="pgNum" type="CT_Empty" minOccurs="0" />
// <xsd:element name="cr" type="CT_Empty" minOccurs="0" />
// <xsd:element name="tab" type="CT_Empty" minOccurs="0" />
// <xsd:element name="object" type="CT_Object" />
// <xsd:element name="pict" type="CT_Picture" />
// <xsd:element name="fldChar" type="CT_FldChar" />
// <xsd:element name="ruby" type="CT_Ruby" />
// <xsd:element name="footnoteReference" type="CT_FtnEdnRef" />
// <xsd:element name="endnoteReference" type="CT_FtnEdnRef" />
// <xsd:element name="commentReference" type="CT_Markup" />
// <xsd:element name="drawing" type="CT_Drawing" />
// <xsd:element name="ptab" type="CT_PTab" minOccurs="0" />
// <xsd:element name="lastRenderedPageBreak" type="CT_Empty" minOccurs="0" maxOccurs="1" />
// </xsd:choice>
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;
}

View File

@ -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": {},
});
});
});
});

View File

@ -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";
// <xsd:group name="EG_RunInnerContent">
// ...
// <xsd:element name="tab" type="CT_Empty" minOccurs="0"/>
//
// 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");
}
}

View File

@ -35,6 +35,11 @@ export class HpsMeasureElement extends XmlComponent {
// This represents element type CT_String, which indicate a string value.
//
// <xsd:complexType name="CT_Empty"/>
export class EmptyElement extends XmlComponent {}
// This represents element type CT_Empty, which indicate aan empty element.
//
// <xsd:complexType name="CT_String">
// <xsd:attribute name="val" type="s:ST_String" use="required"/>
// </xsd:complexType>