Conception d'un document imprimable avec des outils Web

Dans l'univers des graphistes, on appel communément "print" tout ce qui a pour finalité d'être imprimé sur un bout de papier : plaquettes, affiches, coffrets, livrets. Je ne suis pas graphiste mais j'ai eu besoin de concevoir un lot de plaquettes destinées à l'impression. J'ai donc sorti mon navigateur...

Le document

Le document que je dois produire est une sorte de plaquette recto/verso au format carrée. Je ne connais pas encore les dimensions finales (cela dépendra du prix du papier, de l'impression et des enveloppes) mais on est à peu près sur du 14cm x 14cm.

L'agencement des plaquettes n'est pas super bien défini mais ça ressemble à une grille avec bordures blanches contenant des images et du texte.

Pour pimenter le tout, les images sont issues d'une banque d'images et choisis aléatoirement de sorte à ce que deux plaquettes ne possèdent pas les mêmes images. Au final, on aura alors autant de plaquettes différentes que de tirages.

Le principale problème : la résolution

Dans le monde de l'impression, lorsque l'on parle de résolution on parle de finesse d'impression et c'est un paramètre qui se défini en point par pouce (ppp ou dpi en anglais). Cela revient à parler de la capacité qu'a le materiel d'impression à inscrire les points d'encre sur un pouce (2,54cm).

Pour un rendu optimal, je cherche donc à faire correspondre le nombre de pixel du fichier à imprimer avec le nombre de point que la machine peut imprimer. Pour un document de 14,8cm de coté à 300dpi (standard utilisé dans l'impression), il faut donc générer un fichier de 1748 pixels de large.

Pour vérifier mon calcul, je lance Gimp, je rentre la dimension (14,8cm), la résolution (300dpi) et il me calcul automatiquement 1748 pixels de large.

Coté production, je vais utilisé mon ordinateur avec un écran qui lui aussi à une résolution issu des capacités du matériel. Je ne parle pas ici de la taille/définition (1920x1080), mais du nombre de pixel par pouce, appelé aussi "pitch". Mon écran possède 96 pixels par pouce, à peu près 37,8 pixel par cm. Il y a donc à presque un ratio de 3 entre le rendu papier et le rendu sur mon ordinateur.

Le navigateur et les technologies Web

Le choix d'utiliser un navigateur peut paraître un peu exotique mais je ne vois que des avantages à me baser sur des technologies web pour produire ces documents.

Premier Test : Affichage d'une grille

Pour commencer, je vais pas me prendre la tête avec la bonne largeur, je vais commencer par créer une page HTML et m'appuyer sur Bootstrap 4 et le mode de dispostion flex pour réaliser la grille.

Fichier print.html
<!doctype html>
<html lang="fr">
<head>
  <meta charset="utf-8">
  <title>Print</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous" />
  <link rel="stylesheet" href="./style.css" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
  <body class="bg-dark">
    <div class="cadre bg-white">
      <div class="content d-flex">
        <div class="col overflow-hidden col-hpr">
          <div class="overflow-hidden">
            <img class="img" src="https://i.picsum.photos/id/924/500/1200.jpg?hmac=mMxVIur3ofZsfqYOqHsQmNVtGgc9pbzgRty61-_PXCI" />
          </div>
        </div>
        <div class="col overflow-hidden d-flex flex-column col-hpl">
          <div class="col overflow-hidden px-0">
            <div class="overflow-hidden">
              <img class="img" src="https://i.picsum.photos/id/168/500/500.jpg?hmac=Jf1kwyoaDYOUIBE4QzzrQduR_B9jHbQ8WJfGWeEd_DE" />
            </div>
          </div>
          <div class="d-flex flex-column align-items-center py-2">
            <h2 class="title">Titre</h2>
            <span class="subtitle">Sous Titre</span>
          </div>
          <div class="col overflow-hidden px-0">
            <div class="overflow-hidden">
              <img class="img" src="https://i.picsum.photos/id/503/500/500.jpg?hmac=3b9eq3AncmppUsekkvoo5RrDvGBfFNhIvp92PUY1AVI" />
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>
Fichier style.css
:root {
  --size: 14.8cm;
  --padding-part : 15px;
  --padding-demi-part : calc(var(--padding-part) / 2);
}


.cadre { width: var(--size); height:var(--size); padding:var(--padding-part) 0px;}
.content {height: 100%; width: 100%;}
.img { width:100%; }

.col {padding-left: var(--padding-part); padding-right:var(--padding-part);}
.col-hpt {padding-top: var(--padding-demi-part);}
.col-hpl {padding-left:var(--padding-demi-part);}
.col-hpr {padding-right:var(--padding-demi-part);}
.col-hpb {padding-bottom:var(--padding-demi-part);}
Résultat Navigateur

On remarque que l'élément créer par le navigateur fait 559px, le navigateur s'est basé sur ma résolution de 96dpi pour calculer 559px, en effet, 14,8cm * 37,8 = 559,44px.

Second Test : Taille en fonction de la résolution

Cette fois, on va essayer de rendre un peu plus paramétrique notre feuille de style CSS pour avoir un fonctionnement qui s'adapterai dans le cas où :

Fichier style.css
:root {
  /* Constantes, nombre pixel par cm */
  --dpcm-72:28.346457;
  --dpcm-96:37.795276;
  --dpcm-192:75.590551;
  --dpcm-240:94.488189;
  --dpcm-300:118.110236;

  /* Configuration choisie, 13cm pour 240dpi */
  --size-cm : 13;
  --dpcm : var(--dpcm-240);

  /* Calcul final de la taille */
  --size-container-tmp : calc(var(--size-cm) * var(--dpcm));
  --size : calc(var(--size-container-tmp) * 1px);
}
Résultat Navigateur

En effet, pour éviter les problèmes de calcul on n'indique aucune unité sur tout ce qui est constantes et variables de configuration, on calcul des nombres et à la fin on applique une unité en multipliant par 1px.

Mon cadre fait maintenant 1228px, cela correspond parfaitement à 13cm à 240dpi.

Adapatation de la taille des textes et des bordures.

Désormais, le texte et le sous-texte sont clairement trop petit par rapport à la taille de la plaquette. De plus, les bordures de 15px paraissent trop fines par rapport aux images. On va mettre en place un système de ratio pour "adapter" les tailles en fonction du contexte.

On imagine les tailles des bordures et des textes pour une résolution de base (au mieux la résolution de son écran, dans mon cas 96dpi) et on appliquera le ratio en fonction de la résolution d'impression souhaitée.

Fichier style.css
:root {
  --ratio : calc(var(--dpcm) / var(--dpcm-96));

  --padding-part : calc(var(--ratio) * 15px);
  --padding-demi-part : calc(var(--padding-part) / 2);
}

.title { font-size: calc(3rem * var(--ratio)); }
.subtitle { font-size: calc(1.4rem * var(--ratio)); }
Résultat Navigateur

Génération des plaquettes

Je ne vais pas trop m'attarder sur la technique, toutefois je me suis appuyé sur du code Javascript pour constituer à partir d'un ensemble d'images définis par tailles (horizontal, carré, vertical) des jeux d'association pour créer des plaquettes de manière aléatoire.

Ensuite j'ai utilisé l'excellente librairie Javascript Template7 pour générer les cadres à partir de deux templates Recto/Verso défini en HTML.

L'extraction en document imprimable

Bon, c'est bien sympa d'avoir une page HTML avec plein de plaquettes mais comment je les exploitent pour créer des documents imprimables ?

Dans un premier temps, on va utiliser domtoimage et plus précisemment un fork qui comprend plein de correctifs (notamment pour gérer l'importation des fonts google) domtoimagemore pour transformer les noeud HTML en Objet Canvas.

Ensuite on va exporter les Canvas en JPEG et les inclures dans un Zip (à l'aide de la librairie JSZIP) qui sera proposé en téléchargement à la fin du traitement.

Un petit exemple/prototype est testable à cette adresse, le code Javascript suivant est issu de cet exemple. Il comporte une plaquette recto/verso complète avec un bouton d'export positionné en bas de page.

Fichier script.js
let exportImages = function () {
  let zip = new JSZip();
  let keys = ["recto", "verso"];
  let cmpt = keys.length;

  for(let k in keys) {
    let key = keys[k];
  
    let folder = zip.folder(key);
    domtoimage.toJpeg(document.getElementById(key), {quality: 0.99})
      .then(function(dataURI) {
        folder.file(key +".jpg", dataURI.substring(23), {base64:true});
        cmpt--;
        if(cmpt <= 0) {
          zip.generateAsync({type:"blob"})
            .then(function(content) {
              saveAs(content, "files.zip");
            });
        }
      }).catch(function(error) {
        console.error("oops, error!", error);
    });
  }
};


document.addEventListener("DOMContentLoaded", function() {
  document.getElementById('export').addEventListener('click', exportImages);
});

Remarques importantes :

Et voilà, on a nos images dans un Zip, y'a plus qu'à envoyer ça à l'imprimeur.

Conclusion

J'ai le sentiment d'avoir mieux compris la gestion des unités physiques (cm, mm, in) coté navigateur. Même si pour la conception d'interface Web, on préviligie plutôt des tailles en pourcentage combiné à des "breakpoints" en pixel, je pense qu'à partir du même principe on pourrait envisager un comportement CSS qui s'adapte parfaitement en fonction de la résolution du support qui le visualise.

Retour aux billets Billet plus ancien : Développement de ce site : Étape 3 - Analyse des URLs et Multi-pages Billet plus récent : Déploiement d'une application PHP sur un serveur mutualisé OVH qui bloque le clonage d'un dépôt depuis une instance gitlab privée.