From 24c159de37090e711eae50af72ef51260f487f95 Mon Sep 17 00:00:00 2001 From: Dolan Date: Sun, 31 Dec 2023 18:54:35 +0000 Subject: [PATCH] Add SVG image suport (#2487) * Add SVG blip extentions * SVG Image feature now works * Add and simplify tests * Fix falsey issue with file Write tests 100% Coverage --- demo/5-images.ts | 23 +++ demo/images/linux-png.png | Bin 0 -> 72994 bytes demo/images/linux-svg.svg | 183 ++++++++++++++++++ docs/_coverpage.md | 2 +- docs/usage/images.md | 8 + docs/usage/patcher.md | 2 +- src/export/packer/image-replacer.spec.ts | 3 +- src/export/packer/next-compiler.spec.ts | 46 ++++- src/export/packer/next-compiler.ts | 9 +- src/file/content-types/content-types.spec.ts | 31 +-- src/file/content-types/content-types.ts | 1 + src/file/drawing/anchor/anchor.spec.ts | 3 +- src/file/drawing/drawing.spec.ts | 3 +- .../graphic-data/pic/blip/blip-extentions.ts | 35 ++++ .../graphic-data/pic/blip/blip-fill.ts | 4 +- .../graphic/graphic-data/pic/blip/blip.ts | 38 ++-- src/file/drawing/inline/inline.spec.ts | 3 +- src/file/file.spec.ts | 39 +++- src/file/file.ts | 2 +- src/file/media/data.ts | 20 +- src/file/media/media.spec.ts | 3 +- src/file/paragraph/run/image-run.spec.ts | 127 ++++++++++-- src/file/paragraph/run/image-run.ts | 133 ++++++++----- src/file/paragraph/run/run-components/text.ts | 2 - src/patcher/from-docx.spec.ts | 5 + src/patcher/from-docx.ts | 2 +- vite.config.ts | 6 +- 27 files changed, 615 insertions(+), 118 deletions(-) create mode 100644 demo/images/linux-png.png create mode 100644 demo/images/linux-svg.svg create mode 100644 src/file/drawing/inline/graphic/graphic-data/pic/blip/blip-extentions.ts diff --git a/demo/5-images.ts b/demo/5-images.ts index 6039d6c832..7953b939cd 100644 --- a/demo/5-images.ts +++ b/demo/5-images.ts @@ -21,6 +21,7 @@ const doc = new Document({ new Paragraph({ children: [ new ImageRun({ + type: "jpg", data: fs.readFileSync("./demo/images/image1.jpeg"), transformation: { width: 100, @@ -37,6 +38,7 @@ const doc = new Document({ new Paragraph({ children: [ new ImageRun({ + type: "png", data: fs.readFileSync("./demo/images/dog.png").toString("base64"), transformation: { width: 100, @@ -53,6 +55,7 @@ const doc = new Document({ new Paragraph({ children: [ new ImageRun({ + type: "jpg", data: fs.readFileSync("./demo/images/cat.jpg"), transformation: { width: 100, @@ -73,6 +76,7 @@ const doc = new Document({ new Paragraph({ children: [ new ImageRun({ + type: "bmp", data: fs.readFileSync("./demo/images/parrots.bmp"), transformation: { width: 150, @@ -88,6 +92,7 @@ const doc = new Document({ new Paragraph({ children: [ new ImageRun({ + type: "gif", data: fs.readFileSync("./demo/images/pizza.gif"), transformation: { width: 200, @@ -103,6 +108,7 @@ const doc = new Document({ new Paragraph({ children: [ new ImageRun({ + type: "gif", data: fs.readFileSync("./demo/images/pizza.gif"), transformation: { width: 200, @@ -124,6 +130,7 @@ const doc = new Document({ new Paragraph({ children: [ new ImageRun({ + type: "jpg", data: fs.readFileSync("./demo/images/cat.jpg"), transformation: { width: 200, @@ -143,6 +150,22 @@ const doc = new Document({ }), ], }), + new Paragraph({ + children: [ + new ImageRun({ + type: "svg", + data: fs.readFileSync("./demo/images/linux-svg.svg"), + transformation: { + width: 200, + height: 200, + }, + fallback: { + type: "png", + data: fs.readFileSync("./demo/images/linux-png.png"), + }, + }), + ], + }), ], }, ], diff --git a/demo/images/linux-png.png b/demo/images/linux-png.png new file mode 100644 index 0000000000000000000000000000000000000000..f4e2d5ad2779b4d9a35bd363e0da340396dbad0d GIT binary patch literal 72994 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 vc)k0in8$c#{8f9)E&l%i{r<6S literal 0 HcmV?d00001 diff --git a/demo/images/linux-svg.svg b/demo/images/linux-svg.svg new file mode 100644 index 0000000000..da9140f12d --- /dev/null +++ b/demo/images/linux-svg.svg @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_coverpage.md b/docs/_coverpage.md index b8a546f3a4..665583fc68 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -4,7 +4,7 @@ - Simple, declarative API - 80+ usage examples -- Battle tested, mature, 99.9%+ coverage +- Battle tested, mature, 100% coverage (yes, every line is tested) [GitHub](https://github.com/dolanmiu/docx) [Get Started](#Welcome) diff --git a/docs/usage/images.md b/docs/usage/images.md index 1326a780b2..8fa763b3bf 100644 --- a/docs/usage/images.md +++ b/docs/usage/images.md @@ -6,6 +6,7 @@ To create a `floating` image on top of text: ```ts const image = new ImageRun({ + type: 'gif', data: fs.readFileSync("./demo/images/pizza.gif"), transformation: { width: 200, @@ -26,6 +27,7 @@ By default with no arguments, its an `inline` image: ```ts const image = new ImageRun({ + type: 'gif', data: fs.readFileSync("./demo/images/pizza.gif"), transformation: { width: 100, @@ -59,6 +61,7 @@ const doc = new Document({ new Paragraph({ children: [ new ImageRun({ + type: [IMAGE_TYPE], data: [IMAGE_BUFFER], transformation: { width: [IMAGE_SIZE], @@ -97,6 +100,7 @@ To change the position the image to be on top of the text, simply add the `float ```ts const image = new ImageRun({ + type: 'png', data: buffer, transformation: { width: 903, @@ -115,6 +119,7 @@ const image = new ImageRun({ ```ts const image = new ImageRun({ + type: 'png', data: buffer, transformation: { width: 903, @@ -180,6 +185,7 @@ For example: ```ts const image = new ImageRun({ + type: 'gif', data: fs.readFileSync("./demo/images/pizza.gif"), transformation: { width: 200, @@ -228,6 +234,7 @@ For example: ```ts const image = new ImageRun({ + type: 'gif', data: fs.readFileSync("./demo/images/pizza.gif"), transformation: { width: 200, @@ -258,6 +265,7 @@ Specifies common non-visual DrawingML properties. A name, title and description ```ts const image = new ImageRun({ + type: 'gif', data: fs.readFileSync("./demo/images/pizza.gif"), altText: { title: "This is an ultimate title", diff --git a/docs/usage/patcher.md b/docs/usage/patcher.md index 6f76b1d180..fecc709dcf 100644 --- a/docs/usage/patcher.md +++ b/docs/usage/patcher.md @@ -76,7 +76,7 @@ patchDocument(fs.readFileSync("My Document.docx"), { ], link: "https://www.google.co.uk", }), - new ImageRun({ data: fs.readFileSync("./demo/images/dog.png"), transformation: { width: 100, height: 100 } }), + new ImageRun({ type: 'png', data: fs.readFileSync("./demo/images/dog.png"), transformation: { width: 100, height: 100 } }), ], }), ], diff --git a/src/export/packer/image-replacer.spec.ts b/src/export/packer/image-replacer.spec.ts index f9f5f33e47..33ff33f4cf 100644 --- a/src/export/packer/image-replacer.spec.ts +++ b/src/export/packer/image-replacer.spec.ts @@ -12,7 +12,8 @@ describe("ImageReplacer", () => { "test {test-image.png} test", [ { - stream: Buffer.from(""), + type: "png", + data: Buffer.from(""), fileName: "test-image.png", transformation: { pixels: { diff --git a/src/export/packer/next-compiler.spec.ts b/src/export/packer/next-compiler.spec.ts index 6713ee2734..753389de44 100644 --- a/src/export/packer/next-compiler.spec.ts +++ b/src/export/packer/next-compiler.spec.ts @@ -150,12 +150,25 @@ describe("Compiler", () => { new Paragraph({ children: [ new ImageRun({ + type: "png", data: Buffer.from("", "base64"), transformation: { width: 100, height: 100, }, }), + new ImageRun({ + type: "svg", + data: Buffer.from("", "base64"), + transformation: { + width: 100, + height: 100, + }, + fallback: { + type: "png", + data: Buffer.from("", "base64"), + }, + }), ], }), ], @@ -165,7 +178,8 @@ describe("Compiler", () => { vi.spyOn(compiler["imageReplacer"], "getMediaData").mockReturnValue([ { - stream: Buffer.from(""), + type: "png", + data: Buffer.from(""), fileName: "test", transformation: { pixels: { @@ -178,6 +192,36 @@ describe("Compiler", () => { }, }, }, + { + type: "svg", + data: Buffer.from(""), + fileName: "test", + transformation: { + pixels: { + x: 100, + y: 100, + }, + emus: { + x: 100, + y: 100, + }, + }, + fallback: { + type: "png", + data: Buffer.from(""), + fileName: "test", + transformation: { + pixels: { + x: 100, + y: 100, + }, + emus: { + x: 100, + y: 100, + }, + }, + }, + }, ]); compiler.compile(file); diff --git a/src/export/packer/next-compiler.ts b/src/export/packer/next-compiler.ts index 0b0434a0b2..b7df84c899 100644 --- a/src/export/packer/next-compiler.ts +++ b/src/export/packer/next-compiler.ts @@ -62,8 +62,13 @@ export class Compiler { } } - for (const { stream, fileName } of file.Media.Array) { - zip.file(`word/media/${fileName}`, stream); + for (const data of file.Media.Array) { + if (data.type !== "svg") { + zip.file(`word/media/${data.fileName}`, data.data); + } else { + zip.file(`word/media/${data.fileName}`, data.data); + zip.file(`word/media/${data.fallback.fileName}`, data.fallback.data); + } } for (const { data: buffer, name, fontKey } of file.FontTable.fontOptionsWithKey) { diff --git a/src/file/content-types/content-types.spec.ts b/src/file/content-types/content-types.spec.ts index a0203d0dae..1fac77993e 100644 --- a/src/file/content-types/content-types.spec.ts +++ b/src/file/content-types/content-types.spec.ts @@ -25,12 +25,13 @@ describe("ContentTypes", () => { expect(tree["Types"][3]).to.deep.equal({ Default: { _attr: { ContentType: "image/jpeg", Extension: "jpg" } } }); expect(tree["Types"][4]).to.deep.equal({ Default: { _attr: { ContentType: "image/bmp", Extension: "bmp" } } }); expect(tree["Types"][5]).to.deep.equal({ Default: { _attr: { ContentType: "image/gif", Extension: "gif" } } }); - expect(tree["Types"][6]).to.deep.equal({ + expect(tree["Types"][6]).to.deep.equal({ Default: { _attr: { ContentType: "image/svg+xml", Extension: "svg" } } }); + expect(tree["Types"][7]).to.deep.equal({ Default: { _attr: { ContentType: "application/vnd.openxmlformats-package.relationships+xml", Extension: "rels" } }, }); - expect(tree["Types"][7]).to.deep.equal({ Default: { _attr: { ContentType: "application/xml", Extension: "xml" } } }); + expect(tree["Types"][8]).to.deep.equal({ Default: { _attr: { ContentType: "application/xml", Extension: "xml" } } }); - expect(tree["Types"][8]).to.deep.equal({ + expect(tree["Types"][9]).to.deep.equal({ Default: { _attr: { ContentType: "application/vnd.openxmlformats-officedocument.obfuscatedFont", @@ -38,7 +39,7 @@ describe("ContentTypes", () => { }, }, }); - expect(tree["Types"][9]).to.deep.equal({ + expect(tree["Types"][10]).to.deep.equal({ Override: { _attr: { ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", @@ -46,7 +47,7 @@ describe("ContentTypes", () => { }, }, }); - expect(tree["Types"][10]).to.deep.equal({ + expect(tree["Types"][11]).to.deep.equal({ Override: { _attr: { ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", @@ -54,7 +55,7 @@ describe("ContentTypes", () => { }, }, }); - expect(tree["Types"][11]).to.deep.equal({ + expect(tree["Types"][12]).to.deep.equal({ Override: { _attr: { ContentType: "application/vnd.openxmlformats-package.core-properties+xml", @@ -62,7 +63,7 @@ describe("ContentTypes", () => { }, }, }); - expect(tree["Types"][12]).to.deep.equal({ + expect(tree["Types"][13]).to.deep.equal({ Override: { _attr: { ContentType: "application/vnd.openxmlformats-officedocument.custom-properties+xml", @@ -70,7 +71,7 @@ describe("ContentTypes", () => { }, }, }); - expect(tree["Types"][13]).to.deep.equal({ + expect(tree["Types"][14]).to.deep.equal({ Override: { _attr: { ContentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml", @@ -78,7 +79,7 @@ describe("ContentTypes", () => { }, }, }); - expect(tree["Types"][14]).to.deep.equal({ + expect(tree["Types"][15]).to.deep.equal({ Override: { _attr: { ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", @@ -86,7 +87,7 @@ describe("ContentTypes", () => { }, }, }); - expect(tree["Types"][15]).to.deep.equal({ + expect(tree["Types"][16]).to.deep.equal({ Override: { _attr: { ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", @@ -94,7 +95,7 @@ describe("ContentTypes", () => { }, }, }); - expect(tree["Types"][16]).to.deep.equal({ + expect(tree["Types"][17]).to.deep.equal({ Override: { _attr: { ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", @@ -111,7 +112,7 @@ describe("ContentTypes", () => { contentTypes.addFooter(102); const tree = new Formatter().format(contentTypes); - expect(tree["Types"][19]).to.deep.equal({ + expect(tree["Types"][20]).to.deep.equal({ Override: { _attr: { ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", @@ -120,7 +121,7 @@ describe("ContentTypes", () => { }, }); - expect(tree["Types"][20]).to.deep.equal({ + expect(tree["Types"][21]).to.deep.equal({ Override: { _attr: { ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", @@ -137,7 +138,7 @@ describe("ContentTypes", () => { contentTypes.addHeader(202); const tree = new Formatter().format(contentTypes); - expect(tree["Types"][19]).to.deep.equal({ + expect(tree["Types"][20]).to.deep.equal({ Override: { _attr: { ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", @@ -146,7 +147,7 @@ describe("ContentTypes", () => { }, }); - expect(tree["Types"][20]).to.deep.equal({ + expect(tree["Types"][21]).to.deep.equal({ Override: { _attr: { ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", diff --git a/src/file/content-types/content-types.ts b/src/file/content-types/content-types.ts index 399581f7d6..fc7cc8a8b0 100644 --- a/src/file/content-types/content-types.ts +++ b/src/file/content-types/content-types.ts @@ -18,6 +18,7 @@ export class ContentTypes extends XmlComponent { this.root.push(new Default("image/jpeg", "jpg")); this.root.push(new Default("image/bmp", "bmp")); this.root.push(new Default("image/gif", "gif")); + this.root.push(new Default("image/svg+xml", "svg")); this.root.push(new Default("application/vnd.openxmlformats-package.relationships+xml", "rels")); this.root.push(new Default("application/xml", "xml")); this.root.push(new Default("application/vnd.openxmlformats-officedocument.obfuscatedFont", "odttf")); diff --git a/src/file/drawing/anchor/anchor.spec.ts b/src/file/drawing/anchor/anchor.spec.ts index 215aa087db..ab0c9cb401 100644 --- a/src/file/drawing/anchor/anchor.spec.ts +++ b/src/file/drawing/anchor/anchor.spec.ts @@ -11,8 +11,9 @@ import { Anchor } from "./anchor"; const createAnchor = (drawingOptions: IDrawingOptions): Anchor => new Anchor({ mediaData: { + type: "png", fileName: "test.png", - stream: Buffer.from(""), + data: Buffer.from(""), transformation: { pixels: { x: 0, diff --git a/src/file/drawing/drawing.spec.ts b/src/file/drawing/drawing.spec.ts index 60fe335792..73bb1fcd15 100644 --- a/src/file/drawing/drawing.spec.ts +++ b/src/file/drawing/drawing.spec.ts @@ -11,8 +11,9 @@ const imageBase64Data = `iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAACzVBMVEU const createDrawing = (drawingOptions?: IDrawingOptions): Drawing => new Drawing( { + type: "jpg", fileName: "test.jpg", - stream: Buffer.from(imageBase64Data, "base64"), + data: Buffer.from(imageBase64Data, "base64"), transformation: { pixels: { x: 100, diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip-extentions.ts b/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip-extentions.ts new file mode 100644 index 0000000000..909d87f80b --- /dev/null +++ b/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip-extentions.ts @@ -0,0 +1,35 @@ +import { BuilderElement, XmlComponent } from "@file/xml-components"; +import { IMediaData } from "@file/media"; + +const createSvgBlip = (mediaData: IMediaData): XmlComponent => + new BuilderElement({ + name: "asvg:svgBlip", + attributes: { + asvg: { + key: "xmlns:asvg", + value: "http://schemas.microsoft.com/office/drawing/2016/SVG/main", + }, + embed: { + key: "r:embed", + value: `rId{${mediaData.fileName}}`, + }, + }, + }); + +const createExtention = (mediaData: IMediaData): XmlComponent => + new BuilderElement({ + name: "a:ext", + attributes: { + uri: { + key: "uri", + value: "{96DAC541-7B7A-43D3-8B79-37D633B846F1}", + }, + }, + children: [createSvgBlip(mediaData)], + }); + +export const createExtentionList = (mediaData: IMediaData): XmlComponent => + new BuilderElement({ + name: "a:extLst", + children: [createExtention(mediaData)], + }); diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip-fill.ts b/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip-fill.ts index 00b90fcef5..5f9bacebed 100644 --- a/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip-fill.ts +++ b/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip-fill.ts @@ -1,7 +1,7 @@ import { IMediaData } from "@file/media"; import { XmlComponent } from "@file/xml-components"; -import { Blip } from "./blip"; +import { createBlip } from "./blip"; import { SourceRectangle } from "./source-rectangle"; import { Stretch } from "./stretch"; @@ -9,7 +9,7 @@ export class BlipFill extends XmlComponent { public constructor(mediaData: IMediaData) { super("pic:blipFill"); - this.root.push(new Blip(mediaData)); + this.root.push(createBlip(mediaData)); this.root.push(new SourceRectangle()); this.root.push(new Stretch()); } diff --git a/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip.ts b/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip.ts index 8db38d6762..d322272737 100644 --- a/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip.ts +++ b/src/file/drawing/inline/graphic/graphic-data/pic/blip/blip.ts @@ -1,24 +1,24 @@ import { IMediaData } from "@file/media"; -import { XmlAttributeComponent, XmlComponent } from "@file/xml-components"; +import { BuilderElement, XmlComponent } from "@file/xml-components"; +import { createExtentionList } from "./blip-extentions"; -class BlipAttributes extends XmlAttributeComponent<{ +type BlipAttributes = { readonly embed: string; readonly cstate: string; -}> { - protected readonly xmlKeys = { - embed: "r:embed", - cstate: "cstate", - }; -} +}; -export class Blip extends XmlComponent { - public constructor(mediaData: IMediaData) { - super("a:blip"); - this.root.push( - new BlipAttributes({ - embed: `rId{${mediaData.fileName}}`, - cstate: "none", - }), - ); - } -} +export const createBlip = (mediaData: IMediaData): XmlComponent => + new BuilderElement({ + name: "a:blip", + attributes: { + embed: { + key: "r:embed", + value: `rId{${mediaData.type === "svg" ? mediaData.fallback.fileName : mediaData.fileName}}`, + }, + cstate: { + key: "cstate", + value: "none", + }, + }, + children: mediaData.type === "svg" ? [createExtentionList(mediaData)] : [], + }); diff --git a/src/file/drawing/inline/inline.spec.ts b/src/file/drawing/inline/inline.spec.ts index fca5ed61c9..1d2a791507 100644 --- a/src/file/drawing/inline/inline.spec.ts +++ b/src/file/drawing/inline/inline.spec.ts @@ -8,8 +8,9 @@ describe("Inline", () => { const tree = new Formatter().format( createInline({ mediaData: { + type: "png", fileName: "test.png", - stream: Buffer.from(""), + data: Buffer.from(""), transformation: { pixels: { x: 0, diff --git a/src/file/file.spec.ts b/src/file/file.spec.ts index ac0098a379..7f4cca4ae2 100644 --- a/src/file/file.spec.ts +++ b/src/file/file.spec.ts @@ -436,7 +436,44 @@ describe("File", () => { it("should work with external styles", () => { const doc = new File({ sections: [], - externalStyles: "", + externalStyles: ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `, }); expect(doc.Styles).to.not.be.undefined; diff --git a/src/file/file.ts b/src/file/file.ts index 5aee17d7d1..47dd895166 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -84,7 +84,7 @@ export class File { this.media = new Media(); - if (options.externalStyles) { + if (options.externalStyles !== undefined) { const stylesFactory = new ExternalStylesFactory(); this.styles = stylesFactory.newInstance(options.externalStyles); } else if (options.styles) { diff --git a/src/file/media/data.ts b/src/file/media/data.ts index eb5ae4629d..1cdd76f2a7 100644 --- a/src/file/media/data.ts +++ b/src/file/media/data.ts @@ -14,11 +14,25 @@ export interface IMediaDataTransformation { readonly rotation?: number; } -export interface IMediaData { - readonly stream: Buffer | Uint8Array | ArrayBuffer; +type CoreMediaData = { readonly fileName: string; readonly transformation: IMediaDataTransformation; -} + readonly data: Buffer | Uint8Array | ArrayBuffer; +}; + +type RegularMediaData = { + readonly type: "jpg" | "png" | "gif" | "bmp"; +}; + +type SvgMediaData = { + readonly type: "svg"; + /** + * Required in case the Word processor does not support SVG. + */ + readonly fallback: RegularMediaData & CoreMediaData; +}; + +export type IMediaData = (RegularMediaData | SvgMediaData) & CoreMediaData; // Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432 /** diff --git a/src/file/media/media.spec.ts b/src/file/media/media.spec.ts index 1e59502726..8cc0d0f741 100644 --- a/src/file/media/media.spec.ts +++ b/src/file/media/media.spec.ts @@ -19,7 +19,8 @@ describe("Media", () => { const media = new Media(); media.addImage("test2.png", { - stream: Buffer.from(""), + type: "png", + data: Buffer.from(""), fileName: "test.png", transformation: { pixels: { diff --git a/src/file/paragraph/run/image-run.spec.ts b/src/file/paragraph/run/image-run.spec.ts index 77eff04070..f0fb8161f1 100644 --- a/src/file/paragraph/run/image-run.spec.ts +++ b/src/file/paragraph/run/image-run.spec.ts @@ -19,6 +19,7 @@ describe("ImageRun", () => { describe("#constructor()", () => { it("should create with Buffer", () => { const currentImageRun = new ImageRun({ + type: "png", data: Buffer.from(""), transformation: { width: 200, @@ -39,8 +40,7 @@ describe("ImageRun", () => { const tree = new Formatter().format(currentImageRun, { file: { Media: { - // eslint-disable-next-line @typescript-eslint/no-empty-function - addImage: () => {}, + addImage: vi.fn(), }, } as unknown as File, viewWrapper: {} as unknown as IViewWrapper, @@ -271,6 +271,7 @@ describe("ImageRun", () => { it("should create with string", () => { const currentImageRun = new ImageRun({ + type: "png", data: "", transformation: { width: 200, @@ -291,8 +292,7 @@ describe("ImageRun", () => { const tree = new Formatter().format(currentImageRun, { file: { Media: { - // eslint-disable-next-line @typescript-eslint/no-empty-function - addImage: () => {}, + addImage: vi.fn(), }, } as unknown as File, viewWrapper: {} as unknown as IViewWrapper, @@ -522,10 +522,10 @@ describe("ImageRun", () => { }); it("should return UInt8Array if atob is present", () => { - // eslint-disable-next-line functional/immutable-data - global.atob = () => "atob result"; + vi.spyOn(global, "atob").mockReturnValue("atob result"); const currentImageRun = new ImageRun({ + type: "png", data: "", transformation: { width: 200, @@ -546,8 +546,7 @@ describe("ImageRun", () => { const tree = new Formatter().format(currentImageRun, { file: { Media: { - // eslint-disable-next-line @typescript-eslint/no-empty-function - addImage: () => {}, + addImage: vi.fn(), }, } as unknown as File, viewWrapper: {} as unknown as IViewWrapper, @@ -775,16 +774,13 @@ describe("ImageRun", () => { }, ], }); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any, functional/immutable-data - (global as any).atob = undefined; }); it("should use data as is if its not a string", () => { - // eslint-disable-next-line functional/immutable-data - global.atob = () => "atob result"; + vi.spyOn(global, "atob").mockReturnValue("atob result"); const currentImageRun = new ImageRun({ + type: "png", data: "", transformation: { width: 200, @@ -805,8 +801,7 @@ describe("ImageRun", () => { const tree = new Formatter().format(currentImageRun, { file: { Media: { - // eslint-disable-next-line @typescript-eslint/no-empty-function - addImage: () => {}, + addImage: vi.fn(), }, } as unknown as File, viewWrapper: {} as unknown as IViewWrapper, @@ -1034,9 +1029,107 @@ describe("ImageRun", () => { }, ], }); + }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any, functional/immutable-data - (global as any).atob = undefined; + it("should strip base64 marker", () => { + const spy = vi.spyOn(global, "atob").mockReturnValue("atob result"); + + new ImageRun({ + type: "png", + data: ";base64,", + transformation: { + width: 200, + height: 200, + rotation: 45, + }, + }); + + expect(spy).toBeCalledWith(""); + }); + + it("should work with svgs", () => { + const currentImageRun = new ImageRun({ + type: "svg", + data: Buffer.from(""), + transformation: { + width: 200, + height: 200, + }, + fallback: { + type: "png", + data: Buffer.from(""), + }, + }); + + const tree = new Formatter().format(currentImageRun, { + file: { + Media: { + addImage: vi.fn(), + }, + } as unknown as File, + viewWrapper: {} as unknown as IViewWrapper, + stack: [], + }); + + expect(tree).toStrictEqual({ + "w:r": [ + { + "w:drawing": [ + { + "wp:inline": expect.arrayContaining([ + { + "a:graphic": expect.arrayContaining([ + { + "a:graphicData": expect.arrayContaining([ + { + "pic:pic": expect.arrayContaining([ + { + "pic:blipFill": expect.arrayContaining([ + { + "a:blip": [ + { + _attr: { + cstate: "none", + "r:embed": "rId{test-unique-id.png}", + }, + }, + { + "a:extLst": [ + { + "a:ext": [ + { + _attr: { + uri: "{96DAC541-7B7A-43D3-8B79-37D633B846F1}", + }, + }, + { + "asvg:svgBlip": { + _attr: expect.objectContaining({ + "r:embed": + "rId{test-unique-id.svg}", + }), + }, + }, + ], + }, + ], + }, + ], + }, + ]), + }, + ]), + }, + ]), + }, + ]), + }, + ]), + }, + ], + }, + ], + }); }); }); }); diff --git a/src/file/paragraph/run/image-run.ts b/src/file/paragraph/run/image-run.ts index 7a674a13c8..b2dfa1280e 100644 --- a/src/file/paragraph/run/image-run.ts +++ b/src/file/paragraph/run/image-run.ts @@ -9,38 +9,102 @@ import { IMediaTransformation } from "../../media"; import { IMediaData } from "../../media/data"; import { Run } from "../run"; -export interface IImageOptions { - readonly data: Buffer | string | Uint8Array | ArrayBuffer; +type CoreImageOptions = { readonly transformation: IMediaTransformation; readonly floating?: IFloating; readonly altText?: DocPropertiesOptions; readonly outline?: OutlineOptions; -} +}; + +type RegularImageOptions = { + readonly type: "jpg" | "png" | "gif" | "bmp"; + readonly data: Buffer | string | Uint8Array | ArrayBuffer; +}; + +type SvgMediaOptions = { + readonly type: "svg"; + readonly data: Buffer | string | Uint8Array | ArrayBuffer; + /** + * Required in case the Word processor does not support SVG. + */ + readonly fallback: RegularImageOptions; +}; + +export type IImageOptions = (RegularImageOptions | SvgMediaOptions) & CoreImageOptions; + +const convertDataURIToBinary = (dataURI: string): Uint8Array => { + if (typeof atob === "function") { + // https://gist.github.com/borismus/1032746 + // https://github.com/mafintosh/base64-to-uint8array + const BASE64_MARKER = ";base64,"; + const base64Index = dataURI.indexOf(BASE64_MARKER); + + const base64IndexWithOffset = base64Index === -1 ? 0 : base64Index + BASE64_MARKER.length; + + return new Uint8Array( + atob(dataURI.substring(base64IndexWithOffset)) + .split("") + .map((c) => c.charCodeAt(0)), + ); + /* c8 ignore next 6 */ + } else { + // Not possible to test this branch in NodeJS + // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires + const b = require("buf" + "fer"); + return new b.Buffer(dataURI, "base64"); + } +}; + +const standardizeData = (data: string | Buffer | Uint8Array | ArrayBuffer): Buffer | Uint8Array | ArrayBuffer => + typeof data === "string" ? convertDataURIToBinary(data) : data; + +const createImageData = (options: IImageOptions, key: string): Pick => ({ + data: standardizeData(options.data), + fileName: key, + transformation: { + pixels: { + x: Math.round(options.transformation.width), + y: Math.round(options.transformation.height), + }, + emus: { + x: Math.round(options.transformation.width * 9525), + y: Math.round(options.transformation.height * 9525), + }, + flip: options.transformation.flip, + rotation: options.transformation.rotation ? options.transformation.rotation * 60000 : undefined, + }, +}); export class ImageRun extends Run { - private readonly key = `${uniqueId()}.png`; + private readonly key: string; + private readonly fallbackKey = `${uniqueId()}.png`; private readonly imageData: IMediaData; public constructor(options: IImageOptions) { super({}); - const newData = typeof options.data === "string" ? this.convertDataURIToBinary(options.data) : options.data; - this.imageData = { - stream: newData, - fileName: this.key, - transformation: { - pixels: { - x: Math.round(options.transformation.width), - y: Math.round(options.transformation.height), - }, - emus: { - x: Math.round(options.transformation.width * 9525), - y: Math.round(options.transformation.height * 9525), - }, - flip: options.transformation.flip, - rotation: options.transformation.rotation ? options.transformation.rotation * 60000 : undefined, - }, - }; + this.key = `${uniqueId()}.${options.type}`; + + this.imageData = + options.type === "svg" + ? { + type: options.type, + ...createImageData(options, this.key), + fallback: { + type: options.fallback.type, + ...createImageData( + { + ...options.fallback, + transformation: options.transformation, + }, + this.fallbackKey, + ), + }, + } + : { + type: options.type, + ...createImageData(options, this.key), + }; const drawing = new Drawing(this.imageData, { floating: options.floating, docProperties: options.altText, @@ -53,29 +117,10 @@ export class ImageRun extends Run { public prepForXml(context: IContext): IXmlableObject | undefined { context.file.Media.addImage(this.key, this.imageData); + if (this.imageData.type === "svg") { + context.file.Media.addImage(this.fallbackKey, this.imageData.fallback); + } + return super.prepForXml(context); } - - private convertDataURIToBinary(dataURI: string): Uint8Array { - if (typeof atob === "function") { - // https://gist.github.com/borismus/1032746 - // https://github.com/mafintosh/base64-to-uint8array - const BASE64_MARKER = ";base64,"; - const base64Index = dataURI.indexOf(BASE64_MARKER); - - const base64IndexWithOffset = base64Index === -1 ? 0 : base64Index + BASE64_MARKER.length; - - return new Uint8Array( - atob(dataURI.substring(base64IndexWithOffset)) - .split("") - .map((c) => c.charCodeAt(0)), - ); - /* c8 ignore next 6 */ - } else { - // Not possible to test this branch in NodeJS - // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires - const b = require("buf" + "fer"); - return new b.Buffer(dataURI, "base64"); - } - } } diff --git a/src/file/paragraph/run/run-components/text.ts b/src/file/paragraph/run/run-components/text.ts index d6bc671dbb..db6c8d77b0 100644 --- a/src/file/paragraph/run/run-components/text.ts +++ b/src/file/paragraph/run/run-components/text.ts @@ -23,11 +23,9 @@ export class Text extends XmlComponent { if (typeof options === "string") { this.root.push(new TextAttributes({ space: SpaceType.PRESERVE })); this.root.push(options); - return this; } else { this.root.push(new TextAttributes({ space: options.space ?? SpaceType.DEFAULT })); this.root.push(options.text); - return this; } } } diff --git a/src/patcher/from-docx.spec.ts b/src/patcher/from-docx.spec.ts index 088d0b715d..1b939bad17 100644 --- a/src/patcher/from-docx.spec.ts +++ b/src/patcher/from-docx.spec.ts @@ -254,6 +254,7 @@ describe("from-docx", () => { link: "https://www.google.co.uk", }), new ImageRun({ + type: "png", data: Buffer.from(""), transformation: { width: 100, height: 100 }, }), @@ -266,6 +267,7 @@ describe("from-docx", () => { type: PatchType.PARAGRAPH, children: [ new ImageRun({ + type: "png", data: Buffer.from(""), transformation: { width: 100, height: 100 }, }), @@ -310,6 +312,7 @@ describe("from-docx", () => { type: PatchType.PARAGRAPH, children: [ new ImageRun({ + type: "png", data: Buffer.from(""), transformation: { width: 100, height: 100 }, }), @@ -354,6 +357,7 @@ describe("from-docx", () => { type: PatchType.PARAGRAPH, children: [ new ImageRun({ + type: "png", data: Buffer.from(""), transformation: { width: 100, height: 100 }, }), @@ -391,6 +395,7 @@ describe("from-docx", () => { type: PatchType.PARAGRAPH, children: [ new ImageRun({ + type: "png", data: Buffer.from(""), transformation: { width: 100, height: 100 }, }), diff --git a/src/patcher/from-docx.ts b/src/patcher/from-docx.ts index 11c4c9c4ce..1098bf05ea 100644 --- a/src/patcher/from-docx.ts +++ b/src/patcher/from-docx.ts @@ -215,7 +215,7 @@ export const patchDocument = async (data: InputDataType, options: PatchDocumentO zip.file(key, value); } - for (const { stream, fileName } of file.Media.Array) { + for (const { data: stream, fileName } of file.Media.Array) { zip.file(`word/media/${fileName}`, stream); } diff --git a/vite.config.ts b/vite.config.ts index d2fdcd127e..e5d830db50 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -62,10 +62,10 @@ export default defineConfig({ provider: "v8", reporter: ["text", "json", "html"], thresholds: { - statements: 99.98, - branches: 99.15, + statements: 100, + branches: 99.35, functions: 100, - lines: 99.98, + lines: 100, }, exclude: [ ...configDefaults.exclude,