[JS]<canvas> Download & print image

NoSmoking

Objectif

Pouvoir enregistrer et imprimer une image issue d'un élément <canvas>.

Comme image nous allons nous servir d'un graphique réalisé avec la bibliothèque Chart.js qui est une bibliothèque simple et légère qui permet de réaliser des graphiques et utilisant justement un élément <canvas> pour le rendu.

Graphique

➜ Cliquez sur les icônes du graphe pour lancer les actions.

Image récupérée
image récupérée

Affichage de l'image

On utilise la méthode toDataURL() de l'élément <canvas> pour récupérer les données image liées à celui-ci puis on les affecte à l'attribut src d'un élément <img> présent dans la page ou crée dynamiquement.

Dans l'exemple ci-dessous l'élément existe déjà dans la page.

function showImage(idCanvas, idImage) {
    // récup. de l'élément <img> servant pour l'affichage
    const oImg = document.getElementById(idImage);
    // récup. de l'élément <canvas>
    const canvas = document.getElementById(idCanvas);
    // récup. des données de l'image du <canvas>
    const dataImage = canvas.toDataURL("image/png");
    // affectation des données à l'attribut src de l'élément <img>
    oImg.src = dataImage;
}

Impression de l'image

On va récupérer les données image du <canvas>, comme vu précédemment, ouvrir une nouvelle fenêtre, écrire dans le document pour insérer un élément <img> en affectant à son attribut src les données image et enfin lancer l'impression du document en fermant celui-ci l'impression terminée.

function printImage(idCanvas) {
    // récup. de l'élément <canvas>
    const canvas = document.getElementById(idCanvas);
    // récup. des données de l'image du <canvas>
    const dataImage = canvas.toDataURL("image/png");
    // ouverture d'une nouvelle fenêtre
    const fen = window.open();
    // ouverture du document pour écriture
    fen.document.open();
    // insére un élément "<img> avec l'attribut src contenant les données de l'image
    fen.document.write("<img src='" + dataImage + "'>");
    // fermeture du document
    fen.document.close();
    // lance l'impression dès que chargé
    fen.onload = setTimeout(function() {
        fen.print();
        fen.close();
    }, 100);
}

Enregistrement de l'image

Pour l'enregistrement nous allons passer par un élément <a> ayant un attribut download.

Cette méthode ne fonctionne malheureusement pas sous IE-Edge, ceci est lié à la longueur maximum autorisée pour une URL, mais il existe une méthode de remplacement bien plus simple à mettre en place d'ailleurs (voir le code ci-dessous).

Il est également nécessaire de modifier le type des données, en « image/octet-stream », avant affectation à l'attribut href de l'élément.

function saveImage(idCanvas, nomFichier) {
    // nom du fichier pour l'enregistrement
    const nomFile = nomFichier || "image.png";
    // récup. de l'élément <canvas>
    const canvas = document.getElementById(idCanvas);
    let dataImage;
    // pour IE et Edge c'est simple !!!
    // https://technet.microsoft.com/en-us/windows/hh771732(v=vs.60)
    if (canvas.msToBlob) {
      // crée un objet blob contenant le dessin du canvas
      dataImage = canvas.msToBlob();
      // affiche l'invite d'enregistrement
      window.navigator.msSaveBlob(dataImage, nomFile);
    }
    else {
      // création d'un lien HTML5 download
      const lien = document.createElement("A");
      // récup. des data de l'image
      dataImage = canvas.toDataURL("image/png");
      // affectation d'un nom à l'image
      lien.download = nomFile;
      // modifie le type de données
      dataImage = dataImage.replace("image/png", "image/octet-stream");
      // affectation de l'adresse
      lien.href = dataImage;
      // ajout de l'élément
      document.body.appendChild(lien);
      // simulation du click
      lien.click();
      // suppression de l'élément devenu inutile
      document.body.removeChild(lien);
    }
}

Conclusion

Outre le fait que les fonctions ci-dessus pourraient être écrites de façon à les rendre plus génériques et stables, les bases sont posées pour gérer les données image d'un élément <canvas> et ce indépendamment de l'exemple pris ici.

Ressources