#940 - Add positional tab feature
Also add extra run elements as per spec
This commit is contained in:
90
.github/workflows/demos.yml
vendored
90
.github/workflows/demos.yml
vendored
@ -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
|
||||
|
@ -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,
|
||||
}),
|
||||
],
|
||||
|
60
demo/84-positional-tabs.ts
Normal file
60
demo/84-positional-tabs.ts
Normal 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);
|
||||
});
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||

|
||||
|
||||
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
184
docs/usage/tabs.md
Normal 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)
|
||||
|
||||

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

|
||||
|
||||
### 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` |
|
229
src/file/paragraph/run/empty-children.spec.ts
Normal file
229
src/file/paragraph/run/empty-children.spec.ts
Normal 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": {},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
127
src/file/paragraph/run/empty-children.ts
Normal file
127
src/file/paragraph/run/empty-children.ts
Normal 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");
|
||||
}
|
||||
}
|
@ -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";
|
||||
|
27
src/file/paragraph/run/positional-tab.spec.ts
Normal file
27
src/file/paragraph/run/positional-tab.spec.ts
Normal 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",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
80
src/file/paragraph/run/positional-tab.ts
Normal file
80
src/file/paragraph/run/positional-tab.ts
Normal 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,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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": {},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -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");
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
Reference in New Issue
Block a user