Skip to content

Commit

Permalink
Add SVG image suport (#2487)
Browse files Browse the repository at this point in the history
* Add SVG blip extentions

* SVG Image feature now works

* Add and simplify tests

* Fix falsey issue with file

Write tests
100% Coverage
  • Loading branch information
dolanmiu authored Dec 31, 2023
1 parent 7570fc2 commit 24c159d
Show file tree
Hide file tree
Showing 27 changed files with 616 additions and 119 deletions.
23 changes: 23 additions & 0 deletions demo/5-images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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"),
},
}),
],
}),
],
},
],
Expand Down
Binary file added demo/images/linux-png.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
183 changes: 183 additions & 0 deletions demo/images/linux-svg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/_coverpage.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
8 changes: 8 additions & 0 deletions docs/usage/images.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -59,6 +61,7 @@ const doc = new Document({
new Paragraph({
children: [
new ImageRun({
type: [IMAGE_TYPE],
data: [IMAGE_BUFFER],
transformation: {
width: [IMAGE_SIZE],
Expand Down Expand Up @@ -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,
Expand All @@ -115,6 +119,7 @@ const image = new ImageRun({

```ts
const image = new ImageRun({
type: 'png',
data: buffer,
transformation: {
width: 903,
Expand Down Expand Up @@ -180,6 +185,7 @@ For example:

```ts
const image = new ImageRun({
type: 'gif',
data: fs.readFileSync("./demo/images/pizza.gif"),
transformation: {
width: 200,
Expand Down Expand Up @@ -228,6 +234,7 @@ For example:

```ts
const image = new ImageRun({
type: 'gif',
data: fs.readFileSync("./demo/images/pizza.gif"),
transformation: {
width: 200,
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion docs/usage/patcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 } }),
],
}),
],
Expand Down
3 changes: 2 additions & 1 deletion src/export/packer/image-replacer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
46 changes: 45 additions & 1 deletion src/export/packer/next-compiler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
},
}),
],
}),
],
Expand All @@ -165,7 +178,8 @@ describe("Compiler", () => {

vi.spyOn(compiler["imageReplacer"], "getMediaData").mockReturnValue([
{
stream: Buffer.from(""),
type: "png",
data: Buffer.from(""),
fileName: "test",
transformation: {
pixels: {
Expand All @@ -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);
Expand Down
9 changes: 7 additions & 2 deletions src/export/packer/next-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
31 changes: 16 additions & 15 deletions src/file/content-types/content-types.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,76 +25,77 @@ 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",
Extension: "odttf",
},
},
});
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",
PartName: "/word/document.xml",
},
},
});
expect(tree["Types"][10]).to.deep.equal({
expect(tree["Types"][11]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
PartName: "/word/styles.xml",
},
},
});
expect(tree["Types"][11]).to.deep.equal({
expect(tree["Types"][12]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-package.core-properties+xml",
PartName: "/docProps/core.xml",
},
},
});
expect(tree["Types"][12]).to.deep.equal({
expect(tree["Types"][13]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.custom-properties+xml",
PartName: "/docProps/custom.xml",
},
},
});
expect(tree["Types"][13]).to.deep.equal({
expect(tree["Types"][14]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml",
PartName: "/docProps/app.xml",
},
},
});
expect(tree["Types"][14]).to.deep.equal({
expect(tree["Types"][15]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
PartName: "/word/numbering.xml",
},
},
});
expect(tree["Types"][15]).to.deep.equal({
expect(tree["Types"][16]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml",
PartName: "/word/footnotes.xml",
},
},
});
expect(tree["Types"][16]).to.deep.equal({
expect(tree["Types"][17]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down
1 change: 1 addition & 0 deletions src/file/content-types/content-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down
Loading

0 comments on commit 24c159d

Please sign in to comment.