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.