SVGcode: eine PWA zum Konvertieren von Rasterbildern in SVG-Vektorgrafiken

SVGcode ist eine progressive Web-App, mit der Sie Rasterbilder wie JPG, PNG, GIF, WebP, AVIF usw. in Vektorgrafiken im SVG-Format konvertieren können. Dabei werden die File System Access API, die Async Clipboard API, die File Handling API und die Anpassung der Window Controls Overlay API verwendet.

Wenn Sie lieber ein Video ansehen als zu lesen, ist dieser Artikel auch als Video verfügbar.

Von Raster zu Vektor

Haben Sie schon einmal ein Bild skaliert und das Ergebnis war pixelig und unbefriedigend? In diesem Fall haben Sie wahrscheinlich schon einmal mit einem Rasterbildformat wie WebP, PNG oder JPG gearbeitet.

Wenn Sie ein Rasterbild vergrößern, wird es pixelig.

Im Gegensatz dazu sind Vektorgrafiken Bilder, die durch Punkte in einem Koordinatensystem definiert sind. Diese Punkte sind durch Linien und Kurven verbunden, um Polygone und andere Formen zu bilden. Vektorgrafiken haben gegenüber Rastergrafiken den Vorteil, dass sie ohne Pixelung auf jede Auflösung skaliert werden können.

Skalieren eines Vektorbilds ohne Qualitätsverlust.

SVGcode

Ich habe eine PWA namens SVGcode entwickelt, mit der Sie Rasterbilder in Vektoren umwandeln können. Ehre, wo Anerkennung fällig ist: Das habe ich nicht erfunden. Bei SVGcode nutze ich einfach ein Befehlszeilentool namens Potrace von Peter Selinger, das ich in Web Assembly konvertiert habe, damit es in einer Webanwendung verwendet werden kann.

Screenshot der SVGcode-Anwendung
Die SVGcode App.

SVG-Code verwenden

Zuerst möchte ich Ihnen zeigen, wie Sie die App verwenden. Ich beginne mit dem Teaserbild für das Chrome Dev Summit, das ich vom Twitter-Kanal von ChromiumDev heruntergeladen habe. Das ist ein PNG-Rasterbild, das ich dann in die SVGcode App ziehe. Wenn ich die Datei ablege, wird das Bild farbweise nachgezeichnet, bis eine vektorisierte Version der Eingabe erscheint. Ich kann jetzt heranzoomen. Wie Sie sehen, bleiben die Kanten scharf. Beim Heranzoomen des Chrome-Logos sehen Sie jedoch, dass die Nachzeichnung nicht perfekt war und insbesondere die Umrisse des Logos ein wenig gesprengt waren. Ich kann das Ergebnis verbessern, indem ich Flecken bis zu einer Größe von etwa fünf Pixeln aus dem Tracing entferne.

Ein abgelegtes Bild in das SVG-Format konvertieren.

Posterisierung in SVG-Code

Ein wichtiger Schritt bei der Vektorisierung, insbesondere bei fotografischen Bildern, ist die Posterisierung des Eingabebilds, um die Anzahl der Farben zu reduzieren. Mit SVGcode kann ich das pro Farbkanal tun und das resultierende SVG sehen, während ich Änderungen vornehme. Wenn ich mit dem Ergebnis zufrieden bin, kann ich die SVG-Datei auf meiner Festplatte speichern und sie beliebig verwenden.

Posterisieren eines Bilds, um die Anzahl der Farben zu reduzieren.

In SVG-Code verwendete APIs

Nachdem Sie nun wissen, was die App alles kann, möchte ich Ihnen einige der APIs zeigen, die dazu beitragen.

Progressive Web-App

SVGcode ist eine installierbare progressive Web-App und daher vollständig offlinefähig. Die App basiert auf der Vanilla-JS-Vorlage für Vite.js und verwendet die beliebte Vite-Plug-in-PWA. Diese erstellt im Hintergrund einen Service Worker, der Workbox.js verwendet. Die Workbox besteht aus einer Reihe von Bibliotheken, die einen produktionsreifen Service Worker für progressive Web-Apps unterstützen können. Dieses Muster funktioniert möglicherweise nicht für alle Apps, aber für den Anwendungsfall von SVGcode ist es hervorragend.

Overlay für Fenstersteuerelemente

Um die verfügbare Bildschirmfläche zu maximieren, verwendet SVGcode die Anpassung Window Controls Overlay, indem das Hauptmenü in den Bereich der Titelleiste verschoben wird. Sie sehen, dass diese Funktion am Ende des Installationsvorgangs aktiviert wird.

SVG-Code installieren und die Anpassung des Overlays für die Fenstersteuerung aktivieren.

File System Access API

Zum Öffnen von Eingabebilddateien und Speichern der resultierenden SVGs verwende ich die File System Access API. So kann ich einen Verweis auf zuvor geöffnete Dateien behalten und dort weitermachen, wo ich aufgehört habe, auch wenn die App neu geladen wurde. Jedes Mal, wenn ein Bild gespeichert wird, wird es über die svgo-Bibliothek optimiert. Je nach Komplexität des SVG kann dies einen Moment dauern. Das Dialogfeld zum Speichern von Dateien muss durch eine Nutzergeste geöffnet werden. Daher ist es wichtig, den Dateihandle vor der SVG-Optimierung abzurufen, damit die Nutzergeste nicht ungültig wird, bis die optimierte SVG-Datei bereit ist.

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);
}

Drag-and-Drop

Zum Öffnen eines Eingabebilds kann ich entweder die Funktion zum Öffnen von Dateien verwenden oder, wie oben gezeigt, eine Bilddatei per Drag-and-drop in die App ziehen. Die Funktion zum Öffnen von Dateien ist ziemlich einfach, interessanter ist der Drag-and-drop-Fall. Besonders praktisch ist, dass Sie über die Methode getAsFileSystemHandle() einen Dateisystem-Handle vom Datenübertragungselement abrufen können. Wie bereits erwähnt, kann ich diesen Handle beibehalten, damit er bereit ist, wenn die App neu geladen wird.

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);
  }
});

Weitere Informationen finden Sie im Artikel zur File System Access API. Wenn Sie möchten, können Sie sich auch den SVG-Code in src/js/filesystem.js ansehen.

Async Clipboard API

SVG-Code ist außerdem über die Async Clipboard API vollständig in die Zwischenablage des Betriebssystems eingebunden. Sie können Bilder aus dem Dateimanager des Betriebssystems in die App einfügen, indem Sie entweder auf die Schaltfläche „Bild einfügen“ klicken oder auf der Tastatur die Tastenkombination „Befehl“ oder „Strg“ + „V“ drücken.

Ein Bild aus dem Dateimanager in SVG-Code einfügen.

Die Async Clipboard API kann seit Kurzem auch mit SVG-Bildern umgehen. Sie können also auch ein SVG-Bild kopieren und zur weiteren Verarbeitung in eine andere Anwendung einfügen.

Ein Bild aus SVGcode in SVGOMG kopieren.
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'));
});

Weitere Informationen finden Sie im Artikel zur asynchronen Zwischenablage oder in der Datei src/js/clipboard.js.

Dateiverwaltung

Eine der besten Funktionen von SVGcode ist, wie gut sich das Programm in das Betriebssystem einfügt. Als installierte PWA kann sie ein Datei-Handler oder sogar der Standarddatei-Handler für Bilddateien werden. Das bedeutet, dass ich auf meinem macOS-Computer im Finder mit der rechten Maustaste auf ein Bild klicken und es mit SVG-Code öffnen kann. Diese Funktion wird als Dateiverwaltung bezeichnet und basiert auf der Eigenschaft „file_handlers“ im Web-App-Manifest und der Startwarteschlange, über die die App die übergebene Datei verwenden kann.

Dateien mit der installierten SVGcode App auf dem Computer öffnen.
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;
    }
  }
});

Weitere Informationen finden Sie unter Installierte Webanwendungen als Dateihandler verwenden. Den Quellcode finden Sie in src/js/filehandling.js.

Webfreigabe (Dateien)

Ein weiteres Beispiel für die Einbindung in das Betriebssystem ist die Freigabefunktion der App. Angenommen, ich möchte eine mit SVGcode erstellte SVG-Datei bearbeiten. Eine Möglichkeit dazu wäre, die Datei zu speichern, die SVG-Bearbeitungs-App zu starten und die SVG-Datei dort zu öffnen. Die Web Share API bietet jedoch einen einfacheren Ablauf, da Dateien direkt freigegeben werden können. Wenn die SVG-Bearbeitungs-App ein Freigabeziel ist, kann sie die Datei direkt ohne Abweichung empfangen.

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);
      }
    }
  }
});
Ein SVG-Bild in Gmail teilen.

Ziel für die Webfreigabe (Dateien)

Umgekehrt kann SVG-Code auch als Freigabeziel dienen und Dateien von anderen Apps empfangen. Dazu muss die App dem Betriebssystem über die Web Share Target API mitteilen, welche Datentypen sie akzeptieren kann. Das geschieht über ein spezielles Feld im Web-App-Manifest.

{
  "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"]
        }
      ]
    }
  }
}

Die action-Route ist nicht tatsächlich vorhanden, wird aber ausschließlich im fetch-Handler des Service Workers verarbeitet, der empfangene Dateien dann zur eigentlichen Verarbeitung in der Anwendung übergibt.

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);
      })(),
    );
  }
});
Einen Screenshot als SVG-Code teilen.

Fazit

Das war ein kurzer Überblick über einige der erweiterten App-Funktionen in SVG-Code. Ich hoffe, dass diese App neben anderen erstklassigen Apps wie Squoosh oder SVGOMG zu einem unverzichtbaren Tool für Ihre Bildverarbeitungsanforderungen wird.

Der SVG-Code ist unter svgco.de verfügbar. Siehst du, was ich dort gemacht habe? Sie können sich den Quellcode auf GitHub ansehen. Da Potrace unter der GPL-Lizenz steht, gilt dies auch für SVGcode. Viel Spaß beim Vektorisieren! Ich hoffe, dass SVGcode Ihnen nützlich ist und dass einige seiner Funktionen Sie zu Ihrer nächsten App inspirieren.

Danksagungen

Dieser Artikel wurde von Joe Medley überprüft.