将 SVG 转换为 PNG,并将应用图像作为 svg 元素的背景
Convert SVG to PNG with applied images as background to svg elements
我有一个外部 SVG 文件,其中包含一些嵌入图像标签的图案。每当我使用 toDataURL()
将此 SVG 转换为 PNG 时,生成的 PNG 图像不包含我已作为图案应用到某些 SVG 路径的图像。有什么办法可以解决这个问题吗?
是的,有:将 svg 附加到您的文档中并将所有包含的图像编码为 dataURI。
我正在写 a script 来执行此操作以及其他一些内容,例如包括外部样式表和 toDataURL 将在何处失败的其他一些修复(例如,通过 xlink:href
属性引用的外部元素或 <funciri>
).
这是我为解析图像内容而编写的函数:
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>
我有一个外部 SVG 文件,其中包含一些嵌入图像标签的图案。每当我使用 toDataURL()
将此 SVG 转换为 PNG 时,生成的 PNG 图像不包含我已作为图案应用到某些 SVG 路径的图像。有什么办法可以解决这个问题吗?
是的,有:将 svg 附加到您的文档中并将所有包含的图像编码为 dataURI。
我正在写 a script 来执行此操作以及其他一些内容,例如包括外部样式表和 toDataURL 将在何处失败的其他一些修复(例如,通过 xlink:href
属性引用的外部元素或 <funciri>
).
这是我为解析图像内容而编写的函数:
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>