Blog de développement

56 – Un QR code qui a de l’allure : design, lisibilité et l’astuce des MAJUSCULES

Dernière modification : 2026-09-28
QR code aux couleurs de la marque : points ronds, degrade radial bleu vers magenta, yeux magenta, logo G jaune au centre
Le QR final : points ronds, dégradé radial bleu → magenta, yeux magenta et le « G » du favicon au centre. Il encode un lien court écrit en MAJUSCULES.

On peut coller sur une fiche un QR code brut—noir, carré, sans âme—ou en faire un petit objet graphique aux couleurs de la marque. Ce billet raconte comment nous avons fabriqué le second pour nos fiches produit, comment mesurer objectivement sa lisibilité plutôt que de l’espérer, et deux astuces qui changent tout : l’encodage en MAJUSCULES, qui allège le code, et le lien court dynamique, qui le rend modifiable… après impression.

Un QR décoratif, mais qui doit marcher

Sur nos fiches, le QR est décoratif : l’adresse est aussi écrite en clair juste à côté. Le but est de montrer qu’on y a pensé—comme les fiches industrielles du fabricant et de ses concurrents—sans casser l’aspect stylisé. Mais « décoratif » ne veut pas dire « illisible » : un QR qui ne se scanne pas est pire qu’un QR absent, il donne l’impression d’un travail bâclé. D’où une exigence simple : joli ET fonctionnel.

Le moteur : qr-code-styling, sans Node

Le rendu s’appuie sur qr-code-styling—le même moteur que le service en ligne QR Code Monkey. C’est une bibliothèque navigateur ; plutôt que d’installer tout un environnement Node et un moteur de rendu graphique natif (pénible sous Windows), on la fait tourner dans Chromium headless piloté par Playwright, déjà présent sur la machine. Le reste—pilotage, mesures, sortie—est en Python.

Les choix de design retenus :

  • corps en points ronds ;
  • dégradé radial #0277BD (bleu, au centre) vers #991E7E (magenta, sur les bords) ;
  • yeux (les trois coins) en carré arrondi, centre rond, magenta plein ;
  • logo central : le « G » du favicon, dans son cadre jaune #FFED00 ;
  • correction d’erreur au niveau H (~30 %), indispensable dès qu’un logo masque une partie du code.

Le contraste avant l’esthétique

Point clé : un scanner ne lit pas la teinte, il lit le contraste de luminance entre les modules sombres et le fond clair. Cela éclaire un détail du dégradé radial qui n’est pas qu’esthétique : le magenta (fort contraste) tombe sur les coins, c’est-à-dire sur les trois yeux—les repères que l’appareil cherche en premier ; le bleu (contraste plus faible) reste au centre, là où le logo couvre déjà les modules. Le plus lisible est placé là où c’est le plus utile.

Mesurer, ne pas deviner

Comment savoir si un QR stylisé se scanne vraiment ? En le décodant par programme. Un petit lecteur (OpenCV + zxing-cpp) rejoue le PNG en simulant les conditions réelles : réduction de résolution et flou gaussien, puis vérifie que le contenu décodé est bien celui attendu.

python tester_lisibilite.py "qr_TO_fr.png" --attendu "HTTPS://WWW.GRANULOSHOP.COM/T/AB12"

Deux enseignements. D’abord, zxing-cpp se comporte à peu près comme un téléphone, tandis qu’OpenCV est bien plus sévère sur les QR décorés—son verdict est donc conservateur, ce qui rassure. Ensuite, et c’est contre-intuitif : le vrai levier de robustesse n’est pas la couleur, c’est la résolution (viser ≥ 1 024 px) accompagnée d’une quiet zone (la marge blanche autour).

L’astuce des MAJUSCULES

Meme lien encode en mode Byte (code dense) et en mode Alphanumeric (code plus aere, moins de modules)
Le même lien, à gauche en mode Byte, à droite en mode Alphanumeric : moins de modules, donc plus gros—et plus faciles à scanner.

La norme QR propose plusieurs modes d’encodage. Le mode Alphanumeric ne connaît que 45 caractères : les chiffres 0-9, les lettres A-Z en MAJUSCULES, et neuf symboles (espace $ % * + - . / :). Il ignore les minuscules—mais en échange, il est bien plus compact : il code deux caractères sur 11 bits, là où le mode Byte (celui qui accepte tout) en dépense 16.

Conséquence directe : une URL écrite tout en majuscules tient en mode Alphanumeric → une version de QR plus basse → des modules plus gros → une lecture plus robuste, à taille de papier égale. Sur l’image ci-dessus, le passage de l’un à l’autre ne change que le mode, et le code « respire » visiblement.

Côté générateur, un mode auto tranche tout seul : si l’URL est entièrement en majuscules et sans accents, il choisit Alphanumeric ; sinon il retombe sur Byte, sans jamais produire un code invalide.

Le lien court /T/, ou comment rendre un QR dynamique

Reste un obstacle : mettre en majuscules un vrai chemin (souvent plein de minuscules) n’est pas toujours possible. La solution est de ne pas encoder l’adresse longue, mais un lien court qu’on maîtrise. Chaque page reçoit un code court—Crockford base32 en majuscules, soit 0-9 A-Z privé de I, L, O et U pour éviter les confusions à l’œil—de 1 à 4 caractères. L’adresse encodée devient : HTTPS://WWW.GRANULOSHOP.COM/T/AB12. Courte, tout en majuscules : idéale pour l’Alphanumeric.

Un code imprimé se fige : on l’attribue une fois, séquentiellement, on le stocke, et on ne le renumérote jamais. Une simple table (tiny_code, url) tient l’association. Et surtout, /T/<code> est dynamique : on peut le faire pointer ailleurs sans réimprimer le QR. Deux façons de le réaliser.

Version statique : une page de transfert

La plus simple, pour un site majoritairement statique : on écrit, pour chaque code, une petite page HTML de redirection. Aucun code serveur, cachable à l’infini. Elle combine trois déclencheurs—ceinture et bretelles :

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex">
<meta http-equiv="refresh" content="0; url=https://www.granuloshop.com/TO/fr/products/product_ldp-190.html">
<link rel="canonical" href="https://www.granuloshop.com/TO/fr/products/product_ldp-190.html">
<title>Redirection</title>
<script>location.replace("https://www.granuloshop.com/TO/fr/products/product_ldp-190.html");</script>
</head>
<body>
<p><a href="https://www.granuloshop.com/TO/fr/products/product_ldp-190.html">Continuer</a></p>
</body>
</html>

Version dynamique : un point d’entrée ASP

Si l’on veut un seul endroit qui interroge la base à la volée—et pouvoir changer une destination sans regénérer le moindre fichier—un mini-script ASP classique suffit :

<%@ Language="VBScript" %>
<%
Dim code, url
code = Request.QueryString("c")
' ... lire dbo.topas_liens_courts, reconstruire l'adresse ...
' url = "https://www.granuloshop.com/TO/" & ligne("url")
If Len(url) > 0 Then
    Response.Status = "301 Moved Permanently"
    Response.AddHeader "Location", url
Else
    Response.Status = "404 Not Found"
End If
Response.End
%>

Le statique coûte zéro serveur et se prête parfaitement à un site de pages fixes ; l’ASP tient sur une seule page et permet un reciblage instantané via la base. Rien n’interdit de mélanger : pages statiques pour le gros du catalogue, endpoint dynamique pour les liens qu’on veut pouvoir rerouter à tout moment.

Router /T/ vers l’ASP : web.config et une regexp

Ce script ne sert à rien tant que le serveur ne l’appelle pas. Or l’adresse imprimée sur la fiche, /T/AB12, ne désigne aucun fichier : c’est à IIS de faire le lien, par une règle de réécriture d’URL posée dans un web.config. Deux décisions comptent plus que le reste.

Le fichier va dans le dossier T\, pas à la racine. Une règle posée dans un sous-dossier ne voit que les URL de ce sous-dossier : le web.config de la racine—avec ses 301 historiques, sa CSP et sa page 404—reste intact. Si le lien court disparaît un jour, on supprime le fichier, et c’est tout. Corollaire pratique : le motif s’applique à l’URL relative au dossier. Pour /T/AB12, la règle reçoit AB12—le T/ est déjà consommé. D’où un motif sans préfixe.

Réécriture, pas redirection. Un Rewrite est interne : le navigateur garde www.granuloshop.com/T/AB12 du premier au dernier instant. Un Redirect exposerait le chemin réel de l’ASP et ferait passer le code par une étape supplémentaire dans l’historique.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>

    <!-- Le dossier T\ ne contient aucun fichier a servir :
         il n'existe que pour porter ce web.config. -->
    <directoryBrowse enabled="false" />

    <rewrite>
      <rules>
        <!-- Crockford base32 : 0-9 et A-Z prive de I, L, O et U -->
        <rule name="LienCourt_T" stopProcessing="true">
          <match url="^([0-9A-HJKMNP-TV-Z]{1,4})/?$" />
          <action type="Rewrite" url="/TO/ASP/lien_court_relais.asp?c={R:1}" />
        </rule>
      </rules>
    </rewrite>

  </system.webServer>
</configuration>

La regexp dit exactement ce que le générateur produit : l’alphabet Crockford—les quatre lettres ambiguës en moins—et {1,4} pour la longueur. Le /? final tolère la barre oblique de trop : quelqu’un qui recopie l’adresse à la main ne doit pas tomber en 404. Et comme ignoreCase vaut true par défaut, la casse est indifférente—le QR encode AB12 en majuscules pour l’Alphanumeric, mais un lecteur qui tape ab12 arrive au même endroit. Tout ce que la règle ne reconnaît pas retombe sur la 404 du site, sans jamais atteindre l’ASP : un filtre gratuit contre le balayage d’adresses au hasard.

Jusqu’où peut-on s’éloigner ? La distance de lecture

Le même lecteur, poussé plus loin : on simule l’éloignement—moins de pixels par module—en ajoutant ce qu’un téléphone apporte vraiment (léger flou, bruit du capteur, compression JPEG), puis on cherche la taille à laquelle le décodage lâche. Traduite en distance pour un QR imprimé à 25 mm, lu par un téléphone 12 MP, cela donne :

QR noir et blanc carre, sans logo

QR classique

N&B carré · sans logo

Mode
Byte (URL brute)
Correction d’erreur
L — 7 %
Modules
33 × 33
Distance de lecture max~60 cm
QR decoratif : points ronds, degrade bleu-magenta, logo G au centre

QR design

points, dégradé, logo · lien court en MAJUSCULES

Mode
Alphanumeric (MAJUSCULES)
Correction d’erreur
H — 30 %
Modules
29 × 29
Distance de lecture max~55 cm

Distances approximatives ; elles varient avec le téléphone et l’éclairage. Elles sont proportionnelles à la taille d’impression : ×2 à 50 mm, ×4 à 100 mm (une affiche).

Une variante métier : le QR « granulométrique »

QR a points ronds dont la taille croit en diagonale, du fin en haut-gauche au gros en bas-droite, logo G jaune au centre
Le style granulométrique : le rayon des points croît en diagonale (fin → gros), un clin d’œil à la distribution de tailles de grains—notre métier.

Puisque le lien court laisse beaucoup de place libre dans le code, on peut se permettre un rendu plus signifiant. Le style granulométrique garde nos points ronds, mais fait croître leur rayon le long de la diagonale (fins en haut-gauche, gros en bas-droite), à la manière d’une courbe granulométrique—l’objet même de nos instruments. Le code devient une petite signature métier, sans cesser d’être un vrai QR. Côté outil, c’est une simple option, à côté du style classique.

La recette est verrouillée et mesurée : correction H, rayon borné (0,30 → 0,50 module) pour ne jamais empiéter sur le module voisin, yeux magenta, quiet zone de 4 modules. Au banc OpenCV/zxing : 96 %—BON, avec ou sans le logo central (même profil que le QR design). Pousser le curseur plus loin—logo plus gros, gradient plus fort—retombe à 88 % : l’équilibre est ici.

Vectoriel pour l’impression

Dernier détail, mais qui compte pour un PDF : on sort le QR en SVG. C’est net à n’importe quelle résolution d’imprimante, léger, et ça évite toute question de DPI. Une option remplace même le logo raster embarqué par ses formes vectorielles natives, pour un fichier 100 % propre. Le PNG en haute définition reste comme secours pour les usages écran.

Bilan

Un QR « joli » n’est pas incompatible avec un QR qui marche—à condition de mesurer plutôt que de deviner, et de se rappeler que le scanner lit le contraste, pas la teinte. Deux idées à emporter : les MAJUSCULES font basculer l’encodage en mode Alphanumeric et allègent le code ; et un lien court /T/ le rend à la fois compact et modifiable après impression—par une simple page de transfert statique, ou par un point d’entrée ASP quand on veut piloter la destination depuis la base.