svg2pdf.js
svg2pdf.js copied to clipboard
Gradients won't render as gradients in the PDF
Hi,
SVGs won't render in the PDF with simple gradients. I added a method to fill the svg node to the first stop color and a test (test19.html) to the test folder. Is there a way to make the svg2pdf.js render this simple gradient?
Best regards, Henrik Skar
@henrikskar The bug is probably not related to the gradientTransform attribute as suggested in the pull request.
Two changes makes the gradient work in PDF:
- Add
x1,x2,y1andy2arguments. These are usually added, but perhaps svgtopdf should have the same defaults as SVG. - Set the
stop-opacityto 1 on both stops. Seems liks opacity is handled incorrectly if supported at all.
Minimal, working code with current svg2pdf, modified from test19:
<linearGradient id="MyGradient" x1="0" x2="0" y1="0" y2="1">
<stop offset="0" stop-color="#7cb5ec" stop-opacity="1"/>
<stop offset="1" stop-color="#ffffff" stop-opacity="1"/>
</linearGradient>
Regarding opacity: AFAIK PDF does not support different opacities per gradient stop. A global opacity can be applied to the whole gradient, though. The only workaround is to precalculate the fill as a bitmap, which is costly both in terms of processing, resulting pdf size and quality. So we decided to not support opacity for gradients for now. Pull requests are welcome, of course.
Thanks for your clarification @yGuy, that makes sense. It seems to leave us with some options:
- Apply a global opacity to the whole gradient, like svg2pdf.js currently does.
- Precalculate the fill as a bitmap. If I remember correctly, Batik uses this approach. In capable browsers, we could probably create an SVG with the filled shape only, draw it on a canvas, serialize it to PNG and add it to the PDF.
- Add a gradient only if all stops' opacity is 1. It seems like this is the approach PhantomJS is using when saving to PDF.
- If there's a stop with opacity 1, use that as the solid fill. Which would probably work best for our purpose.
If we want to implement this, could we add an option for it? Something in the lines of:
// render the svg element
svg2pdf(svgElement, pdf, {
xOffset: 0,
yOffset: 0,
scale: 1,
gradientOpacity: 'first-solid' // or 'bitmap', 'average'
});
I like the proposed API - we could discuss whether adding another level in between like this is more future-proof, but I don't really have a strong opinion here, now:
// render the svg element
svg2pdf(svgElement, pdf, {
xOffset: 0,
yOffset: 0,
scale: 1,
emulation: {
gradientOpacity: 'first-solid' // or 'bitmap', 'average'
}
});
... in general I like the idea of making this configurable that way instead of hard-coding a single logic.
Closing this, feel free to reopen ;)