sharp icon indicating copy to clipboard operation
sharp copied to clipboard

SVG conversion to PNG ignores <style> variables

Open rsodre opened this issue 1 year ago • 2 comments

Possible bug

SVG conversion to PNG ignores

This issue limits compatibility with several of the assets I am using, and with 100% that I generate.

Maybe... this could be because of libvips, which uses librsvg. Acccording to this post, they still do not support style variables: https://gitlab.gnome.org/GNOME/librsvg/-/issues/459

Even if the issue is because of a dependent package, it is important to track it until it is supported and be transparent to developers that use style variables.

Are you using the latest version of sharp?

  • [x] I am using the latest version of sharp as reported by npm view sharp dist-tags.latest.

What is the output of running npx envinfo --binaries --system --npmPackages=sharp --npmGlobalPackages=sharp?

  System:
    OS: macOS 11.7
    CPU: (4) x64 Intel(R) Core(TM) i5-7500 CPU @ 3.40GHz
    Memory: 6.26 GB / 32.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 16.14.0 - /usr/local/bin/node
    npm: 8.15.1 - /usr/local/bin/npm
  npmPackages:
    sharp: ^0.31.2 => 0.31.2 

What are the steps to reproduce?

  • Convert the attached sample SVG to PNG

What is the expected behaviour?

  • Output should have a blue background, as defined in the --Bg variable, but the background is black.

Please provide a minimal, standalone code sample, without other dependencies, that demonstrates this problem

This is a nextjs api endpoint:

const sharp = require('sharp');
const sharpOptions = {
	compressionLevel: 0,
	quality: 100,
}

export default async function handler(request, response) {
	const svg = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="600" height="600" viewBox="0 0 20 20"><defs><style>:root{--Bg:#0000FF;}.Bg{fill:var(--Bg);}.Rect{fill:#00ff00;}</style></defs><g><rect class="Bg" x="0" y="0" width="20" height="20"/><rect class="Rect" x="5" y="5" width="10" height="10"/></g></svg>';
			
	const svgBuffer = Buffer.from(svg);
	const png = sharp(svgBuffer).png(sharpOptions);
	const pngData = await png.toBuffer();

	response.statusCode = 200;
	response.setHeader('Content-Type', 'image/png');
	response.setHeader('Cache-Control', 'public, max-age=0, must-revalidate');
	response.end(pngData);
}

Please provide sample image(s) that help explain this problem

sample output_BAD

rsodre avatar Dec 21 '22 16:12 rsodre

As a fallback, I'm converting all variables to their actual value. My simple assets use color codes only, so other cases may need a more refined approach.

// match all: --varName:#ff00ff;
const regexp = /(--)[a-zA-Z]+:#[a-fA-F0-9]+;/g;
const vars = [..._svg.matchAll(regexp)];
for (const v of vars) {
	const parts = v[0].split(':');
	const name = parts[0]; // --varName
	const value = parts[1].split(';')[0]; // #ff00ff
	svg = svg.replaceAll(`var(${name})`, value);
}

rsodre avatar Dec 21 '22 16:12 rsodre

Yes, please subscribe to https://gitlab.gnome.org/GNOME/librsvg/-/issues/459 for updates.

lovell avatar Dec 21 '22 16:12 lovell