# Why a security error when downloading PNG?

**URL:** <https://talk.observablehq.com/t/why-a-security-error-when-downloading-png/5208>\
**Category:** Help\
**Created:** [June 16, 2021, 7:28am UTC](https://talk.observablehq.com/t/why-a-security-error-when-downloading-png/5208 "2021-06-16T07:28:23Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![bennotkin](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/bennotkin/32/5128_2.png) [@bennotkin](https://talk.observablehq.com/u/bennotkin)\
**Post date:** [June 16, 2021, 7:28am UTC](https://talk.observablehq.com/t/why-a-security-error-when-downloading-png/5208/1 "2021-06-16T07:28:23Z")

</div>

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](https://observablehq.com/@mbostock/saving-svg) 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

```auto
    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.

```auto
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()
}  

```

---

<div class="post-metadata">

**Author:** ![Cobus](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/cobus/32/9670_2.png) [@Cobus](https://talk.observablehq.com/u/Cobus)\
**Post date:** [June 16, 2021, 1:14pm UTC](https://talk.observablehq.com/t/why-a-security-error-when-downloading-png/5208/2 "2021-06-16T13:14:40Z")

</div>

We will look into it. It would be great if you could link share a notebook (aka unlisted publish) with some test data so we could look at why this particular download is failing.

---

<div class="post-metadata">

**Author:** ![bennotkin](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/bennotkin/32/5128_2.png) [@bennotkin](https://talk.observablehq.com/u/bennotkin)\
**Post date:** [June 16, 2021, 5:41pm UTC](https://talk.observablehq.com/t/why-a-security-error-when-downloading-png/5208/3 "2021-06-16T17:41:47Z")

</div>

Thanks, @Cobus. In classic form, I found the problem when attempting to reduce my code to a minimal example: my SVGs included foreignObjects. When I removed them, the PNG would successfully download.

The question, then, becomes how to have multi-line text in a chart. I was using foreignObjects but that seems to break the download function.

Still sharing my [notebook](https://observablehq.com/d/b95789062bc4793c), even if the original problem is identified.

---

<div class="post-metadata">

**Author:** ![Cobus](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/cobus/32/9670_2.png) [@Cobus](https://talk.observablehq.com/u/Cobus)\
**Post date:** [June 16, 2021, 11:32pm UTC](https://talk.observablehq.com/t/why-a-security-error-when-downloading-png/5208/4 "2021-06-16T23:32:49Z")

</div>

Good to see you figured out the problem! Text formatting in svg is so painful! I don’t have a good answer for you, unfortunately. I wish you were able to embed html into an svg…  
If anyone else here has some good tips, I would love to learn about how to do that.

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [June 22, 2021, 4:32pm UTC](https://talk.observablehq.com/t/why-a-security-error-when-downloading-png/5208/5 "2021-06-22T16:32:53Z")

</div>

Note that you’re using `<text>` inside an HTML `foreignObject`. `<text>` is an SVG element, and while the browser will still display its contents, you could just as well write `<foo>` (although you shouldn’t 🙂).
