SVGcode: une PWA permettant de convertir des images matricielles en images vectorielles SVG

SVGcode est une progressive web app qui vous permet de convertir des images matricielles telles que JPG, PNG, GIF, WebP, AVIF, etc. en graphiques vectoriels au format SVG. Il utilise l'API File System Access, l'API Async Clipboard, l'API File Handling et la personnalisation de la superposition des commandes de fenêtre.

(Si vous préférez regarder cette vidéo plutôt que la lecture, cet article est également disponible sous forme de vidéo.)

D'une trame à un vecteur

Avez-vous déjà mis à l'échelle une image et le résultat était pixélisé et insatisfaisant ? Si c'est le cas, vous avez probablement eu à utiliser un format d'image matricielle tel que WebP, PNG ou JPG.

En augmentant la taille d'une image matricielle, elle semble pixélisée.

En revanche, les graphiques vectoriels sont des images définies par des points dans un système de coordonnées. Ces points sont reliés par des lignes et des courbes pour former des polygones et d'autres formes. Les graphiques vectoriels présentent un avantage par rapport aux graphiques matriciels, car ils peuvent être ajustés à la hausse ou à la baisse selon n'importe quelle résolution sans pixellisation.

Mise à l'échelle d'une image vectorielle sans perte de qualité.

Découvrez SVGcode

J'ai créé une PWA appelée SVGcode qui peut vous aider à convertir des images matricielles en vecteurs. Motif du crédit: je n'ai pas inventé ceci. Avec SVGcode, je me contente d'un outil de ligne de commande appelé Potrace de Peter Selinger que j'ai converti en Web Assembly afin de pouvoir l'utiliser dans une application Web.

Capture d'écran de l'application SVGcode.
L'application SVGcode

Utiliser le code SVG

Tout d'abord, je vais vous montrer comment utiliser l'application. Je commence par l'image d'accroche du Chrome Dev Summit que j'ai téléchargée sur la chaîne Twitter ChromiumDev. Il s'agit d'une image matricielle PNG que je fais ensuite glisser sur l'application SVGcode. Lorsque je dépose le fichier, l'application trace la couleur de l'image par couleur, jusqu'à ce qu'une version vectorisée de l'entrée apparaisse. Je peux zoomer sur l'image et, comme on peut le voir, les bords restent nets. Toutefois, en zoomant sur le logo Chrome, vous pouvez constater que le traçage n'était pas parfait et que les contours du logo sont un peu mouchetés. Je peux améliorer le résultat en supprimant les mouchetures du traçage en supprimant les mouchetures de jusqu'à cinq pixels, par exemple.

Conversion d'une image déposée au format SVG.

Posterisation au format SVGcode

Une étape importante de la vectorisation, en particulier pour les images photographiques, consiste à publier l'image d'entrée afin de réduire le nombre de couleurs. SVGcode me permet de le faire par canal de couleur et de voir le résultat SVG à mesure que j'apporte des modifications. Quand je suis satisfait du résultat, je peux enregistrer le SVG sur mon disque dur et l'utiliser où je veux.

Postérisation d'une image pour réduire le nombre de couleurs

API utilisées dans SVGcode

Maintenant que vous avez vu les fonctionnalités de l'application, voyons quelques-unes des API qui permettent de faire cette magie.

Progressive web app

SVGcode est une progressive web app installable et entièrement disponible hors connexion. L'application est basée sur le modèle Vanilla JS pour Vite.js et utilise la PWA du plug-in Vite, très populaire, qui crée en arrière-plan un service worker utilisant Workbox.js. Workbox est un ensemble de bibliothèques qui peuvent alimenter un service worker prêt pour la production pour les progressive web apps. Ce modèle peut ne pas fonctionner pour toutes les applications, mais pour le cas d'utilisation du SVGcode, c'est très bien.

Superposition des commandes de fenêtre

Pour optimiser l'espace disponible à l'écran, SVGcode utilise la personnalisation de la superposition des commandes de fenêtre en déplaçant son menu principal vers le haut dans la zone de la barre de titre. Vous pouvez voir que cela est activé à la fin du processus d'installation.

Installation du code SVG et activation de la personnalisation de la superposition des commandes de fenêtre.

API File System Access

Pour ouvrir les fichiers image d'entrée et enregistrer les fichiers SVG obtenus, j'utilise l'API File System Access. Cela me permet de conserver une référence aux fichiers ouverts précédemment et de reprendre là où je m'étais arrêté, même après l'actualisation d'une application. Chaque fois qu'une image est enregistrée, elle est optimisée via la bibliothèque svgo, ce qui peut prendre un moment, en fonction de la complexité du SVG. L'affichage de la boîte de dialogue d'enregistrement de fichiers nécessite un geste de l'utilisateur. Il est donc important d'obtenir le handle du fichier avant que l'optimisation SVG ne soit effectuée, afin que le geste de l'utilisateur ne soit pas invalidé au moment où le SVG optimisé est prêt.

try {
  let svg = svgOutput.innerHTML;
  let handle = null;
  // To not consume the user gesture obtain the handle before preparing the
  // blob, which may take longer.
  if (supported) {
    handle = await showSaveFilePicker({
      types: [{description: 'SVG file', accept: {'image/svg+xml': ['.svg']}}],
    });
  }
  showToast(i18n.t('optimizingSVG'), Infinity);
  svg = await optimizeSVG(svg);
  showToast(i18n.t('savedSVG'));
  const blob = new Blob([svg], {type: 'image/svg+xml'});
  await fileSave(blob, {description: 'SVG file'}, handle);
} catch (err) {
  console.error(err.name, err.message);
  showToast(err.message);
}

Glisser-déposer

Pour ouvrir une image d'entrée, je peux utiliser la fonctionnalité d'ouverture de fichier ou, comme vous l'avez vu ci-dessus, il suffit de faire glisser et de déposer un fichier image sur l'application. La fonctionnalité d'ouverture de fichier est assez simple, plus intéressante : le glisser-déposer. Ce qui est particulièrement intéressant, c'est que vous pouvez obtenir un handle de système de fichiers à partir de l'élément de transfert de données via la méthode getAsFileSystemHandle(). Comme indiqué précédemment, je peux conserver cet identifiant afin qu'il soit prêt lorsque l'application sera rechargée.

document.addEventListener('drop', async (event) => {
  event.preventDefault();
  dropContainer.classList.remove('dropenter');
  const item = event.dataTransfer.items[0];
  if (item.kind === 'file') {
    inputImage.addEventListener(
      'load',
      () => {
        URL.revokeObjectURL(blobURL);
      },
      {once: true},
    );
    const handle = await item.getAsFileSystemHandle();
    if (handle.kind !== 'file') {
      return;
    }
    const file = await handle.getFile();
    const blobURL = URL.createObjectURL(file);
    inputImage.src = blobURL;
    await set(FILE_HANDLE, handle);
  }
});

Pour en savoir plus, consultez l'article sur l'API File System Access et, si cela vous intéresse, étudiez le code source SVGcode dans src/js/filesystem.js.

API Async Clipboard

SVGcode est également entièrement intégré au presse-papiers du système d'exploitation via l'API Async Clipboard. Vous pouvez coller des images à partir de l'explorateur de fichiers du système d'exploitation dans l'application en cliquant sur le bouton "Coller l'image" ou en appuyant sur Commande ou Ctrl+V sur votre clavier.

Coller une image de l'explorateur de fichiers dans le code SVG.

L'API Async Clipboard permet également de gérer les images SVG. Vous pouvez donc également copier une image SVG et la coller dans une autre application pour un traitement ultérieur.

Copier une image depuis SVGcode dans SVGOMG.
copyButton.addEventListener('click', async () => {
  let svg = svgOutput.innerHTML;
  showToast(i18n.t('optimizingSVG'), Infinity);
  svg = await optimizeSVG(svg);
  const textBlob = new Blob([svg], {type: 'text/plain'});
  const svgBlob = new Blob([svg], {type: 'image/svg+xml'});
  navigator.clipboard.write([
    new ClipboardItem({
      [svgBlob.type]: svgBlob,
      [textBlob.type]: textBlob,
    }),
  ]);
  showToast(i18n.t('copiedSVG'));
});

Pour en savoir plus, consultez l'article Presse-papiers asynchrone ou accédez au fichier src/js/clipboard.js.

Traitement des fichiers

L'une de mes caractéristiques préférées du code SVG est sa capacité à s'intégrer parfaitement au système d'exploitation. En tant que PWA installée, elle peut devenir un gestionnaire de fichiers, voire le gestionnaire de fichiers par défaut, pour les fichiers image. Cela signifie que lorsque je me trouve dans le Finder de mon ordinateur macOS, je peux effectuer un clic droit sur une image et l'ouvrir avec le code SVG. Cette fonctionnalité, appelée "File Handling" (Gestion des fichiers), fonctionne en fonction de la propriété file_handlers dans le fichier manifeste de l'application Web et dans la file d'attente de lancement, qui permet à l'application d'utiliser le fichier transmis.

Ouverture d'un fichier à partir du bureau avec l'application SVGcode installée.
window.launchQueue.setConsumer(async (launchParams) => {
  if (!launchParams.files.length) {
    return;
  }
  for (const handle of launchParams.files) {
    const file = await handle.getFile();
    if (file.type.startsWith('image/')) {
      const blobURL = URL.createObjectURL(file);
      inputImage.addEventListener(
        'load',
        () => {
          URL.revokeObjectURL(blobURL);
        },
        {once: true},
      );
      inputImage.src = blobURL;
      await set(FILE_HANDLE, handle);
      return;
    }
  }
});

Pour en savoir plus, consultez Autoriser les applications Web installées à devenir gestionnaires de fichiers et affichez le code source dans src/js/filehandling.js.

Partage Web (fichiers)

Un autre exemple d’intégration avec le système d’exploitation est la fonction de partage de l’application. Supposons que je souhaite apporter des modifications à un SVG créé avec SVGcode. Pour résoudre ce problème, vous pouvez enregistrer le fichier, lancer l'application de retouche SVG, puis ouvrir le fichier SVG à partir de là. Cependant, un flux plus fluide consiste à utiliser l'API Web Share, qui permet de partager directement des fichiers. Ainsi, si l'application de modification SVG est une cible de partage, elle peut recevoir directement le fichier sans déviation.

shareSVGButton.addEventListener('click', async () => {
  let svg = svgOutput.innerHTML;
  svg = await optimizeSVG(svg);
  const suggestedFileName =
    getSuggestedFileName(await get(FILE_HANDLE)) || 'Untitled.svg';
  const file = new File([svg], suggestedFileName, { type: 'image/svg+xml' });
  const data = {
    files: [file],
  };
  if (navigator.canShare(data)) {
    try {
      await navigator.share(data);
    } catch (err) {
      if (err.name !== 'AbortError') {
        console.error(err.name, err.message);
      }
    }
  }
});
Partager une image SVG avec Gmail

Cible de partage Web (fichiers)

À l'inverse, SVGcode peut également servir de cible de partage et recevoir des fichiers provenant d'autres applications. Pour que cela fonctionne, l'application doit indiquer au système d'exploitation via l'API Web Share Target les types de données qu'elle peut accepter. Pour ce faire, un champ dédié est disponible dans le fichier manifeste de l'application Web.

{
  "share_target": {
    "action": "https://svgco.de/share-target/",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "files": [
        {
          "name": "image",
          "accept": ["image/jpeg", "image/png", "image/webp", "image/gif"]
        }
      ]
    }
  }
}

La route action n'existe pas réellement, mais elle est gérée uniquement dans le gestionnaire fetch du service worker, qui transmet ensuite les fichiers reçus pour traitement dans l'application.

self.addEventListener('fetch', (fetchEvent) => {
  if (
    fetchEvent.request.url.endsWith('/share-target/') &&
    fetchEvent.request.method === 'POST'
  ) {
    return fetchEvent.respondWith(
      (async () => {
        const formData = await fetchEvent.request.formData();
        const image = formData.get('image');
        const keys = await caches.keys();
        const mediaCache = await caches.open(
          keys.filter((key) => key.startsWith('media'))[0],
        );
        await mediaCache.put('shared-image', new Response(image));
        return Response.redirect('./?share-target', 303);
      })(),
    );
  }
});
Partage d'une capture d'écran en SVGcode.

Conclusion

Très bien, voici une présentation rapide de certaines fonctionnalités d'application avancées dans SVGcode. J'espère que cette application pourra devenir un outil essentiel pour vos besoins de traitement d'images, aux côtés d'autres applications incroyables comme Squoosh ou SVGOMG.

Le format SVG est disponible sur svgco.de. Tu vois ce que j'ai fait ? Vous pouvez examiner son code source sur GitHub. Notez que comme Potrace est sous licence GPL, SVGcode est également appliqué. Bonne vectorisation ! J'espère que le SVGcode vous sera utile et que certaines de ses fonctionnalités pourront inspirer votre prochaine application.

Remerciements

Cet article a été examiné par Joe Medley.