From f015a4353b6dff39ce627f75ddb44ddca44c1d57 Mon Sep 17 00:00:00 2001 From: Piotr Buszka Date: Mon, 14 Jul 2025 22:01:20 +0200 Subject: [PATCH 1/4] feat: basic wps text box support --- src/file/drawing/anchor/anchor.ts | 6 +- .../drawing/doc-properties/doc-properties.ts | 5 +- src/file/drawing/drawing.ts | 7 +- .../graphic/graphic-data/graphic-data.ts | 38 ++- .../inline/graphic/graphic-data/pic/pic.ts | 2 +- .../pic/shape-properties/shape-properties.ts | 19 +- .../graphic-data/wps/body-properties.ts | 60 ++++ .../inline/graphic/graphic-data/wps/index.ts | 2 + .../wps/non-visual-shape-properties.ts | 25 ++ .../graphic-data/wps/text-box-content.ts | 12 + .../graphic/graphic-data/wps/wps-shape.ts | 40 +++ .../graphic/graphic-data/wps/wps-text-box.ts | 11 + src/file/drawing/inline/graphic/graphic.ts | 9 +- src/file/drawing/inline/inline.ts | 10 +- src/file/media/data.ts | 10 + src/file/paragraph/run/image-run.ts | 2 + src/file/paragraph/run/index.ts | 1 + src/file/paragraph/run/wps-shape-run.spec.ts | 271 ++++++++++++++++++ src/file/paragraph/run/wps-shape-run.ts | 53 ++++ 19 files changed, 555 insertions(+), 28 deletions(-) create mode 100644 src/file/drawing/inline/graphic/graphic-data/wps/body-properties.ts create mode 100644 src/file/drawing/inline/graphic/graphic-data/wps/index.ts create mode 100644 src/file/drawing/inline/graphic/graphic-data/wps/non-visual-shape-properties.ts create mode 100644 src/file/drawing/inline/graphic/graphic-data/wps/text-box-content.ts create mode 100644 src/file/drawing/inline/graphic/graphic-data/wps/wps-shape.ts create mode 100644 src/file/drawing/inline/graphic/graphic-data/wps/wps-text-box.ts create mode 100644 src/file/paragraph/run/wps-shape-run.spec.ts create mode 100644 src/file/paragraph/run/wps-shape-run.ts diff --git a/src/file/drawing/anchor/anchor.ts b/src/file/drawing/anchor/anchor.ts index 9e6d445c43e..50e16174b7a 100644 --- a/src/file/drawing/anchor/anchor.ts +++ b/src/file/drawing/anchor/anchor.ts @@ -1,5 +1,5 @@ // http://officeopenxml.com/drwPicFloating.php -import { IMediaData, IMediaDataTransformation } from "@file/media"; +import { IExtendedMediaData, IMediaDataTransformation } from "@file/media"; import { XmlComponent } from "@file/xml-components"; import { IDrawingOptions } from "../drawing"; @@ -43,7 +43,7 @@ export class Anchor extends XmlComponent { transform, drawingOptions, }: { - readonly mediaData: IMediaData; + readonly mediaData: IExtendedMediaData; readonly transform: IMediaDataTransformation; readonly drawingOptions: IDrawingOptions; }) { @@ -101,6 +101,6 @@ export class Anchor extends XmlComponent { this.root.push(new DocProperties(drawingOptions.docProperties)); this.root.push(createGraphicFrameProperties()); - this.root.push(new Graphic({ mediaData, transform, outline: drawingOptions.outline })); + this.root.push(new Graphic({ mediaData, transform, outline: drawingOptions.outline, solidFill: drawingOptions.solidFill })); } } diff --git a/src/file/drawing/doc-properties/doc-properties.ts b/src/file/drawing/doc-properties/doc-properties.ts index ab8470e9a2e..aa5f070bb3a 100644 --- a/src/file/drawing/doc-properties/doc-properties.ts +++ b/src/file/drawing/doc-properties/doc-properties.ts @@ -21,18 +21,19 @@ export type DocPropertiesOptions = { readonly name: string; readonly description?: string; readonly title?: string; + readonly id?: string; }; export class DocProperties extends XmlComponent { private readonly docPropertiesUniqueNumericId = docPropertiesUniqueNumericIdGen(); - public constructor({ name, description, title }: DocPropertiesOptions = { name: "", description: "", title: "" }) { + public constructor({ name, description, title, id }: DocPropertiesOptions = { name: "", description: "", title: "" }) { super("wp:docPr"); const attributes: Record = { id: { key: "id", - value: this.docPropertiesUniqueNumericId(), + value: id ?? this.docPropertiesUniqueNumericId(), }, name: { key: "name", diff --git a/src/file/drawing/drawing.ts b/src/file/drawing/drawing.ts index 2e6cf991fa9..1551efc3eff 100644 --- a/src/file/drawing/drawing.ts +++ b/src/file/drawing/drawing.ts @@ -1,4 +1,4 @@ -import { IMediaData } from "@file/media"; +import { IExtendedMediaData } from "@file/media"; import { XmlComponent } from "@file/xml-components"; import { Anchor } from "./anchor"; @@ -6,6 +6,7 @@ import { DocPropertiesOptions } from "./doc-properties/doc-properties"; import { IFloating } from "./floating"; import { createInline } from "./inline"; import { OutlineOptions } from "./inline/graphic/graphic-data/pic/shape-properties/outline/outline"; +import { SolidFillOptions } from "./inline/graphic/graphic-data/pic/shape-properties/outline/solid-fill"; export type IDistance = { readonly distT?: number; @@ -18,6 +19,7 @@ export type IDrawingOptions = { readonly floating?: IFloating; readonly docProperties?: DocPropertiesOptions; readonly outline?: OutlineOptions; + readonly solidFill?: SolidFillOptions; }; // @@ -28,7 +30,7 @@ export type IDrawingOptions = { // export class Drawing extends XmlComponent { - public constructor(imageData: IMediaData, drawingOptions: IDrawingOptions = {}) { + public constructor(imageData: IExtendedMediaData, drawingOptions: IDrawingOptions = {}) { super("w:drawing"); if (!drawingOptions.floating) { @@ -38,6 +40,7 @@ export class Drawing extends XmlComponent { transform: imageData.transformation, docProperties: drawingOptions.docProperties, outline: drawingOptions.outline, + solidFill: drawingOptions.solidFill, }), ); } else { diff --git a/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts b/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts index 0ca40ef9ed6..9a30b8595be 100644 --- a/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts +++ b/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts @@ -1,32 +1,48 @@ -import { IMediaData, IMediaDataTransformation } from "@file/media"; +import { WpsShape } from "@file/drawing/inline/graphic/graphic-data/wps/wps-shape"; +import { IExtendedMediaData, IMediaDataTransformation } from "@file/media"; import { XmlComponent } from "@file/xml-components"; import { GraphicDataAttributes } from "./graphic-data-attribute"; import { Pic } from "./pic"; import { OutlineOptions } from "./pic/shape-properties/outline/outline"; +import { SolidFillOptions } from "./pic/shape-properties/outline/solid-fill"; export class GraphicData extends XmlComponent { - private readonly pic: Pic; + // private readonly pic: Pic; public constructor({ mediaData, transform, outline, + solidFill, }: { - readonly mediaData: IMediaData; + readonly mediaData: IExtendedMediaData; readonly transform: IMediaDataTransformation; readonly outline?: OutlineOptions; + readonly solidFill?: SolidFillOptions; }) { super("a:graphicData"); - this.root.push( - new GraphicDataAttributes({ - uri: "http://schemas.openxmlformats.org/drawingml/2006/picture", - }), - ); + if (mediaData.type === "wps") { + this.root.push( + new GraphicDataAttributes({ + uri: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape", + }), + ); + } else { + this.root.push( + new GraphicDataAttributes({ + uri: "http://schemas.openxmlformats.org/drawingml/2006/picture", + }), + ); + } - this.pic = new Pic({ mediaData, transform, outline }); - - this.root.push(this.pic); + if (mediaData.type !== "wps") { + const pic = new Pic({ mediaData, transform, outline }); + this.root.push(pic); + } else { + const wps = new WpsShape({ ...mediaData.data, transformation: transform, outline, solidFill }); + this.root.push(wps); + } } } diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/pic.ts b/src/file/drawing/inline/graphic/graphic-data/pic/pic.ts index da43e86f0ad..5842c500952 100644 --- a/src/file/drawing/inline/graphic/graphic-data/pic/pic.ts +++ b/src/file/drawing/inline/graphic/graphic-data/pic/pic.ts @@ -28,6 +28,6 @@ export class Pic extends XmlComponent { this.root.push(new NonVisualPicProperties()); this.root.push(new BlipFill(mediaData)); - this.root.push(new ShapeProperties({ transform, outline })); + this.root.push(new ShapeProperties({ element: "pic", transform, outline })); } } diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/shape-properties.ts b/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/shape-properties.ts index fb00d37eaa2..069d41eec9c 100644 --- a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/shape-properties.ts +++ b/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/shape-properties.ts @@ -5,14 +5,25 @@ import { XmlComponent } from "@file/xml-components"; import { Form } from "./form"; import { createNoFill } from "./outline/no-fill"; import { OutlineOptions, createOutline } from "./outline/outline"; +import { SolidFillOptions, createSolidFill } from "./outline/solid-fill"; import { PresetGeometry } from "./preset-geometry/preset-geometry"; import { ShapePropertiesAttributes } from "./shape-properties-attributes"; export class ShapeProperties extends XmlComponent { private readonly form: Form; - public constructor({ outline, transform }: { readonly outline?: OutlineOptions; readonly transform: IMediaDataTransformation }) { - super("pic:spPr"); + public constructor({ + element, + outline, + solidFill, + transform, + }: { + readonly element: string; + readonly outline?: OutlineOptions; + readonly solidFill?: SolidFillOptions; + readonly transform: IMediaDataTransformation; + }) { + super(`${element}:spPr`); this.root.push( new ShapePropertiesAttributes({ @@ -29,5 +40,9 @@ export class ShapeProperties extends XmlComponent { this.root.push(createNoFill()); this.root.push(createOutline(outline)); } + + if (solidFill) { + this.root.push(createSolidFill(solidFill)); + } } } diff --git a/src/file/drawing/inline/graphic/graphic-data/wps/body-properties.ts b/src/file/drawing/inline/graphic/graphic-data/wps/body-properties.ts new file mode 100644 index 00000000000..6f48c179b27 --- /dev/null +++ b/src/file/drawing/inline/graphic/graphic-data/wps/body-properties.ts @@ -0,0 +1,60 @@ +import { TextWrappingType } from "@file/drawing/text-wrap"; +import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; +import { OnOffElement } from "@file/xml-components/simple-elements"; + +export enum VerticalAnchor { + CENTER = "ctr", + TOP = "t", + BOTTOM = "b", +} + +export type IBodyPropertiesOptions = { + readonly wrap?: (typeof TextWrappingType)[keyof typeof TextWrappingType]; + readonly verticalAnchor?: VerticalAnchor; + readonly margins?: { + readonly top?: number; + readonly bottom?: number; + readonly left?: number; + readonly right?: number; + }; + readonly noAutoFit?: boolean; +}; + +class BodyPropertiesAttributes extends XmlAttributeComponent<{ + // readonly wrap?: (typeof TextWrappingType)[keyof typeof TextWrappingType]; + readonly lIns?: number; + readonly rIns?: number; + readonly tIns?: number; + readonly bIns?: number; + readonly anchor?: VerticalAnchor; +}> { + protected readonly xmlKeys = { + wrap: "wrap", + lIns: "lIns", + rIns: "rIns", + tIns: "tIns", + bIns: "bIns", + anchor: "anchor", + }; +} + +export class BodyProperties extends XmlComponent { + public constructor(options: IBodyPropertiesOptions = {}) { + super("wps:bodyPr"); + + this.root.push( + new BodyPropertiesAttributes({ + // wrap: options.wrap, + lIns: options.margins?.left, + rIns: options.margins?.right, + tIns: options.margins?.top, + bIns: options.margins?.bottom, + anchor: options.verticalAnchor, + }), + ); + + if (options.noAutoFit) { + this.root.push(new OnOffElement("a:noAutofit", options.noAutoFit)); + } + } +} diff --git a/src/file/drawing/inline/graphic/graphic-data/wps/index.ts b/src/file/drawing/inline/graphic/graphic-data/wps/index.ts new file mode 100644 index 00000000000..343c9c3c42d --- /dev/null +++ b/src/file/drawing/inline/graphic/graphic-data/wps/index.ts @@ -0,0 +1,2 @@ +export * from "./wps-shape"; +export * from "./body-properties"; diff --git a/src/file/drawing/inline/graphic/graphic-data/wps/non-visual-shape-properties.ts b/src/file/drawing/inline/graphic/graphic-data/wps/non-visual-shape-properties.ts new file mode 100644 index 00000000000..82656c31b0c --- /dev/null +++ b/src/file/drawing/inline/graphic/graphic-data/wps/non-visual-shape-properties.ts @@ -0,0 +1,25 @@ +import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; + +export type INonVisualShapePropertiesOptions = { + readonly txBox: string; +}; + +class NonVisualShapePropertiesAttributes extends XmlAttributeComponent<{ + readonly txBox: string; +}> { + protected readonly xmlKeys = { + txBox: "txBox", + }; +} + +export class NonVisualShapeProperties extends XmlComponent { + public constructor(options: INonVisualShapePropertiesOptions = { txBox: "1" }) { + super("wps:cNvSpPr"); + + this.root.push( + new NonVisualShapePropertiesAttributes({ + txBox: options.txBox, + }), + ); + } +} diff --git a/src/file/drawing/inline/graphic/graphic-data/wps/text-box-content.ts b/src/file/drawing/inline/graphic/graphic-data/wps/text-box-content.ts new file mode 100644 index 00000000000..75fc52a918e --- /dev/null +++ b/src/file/drawing/inline/graphic/graphic-data/wps/text-box-content.ts @@ -0,0 +1,12 @@ +import { Paragraph } from "@file/paragraph"; +import { XmlComponent } from "@file/xml-components"; + +export class TextBoxContent extends XmlComponent { + public constructor(children: readonly Paragraph[]) { + super("w:txbxContent"); + + for (const child of children) { + this.root.push(child); + } + } +} diff --git a/src/file/drawing/inline/graphic/graphic-data/wps/wps-shape.ts b/src/file/drawing/inline/graphic/graphic-data/wps/wps-shape.ts new file mode 100644 index 00000000000..4034bb9dea2 --- /dev/null +++ b/src/file/drawing/inline/graphic/graphic-data/wps/wps-shape.ts @@ -0,0 +1,40 @@ +import { IMediaDataTransformation } from "@file/media"; +import { Paragraph } from "@file/paragraph"; +import { XmlComponent } from "@file/xml-components"; + +import { BodyProperties, IBodyPropertiesOptions } from "./body-properties"; +import { INonVisualShapePropertiesOptions, NonVisualShapeProperties } from "./non-visual-shape-properties"; +import { WpsTextBox } from "./wps-text-box"; +import { OutlineOptions } from "../pic/shape-properties/outline/outline"; +import { SolidFillOptions } from "../pic/shape-properties/outline/solid-fill"; +import { ShapeProperties } from "../pic/shape-properties/shape-properties"; + +export type WpsShapeCoreOptions = { + readonly children: readonly Paragraph[]; + readonly nonVisualProperties?: INonVisualShapePropertiesOptions; + readonly bodyProperties?: IBodyPropertiesOptions; +}; + +export type WpsShapeOptions = WpsShapeCoreOptions & { + readonly transformation: IMediaDataTransformation; + readonly outline?: OutlineOptions; + readonly solidFill?: SolidFillOptions; +}; + +export class WpsShape extends XmlComponent { + public constructor(options: WpsShapeOptions) { + super("wps:wsp"); + + this.root.push(new NonVisualShapeProperties(options.nonVisualProperties)); + this.root.push( + new ShapeProperties({ + element: "wps", + transform: options.transformation, + outline: options.outline, + solidFill: options.solidFill, + }), + ); + this.root.push(new WpsTextBox(options.children)); + this.root.push(new BodyProperties(options.bodyProperties)); + } +} diff --git a/src/file/drawing/inline/graphic/graphic-data/wps/wps-text-box.ts b/src/file/drawing/inline/graphic/graphic-data/wps/wps-text-box.ts new file mode 100644 index 00000000000..11edf24a136 --- /dev/null +++ b/src/file/drawing/inline/graphic/graphic-data/wps/wps-text-box.ts @@ -0,0 +1,11 @@ +import { Paragraph } from "@file/paragraph"; +import { XmlComponent } from "@file/xml-components"; + +import { TextBoxContent } from "./text-box-content"; + +export class WpsTextBox extends XmlComponent { + public constructor(children: readonly Paragraph[]) { + super("wps:txbx"); + this.root.push(new TextBoxContent(children)); + } +} diff --git a/src/file/drawing/inline/graphic/graphic.ts b/src/file/drawing/inline/graphic/graphic.ts index 9289eee49c0..2cd331320f7 100644 --- a/src/file/drawing/inline/graphic/graphic.ts +++ b/src/file/drawing/inline/graphic/graphic.ts @@ -1,8 +1,9 @@ -import { IMediaData, IMediaDataTransformation } from "@file/media"; +import { IExtendedMediaData, IMediaDataTransformation } from "@file/media"; import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; import { GraphicData } from "./graphic-data"; import { OutlineOptions } from "./graphic-data/pic/shape-properties/outline/outline"; +import { SolidFillOptions } from "./graphic-data/pic/shape-properties/outline/solid-fill"; class GraphicAttributes extends XmlAttributeComponent<{ readonly a: string; @@ -19,10 +20,12 @@ export class Graphic extends XmlComponent { mediaData, transform, outline, + solidFill, }: { - readonly mediaData: IMediaData; + readonly mediaData: IExtendedMediaData; readonly transform: IMediaDataTransformation; readonly outline?: OutlineOptions; + readonly solidFill?: SolidFillOptions; }) { super("a:graphic"); this.root.push( @@ -31,7 +34,7 @@ export class Graphic extends XmlComponent { }), ); - this.data = new GraphicData({ mediaData, transform, outline }); + this.data = new GraphicData({ mediaData, transform, outline, solidFill }); this.root.push(this.data); } diff --git a/src/file/drawing/inline/inline.ts b/src/file/drawing/inline/inline.ts index ac723e6fd65..734df989923 100644 --- a/src/file/drawing/inline/inline.ts +++ b/src/file/drawing/inline/inline.ts @@ -1,5 +1,5 @@ // http://officeopenxml.com/drwPicInline.php -import { IMediaData, IMediaDataTransformation } from "@file/media"; +import { IExtendedMediaData, IMediaDataTransformation } from "@file/media"; import { BuilderElement, XmlComponent } from "@file/xml-components"; import { DocProperties, DocPropertiesOptions } from "./../doc-properties/doc-properties"; @@ -8,12 +8,14 @@ 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"; +import { SolidFillOptions } from "./graphic/graphic-data/pic/shape-properties/outline/solid-fill"; type InlineOptions = { - readonly mediaData: IMediaData; + readonly mediaData: IExtendedMediaData; readonly transform: IMediaDataTransformation; readonly docProperties?: DocPropertiesOptions; readonly outline?: OutlineOptions; + readonly solidFill?: SolidFillOptions; }; // @@ -30,7 +32,7 @@ type InlineOptions = { // // // -export const createInline = ({ mediaData, transform, docProperties, outline }: InlineOptions): XmlComponent => +export const createInline = ({ mediaData, transform, docProperties, outline, solidFill }: InlineOptions): XmlComponent => new BuilderElement({ name: "wp:inline", attributes: { @@ -65,6 +67,6 @@ export const createInline = ({ mediaData, transform, docProperties, outline }: I ), new DocProperties(docProperties), createGraphicFrameProperties(), - new Graphic({ mediaData, transform, outline }), + new Graphic({ mediaData, transform, outline, solidFill }), ], }); diff --git a/src/file/media/data.ts b/src/file/media/data.ts index 7024c74e4b5..bfb9d72a956 100644 --- a/src/file/media/data.ts +++ b/src/file/media/data.ts @@ -1,3 +1,5 @@ +import { WpsShapeCoreOptions } from "@file/drawing/inline/graphic/graphic-data/wps"; + export type IMediaDataTransformation = { readonly pixels: { readonly x: number; @@ -32,6 +34,14 @@ type SvgMediaData = { readonly fallback: RegularMediaData & CoreMediaData; }; +export type WpsMediaData = { + readonly type: "wps"; + readonly transformation: IMediaDataTransformation; + readonly data: WpsShapeCoreOptions; +}; + +export type IExtendedMediaData = IMediaData | WpsMediaData; + export type IMediaData = (RegularMediaData | SvgMediaData) & CoreMediaData; // Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432 diff --git a/src/file/paragraph/run/image-run.ts b/src/file/paragraph/run/image-run.ts index d909e95808e..da8f06198a8 100644 --- a/src/file/paragraph/run/image-run.ts +++ b/src/file/paragraph/run/image-run.ts @@ -4,6 +4,7 @@ import { hashedId } from "@util/convenience-functions"; import { Drawing, IFloating } from "../../drawing"; import { OutlineOptions } from "../../drawing/inline/graphic/graphic-data/pic/shape-properties/outline/outline"; +import { SolidFillOptions } from "../../drawing/inline/graphic/graphic-data/pic/shape-properties/outline/solid-fill"; import { IMediaTransformation } from "../../media"; import { IMediaData } from "../../media/data"; import { Run } from "../run"; @@ -13,6 +14,7 @@ type CoreImageOptions = { readonly floating?: IFloating; readonly altText?: DocPropertiesOptions; readonly outline?: OutlineOptions; + readonly solidFill?: SolidFillOptions; }; type RegularImageOptions = { diff --git a/src/file/paragraph/run/index.ts b/src/file/paragraph/run/index.ts index f459256c59e..606f44d84f6 100644 --- a/src/file/paragraph/run/index.ts +++ b/src/file/paragraph/run/index.ts @@ -3,6 +3,7 @@ export * from "./properties"; export * from "./text-run"; export * from "./symbol-run"; export * from "./image-run"; +export * from "./wps-shape-run"; export * from "./run-fonts"; export * from "./sequential-identifier"; export * from "./underline"; diff --git a/src/file/paragraph/run/wps-shape-run.spec.ts b/src/file/paragraph/run/wps-shape-run.spec.ts new file mode 100644 index 00000000000..2b7f0aa58fa --- /dev/null +++ b/src/file/paragraph/run/wps-shape-run.spec.ts @@ -0,0 +1,271 @@ +import { describe, it } from "vitest"; + +import { Formatter } from "@export/formatter"; +import { IViewWrapper } from "@file/document-wrapper"; +import { File } from "@file/file"; +import { Paragraph } from "@file/index"; + +import { WpsShapeRun } from "./wps-shape-run"; + +describe("WpsShapeRun", () => { + describe("#constructor()", () => { + it("should create with Buffer", () => { + const currentShapeRun = new WpsShapeRun({ + type: "wps", + children: [new Paragraph("Test Paragraph")], + transformation: { + width: 200, + height: 200, + rotation: 45, + }, + solidFill: { + type: "rgb", + value: "FF0000", + }, + floating: { + zIndex: 10, + horizontalPosition: { + offset: 1014400, + }, + verticalPosition: { + offset: 1014400, + }, + }, + }); + + const tree = new Formatter().format(currentShapeRun, { + file: { + Media: {}, + } as unknown as File, + viewWrapper: {} as unknown as IViewWrapper, + stack: [], + }); + + // eslint-disable-next-line no-console + console.log(JSON.stringify(tree, null, 2)); + + // expect(tree).to.deep.equal({ + // "w:r": [ + // { + // "w:drawing": [ + // { + // "wp:anchor": [ + // { + // _attr: { + // allowOverlap: "1", + // behindDoc: "0", + // distB: 0, + // distL: 0, + // distR: 0, + // distT: 0, + // layoutInCell: "1", + // locked: "0", + // relativeHeight: 10, + // simplePos: "0", + // }, + // }, + // { + // "wp:simplePos": { + // _attr: { + // x: 0, + // y: 0, + // }, + // }, + // }, + // { + // "wp:positionH": [ + // { + // _attr: { + // relativeFrom: "page", + // }, + // }, + // { + // "wp:posOffset": ["1014400"], + // }, + // ], + // }, + // { + // "wp:positionV": [ + // { + // _attr: { + // relativeFrom: "page", + // }, + // }, + // { + // "wp:posOffset": ["1014400"], + // }, + // ], + // }, + // { + // "wp:extent": { + // _attr: { + // cx: 1905000, + // cy: 1905000, + // }, + // }, + // }, + // { + // "wp:effectExtent": { + // _attr: { + // b: 0, + // l: 0, + // r: 0, + // t: 0, + // }, + // }, + // }, + // { + // "wp:wrapNone": {}, + // }, + // { + // "wp:docPr": { + // _attr: { + // descr: "", + // id: 1, + // name: "", + // title: "", + // }, + // }, + // }, + // { + // "wp:cNvGraphicFramePr": [ + // { + // "a:graphicFrameLocks": { + // _attr: { + // noChangeAspect: 1, + // "xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main", + // }, + // }, + // }, + // ], + // }, + // { + // "a:graphic": [ + // { + // _attr: { + // "xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main", + // }, + // }, + // { + // "a:graphicData": [ + // { + // _attr: { + // uri: "http://schemas.openxmlformats.org/drawingml/2006/picture", + // }, + // }, + // { + // "pic:pic": [ + // { + // _attr: { + // "xmlns:pic": "http://schemas.openxmlformats.org/drawingml/2006/picture", + // }, + // }, + // { + // "pic:nvPicPr": [ + // { + // "pic:cNvPr": { + // _attr: { + // descr: "", + // id: 0, + // name: "", + // }, + // }, + // }, + // { + // "pic:cNvPicPr": [ + // { + // "a:picLocks": { + // _attr: { + // noChangeArrowheads: 1, + // noChangeAspect: 1, + // }, + // }, + // }, + // ], + // }, + // ], + // }, + // { + // "pic:blipFill": [ + // { + // "a:blip": { + // _attr: { + // cstate: "none", + // "r:embed": + // "rId{da39a3ee5e6b4b0d3255bfef95601890afd80709.png}", + // }, + // }, + // }, + // { + // "a:srcRect": {}, + // }, + // { + // "a:stretch": [ + // { + // "a:fillRect": {}, + // }, + // ], + // }, + // ], + // }, + // { + // "pic:spPr": [ + // { + // _attr: { + // bwMode: "auto", + // }, + // }, + // { + // "a:xfrm": [ + // { + // _attr: { + // rot: 2700000, + // }, + // }, + // { + // "a:off": { + // _attr: { + // x: 0, + // y: 0, + // }, + // }, + // }, + // { + // "a:ext": { + // _attr: { + // cx: 1905000, + // cy: 1905000, + // }, + // }, + // }, + // ], + // }, + // { + // "a:prstGeom": [ + // { + // _attr: { + // prst: "rect", + // }, + // }, + // { + // "a:avLst": {}, + // }, + // ], + // }, + // ], + // }, + // ], + // }, + // ], + // }, + // ], + // }, + // ], + // }, + // ], + // }, + // ], + // }); + }); + }); +}); diff --git a/src/file/paragraph/run/wps-shape-run.ts b/src/file/paragraph/run/wps-shape-run.ts new file mode 100644 index 00000000000..ab51152f634 --- /dev/null +++ b/src/file/paragraph/run/wps-shape-run.ts @@ -0,0 +1,53 @@ +import { DocPropertiesOptions } from "@file/drawing/doc-properties/doc-properties"; +import { WpsShapeCoreOptions } from "@file/drawing/inline/graphic/graphic-data/wps"; + +import { Drawing, IFloating } from "../../drawing"; +import { OutlineOptions } from "../../drawing/inline/graphic/graphic-data/pic/shape-properties/outline/outline"; +import { SolidFillOptions } from "../../drawing/inline/graphic/graphic-data/pic/shape-properties/outline/solid-fill"; +import { IMediaDataTransformation, IMediaTransformation, WpsMediaData } from "../../media"; +import { Run } from "../run"; + +type CoreShapeOptions = { + readonly transformation: IMediaTransformation; + readonly floating?: IFloating; + readonly altText?: DocPropertiesOptions; + readonly outline?: OutlineOptions; + readonly solidFill?: SolidFillOptions; +}; + +export type IWpsShapeOptions = WpsShapeCoreOptions & { readonly type: "wps" } & CoreShapeOptions; + +const createTransformation = (options: IMediaTransformation): IMediaDataTransformation => ({ + pixels: { + x: Math.round(options.width), + y: Math.round(options.height), + }, + emus: { + x: Math.round(options.width * 9525), + y: Math.round(options.height * 9525), + }, + flip: options.flip, + rotation: options.rotation ? options.rotation * 60000 : undefined, +}); + +export class WpsShapeRun extends Run { + private readonly wpsShapeData: WpsMediaData; + + public constructor(options: IWpsShapeOptions) { + super({}); + + this.wpsShapeData = { + type: options.type, + transformation: createTransformation(options.transformation), + data: { ...options }, + }; + const drawing = new Drawing(this.wpsShapeData, { + floating: options.floating, + docProperties: options.altText, + outline: options.outline, + solidFill: options.solidFill, + }); + + this.root.push(drawing); + } +} From a0d51f6ce1603a5ad9790736d6d652d0fb7eae2b Mon Sep 17 00:00:00 2001 From: Piotr Buszka Date: Sat, 26 Jul 2025 10:14:35 +0200 Subject: [PATCH 2/4] fix: wps-export --- src/file/paragraph/run/wps-shape-run.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/file/paragraph/run/wps-shape-run.ts b/src/file/paragraph/run/wps-shape-run.ts index ab51152f634..d5756d65918 100644 --- a/src/file/paragraph/run/wps-shape-run.ts +++ b/src/file/paragraph/run/wps-shape-run.ts @@ -7,6 +7,8 @@ import { SolidFillOptions } from "../../drawing/inline/graphic/graphic-data/pic/ import { IMediaDataTransformation, IMediaTransformation, WpsMediaData } from "../../media"; import { Run } from "../run"; +export * from "@file/drawing/inline/graphic/graphic-data/wps/body-properties"; + type CoreShapeOptions = { readonly transformation: IMediaTransformation; readonly floating?: IFloating; From 51b10b0f20cbd355970ee617c7a98e57bbd2fed0 Mon Sep 17 00:00:00 2001 From: Piotr Buszka Date: Tue, 16 Sep 2025 15:17:47 +0200 Subject: [PATCH 3/4] wps --- .../graphic/graphic-data/graphic-data.ts | 40 +++++++++++--- .../pic/shape-properties/form/form.ts | 4 +- .../pic/shape-properties/form/offset/off.ts | 6 +-- .../graphic/graphic-data/wpg/wpg-group.ts | 54 +++++++++++++++++++ src/file/media/data.ts | 27 +++++++++- src/file/media/media.ts | 4 ++ src/file/paragraph/run/image-run.ts | 2 +- src/file/paragraph/run/index.ts | 1 + src/file/paragraph/run/wpg-group-run.ts | 54 +++++++++++++++++++ src/file/paragraph/run/wps-shape-run.ts | 12 ++++- 10 files changed, 189 insertions(+), 15 deletions(-) create mode 100644 src/file/drawing/inline/graphic/graphic-data/wpg/wpg-group.ts create mode 100644 src/file/paragraph/run/wpg-group-run.ts diff --git a/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts b/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts index 9a30b8595be..67a0c5fe6e6 100644 --- a/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts +++ b/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts @@ -1,11 +1,12 @@ import { WpsShape } from "@file/drawing/inline/graphic/graphic-data/wps/wps-shape"; -import { IExtendedMediaData, IMediaDataTransformation } from "@file/media"; +import { IExtendedMediaData, IMediaData, IMediaDataTransformation, WpgMediaData } from "@file/media"; import { XmlComponent } from "@file/xml-components"; import { GraphicDataAttributes } from "./graphic-data-attribute"; import { Pic } from "./pic"; import { OutlineOptions } from "./pic/shape-properties/outline/outline"; import { SolidFillOptions } from "./pic/shape-properties/outline/solid-fill"; +import { WpgGroup } from "./wpg/wpg-group"; export class GraphicData extends XmlComponent { // private readonly pic: Pic; @@ -29,20 +30,43 @@ export class GraphicData extends XmlComponent { uri: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape", }), ); + const wps = new WpsShape({ ...mediaData.data, transformation: transform, outline, solidFill }); + this.root.push(wps); + } else if (mediaData.type === "wpg") { + this.root.push( + new GraphicDataAttributes({ + uri: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup", + }), + ); + const md = mediaData as WpgMediaData + const children = md.children.map(child => { + // eslint-disable-next-line unicorn/prefer-ternary + if (child.type === "wps") { + return new WpsShape({ ...child.data, transformation: child.transformation, outline: child.outline, solidFill: child.solidFill }); + } else { + return new Pic({ mediaData: child, transform: child.transformation, outline: child.outline }); + } + }) + // const wps = new WpsShape({ ...mediaData.data, transformation: transform, outline, solidFill }); + const wpg = new WpgGroup({ children, transformation: transform }); + this.root.push(wpg); } else { this.root.push( new GraphicDataAttributes({ uri: "http://schemas.openxmlformats.org/drawingml/2006/picture", }), ); - } - - if (mediaData.type !== "wps") { - const pic = new Pic({ mediaData, transform, outline }); + const md = mediaData as IMediaData + const pic = new Pic({ mediaData: md, transform, outline }); this.root.push(pic); - } else { - const wps = new WpsShape({ ...mediaData.data, transformation: transform, outline, solidFill }); - this.root.push(wps); } + + // if (mediaData.type !== "wps") { + // const pic = new Pic({ mediaData, transform, outline }); + // this.root.push(pic); + // } else { + // const wps = new WpsShape({ ...mediaData.data, transformation: transform, outline, solidFill }); + // this.root.push(wps); + // } } } diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/form.ts b/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/form.ts index ca413ca958e..f2058fa05c6 100644 --- a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/form.ts +++ b/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/form.ts @@ -20,6 +20,7 @@ export class FormAttributes extends XmlAttributeComponent<{ export class Form extends XmlComponent { private readonly extents: Extents; + private readonly offset: Offset; public constructor(options: IMediaDataTransformation) { super("a:xfrm"); @@ -32,9 +33,10 @@ export class Form extends XmlComponent { }), ); + this.offset = new Offset(options.offset?.emus?.x, options.offset?.emus?.y); this.extents = new Extents(options.emus.x, options.emus.y); - this.root.push(new Offset()); + this.root.push(this.offset); this.root.push(this.extents); } } diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/offset/off.ts b/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/offset/off.ts index ab0ab07f77b..141df84c856 100644 --- a/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/offset/off.ts +++ b/src/file/drawing/inline/graphic/graphic-data/pic/shape-properties/form/offset/off.ts @@ -4,13 +4,13 @@ import { XmlComponent } from "@file/xml-components"; import { OffsetAttributes } from "./off-attributes"; export class Offset extends XmlComponent { - public constructor() { + public constructor(x: number | undefined, y: number | undefined) { super("a:off"); this.root.push( new OffsetAttributes({ - x: 0, - y: 0, + x: x ?? 0, + y: y ?? 0, }), ); } diff --git a/src/file/drawing/inline/graphic/graphic-data/wpg/wpg-group.ts b/src/file/drawing/inline/graphic/graphic-data/wpg/wpg-group.ts new file mode 100644 index 00000000000..06e836e18a1 --- /dev/null +++ b/src/file/drawing/inline/graphic/graphic-data/wpg/wpg-group.ts @@ -0,0 +1,54 @@ +import { IMediaDataTransformation } from "@file/media"; +import { XmlComponent } from "@file/xml-components"; + +import { Pic } from "../pic"; +import { Form } from "../pic/shape-properties/form"; +import { WpsShape } from "../wps/wps-shape"; + +export type GroupChild = WpsShape | Pic; + +export type WpgGroupCoreOptions = { + readonly children: readonly GroupChild[]; + // readonly nonVisualProperties?: INonVisualShapePropertiesOptions; + // readonly bodyProperties?: IBodyPropertiesOptions; +}; + +export type WpgGroupOptions = WpgGroupCoreOptions & { + readonly transformation: IMediaDataTransformation; +}; + +export class GroupProperties extends XmlComponent { + private readonly form: Form; + + public constructor({ + transform, + }: { + readonly transform: IMediaDataTransformation; + }) { + super(`wpg:grpSpPr`); + + this.form = new Form(transform); + this.root.push(this.form); + } +} + +export class NonVisualGroupProperties extends XmlComponent { + public constructor() { + super('wpg:cNvGrpSpPr'); + } +} + + +export class WpgGroup extends XmlComponent { + public constructor(options: WpgGroupOptions) { + super("wpg:wgp"); + + this.root.push(new NonVisualGroupProperties()); + this.root.push( + new GroupProperties({ + transform: options.transformation, + }), + ); + this.root.push(...options.children); + } +} diff --git a/src/file/media/data.ts b/src/file/media/data.ts index bfb9d72a956..80639308c00 100644 --- a/src/file/media/data.ts +++ b/src/file/media/data.ts @@ -1,6 +1,18 @@ +import { OutlineOptions } from "@file/drawing/inline/graphic/graphic-data/pic/shape-properties/outline/outline"; +import { SolidFillOptions } from "@file/drawing/inline/graphic/graphic-data/pic/shape-properties/outline/solid-fill"; import { WpsShapeCoreOptions } from "@file/drawing/inline/graphic/graphic-data/wps"; export type IMediaDataTransformation = { + readonly offset?: { + readonly pixels: { + readonly x: number; + readonly y: number; + }; + readonly emus?: { + readonly x: number; + readonly y: number; + } + }; readonly pixels: { readonly x: number; readonly y: number; @@ -40,7 +52,20 @@ export type WpsMediaData = { readonly data: WpsShapeCoreOptions; }; -export type IExtendedMediaData = IMediaData | WpsMediaData; +export type WpgCommonMediaData = { + readonly outline?: OutlineOptions; + readonly solidFill?: SolidFillOptions; +}; + +export type IGroupChildMediaData = (WpsMediaData | IMediaData) & WpgCommonMediaData + +export type WpgMediaData = { + readonly type: "wpg"; + readonly transformation: IMediaDataTransformation; + readonly children: readonly IGroupChildMediaData[]; +}; + +export type IExtendedMediaData = IMediaData | WpsMediaData | WpgMediaData; export type IMediaData = (RegularMediaData | SvgMediaData) & CoreMediaData; diff --git a/src/file/media/media.ts b/src/file/media/media.ts index 6007658721b..9da7d504043 100644 --- a/src/file/media/media.ts +++ b/src/file/media/media.ts @@ -1,6 +1,10 @@ import { IMediaData } from "./data"; export type IMediaTransformation = { + readonly offset?: { + readonly top?: number; + readonly left?: number; + }; readonly width: number; readonly height: number; readonly flip?: { diff --git a/src/file/paragraph/run/image-run.ts b/src/file/paragraph/run/image-run.ts index da8f06198a8..954af6a9489 100644 --- a/src/file/paragraph/run/image-run.ts +++ b/src/file/paragraph/run/image-run.ts @@ -56,7 +56,7 @@ const convertDataURIToBinary = (dataURI: string): Uint8Array => { } }; -const standardizeData = (data: string | Buffer | Uint8Array | ArrayBuffer): Buffer | Uint8Array | ArrayBuffer => +export const standardizeData = (data: string | Buffer | Uint8Array | ArrayBuffer): Buffer | Uint8Array | ArrayBuffer => typeof data === "string" ? convertDataURIToBinary(data) : data; const createImageData = (options: IImageOptions, key: string): Pick => ({ diff --git a/src/file/paragraph/run/index.ts b/src/file/paragraph/run/index.ts index 606f44d84f6..7e58ce926d6 100644 --- a/src/file/paragraph/run/index.ts +++ b/src/file/paragraph/run/index.ts @@ -4,6 +4,7 @@ export * from "./text-run"; export * from "./symbol-run"; export * from "./image-run"; export * from "./wps-shape-run"; +export * from "./wpg-group-run"; export * from "./run-fonts"; export * from "./sequential-identifier"; export * from "./underline"; diff --git a/src/file/paragraph/run/wpg-group-run.ts b/src/file/paragraph/run/wpg-group-run.ts new file mode 100644 index 00000000000..96ecab456eb --- /dev/null +++ b/src/file/paragraph/run/wpg-group-run.ts @@ -0,0 +1,54 @@ +import { DocPropertiesOptions } from '@file/drawing/doc-properties/doc-properties'; +import { IContext, IXmlableObject } from "@file/xml-components"; + + +import { Run } from "."; +import { createTransformation } from './wps-shape-run'; +import { Drawing, IFloating } from "../../drawing"; +import { IGroupChildMediaData, IMediaData, IMediaTransformation, WpgMediaData } from "../../media"; + +export * from "@file/drawing/inline/graphic/graphic-data/wps/body-properties"; + +type CoreGroupOptions = { + readonly children: readonly IGroupChildMediaData[]; + readonly transformation: IMediaTransformation; + readonly floating?: IFloating; + readonly altText?: DocPropertiesOptions; +}; + +export type IWpgGroupOptions = { readonly type: "wpg" } & CoreGroupOptions; + +export class WpgGroupRun extends Run { + private readonly wpgGroupData: WpgMediaData; + private readonly mediaDatas: readonly IMediaData[]; + + public constructor(options: IWpgGroupOptions) { + super({}); + + this.wpgGroupData = { + type: options.type, + transformation: createTransformation(options.transformation), + children: options.children, + }; + const drawing = new Drawing(this.wpgGroupData, { + floating: options.floating, + docProperties: options.altText, + }); + + this.mediaDatas = options.children.filter(child => child.type !== "wps").map(child => child as IMediaData) + + this.root.push(drawing); + } + + public prepForXml(context: IContext): IXmlableObject | undefined { + this.mediaDatas.forEach(child => { + + context.file.Media.addImage(child.fileName, child); + + if (child.type === "svg") { + context.file.Media.addImage(child.fallback.fileName, child.fallback); + } + }) + return super.prepForXml(context); + } +} diff --git a/src/file/paragraph/run/wps-shape-run.ts b/src/file/paragraph/run/wps-shape-run.ts index d5756d65918..bde668090e4 100644 --- a/src/file/paragraph/run/wps-shape-run.ts +++ b/src/file/paragraph/run/wps-shape-run.ts @@ -19,7 +19,17 @@ type CoreShapeOptions = { export type IWpsShapeOptions = WpsShapeCoreOptions & { readonly type: "wps" } & CoreShapeOptions; -const createTransformation = (options: IMediaTransformation): IMediaDataTransformation => ({ +export const createTransformation = (options: IMediaTransformation): IMediaDataTransformation => ({ + offset: { + pixels: { + x: Math.round(options.offset?.left ?? 0), + y: Math.round(options.offset?.top ?? 0), + }, + emus: { + x: Math.round((options.offset?.left ?? 0) * 9525), + y: Math.round((options.offset?.top ?? 0) * 9525), + }, + }, pixels: { x: Math.round(options.width), y: Math.round(options.height), From e12395ac5f128c4732e3447dde13b05b8cb98376 Mon Sep 17 00:00:00 2001 From: Piotr Buszka Date: Tue, 30 Sep 2025 20:25:31 +0200 Subject: [PATCH 4/4] chore: publish workaround --- .npmrc | 2 +- package-lock.json | 8 ++++---- package.json | 6 +++--- .../graphic/graphic-data/graphic-data.ts | 15 +++++++++----- .../graphic/graphic-data/wpg/wpg-group.ts | 9 ++------- src/file/media/data.ts | 4 ++-- src/file/paragraph/run/wpg-group-run.ts | 20 +++++++++---------- 7 files changed, 31 insertions(+), 33 deletions(-) diff --git a/.npmrc b/.npmrc index 214c29d1395..ca0f273927f 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1 @@ -registry=https://registry.npmjs.org/ +@feecompass:registry=https://npm.pkg.github.com diff --git a/package-lock.json b/package-lock.json index 30d44606f0a..27965176eac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "docx", - "version": "9.5.1", + "name": "@feecompass/docx", + "version": "9.6.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "docx", - "version": "9.5.1", + "name": "@feecompass/docx", + "version": "9.6.0", "license": "MIT", "dependencies": { "@types/node": "^24.0.1", diff --git a/package.json b/package.json index 517848f7f7e..7c245eefce6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "docx", - "version": "9.5.1", + "name": "@feecompass/docx", + "version": "9.6.0", "description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.", "type": "module", "main": "dist/index.umd.cjs", @@ -43,7 +43,7 @@ ], "repository": { "type": "git", - "url": "git+https://github.com/dolanmiu/docx.git" + "url": "https://github.com/feecompass/docx.git" }, "keywords": [ "docx", diff --git a/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts b/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts index 67a0c5fe6e6..55db7fdced6 100644 --- a/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts +++ b/src/file/drawing/inline/graphic/graphic-data/graphic-data.ts @@ -38,15 +38,20 @@ export class GraphicData extends XmlComponent { uri: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup", }), ); - const md = mediaData as WpgMediaData - const children = md.children.map(child => { + const md = mediaData as WpgMediaData; + const children = md.children.map((child) => { // eslint-disable-next-line unicorn/prefer-ternary if (child.type === "wps") { - return new WpsShape({ ...child.data, transformation: child.transformation, outline: child.outline, solidFill: child.solidFill }); + return new WpsShape({ + ...child.data, + transformation: child.transformation, + outline: child.outline, + solidFill: child.solidFill, + }); } else { return new Pic({ mediaData: child, transform: child.transformation, outline: child.outline }); } - }) + }); // const wps = new WpsShape({ ...mediaData.data, transformation: transform, outline, solidFill }); const wpg = new WpgGroup({ children, transformation: transform }); this.root.push(wpg); @@ -56,7 +61,7 @@ export class GraphicData extends XmlComponent { uri: "http://schemas.openxmlformats.org/drawingml/2006/picture", }), ); - const md = mediaData as IMediaData + const md = mediaData as IMediaData; const pic = new Pic({ mediaData: md, transform, outline }); this.root.push(pic); } diff --git a/src/file/drawing/inline/graphic/graphic-data/wpg/wpg-group.ts b/src/file/drawing/inline/graphic/graphic-data/wpg/wpg-group.ts index 06e836e18a1..39fb33055c7 100644 --- a/src/file/drawing/inline/graphic/graphic-data/wpg/wpg-group.ts +++ b/src/file/drawing/inline/graphic/graphic-data/wpg/wpg-group.ts @@ -20,11 +20,7 @@ export type WpgGroupOptions = WpgGroupCoreOptions & { export class GroupProperties extends XmlComponent { private readonly form: Form; - public constructor({ - transform, - }: { - readonly transform: IMediaDataTransformation; - }) { + public constructor({ transform }: { readonly transform: IMediaDataTransformation }) { super(`wpg:grpSpPr`); this.form = new Form(transform); @@ -34,11 +30,10 @@ export class GroupProperties extends XmlComponent { export class NonVisualGroupProperties extends XmlComponent { public constructor() { - super('wpg:cNvGrpSpPr'); + super("wpg:cNvGrpSpPr"); } } - export class WpgGroup extends XmlComponent { public constructor(options: WpgGroupOptions) { super("wpg:wgp"); diff --git a/src/file/media/data.ts b/src/file/media/data.ts index 80639308c00..4ba3f77c6f1 100644 --- a/src/file/media/data.ts +++ b/src/file/media/data.ts @@ -11,7 +11,7 @@ export type IMediaDataTransformation = { readonly emus?: { readonly x: number; readonly y: number; - } + }; }; readonly pixels: { readonly x: number; @@ -57,7 +57,7 @@ export type WpgCommonMediaData = { readonly solidFill?: SolidFillOptions; }; -export type IGroupChildMediaData = (WpsMediaData | IMediaData) & WpgCommonMediaData +export type IGroupChildMediaData = (WpsMediaData | IMediaData) & WpgCommonMediaData; export type WpgMediaData = { readonly type: "wpg"; diff --git a/src/file/paragraph/run/wpg-group-run.ts b/src/file/paragraph/run/wpg-group-run.ts index 96ecab456eb..466280206f5 100644 --- a/src/file/paragraph/run/wpg-group-run.ts +++ b/src/file/paragraph/run/wpg-group-run.ts @@ -1,9 +1,8 @@ -import { DocPropertiesOptions } from '@file/drawing/doc-properties/doc-properties'; +import { DocPropertiesOptions } from "@file/drawing/doc-properties/doc-properties"; import { IContext, IXmlableObject } from "@file/xml-components"; - import { Run } from "."; -import { createTransformation } from './wps-shape-run'; +import { createTransformation } from "./wps-shape-run"; import { Drawing, IFloating } from "../../drawing"; import { IGroupChildMediaData, IMediaData, IMediaTransformation, WpgMediaData } from "../../media"; @@ -35,20 +34,19 @@ export class WpgGroupRun extends Run { docProperties: options.altText, }); - this.mediaDatas = options.children.filter(child => child.type !== "wps").map(child => child as IMediaData) + this.mediaDatas = options.children.filter((child) => child.type !== "wps").map((child) => child as IMediaData); this.root.push(drawing); } public prepForXml(context: IContext): IXmlableObject | undefined { - this.mediaDatas.forEach(child => { - - context.file.Media.addImage(child.fileName, child); + this.mediaDatas.forEach((child) => { + context.file.Media.addImage(child.fileName, child); - if (child.type === "svg") { - context.file.Media.addImage(child.fallback.fileName, child.fallback); - } - }) + if (child.type === "svg") { + context.file.Media.addImage(child.fallback.fileName, child.fallback); + } + }); return super.prepForXml(context); } }