veilletech.fr
7 sept. Feed du jour
#03 FRONT Article

Safari rend un PNG quand vous demandez du WebP

Une sortie plausible n'est pas une sortie correcte.

Sur Safari, `canvas.toBlob(cb, 'image/webp')` ne lève pas d'erreur : la spécification HTML impose de retomber sur `image/png` quand le type demandé n'est pas supporté. On livre donc un PNG portant l'extension `.webp`, sans qu'aucun signal ne le dise. Le reste de la chaîne de conversion côté client accumule le même genre de défaillances muettes.

3 min de lectureintermédiairevidéo 1:22
Partager
Sommaire4 sections
  1. Ce qui se passe
  2. La seule détection fiable
  3. Les pannes suivantes sont tout aussi muettes
  4. À retenir

Ce qui se passe

La quasi-totalité des convertisseurs WebP qui tournent dans le navigateur reposent sur une ligne :

JavaScript
canvas.toBlob(blob => { /* ... */ }, 'image/webp', quality)

Sur Safari, cette ligne rend un PNG. Pas une erreur, pas null : un fichier parfaitement valide, du mauvais format.

Ce n'est pas un bug. Safari décode le WebP depuis la version 14, mais n'a jamais su l'encoder depuis un canvas. Or la spécification HTML prévoit que si le type demandé n'est pas supporté, le navigateur retombe sur image/png. Si vous avez nommé le téléchargement photo.webp — et pourquoi ne l'auriez-vous pas fait, vous avez demandé un WebP — l'utilisateur repart avec un PNG mal étiqueté, et ne l'apprendra que le jour où quelque chose en aval le refusera.

La seule détection fiable

Il faut comparer ce qu'on a reçu à ce qu'on a demandé :

JavaScript
const blob = await canvas.convertToBlob({ type: mime, quality: quality / 100 })
if (blob.type !== mime) {
  throw new Error(
    `This browser cannot encode ${format} via canvas. ` +
    `Safari does not support it — the WebAssembly encoder is required.`
  )
}

Une fois admis que le canvas n'est pas fiable pour encoder, la suite s'impose : embarquer le codec soi-même, libwebp compilé en WebAssembly.

Les pannes suivantes sont tout aussi muettes

L'auteur en liste six. Quatre méritent d'être retenues.

Vite a besoin de deux réglages opposés en même temps. Les codecs jSquash doivent être exclus du pré-bundling — le pré-bundler réécrit leur glue Emscripten, qui ne retrouve alors plus son fichier .wasm voisin. À l'inverse, utif2 (TIFF) et libheif-js/wasm-bundle (HEIC) sont du CommonJS et doivent y être inclus, faute de quoi on récolte un UTIF.decode is not a function à l'exécution. Le critère qui tranche : le paquet charge-t-il un .wasm séparé à l'exécution ? Piège supplémentaire, Vite compare le spécificateur exact : déclarer @jsquash/webp ne couvre pas @jsquash/webp/encode.

Les imports dynamiques dans un worker échappent au scanner de dépendances. En développement, le paquet reste inconnu jusqu'à la première conversion : Vite découvre alors une dépendance, ré-optimise en pleine session et force un rechargement complet. Vu de l'utilisateur, une file de 50 fichiers part à la poubelle, et la console affiche 504 (Outdated Optimize Dep).

target_size est ignoré tant que pass vaut 1. libwebp atteint un budget d'octets par analyses d'entropie répétées ; sans passes multiples, il n'y a rien sur quoi converger. Le réglage est silencieusement sans effet.

Un PNG transparent converti en JPEG ressort noir, des deux côtés. mozjpeg reçoit le tampon RGBA et ignore un octet sur quatre ; or le canvas donne 0,0,0 aux pixels transparents. Et la spec HTML impose de composer un contexte avec alpha sur du noir lors de l'encodage vers un format qui n'en a pas. Aucune des deux voies n'échoue : elles produisent une image abîmée. La parade est d'aplatir soi-même sur du blanc, comme le font les éditeurs de bureau, avant que l'encodeur ne voie le tampon — en se rappelant qu'ImageData n'est pas prémultiplié, et qu'un Uint8ClampedArray arrondit déjà à l'affectation.

À retenir

Le fil rouge n'est pas la difficulté des codecs, c'est le silence. Safari rend le mauvais format, target_size vous ignore, mozjpeg jette votre canal alpha, Vite vide votre file d'attente. L'auteur en tire une consigne inverse du réflexe habituel : un repli qui ment est pire que pas de repli du tout. Le sien échoue franchement sur Safari plutôt que d'y renvoyer un PNG déguisé.

Source : DEV Community