Converta SVG em imagem (JPEG, PNG, etc.) no navegador

300

Quero converter SVG em imagens de bitmap (como JPEG, PNG etc.) através de JavaScript.

Zain
fonte
Que tarefa é que você realmente deseja realizar? Embora a resposta dos fluxos de eco nos diga que é (em alguns navegadores) possível, existem métodos de conversão melhores e mais fáceis para quase todos os casos práticos.
Aaaaaaaaaaaa
2
Aqui está um exemplo usando d3: stackoverflow.com/a/23667012/439699
ace
svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - Funciona perfeitamente! [Na página do link, sourceSVG = $ ("# your_svg_elem_name"). Get (0)]
Vijay Singh
relacionado: stackoverflow.com/questions/3173048/…
mathheadinclouds

Respostas:

244

Aqui está como você pode fazer isso através do JavaScript:

  1. Use a biblioteca JavaScript canvg para renderizar a imagem SVG usando o Canvas: https://github.com/gabelerner/canvg
  2. Capturar um URI de dados codificado como JPG (ou PNG) na Tela, de acordo com estas instruções: Capturar Tela HTML como gif / jpg / png / pdf?
jbeard4
fonte
28
Isso não é estritamente Javascript, mas HTML5 também. Isso não funcionará no IE8 ou em qualquer outro navegador que não suporte o HTML5 Canvas.
James
16
Se o navegador suportar SVG e tela, haveria uma maneira muito mais simples de carregar o SVG na memória e pintá-lo em uma tela, sem a necessidade do Canvg, que é uma biblioteca bastante grande porque lida com toda a análise SVG que um navegador compatível com SVG já oferece gratuitamente. Não tenho certeza se isso satisfaz o caso de uso original, mas, se for o caso, consulte este recurso para obter detalhes .
Premasagar
120
Obrigado por não apoiar o IE8. As pessoas devem entender que é hora de seguir em frente.
Sanket Sahu
9
Agora você pode usar a biblioteca JavaScript SVG Pablo, Pablo, para conseguir isso (eu consegui). Consulte toImage()e também download()para obter uma imagem baixada automaticamente.
Premasagar 15/09/14
2
svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - Funciona perfeitamente! [Na página do link, sourceSVG = $ ("# your_svg_elem_name"). Get (0)]
Vijay Singh
44

A solução jbeard4 funcionou lindamente.

Estou usando o Raphael SketchPad para criar um SVG. Link para os arquivos na etapa 1.

Para um botão Salvar (o id do svg é "editor", o id do canvas é "canvas"):

$("#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...
});
coop
fonte
1
canvg precisa o segundo parâmetro a ser <svg>...</svgmas a função jquery html () não adicionar tag svg, de modo que este código funciona para mim, mas eu precisava para editar o canvg ao vivo paracanvg('canvas', '<svg>'+$("#editor").html()+'</svg>');
Luckyn
1
@Luckyn se você ligar $(selector).html()no pai de sua SVG elemento, ele vai trabalhar
jonathanGB
@Luckyn e @jonathanGB, você não precisa usar html()em wrappers ou construir manualmente a svgtag pai - que pode até ter atributos que você deixa de fora com esse hack. Apenas use $(svg_elem)[0].outerHTMLfornece a fonte completa do svg e seu conteúdo. Apenas dizendo ...
nemesisfixx
18

Isso parece funcionar na maioria dos 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
fonte
3
Isto não está funcionando no IE11, por causa do problema de segurança com.msToBlob()
Florian Leitgeb
Obrigado!! Eu amo como isso funciona tanto para um nó HTML SVG "local" quanto para um URL SVG remoto. Além disso, ele não requer uma biblioteca externa completa
Fabricio PH
7

A solução para converter SVG para URL de blob e URL de blob para imagem 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
fonte
3

Eu escrevi essa classe ES6 que faz o trabalho.

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;
  }
}

Aqui está como você o usa

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.
});

Se você quiser uma versão JavaScript de baunilha, poderá acessar o site da Babel e transpilar o código para lá.

Cels
fonte
2

Aqui está uma solução do lado do servidor baseada no PhantomJS. Você pode usar JSONP para fazer uma chamada entre domínios para o serviço de imagem:

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

Por exemplo:

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

Então você pode exibir a imagem com a tag img:

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

Funciona através do navegador.

Phuoc Do
fonte
O serviço parece estar morto.
3
Nosso anfitrião foi atingido por pedidos falsos. Então decidimos derrubá-lo. Você terá que executar seu próprio servidor agora. Veja o repositório do github para obter mais informações.
Phuoc Do 4/15
1

mude svgpara combinar com seu 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
fonte
1
ele não está funcionando para mim, eu recebo este erro:Uncaught TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.
Xsmael
1
@Xsmael tentar mudar a interface DOMParser developer.mozilla.org/en-US/docs/Web/API/DOMParser
Mahdi Khalili
1

Svga pngpode ser convertida de acordo com as condições de:

  1. Se svgestiver no formato SVG (string) de caminhos :
    • criar tela
    • criar new Path2D()e definir svgcomo parâmetro
    • desenhar caminho na tela
    • criar imagem e usar canvas.toDataURL()como src.

exemplo:

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;

Observe que Path2Dnão é suportado iee parcialmente suportado na borda. O Polyfill resolve isso: https://github.com/nilzona/path2d-polyfill

  1. Crie svgblob e desenhe na tela usando .drawImage():
    • fazer elemento de tela
    • crie um objeto svgBlob a partir do xml svg
    • crie um objeto de URL a partir de domUrl.createObjectURL (svgBlob);
    • crie um objeto de imagem e atribua URL à imagem src
    • desenhar imagem na tela
    • obter string de dados png da tela: canvas.toDataURL ();

Boa descrição: http://ramblings.mcpher.com/Home/excelquirks/gassnips/svgtopng

Note que em ie você receberá uma exceção no palco do canvas.toDataURL (); Isso ocorre porque o IE possui uma restrição de segurança muito alta e trata a tela como somente leitura após desenhar a imagem lá. Todos os outros navegadores restringem apenas se a imagem for de origem cruzada.

  1. Use a canvgbiblioteca JavaScript. É uma biblioteca separada, mas possui funções úteis.

Gostar:

ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();
Alex Vovchuk
fonte
Terceiro link quebrado
Serdar Sayın
sim, de fato. Não sei como chegar lá agora. Mas a descrição acima pode ser suficiente para alguma compreensão. Boa ideia para o futuro copia algum contexto após a referência
Alex Vovchuk 10/06
0

Recentemente, descobri algumas bibliotecas de rastreamento de imagens para JavaScript que realmente são capazes de criar uma aproximação aceitável ao bitmap, tanto em tamanho quanto em qualidade. Estou desenvolvendo esta biblioteca JavaScript e CLI:

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

Que fornece API unificada para todos eles, suportando navegador e nó, não dependendo do DOM, e uma ferramenta de linha de comando.

Para converter logotipos / desenhos animados / imagens semelhantes, ele faz um excelente trabalho. Para fotos / realismo, são necessários alguns ajustes, pois o tamanho da saída pode aumentar bastante.

Tem um playground, embora agora eu esteja trabalhando em um melhor, mais fácil de usar, pois mais recursos foram adicionados:

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

cancerbero
fonte