diff --git a/CHANGELOG.md b/CHANGELOG.md index 9705f1a..bf2299a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG.md +## v1.1.0 + +Added SVG and PNG downloads + ## v1.0.2 * Fixed bug which was stopping slanted grids diff --git a/src/components/download.js b/src/components/download.js new file mode 100644 index 0000000..9089c03 --- /dev/null +++ b/src/components/download.js @@ -0,0 +1,85 @@ + +import * as resvg from 'npm:@resvg/resvg-wasm'; + + +const xmlns = "http://www.w3.org/2000/xmlns/"; +const xlinkns = "http://www.w3.org/1999/xlink"; +const svgns = "http://www.w3.org/2000/svg"; + +// adapted from the DOM.download method as per this issue: + +// https://github.com/observablehq/framework/issues/906 + + +export function download(value, name = "untitled", label = "Save") { + const a = document.createElement("a"); + const b = a.appendChild(document.createElement("button")); + b.textContent = label; + a.download = name; + + async function reset() { + await new Promise(requestAnimationFrame); + URL.revokeObjectURL(a.href); + a.removeAttribute("href"); + b.textContent = label; + b.disabled = false; + } + + a.onclick = async event => { + console.log("clicked download"); + b.disabled = true; + if (a.href) { + console.log(`already saved: ${a.href}`); + return reset(); // Already saved. + } + b.textContent = "Saving…"; + try { + console.log("awaiting value function"); + const object = await (typeof value === "function" ? value() : value); + console.log(object); + b.textContent = "Download"; + a.href = URL.createObjectURL(object); // eslint-disable-line require-atomic-updates + console.log(`url = ${a.href}`); + } catch (ignore) { + b.textContent = label; + } + if (event.eventPhase) return reset(); // Already downloaded. + b.disabled = false; + }; + + return a; +} + + +export function svg_to_string(svg) { + svg = svg.cloneNode(true); + svg.setAttributeNS(xmlns, "xmlns", svgns); + svg.setAttributeNS(xmlns, "xmlns:xlink", xlinkns); + const serializer = new window.XMLSerializer; + return serializer.serializeToString(svg); +} + + +export function download_as_svg(svg) { + console.log("HEY download_as_svg"); + const str = svg_to_string(svg); + return new Blob([str], {type: "image/svg+xml"}) +} + +export async function download_as_png (svg) { + // The Wasm must be initialized first + + const svgstr = svg_to_string(svg); + + const opts = { + fitTo: { + mode: 'width', // If you need to change the size + value: 400, + } + }; + const resvgJS = new resvg.Resvg(svgstr, opts) + const pngData = resvgJS.render(svgstr, opts) + const pngBuffer = pngData.asPng(); + return new Blob([pngBuffer], { type: 'image/png' }); +} + diff --git a/src/index.md b/src/index.md index bf32c35..d63cead 100644 --- a/src/index.md +++ b/src/index.md @@ -2,9 +2,11 @@ toc: false --- + +
v1.0.2 | by mike lynch | @mikelynch@aus.social | source
+v1.1.0 | by mike lynch | @mikelynch@aus.social | source