From 4d1a351649b80d1675019317f3ec04a59042c081 Mon Sep 17 00:00:00 2001 From: Dolan Date: Mon, 14 Apr 2025 16:43:15 +0530 Subject: [PATCH] Documentation and Refactoring (#3028) * Documentation and Refactoring * Documentation and Refactoring * Fix lint issues * Convert components to Builder style --------- Co-authored-by: Dolan Miu --- demo/71-page-borders-2.ts | 2 +- src/file/border/border.ts | 69 ++++++++ .../properties/doc-grid.spec.ts | 10 +- .../section-properties/properties/doc-grid.ts | 138 ++++++++++----- .../properties/header-footer-reference.ts | 27 ++- .../properties/line-number.ts | 125 ++++++++++++-- .../properties/page-size.spec.ts | 6 +- .../properties/page-size.ts | 158 ++++++++++++++---- .../section-properties/section-properties.ts | 12 +- src/file/drawing/anchor/anchor.spec.ts | 12 +- src/file/drawing/anchor/anchor.ts | 16 +- .../drawing/effect-extent/effect-extent.ts | 44 ++++- src/file/drawing/extent/extent-attributes.ts | 11 -- src/file/drawing/extent/extent.ts | 74 ++++++-- src/file/drawing/floating/align.spec.ts | 4 +- src/file/drawing/floating/align.ts | 33 ++-- .../drawing/floating/floating-position.ts | 90 ++++++++++ .../floating/horizontal-position.spec.ts | 8 +- .../drawing/floating/horizontal-position.ts | 59 ++++--- .../drawing/floating/position-offset.spec.ts | 6 +- src/file/drawing/floating/position-offset.ts | 24 ++- src/file/drawing/floating/simple-pos.spec.ts | 6 +- src/file/drawing/floating/simple-pos.ts | 82 ++++++--- .../floating/vertical-position.spec.ts | 8 +- .../drawing/floating/vertical-position.ts | 61 +++---- .../graphic-frame/graphic-frame-properties.ts | 33 +++- src/file/drawing/inline/inline.ts | 8 +- src/file/paragraph/math/bar/math-bar-pos.ts | 19 ++- .../math/bar/math-bar-properties.spec.ts | 7 +- .../paragraph/math/bar/math-bar-properties.ts | 15 +- src/file/paragraph/math/bar/math-bar.spec.ts | 4 +- src/file/paragraph/math/bar/math-bar.ts | 21 ++- .../math/brackets/math-angled-brackets.ts | 18 +- .../brackets/math-beginning-character.spec.ts | 6 +- .../math/brackets/math-beginning-character.ts | 20 +-- .../brackets/math-bracket-properties.spec.ts | 14 +- .../math/brackets/math-bracket-properties.ts | 26 +-- .../math/brackets/math-curly-brackets.ts | 14 +- .../math/brackets/math-ending-char.ts | 20 +-- .../brackets/math-ending-character.spec.ts | 6 +- .../math/brackets/math-round-brackets.ts | 8 +- .../math/brackets/math-square-brackets.ts | 14 +- .../paragraph/math/function/math-function.ts | 4 +- .../math/n-ary/math-accent-character.spec.ts | 4 +- .../math/n-ary/math-accent-character.ts | 20 +-- .../paragraph/math/n-ary/math-base.spec.ts | 6 +- src/file/paragraph/math/n-ary/math-base.ts | 18 +- .../paragraph/math/n-ary/math-integral.ts | 23 ++- .../math/n-ary/math-limit-location.spec.ts | 6 +- .../math/n-ary/math-limit-location.ts | 20 +-- .../paragraph/math/n-ary/math-limit-lower.ts | 4 +- .../paragraph/math/n-ary/math-limit-upper.ts | 4 +- .../math/n-ary/math-n-ary-properties.spec.ts | 28 +++- .../math/n-ary/math-n-ary-properties.ts | 48 +++--- .../math/n-ary/math-sub-script-hide.spec.ts | 6 +- .../math/n-ary/math-sub-script-hide.ts | 20 +-- .../math/n-ary/math-sub-script.spec.ts | 6 +- .../paragraph/math/n-ary/math-sub-script.ts | 18 +- src/file/paragraph/math/n-ary/math-sum.ts | 22 ++- .../math/n-ary/math-super-script-hide.spec.ts | 6 +- .../math/n-ary/math-super-script-hide.ts | 20 +-- .../math/n-ary/math-super-script.spec.ts | 6 +- .../paragraph/math/n-ary/math-super-script.ts | 18 +- .../paragraph/math/radical/math-radical.ts | 4 +- ...b-super-script-function-properties.spec.ts | 6 +- ...re-sub-super-script-function-properties.ts | 11 +- .../math-pre-sub-super-script-function.ts | 25 +-- ...ath-sub-script-function-properties.spec.ts | 6 +- .../math-sub-script-function-properties.ts | 11 +- .../sub-script/math-sub-script-function.ts | 10 +- ...b-super-script-function-properties.spec.ts | 6 +- ...th-sub-super-script-function-properties.ts | 11 +- .../math-sub-super-script-function.ts | 12 +- ...h-super-script-function-properties.spec.ts | 6 +- .../math-super-script-function-properties.ts | 11 +- .../math-super-script-function.ts | 10 +- src/file/textbox/shape/shape.ts | 21 +-- src/file/xml-components/simple-elements.ts | 3 +- 78 files changed, 1178 insertions(+), 620 deletions(-) delete mode 100644 src/file/drawing/extent/extent-attributes.ts diff --git a/demo/71-page-borders-2.ts b/demo/71-page-borders-2.ts index de03c7f875..c4dd97054d 100644 --- a/demo/71-page-borders-2.ts +++ b/demo/71-page-borders-2.ts @@ -1,7 +1,7 @@ // Example demonstrating page borders with style, colors and size +import { BorderStyle, Document, Packer, PageBorderDisplay, PageBorderOffsetFrom, PageBorderZOrder, Paragraph, TextRun } from "docx"; import * as fs from "fs"; -import { Document, Packer, TextRun, Paragraph, BorderStyle, PageBorderDisplay, PageBorderOffsetFrom, PageBorderZOrder } from "docx"; const doc = new Document({ sections: [ diff --git a/src/file/border/border.ts b/src/file/border/border.ts index 55942c2595..9ca63426bf 100644 --- a/src/file/border/border.ts +++ b/src/file/border/border.ts @@ -55,32 +55,101 @@ class BordersAttributes extends XmlAttributeComponent { }; } +/** + * Table borders are defined with the element. Child elements of this element specify the kinds of `border`: + * + * `bottom`, `end` (`right` in the previous version of the standard), `insideH`, `insideV`, `start` (`left` in the previous version of the standard), and `top`. + * + * Reference: http://officeopenxml.com/WPtableBorders.php + * + * ## XSD Schema + * ```xml + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * ``` + */ export const BorderStyle = { + /** a single line */ SINGLE: "single", + /** a line with a series of alternating thin and thick strokes */ DASH_DOT_STROKED: "dashDotStroked", + /** a dashed line */ DASHED: "dashed", + /** a dashed line with small gaps */ DASH_SMALL_GAP: "dashSmallGap", + /** a line with alternating dots and dashes */ DOT_DASH: "dotDash", + /** a line with a repeating dot - dot - dash sequence */ DOT_DOT_DASH: "dotDotDash", + /** a dotted line */ DOTTED: "dotted", + /** a double line */ DOUBLE: "double", + /** a double wavy line */ DOUBLE_WAVE: "doubleWave", + /** an inset set of lines */ INSET: "inset", + /** no border */ NIL: "nil", + /** no border */ NONE: "none", + /** an outset set of lines */ OUTSET: "outset", + /** a single line */ THICK: "thick", + /** a thick line contained within a thin line with a large-sized intermediate gap */ THICK_THIN_LARGE_GAP: "thickThinLargeGap", + /** a thick line contained within a thin line with a medium-sized intermediate gap */ THICK_THIN_MEDIUM_GAP: "thickThinMediumGap", + /** a thick line contained within a thin line with a small intermediate gap */ THICK_THIN_SMALL_GAP: "thickThinSmallGap", + /** a thin line contained within a thick line with a large-sized intermediate gap */ THIN_THICK_LARGE_GAP: "thinThickLargeGap", + /** a thick line contained within a thin line with a medium-sized intermediate gap */ THIN_THICK_MEDIUM_GAP: "thinThickMediumGap", + /** a thick line contained within a thin line with a small intermediate gap */ THIN_THICK_SMALL_GAP: "thinThickSmallGap", + /** a thin-thick-thin line with a large gap */ THIN_THICK_THIN_LARGE_GAP: "thinThickThinLargeGap", + /** a thin-thick-thin line with a medium gap */ THIN_THICK_THIN_MEDIUM_GAP: "thinThickThinMediumGap", + /** a thin-thick-thin line with a small gap */ THIN_THICK_THIN_SMALL_GAP: "thinThickThinSmallGap", + /** a three-staged gradient line, getting darker towards the paragraph */ THREE_D_EMBOSS: "threeDEmboss", + /** a three-staged gradient like, getting darker away from the paragraph */ THREE_D_ENGRAVE: "threeDEngrave", + /** a triple line */ TRIPLE: "triple", + /** a wavy line */ WAVE: "wave", } as const; diff --git a/src/file/document/body/section-properties/properties/doc-grid.spec.ts b/src/file/document/body/section-properties/properties/doc-grid.spec.ts index f2ee945f48..b950bb43b7 100644 --- a/src/file/document/body/section-properties/properties/doc-grid.spec.ts +++ b/src/file/document/body/section-properties/properties/doc-grid.spec.ts @@ -2,26 +2,26 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { DocumentGrid, DocumentGridType } from "."; +import { DocumentGridType, createDocumentGrid } from "."; -describe("DocumentGrid", () => { +describe("createDocumentGrid", () => { describe("#constructor()", () => { it("should create documentGrid with specified linePitch", () => { - const docGrid = new DocumentGrid(360); + const docGrid = createDocumentGrid({ linePitch: 360 }); const tree = new Formatter().format(docGrid); expect(tree["w:docGrid"]).to.deep.equal({ _attr: { "w:linePitch": 360 } }); }); it("should create documentGrid with specified linePitch and type", () => { - const docGrid = new DocumentGrid(360, undefined, DocumentGridType.LINES); + const docGrid = createDocumentGrid({ linePitch: 360, type: DocumentGridType.LINES }); const tree = new Formatter().format(docGrid); expect(tree["w:docGrid"]).to.deep.equal({ _attr: { "w:linePitch": 360, "w:type": "lines" } }); }); it("should create documentGrid with specified linePitch,charSpace and type", () => { - const docGrid = new DocumentGrid(346, -1541, DocumentGridType.LINES_AND_CHARS); + const docGrid = createDocumentGrid({ linePitch: 346, charSpace: -1541, type: DocumentGridType.LINES_AND_CHARS }); const tree = new Formatter().format(docGrid); expect(tree["w:docGrid"]).to.deep.equal({ _attr: { "w:linePitch": 346, "w:charSpace": -1541, "w:type": "linesAndChars" } }); diff --git a/src/file/document/body/section-properties/properties/doc-grid.ts b/src/file/document/body/section-properties/properties/doc-grid.ts index e9f086045a..03a1ebc0bc 100644 --- a/src/file/document/body/section-properties/properties/doc-grid.ts +++ b/src/file/document/body/section-properties/properties/doc-grid.ts @@ -1,53 +1,113 @@ -import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; import { decimalNumber } from "@util/values"; -// not implemented -// -// -// -// -// -// -// -// - -// -// -// -// -// - +/** + * Specifies the type of the current document grid, which defines the grid behavior. + * + * The grid can define a grid which snaps all East Asian characters to grid positions, but leaves Latin text with its default spacing; a grid which adds the specified character pitch to all characters on each row; or a grid which affects only the line pitch for the current section. + * + * Reference: https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_ST_DocGrid_topic_ID0ELYP2.html + * + * ## XSD Schema + * ```xml + * + * + * + * + * + * + * + * + * ``` + */ export const DocumentGridType = { + /** + * Specifies that no document grid shall be applied to the contents of the current section in the document. + */ DEFAULT: "default", + /** + * Specifies that the parent section shall have additional line pitch added to each line within it (as specified on the element (§2.6.5)) in order to maintain the specified number of lines per page. + */ LINES: "lines", + /** + * Specifies that the parent section shall have both the additional line pitch and character pitch added to each line and character within it (as specified on the element (§2.6.5)) in order to maintain a specific number of lines per page and characters per line. + * + * When this value is set, the input specified via the user interface may be allowed in exact number of line/character pitch units. */ LINES_AND_CHARS: "linesAndChars", + /** + * Specifies that the parent section shall have both the additional line pitch and character pitch added to each line and character within it (as specified on the element (§2.6.5)) in order to maintain a specific number of lines per page and characters per line. + * + * When this value is set, the input specified via the user interface may be restricted to the number of lines per page and characters per line, with the consumer or producer translating this information based on the current font data to get the resulting line and character pitch values + */ SNAP_TO_CHARS: "snapToChars", } as const; export type IDocGridAttributesProperties = { + /** + * Specifies the type of the current document grid, which defines the grid behavior. + * + * The grid can define a grid which snaps all East Asian characters to grid positions, but leaves Latin text with its default spacing; a grid which adds the specified character pitch to each character on each row; or a grid which affects only the line pitch for the current section. + */ readonly type?: (typeof DocumentGridType)[keyof typeof DocumentGridType]; - readonly linePitch?: number; + /** + * Specifies the number of lines to be allowed on the document grid for the current page assuming all lines have equal line pitch applied to them. This line pitch shall not be added to any line which appears within a table cell unless the element (§2.15.3.1) is present in the document's compatibility settings. + * + * This attribute is specified in twentieths of a point, and defines the pitch for each line of text on this page such that the desired number of single spaced lines of text fits on the current page. + * + * ```xml + * + * ``` + * + * The `linePitch` attribute specifies that 34.2 points is to the amount of pitch allowed for each line on this page in order to maintain the specific document grid. ] + * + * Individual paragraphs can override the line pitch information specified for the document grid by either: + * + * Specifying an exact line spacing value using the `lineRule` attribute of value exact on the element (§2.3.1.33). + * + * Specifying that the paragraph text shall not snap to the document grid via the element (§2.3.1.32). + * + * The possible values for this attribute are defined by the ST_DecimalNumber simple type (§2.18.16). + */ + readonly linePitch: number; + /** + * Specifies the number of characters to be allowed on the document grid for each line in this section. + * + * This attribute's value shall be specified by multiplying the difference between the desired character pitch and the character pitch for that character in the font size of the Normal font by 4096. + * + * This value shall then be used to add the character pitch for the specified point size to each character in the section [: This results in text in the Normal style having a specific number of characters per line. ] + * + * ```xml + * + * ``` + * The `charSpace` attribute specifies a value of 40960, which means that the delta between the character pitch of each character in the grid and the Normal font is 10 points, resulting in a character pitch of 11+10 = 21 points for all characters in this section. ] + * + * Individual runs of text can override the line pitch information specified for the document grid by specifying that the run text shall not snap to the document grid via the element (§2.3.2.32). + * + * The possible values for this attribute are defined by the `ST_DecimalNumber` simple type (§2.18.16). + */ readonly charSpace?: number; }; -export class DocGridAttributes extends XmlAttributeComponent { - protected readonly xmlKeys = { - type: "w:type", - linePitch: "w:linePitch", - charSpace: "w:charSpace", - }; -} - -export class DocumentGrid extends XmlComponent { - public constructor(linePitch: number, charSpace?: number, type?: (typeof DocumentGridType)[keyof typeof DocumentGridType]) { - super("w:docGrid"); - - this.root.push( - new DocGridAttributes({ - type: type, - linePitch: decimalNumber(linePitch), - charSpace: charSpace ? decimalNumber(charSpace) : undefined, - }), - ); - } -} +/** + * This element specifies the settings for the document grid, which enables precise layout of full-width East Asian language characters within a document by specifying the desired number of characters per line and lines per page for all East Asian text content in this section. + * + * Reference: https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_docGrid_topic_ID0EHU5S.html + * + * ```xml + * + * + * + * + * + * ``` + * @returns + */ +export const createDocumentGrid = ({ type, linePitch, charSpace }: IDocGridAttributesProperties): XmlComponent => + new BuilderElement({ + name: "w:docGrid", + attributes: { + type: { key: "w:type", value: type }, + linePitch: { key: "w:linePitch", value: decimalNumber(linePitch) }, + charSpace: { key: "w:charSpace", value: charSpace ? decimalNumber(charSpace) : undefined }, + }, + }); diff --git a/src/file/document/body/section-properties/properties/header-footer-reference.ts b/src/file/document/body/section-properties/properties/header-footer-reference.ts index ee8a45a226..568ccb4427 100644 --- a/src/file/document/body/section-properties/properties/header-footer-reference.ts +++ b/src/file/document/body/section-properties/properties/header-footer-reference.ts @@ -1,19 +1,30 @@ import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; -// -// -// -// -// -// -// +/** + * This simple type specifies the possible types of headers and footers which may be specified for a given header or footer reference in a document. This value determines the page(s) on which the current header or footer shall be displayed. + * + * Reference: https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_ST_HdrFtr_topic_ID0E2UW2.html + * + * ## XSD Schema + * ```xml + * + * + * + * + * + * + * + * ``` + */ export const HeaderFooterReferenceType = { + /** Specifies that this header or footer shall appear on every page in this section which is not overridden with a specific `even` or `first` page header/footer. In a section with all three types specified, this type shall be used on all odd numbered pages (counting from the `first` page in the section, not the section numbering). */ DEFAULT: "default", + /** Specifies that this header or footer shall appear on the first page in this section. The appearance of this header or footer is contingent on the setting of the `titlePg` element (§2.10.6). */ FIRST: "first", + /** Specifies that this header or footer shall appear on all even numbered pages in this section (counting from the first page in the section, not the section numbering). The appearance of this header or footer is contingent on the setting of the `evenAndOddHeaders` element (§2.10.1). */ EVEN: "even", } as const; -// // // // diff --git a/src/file/document/body/section-properties/properties/line-number.ts b/src/file/document/body/section-properties/properties/line-number.ts index 50bfd17ce0..bc25285497 100644 --- a/src/file/document/body/section-properties/properties/line-number.ts +++ b/src/file/document/body/section-properties/properties/line-number.ts @@ -1,35 +1,128 @@ -// http://officeopenxml.com/WPsectionLineNumbering.php import { BuilderElement, XmlComponent } from "@file/xml-components"; import { PositiveUniversalMeasure, decimalNumber, twipsMeasureValue } from "@util/values"; -// -// -// -// -// -// -// - +/** + * This simple type specifies when the line numbering in the parent section shall be reset to its restart value. The line numbering increments for each line (even if the line number itself is not displayed) until it reaches the restart point specified by this element. + * + * Reference: https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_ST_LineNumberRestart_topic_ID0EUS42.html + * + * ## XSD Schema + * + * ```xml + * + * + * + * + * + * + * + * ``` + */ export const LineNumberRestartFormat = { + /** + * ## Restart Line Numbering on Each Page + * + * Specifies that line numbering for the parent section shall restart to the starting value whenever a new page is displayed. + */ NEW_PAGE: "newPage", + /** + * ## Restart Line Numbering for Each Section + * + * Specifies that line numbering for the parent section shall restart to the starting value whenever the parent begins. + */ NEW_SECTION: "newSection", + /** + * ## Continue Line Numbering From Previous Section + * + * Specifies that line numbering for the parent section shall continue from the line numbering from the end of the previous section, if any. + */ CONTINUOUS: "continuous", } as const; -// -// -// -// -// -// - export type ILineNumberAttributes = { + /** + * Specifies the line number increments to be displayed in the current document. + * + * Although each line has an associated line number, only lines which are an even multiple of this value shall be displayed. + * + * ### Example + * + * ```xml + * + * ``` + * + * This setting ensures that only lines whose number is a multiple of (e.g. 5, 10, and 15) will have a line number displayed. ] + * + * The possible values for this attribute are defined by the ST_DecimalNumber simple type (§2.18.16). + */ readonly countBy?: number; + /** + * ## Line Numbering Starting Value + * + * Specifies the starting value used for the first line whenever the line numbering is restarted by use of the `restart` attribute. + * + * ### Example + * + * ```xml + * + * ``` + * + * The `start` attribute specifies that line numbers shall be counted starting from the number 3. + * + * The possible values for this attribute are defined by the ST_DecimalNumber simple type (§2.18.16). + */ readonly start?: number; + /** + * ## Line Numbering Restart Setting + * + * Specifies when the line numbering in this section shall be reset to the line number specified by the `start` attribute's value. + * + * The line numbering increments for each line (even if it is not displayed) until it reaches the restart point specified by this element. + * + * ### Example + * + * ```xml + * + * ... + * + * + * ``` + * + * The value of `newPage` specifies that the line numbers shall restart at the top of each page to the value specified by the `start` attribute. In this case, `newPage` is the default, so this value could have been omitted entirely. + * + * The possible values for this attribute are defined by the ST_LineNumberRestart simple type (§2.18.54). + */ readonly restart?: (typeof LineNumberRestartFormat)[keyof typeof LineNumberRestartFormat]; + /** + * Specifies the distance between the text margin and the edge of any line numbers appearing in that section. + * + * ```.xml + * + * ``` + * + * The possible values for this attribute are defined by the ST_TwipsMeasure simple type (§2.18.105). + */ readonly distance?: number | PositiveUniversalMeasure; }; +/** + * This element specifies the settings for line numbering to be displayed before each column of text in this section in the document. + * + * References: + * - https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_lnNumType_topic_ID0EVRAT.html + * - http://officeopenxml.com/WPsectionLineNumbering.php + * + * ## XSD Schema + * + * ```xml + * + * + * + * + * + * + * ``` + */ export const createLineNumberType = ({ countBy, start, restart, distance }: ILineNumberAttributes): XmlComponent => new BuilderElement({ name: "w:lnNumType", diff --git a/src/file/document/body/section-properties/properties/page-size.spec.ts b/src/file/document/body/section-properties/properties/page-size.spec.ts index 807d2312fc..f18753fced 100644 --- a/src/file/document/body/section-properties/properties/page-size.spec.ts +++ b/src/file/document/body/section-properties/properties/page-size.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { PageOrientation, PageSize } from "./page-size"; +import { PageOrientation, createPageSize } from "./page-size"; describe("PageSize", () => { describe("#constructor()", () => { it("should create page size with portrait", () => { - const properties = new PageSize(100, 200, PageOrientation.PORTRAIT); + const properties = createPageSize({ width: 100, height: 200, orientation: PageOrientation.PORTRAIT }); const tree = new Formatter().format(properties); expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]); @@ -15,7 +15,7 @@ describe("PageSize", () => { }); it("should create page size with horizontal and invert the lengths", () => { - const properties = new PageSize(100, 200, PageOrientation.LANDSCAPE); + const properties = createPageSize({ width: 100, height: 200, orientation: PageOrientation.LANDSCAPE }); const tree = new Formatter().format(properties); expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]); diff --git a/src/file/document/body/section-properties/properties/page-size.ts b/src/file/document/body/section-properties/properties/page-size.ts index 159921b4da..95d55ad75a 100644 --- a/src/file/document/body/section-properties/properties/page-size.ts +++ b/src/file/document/body/section-properties/properties/page-size.ts @@ -1,48 +1,132 @@ -import { NextAttributeComponent, XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; import { PositiveUniversalMeasure, twipsMeasureValue } from "@util/values"; -// -// -// -// -// -// +/** + * This simple type specifies the orientation of all pages in the parent section. This information is used to determine the actual paper size to use when printing the file. + * + * Reference: https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_ST_PageOrientation_topic_ID0EKBK3.html + * + * ## XSD Schema + * + * ```xml + * + * + * + * + * + * + * ``` + */ export const PageOrientation = { + /** + * ## Portrait Mode + * + * Specifies that pages in this section shall be printed in portrait mode. + */ PORTRAIT: "portrait", + /** + * ## Landscape Mode + * + * Specifies that pages in this section shall be printed in landscape mode, which prints the page contents with a 90 degree rotation with respect to the normal page orientation. + */ LANDSCAPE: "landscape", } as const; -// -// -// -// -// -// export type IPageSizeAttributes = { - readonly width?: number | PositiveUniversalMeasure; - readonly height?: number | PositiveUniversalMeasure; + /** + * ## Page Width + * + * This attribute indicates the width (in twentieths of a point) for all pages in the current section. + * + * ### Example + * + * ```xml + * + * ``` + * + * All pages in this section are displayed on a page that is 15840 twentieths of a point (11") wide. + * + * The possible values for this attribute are defined by the ST_TwipsMeasure simple type (§2.18.105). + */ + readonly width: number | PositiveUniversalMeasure; + /** + * ## Page Height + * + * Specifies the height (in twentieths of a point) for all pages in the current section. + * + * ### Example + * + * ```xml + * + * ``` + * + * All pages in this section are displayed on a page that is `12240` twentieths of a point (`8.5"`) tall. + * + * The possible values for this attribute are defined by the `ST_TwipsMeasure` simple type (§2.18.105). + */ + readonly height: number | PositiveUniversalMeasure; + /** + * ## Page Orientation + * + * Specifies the orientation of all pages in this section. + * + * This information is used to determine the actual paper size to use on the printer. + * + * This implies that the actual paper size width and height are reversed for pages in this section. If this attribute is omitted, then portrait shall be implied. + * + * ### Example + * + * ```xml + * + * ``` + * + * Although the page width is 11", and page height is 8.5", according to the `w` and `h` attributes, because the `orient` attribute is set to landscape, pages in this section are printed on 8.5x11" paper in landscape mode. + * + * The possible values for this attribute are defined by the `ST_PageOrientation` simple type (§2.18.71). + */ readonly orientation?: (typeof PageOrientation)[keyof typeof PageOrientation]; + /** + * ## Printer Paper Code + * + * Specifies a printer-specific paper code for the paper type, which shall be used by the printer for pages in this section. + * + * This code is stored to ensure the proper paper type is chosen if the specified paper size matches the sizes of multiple paper types supported by the current printer. + * + * It will be sent to the printer and used by the printer to determine the appropriate paper type to use when printing. + * + * This value is not interpreted or modified other than storing it as specified by the printer. + * + * The possible values for this attribute are defined by the `ST_DecimalNumber` simple type (§2.18.16). + */ + readonly code?: number; }; -export class PageSize extends XmlComponent { - public constructor( - width: number | PositiveUniversalMeasure, - height: number | PositiveUniversalMeasure, - orientation: (typeof PageOrientation)[keyof typeof PageOrientation], - ) { - super("w:pgSz"); - - const flip = orientation === PageOrientation.LANDSCAPE; - - const widthTwips = twipsMeasureValue(width); - const heightTwips = twipsMeasureValue(height); - - this.root.push( - new NextAttributeComponent({ - width: { key: "w:w", value: flip ? heightTwips : widthTwips }, - height: { key: "w:h", value: flip ? widthTwips : heightTwips }, - orientation: { key: "w:orient", value: orientation }, - }), - ); - } -} +/** + * This element specifies the properties (size and orientation) for all pages in the current section. + * + * Reference: https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_pgSz_topic_ID0ENEDT.html?hl=pgsz%2Cpage%2Csize + * + * ## XSD Schema + * + * ```xml + * + * + * + * + * + * + * ``` + */ +export const createPageSize = ({ width, height, orientation, code }: IPageSizeAttributes): XmlComponent => { + const widthTwips = twipsMeasureValue(width); + const heightTwips = twipsMeasureValue(height); + return new BuilderElement({ + name: "w:pgSz", + attributes: { + width: { key: "w:w", value: orientation === PageOrientation.LANDSCAPE ? heightTwips : widthTwips }, + height: { key: "w:h", value: orientation === PageOrientation.LANDSCAPE ? widthTwips : heightTwips }, + orientation: { key: "w:orient", value: orientation }, + code: { key: "w:code", value: code }, + }, + }); +}; diff --git a/src/file/document/body/section-properties/section-properties.ts b/src/file/document/body/section-properties/section-properties.ts index 3a83945691..004f2c2cce 100644 --- a/src/file/document/body/section-properties/section-properties.ts +++ b/src/file/document/body/section-properties/section-properties.ts @@ -6,13 +6,13 @@ import { VerticalAlign, VerticalAlignElement } from "@file/vertical-align"; import { OnOffElement, XmlComponent } from "@file/xml-components"; import { Columns, IColumnsAttributes } from "./properties/columns"; -import { DocumentGrid, IDocGridAttributesProperties } from "./properties/doc-grid"; +import { IDocGridAttributesProperties, createDocumentGrid } from "./properties/doc-grid"; import { HeaderFooterReference, HeaderFooterReferenceType, HeaderFooterType } from "./properties/header-footer-reference"; import { ILineNumberAttributes, createLineNumberType } from "./properties/line-number"; import { IPageBordersOptions, PageBorders } from "./properties/page-borders"; import { IPageMarginAttributes, PageMargin } from "./properties/page-margin"; import { IPageNumberTypeAttributes, PageNumberType } from "./properties/page-number"; -import { IPageSizeAttributes, PageOrientation, PageSize } from "./properties/page-size"; +import { IPageSizeAttributes, PageOrientation, createPageSize } from "./properties/page-size"; import { PageTextDirection, PageTextDirectionType } from "./properties/page-text-direction"; import { SectionType, Type } from "./properties/section-type"; @@ -24,13 +24,13 @@ export type IHeaderFooterGroup = { export type ISectionPropertiesOptions = { readonly page?: { - readonly size?: IPageSizeAttributes; + readonly size?: Partial; readonly margin?: IPageMarginAttributes; readonly pageNumbers?: IPageNumberTypeAttributes; readonly borders?: IPageBordersOptions; readonly textDirection?: (typeof PageTextDirectionType)[keyof typeof PageTextDirectionType]; }; - readonly grid?: IDocGridAttributesProperties; + readonly grid?: Partial; readonly headerWrapperGroup?: IHeaderFooterGroup; readonly footerWrapperGroup?: IHeaderFooterGroup; readonly lineNumbers?: ILineNumberAttributes; @@ -128,7 +128,7 @@ export class SectionProperties extends XmlComponent { this.root.push(new Type(type)); } - this.root.push(new PageSize(width, height, orientation)); + this.root.push(createPageSize({ width, height, orientation })); this.root.push(new PageMargin(top, right, bottom, left, header, footer, gutter)); if (borders) { @@ -157,7 +157,7 @@ export class SectionProperties extends XmlComponent { this.root.push(new PageTextDirection(textDirection)); } - this.root.push(new DocumentGrid(linePitch, charSpace, gridType)); + this.root.push(createDocumentGrid({ linePitch, charSpace, type: gridType })); } private addHeaderFooterGroup( diff --git a/src/file/drawing/anchor/anchor.spec.ts b/src/file/drawing/anchor/anchor.spec.ts index b5f5f2e3e3..d986de4171 100644 --- a/src/file/drawing/anchor/anchor.spec.ts +++ b/src/file/drawing/anchor/anchor.spec.ts @@ -91,28 +91,20 @@ describe("Anchor", () => { // 2: horizontal position const horizontalPosition = newJson.root[2]; assert.equal(horizontalPosition.rootKey, "wp:positionH"); - assert.include(horizontalPosition.root[0].root, { - relativeFrom: "page", - }); + assert.equal(horizontalPosition.root[1].rootKey, "wp:posOffset"); assert.include(horizontalPosition.root[1].root[0], 0); // 3: vertical position const verticalPosition = newJson.root[3]; assert.equal(verticalPosition.rootKey, "wp:positionV"); - assert.include(verticalPosition.root[0].root, { - relativeFrom: "page", - }); + assert.equal(verticalPosition.root[1].rootKey, "wp:posOffset"); assert.include(verticalPosition.root[1].root[0], 0); // 4: extent const extent = newJson.root[4]; assert.equal(extent.rootKey, "wp:extent"); - assert.include(extent.root[0].root, { - cx: 952500, - cy: 952500, - }); // 5: effect extent const effectExtent = newJson.root[5]; diff --git a/src/file/drawing/anchor/anchor.ts b/src/file/drawing/anchor/anchor.ts index 2d4ba1aa20..9e6d445c43 100644 --- a/src/file/drawing/anchor/anchor.ts +++ b/src/file/drawing/anchor/anchor.ts @@ -3,13 +3,13 @@ import { IMediaData, IMediaDataTransformation } from "@file/media"; import { XmlComponent } from "@file/xml-components"; import { IDrawingOptions } from "../drawing"; -import { HorizontalPosition, IFloating, SimplePos, VerticalPosition } from "../floating"; +import { IFloating, createHorizontalPosition, createSimplePos, createVerticalPosition } from "../floating"; import { Graphic } from "../inline/graphic"; import { TextWrappingType, WrapNone, WrapSquare, WrapTight, WrapTopAndBottom } from "../text-wrap"; import { DocProperties } from "./../doc-properties/doc-properties"; import { createEffectExtent } from "./../effect-extent/effect-extent"; -import { Extent } from "./../extent/extent"; -import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties"; +import { createExtent } from "./../extent/extent"; +import { createGraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties"; import { AnchorAttributes } from "./anchor-attributes"; // @@ -74,10 +74,10 @@ export class Anchor extends XmlComponent { }), ); - this.root.push(new SimplePos()); - this.root.push(new HorizontalPosition(floating.horizontalPosition)); - this.root.push(new VerticalPosition(floating.verticalPosition)); - this.root.push(new Extent(transform.emus.x, transform.emus.y)); + this.root.push(createSimplePos()); + this.root.push(createHorizontalPosition(floating.horizontalPosition)); + this.root.push(createVerticalPosition(floating.verticalPosition)); + this.root.push(createExtent({ x: transform.emus.x, y: transform.emus.y })); this.root.push(createEffectExtent({ top: 0, right: 0, bottom: 0, left: 0 })); if (drawingOptions.floating !== undefined && drawingOptions.floating.wrap !== undefined) { @@ -100,7 +100,7 @@ export class Anchor extends XmlComponent { } this.root.push(new DocProperties(drawingOptions.docProperties)); - this.root.push(new GraphicFrameProperties()); + this.root.push(createGraphicFrameProperties()); this.root.push(new Graphic({ mediaData, transform, outline: drawingOptions.outline })); } } diff --git a/src/file/drawing/effect-extent/effect-extent.ts b/src/file/drawing/effect-extent/effect-extent.ts index 90ee950886..16e39cf021 100644 --- a/src/file/drawing/effect-extent/effect-extent.ts +++ b/src/file/drawing/effect-extent/effect-extent.ts @@ -1,18 +1,50 @@ import { BuilderElement, XmlComponent } from "@file/xml-components"; export type EffectExtentAttributes = { + /** + * ## Additional Extent on Top Edge + * + * Specifies the additional length, in EMUs, which shall be added to the top edge of the DrawingML object to determine its actual top edge including effects. + */ readonly top: number; + /** + * ## Additional Extent on Right Edge + * + * Specifies the additional length, in EMUs, which shall be added to the right edge of the DrawingML object to determine its actual right edge including effects. + */ readonly right: number; + /** + * ## Additional Extent on Bottom Edge + * + * Specifies the additional length, in EMUs, which shall be added to the bottom edge of the DrawingML object to determine its actual bottom edge including effects. + */ readonly bottom: number; + /** + * ## Additional Extent on Left Edge + * + * Specifies the additional length, in EMUs, which shall be added to the left edge of the DrawingML object to determine its actual left edge including effects. + */ readonly left: number; }; -// -// -// -// -// -// +/** + * This element specifies the additional extent which shall be added to each edge of the image (top, bottom, left, right) in order to compensate for any drawing effects applied to the DrawingML object. + * + * The `` element (§5.5.2.7) specifies the size of the actual DrawingML object; however, an object may have effects applied which change its overall size + * + * Reference: https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_effectExtent_topic_ID0E5O3OB.html + * + * ## XSD Schema + * + * ```xml + * + * + * + * + * + * + * ``` + */ export const createEffectExtent = ({ top, right, bottom, left }: EffectExtentAttributes): XmlComponent => new BuilderElement({ name: "wp:effectExtent", diff --git a/src/file/drawing/extent/extent-attributes.ts b/src/file/drawing/extent/extent-attributes.ts deleted file mode 100644 index 8dd447ffe5..0000000000 --- a/src/file/drawing/extent/extent-attributes.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { XmlAttributeComponent } from "@file/xml-components"; - -export class ExtentAttributes extends XmlAttributeComponent<{ - readonly cx?: number; - readonly cy?: number; -}> { - protected readonly xmlKeys = { - cx: "cx", - cy: "cy", - }; -} diff --git a/src/file/drawing/extent/extent.ts b/src/file/drawing/extent/extent.ts index 2a4d80aff7..1da44a67c5 100644 --- a/src/file/drawing/extent/extent.ts +++ b/src/file/drawing/extent/extent.ts @@ -1,18 +1,60 @@ -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -import { ExtentAttributes } from "./extent-attributes"; +type ExtentAttributes = { + /** + * ## Extent Length + * + * Specifies the length of the extents rectangle in EMUs. This rectangle shall dictate the size of the object as displayed (the result of any scaling to the original object). + * + * + * ### Example + * + * ```xml + * <... cx="1828800" cy="200000"/> + * ``` + * + * The `cx` attributes specifies that this object has a height of `1828800` EMUs (English Metric Units). + * + * The possible values for this attribute are defined by the `ST_PositiveCoordinate` simple type (§5.1.12.42). + */ + readonly x?: number; + /** + * ## Extent Width + * + * Specifies the width of the extents rectangle in EMUs. This rectangle shall dictate the size of the object as displayed (the result of any scaling to the original object). + * + * ### Example + * + * ```xml + * <... cx="1828800" cy="200000"/> + * ``` + * + * The `cy` attribute specifies that this object has a width of `200000` EMUs (English Metric Units). + * + * The possible values for this attribute are defined by the `ST_PositiveCoordinate` simple type (§5.1.12.42). + */ + readonly y?: number; +}; -export class Extent extends XmlComponent { - private readonly attributes: ExtentAttributes; - - public constructor(x: number, y: number) { - super("wp:extent"); - - this.attributes = new ExtentAttributes({ - cx: x, - cy: y, - }); - - this.root.push(this.attributes); - } -} +/** + * This element specifies the extents of the parent `DrawingML` object within the document (i.e. its final height and width). + * + * Reference: https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_extent_topic_ID0EQB4OB.html + * + * ## XSD Schema + * + * ```xml + * + * + * + * + * ``` + */ +export const createExtent = ({ x, y }: ExtentAttributes): XmlComponent => + new BuilderElement({ + name: "wp:extent", + attributes: { + x: { key: "cx", value: x }, + y: { key: "cy", value: y }, + }, + }); diff --git a/src/file/drawing/floating/align.spec.ts b/src/file/drawing/floating/align.spec.ts index fb25860feb..e6b2973965 100644 --- a/src/file/drawing/floating/align.spec.ts +++ b/src/file/drawing/floating/align.spec.ts @@ -3,12 +3,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; import { VerticalPositionAlign } from "@file/shared/alignment"; -import { Align } from "./align"; +import { createAlign } from "./align"; describe("Align", () => { describe("#constructor()", () => { it("should create a element with correct root key", () => { - const tree = new Formatter().format(new Align(VerticalPositionAlign.CENTER)); + const tree = new Formatter().format(createAlign(VerticalPositionAlign.CENTER)); expect(tree).to.deep.equal({ "wp:align": ["center"], }); diff --git a/src/file/drawing/floating/align.ts b/src/file/drawing/floating/align.ts index 4a2cf0bcd7..d4dcb49805 100644 --- a/src/file/drawing/floating/align.ts +++ b/src/file/drawing/floating/align.ts @@ -1,14 +1,23 @@ -// http://officeopenxml.com/drwPicFloating-position.php import { HorizontalPositionAlign, VerticalPositionAlign } from "@file/shared/alignment"; -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -export class Align extends XmlComponent { - public constructor( - value: - | (typeof HorizontalPositionAlign)[keyof typeof HorizontalPositionAlign] - | (typeof VerticalPositionAlign)[keyof typeof VerticalPositionAlign], - ) { - super("wp:align"); - this.root.push(value); - } -} +/** + * # Relative Horizontal/Vertical Alignment + * + * This element specifies how a DrawingML object shall be horizontally/vertically aligned relative to the horizontal/vertical alignment base defined by the parent element. Once an alignment base is defined, this element shall determine how the DrawingML object shall be aligned relative to that location. + * + * References: + * - https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_align_topic_ID0EYZZOB.html + * - https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_align_topic_ID0ET4ZOB.html + * - http://officeopenxml.com/drwPicFloating-position.php + */ +export const createAlign = ( + value: + | (typeof HorizontalPositionAlign)[keyof typeof HorizontalPositionAlign] + | (typeof VerticalPositionAlign)[keyof typeof VerticalPositionAlign], +): XmlComponent => + new BuilderElement({ + name: "wp:align", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + children: [value as any], + }); diff --git a/src/file/drawing/floating/floating-position.ts b/src/file/drawing/floating/floating-position.ts index dc4b7fb8e5..379765fb0c 100644 --- a/src/file/drawing/floating/floating-position.ts +++ b/src/file/drawing/floating/floating-position.ts @@ -4,25 +4,115 @@ import { HorizontalPositionAlign, VerticalPositionAlign } from "@file/shared/ali import { ITextWrapping } from "../text-wrap"; +/** + * Horizontal Relative Positioning + * + * Reference: https://www.datypic.com/sc/ooxml/t-wp_ST_RelFromH.html + */ export const HorizontalPositionRelativeFrom = { + /** + * ## Character + * + * Specifies that the horizontal positioning shall be relative to the position of the anchor within its run content. + */ CHARACTER: "character", + /** + * ## Column + * + * Specifies that the horizontal positioning shall be relative to the extents of the column which contains its anchor. + */ COLUMN: "column", + /** + * ## Inside Margin + * + * Specifies that the horizontal positioning shall be relative to the inside margin of the current page (the left margin on odd pages, right on even pages). + */ INSIDE_MARGIN: "insideMargin", + /** + * ## Left Margin + * + * Specifies that the horizontal positioning shall be relative to the left margin of the page. + */ LEFT_MARGIN: "leftMargin", + /** + * ## Page Margin + * + * Specifies that the horizontal positioning shall be relative to the page margins. + */ MARGIN: "margin", + /** + * ## Outside Margin + * + * Specifies that the horizontal positioning shall be relative to the outside margin of the current page (the right margin on odd pages, left on even pages). + */ OUTSIDE_MARGIN: "outsideMargin", + /** + * ## Page Edge + * + * Specifies that the horizontal positioning shall be relative to the edge of the page. + */ PAGE: "page", + /** + * ## Right Margin + * + * Specifies that the horizontal positioning shall be relative to the right margin of the page. + */ RIGHT_MARGIN: "rightMargin", } as const; +/** + * Vertical Relative Positioning + * + * Reference: https://www.datypic.com/sc/ooxml/t-wp_ST_RelFromV.html + */ export const VerticalPositionRelativeFrom = { + /** + * ## Bottom Margin + * + * Specifies that the vertical positioning shall be relative to the bottom margin of the current page. + */ BOTTOM_MARGIN: "bottomMargin", + /** + * ## Inside Margin + * + * Specifies that the vertical positioning shall be relative to the inside margin of the current page. + */ INSIDE_MARGIN: "insideMargin", + /** + * ## Line + * + * Specifies that the vertical positioning shall be relative to the line containing the anchor character. + */ LINE: "line", + /** + * ## Page Margin + * + * Specifies that the vertical positioning shall be relative to the page margins. + */ MARGIN: "margin", + /** + * ## Outside Margin + * + * Specifies that the vertical positioning shall be relative to the outside margin of the current page. + */ OUTSIDE_MARGIN: "outsideMargin", + /** + * ## Page Edge + * + * Specifies that the vertical positioning shall be relative to the edge of the page. + */ PAGE: "page", + /** + * ## Paragraph + * + * Specifies that the vertical positioning shall be relative to the paragraph which contains the drawing anchor. + */ PARAGRAPH: "paragraph", + /** + * ## Top Margin + * + * Specifies that the vertical positioning shall be relative to the top margin of the current page. + */ TOP_MARGIN: "topMargin", } as const; diff --git a/src/file/drawing/floating/horizontal-position.spec.ts b/src/file/drawing/floating/horizontal-position.spec.ts index ad17580c07..a39cebf456 100644 --- a/src/file/drawing/floating/horizontal-position.spec.ts +++ b/src/file/drawing/floating/horizontal-position.spec.ts @@ -4,13 +4,13 @@ import { Formatter } from "@export/formatter"; import { HorizontalPositionAlign } from "@file/shared/alignment"; import { HorizontalPositionRelativeFrom } from "./floating-position"; -import { HorizontalPosition } from "./horizontal-position"; +import { createHorizontalPosition } from "./horizontal-position"; describe("HorizontalPosition", () => { describe("#constructor()", () => { it("should create a element with position align", () => { const tree = new Formatter().format( - new HorizontalPosition({ + createHorizontalPosition({ relative: HorizontalPositionRelativeFrom.MARGIN, align: HorizontalPositionAlign.CENTER, }), @@ -31,7 +31,7 @@ describe("HorizontalPosition", () => { it("should create a element with offset", () => { const tree = new Formatter().format( - new HorizontalPosition({ + createHorizontalPosition({ relative: HorizontalPositionRelativeFrom.MARGIN, offset: 40, }), @@ -51,7 +51,7 @@ describe("HorizontalPosition", () => { }); it("should require one of align or offset", () => { - expect(() => new HorizontalPosition({})).to.throw(); + expect(() => createHorizontalPosition({})).to.throw(); }); }); }); diff --git a/src/file/drawing/floating/horizontal-position.ts b/src/file/drawing/floating/horizontal-position.ts index 0cdc495c5b..4e6e32d889 100644 --- a/src/file/drawing/floating/horizontal-position.ts +++ b/src/file/drawing/floating/horizontal-position.ts @@ -1,34 +1,33 @@ // http://officeopenxml.com/drwPicFloating-position.php -import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -import { Align } from "./align"; +import { createAlign } from "./align"; import { HorizontalPositionRelativeFrom, IHorizontalPositionOptions } from "./floating-position"; -import { PositionOffset } from "./position-offset"; +import { createPositionOffset } from "./position-offset"; -class HorizontalPositionAttributes extends XmlAttributeComponent<{ - readonly relativeFrom: (typeof HorizontalPositionRelativeFrom)[keyof typeof HorizontalPositionRelativeFrom]; -}> { - protected readonly xmlKeys = { - relativeFrom: "relativeFrom", - }; -} - -export class HorizontalPosition extends XmlComponent { - public constructor(horizontalPosition: IHorizontalPositionOptions) { - super("wp:positionH"); - - this.root.push( - new HorizontalPositionAttributes({ - relativeFrom: horizontalPosition.relative || HorizontalPositionRelativeFrom.PAGE, - }), - ); - - if (horizontalPosition.align) { - this.root.push(new Align(horizontalPosition.align)); - } else if (horizontalPosition.offset !== undefined) { - this.root.push(new PositionOffset(horizontalPosition.offset)); - } else { - throw new Error("There is no configuration provided for floating position (Align or offset)"); - } - } -} +/** + * Horizontal Positioning + * + * Reference: https://www.datypic.com/sc/ooxml/e-wp_positionH-1.html + */ +export const createHorizontalPosition = ({ relative, align, offset }: IHorizontalPositionOptions): XmlComponent => + new BuilderElement<{ + /** Horizontal Position Relative Base */ + readonly relativeFrom: (typeof HorizontalPositionRelativeFrom)[keyof typeof HorizontalPositionRelativeFrom]; + }>({ + name: "wp:positionH", + attributes: { + relativeFrom: { key: "relativeFrom", value: relative ?? HorizontalPositionRelativeFrom.PAGE }, + }, + children: [ + (() => { + if (align) { + return createAlign(align); + } else if (offset !== undefined) { + return createPositionOffset(offset); + } else { + throw new Error("There is no configuration provided for floating position (Align or offset)"); + } + })(), + ], + }); diff --git a/src/file/drawing/floating/position-offset.spec.ts b/src/file/drawing/floating/position-offset.spec.ts index 3c9009decf..7192831999 100644 --- a/src/file/drawing/floating/position-offset.spec.ts +++ b/src/file/drawing/floating/position-offset.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { PositionOffset } from "./position-offset"; +import { createPositionOffset } from "./position-offset"; -describe("PositionOffset", () => { +describe("createPositionOffset", () => { describe("#constructor()", () => { it("should create a element with correct root key", () => { - const tree = new Formatter().format(new PositionOffset(50)); + const tree = new Formatter().format(createPositionOffset(50)); expect(tree).to.deep.equal({ "wp:posOffset": ["50"], }); diff --git a/src/file/drawing/floating/position-offset.ts b/src/file/drawing/floating/position-offset.ts index aa49e75fe2..1e0b5fc978 100644 --- a/src/file/drawing/floating/position-offset.ts +++ b/src/file/drawing/floating/position-offset.ts @@ -1,9 +1,17 @@ -// http://officeopenxml.com/drwPicFloating-position.php -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -export class PositionOffset extends XmlComponent { - public constructor(offsetValue: number) { - super("wp:posOffset"); - this.root.push(offsetValue.toString()); - } -} +/** + * # Absolute Position Offset + * + * This element specifies an absolute measurement for the positioning of a floating DrawingML object within a WordprocessingML document. This measurement shall be calculated relative to the top left edge of the positioning base specified by the parent element's `relativeFrom` attribute. + * + * References: + * - https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_posOffset_topic_ID0EMG6OB.html + * - http://officeopenxml.com/drwPicFloating-position.php + */ +export const createPositionOffset = (offsetValue: number): XmlComponent => + new BuilderElement({ + name: "wp:posOffset", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + children: [offsetValue.toString() as any], + }); diff --git a/src/file/drawing/floating/simple-pos.spec.ts b/src/file/drawing/floating/simple-pos.spec.ts index 536f6177cc..52f54dc9b5 100644 --- a/src/file/drawing/floating/simple-pos.spec.ts +++ b/src/file/drawing/floating/simple-pos.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { SimplePos } from "./simple-pos"; +import { createSimplePos } from "./simple-pos"; -describe("SimplePos", () => { +describe("createSimplePos", () => { describe("#constructor()", () => { it("should create a element with correct root key", () => { - const tree = new Formatter().format(new SimplePos()); + const tree = new Formatter().format(createSimplePos()); expect(tree).to.deep.equal({ "wp:simplePos": { _attr: { diff --git a/src/file/drawing/floating/simple-pos.ts b/src/file/drawing/floating/simple-pos.ts index 85a92a72c0..ccb822ecc8 100644 --- a/src/file/drawing/floating/simple-pos.ts +++ b/src/file/drawing/floating/simple-pos.ts @@ -1,26 +1,62 @@ // http://officeopenxml.com/drwPicFloating-position.php -import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; - -class SimplePosAttributes extends XmlAttributeComponent<{ - readonly x: number; - readonly y: number; -}> { - protected readonly xmlKeys = { - x: "x", - y: "y", - }; -} - -export class SimplePos extends XmlComponent { - public constructor() { - super("wp:simplePos"); +import { BuilderElement, XmlComponent } from "@file/xml-components"; +/** + * # Simple Positioning Coordinates + * + * This element specifies the coordinates at which a DrawingML object shall be positioned relative to the top-left edge of its page, when the `simplePos` attribute is specified on the element (§5.5.2.3). + * + * References: + * - https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_simplePos_topic_ID0E5K6OB.html + * - http://officeopenxml.com/drwPicFloating-position.php + * + * ## XSD Schema + * + * ```xml + * + * + * + * + * ``` + */ +export const createSimplePos = (): XmlComponent => + new BuilderElement<{ + /** + * ## X-Axis Coordinate + * + * Specifies a coordinate on the x-axis. The origin point for this coordinate shall be specified by the parent XML element. + * + * ### Example + * + * ```xml + * + * ``` + * + * The `x` attribute defines an x-coordinate of 0. + * + * The possible values for this attribute are defined by the `ST_Coordinate` simple type (§5.1.12.16). + */ + readonly x: number; + /** + * ## Y-Axis Coordinate + * + * Specifies a coordinate on the x-axis. The origin point for this coordinate shall be specified by the parent XML element. + * + * ### Example + * ```xml + * + * ``` + * + * The `y` attribute defines a y-coordinate of 100. + * + * The possible values for this attribute are defined by the `ST_Coordinate` simple type (§5.1.12.16). + */ + readonly y: number; + }>({ + name: "wp:simplePos", // NOTE: It's not fully supported in Microsoft Word, but this element is needed anyway - this.root.push( - new SimplePosAttributes({ - x: 0, - y: 0, - }), - ); - } -} + attributes: { + x: { key: "x", value: 0 }, + y: { key: "y", value: 0 }, + }, + }); diff --git a/src/file/drawing/floating/vertical-position.spec.ts b/src/file/drawing/floating/vertical-position.spec.ts index 7d8762305a..1541da7cc8 100644 --- a/src/file/drawing/floating/vertical-position.spec.ts +++ b/src/file/drawing/floating/vertical-position.spec.ts @@ -4,13 +4,13 @@ import { Formatter } from "@export/formatter"; import { VerticalPositionAlign } from "@file/shared/alignment"; import { VerticalPositionRelativeFrom } from "./floating-position"; -import { VerticalPosition } from "./vertical-position"; +import { createVerticalPosition } from "./vertical-position"; describe("VerticalPosition", () => { describe("#constructor()", () => { it("should create a element with position align", () => { const tree = new Formatter().format( - new VerticalPosition({ + createVerticalPosition({ relative: VerticalPositionRelativeFrom.MARGIN, align: VerticalPositionAlign.INSIDE, }), @@ -31,7 +31,7 @@ describe("VerticalPosition", () => { it("should create a element with offset", () => { const tree = new Formatter().format( - new VerticalPosition({ + createVerticalPosition({ relative: VerticalPositionRelativeFrom.MARGIN, offset: 40, }), @@ -51,7 +51,7 @@ describe("VerticalPosition", () => { }); it("should require one of align or offset", () => { - expect(() => new VerticalPosition({})).to.throw(); + expect(() => createVerticalPosition({})).to.throw(); }); }); }); diff --git a/src/file/drawing/floating/vertical-position.ts b/src/file/drawing/floating/vertical-position.ts index 855a1f43b4..bacd08819a 100644 --- a/src/file/drawing/floating/vertical-position.ts +++ b/src/file/drawing/floating/vertical-position.ts @@ -1,34 +1,35 @@ // http://officeopenxml.com/drwPicFloating-position.php -import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -import { Align } from "./align"; +import { createAlign } from "./align"; import { IVerticalPositionOptions, VerticalPositionRelativeFrom } from "./floating-position"; -import { PositionOffset } from "./position-offset"; +import { createPositionOffset } from "./position-offset"; -class VerticalPositionAttributes extends XmlAttributeComponent<{ - readonly relativeFrom: (typeof VerticalPositionRelativeFrom)[keyof typeof VerticalPositionRelativeFrom]; -}> { - protected readonly xmlKeys = { - relativeFrom: "relativeFrom", - }; -} - -export class VerticalPosition extends XmlComponent { - public constructor(verticalPosition: IVerticalPositionOptions) { - super("wp:positionV"); - - this.root.push( - new VerticalPositionAttributes({ - relativeFrom: verticalPosition.relative || VerticalPositionRelativeFrom.PAGE, - }), - ); - - if (verticalPosition.align) { - this.root.push(new Align(verticalPosition.align)); - } else if (verticalPosition.offset !== undefined) { - this.root.push(new PositionOffset(verticalPosition.offset)); - } else { - throw new Error("There is no configuration provided for floating position (Align or offset)"); - } - } -} +/** + * Vertical Positioning + * + * This simple type specifies the possible values for the base from which the relative vertical positioning of an object shall be calculated. + * + * Reference: https://www.datypic.com/sc/ooxml/e-wp_positionV-1.html + */ +export const createVerticalPosition = ({ relative, align, offset }: IVerticalPositionOptions): XmlComponent => + new BuilderElement<{ + /** Vertical Position Relative Base */ + readonly relativeFrom: (typeof VerticalPositionRelativeFrom)[keyof typeof VerticalPositionRelativeFrom]; + }>({ + name: "wp:positionV", + attributes: { + relativeFrom: { key: "relativeFrom", value: relative ?? VerticalPositionRelativeFrom.PAGE }, + }, + children: [ + (() => { + if (align) { + return createAlign(align); + } else if (offset !== undefined) { + return createPositionOffset(offset); + } else { + throw new Error("There is no configuration provided for floating position (Align or offset)"); + } + })(), + ], + }); diff --git a/src/file/drawing/graphic-frame/graphic-frame-properties.ts b/src/file/drawing/graphic-frame/graphic-frame-properties.ts index 03d0cd2728..624768acc8 100644 --- a/src/file/drawing/graphic-frame/graphic-frame-properties.ts +++ b/src/file/drawing/graphic-frame/graphic-frame-properties.ts @@ -1,11 +1,28 @@ -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; import { GraphicFrameLocks } from "./graphic-frame-locks/graphic-frame-locks"; -export class GraphicFrameProperties extends XmlComponent { - public constructor() { - super("wp:cNvGraphicFramePr"); - - this.root.push(new GraphicFrameLocks()); - } -} +/** + * # Common DrawingML Non-Visual Properties + * + * This element specifies common non-visual DrawingML object properties for the parent DrawingML object. These properties are specified as child elements of this element. + * + * References: + * - https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_cNvGraphicFramePr_topic_ID0E6U2OB.html + * + * ## XSD Schema + * + * ```xml + * + * + * + * + * + * + * ``` + */ +export const createGraphicFrameProperties = (): XmlComponent => + new BuilderElement({ + name: "wp:cNvGraphicFramePr", + children: [new GraphicFrameLocks()], + }); diff --git a/src/file/drawing/inline/inline.ts b/src/file/drawing/inline/inline.ts index 09959210b3..ac723e6fd6 100644 --- a/src/file/drawing/inline/inline.ts +++ b/src/file/drawing/inline/inline.ts @@ -4,8 +4,8 @@ import { BuilderElement, XmlComponent } from "@file/xml-components"; import { DocProperties, DocPropertiesOptions } from "./../doc-properties/doc-properties"; import { createEffectExtent } from "./../effect-extent/effect-extent"; -import { Extent } from "./../extent/extent"; -import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties"; +import { createExtent } from "./../extent/extent"; +import { createGraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties"; import { Graphic } from "./../inline/graphic"; import { OutlineOptions } from "./graphic/graphic-data/pic/shape-properties/outline/outline"; @@ -52,7 +52,7 @@ export const createInline = ({ mediaData, transform, docProperties, outline }: I }, }, children: [ - new Extent(transform.emus.x, transform.emus.y), + createExtent({ x: transform.emus.x, y: transform.emus.y }), createEffectExtent( outline ? { @@ -64,7 +64,7 @@ export const createInline = ({ mediaData, transform, docProperties, outline }: I : { top: 0, right: 0, bottom: 0, left: 0 }, ), new DocProperties(docProperties), - new GraphicFrameProperties(), + createGraphicFrameProperties(), new Graphic({ mediaData, transform, outline }), ], }); diff --git a/src/file/paragraph/math/bar/math-bar-pos.ts b/src/file/paragraph/math/bar/math-bar-pos.ts index d5051d1f2d..f91ee0cf13 100644 --- a/src/file/paragraph/math/bar/math-bar-pos.ts +++ b/src/file/paragraph/math/bar/math-bar-pos.ts @@ -1,11 +1,12 @@ // https://www.datypic.com/sc/ooxml/e-m_pos-1.html -import { Attributes, XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -export class MathBarPos extends XmlComponent { - // TODO: Use correct types rather than any - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public constructor(attributes: any) { - super("m:pos"); - this.root.push(new Attributes(attributes)); - } -} +type MathBarPosOptions = { readonly val: string }; + +export const createMathBarPos = ({ val }: MathBarPosOptions): XmlComponent => + new BuilderElement({ + name: "m:pos", + attributes: { + val: { key: "w:val", value: val }, + }, + }); diff --git a/src/file/paragraph/math/bar/math-bar-properties.spec.ts b/src/file/paragraph/math/bar/math-bar-properties.spec.ts index 1e5a369d84..1b9fbea6b1 100644 --- a/src/file/paragraph/math/bar/math-bar-properties.spec.ts +++ b/src/file/paragraph/math/bar/math-bar-properties.spec.ts @@ -2,11 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathBarProperties } from "./math-bar-properties"; +import { createMathBarProperties } from "./math-bar-properties"; + describe("MathBarProperties", () => { describe("#constructor()", () => { it("should create a MathBarProperties with top key", () => { - const mathBarProperties = new MathBarProperties("top"); + const mathBarProperties = createMathBarProperties({ type: "top" }); const tree = new Formatter().format(mathBarProperties); @@ -23,7 +24,7 @@ describe("MathBarProperties", () => { }); }); it("should create a MathBarProperties with bottom key", () => { - const mathBarProperties = new MathBarProperties("bot"); + const mathBarProperties = createMathBarProperties({ type: "bot" }); const tree = new Formatter().format(mathBarProperties); diff --git a/src/file/paragraph/math/bar/math-bar-properties.ts b/src/file/paragraph/math/bar/math-bar-properties.ts index 71201d85be..90e91642ec 100644 --- a/src/file/paragraph/math/bar/math-bar-properties.ts +++ b/src/file/paragraph/math/bar/math-bar-properties.ts @@ -1,11 +1,10 @@ // https://www.datypic.com/sc/ooxml/e-m_barPr-1.html -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -import { MathBarPos } from "./math-bar-pos"; +import { createMathBarPos } from "./math-bar-pos"; -export class MathBarProperties extends XmlComponent { - public constructor(type: string) { - super("m:barPr"); - this.root.push(new MathBarPos({ val: type })); - } -} +export const createMathBarProperties = ({ type }: { readonly type: string }): XmlComponent => + new BuilderElement({ + name: "m:barPr", + children: [createMathBarPos({ val: type })], + }); diff --git a/src/file/paragraph/math/bar/math-bar.spec.ts b/src/file/paragraph/math/bar/math-bar.spec.ts index 1a0709395e..e539f6d22e 100644 --- a/src/file/paragraph/math/bar/math-bar.spec.ts +++ b/src/file/paragraph/math/bar/math-bar.spec.ts @@ -2,13 +2,13 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathBar } from "./math-bar"; import { MathRun } from "../math-run"; +import { createMathBar } from "./math-bar"; describe("MathBar", () => { describe("#constructor()", () => { it("should create a MathBar with correct root key", () => { - const mathBar = new MathBar({ type: "top", children: [new MathRun("text")] }); + const mathBar = createMathBar({ type: "top", children: [new MathRun("text")] }); const tree = new Formatter().format(mathBar); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/bar/math-bar.ts b/src/file/paragraph/math/bar/math-bar.ts index e2499b8de7..4d1459e56c 100644 --- a/src/file/paragraph/math/bar/math-bar.ts +++ b/src/file/paragraph/math/bar/math-bar.ts @@ -1,18 +1,17 @@ // https://www.datypic.com/sc/ooxml/e-m_bar-1.html -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -import { MathBarProperties } from "./math-bar-properties"; import type { MathComponent } from "../math-component"; -import { MathBase } from "../n-ary"; +import { createMathBase } from "../n-ary"; +import { createMathBarProperties } from "./math-bar-properties"; -type MathBarOption = { +type MathBarOptions = { readonly type: "top" | "bot"; readonly children: readonly MathComponent[]; }; -export class MathBar extends XmlComponent { - public constructor(options: MathBarOption) { - super("m:bar"); - this.root.push(new MathBarProperties(options.type)); - this.root.push(new MathBase(options.children)); - } -} + +export const createMathBar = ({ type, children }: MathBarOptions): XmlComponent => + new BuilderElement({ + name: "m:bar", + children: [createMathBarProperties({ type }), createMathBase({ children })], + }); diff --git a/src/file/paragraph/math/brackets/math-angled-brackets.ts b/src/file/paragraph/math/brackets/math-angled-brackets.ts index efef05ea1b..819ffb34fc 100644 --- a/src/file/paragraph/math/brackets/math-angled-brackets.ts +++ b/src/file/paragraph/math/brackets/math-angled-brackets.ts @@ -2,19 +2,23 @@ import { XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -import { MathBase } from "../n-ary"; -import { MathBracketProperties } from "./math-bracket-properties"; +import { createMathBase } from "../n-ary"; +import { createMathBracketProperties } from "./math-bracket-properties"; + +type MathAngledBracketsOptions = { readonly children: readonly MathComponent[] }; export class MathAngledBrackets extends XmlComponent { - public constructor(options: { readonly children: readonly MathComponent[] }) { + public constructor(options: MathAngledBracketsOptions) { super("m:d"); this.root.push( - new MathBracketProperties({ - beginningCharacter: "〈", - endingCharacter: "〉", + createMathBracketProperties({ + characters: { + beginningCharacter: "〈", + endingCharacter: "〉", + }, }), ); - this.root.push(new MathBase(options.children)); + this.root.push(createMathBase({ children: options.children })); } } diff --git a/src/file/paragraph/math/brackets/math-beginning-character.spec.ts b/src/file/paragraph/math/brackets/math-beginning-character.spec.ts index 9ca4a7cbd5..66e7f69942 100644 --- a/src/file/paragraph/math/brackets/math-beginning-character.spec.ts +++ b/src/file/paragraph/math/brackets/math-beginning-character.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathBeginningCharacter } from "./math-beginning-character"; +import { createMathBeginningCharacter } from "./math-beginning-character"; -describe("MathBeginningCharacter", () => { +describe("createMathBeginningCharacter", () => { describe("#constructor()", () => { it("should create a MathBeginningCharacter with correct root key", () => { - const mathBeginningCharacter = new MathBeginningCharacter("["); + const mathBeginningCharacter = createMathBeginningCharacter({ character: "[" }); const tree = new Formatter().format(mathBeginningCharacter); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/brackets/math-beginning-character.ts b/src/file/paragraph/math/brackets/math-beginning-character.ts index ad0ee133c5..c590411e0e 100644 --- a/src/file/paragraph/math/brackets/math-beginning-character.ts +++ b/src/file/paragraph/math/brackets/math-beginning-character.ts @@ -1,14 +1,12 @@ // http://www.datypic.com/sc/ooxml/e-m_begChr-1.html -import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -class MathBeginningCharacterAttributes extends XmlAttributeComponent<{ readonly character: string }> { - protected readonly xmlKeys = { character: "m:val" }; -} +type MathBeginningCharacterOptions = { readonly character: string }; -export class MathBeginningCharacter extends XmlComponent { - public constructor(character: string) { - super("m:begChr"); - - this.root.push(new MathBeginningCharacterAttributes({ character })); - } -} +export const createMathBeginningCharacter = ({ character }: MathBeginningCharacterOptions): XmlComponent => + new BuilderElement({ + name: "m:begChr", + attributes: { + character: { key: "m:val", value: character }, + }, + }); diff --git a/src/file/paragraph/math/brackets/math-bracket-properties.spec.ts b/src/file/paragraph/math/brackets/math-bracket-properties.spec.ts index 3e77cda292..8ed6aa098d 100644 --- a/src/file/paragraph/math/brackets/math-bracket-properties.spec.ts +++ b/src/file/paragraph/math/brackets/math-bracket-properties.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathBracketProperties } from "./math-bracket-properties"; +import { createMathBracketProperties } from "./math-bracket-properties"; -describe("MathBracketProperties", () => { +describe("createMathBracketProperties", () => { describe("#constructor()", () => { it("should create a MathBracketProperties with correct root key", () => { - const mathBracketProperties = new MathBracketProperties(); + const mathBracketProperties = createMathBracketProperties({}); const tree = new Formatter().format(mathBracketProperties); expect(tree).to.deep.equal({ @@ -16,9 +16,11 @@ describe("MathBracketProperties", () => { }); it("should create a MathBracketProperties with correct root key and add brackets", () => { - const mathBracketProperties = new MathBracketProperties({ - beginningCharacter: "[", - endingCharacter: "]", + const mathBracketProperties = createMathBracketProperties({ + characters: { + beginningCharacter: "[", + endingCharacter: "]", + }, }); const tree = new Formatter().format(mathBracketProperties); diff --git a/src/file/paragraph/math/brackets/math-bracket-properties.ts b/src/file/paragraph/math/brackets/math-bracket-properties.ts index 86070a1180..c80fae1648 100644 --- a/src/file/paragraph/math/brackets/math-bracket-properties.ts +++ b/src/file/paragraph/math/brackets/math-bracket-properties.ts @@ -1,16 +1,18 @@ // http://www.datypic.com/sc/ooxml/e-m_dPr-1.html -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -import { MathBeginningCharacter } from "./math-beginning-character"; -import { MathEndingCharacter } from "./math-ending-char"; +import { createMathBeginningCharacter } from "./math-beginning-character"; +import { createMathEndingCharacter } from "./math-ending-char"; -export class MathBracketProperties extends XmlComponent { - public constructor(options?: { readonly beginningCharacter: string; readonly endingCharacter: string }) { - super("m:dPr"); +type MathBracketPropertiesOptions = { readonly characters?: { readonly beginningCharacter: string; readonly endingCharacter: string } }; - if (!!options) { - this.root.push(new MathBeginningCharacter(options.beginningCharacter)); - this.root.push(new MathEndingCharacter(options.endingCharacter)); - } - } -} +export const createMathBracketProperties = ({ characters }: MathBracketPropertiesOptions): XmlComponent => + new BuilderElement({ + name: "m:dPr", + children: !!characters + ? [ + createMathBeginningCharacter({ character: characters.beginningCharacter }), + createMathEndingCharacter({ character: characters.endingCharacter }), + ] + : [], + }); diff --git a/src/file/paragraph/math/brackets/math-curly-brackets.ts b/src/file/paragraph/math/brackets/math-curly-brackets.ts index d7336a7297..c90259e0b1 100644 --- a/src/file/paragraph/math/brackets/math-curly-brackets.ts +++ b/src/file/paragraph/math/brackets/math-curly-brackets.ts @@ -2,19 +2,21 @@ import { XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -import { MathBase } from "../n-ary"; -import { MathBracketProperties } from "./math-bracket-properties"; +import { createMathBase } from "../n-ary"; +import { createMathBracketProperties } from "./math-bracket-properties"; export class MathCurlyBrackets extends XmlComponent { public constructor(options: { readonly children: readonly MathComponent[] }) { super("m:d"); this.root.push( - new MathBracketProperties({ - beginningCharacter: "{", - endingCharacter: "}", + createMathBracketProperties({ + characters: { + beginningCharacter: "{", + endingCharacter: "}", + }, }), ); - this.root.push(new MathBase(options.children)); + this.root.push(createMathBase({ children: options.children })); } } diff --git a/src/file/paragraph/math/brackets/math-ending-char.ts b/src/file/paragraph/math/brackets/math-ending-char.ts index f8cf5fe538..9634e26c8e 100644 --- a/src/file/paragraph/math/brackets/math-ending-char.ts +++ b/src/file/paragraph/math/brackets/math-ending-char.ts @@ -1,14 +1,12 @@ // http://www.datypic.com/sc/ooxml/e-m_endChr-1.html -import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -class MathEndingCharacterAttributes extends XmlAttributeComponent<{ readonly character: string }> { - protected readonly xmlKeys = { character: "m:val" }; -} +type MathEndingCharacterOptions = { readonly character: string }; -export class MathEndingCharacter extends XmlComponent { - public constructor(character: string) { - super("m:endChr"); - - this.root.push(new MathEndingCharacterAttributes({ character })); - } -} +export const createMathEndingCharacter = ({ character }: MathEndingCharacterOptions): XmlComponent => + new BuilderElement({ + name: "m:endChr", + attributes: { + character: { key: "m:val", value: character }, + }, + }); diff --git a/src/file/paragraph/math/brackets/math-ending-character.spec.ts b/src/file/paragraph/math/brackets/math-ending-character.spec.ts index f81921872e..f5692322ed 100644 --- a/src/file/paragraph/math/brackets/math-ending-character.spec.ts +++ b/src/file/paragraph/math/brackets/math-ending-character.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathEndingCharacter } from "./math-ending-char"; +import { createMathEndingCharacter } from "./math-ending-char"; -describe("MathEndingCharacter", () => { +describe("createMathEndingCharacter", () => { describe("#constructor()", () => { it("should create a MathEndingCharacter with correct root key", () => { - const mathEndingCharacter = new MathEndingCharacter("]"); + const mathEndingCharacter = createMathEndingCharacter({ character: "]" }); const tree = new Formatter().format(mathEndingCharacter); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/brackets/math-round-brackets.ts b/src/file/paragraph/math/brackets/math-round-brackets.ts index 249d931ec2..93de3aab7b 100644 --- a/src/file/paragraph/math/brackets/math-round-brackets.ts +++ b/src/file/paragraph/math/brackets/math-round-brackets.ts @@ -2,14 +2,14 @@ import { XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -import { MathBase } from "../n-ary"; -import { MathBracketProperties } from "./math-bracket-properties"; +import { createMathBase } from "../n-ary"; +import { createMathBracketProperties } from "./math-bracket-properties"; export class MathRoundBrackets extends XmlComponent { public constructor(options: { readonly children: readonly MathComponent[] }) { super("m:d"); - this.root.push(new MathBracketProperties()); - this.root.push(new MathBase(options.children)); + this.root.push(createMathBracketProperties({})); + this.root.push(createMathBase({ children: options.children })); } } diff --git a/src/file/paragraph/math/brackets/math-square-brackets.ts b/src/file/paragraph/math/brackets/math-square-brackets.ts index 6b27005d36..9914a5bb48 100644 --- a/src/file/paragraph/math/brackets/math-square-brackets.ts +++ b/src/file/paragraph/math/brackets/math-square-brackets.ts @@ -2,19 +2,21 @@ import { XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -import { MathBase } from "../n-ary"; -import { MathBracketProperties } from "./math-bracket-properties"; +import { createMathBase } from "../n-ary"; +import { createMathBracketProperties } from "./math-bracket-properties"; export class MathSquareBrackets extends XmlComponent { public constructor(options: { readonly children: readonly MathComponent[] }) { super("m:d"); this.root.push( - new MathBracketProperties({ - beginningCharacter: "[", - endingCharacter: "]", + createMathBracketProperties({ + characters: { + beginningCharacter: "[", + endingCharacter: "]", + }, }), ); - this.root.push(new MathBase(options.children)); + this.root.push(createMathBase({ children: options.children })); } } diff --git a/src/file/paragraph/math/function/math-function.ts b/src/file/paragraph/math/function/math-function.ts index fff5fa2868..0b1c0de0b5 100644 --- a/src/file/paragraph/math/function/math-function.ts +++ b/src/file/paragraph/math/function/math-function.ts @@ -2,7 +2,7 @@ import { XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -import { MathBase } from "../n-ary"; +import { createMathBase } from "../n-ary"; import { MathFunctionName } from "./math-function-name"; import { MathFunctionProperties } from "./math-function-properties"; @@ -17,6 +17,6 @@ export class MathFunction extends XmlComponent { this.root.push(new MathFunctionProperties()); this.root.push(new MathFunctionName(options.name)); - this.root.push(new MathBase(options.children)); + this.root.push(createMathBase({ children: options.children })); } } diff --git a/src/file/paragraph/math/n-ary/math-accent-character.spec.ts b/src/file/paragraph/math/n-ary/math-accent-character.spec.ts index afb4d98b69..c350254a7d 100644 --- a/src/file/paragraph/math/n-ary/math-accent-character.spec.ts +++ b/src/file/paragraph/math/n-ary/math-accent-character.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathAccentCharacter } from "./math-accent-character"; +import { createMathAccentCharacter } from "./math-accent-character"; describe("MathAccentCharacter", () => { describe("#constructor()", () => { it("should create a MathAccentCharacter with correct root key", () => { - const mathAccentCharacter = new MathAccentCharacter("∑"); + const mathAccentCharacter = createMathAccentCharacter({ accent: "∑" }); const tree = new Formatter().format(mathAccentCharacter); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/n-ary/math-accent-character.ts b/src/file/paragraph/math/n-ary/math-accent-character.ts index f0e124b9c7..9887ca8a7b 100644 --- a/src/file/paragraph/math/n-ary/math-accent-character.ts +++ b/src/file/paragraph/math/n-ary/math-accent-character.ts @@ -1,14 +1,12 @@ // http://www.datypic.com/sc/ooxml/e-m_chr-1.html -import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -class MathAccentCharacterAttributes extends XmlAttributeComponent<{ readonly accent: string }> { - protected readonly xmlKeys = { accent: "m:val" }; -} +type MathAccentCharacterOptions = { readonly accent: string }; -export class MathAccentCharacter extends XmlComponent { - public constructor(accent: string) { - super("m:chr"); - - this.root.push(new MathAccentCharacterAttributes({ accent })); - } -} +export const createMathAccentCharacter = ({ accent }: MathAccentCharacterOptions): XmlComponent => + new BuilderElement({ + name: "m:chr", + attributes: { + accent: { key: "m:val", value: accent }, + }, + }); diff --git a/src/file/paragraph/math/n-ary/math-base.spec.ts b/src/file/paragraph/math/n-ary/math-base.spec.ts index 0d4a8c8a3c..23f5a0077b 100644 --- a/src/file/paragraph/math/n-ary/math-base.spec.ts +++ b/src/file/paragraph/math/n-ary/math-base.spec.ts @@ -3,12 +3,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; import { MathRun } from "../math-run"; -import { MathBase } from "./math-base"; +import { createMathBase } from "./math-base"; -describe("MathBase", () => { +describe("createMathBase", () => { describe("#constructor()", () => { it("should create a MathBase with correct root key", () => { - const mathBase = new MathBase([new MathRun("2+2")]); + const mathBase = createMathBase({ children: [new MathRun("2+2")] }); const tree = new Formatter().format(mathBase); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/n-ary/math-base.ts b/src/file/paragraph/math/n-ary/math-base.ts index 0b95eed36d..a390f80b8c 100644 --- a/src/file/paragraph/math/n-ary/math-base.ts +++ b/src/file/paragraph/math/n-ary/math-base.ts @@ -1,14 +1,14 @@ // http://www.datypic.com/sc/ooxml/e-m_e-1.html -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -export class MathBase extends XmlComponent { - public constructor(children: readonly MathComponent[]) { - super("m:e"); +type MathBaseOptions = { + readonly children: readonly MathComponent[]; +}; - for (const child of children) { - this.root.push(child); - } - } -} +export const createMathBase = ({ children }: MathBaseOptions): XmlComponent => + new BuilderElement({ + name: "m:e", + children, + }); diff --git a/src/file/paragraph/math/n-ary/math-integral.ts b/src/file/paragraph/math/n-ary/math-integral.ts index 03d251e280..54216d6c58 100644 --- a/src/file/paragraph/math/n-ary/math-integral.ts +++ b/src/file/paragraph/math/n-ary/math-integral.ts @@ -1,10 +1,10 @@ import { XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -import { MathBase } from "./math-base"; -import { MathNAryProperties } from "./math-n-ary-properties"; -import { MathSubScriptElement } from "./math-sub-script"; -import { MathSuperScriptElement } from "./math-super-script"; +import { createMathBase } from "./math-base"; +import { createMathNAryProperties } from "./math-n-ary-properties"; +import { createMathSubScriptElement } from "./math-sub-script"; +import { createMathSuperScriptElement } from "./math-super-script"; export type IMathIntegralOptions = { readonly children: readonly MathComponent[]; @@ -16,16 +16,23 @@ export class MathIntegral extends XmlComponent { public constructor(options: IMathIntegralOptions) { super("m:nary"); - this.root.push(new MathNAryProperties("", !!options.superScript, !!options.subScript, "subSup")); + this.root.push( + createMathNAryProperties({ + accent: "", + hasSuperScript: !!options.superScript, + hasSubScript: !!options.subScript, + limitLocationVal: "subSup", + }), + ); if (!!options.subScript) { - this.root.push(new MathSubScriptElement(options.subScript)); + this.root.push(createMathSubScriptElement({ children: options.subScript })); } if (!!options.superScript) { - this.root.push(new MathSuperScriptElement(options.superScript)); + this.root.push(createMathSuperScriptElement({ children: options.superScript })); } - this.root.push(new MathBase(options.children)); + this.root.push(createMathBase({ children: options.children })); } } diff --git a/src/file/paragraph/math/n-ary/math-limit-location.spec.ts b/src/file/paragraph/math/n-ary/math-limit-location.spec.ts index 91ba473c83..fa851aab05 100644 --- a/src/file/paragraph/math/n-ary/math-limit-location.spec.ts +++ b/src/file/paragraph/math/n-ary/math-limit-location.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathLimitLocation } from "./math-limit-location"; +import { createMathLimitLocation } from "./math-limit-location"; -describe("MathLimitLocation", () => { +describe("createMathLimitLocation", () => { describe("#constructor()", () => { it("should create a MathLimitLocation with correct root key", () => { - const mathLimitLocation = new MathLimitLocation(); + const mathLimitLocation = createMathLimitLocation({}); const tree = new Formatter().format(mathLimitLocation); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/n-ary/math-limit-location.ts b/src/file/paragraph/math/n-ary/math-limit-location.ts index 2ee905cd83..b7de16c002 100644 --- a/src/file/paragraph/math/n-ary/math-limit-location.ts +++ b/src/file/paragraph/math/n-ary/math-limit-location.ts @@ -1,14 +1,12 @@ // http://www.datypic.com/sc/ooxml/e-m_limLoc-1.html -import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -class MathLimitLocationAttributes extends XmlAttributeComponent<{ readonly value: string }> { - protected readonly xmlKeys = { value: "m:val" }; -} +type MathLimitLocationOptions = { readonly value?: string }; -export class MathLimitLocation extends XmlComponent { - public constructor(value?: string) { - super("m:limLoc"); - - this.root.push(new MathLimitLocationAttributes({ value: value || "undOvr" })); - } -} +export const createMathLimitLocation = ({ value }: MathLimitLocationOptions): XmlComponent => + new BuilderElement>({ + name: "m:limLoc", + attributes: { + value: { key: "m:val", value: value || "undOvr" }, + }, + }); diff --git a/src/file/paragraph/math/n-ary/math-limit-lower.ts b/src/file/paragraph/math/n-ary/math-limit-lower.ts index ce272a6557..c5706d69a7 100644 --- a/src/file/paragraph/math/n-ary/math-limit-lower.ts +++ b/src/file/paragraph/math/n-ary/math-limit-lower.ts @@ -2,7 +2,7 @@ import { XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -import { MathBase } from "./math-base"; +import { createMathBase } from "./math-base"; import { MathLimit } from "./math-limit"; export type IMathLimitLowerOptions = { @@ -14,7 +14,7 @@ export class MathLimitLower extends XmlComponent { public constructor(options: IMathLimitLowerOptions) { super("m:limLow"); - this.root.push(new MathBase(options.children)); + this.root.push(createMathBase({ children: options.children })); this.root.push(new MathLimit(options.limit)); } } diff --git a/src/file/paragraph/math/n-ary/math-limit-upper.ts b/src/file/paragraph/math/n-ary/math-limit-upper.ts index bb70a36f62..61bffc074e 100644 --- a/src/file/paragraph/math/n-ary/math-limit-upper.ts +++ b/src/file/paragraph/math/n-ary/math-limit-upper.ts @@ -2,7 +2,7 @@ import { XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -import { MathBase } from "./math-base"; +import { createMathBase } from "./math-base"; import { MathLimit } from "./math-limit"; export type IMathLimitUpperOptions = { @@ -14,7 +14,7 @@ export class MathLimitUpper extends XmlComponent { public constructor(options: IMathLimitUpperOptions) { super("m:limUpp"); - this.root.push(new MathBase(options.children)); + this.root.push(createMathBase({ children: options.children })); this.root.push(new MathLimit(options.limit)); } } diff --git a/src/file/paragraph/math/n-ary/math-n-ary-properties.spec.ts b/src/file/paragraph/math/n-ary/math-n-ary-properties.spec.ts index 131a275461..b03a6aad0b 100644 --- a/src/file/paragraph/math/n-ary/math-n-ary-properties.spec.ts +++ b/src/file/paragraph/math/n-ary/math-n-ary-properties.spec.ts @@ -2,12 +2,16 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathNAryProperties } from "./math-n-ary-properties"; +import { createMathNAryProperties } from "./math-n-ary-properties"; -describe("MathNAryProperties", () => { +describe("createMathNAryProperties", () => { describe("#constructor()", () => { it("should create a MathNAryProperties with correct root key", () => { - const mathNAryProperties = new MathNAryProperties("∑", true, true); + const mathNAryProperties = createMathNAryProperties({ + accent: "∑", + hasSuperScript: true, + hasSubScript: true, + }); const tree = new Formatter().format(mathNAryProperties); expect(tree).to.deep.equal({ @@ -31,7 +35,11 @@ describe("MathNAryProperties", () => { }); it("should add super-script hide attributes", () => { - const mathNAryProperties = new MathNAryProperties("∑", false, true); + const mathNAryProperties = createMathNAryProperties({ + accent: "∑", + hasSuperScript: false, + hasSubScript: true, + }); const tree = new Formatter().format(mathNAryProperties); expect(tree).to.deep.equal({ @@ -62,7 +70,11 @@ describe("MathNAryProperties", () => { }); it("should add sub-script hide attributes", () => { - const mathNAryProperties = new MathNAryProperties("∑", true, false); + const mathNAryProperties = createMathNAryProperties({ + accent: "∑", + hasSuperScript: true, + hasSubScript: false, + }); const tree = new Formatter().format(mathNAryProperties); expect(tree).to.deep.equal({ @@ -93,7 +105,11 @@ describe("MathNAryProperties", () => { }); it("should add both super-script and sub-script hide attributes", () => { - const mathNAryProperties = new MathNAryProperties("∑", false, false); + const mathNAryProperties = createMathNAryProperties({ + accent: "∑", + hasSuperScript: false, + hasSubScript: false, + }); const tree = new Formatter().format(mathNAryProperties); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/n-ary/math-n-ary-properties.ts b/src/file/paragraph/math/n-ary/math-n-ary-properties.ts index 7ad1f5dcdb..779c811bda 100644 --- a/src/file/paragraph/math/n-ary/math-n-ary-properties.ts +++ b/src/file/paragraph/math/n-ary/math-n-ary-properties.ts @@ -1,26 +1,30 @@ // http://www.datypic.com/sc/ooxml/e-m_naryPr-1.html -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, 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"; +import { createMathAccentCharacter } from "./math-accent-character"; +import { createMathLimitLocation } from "./math-limit-location"; +import { createMathSubScriptHide } from "./math-sub-script-hide"; +import { createMathSuperScriptHide } from "./math-super-script-hide"; -export class MathNAryProperties extends XmlComponent { - public constructor(accent: string, hasSuperScript: boolean, hasSubScript: boolean, limitLocationVal?: string) { - super("m:naryPr"); +type MathNAryPropertiesOptions = { + readonly accent: string; + readonly hasSuperScript: boolean; + readonly hasSubScript: boolean; + readonly limitLocationVal?: string; +}; - if (!!accent) { - this.root.push(new MathAccentCharacter(accent)); - } - this.root.push(new MathLimitLocation(limitLocationVal)); - - if (!hasSuperScript) { - this.root.push(new MathSuperScriptHide()); - } - - if (!hasSubScript) { - this.root.push(new MathSubScriptHide()); - } - } -} +export const createMathNAryProperties = ({ + accent, + hasSuperScript, + hasSubScript, + limitLocationVal, +}: MathNAryPropertiesOptions): XmlComponent => + new BuilderElement({ + name: "m:naryPr", + children: [ + ...(!!accent ? [createMathAccentCharacter({ accent })] : []), + createMathLimitLocation({ value: limitLocationVal }), + ...(!hasSuperScript ? [createMathSuperScriptHide()] : []), + ...(!hasSubScript ? [createMathSubScriptHide()] : []), + ], + }); diff --git a/src/file/paragraph/math/n-ary/math-sub-script-hide.spec.ts b/src/file/paragraph/math/n-ary/math-sub-script-hide.spec.ts index 19148530d7..7e6ef581f3 100644 --- a/src/file/paragraph/math/n-ary/math-sub-script-hide.spec.ts +++ b/src/file/paragraph/math/n-ary/math-sub-script-hide.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathSubScriptHide } from "./math-sub-script-hide"; +import { createMathSubScriptHide } from "./math-sub-script-hide"; -describe("MathSubScriptHide", () => { +describe("createMathSubScriptHide", () => { describe("#constructor()", () => { it("should create a MathSubScriptHide with correct root key", () => { - const mathSubScriptHide = new MathSubScriptHide(); + const mathSubScriptHide = createMathSubScriptHide(); const tree = new Formatter().format(mathSubScriptHide); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/n-ary/math-sub-script-hide.ts b/src/file/paragraph/math/n-ary/math-sub-script-hide.ts index 84b8300f04..1680caf97d 100644 --- a/src/file/paragraph/math/n-ary/math-sub-script-hide.ts +++ b/src/file/paragraph/math/n-ary/math-sub-script-hide.ts @@ -1,14 +1,10 @@ // http://www.datypic.com/sc/ooxml/e-m_subHide-1.html -import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -class MathSubScriptHideAttributes extends XmlAttributeComponent<{ readonly hide: number }> { - protected readonly xmlKeys = { hide: "m:val" }; -} - -export class MathSubScriptHide extends XmlComponent { - public constructor() { - super("m:subHide"); - - this.root.push(new MathSubScriptHideAttributes({ hide: 1 })); - } -} +export const createMathSubScriptHide = (): XmlComponent => + new BuilderElement<{ readonly hide: number }>({ + name: "m:subHide", + attributes: { + hide: { key: "m:val", value: 1 }, + }, + }); diff --git a/src/file/paragraph/math/n-ary/math-sub-script.spec.ts b/src/file/paragraph/math/n-ary/math-sub-script.spec.ts index 6b1a783165..bf640935ae 100644 --- a/src/file/paragraph/math/n-ary/math-sub-script.spec.ts +++ b/src/file/paragraph/math/n-ary/math-sub-script.spec.ts @@ -3,12 +3,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; import { MathRun } from "../math-run"; -import { MathSubScriptElement } from "./math-sub-script"; +import { createMathSubScriptElement } from "./math-sub-script"; -describe("MathSubScriptElement", () => { +describe("createMathSubScriptElement", () => { describe("#constructor()", () => { it("should create a MathSubScriptElement with correct root key", () => { - const mathSubScriptElement = new MathSubScriptElement([new MathRun("2+2")]); + const mathSubScriptElement = createMathSubScriptElement({ children: [new MathRun("2+2")] }); const tree = new Formatter().format(mathSubScriptElement); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/n-ary/math-sub-script.ts b/src/file/paragraph/math/n-ary/math-sub-script.ts index 69ed2035b2..b1fbf4708a 100644 --- a/src/file/paragraph/math/n-ary/math-sub-script.ts +++ b/src/file/paragraph/math/n-ary/math-sub-script.ts @@ -1,14 +1,14 @@ // http://www.datypic.com/sc/ooxml/e-m_sub-3.html -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -export class MathSubScriptElement extends XmlComponent { - public constructor(children: readonly MathComponent[]) { - super("m:sub"); +type MathSubScriptElementOptions = { + readonly children: readonly MathComponent[]; +}; - for (const child of children) { - this.root.push(child); - } - } -} +export const createMathSubScriptElement = ({ children }: MathSubScriptElementOptions): XmlComponent => + new BuilderElement({ + name: "m:sub", + children, + }); diff --git a/src/file/paragraph/math/n-ary/math-sum.ts b/src/file/paragraph/math/n-ary/math-sum.ts index a4758e31a6..5221282bcf 100644 --- a/src/file/paragraph/math/n-ary/math-sum.ts +++ b/src/file/paragraph/math/n-ary/math-sum.ts @@ -2,10 +2,10 @@ import { XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -import { MathBase } from "./math-base"; -import { MathNAryProperties } from "./math-n-ary-properties"; -import { MathSubScriptElement } from "./math-sub-script"; -import { MathSuperScriptElement } from "./math-super-script"; +import { createMathBase } from "./math-base"; +import { createMathNAryProperties } from "./math-n-ary-properties"; +import { createMathSubScriptElement } from "./math-sub-script"; +import { createMathSuperScriptElement } from "./math-super-script"; export type IMathSumOptions = { readonly children: readonly MathComponent[]; @@ -17,16 +17,22 @@ export class MathSum extends XmlComponent { public constructor(options: IMathSumOptions) { super("m:nary"); - this.root.push(new MathNAryProperties("∑", !!options.superScript, !!options.subScript)); + this.root.push( + createMathNAryProperties({ + accent: "∑", + hasSuperScript: !!options.superScript, + hasSubScript: !!options.subScript, + }), + ); if (!!options.subScript) { - this.root.push(new MathSubScriptElement(options.subScript)); + this.root.push(createMathSubScriptElement({ children: options.subScript })); } if (!!options.superScript) { - this.root.push(new MathSuperScriptElement(options.superScript)); + this.root.push(createMathSuperScriptElement({ children: options.superScript })); } - this.root.push(new MathBase(options.children)); + this.root.push(createMathBase({ children: options.children })); } } diff --git a/src/file/paragraph/math/n-ary/math-super-script-hide.spec.ts b/src/file/paragraph/math/n-ary/math-super-script-hide.spec.ts index 85cb0c40af..81044ff439 100644 --- a/src/file/paragraph/math/n-ary/math-super-script-hide.spec.ts +++ b/src/file/paragraph/math/n-ary/math-super-script-hide.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathSuperScriptHide } from "./math-super-script-hide"; +import { createMathSuperScriptHide } from "./math-super-script-hide"; -describe("MathSuperScriptHide", () => { +describe("createMathSuperScriptHide", () => { describe("#constructor()", () => { it("should create a MathSuperScriptHide with correct root key", () => { - const mathSuperScriptHide = new MathSuperScriptHide(); + const mathSuperScriptHide = createMathSuperScriptHide(); const tree = new Formatter().format(mathSuperScriptHide); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/n-ary/math-super-script-hide.ts b/src/file/paragraph/math/n-ary/math-super-script-hide.ts index 8b60071a30..9af37c18ef 100644 --- a/src/file/paragraph/math/n-ary/math-super-script-hide.ts +++ b/src/file/paragraph/math/n-ary/math-super-script-hide.ts @@ -1,14 +1,10 @@ // http://www.datypic.com/sc/ooxml/e-m_subHide-1.html -import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -class MathSuperScriptHideAttributes extends XmlAttributeComponent<{ readonly hide: number }> { - protected readonly xmlKeys = { hide: "m:val" }; -} - -export class MathSuperScriptHide extends XmlComponent { - public constructor() { - super("m:supHide"); - - this.root.push(new MathSuperScriptHideAttributes({ hide: 1 })); - } -} +export const createMathSuperScriptHide = (): XmlComponent => + new BuilderElement<{ readonly hide: number }>({ + name: "m:supHide", + attributes: { + hide: { key: "m:val", value: 1 }, + }, + }); diff --git a/src/file/paragraph/math/n-ary/math-super-script.spec.ts b/src/file/paragraph/math/n-ary/math-super-script.spec.ts index 54556f2ec1..6d904694a8 100644 --- a/src/file/paragraph/math/n-ary/math-super-script.spec.ts +++ b/src/file/paragraph/math/n-ary/math-super-script.spec.ts @@ -3,12 +3,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; import { MathRun } from "../math-run"; -import { MathSuperScriptElement } from "./math-super-script"; +import { createMathSuperScriptElement } from "./math-super-script"; -describe("MathSuperScriptElement", () => { +describe("createMathSuperScriptElement", () => { describe("#constructor()", () => { it("should create a MathSuperScriptElement with correct root key", () => { - const mathSuperScriptElement = new MathSuperScriptElement([new MathRun("2+2")]); + const mathSuperScriptElement = createMathSuperScriptElement({ children: [new MathRun("2+2")] }); const tree = new Formatter().format(mathSuperScriptElement); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/n-ary/math-super-script.ts b/src/file/paragraph/math/n-ary/math-super-script.ts index e2d6becf3c..5c09d4a72e 100644 --- a/src/file/paragraph/math/n-ary/math-super-script.ts +++ b/src/file/paragraph/math/n-ary/math-super-script.ts @@ -1,14 +1,14 @@ // http://www.datypic.com/sc/ooxml/e-m_sup-3.html -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -export class MathSuperScriptElement extends XmlComponent { - public constructor(children: readonly MathComponent[]) { - super("m:sup"); +type MathSuperScriptElementOptions = { + readonly children: readonly MathComponent[]; +}; - for (const child of children) { - this.root.push(child); - } - } -} +export const createMathSuperScriptElement = ({ children }: MathSuperScriptElementOptions): XmlComponent => + new BuilderElement({ + name: "m:sup", + children, + }); diff --git a/src/file/paragraph/math/radical/math-radical.ts b/src/file/paragraph/math/radical/math-radical.ts index a42addc9de..c00a8009f0 100644 --- a/src/file/paragraph/math/radical/math-radical.ts +++ b/src/file/paragraph/math/radical/math-radical.ts @@ -2,7 +2,7 @@ import { XmlComponent } from "@file/xml-components"; import { MathComponent } from "../math-component"; -import { MathBase } from "../n-ary"; +import { createMathBase } from "../n-ary"; import { MathDegree } from "./math-degree"; import { MathRadicalProperties } from "./math-radical-properties"; @@ -17,6 +17,6 @@ export class MathRadical extends XmlComponent { this.root.push(new MathRadicalProperties(!!options.degree)); this.root.push(new MathDegree(options.degree)); - this.root.push(new MathBase(options.children)); + this.root.push(createMathBase({ children: options.children })); } } diff --git a/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.spec.ts b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.spec.ts index c111997650..d089380d8f 100644 --- a/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.spec.ts +++ b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathPreSubSuperScriptProperties } from "./math-pre-sub-super-script-function-properties"; +import { createMathPreSubSuperScriptProperties } from "./math-pre-sub-super-script-function-properties"; -describe("MathPreSubSuperScriptProperties", () => { +describe("createMathPreSubSuperScriptProperties", () => { describe("#constructor()", () => { it("should create a MathPreSubSuperScriptProperties with correct root key", () => { - const mathPreSubSuperScriptProperties = new MathPreSubSuperScriptProperties(); + const mathPreSubSuperScriptProperties = createMathPreSubSuperScriptProperties(); const tree = new Formatter().format(mathPreSubSuperScriptProperties); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.ts b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.ts index 47ba68d320..ab1bc48bd5 100644 --- a/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.ts +++ b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.ts @@ -1,8 +1,7 @@ // http://www.datypic.com/sc/ooxml/e-m_sPrePr-1.html -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -export class MathPreSubSuperScriptProperties extends XmlComponent { - public constructor() { - super("m:sPrePr"); - } -} +export const createMathPreSubSuperScriptProperties = (): XmlComponent => + new BuilderElement({ + name: "m:sPrePr", + }); diff --git a/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function.ts b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function.ts index e3fe6d76b7..4fdb62d812 100644 --- a/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function.ts +++ b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function.ts @@ -1,9 +1,9 @@ // http://www.datypic.com/sc/ooxml/e-m_sPre-1.html -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement } from "@file/xml-components"; -import { MathPreSubSuperScriptProperties } from "./math-pre-sub-super-script-function-properties"; +import { createMathPreSubSuperScriptProperties } from "./math-pre-sub-super-script-function-properties"; import type { MathComponent } from "../../math-component"; -import { MathBase, MathSubScriptElement, MathSuperScriptElement } from "../../n-ary"; +import { createMathBase, createMathSubScriptElement, createMathSuperScriptElement } from "../../n-ary"; export type IMathPreSubSuperScriptOptions = { readonly children: readonly MathComponent[]; @@ -11,13 +11,16 @@ export type IMathPreSubSuperScriptOptions = { readonly superScript: readonly MathComponent[]; }; -export class MathPreSubSuperScript extends XmlComponent { - public constructor(options: IMathPreSubSuperScriptOptions) { - super("m:sPre"); - - this.root.push(new MathPreSubSuperScriptProperties()); - this.root.push(new MathBase(options.children)); - this.root.push(new MathSubScriptElement(options.subScript)); - this.root.push(new MathSuperScriptElement(options.superScript)); +export class MathPreSubSuperScript extends BuilderElement { + public constructor({ children, subScript, superScript }: IMathPreSubSuperScriptOptions) { + super({ + name: "m:sPre", + children: [ + createMathPreSubSuperScriptProperties(), + createMathBase({ children: children }), + createMathSubScriptElement({ children: subScript }), + createMathSuperScriptElement({ children: superScript }), + ], + }); } } diff --git a/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.spec.ts b/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.spec.ts index 3e1899a84a..8c670b614f 100644 --- a/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.spec.ts +++ b/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathSubScriptProperties } from "./math-sub-script-function-properties"; +import { createMathSubScriptProperties } from "./math-sub-script-function-properties"; -describe("MathSubScriptProperties", () => { +describe("createMathSubScriptProperties", () => { describe("#constructor()", () => { it("should create a MathSubScriptProperties with correct root key", () => { - const mathSubScriptProperties = new MathSubScriptProperties(); + const mathSubScriptProperties = createMathSubScriptProperties(); const tree = new Formatter().format(mathSubScriptProperties); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.ts b/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.ts index eb1995f6cb..37457ea1ae 100644 --- a/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.ts +++ b/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.ts @@ -1,8 +1,7 @@ // http://www.datypic.com/sc/ooxml/e-m_sSubPr-1.html -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -export class MathSubScriptProperties extends XmlComponent { - public constructor() { - super("m:sSubPr"); - } -} +export const createMathSubScriptProperties = (): XmlComponent => + new BuilderElement({ + name: "m:sSubPr", + }); diff --git a/src/file/paragraph/math/script/sub-script/math-sub-script-function.ts b/src/file/paragraph/math/script/sub-script/math-sub-script-function.ts index ee8185b3e5..29e0e66914 100644 --- a/src/file/paragraph/math/script/sub-script/math-sub-script-function.ts +++ b/src/file/paragraph/math/script/sub-script/math-sub-script-function.ts @@ -1,9 +1,9 @@ // http://www.datypic.com/sc/ooxml/e-m_sSub-1.html import { XmlComponent } from "@file/xml-components"; -import { MathSubScriptProperties } from "./math-sub-script-function-properties"; +import { createMathSubScriptProperties } from "./math-sub-script-function-properties"; import { MathComponent } from "../../math-component"; -import { MathBase, MathSubScriptElement } from "../../n-ary"; +import { createMathBase, createMathSubScriptElement } from "../../n-ary"; export type IMathSubScriptOptions = { readonly children: readonly MathComponent[]; @@ -14,8 +14,8 @@ export class MathSubScript extends XmlComponent { public constructor(options: IMathSubScriptOptions) { super("m:sSub"); - this.root.push(new MathSubScriptProperties()); - this.root.push(new MathBase(options.children)); - this.root.push(new MathSubScriptElement(options.subScript)); + this.root.push(createMathSubScriptProperties()); + this.root.push(createMathBase({ children: options.children })); + this.root.push(createMathSubScriptElement({ children: options.subScript })); } } diff --git a/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.spec.ts b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.spec.ts index ea61bd31e4..71565549a9 100644 --- a/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.spec.ts +++ b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathSubSuperScriptProperties } from "./math-sub-super-script-function-properties"; +import { createMathSubSuperScriptProperties } from "./math-sub-super-script-function-properties"; -describe("MathSubSuperScriptProperties", () => { +describe("createMathSuperScriptProperties", () => { describe("#constructor()", () => { it("should create a MathSubSuperScriptProperties with correct root key", () => { - const mathSubSuperScriptProperties = new MathSubSuperScriptProperties(); + const mathSubSuperScriptProperties = createMathSubSuperScriptProperties(); const tree = new Formatter().format(mathSubSuperScriptProperties); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.ts b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.ts index c07d41deb7..e5002ded21 100644 --- a/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.ts +++ b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.ts @@ -1,8 +1,7 @@ // http://www.datypic.com/sc/ooxml/e-m_sSubSupPr-1.html -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -export class MathSubSuperScriptProperties extends XmlComponent { - public constructor() { - super("m:sSubSupPr"); - } -} +export const createMathSubSuperScriptProperties = (): XmlComponent => + new BuilderElement({ + name: "m:sSubSupPr", + }); diff --git a/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function.ts b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function.ts index be64e6efc3..aab62de7fd 100644 --- a/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function.ts +++ b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function.ts @@ -1,9 +1,9 @@ // http://www.datypic.com/sc/ooxml/e-m_sSubSup-1.html import { XmlComponent } from "@file/xml-components"; -import { MathSubSuperScriptProperties } from "./math-sub-super-script-function-properties"; +import { createMathSubSuperScriptProperties } from "./math-sub-super-script-function-properties"; import { MathComponent } from "../../math-component"; -import { MathBase, MathSubScriptElement, MathSuperScriptElement } from "../../n-ary"; +import { createMathBase, createMathSubScriptElement, createMathSuperScriptElement } from "../../n-ary"; export type IMathSubSuperScriptOptions = { readonly children: readonly MathComponent[]; @@ -15,9 +15,9 @@ export class MathSubSuperScript extends XmlComponent { public constructor(options: IMathSubSuperScriptOptions) { super("m:sSubSup"); - this.root.push(new MathSubSuperScriptProperties()); - this.root.push(new MathBase(options.children)); - this.root.push(new MathSubScriptElement(options.subScript)); - this.root.push(new MathSuperScriptElement(options.superScript)); + this.root.push(createMathSubSuperScriptProperties()); + this.root.push(createMathBase({ children: options.children })); + this.root.push(createMathSubScriptElement({ children: options.subScript })); + this.root.push(createMathSuperScriptElement({ children: options.superScript })); } } diff --git a/src/file/paragraph/math/script/super-script/math-super-script-function-properties.spec.ts b/src/file/paragraph/math/script/super-script/math-super-script-function-properties.spec.ts index bcc2d33512..acc025300b 100644 --- a/src/file/paragraph/math/script/super-script/math-super-script-function-properties.spec.ts +++ b/src/file/paragraph/math/script/super-script/math-super-script-function-properties.spec.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { Formatter } from "@export/formatter"; -import { MathSuperScriptProperties } from "./math-super-script-function-properties"; +import { createMathSuperScriptProperties } from "./math-super-script-function-properties"; -describe("MathSuperScriptProperties", () => { +describe("createMathSuperScriptProperties", () => { describe("#constructor()", () => { it("should create a MathSuperScriptProperties with correct root key", () => { - const mathSuperScriptProperties = new MathSuperScriptProperties(); + const mathSuperScriptProperties = createMathSuperScriptProperties(); const tree = new Formatter().format(mathSuperScriptProperties); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/math/script/super-script/math-super-script-function-properties.ts b/src/file/paragraph/math/script/super-script/math-super-script-function-properties.ts index 4739cb895d..732df2b6f0 100644 --- a/src/file/paragraph/math/script/super-script/math-super-script-function-properties.ts +++ b/src/file/paragraph/math/script/super-script/math-super-script-function-properties.ts @@ -1,8 +1,7 @@ // http://www.datypic.com/sc/ooxml/e-m_sSupPr-1.html -import { XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; -export class MathSuperScriptProperties extends XmlComponent { - public constructor() { - super("m:sSupPr"); - } -} +export const createMathSuperScriptProperties = (): XmlComponent => + new BuilderElement({ + name: "m:sSupPr", + }); diff --git a/src/file/paragraph/math/script/super-script/math-super-script-function.ts b/src/file/paragraph/math/script/super-script/math-super-script-function.ts index 25364fc53b..08215bbf40 100644 --- a/src/file/paragraph/math/script/super-script/math-super-script-function.ts +++ b/src/file/paragraph/math/script/super-script/math-super-script-function.ts @@ -1,9 +1,9 @@ // http://www.datypic.com/sc/ooxml/e-m_sSup-1.html import { XmlComponent } from "@file/xml-components"; -import { MathSuperScriptProperties } from "./math-super-script-function-properties"; +import { createMathSuperScriptProperties } from "./math-super-script-function-properties"; import { MathComponent } from "../../math-component"; -import { MathBase, MathSuperScriptElement } from "../../n-ary"; +import { createMathBase, createMathSuperScriptElement } from "../../n-ary"; export type IMathSuperScriptOptions = { readonly children: readonly MathComponent[]; @@ -14,8 +14,8 @@ export class MathSuperScript extends XmlComponent { public constructor(options: IMathSuperScriptOptions) { super("m:sSup"); - this.root.push(new MathSuperScriptProperties()); - this.root.push(new MathBase(options.children)); - this.root.push(new MathSuperScriptElement(options.superScript)); + this.root.push(createMathSuperScriptProperties()); + this.root.push(createMathBase({ children: options.children })); + this.root.push(createMathSuperScriptElement({ children: options.superScript })); } } diff --git a/src/file/textbox/shape/shape.ts b/src/file/textbox/shape/shape.ts index fdbc4bff7e..30cdac074b 100644 --- a/src/file/textbox/shape/shape.ts +++ b/src/file/textbox/shape/shape.ts @@ -90,22 +90,19 @@ const formatShapeStyle = (style?: VmlShapeStyle): string | undefined => .join(";") : undefined; -export const createShape = ({ - id, - children, - type = SHAPE_TYPE, - style, -}: { +type ShapeOptions = { readonly id: string; readonly children?: readonly ParagraphChild[]; readonly type?: string; readonly style?: VmlShapeStyle; -}): XmlComponent => - new BuilderElement<{ - readonly id: string; - readonly type?: string; - readonly style?: string; - }>({ +}; + +export const createShape = ({ id, children, type = SHAPE_TYPE, style }: ShapeOptions): XmlComponent => + new BuilderElement< + Pick & { + readonly style?: string; + } + >({ name: "v:shape", attributes: { id: { diff --git a/src/file/xml-components/simple-elements.ts b/src/file/xml-components/simple-elements.ts index c941b35c4d..842c6f9093 100644 --- a/src/file/xml-components/simple-elements.ts +++ b/src/file/xml-components/simple-elements.ts @@ -88,7 +88,8 @@ export class StringContainer extends XmlComponent { } } -export class BuilderElement extends XmlComponent { +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export class BuilderElement extends XmlComponent { public constructor({ name, attributes,