I’m unable to download the PNG of my SVG, though “Download as PNG” is visible after clicking the three dots.
In the console, I’m told the action is insecure:
Unhandled Promise Rejection: SecurityError: The operation is insecure.
(anonymous function) — worker-7ff35fe9.js:2754
asyncFunctionResume
(anonymous function)
promiseReactionJobWithoutPromise
promiseReactionJob
When I got to line 2754 of worker-7ff35fe9.js
, I’m told
Unhandled Promise Rejection: SecurityError: The operation is insecure.
I can download SVGs without a problem. I thought the issue may be that the notebook isn’t published, but I imported someone else’s chart and had no problem downloading the PNG.
I also attempted to use Mike Bostock’s precursor to the built-in PNG download, but again ran into security problems.
This time, the console reads
SecurityError: The operation is insecure.
toBlob — saving-svg.js:67
(anonymous function) — saving-svg.js:67
Line 67 is
context.canvas.toBlob(resolve);
which is flagged with “SecurityError: The operation is insecure.”
I’m using Safari but have also tested Chrome.
Is there typically something you need to do in order to make PNGs downloadable?
My code is below. Tomorrow I can reduce it to something minimal and provide fake data for it.
function heatmap(data, country) { // forEach method (non-standard d3)
// Set the svg size; this can be made responsive later (if even wanted)
const width = 600;
const height = 600;
// Row height (legend has a half height for now)
let rowHeight = height/6
let rowGap = 6;
let margin = {"top": rowHeight/3, "bottom": rowHeight/3}
// Initiate svg
const svg = d3.select(DOM.svg(width, height));
svg.append("text")
.text(country + ": Crisis Preparedness Assessment")
.attr("class", "chartTitle")
.attr("y", margin.top - rowGap/2)
// Color Ramp
const colorRamp = d3.scaleLinear()
.domain([0, 1, 2, 4])
.range(["#D22027", "#FDB52A", "#589643", "#027546"])
data.forEach( function (comp, i) {
svg.append("line")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", margin.top + i * rowHeight)
.attr("y2", margin.top + i * rowHeight)
.attr("stroke-width", 0.75)
.attr("stroke", "#999999")
svg.append("foreignObject")
.attr("width", width/3 - rowHeight/5)
.attr("height", rowHeight)
.attr("x", rowHeight/5)
.attr("y", margin.top + rowHeight * (i))
.append("xhtml:body")
.html("<div class='components' style='height:" + rowHeight + "px'><text>" + comp.component + "</text></div>")
svg.append("rect")
.attr("y", margin.top + rowHeight * i + rowGap/2)
.attr("height", rowHeight - rowGap)
.attr("width", rowHeight/5)
.attr("fill", colorRamp(Math.floor(comp.maturity)))
let littleRow = (rowHeight - rowGap)/comp.dimensions.length
comp.dimensions.forEach( (dim, j) => {
svg.append("foreignObject")
.attr("width", width/3)
.attr("height", littleRow)
.attr("y", margin.top + (rowHeight * i) + rowGap/2 + littleRow * j)
.attr("x", width / 3)
.append("xhtml:body")
.html("<div class='dimensions' style='height:" + littleRow + "px'><text>" + dim.dimension + "</text></div>")
svg.append("rect")
.attr("y", margin.top + (rowHeight * i) + rowGap/2 + littleRow * j)
.attr("x", 2 * width / 3)
.attr("width", width / 3)
.attr("height", littleRow)
.attr("fill", colorRamp(Math.floor(dim.maturity)))
svg.append("text")
.attr("y", margin.top + (rowHeight * i) + rowGap/2 + littleRow * (j + 0.5))
.attr("x", 2 * width / 3 + width/6)
.attr("width", width / 3)
.attr("height", littleRow)
.text(Math.round(10 * dim.maturity) / 10)
.attr("text-anchor", "middle")
.attr("alignment-baseline", "central")
.attr("fill", "white")
.attr("font-weight", "bold")
})
})
svg.append("line")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", margin.top + 5 * rowHeight)
.attr("y2", margin.top + 5 * rowHeight)
.attr("stroke-width", 0.75)
.attr("stroke", "#999999")
svg.append("foreignObject")
.attr("width", width/3 - rowHeight/5)
.attr("height", rowHeight)
.attr("y", margin.top + rowHeight * (data.length + 1/3))
.attr("x", rowHeight/5)
.append("xhtml:body")
.html("<div class='components' style='height:" + rowHeight/3 + "px'><text>Maturity Level</text></div>")
for (let i = 0; i < 5; ++i) {
svg.append("rect")
.attr("x", width / 3 + (width / 3 * 2 / 5) * i)
.attr("y", margin.top + rowHeight * 16/3)
.attr("width", (width / 3 * 2 / 5))
.attr("height", rowHeight / 3)
.attr("fill", colorRamp(i))
svg.append("text")
.text(["0 Unmet", "1 Nascent", "2 Basic", "3 Good", "4 Advanced"][i])
.attr("text-anchor", "middle")
.attr("alignment-baseline", "central")
.attr("x", width / 3 + (width / 3 * 2 / 5) * (i + .5))
.attr("y", margin.top + rowHeight * (16/3) + rowHeight / 6)
.attr("height", rowHeight / 3)
.attr("fill", "white")
.attr("font-weight", "bold")
}
svg.append("style")
.text(css)
return svg.node()
}