Convertir SVG en image (JPEG, PNG, etc.) dans le navigateur

300

Je veux convertir SVG en images bitmap (comme JPEG, PNG, etc.) via JavaScript.

Zain
la source
Quelle tâche voulez-vous réellement accomplir? Même si les échos-réponses nous disent qu'il est (dans certains navigateurs) possible, il existe des méthodes de conversion meilleures et plus faciles pour presque tous les cas pratiques.
aaaaaaaaaaaa
2
Voici un exemple utilisant d3: stackoverflow.com/a/23667012/439699
ace
svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - Fonctionne parfaitement! [Sur la page des liens, sourceSVG = $ ("# your_svg_elem_name"). Get (0)]
Vijay Singh
en relation: stackoverflow.com/questions/3173048/…
mathheadinclouds

Réponses:

244

Voici comment vous pouvez le faire via JavaScript:

  1. Utilisez la bibliothèque JavaScript canvg pour rendre l'image SVG à l'aide de Canvas: https://github.com/gabelerner/canvg
  2. Capturez un URI de données encodé en JPG (ou PNG) à partir du canevas, selon ces instructions: Capturer le canevas HTML au format gif / jpg / png / pdf?
jbeard4
la source
28
Ce n'est pas strictement Javascript, mais également HTML5. Cela ne fonctionnera pas sur IE8 ou tout autre navigateur qui ne prend pas en charge HTML5 Canvas.
James
16
Si le navigateur prend en charge SVG et le canevas, il y aurait un moyen beaucoup plus simple de charger le SVG en mémoire et de le peindre ensuite dans un canevas, sans avoir besoin de Canvg, qui est une bibliothèque assez grande car il gère toutes les analyses SVG qui un navigateur compatible SVG fournit déjà gratuitement. Je ne sais pas si cela satisfait le cas d'utilisation d'origine, mais si c'est le cas, consultez cette ressource pour plus de détails .
Premasagar
120
Merci de ne pas prendre en charge IE8. Les gens devraient comprendre qu'il est temps de passer à autre chose.
Sanket Sahu
9
Vous pouvez maintenant utiliser la bibliothèque JavaScript SVG Pablo pour y parvenir (je l'ai fait). Voir le toImage()et aussi download()pour une image téléchargée automatiquement.
Premasagar
2
svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - Fonctionne parfaitement! [Sur la page des liens, sourceSVG = $ ("# your_svg_elem_name"). Get (0)]
Vijay Singh
44

La solution jbeard4 a parfaitement fonctionné.

J'utilise Raphael SketchPad pour créer un SVG. Lien vers les fichiers de l'étape 1.

Pour un bouton Enregistrer (l'id de svg est "éditeur", l'id de canvas est "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érative
la source
1
canvg a besoin du second paramètre, <svg>...</svgmais la fonction jquery html () n'ajoute pas de balise svg, donc ce code fonctionne pour moi mais j'avais besoin de modifier le canvg en directcanvg('canvas', '<svg>'+$("#editor").html()+'</svg>');
Luckyn
1
@Luckyn si vous appelez $(selector).html()le parent de votre élément svg , cela fonctionnera
jonathanGB
@Luckyn et @jonathanGB, vous ne devriez pas avoir à utiliser html()sur des wrappers, ou construire manuellement la svgbalise parent - qui pourrait même avoir des attributs que vous omettez avec ce hack. Une simple utilisation $(svg_elem)[0].outerHTMLvous donne la source complète du svg et de son contenu. Je dis juste ...
nemesisfixx
18

Cela semble fonctionner dans la plupart des navigateurs:

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;
}
le pire
la source
3
Cela ne fonctionne pas dans IE11, en raison du problème de sécurité avec.msToBlob()
Florian Leitgeb
Merci!! J'adore comment cela fonctionne à la fois pour un nœud HTML SVG "local" et une URL SVG distante. De plus, il ne nécessite pas de bibliothèque externe complète
Fabricio PH
7

La solution pour convertir SVG en URL blob et URL blob en image 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
la source
3

J'ai écrit cette classe ES6 qui fait le travail.

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

Voici comment vous l'utilisez

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 vous voulez une version JavaScript vanille, vous pouvez vous rendre sur le site Babel et y transpiler le code.

Cels
la source
2

Voici une solution côté serveur basée sur PhantomJS. Vous pouvez utiliser JSONP pour effectuer un appel interdomaine au service d'imagerie:

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

Par exemple:

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

Ensuite, vous pouvez afficher l'image avec la balise img:

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

Cela fonctionne à travers le navigateur.

Phuoc Do
la source
Le service semble mort.
3
Notre hôte avait été frappé de fausses demandes. Nous avons donc décidé de le retirer. Vous devrez maintenant exécuter votre propre serveur. Voir le dépôt github pour plus d'informations.
Phuoc Do
1

changer svgpour correspondre à votre élément

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
la source
1
ça ne marche pas pour moi, je reçois cette erreur:Uncaught TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.
Xsmael
1
@Xsmael essaie de changer l'interface DOMParser developer.mozilla.org/en-US/docs/Web/API/DOMParser
Mahdi Khalili
1

Svgà pngconvertir selon les conditions:

  1. If svgest au format SVG (chaîne) :
    • créer une toile
    • créer new Path2D()et définir svgcomme paramètre
    • dessiner un chemin sur toile
    • créer une image et l'utiliser canvas.toDataURL()comme src.

exemple:

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;

Notez que Path2Dnon pris en charge dans ieet partiellement pris en charge dans Edge. Polyfill résout cela: https://github.com/nilzona/path2d-polyfill

  1. Créez un svgblob et dessinez sur un canevas en utilisant .drawImage():
    • faire un élément de toile
    • créer un objet svgBlob à partir du xml svg
    • créer un objet url à partir de domUrl.createObjectURL (svgBlob);
    • créer un objet Image et attribuer une URL à l'image src
    • dessiner l'image dans la toile
    • obtenir la chaîne de données png à partir de canvas: canvas.toDataURL ();

Belle description: http://ramblings.mcpher.com/Home/excelquirks/gassnips/svgtopng

Notez que dans ie vous obtiendrez une exception sur scène de canvas.toDataURL (); C'est parce qu'IE a une restriction de sécurité trop élevée et traite le canevas en lecture seule après y avoir dessiné une image. Tous les autres navigateurs ne restreignent que si l'image est d'origine croisée.

  1. Utilisez la canvgbibliothèque JavaScript. Il s'agit d'une bibliothèque distincte mais a des fonctions utiles.

Comme:

ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();
Alex Vovchuk
la source
Le troisième lien est rompu
Serdar Sayın
Oui en effet. Je ne sais pas comment y arriver maintenant. Mais la description ci-dessus peut être suffisante pour une certaine compréhension. Bonne idée pour le futur co copier du contexte après la référence
Alex Vovchuk
0

J'ai récemment découvert quelques bibliothèques de suivi d'image pour JavaScript qui sont en effet capables de construire une approximation acceptable du bitmap, à la fois en taille et en qualité. Je développe cette bibliothèque JavaScript et CLI:

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

Qui fournit une API unifiée pour tous, prenant en charge le navigateur et le nœud, sans dépendre du DOM, et un outil de ligne de commande.

Pour convertir des logos / dessins animés / images similaires, il fait un excellent travail. Pour les photos / réalisme, quelques ajustements sont nécessaires car la taille de sortie peut augmenter considérablement.

Il a une aire de jeux, bien que je travaille actuellement sur une meilleure, plus facile à utiliser, car plus de fonctionnalités ont été ajoutées:

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

cancerbero
la source