Skip to content

Commit

Permalink
fix(api): Fix css encoding in exported svg
Browse files Browse the repository at this point in the history
Encode all style elements before including them in export svg, to avoid XML special character problems.
Added a test for export with "weird" css.

Fix #843
Close #844
  • Loading branch information
batram authored and netil committed Apr 16, 2019
1 parent 5ee8a08 commit 321971e
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 5 deletions.
14 changes: 14 additions & 0 deletions spec/api/api.export-spec.js
Expand Up @@ -101,4 +101,18 @@ describe("API export", () => {
});
}, 500);
});

it("should export valid svg even with weird css", () => {
document.body.innerHTML += `<style>@font-face{src:url("#&<>'\0");}</style>`;

const dataURL = chart.export();

// test generated svg
const svg = atob(dataURL.split("base64,")[1]);
const oParser = new DOMParser();
const doc = oParser.parseFromString(svg, "image/svg+xml");

// check that it does not start with error message
expect(doc.documentElement.nodeName === "svg").to.be.true;
});
});
18 changes: 13 additions & 5 deletions src/api/api.export.js
Expand Up @@ -25,20 +25,28 @@ const b64EncodeUnicode = str => btoa(
* @private
*/
const nodeToSvgDataUrl = (node, size) => {
const serializer = new XMLSerializer();
const clone = node.cloneNode(true);
const styleSheets = toArray(document.styleSheets);
const cssRules = getCssRules(styleSheets);
const cssText = cssRules.filter(r => r.cssText).map(r => r.cssText);
const cssText = getCssRules(toArray(document.styleSheets))
.filter(r => r.cssText)
.map(r => r.cssText);

clone.setAttribute("xmlns", d3Namespaces.xhtml);

const nodeXml = new XMLSerializer().serializeToString(clone);
const nodeXml = serializer.serializeToString(clone);

// escape css for XML
const style = document.createElement("style");

style.appendChild(document.createTextNode(cssText.join("\n")));

const styleXml = serializer.serializeToString(style);

// foreignObject not supported in IE11 and below
// https://msdn.microsoft.com/en-us/library/hh834675(v=vs.85).aspx
const dataStr = `<svg xmlns="${d3Namespaces.svg}" width="${size.width}" height="${size.height}">
<foreignObject width="100%" height="100%">
<style>${cssText.join("\n")}</style>
${styleXml}
${nodeXml.replace(/(url\()[^#]+/g, "$1")}
</foreignObject></svg>`
.replace("/\n/g", "%0A");
Expand Down

0 comments on commit 321971e

Please sign in to comment.