我有一个外部SVG文件,其中包含一些模式中嵌入的图像标记.每当我使用此SVG转换为PNG时toDataURL()
,生成的PNG图像不包含我作为模式应用于某些SVG路径的图像.有什么方法可以解决这个问题吗?
是的,有:将svg附加到您的文档中,并将所有包含的图像编码为dataURI.
我正在编写一个执行此操作的脚本以及其他一些内容,例如包括外部样式表以及toDataURL将失败的其他一些修复(例如,通过xlink:href
属性引用的外部元素
).
这是我为解析图像内容而编写的函数:
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(); }
这里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或者一个或一个
元素,你必须确保所有资源都符合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 '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;
}