poptimal/src/components/download.js
2025-03-26 18:33:36 +11:00

85 lines
2.2 KiB
JavaScript

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) {
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: 1200,
}
};
const resvgJS = new resvg.Resvg(svgstr, opts)
const pngData = resvgJS.render(svgstr, opts)
const pngBuffer = pngData.asPng();
return new Blob([pngBuffer], { type: 'image/png' });
}