Convierta SVG a imagen (JPEG, PNG, etc.) en el navegador

300

Quiero convertir SVG en imágenes de mapa de bits (como JPEG, PNG, etc.) a través de JavaScript.

Zain
fuente
¿Qué tarea es lo que realmente quieres lograr? Aunque la respuesta echo-flow nos dice que es posible (en algunos navegadores) que existan métodos de conversión mejores y más fáciles para casi todos los casos prácticos.
aaaaaaaaaaaa
2
Aquí hay un ejemplo usando d3: stackoverflow.com/a/23667012/439699
as
svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - ¡Funciona perfectamente! [En la página de enlace, sourceSVG = $ ("# your_svg_elem_name"). Get (0)]
Vijay Singh
relacionado: stackoverflow.com/questions/3173048/…
mathheadinclouds

Respuestas:

244

Así es como puedes hacerlo a través de JavaScript:

  1. Use la biblioteca JavaScript de canvg para representar la imagen SVG usando Canvas: https://github.com/gabelerner/canvg
  2. Capture un URI de datos codificado como JPG (o PNG) desde el lienzo, de acuerdo con estas instrucciones: ¿ Capturar lienzo HTML como gif / jpg / png / pdf?
jbeard4
fuente
28
Esto no es estrictamente Javascript, sino también HTML5. Esto no funcionará en IE8 ni en ningún otro navegador que no sea compatible con HTML5 Canvas.
James
16
Si el navegador admite SVG y lienzo, entonces habría una forma mucho más simple de cargar el SVG en la memoria y luego pintarlo en un lienzo, sin la necesidad de Canvg, que es una biblioteca bastante grande porque maneja todo el análisis SVG que un navegador compatible con SVG ya proporciona de forma gratuita. No estoy seguro de si esto satisface el caso de uso original, pero si es así, consulte este recurso para obtener más detalles .
Premasagar
120
Gracias por no apoyar IE8. La gente debería entender que es hora de seguir adelante.
Sanket Sahu
99
Ahora puede usar la biblioteca de JavaScript SVG Pablo para lograr esto (lo hice). Vea toImage()y también download()para una imagen descargada automáticamente.
Premasagar
2
svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - ¡Funciona perfectamente! [En la página de enlace, sourceSVG = $ ("# your_svg_elem_name"). Get (0)]
Vijay Singh
44

La solución jbeard4 funcionó a la perfección.

Estoy usando Raphael SketchPad para crear un SVG. Enlace a los archivos en el paso 1.

Para un botón Guardar (la identificación de svg es "editor", la identificación del lienzo es "lienzo"):

$("#editor_save").click(function() {

// the canvg call that takes the svg xml and converts it to a canvas
canvg('canvas', $("#editor").html());

// the canvas calls to output a png
var canvas = document.getElementById("canvas");
var img = canvas.toDataURL("image/png");
// do what you want with the base64, write to screen, post to server, etc...
});
cooperativa
fuente
1
canvg necesita que el segundo parámetro sea, <svg>...</svgpero la función jquery html () no agrega la etiqueta svg, por lo que este código funciona para mí, pero necesitaba editar canvg livecanvg('canvas', '<svg>'+$("#editor").html()+'</svg>');
Luckyn
1
@Luckyn si llamas $(selector).html()al padre de tu elemento svg , funcionará
jonathanGB
@Luckyn y @jonathanGB, no debería tener que usar html()en envoltorios, o construir manualmente la svgetiqueta principal , que incluso podría tener atributos que omita con este truco. Solo use $(svg_elem)[0].outerHTMLle da la fuente completa de la svg y su contenido. Solo digo ...
nemesisfixx
18

Esto parece funcionar en la mayoría de los navegadores:

function copyStylesInline(destinationNode, sourceNode) {
   var containerElements = ["svg","g"];
   for (var cd = 0; cd < destinationNode.childNodes.length; cd++) {
       var child = destinationNode.childNodes[cd];
       if (containerElements.indexOf(child.tagName) != -1) {
            copyStylesInline(child, sourceNode.childNodes[cd]);
            continue;
       }
       var style = sourceNode.childNodes[cd].currentStyle || window.getComputedStyle(sourceNode.childNodes[cd]);
       if (style == "undefined" || style == null) continue;
       for (var st = 0; st < style.length; st++){
            child.style.setProperty(style[st], style.getPropertyValue(style[st]));
       }
   }
}

function triggerDownload (imgURI, fileName) {
  var evt = new MouseEvent("click", {
    view: window,
    bubbles: false,
    cancelable: true
  });
  var a = document.createElement("a");
  a.setAttribute("download", fileName);
  a.setAttribute("href", imgURI);
  a.setAttribute("target", '_blank');
  a.dispatchEvent(evt);
}

function downloadSvg(svg, fileName) {
  var copy = svg.cloneNode(true);
  copyStylesInline(copy, svg);
  var canvas = document.createElement("canvas");
  var bbox = svg.getBBox();
  canvas.width = bbox.width;
  canvas.height = bbox.height;
  var ctx = canvas.getContext("2d");
  ctx.clearRect(0, 0, bbox.width, bbox.height);
  var data = (new XMLSerializer()).serializeToString(copy);
  var DOMURL = window.URL || window.webkitURL || window;
  var img = new Image();
  var svgBlob = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
  var url = DOMURL.createObjectURL(svgBlob);
  img.onload = function () {
    ctx.drawImage(img, 0, 0);
    DOMURL.revokeObjectURL(url);
    if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob)
    {
        var blob = canvas.msToBlob();         
        navigator.msSaveOrOpenBlob(blob, fileName);
    } 
    else {
        var imgURI = canvas
            .toDataURL("image/png")
            .replace("image/png", "image/octet-stream");
        triggerDownload(imgURI, fileName);
    }
    document.removeChild(canvas);
  };
  img.src = url;
}
worstenbrood
fuente
3
Esto no funciona en IE11, debido al problema de seguridad con.msToBlob()
Florian Leitgeb
¡¡Gracias!! Me encanta cómo funciona esto para un nodo HTML SVG "local" y una URL SVG remota. Además, no requiere una biblioteca externa completa
Fabricio PH
7

La solución para convertir SVG a blob URL y blob URL a imagen png

const svg=`<svg version="1.1" baseProfile="full" width="300" height="200"
xmlns="http://www.w3.org/2000/svg">
   <rect width="100%" height="100%" fill="red" />
   <circle cx="150" cy="100" r="80" fill="green" />
   <text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text></svg>`
svgToPng(svg,(imgData)=>{
    const pngImage = document.createElement('img');
    document.body.appendChild(pngImage);
    pngImage.src=imgData;
});
 function svgToPng(svg, callback) {
    const url = getSvgUrl(svg);
    svgUrlToPng(url, (imgData) => {
        callback(imgData);
        URL.revokeObjectURL(url);
    });
}
function getSvgUrl(svg) {
    return  URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }));
}
function svgUrlToPng(svgUrl, callback) {
    const svgImage = document.createElement('img');
    // imgPreview.style.position = 'absolute';
    // imgPreview.style.top = '-9999px';
    document.body.appendChild(svgImage);
    svgImage.onload = function () {
        const canvas = document.createElement('canvas');
        canvas.width = svgImage.clientWidth;
        canvas.height = svgImage.clientHeight;
        const canvasCtx = canvas.getContext('2d');
        canvasCtx.drawImage(svgImage, 0, 0);
        const imgData = canvas.toDataURL('image/png');
        callback(imgData);
        // document.body.removeChild(imgPreview);
    };
    svgImage.src = svgUrl;
 }

Thom Kiesewetter
fuente
3

Escribí esta clase de ES6 que hace el trabajo.

class SvgToPngConverter {
  constructor() {
    this._init = this._init.bind(this);
    this._cleanUp = this._cleanUp.bind(this);
    this.convertFromInput = this.convertFromInput.bind(this);
  }

  _init() {
    this.canvas = document.createElement("canvas");
    this.imgPreview = document.createElement("img");
    this.imgPreview.style = "position: absolute; top: -9999px";

    document.body.appendChild(this.imgPreview);
    this.canvasCtx = this.canvas.getContext("2d");
  }

  _cleanUp() {
    document.body.removeChild(this.imgPreview);
  }

  convertFromInput(input, callback) {
    this._init();
    let _this = this;
    this.imgPreview.onload = function() {
      const img = new Image();
      _this.canvas.width = _this.imgPreview.clientWidth;
      _this.canvas.height = _this.imgPreview.clientHeight;
      img.crossOrigin = "anonymous";
      img.src = _this.imgPreview.src;
      img.onload = function() {
        _this.canvasCtx.drawImage(img, 0, 0);
        let imgData = _this.canvas.toDataURL("image/png");
        if(typeof callback == "function"){
            callback(imgData)
        }
        _this._cleanUp();
      };
    };

    this.imgPreview.src = input;
  }
}

Así es como lo usas

let input = "https://restcountries.eu/data/afg.svg"
new SvgToPngConverter().convertFromInput(input, function(imgData){
    // You now have your png data in base64 (imgData). 
    // Do what ever you wish with it here.
});

Si desea una versión JavaScript de vainilla, puede dirigirse al sitio web de Babel y transpilar el código allí.

Cels
fuente
2

Aquí hay una solución del lado del servidor basada en PhantomJS. Puede usar JSONP para realizar una llamada de dominio cruzado al servicio de imágenes:

https://github.com/vidalab/banquo-server

Por ejemplo:

http: // [host] /api/https%3A%2F%2Fvida.io%2Fdocuments%2FWgBMc4zDWF7YpqXGR/viewport_width=980&viewport_height=900&delay=5000&selector=%23canvas

Luego puede mostrar la imagen con la etiqueta img:

<img src="data:image/png;base64, [base64 data]"/>

Funciona a través del navegador.

Phuoc Do
fuente
El servicio parece estar muerto.
3
Nuestro anfitrión había sido golpeado con solicitudes falsas. Entonces decidimos derribarlo. Tendrás que ejecutar tu propio servidor ahora. Consulte el repositorio de github para obtener más información.
Phuoc Do
1

cambiar svgpara que coincida con tu elemento

function svg2img(){
    var svg = document.querySelector('svg');
    var xml = new XMLSerializer().serializeToString(svg);
    var svg64 = btoa(xml); //for utf8: btoa(unescape(encodeURIComponent(xml)))
    var b64start = 'data:image/svg+xml;base64,';
    var image64 = b64start + svg64;
    return image64;
};svg2img()
Mahdi Khalili
fuente
1
no funciona para mí, recibo este error:Uncaught TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.
Xsmael
1
@Xsmael intenta cambiar la interfaz DOMParser developer.mozilla.org/en-US/docs/Web/API/DOMParser
Mahdi Khalili
1

Svga pngse puede convertir dependiendo de las condiciones:

  1. Si svgestá en formato SVG (cadena) rutas :
    • crear lienzo
    • crear new Path2D()y establecer svgcomo parámetro
    • dibujar camino sobre lienzo
    • crear imagen y usar canvas.toDataURL()como src.

ejemplo:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let svgText = 'M10 10 h 80 v 80 h -80 Z';
let p = new Path2D('M10 10 h 80 v 80 h -80 Z');
ctx.stroke(p);
let url = canvas.toDataURL();
const img = new Image();
img.src = url;

Tenga en cuenta que Path2Dno es compatible iey es parcialmente compatible en edge. Polyfill resuelve eso: https://github.com/nilzona/path2d-polyfill

  1. Crea svgblob y dibuja sobre lienzo usando .drawImage():
    • hacer elemento de lienzo
    • hacer un objeto svgBlob desde el svg xml
    • hacer un objeto url desde domUrl.createObjectURL (svgBlob);
    • crear un objeto de imagen y asignar url a src de imagen
    • dibujar imagen en lienzo
    • obtener la cadena de datos png del lienzo: canvas.toDataURL ();

Buena descripción: http://ramblings.mcpher.com/Home/excelquirks/gassnips/svgtopng

Tenga en cuenta que en ie obtendrá una excepción en el escenario de canvas.toDataURL (); Es porque IE tiene una restricción de seguridad demasiado alta y trata el lienzo como de solo lectura después de dibujar la imagen allí. Todos los demás navegadores restringen solo si la imagen es de origen cruzado.

  1. Utiliza la canvgbiblioteca de JavaScript. Es una biblioteca separada pero tiene funciones útiles.

Me gusta:

ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();
Alex Vovchuk
fuente
3er enlace está roto
Serdar Sayın
Si, de hecho. No sé cómo llegar allí ahora. Pero la descripción anterior puede ser suficiente para cierta comprensión. Buena idea para el futuro co copia algo de contexto después de la referencia
Alex Vovchuk
0

Recientemente descubrí un par de bibliotecas de rastreo de imágenes para JavaScript que pueden generar una aproximación aceptable al mapa de bits, tanto en tamaño como en calidad. Estoy desarrollando esta biblioteca de JavaScript y CLI:

https://www.npmjs.com/package/svg-png-converter

Que proporciona una API unificada para todos ellos, compatible con el navegador y el nodo, sin depender del DOM, y una herramienta de línea de comando.

Para convertir logotipos / dibujos animados / imágenes similares, hace un excelente trabajo. Para fotos / realismo, se necesitan algunos ajustes, ya que el tamaño de salida puede crecer mucho.

Tiene un parque infantil, aunque en este momento estoy trabajando en uno mejor, más fácil de usar, ya que se han agregado más funciones:

https://cancerberosgx.github.io/demos/svg-png-converter/playground/#

cancerbero
fuente