Merge branch 'master' into custom-properties

This commit is contained in:
Dolan
2021-03-02 00:01:03 +00:00
committed by GitHub
205 changed files with 6259 additions and 1047 deletions

View File

@ -0,0 +1,18 @@
import { expect } from "chai";
import { convertInchesToTwip, convertMillimetersToTwip } from "./convenience-functions";
describe("Utility", () => {
describe("#convertMillimetersToTwip", () => {
it("should call the underlying header's addChildElement for Paragraph", () => {
expect(convertMillimetersToTwip(1000)).to.equal(56692);
});
});
describe("#convertInchesToTwip", () => {
it("should call the underlying header's addChildElement", () => {
expect(convertInchesToTwip(1)).to.equal(1440);
expect(convertInchesToTwip(0.5)).to.equal(720);
expect(convertInchesToTwip(0.25)).to.equal(360);
});
});
});

View File

@ -0,0 +1,8 @@
// Twip - twentieths of a point
export const convertMillimetersToTwip = (millimeters: number): number => {
return Math.floor((millimeters / 25.4) * 72 * 20);
};
export const convertInchesToTwip = (inches: number): number => {
return Math.floor(inches * 72 * 20);
};

View File

@ -1,8 +1,8 @@
import { IViewWrapper } from "file/document-wrapper";
import { BaseXmlComponent, IXmlableObject } from "file/xml-components";
import { File } from "../file";
export class Formatter {
public format(input: BaseXmlComponent, file?: File): IXmlableObject {
public format(input: BaseXmlComponent, file?: IViewWrapper): IXmlableObject {
const output = input.prepForXml(file);
if (output) {

View File

@ -70,23 +70,23 @@ export class Compiler {
private xmlifyFile(file: File, prettify?: boolean): IXmlifyedFileMapping {
file.verifyUpdateFields();
const documentRelationshipCount = file.DocumentRelationships.RelationshipCount + 1;
const documentRelationshipCount = file.Document.Relationships.RelationshipCount + 1;
const documentXmlData = xml(this.formatter.format(file.Document, file), prettify);
const documentXmlData = xml(this.formatter.format(file.Document.View, file.Document), prettify);
const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media);
return {
Relationships: {
data: (() => {
documentMediaDatas.forEach((mediaData, i) => {
file.DocumentRelationships.createRelationship(
file.Document.Relationships.createRelationship(
documentRelationshipCount + i,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
});
return xml(this.formatter.format(file.DocumentRelationships, file), prettify);
return xml(this.formatter.format(file.Document.Relationships, file.Document), prettify);
})(),
path: "word/_rels/document.xml.rels",
},
@ -100,11 +100,11 @@ export class Compiler {
path: "word/document.xml",
},
Styles: {
data: xml(this.formatter.format(file.Styles, file), prettify),
data: xml(this.formatter.format(file.Styles, file.Document), prettify),
path: "word/styles.xml",
},
Properties: {
data: xml(this.formatter.format(file.CoreProperties, file), {
data: xml(this.formatter.format(file.CoreProperties, file.Document), {
declaration: {
standalone: "yes",
encoding: "UTF-8",
@ -113,15 +113,15 @@ export class Compiler {
path: "docProps/core.xml",
},
Numbering: {
data: xml(this.formatter.format(file.Numbering, file), prettify),
data: xml(this.formatter.format(file.Numbering, file.Document), prettify),
path: "word/numbering.xml",
},
FileRelationships: {
data: xml(this.formatter.format(file.FileRelationships, file), prettify),
data: xml(this.formatter.format(file.FileRelationships, file.Document), prettify),
path: "_rels/.rels",
},
HeaderRelationships: file.Headers.map((headerWrapper, index) => {
const xmlData = xml(this.formatter.format(headerWrapper.Header, file), prettify);
const xmlData = xml(this.formatter.format(headerWrapper.View, headerWrapper), prettify);
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
mediaDatas.forEach((mediaData, i) => {
@ -133,12 +133,12 @@ export class Compiler {
});
return {
data: xml(this.formatter.format(headerWrapper.Relationships, file), prettify),
data: xml(this.formatter.format(headerWrapper.Relationships, headerWrapper), prettify),
path: `word/_rels/header${index + 1}.xml.rels`,
};
}),
FooterRelationships: file.Footers.map((footerWrapper, index) => {
const xmlData = xml(this.formatter.format(footerWrapper.Footer, file), prettify);
const xmlData = xml(this.formatter.format(footerWrapper.View, footerWrapper), prettify);
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
mediaDatas.forEach((mediaData, i) => {
@ -150,12 +150,12 @@ export class Compiler {
});
return {
data: xml(this.formatter.format(footerWrapper.Relationships, file), prettify),
data: xml(this.formatter.format(footerWrapper.Relationships, footerWrapper), prettify),
path: `word/_rels/footer${index + 1}.xml.rels`,
};
}),
Headers: file.Headers.map((headerWrapper, index) => {
const tempXmlData = xml(this.formatter.format(headerWrapper.Header, file), prettify);
const tempXmlData = xml(this.formatter.format(headerWrapper.View, headerWrapper), prettify);
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
// TODO: 0 needs to be changed when headers get relationships of their own
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
@ -166,7 +166,7 @@ export class Compiler {
};
}),
Footers: file.Footers.map((footerWrapper, index) => {
const tempXmlData = xml(this.formatter.format(footerWrapper.Footer, file), prettify);
const tempXmlData = xml(this.formatter.format(footerWrapper.View, footerWrapper), prettify);
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
// TODO: 0 needs to be changed when headers get relationships of their own
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
@ -177,7 +177,7 @@ export class Compiler {
};
}),
ContentTypes: {
data: xml(this.formatter.format(file.ContentTypes, file), prettify),
data: xml(this.formatter.format(file.ContentTypes, file.Document), prettify),
path: "[Content_Types].xml",
},
CustomProperties: {
@ -185,15 +185,15 @@ export class Compiler {
path: "docProps/custom.xml",
},
AppProperties: {
data: xml(this.formatter.format(file.AppProperties, file), prettify),
data: xml(this.formatter.format(file.AppProperties, file.Document), prettify),
path: "docProps/app.xml",
},
FootNotes: {
data: xml(this.formatter.format(file.FootNotes, file), prettify),
data: xml(this.formatter.format(file.FootNotes, file.Document), prettify),
path: "word/footnotes.xml",
},
Settings: {
data: xml(this.formatter.format(file.Settings, file), prettify),
data: xml(this.formatter.format(file.Settings, file.Document), prettify),
path: "word/settings.xml",
},
};

View File

@ -1,22 +1,12 @@
import { XmlComponent } from "file/xml-components";
import { IDocumentBackgroundOptions } from "../document";
import { DocumentAttributes } from "../document/document-attributes";
import { INumberingOptions } from "../numbering";
import { HyperlinkType, Paragraph } from "../paragraph";
import { Paragraph } from "../paragraph";
import { IStylesOptions } from "../styles";
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
export interface IInternalHyperlinkDefinition {
readonly text: string;
readonly type: HyperlinkType.INTERNAL;
}
export interface IExternalHyperlinkDefinition {
readonly link: string;
readonly text: string;
readonly type: HyperlinkType.EXTERNAL;
}
export interface IPropertiesOptions {
readonly title?: string;
readonly subject?: string;
@ -29,9 +19,11 @@ export interface IPropertiesOptions {
readonly styles?: IStylesOptions;
readonly numbering?: INumberingOptions;
readonly footnotes?: Paragraph[];
readonly hyperlinks?: {
readonly [key: string]: IInternalHyperlinkDefinition | IExternalHyperlinkDefinition;
readonly background?: IDocumentBackgroundOptions;
readonly features?: {
readonly trackRevisions?: boolean;
};
readonly compatabilityModeVersion?: number;
}
export class CoreProperties extends XmlComponent {

View File

@ -0,0 +1,50 @@
import { expect } from "chai";
import * as sinon from "sinon";
import { FooterWrapper } from "./footer-wrapper";
import { Media } from "./media";
import { Paragraph } from "./paragraph";
import { Table, TableCell, TableRow } from "./table";
describe("FooterWrapper", () => {
describe("#add", () => {
it("should call the underlying footer's addParagraph", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.View, "add");
file.add(new Paragraph({}));
expect(spy.called).to.equal(true);
});
it("should call the underlying footer's addParagraph", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.View, "add");
file.add(
new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("hello")],
}),
],
}),
],
}),
);
expect(spy.called).to.equal(true);
});
});
describe("#addChildElement", () => {
it("should call the underlying footer's addChildElement", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.View, "addChildElement");
// tslint:disable-next-line:no-any
file.addChildElement({} as any);
expect(spy.called).to.equal(true);
});
});
});

View File

@ -0,0 +1,27 @@
import { Document, IDocumentOptions } from "./document";
import { Footer } from "./footer";
import { Header } from "./header/header";
import { Relationships } from "./relationships";
export interface IViewWrapper {
readonly View: Document | Footer | Header;
readonly Relationships: Relationships;
}
export class DocumentWrapper implements IViewWrapper {
private readonly document: Document;
private readonly relationships: Relationships;
constructor(options: IDocumentOptions) {
this.document = new Document(options);
this.relationships = new Relationships();
}
public get View(): Document {
return this.document;
}
public get Relationships(): Relationships {
return this.relationships;
}
}

View File

@ -1,6 +1,7 @@
import { IViewWrapper } from "file/document-wrapper";
import { IXmlableObject, XmlComponent } from "file/xml-components";
import { Paragraph, ParagraphProperties, TableOfContents } from "../..";
import { File } from "../../../file";
import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties";
export class Body extends XmlComponent {
@ -25,7 +26,7 @@ export class Body extends XmlComponent {
this.sections.push(new SectionProperties(options));
}
public prepForXml(file?: File): IXmlableObject | undefined {
public prepForXml(file?: IViewWrapper): IXmlableObject | undefined {
if (this.sections.length === 1) {
this.root.splice(0, 1);
this.root.push(this.sections.pop() as SectionProperties);

View File

@ -6,3 +6,4 @@ export * from "./page-number";
export * from "./page-border";
export * from "./line-number";
export * from "./vertical-align";
export * from "./type";

View File

@ -1,5 +1,6 @@
import { expect } from "chai";
import { convertInchesToTwip } from "convenience-functions";
import { Formatter } from "export/formatter";
import { FooterWrapper } from "file/footer-wrapper";
import { HeaderWrapper } from "file/header-wrapper";
@ -8,6 +9,7 @@ import { Media } from "file/media";
import { PageBorderOffsetFrom } from "./page-border";
import { PageNumberFormat } from "./page-number";
import { SectionProperties } from "./section-properties";
import { SectionType } from "./type/section-type-attributes";
import { SectionVerticalAlignValue } from "./vertical-align";
describe("SectionProperties", () => {
@ -18,10 +20,10 @@ describe("SectionProperties", () => {
const properties = new SectionProperties({
width: 11906,
height: 16838,
top: 1440,
right: 1440,
bottom: 1440,
left: 1440,
top: convertInchesToTwip(1),
right: convertInchesToTwip(1),
bottom: convertInchesToTwip(1),
left: convertInchesToTwip(1),
header: 708,
footer: 708,
gutter: 0,
@ -30,7 +32,7 @@ describe("SectionProperties", () => {
space: 708,
count: 1,
},
linePitch: 360,
linePitch: convertInchesToTwip(0.25),
headers: {
default: new HeaderWrapper(media, 100),
},
@ -198,5 +200,17 @@ describe("SectionProperties", () => {
const pgNumType = tree["w:sectPr"].find((item) => item["w:pgNumType"] !== undefined);
expect(pgNumType).to.equal(undefined);
});
it("should create section properties with section type", () => {
const properties = new SectionProperties({
type: SectionType.CONTINUOUS,
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
const type = tree["w:sectPr"].find((item) => item["w:type"] !== undefined);
expect(type).to.deep.equal({
"w:type": { _attr: { "w:val": "continuous" } },
});
});
});
});

View File

@ -1,4 +1,5 @@
// http://officeopenxml.com/WPsection.php
import { convertInchesToTwip } from "convenience-functions";
import { FooterWrapper } from "file/footer-wrapper";
import { HeaderWrapper } from "file/header-wrapper";
import { XmlComponent } from "file/xml-components";
@ -18,6 +19,8 @@ import { IPageNumberTypeAttributes, PageNumberType } from "./page-number";
import { PageSize } from "./page-size/page-size";
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
import { TitlePage } from "./title-page/title-page";
import { Type } from "./type/section-type";
import { SectionType } from "./type/section-type-attributes";
import { ISectionVerticalAlignAttributes, SectionVerticalAlign } from "./vertical-align";
export interface IHeaderFooterGroup<T> {
@ -52,6 +55,7 @@ export type SectionPropertiesOptions = IPageSizeAttributes &
readonly space?: number;
readonly count?: number;
};
readonly type?: SectionType;
};
// Need to decouple this from the attributes
@ -64,10 +68,10 @@ export class SectionProperties extends XmlComponent {
const {
width = 11906,
height = 16838,
top = 1440,
right = 1440,
bottom = 1440,
left = 1440,
top = convertInchesToTwip(1),
right = convertInchesToTwip(1),
bottom = convertInchesToTwip(1),
left = convertInchesToTwip(1),
header = 708,
footer = 708,
gutter = 0,
@ -90,6 +94,7 @@ export class SectionProperties extends XmlComponent {
pageBorderLeft,
titlePage = false,
verticalAlign,
type,
} = options;
this.options = options;
@ -128,6 +133,10 @@ export class SectionProperties extends XmlComponent {
if (verticalAlign) {
this.root.push(new SectionVerticalAlign(verticalAlign));
}
if (type) {
this.root.push(new Type(type));
}
}
private addHeaders(headers?: IHeaderFooterGroup<HeaderWrapper>): void {
@ -136,7 +145,7 @@ export class SectionProperties extends XmlComponent {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.DEFAULT,
headerId: headers.default.Header.ReferenceId,
headerId: headers.default.View.ReferenceId,
}),
);
}
@ -145,7 +154,7 @@ export class SectionProperties extends XmlComponent {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.FIRST,
headerId: headers.first.Header.ReferenceId,
headerId: headers.first.View.ReferenceId,
}),
);
}
@ -154,7 +163,7 @@ export class SectionProperties extends XmlComponent {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.EVEN,
headerId: headers.even.Header.ReferenceId,
headerId: headers.even.View.ReferenceId,
}),
);
}
@ -167,7 +176,7 @@ export class SectionProperties extends XmlComponent {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.DEFAULT,
footerId: footers.default.Footer.ReferenceId,
footerId: footers.default.View.ReferenceId,
}),
);
}
@ -176,7 +185,7 @@ export class SectionProperties extends XmlComponent {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.FIRST,
footerId: footers.first.Footer.ReferenceId,
footerId: footers.first.View.ReferenceId,
}),
);
}
@ -185,7 +194,7 @@ export class SectionProperties extends XmlComponent {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.EVEN,
footerId: footers.even.Footer.ReferenceId,
footerId: footers.even.View.ReferenceId,
}),
);
}

View File

@ -0,0 +1,2 @@
export * from "./section-type";
export * from "./section-type-attributes";

View File

@ -0,0 +1,17 @@
import { XmlAttributeComponent } from "file/xml-components";
export enum SectionType {
CONTINUOUS = "continuous",
EVEN_PAGE = "evenPage",
NEXT_COLUMN = "nextColumn",
NEXT_PAGE = "nextPage",
ODD_PAGE = "oddPage",
}
export class SectionTypeAttributes extends XmlAttributeComponent<{
readonly val: SectionType;
}> {
protected readonly xmlKeys = {
val: "w:val",
};
}

View File

@ -0,0 +1,35 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { Type } from "./section-type";
import { SectionType } from "./section-type-attributes";
describe("Type", () => {
it("should create with even page section type", () => {
const sectionType = new Type(SectionType.EVEN_PAGE);
const tree = new Formatter().format(sectionType);
expect(tree).to.deep.equal({
"w:type": {
_attr: {
"w:val": "evenPage",
},
},
});
});
it("should create with continuous section type", () => {
const sectionType = new Type(SectionType.CONTINUOUS);
const tree = new Formatter().format(sectionType);
expect(tree).to.deep.equal({
"w:type": {
_attr: {
"w:val": "continuous",
},
},
});
});
});

View File

@ -0,0 +1,10 @@
// http://officeopenxml.com/WPsection.php
import { XmlComponent } from "file/xml-components";
import { SectionType, SectionTypeAttributes } from "./section-type-attributes";
export class Type extends XmlComponent {
constructor(value: SectionType) {
super("w:type");
this.root.push(new SectionTypeAttributes({ val: value }));
}
}

View File

@ -0,0 +1,53 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { DocumentBackground } from "./document-background";
describe("DocumentBackground", () => {
describe("#constructor()", () => {
it("should create a DocumentBackground with no options and set color to auto", () => {
const documentBackground = new DocumentBackground({});
const tree = new Formatter().format(documentBackground);
expect(tree).to.deep.equal({
"w:background": {
_attr: {
"w:color": "FFFFFF",
},
},
});
});
it("should create a DocumentBackground with no options and set color to value", () => {
const documentBackground = new DocumentBackground({ color: "ffff00" });
const tree = new Formatter().format(documentBackground);
expect(tree).to.deep.equal({
"w:background": {
_attr: {
"w:color": "ffff00",
},
},
});
});
it("should create a DocumentBackground with no options and set other values", () => {
const documentBackground = new DocumentBackground({
color: "ffff00",
themeColor: "test",
themeShade: "test",
themeTint: "test",
});
const tree = new Formatter().format(documentBackground);
expect(tree).to.deep.equal({
"w:background": {
_attr: {
"w:color": "ffff00",
"w:themeColor": "test",
"w:themeShade": "test",
"w:themeTint": "test",
},
},
});
});
});
});

View File

@ -0,0 +1,39 @@
// http://officeopenxml.com/WPdocument.php
// http://www.datypic.com/sc/ooxml/e-w_background-1.html
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export class DocumentBackgroundAttributes extends XmlAttributeComponent<{
readonly color: string;
readonly themeColor?: string;
readonly themeShade?: string;
readonly themeTint?: string;
}> {
protected readonly xmlKeys = {
color: "w:color",
themeColor: "w:themeColor",
themeShade: "w:themeShade",
themeTint: "w:themeTint",
};
}
export interface IDocumentBackgroundOptions {
readonly color?: string;
readonly themeColor?: string;
readonly themeShade?: string;
readonly themeTint?: string;
}
export class DocumentBackground extends XmlComponent {
constructor(options: IDocumentBackgroundOptions) {
super("w:background");
this.root.push(
new DocumentBackgroundAttributes({
color: options.color ? options.color : "FFFFFF",
themeColor: options.themeColor,
themeShade: options.themeShade,
themeTint: options.themeTint,
}),
);
}
}

View File

@ -0,0 +1 @@
export * from "./document-background";

View File

@ -8,7 +8,7 @@ describe("Document", () => {
let document: Document;
beforeEach(() => {
document = new Document();
document = new Document({ background: {} });
});
describe("#constructor()", () => {
@ -38,6 +38,13 @@ describe("Document", () => {
"mc:Ignorable": "w14 w15 wp14",
},
},
{
"w:background": {
_attr: {
"w:color": "FFFFFF",
},
},
},
{ "w:body": {} },
],
});

View File

@ -1,15 +1,20 @@
// http://officeopenxml.com/WPdocument.php
import { XmlComponent } from "file/xml-components";
import { Hyperlink, Paragraph } from "../paragraph";
import { ConcreteHyperlink, Paragraph } from "../paragraph";
import { Table } from "../table";
import { TableOfContents } from "../table-of-contents";
import { Body } from "./body";
import { DocumentAttributes } from "./document-attributes";
import { DocumentBackground, IDocumentBackgroundOptions } from "./document-background";
export interface IDocumentOptions {
readonly background: IDocumentBackgroundOptions;
}
export class Document extends XmlComponent {
private readonly body: Body;
constructor() {
constructor(options: IDocumentOptions) {
super("w:document");
this.root.push(
new DocumentAttributes({
@ -33,10 +38,11 @@ export class Document extends XmlComponent {
}),
);
this.body = new Body();
this.root.push(new DocumentBackground(options.background));
this.root.push(this.body);
}
public add(item: Paragraph | Table | TableOfContents | Hyperlink): Document {
public add(item: Paragraph | Table | TableOfContents | ConcreteHyperlink): Document {
this.body.push(item);
return this;
}

View File

@ -1,3 +1,4 @@
export * from "./document";
export * from "./document-attributes";
export * from "./body";
export * from "./document-background";

View File

@ -218,5 +218,150 @@ describe("Anchor", () => {
const textWrap = newJson.root[6];
assert.equal(textWrap.rootKey, "wp:wrapTopAndBottom");
});
it("should create a Drawing with a margin", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
margins: {
top: 10,
left: 10,
bottom: 10,
right: 10,
},
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
distT: 10,
distB: 10,
distL: 10,
distR: 10,
});
});
it("should create a Drawing with a default margin", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
margins: {},
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
distT: 0,
distB: 0,
distL: 0,
distR: 0,
});
});
it("should create a Drawing with allowOverlap being false", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
allowOverlap: false,
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
allowOverlap: "0",
});
});
it("should create a Drawing with behindDocument being true", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
behindDocument: true,
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
behindDoc: "1",
});
});
it("should create a Drawing with locked being true", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
lockAnchor: true,
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
locked: "1",
});
});
it("should create a Drawing with locked being false", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
layoutInCell: false,
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
layoutInCell: "0",
});
});
it("should create a Drawing with a certain z-index", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
zIndex: 120,
},
});
const newJson = Utility.jsonify(anchor);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
relativeHeight: 120,
});
});
});
});

View File

@ -11,42 +11,32 @@ import { Extent } from "./../extent/extent";
import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties";
import { AnchorAttributes } from "./anchor-attributes";
const defaultOptions: IFloating = {
allowOverlap: true,
behindDocument: false,
lockAnchor: false,
layoutInCell: true,
verticalPosition: {},
horizontalPosition: {},
};
export class Anchor extends XmlComponent {
constructor(mediaData: IMediaData, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) {
super("wp:anchor");
const floating = {
margins: {
top: 0,
bottom: 0,
left: 0,
right: 0,
},
...defaultOptions,
const floating: IFloating = {
allowOverlap: true,
behindDocument: false,
lockAnchor: false,
layoutInCell: true,
verticalPosition: {},
horizontalPosition: {},
...drawingOptions.floating,
};
this.root.push(
new AnchorAttributes({
distT: floating.margins.top || 0,
distB: floating.margins.bottom || 0,
distL: floating.margins.left || 0,
distR: floating.margins.right || 0,
distT: floating.margins ? floating.margins.top || 0 : 0,
distB: floating.margins ? floating.margins.bottom || 0 : 0,
distL: floating.margins ? floating.margins.left || 0 : 0,
distR: floating.margins ? floating.margins.right || 0 : 0,
simplePos: "0", // note: word doesn't fully support - so we use 0
allowOverlap: floating.allowOverlap === true ? "1" : "0",
behindDoc: floating.behindDocument === true ? "1" : "0",
locked: floating.lockAnchor === true ? "1" : "0",
layoutInCell: floating.layoutInCell === true ? "1" : "0",
relativeHeight: dimensions.emus.y,
relativeHeight: floating.zIndex ? floating.zIndex : dimensions.emus.y,
}),
);

View File

@ -1,4 +1,5 @@
// http://officeopenxml.com/drwPicFloating-position.php
// http://officeopenxml.com/drwPicFloating.php
import { ITextWrapping } from "../text-wrap";
export enum HorizontalPositionRelativeFrom {
@ -67,4 +68,5 @@ export interface IFloating {
readonly layoutInCell?: boolean;
readonly margins?: IMargins;
readonly wrap?: ITextWrapping;
readonly zIndex?: number;
}

View File

@ -12,7 +12,7 @@ export class Inline extends XmlComponent {
private readonly extent: Extent;
private readonly graphic: Graphic;
constructor(readonly mediaData: IMediaData, private readonly dimensions: IMediaDataDimensions) {
constructor(mediaData: IMediaData, private readonly dimensions: IMediaDataDimensions) {
super("wp:inline");
this.root.push(

View File

@ -5,7 +5,7 @@ import { Formatter } from "export/formatter";
import { File } from "./file";
import { Footer, Header } from "./header";
import { HyperlinkRef, Paragraph } from "./paragraph";
import { Paragraph } from "./paragraph";
import { Table, TableCell, TableRow } from "./table";
import { TableOfContents } from "./table-of-contents";
@ -18,7 +18,7 @@ describe("File", () => {
children: [],
});
const tree = new Formatter().format(doc.Document.Body);
const tree = new Formatter().format(doc.Document.View.Body);
expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][0]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default");
@ -37,7 +37,7 @@ describe("File", () => {
children: [],
});
const tree = new Formatter().format(doc.Document.Body);
const tree = new Formatter().format(doc.Document.View.Body);
expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][0]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default");
@ -56,7 +56,7 @@ describe("File", () => {
children: [],
});
const tree = new Formatter().format(doc.Document.Body);
const tree = new Formatter().format(doc.Document.View.Body);
expect(tree["w:body"][0]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first");
expect(tree["w:body"][0]["w:sectPr"][7]["w:footerReference"]._attr["w:type"]).to.equal("first");
@ -79,7 +79,7 @@ describe("File", () => {
children: [],
});
const tree = new Formatter().format(doc.Document.Body);
const tree = new Formatter().format(doc.Document.View.Body);
expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][0]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first");
@ -97,7 +97,7 @@ describe("File", () => {
},
]);
const tree = new Formatter().format(doc.Document.Body);
const tree = new Formatter().format(doc.Document.View.Body);
expect(tree).to.deep.equal({
"w:body": [
@ -164,22 +164,12 @@ describe("File", () => {
],
});
});
it("should add hyperlink child", () => {
const doc = new File(undefined, undefined, [
{
children: [new HyperlinkRef("test")],
},
]);
expect(doc.HyperlinkCache).to.deep.equal({});
});
});
describe("#addSection", () => {
it("should call the underlying document's add a Paragraph", () => {
const file = new File();
const spy = sinon.spy(file.Document, "add");
const spy = sinon.spy(file.Document.View, "add");
file.addSection({
children: [new Paragraph({})],
});
@ -187,19 +177,9 @@ describe("File", () => {
expect(spy.called).to.equal(true);
});
it("should add hyperlink child", () => {
const doc = new File();
doc.addSection({
children: [new HyperlinkRef("test")],
});
expect(doc.HyperlinkCache).to.deep.equal({});
});
it("should call the underlying document's add when adding a Table", () => {
const file = new File();
const spy = sinon.spy(file.Document, "add");
const spy = sinon.spy(file.Document.View, "add");
file.addSection({
children: [
new Table({
@ -221,7 +201,7 @@ describe("File", () => {
it("should call the underlying document's add when adding an Image (paragraph)", () => {
const file = new File();
const spy = sinon.spy(file.Document, "add");
const spy = sinon.spy(file.Document.View, "add");
// tslint:disable-next-line:no-any
file.addSection({
children: [new Paragraph("")],
@ -234,7 +214,7 @@ describe("File", () => {
describe("#addSection", () => {
it("should call the underlying document's add", () => {
const file = new File();
const spy = sinon.spy(file.Document, "add");
const spy = sinon.spy(file.Document.View, "add");
file.addSection({
children: [new TableOfContents()],
});
@ -243,11 +223,16 @@ describe("File", () => {
});
});
describe("#HyperlinkCache", () => {
it("should initially have empty hyperlink cache", () => {
const file = new File();
describe("#addTrackRevisionsFeature", () => {
it("should call the underlying document's add", () => {
const file = new File({
features: {
trackRevisions: true,
},
});
expect(file.HyperlinkCache).to.deep.equal({});
// tslint:disable-next-line: no-unused-expression no-string-literal
expect(file.Settings["trackRevisions"]).to.exist;
});
});

View File

@ -1,9 +1,8 @@
import * as shortid from "shortid";
import { AppProperties } from "./app-properties/app-properties";
import { ContentTypes } from "./content-types/content-types";
import { CoreProperties, IPropertiesOptions } from "./core-properties";
import { CustomProperties, ICustomPropertyOptions } from "./custom-properties";
import { Document } from "./document";
import { DocumentWrapper } from "./document-wrapper";
import {
FooterReferenceType,
HeaderReferenceType,
@ -18,9 +17,8 @@ import { Footer, Header } from "./header";
import { HeaderWrapper, IDocumentHeader } from "./header-wrapper";
import { Media } from "./media";
import { Numbering } from "./numbering";
import { Hyperlink, HyperlinkRef, HyperlinkType, Paragraph } from "./paragraph";
import { Paragraph } from "./paragraph";
import { Relationships } from "./relationships";
import { TargetModeType } from "./relationships/relationship/relationship";
import { Settings } from "./settings";
import { Styles } from "./styles";
import { ExternalStylesFactory } from "./styles/external-styles-factory";
@ -42,17 +40,16 @@ export interface ISectionOptions {
readonly size?: IPageSizeAttributes;
readonly margins?: IPageMarginAttributes;
readonly properties?: SectionPropertiesOptions;
readonly children: (Paragraph | Table | TableOfContents | HyperlinkRef)[];
readonly children: (Paragraph | Table | TableOfContents)[];
}
export class File {
// tslint:disable-next-line:readonly-keyword
private currentRelationshipId: number = 1;
private readonly document: Document;
private readonly documentWrapper: DocumentWrapper;
private readonly headers: IDocumentHeader[] = [];
private readonly footers: IDocumentFooter[] = [];
private readonly docRelationships: Relationships;
private readonly coreProperties: CoreProperties;
private readonly numbering: Numbering;
private readonly media: Media;
@ -63,7 +60,6 @@ export class File {
private readonly customProperties: CustomProperties;
private readonly appProperties: AppProperties;
private readonly styles: Styles;
private readonly hyperlinkCache: { readonly [key: string]: Hyperlink } = {};
constructor(
options: IPropertiesOptions = {
@ -83,14 +79,16 @@ export class File {
config: [],
},
);
this.docRelationships = new Relationships();
// this.documentWrapper.Relationships = new Relationships();
this.fileRelationships = new Relationships();
this.customProperties = new CustomProperties(customProperties);
this.appProperties = new AppProperties();
this.footNotes = new FootNotes();
this.contentTypes = new ContentTypes();
this.document = new Document();
this.settings = new Settings();
this.documentWrapper = new DocumentWrapper({ background: options.background || {} });
this.settings = new Settings({
compatabilityModeVersion: options.compatabilityModeVersion,
});
this.media = fileProperties.template && fileProperties.template.media ? fileProperties.template.media : new Media();
@ -110,7 +108,7 @@ export class File {
this.styles = stylesFactory.newInstance(options.externalStyles);
} else if (options.styles) {
const stylesFactory = new DefaultStylesFactory();
const defaultStyles = stylesFactory.newInstance();
const defaultStyles = stylesFactory.newInstance(options.styles.default);
this.styles = new Styles({
...defaultStyles,
...options.styles,
@ -135,16 +133,10 @@ export class File {
}
for (const section of sections) {
this.document.Body.addSection(section.properties ? section.properties : {});
this.documentWrapper.View.Body.addSection(section.properties ? section.properties : {});
for (const child of section.children) {
if (child instanceof HyperlinkRef) {
const hyperlink = this.hyperlinkCache[child.id];
this.document.add(hyperlink);
continue;
}
this.document.add(child);
this.documentWrapper.View.add(child);
}
}
@ -154,25 +146,10 @@ export class File {
}
}
if (options.hyperlinks) {
const cache = {};
for (const key in options.hyperlinks) {
if (!options.hyperlinks[key]) {
continue;
}
const hyperlinkRef = options.hyperlinks[key];
const hyperlink =
hyperlinkRef.type === HyperlinkType.EXTERNAL
? this.createHyperlink(hyperlinkRef.link, hyperlinkRef.text)
: this.createInternalHyperLink(key, hyperlinkRef.text);
cache[key] = hyperlink;
if (options.features) {
if (options.features.trackRevisions) {
this.settings.addTrackRevisions();
}
this.hyperlinkCache = cache;
}
}
@ -184,7 +161,7 @@ export class File {
properties,
children,
}: ISectionOptions): void {
this.document.Body.addSection({
this.documentWrapper.View.Body.addSection({
...properties,
headers: {
default: headers.default ? this.createHeader(headers.default) : this.createHeader(new Header()),
@ -201,40 +178,16 @@ export class File {
});
for (const child of children) {
if (child instanceof HyperlinkRef) {
const hyperlink = this.hyperlinkCache[child.id];
this.document.add(hyperlink);
continue;
}
this.document.add(child);
this.documentWrapper.View.add(child);
}
}
public verifyUpdateFields(): void {
if (this.document.getTablesOfContents().length) {
if (this.documentWrapper.View.getTablesOfContents().length) {
this.settings.addUpdateFields();
}
}
private createHyperlink(link: string, text: string = link): Hyperlink {
const hyperlink = new Hyperlink(text, shortid.generate().toLowerCase());
this.docRelationships.createRelationship(
hyperlink.linkId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
link,
TargetModeType.EXTERNAL,
);
return hyperlink;
}
private createInternalHyperLink(anchor: string, text: string = anchor): Hyperlink {
const hyperlink = new Hyperlink(text, shortid.generate().toLowerCase(), anchor);
// NOTE: unlike File#createHyperlink(), since the link is to an internal bookmark
// we don't need to create a new relationship.
return hyperlink;
}
private createHeader(header: Header): HeaderWrapper {
const wrapper = new HeaderWrapper(this.media, this.currentRelationshipId++);
@ -259,8 +212,8 @@ export class File {
private addHeaderToDocument(header: HeaderWrapper, type: HeaderReferenceType = HeaderReferenceType.DEFAULT): void {
this.headers.push({ header, type });
this.docRelationships.createRelationship(
header.Header.ReferenceId,
this.documentWrapper.Relationships.createRelationship(
header.View.ReferenceId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
`header${this.headers.length}.xml`,
);
@ -269,8 +222,8 @@ export class File {
private addFooterToDocument(footer: FooterWrapper, type: FooterReferenceType = FooterReferenceType.DEFAULT): void {
this.footers.push({ footer, type });
this.docRelationships.createRelationship(
footer.Footer.ReferenceId,
this.documentWrapper.Relationships.createRelationship(
footer.View.ReferenceId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
`footer${this.footers.length}.xml`,
);
@ -299,30 +252,30 @@ export class File {
"docProps/custom.xml",
);
this.docRelationships.createRelationship(
this.documentWrapper.Relationships.createRelationship(
this.currentRelationshipId++,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
"styles.xml",
);
this.docRelationships.createRelationship(
this.documentWrapper.Relationships.createRelationship(
this.currentRelationshipId++,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering",
"numbering.xml",
);
this.docRelationships.createRelationship(
this.documentWrapper.Relationships.createRelationship(
this.currentRelationshipId++,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
"footnotes.xml",
);
this.docRelationships.createRelationship(
this.documentWrapper.Relationships.createRelationship(
this.currentRelationshipId++,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings",
"settings.xml",
);
}
public get Document(): Document {
return this.document;
public get Document(): DocumentWrapper {
return this.documentWrapper;
}
public get Styles(): Styles {
@ -341,10 +294,6 @@ export class File {
return this.media;
}
public get DocumentRelationships(): Relationships {
return this.docRelationships;
}
public get FileRelationships(): Relationships {
return this.fileRelationships;
}
@ -376,8 +325,4 @@ export class File {
public get Settings(): Settings {
return this.settings;
}
public get HyperlinkCache(): { readonly [key: string]: Hyperlink } {
return this.hyperlinkCache;
}
}

View File

@ -10,7 +10,7 @@ describe("FooterWrapper", () => {
describe("#add", () => {
it("should call the underlying footer's addParagraph", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Footer, "add");
const spy = sinon.spy(file.View, "add");
file.add(new Paragraph({}));
expect(spy.called).to.equal(true);
@ -18,7 +18,7 @@ describe("FooterWrapper", () => {
it("should call the underlying footer's addParagraph", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Footer, "add");
const spy = sinon.spy(file.View, "add");
file.add(
new Table({
rows: [
@ -40,7 +40,7 @@ describe("FooterWrapper", () => {
describe("#addChildElement", () => {
it("should call the underlying footer's addChildElement", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Footer, "addChildElement");
const spy = sinon.spy(file.View, "addChildElement");
// tslint:disable-next-line:no-any
file.addChildElement({} as any);

View File

@ -1,6 +1,7 @@
import { XmlComponent } from "file/xml-components";
import { FooterReferenceType } from "./document";
import { IViewWrapper } from "./document-wrapper";
import { Footer } from "./footer/footer";
import { Media } from "./media";
import { Paragraph } from "./paragraph";
@ -12,7 +13,7 @@ export interface IDocumentFooter {
readonly type: FooterReferenceType;
}
export class FooterWrapper {
export class FooterWrapper implements IViewWrapper {
private readonly footer: Footer;
private readonly relationships: Relationships;
@ -29,7 +30,7 @@ export class FooterWrapper {
this.footer.addChildElement(childElement);
}
public get Footer(): Footer {
public get View(): Footer {
return this.footer;
}

View File

@ -10,7 +10,7 @@ describe("HeaderWrapper", () => {
describe("#add", () => {
it("should call the underlying header's addChildElement for Paragraph", () => {
const wrapper = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(wrapper.Header, "add");
const spy = sinon.spy(wrapper.View, "add");
wrapper.add(new Paragraph({}));
expect(spy.called).to.equal(true);
@ -18,7 +18,7 @@ describe("HeaderWrapper", () => {
it("should call the underlying header's addChildElement for Table", () => {
const wrapper = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(wrapper.Header, "add");
const spy = sinon.spy(wrapper.View, "add");
wrapper.add(
new Table({
rows: [
@ -40,7 +40,7 @@ describe("HeaderWrapper", () => {
describe("#addChildElement", () => {
it("should call the underlying header's addChildElement", () => {
const file = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(file.Header, "addChildElement");
const spy = sinon.spy(file.View, "addChildElement");
// tslint:disable-next-line:no-any
file.addChildElement({} as any);

View File

@ -1,6 +1,7 @@
import { XmlComponent } from "file/xml-components";
import { HeaderReferenceType } from "./document";
import { IViewWrapper } from "./document-wrapper";
import { Header } from "./header/header";
import { Media } from "./media";
import { Paragraph } from "./paragraph";
@ -12,7 +13,7 @@ export interface IDocumentHeader {
readonly type: HeaderReferenceType;
}
export class HeaderWrapper {
export class HeaderWrapper implements IViewWrapper {
private readonly header: Header;
private readonly relationships: Relationships;
@ -31,7 +32,7 @@ export class HeaderWrapper {
this.header.addChildElement(childElement);
}
public get Header(): Header {
public get View(): Header {
return this.header;
}

View File

@ -13,3 +13,4 @@ export * from "./header-wrapper";
export * from "./footer-wrapper";
export * from "./header";
export * from "./footnotes";
export * from "./track-revision";

View File

@ -1,14 +1,12 @@
import { IDrawingOptions } from "../drawing";
import { File } from "../file";
import { FooterWrapper } from "../footer-wrapper";
import { HeaderWrapper } from "../header-wrapper";
import { PictureRun } from "../paragraph";
import { IMediaData } from "./data";
// import { Image } from "./image";
export class Media {
public static addImage(
file: File | HeaderWrapper | FooterWrapper,
file: File,
buffer: Buffer | string | Uint8Array | ArrayBuffer,
width?: number,
height?: number,
@ -82,7 +80,7 @@ export class Media {
return imageData;
}
public get Array(): IMediaData[] {
public get Array(): readonly IMediaData[] {
const array = new Array<IMediaData>();
this.map.forEach((data) => {

View File

@ -7,7 +7,7 @@ import { AlignmentType, EmphasisMarkType, TabStopPosition } from "../paragraph";
import { UnderlineType } from "../paragraph/run/underline";
import { ShadingType } from "../table";
import { AbstractNumbering } from "./abstract-numbering";
import { LevelSuffix } from "./level";
import { LevelFormat, LevelSuffix } from "./level";
describe("AbstractNumbering", () => {
it("stores its ID at its .id property", () => {
@ -20,7 +20,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 3,
format: "lowerLetter",
format: LevelFormat.LOWER_LETTER,
text: "%1)",
alignment: AlignmentType.END,
},
@ -29,7 +29,7 @@ describe("AbstractNumbering", () => {
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "end" } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": "lowerLetter" } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": LevelFormat.LOWER_LETTER } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } });
});
@ -37,7 +37,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 3,
format: "lowerLetter",
format: LevelFormat.LOWER_LETTER,
text: "%1)",
},
]);
@ -45,7 +45,7 @@ describe("AbstractNumbering", () => {
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "start" } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": "lowerLetter" } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": LevelFormat.LOWER_LETTER } } });
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } });
});
@ -53,7 +53,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 3,
format: "lowerLetter",
format: LevelFormat.LOWER_LETTER,
text: "%1)",
alignment: AlignmentType.END,
suffix: LevelSuffix.SPACE,
@ -68,7 +68,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
paragraph: {
@ -87,7 +87,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
paragraph: {
@ -106,7 +106,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
paragraph: {
@ -125,7 +125,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
paragraph: {
@ -144,7 +144,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
paragraph: {
@ -163,7 +163,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
paragraph: {
@ -182,7 +182,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
paragraph: {
@ -216,7 +216,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
paragraph: {
@ -239,7 +239,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
paragraph: {
@ -262,7 +262,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
paragraph: {
@ -281,7 +281,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
paragraph: {
@ -324,7 +324,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: { size, sizeComplexScript },
@ -340,7 +340,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -359,7 +359,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -378,7 +378,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -398,7 +398,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -417,7 +417,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -436,7 +436,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -455,7 +455,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -485,7 +485,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -533,7 +533,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: { bold, boldComplexScript },
@ -566,7 +566,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: { italics, italicsComplexScript },
@ -604,7 +604,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: { highlight, highlightComplexScript },
@ -682,7 +682,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: { shadow, shading, shadingComplexScript },
@ -699,7 +699,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -718,7 +718,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -739,7 +739,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -763,7 +763,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -782,7 +782,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {
@ -804,7 +804,7 @@ describe("AbstractNumbering", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: "lowerRoman",
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: {

View File

@ -1,8 +1,26 @@
// http://officeopenxml.com/WPnumbering-numFmt.php
import { Attributes, XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { AlignmentType } from "../paragraph/formatting";
import { IParagraphStylePropertiesOptions, ParagraphProperties } from "../paragraph/properties";
import { IRunStylePropertiesOptions, RunProperties } from "../paragraph/run/properties";
export enum LevelFormat {
BULLET = "bullet",
CARDINAL_TEXT = "cardinalText",
CHICAGO = "chicago",
DECIMAL = "decimal",
DECIMAL_ENCLOSED_CIRCLE = "decimalEnclosedCircle",
DECIMAL_ENCLOSED_FULLSTOP = "decimalEnclosedFullstop",
DECIMAL_ENCLOSED_PARENTHESES = "decimalEnclosedParen",
DECIMAL_ZERO = "decimalZero",
LOWER_LETTER = "lowerLetter",
LOWER_ROMAN = "lowerRoman",
NONE = "none",
ORDINAL_TEXT = "ordinalText",
UPPER_LETTER = "upperLetter",
UPPER_ROMAN = "upperRoman",
}
interface ILevelAttributesProperties {
readonly ilvl?: number;
readonly tentative?: number;
@ -67,7 +85,7 @@ export enum LevelSuffix {
export interface ILevelsOptions {
readonly level: number;
readonly format?: string;
readonly format?: LevelFormat;
readonly text?: string;
readonly alignment?: AlignmentType;
readonly start?: number;

View File

@ -1,10 +1,11 @@
// http://officeopenxml.com/WPnumbering.php
import { convertInchesToTwip } from "convenience-functions";
import { AlignmentType } from "file/paragraph";
import { IXmlableObject, XmlComponent } from "file/xml-components";
import { DocumentAttributes } from "../document/document-attributes";
import { AbstractNumbering } from "./abstract-numbering";
import { ILevelsOptions } from "./level";
import { ILevelsOptions, LevelFormat } from "./level";
import { ConcreteNumbering } from "./num";
export interface INumberingOptions {
@ -50,100 +51,100 @@ export class Numbering extends XmlComponent {
const abstractNumbering = this.createAbstractNumbering([
{
level: 0,
format: "bullet",
format: LevelFormat.BULLET,
text: "\u25CF",
alignment: AlignmentType.LEFT,
style: {
paragraph: {
indent: { left: 720, hanging: 360 },
indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.25) },
},
},
},
{
level: 1,
format: "bullet",
format: LevelFormat.BULLET,
text: "\u25CB",
alignment: AlignmentType.LEFT,
style: {
paragraph: {
indent: { left: 1440, hanging: 360 },
indent: { left: convertInchesToTwip(1), hanging: convertInchesToTwip(0.25) },
},
},
},
{
level: 2,
format: "bullet",
format: LevelFormat.BULLET,
text: "\u25A0",
alignment: AlignmentType.LEFT,
style: {
paragraph: {
indent: { left: 2160, hanging: 360 },
indent: { left: 2160, hanging: convertInchesToTwip(0.25) },
},
},
},
{
level: 3,
format: "bullet",
format: LevelFormat.BULLET,
text: "\u25CF",
alignment: AlignmentType.LEFT,
style: {
paragraph: {
indent: { left: 2880, hanging: 360 },
indent: { left: 2880, hanging: convertInchesToTwip(0.25) },
},
},
},
{
level: 4,
format: "bullet",
format: LevelFormat.BULLET,
text: "\u25CB",
alignment: AlignmentType.LEFT,
style: {
paragraph: {
indent: { left: 3600, hanging: 360 },
indent: { left: 3600, hanging: convertInchesToTwip(0.25) },
},
},
},
{
level: 5,
format: "bullet",
format: LevelFormat.BULLET,
text: "\u25A0",
alignment: AlignmentType.LEFT,
style: {
paragraph: {
indent: { left: 4320, hanging: 360 },
indent: { left: 4320, hanging: convertInchesToTwip(0.25) },
},
},
},
{
level: 6,
format: "bullet",
format: LevelFormat.BULLET,
text: "\u25CF",
alignment: AlignmentType.LEFT,
style: {
paragraph: {
indent: { left: 5040, hanging: 360 },
indent: { left: 5040, hanging: convertInchesToTwip(0.25) },
},
},
},
{
level: 7,
format: "bullet",
format: LevelFormat.BULLET,
text: "\u25CF",
alignment: AlignmentType.LEFT,
style: {
paragraph: {
indent: { left: 5760, hanging: 360 },
indent: { left: 5760, hanging: convertInchesToTwip(0.25) },
},
},
},
{
level: 8,
format: "bullet",
format: LevelFormat.BULLET,
text: "\u25CF",
alignment: AlignmentType.LEFT,
style: {
paragraph: {
indent: { left: 6480, hanging: 360 },
indent: { left: 6480, hanging: convertInchesToTwip(0.25) },
},
},
},

View File

@ -3,3 +3,4 @@ export * from "./paragraph";
export * from "./properties";
export * from "./run";
export * from "./links";
export * from "./math";

View File

@ -2,14 +2,20 @@ import { expect } from "chai";
import { Formatter } from "export/formatter";
import { Hyperlink } from "./";
import { HyperlinkRef } from "./hyperlink";
import { TextRun } from "../run";
import { ConcreteHyperlink, ExternalHyperlink, InternalHyperlink } from "./hyperlink";
describe("Hyperlink", () => {
let hyperlink: Hyperlink;
describe("ConcreteHyperlink", () => {
let hyperlink: ConcreteHyperlink;
beforeEach(() => {
hyperlink = new Hyperlink("https://example.com", "superid");
hyperlink = new ConcreteHyperlink(
new TextRun({
text: "https://example.com",
style: "Hyperlink",
}),
"superid",
);
});
describe("#constructor()", () => {
@ -35,7 +41,14 @@ describe("Hyperlink", () => {
describe("with optional anchor parameter", () => {
beforeEach(() => {
hyperlink = new Hyperlink("Anchor Text", "superid2", "anchor");
hyperlink = new ConcreteHyperlink(
new TextRun({
text: "Anchor Text",
style: "Hyperlink",
}),
"superid2",
"anchor",
);
});
it("should create an internal link with anchor tag", () => {
@ -61,10 +74,53 @@ describe("Hyperlink", () => {
});
});
describe("HyperlinkRef", () => {
describe("ExternalHyperlink", () => {
describe("#constructor()", () => {
const hyperlinkRef = new HyperlinkRef("test-id");
it("should create", () => {
const externalHyperlink = new ExternalHyperlink({
child: new TextRun("test"),
link: "http://www.google.com",
});
expect(hyperlinkRef.id).to.equal("test-id");
expect(externalHyperlink.options.link).to.equal("http://www.google.com");
});
});
});
describe("InternalHyperlink", () => {
describe("#constructor()", () => {
it("should create", () => {
const internalHyperlink = new InternalHyperlink({
child: new TextRun("test"),
anchor: "test-id",
});
const tree = new Formatter().format(internalHyperlink);
expect(tree).to.deep.equal({
"w:hyperlink": [
{
_attr: {
"w:anchor": "test-id",
"w:history": 1,
},
},
{
"w:r": [
{
"w:t": [
{
_attr: {
"xml:space": "preserve",
},
},
"test",
],
},
],
},
],
});
});
});
});

View File

@ -1,6 +1,9 @@
// http://officeopenxml.com/WPhyperlink.php
import * as shortid from "shortid";
import { XmlComponent } from "file/xml-components";
import { TextRun } from "../run";
import { ParagraphChild } from "../paragraph";
import { HyperlinkAttributes, IHyperlinkAttributesProperties } from "./hyperlink-attributes";
export enum HyperlinkType {
@ -8,15 +11,10 @@ export enum HyperlinkType {
EXTERNAL = "EXTERNAL",
}
export class HyperlinkRef {
constructor(public readonly id: string) {}
}
export class Hyperlink extends XmlComponent {
export class ConcreteHyperlink extends XmlComponent {
public readonly linkId: string;
private readonly textRun: TextRun;
constructor(text: string, relationshipId: string, anchor?: string) {
constructor(child: ParagraphChild, relationshipId: string, anchor?: string) {
super("w:hyperlink");
this.linkId = relationshipId;
@ -29,14 +27,16 @@ export class Hyperlink extends XmlComponent {
const attributes = new HyperlinkAttributes(props);
this.root.push(attributes);
this.textRun = new TextRun({
text: text,
style: "Hyperlink",
});
this.root.push(this.textRun);
}
public get TextRun(): TextRun {
return this.textRun;
this.root.push(child);
}
}
export class InternalHyperlink extends ConcreteHyperlink {
constructor(options: { readonly child: ParagraphChild; readonly anchor: string }) {
super(options.child, shortid.generate().toLowerCase(), options.anchor);
}
}
export class ExternalHyperlink {
constructor(public readonly options: { readonly child: ParagraphChild; readonly link: string }) {}
}

View File

@ -0,0 +1,4 @@
export * from "./math-round-brackets";
export * from "./math-square-brackets";
export * from "./math-curly-brackets";
export * from "./math-angled-brackets";

View File

@ -0,0 +1,51 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathAngledBrackets } from "./math-angled-brackets";
describe("MathAngledBrackets", () => {
describe("#constructor()", () => {
it("should create a MathAngledBrackets with correct root key", () => {
const mathAngledBrackets = new MathAngledBrackets({
children: [new MathRun("60")],
});
const tree = new Formatter().format(mathAngledBrackets);
expect(tree).to.deep.equal({
"m:d": [
{
"m:dPr": [
{
"m:begChr": {
_attr: {
"m:val": "〈",
},
},
},
{
"m:endChr": {
_attr: {
"m:val": "〉",
},
},
},
],
},
{
"m:e": [
{
"m:r": [
{
"m:t": ["60"],
},
],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,20 @@
// http://www.datypic.com/sc/ooxml/e-m_d-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
import { MathBase } from "../n-ary";
import { MathBracketProperties } from "./math-bracket-properties";
export class MathAngledBrackets extends XmlComponent {
constructor(options: { readonly children: MathComponent[] }) {
super("m:d");
this.root.push(
new MathBracketProperties({
beginningCharacter: "〈",
endingCharacter: "〉",
}),
);
this.root.push(new MathBase(options.children));
}
}

View File

@ -0,0 +1,22 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathBeginningCharacter } from "./math-beginning-character";
describe("MathBeginningCharacter", () => {
describe("#constructor()", () => {
it("should create a MathBeginningCharacter with correct root key", () => {
const mathBeginningCharacter = new MathBeginningCharacter("[");
const tree = new Formatter().format(mathBeginningCharacter);
expect(tree).to.deep.equal({
"m:begChr": {
_attr: {
"m:val": "[",
},
},
});
});
});
});

View File

@ -0,0 +1,14 @@
// http://www.datypic.com/sc/ooxml/e-m_begChr-1.html
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
class MathBeginningCharacterAttributes extends XmlAttributeComponent<{ readonly character: string }> {
protected readonly xmlKeys = { character: "m:val" };
}
export class MathBeginningCharacter extends XmlComponent {
constructor(character: string) {
super("m:begChr");
this.root.push(new MathBeginningCharacterAttributes({ character }));
}
}

View File

@ -0,0 +1,45 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathBracketProperties } from "./math-bracket-properties";
describe("MathBracketProperties", () => {
describe("#constructor()", () => {
it("should create a MathBracketProperties with correct root key", () => {
const mathBracketProperties = new MathBracketProperties();
const tree = new Formatter().format(mathBracketProperties);
expect(tree).to.deep.equal({
"m:dPr": {},
});
});
it("should create a MathBracketProperties with correct root key and add brackets", () => {
const mathBracketProperties = new MathBracketProperties({
beginningCharacter: "[",
endingCharacter: "]",
});
const tree = new Formatter().format(mathBracketProperties);
expect(tree).to.deep.equal({
"m:dPr": [
{
"m:begChr": {
_attr: {
"m:val": "[",
},
},
},
{
"m:endChr": {
_attr: {
"m:val": "]",
},
},
},
],
});
});
});
});

View File

@ -0,0 +1,16 @@
// http://www.datypic.com/sc/ooxml/e-m_dPr-1.html
import { XmlComponent } from "file/xml-components";
import { MathBeginningCharacter } from "./math-beginning-character";
import { MathEndingCharacter } from "./math-ending-char";
export class MathBracketProperties extends XmlComponent {
constructor(options?: { readonly beginningCharacter: string; readonly endingCharacter: string }) {
super("m:dPr");
if (!!options) {
this.root.push(new MathBeginningCharacter(options.beginningCharacter));
this.root.push(new MathEndingCharacter(options.endingCharacter));
}
}
}

View File

@ -0,0 +1,51 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathCurlyBrackets } from "./math-curly-brackets";
describe("MathCurlyBrackets", () => {
describe("#constructor()", () => {
it("should create a MathCurlyBrackets with correct root key", () => {
const mathCurlyBrackets = new MathCurlyBrackets({
children: [new MathRun("60")],
});
const tree = new Formatter().format(mathCurlyBrackets);
expect(tree).to.deep.equal({
"m:d": [
{
"m:dPr": [
{
"m:begChr": {
_attr: {
"m:val": "{",
},
},
},
{
"m:endChr": {
_attr: {
"m:val": "}",
},
},
},
],
},
{
"m:e": [
{
"m:r": [
{
"m:t": ["60"],
},
],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,20 @@
// http://www.datypic.com/sc/ooxml/e-m_d-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
import { MathBase } from "../n-ary";
import { MathBracketProperties } from "./math-bracket-properties";
export class MathCurlyBrackets extends XmlComponent {
constructor(options: { readonly children: MathComponent[] }) {
super("m:d");
this.root.push(
new MathBracketProperties({
beginningCharacter: "{",
endingCharacter: "}",
}),
);
this.root.push(new MathBase(options.children));
}
}

View File

@ -0,0 +1,14 @@
// http://www.datypic.com/sc/ooxml/e-m_endChr-1.html
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
class MathEndingCharacterAttributes extends XmlAttributeComponent<{ readonly character: string }> {
protected readonly xmlKeys = { character: "m:val" };
}
export class MathEndingCharacter extends XmlComponent {
constructor(character: string) {
super("m:endChr");
this.root.push(new MathEndingCharacterAttributes({ character }));
}
}

View File

@ -0,0 +1,22 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathEndingCharacter } from "./math-ending-char";
describe("MathEndingCharacter", () => {
describe("#constructor()", () => {
it("should create a MathEndingCharacter with correct root key", () => {
const mathEndingCharacter = new MathEndingCharacter("]");
const tree = new Formatter().format(mathEndingCharacter);
expect(tree).to.deep.equal({
"m:endChr": {
_attr: {
"m:val": "]",
},
},
});
});
});
});

View File

@ -0,0 +1,36 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathRoundBrackets } from "./math-round-brackets";
describe("MathRoundBrackets", () => {
describe("#constructor()", () => {
it("should create a MathRoundBrackets with correct root key", () => {
const mathRoundBrackets = new MathRoundBrackets({
children: [new MathRun("60")],
});
const tree = new Formatter().format(mathRoundBrackets);
expect(tree).to.deep.equal({
"m:d": [
{
"m:dPr": {},
},
{
"m:e": [
{
"m:r": [
{
"m:t": ["60"],
},
],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,15 @@
// http://www.datypic.com/sc/ooxml/e-m_d-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
import { MathBase } from "../n-ary";
import { MathBracketProperties } from "./math-bracket-properties";
export class MathRoundBrackets extends XmlComponent {
constructor(options: { readonly children: MathComponent[] }) {
super("m:d");
this.root.push(new MathBracketProperties());
this.root.push(new MathBase(options.children));
}
}

View File

@ -0,0 +1,51 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathSquareBrackets } from "./math-square-brackets";
describe("MathSquareBrackets", () => {
describe("#constructor()", () => {
it("should create a MathSquareBrackets with correct root key", () => {
const mathSquareBrackets = new MathSquareBrackets({
children: [new MathRun("60")],
});
const tree = new Formatter().format(mathSquareBrackets);
expect(tree).to.deep.equal({
"m:d": [
{
"m:dPr": [
{
"m:begChr": {
_attr: {
"m:val": "[",
},
},
},
{
"m:endChr": {
_attr: {
"m:val": "]",
},
},
},
],
},
{
"m:e": [
{
"m:r": [
{
"m:t": ["60"],
},
],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,20 @@
// http://www.datypic.com/sc/ooxml/e-m_d-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
import { MathBase } from "../n-ary";
import { MathBracketProperties } from "./math-bracket-properties";
export class MathSquareBrackets extends XmlComponent {
constructor(options: { readonly children: MathComponent[] }) {
super("m:d");
this.root.push(
new MathBracketProperties({
beginningCharacter: "[",
endingCharacter: "]",
}),
);
this.root.push(new MathBase(options.children));
}
}

View File

@ -0,0 +1,3 @@
export * from "./math-fraction";
export * from "./math-denominator";
export * from "./math-numerator";

View File

@ -0,0 +1,26 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathDenominator } from "./math-denominator";
describe("MathDenominator", () => {
describe("#constructor()", () => {
it("should create a MathDenominator with correct root key", () => {
const mathDenominator = new MathDenominator([new MathRun("2+2")]);
const tree = new Formatter().format(mathDenominator);
expect(tree).to.deep.equal({
"m:den": [
{
"m:r": [
{
"m:t": ["2+2"],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,13 @@
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
export class MathDenominator extends XmlComponent {
constructor(children: MathComponent[]) {
super("m:den");
for (const child of children) {
this.root.push(child);
}
}
}

View File

@ -0,0 +1,44 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathFraction } from "./math-fraction";
describe("MathFraction", () => {
describe("#constructor()", () => {
it("should create a MathFraction with correct root key", () => {
const mathFraction = new MathFraction({
numerator: [new MathRun("2")],
denominator: [new MathRun("2")],
});
const tree = new Formatter().format(mathFraction);
expect(tree).to.deep.equal({
"m:f": [
{
"m:num": [
{
"m:r": [
{
"m:t": ["2"],
},
],
},
],
},
{
"m:den": [
{
"m:r": [
{
"m:t": ["2"],
},
],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,19 @@
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
import { MathDenominator } from "./math-denominator";
import { MathNumerator } from "./math-numerator";
export interface IMathFractionOptions {
readonly numerator: MathComponent[];
readonly denominator: MathComponent[];
}
export class MathFraction extends XmlComponent {
constructor(options: IMathFractionOptions) {
super("m:f");
this.root.push(new MathNumerator(options.numerator));
this.root.push(new MathDenominator(options.denominator));
}
}

View File

@ -0,0 +1,26 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathNumerator } from "./math-numerator";
describe("MathNumerator", () => {
describe("#constructor()", () => {
it("should create a MathNumerator with correct root key", () => {
const mathNumerator = new MathNumerator([new MathRun("2+2")]);
const tree = new Formatter().format(mathNumerator);
expect(tree).to.deep.equal({
"m:num": [
{
"m:r": [
{
"m:t": ["2+2"],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,13 @@
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
export class MathNumerator extends XmlComponent {
constructor(children: MathComponent[]) {
super("m:num");
for (const child of children) {
this.root.push(child);
}
}
}

View File

@ -0,0 +1,3 @@
export * from "./math-function";
export * from "./math-function-name";
export * from "./math-function-properties";

View File

@ -0,0 +1,27 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathFunctionName } from "./math-function-name";
describe("MathFunctionName", () => {
describe("#constructor()", () => {
it("should create a MathFunctionName with correct root key", () => {
const mathFunctionName = new MathFunctionName([new MathRun("2")]);
const tree = new Formatter().format(mathFunctionName);
expect(tree).to.deep.equal({
"m:fName": [
{
"m:r": [
{
"m:t": ["2"],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,13 @@
// http://www.datypic.com/sc/ooxml/e-m_fName-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
export class MathFunctionName extends XmlComponent {
constructor(children: MathComponent[]) {
super("m:fName");
for (const child of children) {
this.root.push(child);
}
}
}

View File

@ -0,0 +1,18 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathFunctionProperties } from "./math-function-properties";
describe("MathFunctionProperties", () => {
describe("#constructor()", () => {
it("should create a MathFunctionProperties with correct root key", () => {
const mathFunctionProperties = new MathFunctionProperties();
const tree = new Formatter().format(mathFunctionProperties);
expect(tree).to.deep.equal({
"m:funcPr": {},
});
});
});
});

View File

@ -0,0 +1,8 @@
// http://www.datypic.com/sc/ooxml/e-m_radPr-1.html
import { XmlComponent } from "file/xml-components";
export class MathFunctionProperties extends XmlComponent {
constructor() {
super("m:funcPr");
}
}

View File

@ -0,0 +1,48 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathFunction } from "./math-function";
describe("MathFunction", () => {
describe("#constructor()", () => {
it("should create a MathFunction with correct root key", () => {
const mathFunction = new MathFunction({
name: [new MathRun("sin")],
children: [new MathRun("60")],
});
const tree = new Formatter().format(mathFunction);
expect(tree).to.deep.equal({
"m:func": [
{
"m:funcPr": {},
},
{
"m:fName": [
{
"m:r": [
{
"m:t": ["sin"],
},
],
},
],
},
{
"m:e": [
{
"m:r": [
{
"m:t": ["60"],
},
],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,22 @@
// http://www.datypic.com/sc/ooxml/e-m_func-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
import { MathBase } from "../n-ary";
import { MathFunctionName } from "./math-function-name";
import { MathFunctionProperties } from "./math-function-properties";
export interface IMathFunctionOptions {
readonly children: MathComponent[];
readonly name: MathComponent[];
}
export class MathFunction extends XmlComponent {
constructor(options: IMathFunctionOptions) {
super("m:func");
this.root.push(new MathFunctionProperties());
this.root.push(new MathFunctionName(options.name));
this.root.push(new MathBase(options.children));
}
}

View File

@ -0,0 +1,9 @@
export * from "./math";
export * from "./math-run";
export * from "./fraction";
export * from "./n-ary";
export * from "./script";
export * from "./math-component";
export * from "./radical";
export * from "./function";
export * from "./brackets";

View File

@ -0,0 +1,27 @@
import { MathAngledBrackets, MathCurlyBrackets, MathRoundBrackets, MathSquareBrackets } from "./brackets";
import { MathFraction } from "./fraction";
import { MathFunction } from "./function";
import { MathRun } from "./math-run";
import { MathSum } from "./n-ary";
import { MathRadical } from "./radical";
import { MathSubScript, MathSubSuperScript, MathSuperScript } from "./script";
export type MathComponent =
| MathRun
| MathFraction
| MathSum
| MathSuperScript
| MathSubScript
| MathSubSuperScript
| MathRadical
| MathFunction
| MathRoundBrackets
| MathCurlyBrackets
| MathAngledBrackets
| MathSquareBrackets;
// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432
/**
* @ignore
*/
export const WORKAROUND4 = "";

View File

@ -0,0 +1,21 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "./math-run";
describe("MathRun", () => {
describe("#constructor()", () => {
it("should create a MathRun with correct root key", () => {
const mathRun = new MathRun("2+2");
const tree = new Formatter().format(mathRun);
expect(tree).to.deep.equal({
"m:r": [
{
"m:t": ["2+2"],
},
],
});
});
});
});

View File

@ -0,0 +1,12 @@
// http://www.datypic.com/sc/ooxml/e-m_r-1.html
import { XmlComponent } from "file/xml-components";
import { MathText } from "./math-text";
export class MathRun extends XmlComponent {
constructor(text: string) {
super("m:r");
this.root.push(new MathText(text));
}
}

View File

@ -0,0 +1,17 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathText } from "./math-text";
describe("MathText", () => {
describe("#constructor()", () => {
it("should create a MathText with correct root key", () => {
const mathText = new MathText("2+2");
const tree = new Formatter().format(mathText);
expect(tree).to.deep.equal({
"m:t": ["2+2"],
});
});
});
});

View File

@ -0,0 +1,9 @@
import { XmlComponent } from "file/xml-components";
export class MathText extends XmlComponent {
constructor(text: string) {
super("m:t");
this.root.push(text);
}
}

View File

@ -0,0 +1,38 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { Math } from "./math";
import { MathRun } from "./math-run";
describe("Math", () => {
describe("#constructor()", () => {
it("should create a Math with correct root key", () => {
const math = new Math({
children: [],
});
const tree = new Formatter().format(math);
expect(tree).to.deep.equal({
"m:oMath": {},
});
});
it("should be able to add children", () => {
const math = new Math({
children: [new MathRun("2+2")],
});
const tree = new Formatter().format(math);
expect(tree).to.deep.equal({
"m:oMath": [
{
"m:r": [
{
"m:t": ["2+2"],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,18 @@
// http://www.datypic.com/sc/ooxml/e-m_oMath-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "./math-component";
export interface IMathOptions {
readonly children: MathComponent[];
}
export class Math extends XmlComponent {
constructor(options: IMathOptions) {
super("m:oMath");
for (const child of options.children) {
this.root.push(child);
}
}
}

View File

@ -0,0 +1,7 @@
export * from "./math-accent-character";
export * from "./math-base";
export * from "./math-limit-location";
export * from "./math-naray-properties";
export * from "./math-sub-script";
export * from "./math-sum";
export * from "./math-super-script";

View File

@ -0,0 +1,22 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathAccentCharacter } from "./math-accent-character";
describe("MathAccentCharacter", () => {
describe("#constructor()", () => {
it("should create a MathAccentCharacter with correct root key", () => {
const mathAccentCharacter = new MathAccentCharacter("∑");
const tree = new Formatter().format(mathAccentCharacter);
expect(tree).to.deep.equal({
"m:chr": {
_attr: {
"m:val": "∑",
},
},
});
});
});
});

View File

@ -0,0 +1,14 @@
// http://www.datypic.com/sc/ooxml/e-m_chr-1.html
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
class MathAccentCharacterAttributes extends XmlAttributeComponent<{ readonly accent: string }> {
protected readonly xmlKeys = { accent: "m:val" };
}
export class MathAccentCharacter extends XmlComponent {
constructor(accent: string) {
super("m:chr");
this.root.push(new MathAccentCharacterAttributes({ accent }));
}
}

View File

@ -0,0 +1,27 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathBase } from "./math-base";
describe("MathBase", () => {
describe("#constructor()", () => {
it("should create a MathBase with correct root key", () => {
const mathBase = new MathBase([new MathRun("2+2")]);
const tree = new Formatter().format(mathBase);
expect(tree).to.deep.equal({
"m:e": [
{
"m:r": [
{
"m:t": ["2+2"],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,14 @@
// http://www.datypic.com/sc/ooxml/e-m_e-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
export class MathBase extends XmlComponent {
constructor(children: MathComponent[]) {
super("m:e");
for (const child of children) {
this.root.push(child);
}
}
}

View File

@ -0,0 +1,22 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathLimitLocation } from "./math-limit-location";
describe("MathLimitLocation", () => {
describe("#constructor()", () => {
it("should create a MathLimitLocation with correct root key", () => {
const mathLimitLocation = new MathLimitLocation();
const tree = new Formatter().format(mathLimitLocation);
expect(tree).to.deep.equal({
"m:limLoc": {
_attr: {
"m:val": "undOvr",
},
},
});
});
});
});

View File

@ -0,0 +1,14 @@
// http://www.datypic.com/sc/ooxml/e-m_limLoc-1.html
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
class MathLimitLocationAttributes extends XmlAttributeComponent<{ readonly value: string }> {
protected readonly xmlKeys = { value: "m:val" };
}
export class MathLimitLocation extends XmlComponent {
constructor() {
super("m:limLoc");
this.root.push(new MathLimitLocationAttributes({ value: "undOvr" }));
}
}

View File

@ -0,0 +1,133 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathNArayProperties } from "./math-naray-properties";
describe("MathNArayProperties", () => {
describe("#constructor()", () => {
it("should create a MathNArayProperties with correct root key", () => {
const mathNArayProperties = new MathNArayProperties("∑", true, true);
const tree = new Formatter().format(mathNArayProperties);
expect(tree).to.deep.equal({
"m:naryPr": [
{
"m:chr": {
_attr: {
"m:val": "∑",
},
},
},
{
"m:limLoc": {
_attr: {
"m:val": "undOvr",
},
},
},
],
});
});
it("should add super-script hide attributes", () => {
const mathNArayProperties = new MathNArayProperties("∑", false, true);
const tree = new Formatter().format(mathNArayProperties);
expect(tree).to.deep.equal({
"m:naryPr": [
{
"m:chr": {
_attr: {
"m:val": "∑",
},
},
},
{
"m:limLoc": {
_attr: {
"m:val": "undOvr",
},
},
},
{
"m:supHide": {
_attr: {
"m:val": 1,
},
},
},
],
});
});
it("should add sub-script hide attributes", () => {
const mathNArayProperties = new MathNArayProperties("∑", true, false);
const tree = new Formatter().format(mathNArayProperties);
expect(tree).to.deep.equal({
"m:naryPr": [
{
"m:chr": {
_attr: {
"m:val": "∑",
},
},
},
{
"m:limLoc": {
_attr: {
"m:val": "undOvr",
},
},
},
{
"m:subHide": {
_attr: {
"m:val": 1,
},
},
},
],
});
});
it("should add both super-script and sub-script hide attributes", () => {
const mathNArayProperties = new MathNArayProperties("∑", false, false);
const tree = new Formatter().format(mathNArayProperties);
expect(tree).to.deep.equal({
"m:naryPr": [
{
"m:chr": {
_attr: {
"m:val": "∑",
},
},
},
{
"m:limLoc": {
_attr: {
"m:val": "undOvr",
},
},
},
{
"m:supHide": {
_attr: {
"m:val": 1,
},
},
},
{
"m:subHide": {
_attr: {
"m:val": 1,
},
},
},
],
});
});
});
});

View File

@ -0,0 +1,24 @@
// http://www.datypic.com/sc/ooxml/e-m_naryPr-1.html
import { XmlComponent } from "file/xml-components";
import { MathAccentCharacter } from "./math-accent-character";
import { MathLimitLocation } from "./math-limit-location";
import { MathSubScriptHide } from "./math-sub-script-hide";
import { MathSuperScriptHide } from "./math-super-script-hide";
export class MathNArayProperties extends XmlComponent {
constructor(accent: string, hasSuperScript: boolean, hasSubScript: boolean) {
super("m:naryPr");
this.root.push(new MathAccentCharacter(accent));
this.root.push(new MathLimitLocation());
if (!hasSuperScript) {
this.root.push(new MathSuperScriptHide());
}
if (!hasSubScript) {
this.root.push(new MathSubScriptHide());
}
}
}

View File

@ -0,0 +1,22 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathSubScriptHide } from "./math-sub-script-hide";
describe("MathSubScriptHide", () => {
describe("#constructor()", () => {
it("should create a MathSubScriptHide with correct root key", () => {
const mathSubScriptHide = new MathSubScriptHide();
const tree = new Formatter().format(mathSubScriptHide);
expect(tree).to.deep.equal({
"m:subHide": {
_attr: {
"m:val": 1,
},
},
});
});
});
});

View File

@ -0,0 +1,14 @@
// http://www.datypic.com/sc/ooxml/e-m_subHide-1.html
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
class MathSubScriptHideAttributes extends XmlAttributeComponent<{ readonly hide: number }> {
protected readonly xmlKeys = { hide: "m:val" };
}
export class MathSubScriptHide extends XmlComponent {
constructor() {
super("m:subHide");
this.root.push(new MathSubScriptHideAttributes({ hide: 1 }));
}
}

View File

@ -0,0 +1,27 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathSubScriptElement } from "./math-sub-script";
describe("MathSubScriptElement", () => {
describe("#constructor()", () => {
it("should create a MathSubScriptElement with correct root key", () => {
const mathSubScriptElement = new MathSubScriptElement([new MathRun("2+2")]);
const tree = new Formatter().format(mathSubScriptElement);
expect(tree).to.deep.equal({
"m:sub": [
{
"m:r": [
{
"m:t": ["2+2"],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,14 @@
// http://www.datypic.com/sc/ooxml/e-m_sub-3.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
export class MathSubScriptElement extends XmlComponent {
constructor(children: MathComponent[]) {
super("m:sub");
for (const child of children) {
this.root.push(child);
}
}
}

View File

@ -0,0 +1,75 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathSum } from "./math-sum";
describe("MathSum", () => {
describe("#constructor()", () => {
it("should create a MathSum with correct root key", () => {
const mathSum = new MathSum({
children: [new MathRun("1")],
subScript: [new MathRun("2")],
superScript: [new MathRun("3")],
});
const tree = new Formatter().format(mathSum);
expect(tree).to.deep.equal({
"m:nary": [
{
"m:naryPr": [
{
"m:chr": {
_attr: {
"m:val": "∑",
},
},
},
{
"m:limLoc": {
_attr: {
"m:val": "undOvr",
},
},
},
],
},
{
"m:sub": [
{
"m:r": [
{
"m:t": ["2"],
},
],
},
],
},
{
"m:sup": [
{
"m:r": [
{
"m:t": ["3"],
},
],
},
],
},
{
"m:e": [
{
"m:r": [
{
"m:t": ["1"],
},
],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,32 @@
// http://www.datypic.com/sc/ooxml/e-m_nary-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
import { MathBase } from "./math-base";
import { MathNArayProperties } from "./math-naray-properties";
import { MathSubScriptElement } from "./math-sub-script";
import { MathSuperScriptElement } from "./math-super-script";
export interface IMathSumOptions {
readonly children: MathComponent[];
readonly subScript?: MathComponent[];
readonly superScript?: MathComponent[];
}
export class MathSum extends XmlComponent {
constructor(options: IMathSumOptions) {
super("m:nary");
this.root.push(new MathNArayProperties("∑", !!options.superScript, !!options.subScript));
if (!!options.subScript) {
this.root.push(new MathSubScriptElement(options.subScript));
}
if (!!options.superScript) {
this.root.push(new MathSuperScriptElement(options.superScript));
}
this.root.push(new MathBase(options.children));
}
}

View File

@ -0,0 +1,22 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathSuperScriptHide } from "./math-super-script-hide";
describe("MathSuperScriptHide", () => {
describe("#constructor()", () => {
it("should create a MathSuperScriptHide with correct root key", () => {
const mathSuperScriptHide = new MathSuperScriptHide();
const tree = new Formatter().format(mathSuperScriptHide);
expect(tree).to.deep.equal({
"m:supHide": {
_attr: {
"m:val": 1,
},
},
});
});
});
});

View File

@ -0,0 +1,14 @@
// http://www.datypic.com/sc/ooxml/e-m_subHide-1.html
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
class MathSuperScriptHideAttributes extends XmlAttributeComponent<{ readonly hide: number }> {
protected readonly xmlKeys = { hide: "m:val" };
}
export class MathSuperScriptHide extends XmlComponent {
constructor() {
super("m:supHide");
this.root.push(new MathSuperScriptHideAttributes({ hide: 1 }));
}
}

View File

@ -0,0 +1,27 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathSuperScriptElement } from "./math-super-script";
describe("MathSuperScriptElement", () => {
describe("#constructor()", () => {
it("should create a MathSuperScriptElement with correct root key", () => {
const mathSuperScriptElement = new MathSuperScriptElement([new MathRun("2+2")]);
const tree = new Formatter().format(mathSuperScriptElement);
expect(tree).to.deep.equal({
"m:sup": [
{
"m:r": [
{
"m:t": ["2+2"],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,14 @@
// http://www.datypic.com/sc/ooxml/e-m_sup-3.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
export class MathSuperScriptElement extends XmlComponent {
constructor(children: MathComponent[]) {
super("m:sup");
for (const child of children) {
this.root.push(child);
}
}
}

View File

@ -0,0 +1,3 @@
export * from "./math-degree";
export * from "./math-radical";
export * from "./math-radical-properties";

View File

@ -0,0 +1,22 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathDegreeHide } from "./math-degree-hide";
describe("MathDegreeHide", () => {
describe("#constructor()", () => {
it("should create a MathDegreeHide with correct root key", () => {
const mathDegreeHide = new MathDegreeHide();
const tree = new Formatter().format(mathDegreeHide);
expect(tree).to.deep.equal({
"m:degHide": {
_attr: {
"m:val": 1,
},
},
});
});
});
});

View File

@ -0,0 +1,14 @@
// http://www.datypic.com/sc/ooxml/e-m_degHide-1.html
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
class MathDegreeHideAttributes extends XmlAttributeComponent<{ readonly hide: number }> {
protected readonly xmlKeys = { hide: "m:val" };
}
export class MathDegreeHide extends XmlComponent {
constructor() {
super("m:degHide");
this.root.push(new MathDegreeHideAttributes({ hide: 1 }));
}
}

View File

@ -0,0 +1,36 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { MathRun } from "../math-run";
import { MathDegree } from "./math-degree";
describe("MathDegree", () => {
describe("#constructor()", () => {
it("should create a MathDegree with correct root key", () => {
const mathDegree = new MathDegree();
const tree = new Formatter().format(mathDegree);
expect(tree).to.deep.equal({
"m:deg": {},
});
});
it("should create a MathDegree with correct root key with child", () => {
const mathDegree = new MathDegree([new MathRun("2")]);
const tree = new Formatter().format(mathDegree);
expect(tree).to.deep.equal({
"m:deg": [
{
"m:r": [
{
"m:t": ["2"],
},
],
},
],
});
});
});
});

View File

@ -0,0 +1,15 @@
// http://www.datypic.com/sc/ooxml/e-m_deg-1.html
import { XmlComponent } from "file/xml-components";
import { MathComponent } from "../math-component";
export class MathDegree extends XmlComponent {
constructor(children?: MathComponent[]) {
super("m:deg");
if (!!children) {
for (const child of children) {
this.root.push(child);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More