svg 2 png grunt workflow

Vektorgrafiken sind toll. Man kann sie unendlich skalieren und der Detailgrad bleibt immer gleich gut. Auf HiDPI((HiDPI – High DPI Bildschirme, sind Bildschirme deren Device Pixel Ratio > 1 ist, also ein berechnetes Pixel von mehreren dargestellt wird und daher ein feineres Bild entsteht)) Displays sind sie die beste Lösung wenn es darum geht einfache Grafiken darzustellen. Photorealismus ist mit Vektorgrafiken allerdings nicht effizient zu erreichen. Hier ist weiterhin progressive JPEG oder ähnliches die beste Wahl.

Auch die Renderperformance sollte beachtet werden. In einem Projekt im Sommer 2013 habe ich am Ende alle SVGs wieder rausgenommen, da in diesem Artikel((Data URI Performance: Don’t Blame it on Base64 | Mobify)) die Rendergeschwindigkeit von SVGs auf mobilen Geräten unter dem Punkt „Secondary Result: SVGs are Surprisingly Slow!“ generell in Frage gestellt wurde. Und damals ging es um die Renderperformance…

Pro & Contrao SVG

Nun ist dieser Beitrag ja aus dem Sommer 2013 und alle mobilen Geräte wurden seitdem leistungsfähiger?! Natürlich nicht. Die neueren Android Versionen((Developer Dashboard Android)) setzen sich ja eigentlich nur per Neuerwerb eines Smartphones durch. Daher ist es zwar toll dass über 40% Kitkat((Android Version 4.4.x)) und neuer((Lollipop, Android 5.0)) besitzen, knapp 45% auf JB((Jelly Bean, Android 4.1.x, 4.2.x und 4.3)) festhängen und somit 15% ICS((Ice Cream Sandwitch, Android 4.0.3, Android 4.0.4)), GB((Ginger Bread, Android 2.3.3 – 2.3.7)) oder Froyo((Android 2.2)) nutzen. Geräte ab JB abwärts können gut und gerne 2 Jahre und älter sein, es ergibt sich also 60% nicht so leistungsfähige Geräte im Android Revier.

Andere Plattformen haben ähnliche Probleme, auch wenn sich neue Betriebssystemversionen besser durchsetzen, sind die Geräte von der Hardware nicht mit aktualisiert worden. Mit einem Cutting Edge Responsive User Experience kann man also auch manchen Besucher vegraulen, wenn das Endgerät überfordert wird.

Und es gibt noch weitere Dinge die berücksichtigt werden wollen – um eins zu nennen: Der IE8((Internet Explorer 8)). Hier fehlt komplett die Unterstützung für SVGs. Natürlich kann man ein Polyfill((github.com – HTML5 Cross Browser Polyfills)) nutzen, aber auch diese wirken negativ auf die Renderperformance, wenn zum Beispiel jedes SVG per Flash((svgweb)) dargestellt wird.

In einer Quelle((Browser – Versionen)) kommt der IE8 auf 9% der IE Nutzer – also knapp jeder 10te Internet Explorer Besucher nutzt den IE8. Eine Andere kommt auf gesamt 3.75%((de statista – Browser Januar 2015)). Wenn man an einem Rebrush sitzt, sollten alle Analytics Daten angeschaut werden um abschätzen zu können, welche Stammbesucher nach einem Rebrush ausgeschlossen werden könnten – häufig weicht die Zielgruppe vom nationalen oder internationalen Durchschnitt ab!

Also… …zum Thema

Nach diesen – zugegeben etwas ausschweifenden – Gedanken warum SVGs nicht unbedingt das Mittel der Wahl sein müssen, komme ich doch noch zum Thema: einem grunt((npmjs.com – grunt)) SVG zu PNG Arbeitsablauf, der nicht auf Sprites und Icons setzt, sondern verschieden große SVGs als PNGs ausspuckt.

Neben Icons (die als Webfont besser aufgehoben sind) kommt es vor, dass ein Logo in verschiedenen Größen/Versionen ausgespielt wird oder andere größere Schmuck-Grafiken.

Die Suche scheint schnell zu Ende sein: grunt-svg2png((npmjs.com – grunt-svg2png)) doch unerwartete Ergebnisse trüben die Freude. Das erzeugte PNG stimmt in seinen Pixelmaßen nicht mit dem Ursprungssvg überein…

Die Quell-SVGs definieren ihre Größe per viewBox((MDN – viewBox SVG)) Attribut und für die responsive Darstellung ist dies auch genau richtig. Aber svg2png erzeugt erst die erwarteten PNG Dateien mit den richtigen Pixelmaßen, nachdem ich testweise händisch ein width((MDN – width SVG)) und height((MDN – height SVG)) Attribut hinzugefügt hatte. Durch diesen Eingriff waren die SVGs aber nicht mehr richtig skalierbar, also eine Sackgasse…
Andere Ableger des grunt-svg2png (grunt-svg-to-png((npmjs.com – grunt-svg-to-png))) Task halfen auch nicht.

Man benötigt einen Weg der automatisch aus dem viewBox Attribut die Breite und Höhe ausliest, in das Dokument schreibt, ein PNG erstellt und dann wieder entfernt für den weiteren Einsatz der SVG Dateien…

Ein Schritt auf diesem Weg bildet der grunt-check-svg-dimensions((npmjs.com – grunt-check-svg-dimensions)) Task. Dieser öffnet das SVG in einem XML DOM Object, liest die Höhe und Breite aus und schreibt diese als Attribute in die Datei und speichert die Datei wieder… Allerdings mit der kleinen Einschränkung dass nach dem Speichern das viewBox Attribut viewbox lautet und jetzt der svg2png Task nicht mehr damit arbeiten kann.
Im node_modules Ordner liegen ja alle Quelltexte, daher habe ich versucht das Problem zu lösen, doch leider ohne Erfolg. Der XML Parser machte immer aus viewBox wieder viewbox.

Zum Glück sind SVG Dateien im Endeffekt Textdateien. Anstatt das Problem richtig zu lösen, mit Pull Request etc. kann man den quick and dirty Weg nehmen und den Task grunt-text-replace((npmjs.com – grunt-text-replace)) nutzen um aus viewbox wieder viewBox zu machen.

Eines habe ich noch vergessen, bevor grunt-check-dimensions loslegt habe ich die SVG Dateien noch per grunt-contrib-copy((npmjs.com – grunt-contrib-copy)) kopiert und nach erfolgreicher Verarbeitung mit grunt-contrib-clean((npmjs.com – grunt-contrib-clean)) wieder gelöscht.

Der grunt Ablauf

Der komplette Ablauf sieht also so aus:

  1. SVG Dateien an einen temporären Ort kopieren
  2. kopierte SVG Daten mit width und height Attribut anreichern
  3. viewbox suchen und mit viewBox ersetzen
  4. svg2png arbeiten lassen
  5. temporäre SVG Dateien wieder löschen

Die Gruntfile Tasks lesen sich wie folgt, alle Pfade werden in der package.json konfiguriert:

copy: {
            main: {
                files: [{
                    expand: true,
                    cwd: "< %= pkg.paths.imgSrcFolder %>",
                    src: ["*.svg"],
                    dest: "< %= pkg.paths.imgSrcFolder %>tmp",
                    filter: "isFile"
                }]
            }
        },
 
        "check-svg-dimensions": {
            all: {
                src: [
                    "< %= pkg.paths.imgSrcFolder %>tmp/*.svg"
                ]
            }
        },
 
        replace: {
            fixViewBox: {
                src: ["< %= pkg.paths.imgSrcFolder %>tmp/*.svg"],
                dest: "< %= pkg.paths.imgSrcFolder %>tmp/",
                replacements: [{
                  from: "viewbox",
                  to: "viewBox"
                }]
            }
        },
 
        svg2png: {
            all: {
                files: [
                    {
                        cwd: "< %= pkg.paths.imgSrcFolder %>tmp/",
                        src: ["*.svg"],
                        dest: "< %= pkg.paths.imgDestFolder >"
                      }
                ]
            }
        },
 
        clean: ["< %= copy.main.files[0].dest %>"],
...
 
grunt.registerTask("svg-png-handling", ["copy", "check-svg-dimensions", "replace:fixViewBox", "svg2png", "clean"]);

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.