SVG as SVG's image tag not working while using with sharp

Viewed 3140

I'm using sharp to convert the file but sharp gives me an empty png file.

I want to use a base64 formatted SVG image as <image> tag's source and want to convert it to a png file.

Here's an example to show what I'm trying to do;

const sharp = require('sharp');
sharp(Buffer.from(<look below js file to see the SVG>))
    .toFormat('png')
    .toFile('output.png')

And here's a JS to show what I tried to do. Basically, I want to convert this SVG to a png file using sharp but sharp gives me an empty png.

1 Answers

It looks like sharp does not handle external images well.

You can try to "flatten" SVG before passing it to sharp.

Quick and dirty implementation

This is very error prone. Ideally you would use something like Cheerio for parsing and modifying SVG input.

It also preserves only x, y, width, height and opacity attributes. Others attributes would require further changes.

function flattenSVG (svg) {
  const images = svg.match(/<image [^>]+>/g);
  if (!images || images.length < 1) {
    return svg;
  }

  var result = svg;
  images.forEach(image => {
    const [,data] = image.match(/ xlink:href="data:image\/svg\+xml;base64,([^"]+)"/) || [];
    if (!data) {
      return;
    }

    const innerSVG = Buffer.from(data, 'base64').toString();
    const [,width] = image.match(/ width="([^"]+)"/) || [];
    const [,height] = image.match(/ height="([^"]+)"/) || [];
    const [,opacity] = image.match(/ opacity="([^"]+)"/) || [];
    const [,x] = image.match(/ x="([^"]+)"/) || [];
    const [,y] = image.match(/ y="([^"]+)"/) || [];
    const [header] = innerSVG && innerSVG.match(/<svg[^>]+>/) || [];
    const fixedHeader = header
      .replace(/ (x|y|width|height)="([^"]+)"/g, '')
      .replace('<svg', `<svg x="${x}" y="${y}" width="${width}" height="${height}" opacity="${opacity || 1.0}"`);
    const replacement = innerSVG && innerSVG.replace(header, fixedHeader);
    result = result.replace(image, replacement);
  });

  return result;
}

So, in your code, you would use something like:

sharp(Buffer.from(flattenSVG(testSVG)))
      .toFormat('png')
      .toFile('output.png')

You can check working code (a bit different for test purposes, as it uses buffer instead of file output) at Glitch.

Related