I am currently working on developing a browser-based SVG rasterizer. The unique aspect of this project is that the SVG files may contain JavaScript code that directly impacts the output, such as randomly changing element colors, and utilizes external libraries like chroma.js.
The workflow I am aiming to establish includes:
- loading the SVG file
- loading linked libraries within the SVG file
- executing the included JavaScript code within script tags
- displaying on a canvas
- saving the canvas as a PNG image
This process is crucial because simply appending the SVG to an HTML element does not execute the JavaScript contained within the SVG itself.
While most components are functional, the challenge lies in loading external libraries (as outlined in step 2). The snippet responsible for this task looks like this:
$.get('/some_svg_file.svg', function(d) {
// functionality will be updated for file drag/drop feature
var getLibs = Array.from(d.querySelectorAll('script'))
.filter(p => p.getAttribute('xlink:href'))
.map(p => {
return axios.get(p.getAttribute('xlink:href'))
})
// ensure embedded JS runs after library downloads
Promise.all(getLibs).then((values) => {
values.forEach(d => {
try {
eval(d.data);
} catch (e) {
console.log(e)
}
});
Array.from(d.querySelectorAll('script'))
.forEach(p => {
if (p.textContent.length) {
try {
eval(p.textContent)
} catch (e) {
console.log(e)
}
}
})
// include code for writing to canvas here
})
})
When attempting to load chroma.js from an external source, all scripts within the SVG fail with the error
ReferenceError: chroma is not defined
.
An alternative approach involved using the following script tag method:
$.get('/foo_with_js.svg', function(d) {
var getLibs = Array.from(d.querySelectorAll('script'))
.filter(p => p.getAttribute('xlink:href'))
.map(p => {
var s = document.createElement('script');
s.src = p.getAttribute('xlink:href');
s.type = "text/javascript";
s.async = false;
document.querySelector('head').appendChild(s);
})
// complete script loading and save to canvas
})
This method also resulted in a similar failure, despite chroma.js being correctly placed in the head section.
Hence, my question remains - how can I ensure seamless loading of SVG, embedding it into HTML, and executing internal scripts without manually pre-linking all potential scripts in the HTML?
And if you're wondering why not opt for another conversion technique, the simple answer is "due to inconsistent support for SVG filters."