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 umwandeln können. Dabei werden die File System Access API, die Async Clipboard API, die File Handling API und die Anpassung des Window Controls Overlays verwendet.

Wenn du lieber schauen möchtest, statt zu lesen, kannst du diesen Artikel auch als Video ansehen.

Von Raster zu Vektor

Haben Sie schon einmal ein Bild skaliert und das Ergebnis war verpixelt und nicht zufriedenstellend? In diesem Fall kennen Sie sich wahrscheinlich mit einem Rasterbildformat wie WebP, PNG oder JPG aus.

Wenn Sie ein Rasterbild hochskalieren, sieht es pixelig aus.

Vektorgrafiken sind hingegen Bilder, die durch Punkte in einem Koordinatensystem definiert werden. Diese Punkte sind durch Linien und Kurven zu Polygonen und anderen Formen verbunden. Vektorgrafiken haben gegenüber Rastergrafiken den Vorteil, dass sie auf eine beliebige Auflösung ohne Verpixelung skaliert werden können.

Vektorbild ohne Qualitätsverlust skalieren.

Jetzt neu: SVGcode

Ich habe eine PWA namens SVGcode erstellt, mit der Sie Rasterbilder in Vektoren umwandeln können. Anerkennung für die Mitwirkenden: Ich habe das nicht erfunden. Mit SVGcode stehe ich einfach auf den Schultern eines Befehlszeilentools 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 App SVGcode.

SVG-Code verwenden

Zuerst möchte ich dir zeigen, wie die App funktioniert. Ich beginne mit dem Teaser-Image vom Chrome Dev Summit, den ich vom ChromiumDev Twitter-Kanal heruntergeladen habe. Dies ist ein PNG-Rasterbild, das ich dann in die SVGcode-App ziehe. Wenn ich die Datei ablege, verfolgt die App die Bildfarbe nach Farbe, bis eine vektorisierte Version der Eingabe angezeigt wird. Jetzt kann ich das Bild heranzoomen. Die Ränder bleiben scharf. Beim Heranzoomen des Chrome-Logos stellen Sie jedoch fest, dass das Tracing nicht perfekt war und vor allem die Umrisse des Logos ein wenig gesprengt sind. Das Ergebnis kann ich verbessern, indem ich Speppen von bis zu fünf Pixeln unterdrücke.

Ein gesetztes Bild in SVG konvertieren.

Posterisierung in SVGcode

Ein wichtiger Schritt bei der Vektorisierung, insbesondere bei fotografischen Bildern, besteht darin, das Eingabebild zu posten, um die Anzahl der Farben zu reduzieren. Mit SVGcode kann ich dies für jeden Farbkanal tun und das resultierende SVG sehen, wenn ich Änderungen vornehme. Wenn ich mit dem Ergebnis zufrieden bin, kann ich die SVG-Datei auf meiner Festplatte speichern und überall dort verwenden, wo ich möchte.

Posterisieren von Bildern, um die Anzahl der Farben zu reduzieren.

In SVGcode verwendete APIs

Nachdem Sie nun gesehen haben, was die App kann, möchte ich Ihnen einige der APIs zeigen, mit denen das möglich ist.

Progressive Web-App

SVGcode ist eine installierbare progressive Web-App, die vollständig offline aktiviert werden kann. Die App basiert auf der Vanilla JS-Vorlage für Vite.js und verwendet das beliebte Vite-Plug-in PWA, das einen Service Worker erstellt, der Workbox.js im Hintergrund verwendet. 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 unbedingt für alle Apps, für SVGcode-Anwendungsfall ist es jedoch hervorragend.

Overlay für Fenstersteuerelemente

Um den verfügbaren Platz auf dem Bildschirm zu maximieren, verwendet SVGcode die Anpassung des Overlays für Fenstersteuerelemente, indem das Hauptmenü nach oben in den Bereich der Titelleiste verschoben wird. Die Aktivierung wird am Ende der Installation angezeigt.

SVG-Code wird installiert und die Anpassung des Overlay für Fenstersteuerelemente aktiviert.

File System Access API

Um die eingegebenen Bilddateien zu öffnen und die resultierenden SVGs zu speichern, verwende ich die File System Access API. So kann ich auf zuvor geöffnete Dateien verweisen und dort weitermachen, wo ich aufgehört habe, auch nach einem Neuladevorgang der App. Immer, wenn ein Bild gespeichert wird, wird es mithilfe der svgo-Bibliothek optimiert. Dies kann je nach Komplexität des SVG einen Moment dauern. Zum Anzeigen des Dialogfelds zum Speichern der Datei ist eine Nutzergeste erforderlich. Daher ist es wichtig, das Datei-Handle vor der SVG-Optimierung abzurufen, damit die Touch-Geste für den Nutzer nicht ungültig wird, wenn das optimierte SVG 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 Eingabebildes kann ich entweder die Funktion zum Öffnen von Dateien verwenden oder, wie Sie oben gesehen haben, einfach eine Bilddatei per Drag-and-drop in die App ziehen. Die Funktion zum Öffnen von Dateien ist ziemlich geradlinig, interessanter als Drag-and-drop. Das Besondere daran ist, dass Sie mit der Methode getAsFileSystemHandle() ein Dateisystem-Handle aus dem Datenübertragungselement abrufen können. Wie bereits erwähnt, kann ich diesen Alias 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. Außerdem können Sie sich bei Interesse den SVG-Quellcode in src/js/filesystem.js ansehen.

Async Clipboard API

SVGcode ist über die Async Clipboard API außerdem vollständig in die Zwischenablage des Betriebssystems integriert. Sie können Bilder aus dem Datei-Explorer des Betriebssystems in die Anwendung einfügen. Klicken Sie dazu entweder auf die Schaltfläche „Bild einfügen“ oder drücken Sie die Befehlstaste oder Strg + V auf Ihrer Tastatur.

Ein Bild aus dem Datei-Explorer in SVGcode einfügen.

Mit der Async Clipboard API können seit Kurzem auch SVG-Bilder verarbeitet werden. Sie können also ein SVG-Bild auch 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 Asynchrone Zwischenablage oder in der Datei src/js/clipboard.js.

Dateihandhabung

Eine meiner Lieblingsfunktionen von SVGcode ist die Einbindung in das Betriebssystem. Als installierte PWA kann sie ein Datei-Handler oder sogar der Standard-Datei-Handler für Bilddateien werden. Das bedeutet, dass ich im Finder auf meinem macOS-Computer mit der rechten Maustaste auf ein Bild klicken und es mit SVGcode öffnen kann. Diese Funktion wird als Dateiverarbeitung bezeichnet und basiert auf der Eigenschaft „file_handlers“ im Web-App-Manifest und auf der Startwarteschlange, mit der die Anwendung die übergebene Datei verarbeiten kann.

Eine Datei auf dem Desktop öffnen, auf der die SVGcode App installiert ist.
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 Datei-Handler verwenden. Den Quellcode finden Sie in src/js/filehandling.js.

Web Share (Dateien)

Ein weiteres Beispiel für die Integration in das Betriebssystem ist die Freigabefunktion der App. Angenommen, ich möchte eine mit SVGcode erstellte SVG bearbeiten. Eine Möglichkeit dafür wäre, die Datei zu speichern, die SVG-Bearbeitungs-App zu starten und die SVG-Datei von dort zu öffnen. Ein reibungsloserer Ablauf ist jedoch die Verwendung der Web Share API, mit der Dateien direkt freigegeben werden können. Wenn also die SVG-Bearbeitungs-App ein Ziel zum Teilen ist, kann sie die Datei direkt und 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);
      }
    }
  }
});
SVG-Bild in Gmail teilen.

Web Share Target (Dateien)

Umgekehrt kann SVG-Code auch zum Teilen von Inhalten genutzt werden und Dateien von anderen Apps empfangen. Dazu muss die App dem Betriebssystem über die Web Share Target API mitteilen, welche Datentypen sie akzeptieren kann. Dies 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 Route action existiert nicht, sondern wird ausschließlich im fetch-Handler des Service Workers verarbeitet, der dann empfangene Dateien zur tatsächlichen 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);
      })(),
    );
  }
});
Screenshot mit SVGcode teilen.

Fazit

Alles klar, das war eine kurze Tour durch einige der erweiterten App-Funktionen in SVGcode. Ich hoffe, dass diese App neben anderen tollen Apps wie Squoosh oder SVGOMG zu einem unverzichtbaren Tool für Ihre Bildverarbeitung werden kann.

SVGcode ist unter svgco.de verfügbar. Sehen Sie, was ich dort gemacht habe? Sie können den Quellcode auf GitHub prüfen. Da Potrace GPL-lizenziert ist, gilt dies auch für SVGcode. Viel Spaß beim Vektoren! Ich hoffe, SVGcode ist nützlich und Sie inspirieren Ihre nächste App.

Danksagungen

Dieser Artikel wurde von Joe Medley geprüft.