Stuart
Stuart

Reputation: 9858

How can I ensure text is valid for an SVG?

When I include a non-breaking space (character encoded as \u00A0) in an SVG text element, it works fine on a HTML page, but the resulting SVG contains an   character and is therefore not valid as an SVG file. For example, if I save the SVG to a file (using a function like this) and open that file in Chrome, it reports an error "Entity 'nbsp' not defined" and does not display the text.

const NS = { SVG: "http://www.w3.org/2000/svg" };
const svg = document.createElementNS(NS.SVG, 'svg');
const text = document.createElementNS(NS.SVG, "text");
text.setAttribute("y", 20);
const s = "Non-breaking> <space"; // the space here is a non-breaking one
text.appendChild(document.createTextNode(s));
svg.appendChild(text);
document.body.appendChild(svg);
console.log(svg.outerHTML);

I can easily replace the problematic character with s.replace("\u00A0", " "). But is there a more general way of sanitizing text going into an SVG to ensure it is valid and replacing the non-valid characters?

Upvotes: 0

Views: 620

Answers (1)

vanowm
vanowm

Reputation: 10221

Since svg recognizes only 5 html entities: &amp;, &quot;, &apos;, &lt;, and &gt;, we can filter out the rest:

const NS = { SVG: "http://www.w3.org/2000/svg" };
const svg = document.createElementNS(NS.SVG, 'svg');
const text = document.createElementNS(NS.SVG, "text");
text.setAttribute("y", 20);
const s = "Non-breaking> <space & other\r\u0009characters"; // the space here is a non-breaking one

text.appendChild(document.createTextNode(s));
svg.appendChild(text);
document.body.appendChild(svg);
console.log(svg.outerHTML);

saveSvg(svg, "test")

function saveSvg(svgEl, name) {
    svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    const dummy = document.createElement("div");
    /* find all html entities and replace them with real value */
    var svgData = svgEl.outerHTML.replace(/(&(?!(amp|gt|lt|quot|apos))[^;]+;)/g, a =>
    {
      dummy.innerHTML = a;
      return dummy.textContent;
    });
    var preface = '<?xml version="1.0" standalone="no"?>\r\n';
    var svgBlob = new Blob([preface, svgData], {type:"image/svg+xml;charset=utf-8"});
    var svgUrl = URL.createObjectURL(svgBlob);
    var downloadLink = document.createElement("a");
    downloadLink.href = svgUrl;
    downloadLink.download = name;
    downloadLink.textContent = "download";
    document.body.appendChild(downloadLink);
    downloadLink.click();
//    document.body.removeChild(downloadLink);
}

Upvotes: 2

Related Questions