将SVG转换为带有应用图像的PNG作为svg元素的背景

我有一个外部的SVG文件,其中包含一些模式中的embedded式图像标签。 每当我使用toDataURL()将这个SVG转换成PNG时,生成的PNG图像不包含我已经作为模式应用于某些SVGpath的图像。 有什么办法可以解决这个问题吗?

是的:将svg附加到文档中,并将所有包含的图像编码为dataURI。

我正在编写一个脚本 ,这样做,还有一些其他的东西,如包括外部样式表和其他toDataURL将失败的其他东西(例如通过xlink:href属性或<funciri>引用的外部元素)。

这是我写的parsing图像内容的函数:

 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 <image>'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 <img> 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 <images> 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 <image> element if (total === 0) exportDoc(); } 

这里的svgDoc被称为svg
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; }; 

但是再一次,你必须首先将你的svg附加到文档中(通过xhr或者<iframe><object>元素),并且必须确保所有资源都符合CORS (或来自同一个域)为了得到这些呈现。

 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 <image>'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 <img> 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 <images> 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 <image> 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; } 
 <svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" version="1.1"> <defs> <pattern id="Pattern" x="0" y="0" width=".25" height=".25"> <image xlink:href="https://dl.dropboxusercontent.com/s/1alt1303g9zpemd/UFBxY.png" width="100" height="100"/> </pattern> </defs> <rect fill="url(#Pattern)" x="0" y="0" width="200" height="200"/> </svg>