javascript - insertar - svg web
Convierta SVG a PNG con imágenes aplicadas como fondo a elementos svg (1)
Tengo un archivo SVG externo que contiene algunas etiquetas de imagen incrustadas en el patrón.
Cada vez que convierto este SVG en PNG usando
toDataURL()
, las imágenes PNG generadas no contienen la imagen que he aplicado como patrón a algunas rutas SVG.
¿Hay alguna forma de resolver este problema?
Sí, hay: agregue el svg a su documento y codifique todas las imágenes incluidas en dataURIs.
Estoy escribiendo
un script
que hace esto y también algunas otras cosas como incluir hojas de estilo externas y alguna otra solución de dónde toDataURL fallará (por ejemplo, elementos externos a los que se hace referencia a través de
xlink:href
atributo
xlink:href
o
<funciri>
).
Aquí está la función que escribí para analizar el contenido de las imágenes:
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();
}
Aquí el svgDoc se llama
svg
,
y la función
exportDoc()
podría escribirse simplemente 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;
};
Pero una vez más, primero deberá agregar su svg al documento (ya sea a través de xhr o en un elemento
<iframe>
o
<object>
, y deberá asegurarse de que todos sus recursos
cumplan con CORS
(o del mismo dominio ) para obtener estos renderizados.
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>