SVG to PNG

Hi all,

I want to download my SVG plots as PNG’s. I know the in-built ‘save as PNG’ works, but I’m working with the runtime outside of observable for some tests.

The ‘rasterize’ function in the saving SVG notebook fails, but the ‘serialize’ function successfully allows me to download SVG’s.

Can somebody please help me hack the rasterize function to make it work again?

Thanks!
M

1 Like

Hi @Euphrasiologist, do you have a public example that you can share? And what errors do you see in your dev tools console (likely something about a tainted canvas)?

I streamed how to send dashboards over WhatsApp which included converting an SVG to a PNG (and serving it on a public URL!) so there is a working example in there.

The problem exists in Mike’s Saving SVG notebook pointed to by the OP. If you hit the “Save as PNG” button in that notebook, a PNG file downloads but is empty and the console gives the following error:

document.requestStorageAccess() may not be called in a sandboxed iframe without allow-storage-access-by-user-activation in its sandbox attribute.

There’s also a comment on that cell about this issue from Dave Kirkby that’s been resolved but not responded to.

Strangley, I use it in a number of my own notebooks without problem - here, for example. Now that I check, I get the same error message but the image downloads as expected.

Likely because your own notebooks run under a different subdomain?

Thanks, was typing to clarify but I have a different error. If I look on the developer tools (chrome in this case) on the network pane, I get this GET request (e.g.):

blob:https://euphrasiologist.static.observableusercontent.com/18998a56-1737-47ad-912b-a17286765ac9

But it’s not a PNG, it’s an SVG, and it doesn’t open a download…

That blob is just an intermediary object URL, to be used as the src for an Image which then gets drawn onto a canvas.

Can you please share a link to the notebook in which you’re trying to add the download?

There’s a test plot half way down this half-baked notebook…

test is not an <svg> but a <figure> because you enabled Plot’s legend option. You will have to call

DOM.download(() => rasterize(test.querySelector('svg')), undefined, "Save as PNG")

instead.

If you’re unsure, add a cell with the content

test.localName

to inspect the outermost tag name.

2 Likes

I really would like to be able to download the figure and all the legend data too… It’s possible on the in-built ‘Download PNG’ button. Is that proprietary source code?

How so? The PNG does not include the legend in my tests.

… but to answer your actual question: You would have to wrap the HTML in a <foreignObject> and inject it into the SVG. And many programs will likely not be able to handle this construct.

Your best bet is probably to generate the legend as an SVG element, e.g. by converting the HTML legend via a library like dom-to-svg (which I haven’t used myself yet).

Note also that you can put SVGs in your SVGs because <svg> elements can be nested. So you may be able to combine your custom SVG legend and the plot SVG into a new SVG without having to modify the plot.

I also faced a similar issue recently. One part of the problem solved by using nested SVG like @mootari pointed out. I had to generate SVG based legend. The colour legend generated by Plot is not SVG. The notebook linked below has some code to generate SVG colour legend.

Another problem I faced was, D3 charts which get updated after initial render like wrapping labels or force directed changes. For this, I didn’t find a good solution. But for my use case, a user action-based export worked.

I needed to wrap the image of the chart with some title, attribution text, and logo. So, when the use presses ‘export as …’ I clone the current SVG node (usually finished rendering) create another nested SVG with chart, title, and logo and save as SVG or PNG. I made use of DOM.download.

Here is an unlisted fork of the work I did: Export SVG as Branded Card Component / Saneef H. Ansari | Observable. Hope it will give you some ideas.

2 Likes