Converter SVG para PNG com imagens aplicadas como plano de fundo para elementos svg

Eu tenho um arquivo SVG externo que contém algumas tags de imagem incorporadas no padrão. Sempre que eu converter esse SVG em PNG usando toDataURL() , as imagens PNG geradas não contêm a imagem que apliquei como padrão a alguns caminhos SVG. Existe alguma maneira de resolver este problema?

Sim, existem: adicione o svg ao seu documento e codifique todas as imagens incluídas para dataURIs.

Eu estou escrevendo um script que faz isso e também algumas outras coisas como include folhas de estilo externas e alguma outra correção de onde toDataURL falhará (por exemplo, elementos externos referenciados através do atributo xlink:href ou ).

Aqui está a function que escrevi para analisar o conteúdo das imagens:

 function parseImages(){ var xlinkNS = "http://www.w3.org/1999/xlink"; var total, encoded; // convert an external bitmap image to a dataURL var toDataURL = function (image) { var img = new Image(); // CORS workaround, this won't work in IE<11 // If you are sure you don't need it, remove the next line and the double onerror handler // First try with crossorigin set, it should fire an error if not needed img.crossOrigin = 'Anonymous'; img.onload = function () { // we should now be able to draw it without tainting the canvas var canvas = document.createElement('canvas'); canvas.width = this.width; canvas.height = this.height; // draw the loaded image canvas.getContext('2d').drawImage(this, 0, 0); // set our 's href attribute to the dataURL of our canvas image.setAttributeNS(xlinkNS, 'href', canvas.toDataURL()); // that was the last one if (++encoded === total) exportDoc(); }; // No CORS set in the response img.onerror = function () { // save the src var oldSrc = this.src; // there is an other problem this.onerror = function () { console.warn('failed to load an image at : ', this.src); if (--total === encoded && encoded > 0) exportDoc(); }; // remove the crossorigin attribute this.removeAttribute('crossorigin'); // retry this.src = ''; this.src = oldSrc; }; // load our external image into our img img.src = image.getAttributeNS(xlinkNS, 'href'); }; // get an external svg doc to data String var parseFromUrl = function(url, element){ var xhr = new XMLHttpRequest(); xhr.onload = function(){ if(this.status === 200){ var response = this.responseText || this.response; var dataUrl = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(response); element.setAttributeNS(xlinkNS, 'href', dataUrl); if(++encoded === total) exportDoc(); } // request failed with xhr, try as an  else{ toDataURL(element); } }; xhr.onerror = function(){toDataURL(element);}; xhr.open('GET', url); xhr.send(); }; var images = svg.querySelectorAll('image'); total = images.length; encoded = 0; // loop through all our  elements for (var i = 0; i < images.length; i++) { var href = images[i].getAttributeNS(xlinkNS, 'href'); // check if the image is external if (href.indexOf('data:image') < 0){ // if it points to another svg element if(href.indexOf('.svg') > 0){ parseFromUrl(href, images[i]); } else // a pixel image toDataURL(images[i]); } // else increment our counter else if (++encoded === total) exportDoc(); } // if there were no  element if (total === 0) exportDoc(); } 

Aqui o svgDoc é chamado svg ,
e a function exportDoc() poderia ser escrita apenas como:

 var exportDoc = function() { // check if our svgNode has width and height properties set to absolute values // otherwise, canvas won't be able to draw it var bbox = svg.getBoundingClientRect(); if (svg.width.baseVal.unitType !== 1) svg.setAttribute('width', bbox.width); if (svg.height.baseVal.unitType !== 1) svg.setAttribute('height', bbox.height); // serialize our node var svgData = (new XMLSerializer()).serializeToString(svg); // remember to encode special chars var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData); var svgImg = new Image(); svgImg.onload = function () { var canvas = document.createElement('canvas'); // IE11 doesn't set a width on svg images... canvas.width = this.width || bbox.width; canvas.height = this.height || bbox.height; canvas.getContext('2d').drawImage(svgImg, 0, 0, canvas.width, canvas.height); doSomethingWith(canvas) }; svgImg.src = svgURL; }; 

Mas, mais uma vez, você terá que append seu svg no documento primeiro (através do xhr ou em um elemento ou , e você terá que ter certeza de que todos os seus resources são compatíveis com CORS (ou do mesmo domínio). ) para obter estes processados.

 var svg = document.querySelector('svg'); var doSomethingWith = function(canvas) { document.body.appendChild(canvas) }; function parseImages() { var xlinkNS = "http://www.w3.org/1999/xlink"; var total, encoded; // convert an external bitmap image to a dataURL var toDataURL = function(image) { var img = new Image(); // CORS workaround, this won't work in IE<11 // If you are sure you don't need it, remove the next line and the double onerror handler // First try with crossorigin set, it should fire an error if not needed img.crossOrigin = 'anonymous'; img.onload = function() { // we should now be able to draw it without tainting the canvas var canvas = document.createElement('canvas'); canvas.width = this.width; canvas.height = this.height; // draw the loaded image canvas.getContext('2d').drawImage(this, 0, 0); // set our 's href attribute to the dataURL of our canvas image.setAttributeNS(xlinkNS, 'href', canvas.toDataURL()); // that was the last one if (++encoded === total) exportDoc(); }; // No CORS set in the response img.onerror = function() { // save the src var oldSrc = this.src; // there is an other problem this.onerror = function() { console.warn('failed to load an image at : ', this.src); if (--total === encoded && encoded > 0) exportDoc(); }; // remove the crossorigin attribute this.removeAttribute('crossorigin'); // retry this.src = ''; this.src = oldSrc; }; // load our external image into our img var href = image.getAttributeNS(xlinkNS, 'href'); // really weird bug that appeared since this answer was first posted // we need to force a no-cached request for the crossOrigin be applied img.src = href + (href.indexOf('?') > -1 ? + '&1': '?1'); }; // get an external svg doc to data String var parseFromUrl = function(url, element) { var xhr = new XMLHttpRequest(); xhr.onload = function() { if (this.status === 200) { var response = this.responseText || this.response; var dataUrl = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(response); element.setAttributeNS(xlinkNS, 'href', dataUrl); if (++encoded === total) exportDoc(); } // request failed with xhr, try as an  else { toDataURL(element); } }; xhr.onerror = function() { toDataURL(element); }; xhr.open('GET', url); xhr.send(); }; var images = svg.querySelectorAll('image'); total = images.length; encoded = 0; // loop through all our  elements for (var i = 0; i < images.length; i++) { var href = images[i].getAttributeNS(xlinkNS, 'href'); // check if the image is external if (href.indexOf('data:image') < 0) { // if it points to another svg element if (href.indexOf('.svg') > 0) { parseFromUrl(href, images[i]); } else // a pixel image toDataURL(images[i]); } // else increment our counter else if (++encoded === total) exportDoc(); } // if there were no  element if (total === 0) exportDoc(); } var exportDoc = function() { // check if our svgNode has width and height properties set to absolute values // otherwise, canvas won't be able to draw it var bbox = svg.getBoundingClientRect(); if (svg.width.baseVal.unitType !== 1) svg.setAttribute('width', bbox.width); if (svg.height.baseVal.unitType !== 1) svg.setAttribute('height', bbox.height); // serialize our node var svgData = (new XMLSerializer()).serializeToString(svg); // remember to encode special chars var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData); var svgImg = new Image(); svgImg.onload = function() { var canvas = document.createElement('canvas'); // IE11 doesn't set a width on svg images... canvas.width = this.width || bbox.width; canvas.height = this.height || bbox.height; canvas.getContext('2d').drawImage(svgImg, 0, 0, canvas.width, canvas.height); doSomethingWith(canvas) }; svgImg.src = svgURL; }; window.onload = parseImages; 
 canvas { border: 1px solid green !important; }