🚀 PWA & Favicon Generator Pro

Komplettes Tool zur Erstellung von Progressive Web Apps mit Favicon-Generator, manifest.json, Service Worker und vollständiger SEO-Optimierung

📱 Favicon & Icon Generator

📤

Bild hier hineinziehen

oder klicken Sie, um eine Datei auszuwählen

Unterstützte Formate: PNG, JPG, SVG, WebP

⚙️ PWA-Konfiguration

manifest.json

Die manifest.json-Datei definiert, wie sich Ihre PWA-App nach der Installation verhält.

{
  "name": "Meine PWA-App",
  "short_name": "MeinePWA",
  "description": "Professionelle Progressive Web App",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#2196F3",
  "orientation": "any",
  "icons": [
    {
      "src": "/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

Service Worker

Der Service Worker ermöglicht Offline-Funktionalität und Ressourcen-Caching.

// PWA Service Worker mit erweiterten Funktionen
const CACHE_NAME = 'pwa-cache-v1';
const RUNTIME_CACHE = 'runtime-cache-v1';
const IMAGE_CACHE = 'image-cache-v1';

// Zu cachende Ressourcen
const STATIC_ASSETS = [
  '/',
  '/index.html',
  '/styles.css',
  '/script.js',
  '/manifest.json',
  '/offline.html',
  // Icons
  '/icon-192x192.png',
  '/icon-512x512.png',
  '/apple-touch-icon.png'
];

// Cache-Strategien
const CACHE_STRATEGIES = {
  networkFirst: [
    /\/api\//,
    /\/data\//
  ],
  cacheFirst: [
    /\.(?:png|jpg|jpeg|svg|gif|webp|ico)$/,
    /\.(?:woff|woff2|ttf|otf|eot)$/,
    /\.(?:css|js)$/
  ],
  staleWhileRevalidate: [
    /\.(?:json)$/,
    /\/content\//
  ]
};

// Installation
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => {
        console.log('Service Worker: Caching statische Assets');
        return cache.addAll(STATIC_ASSETS);
      })
      .then(() => self.skipWaiting())
  );
});

// Aktivierung
self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (cacheName !== CACHE_NAME &&
              cacheName !== RUNTIME_CACHE &&
              cacheName !== IMAGE_CACHE) {
            console.log('Service Worker: Lösche alten Cache:', cacheName);
            return caches.delete(cacheName);
          }
        })
      );
    }).then(() => self.clients.claim())
  );
});

// Fetch-Strategien
self.addEventListener('fetch', event => {
  const { request } = event;
  const url = new URL(request.url);

  // Ignore non-HTTP(S) requests
  if (!url.protocol.startsWith('http')) return;

  // Determine strategy
  if (isNetworkFirst(url)) {
    event.respondWith(networkFirst(request));
  } else if (isCacheFirst(url)) {
    event.respondWith(cacheFirst(request));
  } else if (isStaleWhileRevalidate(url)) {
    event.respondWith(staleWhileRevalidate(request));
  } else {
    event.respondWith(networkFirst(request));
  }
});

// Network First Strategy
async function networkFirst(request) {
  try {
    const networkResponse = await fetch(request);
    if (networkResponse.ok) {
      const cache = await caches.open(RUNTIME_CACHE);
      cache.put(request, networkResponse.clone());
    }
    return networkResponse;
  } catch (error) {
    const cachedResponse = await caches.match(request);
    if (cachedResponse) {
      return cachedResponse;
    }

    // Offline fallback
    if (request.destination === 'document') {
      return caches.match('/offline.html');
    }

    throw error;
  }
}

// Cache First Strategy
async function cacheFirst(request) {
  const cachedResponse = await caches.match(request);
  if (cachedResponse) {
    return cachedResponse;
  }

  try {
    const networkResponse = await fetch(request);
    if (networkResponse.ok) {
      const cacheName = request.destination === 'image' ? IMAGE_CACHE : CACHE_NAME;
      const cache = await caches.open(cacheName);
      cache.put(request, networkResponse.clone());
    }
    return networkResponse;
  } catch (error) {
    // Return placeholder for images
    if (request.destination === 'image') {
      return caches.match('/placeholder.png');
    }
    throw error;
  }
}

// Stale While Revalidate Strategy
async function staleWhileRevalidate(request) {
  const cachedResponse = await caches.match(request);

  const fetchPromise = fetch(request).then(networkResponse => {
    if (networkResponse.ok) {
      const cache = caches.open(RUNTIME_CACHE);
      cache.then(cache => cache.put(request, networkResponse.clone()));
    }
    return networkResponse;
  });

  return cachedResponse || fetchPromise;
}

// Helper functions
function isNetworkFirst(url) {
  return CACHE_STRATEGIES.networkFirst.some(regex => regex.test(url.pathname));
}

function isCacheFirst(url) {
  return CACHE_STRATEGIES.cacheFirst.some(regex => regex.test(url.pathname));
}

function isStaleWhileRevalidate(url) {
  return CACHE_STRATEGIES.staleWhileRevalidate.some(regex => regex.test(url.pathname));
}

// Background Sync
self.addEventListener('sync', event => {
  if (event.tag === 'sync-data') {
    event.waitUntil(syncData());
  }
});

async function syncData() {
  try {
    const cache = await caches.open('sync-cache');
    const requests = await cache.keys();

    for (const request of requests) {
      try {
        const response = await fetch(request);
        if (response.ok) {
          await cache.delete(request);
        }
      } catch (error) {
        console.error('Sync failed for:', request.url);
      }
    }
  } catch (error) {
    console.error('Background sync error:', error);
  }
}

// Push Notifications
self.addEventListener('push', event => {
  const options = {
    body: event.data ? event.data.text() : 'Neue Benachrichtigung',
    icon: '/icon-192x192.png',
    badge: '/icon-96x96.png',
    vibrate: [200, 100, 200],
    data: {
      dateOfArrival: Date.now(),
      primaryKey: 1
    },
    actions: [
      {
        action: 'explore',
        title: 'Öffnen',
        icon: '/icon-check.png'
      },
      {
        action: 'close',
        title: 'Schließen',
        icon: '/icon-close.png'
      }
    ]
  };

  event.waitUntil(
    self.registration.showNotification('PWA Notification', options)
  );
});

// Notification Click
self.addEventListener('notificationclick', event => {
  event.notification.close();

  if (event.action === 'explore') {
    event.waitUntil(
      clients.openWindow('/')
    );
  }
});

// Periodic Background Sync (wenn verfügbar)
self.addEventListener('periodicsync', event => {
  if (event.tag === 'content-sync') {
    event.waitUntil(updateContent());
  }
});

async function updateContent() {
  try {
    const response = await fetch('/api/content/latest');
    const data = await response.json();

    // Update cache with new content
    const cache = await caches.open(RUNTIME_CACHE);
    await cache.put('/api/content/latest', new Response(JSON.stringify(data)));

    // Show notification if new content
    if (data.hasUpdates) {
      self.registration.showNotification('Neue Inhalte verfügbar', {
        body: 'Tippen Sie hier, um die neuesten Updates zu sehen',
        icon: '/icon-192x192.png'
      });
    }
  } catch (error) {
    console.error('Content update failed:', error);
  }
}

// Message handling
self.addEventListener('message', event => {
  if (event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }

  if (event.data.type === 'CLEAR_CACHE') {
    event.waitUntil(
      caches.keys().then(cacheNames => {
        return Promise.all(
          cacheNames.map(cacheName => caches.delete(cacheName))
        );
      })
    );
  }
});

HTML-Code

Vollständiger HTML-Code mit allen Meta-Tags für PWA und SEO.

<!-- PWA Meta Tags -->
<meta name="theme-color" content="#2196F3">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="MeinePWA">

<!-- Favicon -->
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#2196F3">

<!-- Manifest -->
<link rel="manifest" href="/manifest.json">

<!-- Service Worker Registrierung -->
<script>
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then(reg => console.log('Service Worker registriert'))
      .catch(err => console.error('SW-Registrierungsfehler:', err));
  });
}
</script>

SEO & Schema.org

Optimierung für Suchmaschinen und AI Overview.

<!-- Erweiterte SEO & AI Overview Optimierung -->
<!-- Primary Meta Tags -->
<meta name="title" content="PWA Generator - Progressive Web App Tools">
<meta name="description" content="Professioneller PWA & Favicon Generator. Erstellen Sie Icons in allen Größen, manifest.json, Service Worker. Optimiert für SEO, SGE und AI Overview.">
<meta name="keywords" content="PWA, Progressive Web App, Favicon Generator, manifest.json, Service Worker, Web App Icons, SEO, AI Overview, SGE">
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1">
<meta name="language" content="de">
<meta name="author" content="PWA Generator Pro">
<meta name="publisher" content="PWA Generator Pro">
<meta name="copyright" content="2025 PWA Generator Pro">
<meta name="rating" content="general">
<meta name="distribution" content="global">
<link rel="canonical" href="https://example.com/pwa-generator">

<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://example.com/pwa-generator">
<meta property="og:title" content="PWA & Favicon Generator - Komplettes Tool">
<meta property="og:description" content="Erstellen Sie professionelle PWA-Icons und Konfigurationen">
<meta property="og:image" content="https://example.com/og-image.jpg">
<meta property="og:image:alt" content="PWA Generator Screenshot">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:locale" content="de_DE">
<meta property="og:site_name" content="PWA Generator Pro">

<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://example.com/pwa-generator">
<meta property="twitter:title" content="PWA & Favicon Generator">
<meta property="twitter:description" content="Professionelle PWA-Tools">
<meta property="twitter:image" content="https://example.com/twitter-image.jpg">
<meta property="twitter:creator" content="@pwagenerator">

<!-- Schema.org für Google+ -->
<meta itemprop="name" content="PWA & Favicon Generator">
<meta itemprop="description" content="Komplettes Tool für PWA-Entwicklung">
<meta itemprop="image" content="https://example.com/schema-image.jpg">

<!-- Extended Schema.org Structured Data -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "WebApplication",
      "@id": "https://example.com/pwa-generator#webapp",
      "url": "https://example.com/pwa-generator",
      "name": "PWA & Favicon Generator Pro",
      "description": "Professioneller Generator für Progressive Web App Icons, Favicons, manifest.json und Service Worker. Unterstützt alle Plattformen und Geräte.",
      "applicationCategory": "DeveloperApplication",
      "operatingSystem": "Any",
      "browserRequirements": "Requires JavaScript. Requires HTML5.",
      "softwareVersion": "2.0",
      "datePublished": "2023-01-01",
      "dateModified": "2025-06-17",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "EUR",
        "availability": "https://schema.org/InStock"
      },
      "featureList": [
        "Favicon-Generierung in allen Größen (16x16 bis 512x512)",
        "Apple Touch Icons für alle iOS-Geräte",
        "Android Chrome Icons mit Maskable Support",
        "Windows Tile Images",
        "Safari Pinned Tab SVG",
        "manifest.json Generator mit erweiterten PWA-Features",
        "Service Worker mit Offline-Support",
        "Bildbearbeitung mit Cropper.js",
        "PHP 8.4 Backend für serverseitige Verarbeitung",
        "WebP und ICO Format-Unterstützung",
        "iOS Splash Screens für alle Geräte",
        "SEO und AI Overview Optimierung"
      ],
      "screenshot": [
        {
          "@type": "ImageObject",
          "url": "https://example.com/screenshot-main.jpg",
          "caption": "PWA Generator Hauptansicht"
        },
        {
          "@type": "ImageObject",
          "url": "https://example.com/screenshot-editor.jpg",
          "caption": "Icon-Editor mit Cropper.js"
        }
      ],
      "aggregateRating": {
        "@type": "AggregateRating",
        "ratingValue": "4.9",
        "reviewCount": "2847",
        "bestRating": "5",
        "worstRating": "1"
      },
      "creator": {
        "@type": "Organization",
        "name": "PWA Generator Pro",
        "url": "https://example.com",
        "logo": {
          "@type": "ImageObject",
          "url": "https://example.com/logo.png"
        }
      }
    },
    {
      "@type": "FAQPage",
      "@id": "https://example.com/pwa-generator#faq",
      "mainEntity": [
        {
          "@type": "Question",
          "name": "Was ist eine Progressive Web App (PWA)?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Eine Progressive Web App ist eine Webanwendung, die moderne Web-Technologien nutzt, um Nutzern ein app-ähnliches Erlebnis zu bieten. PWAs können offline funktionieren, auf dem Homescreen installiert werden und Push-Benachrichtigungen senden."
          }
        },
        {
          "@type": "Question",
          "name": "Welche Icon-Größen werden für PWAs benötigt?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Für maximale Kompatibilität sollten Icons in folgenden Größen bereitgestellt werden: 16x16, 32x32, 48x48, 72x72, 96x96, 128x128, 144x144, 152x152, 192x192, 256x256, 384x384 und 512x512 Pixel. Zusätzlich werden spezielle Größen für Apple Touch Icons (bis 180x180) und Windows Tiles benötigt."
          }
        },
        {
          "@type": "Question",
          "name": "Wie optimiere ich meine PWA für SEO?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Für optimale SEO sollten Sie: 1) Strukturierte Daten (Schema.org) implementieren, 2) Meta-Tags für Social Media hinzufügen, 3) Eine manifest.json mit vollständigen Informationen erstellen, 4) Service Worker für bessere Performance nutzen, 5) Responsive Design sicherstellen, 6) Schnelle Ladezeiten durch Caching optimieren."
          }
        },
        {
          "@type": "Question",
          "name": "Unterstützt der Generator alle Plattformen?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Ja, unser Generator erstellt Icons für alle wichtigen Plattformen: iOS (Apple Touch Icons), Android (Chrome Icons mit Maskable Support), Windows (Tile Images), Safari (Pinned Tab), und Standard-Favicons für alle Browser."
          }
        }
      ]
    },
    {
      "@type": "HowTo",
      "@id": "https://example.com/pwa-generator#howto",
      "name": "Wie man eine PWA mit Icons erstellt",
      "description": "Schritt-für-Schritt-Anleitung zur Erstellung einer Progressive Web App mit allen benötigten Icons",
      "image": "https://example.com/howto-image.jpg",
      "totalTime": "PT10M",
      "estimatedCost": {
        "@type": "MonetaryAmount",
        "currency": "EUR",
        "value": "0"
      },
      "supply": [
        {
          "@type": "HowToSupply",
          "name": "Quellbild",
          "requiredQuantity": 1,
          "estimatedCost": {
            "@type": "MonetaryAmount",
            "currency": "EUR",
            "value": "0"
          }
        }
      ],
      "tool": [
        {
          "@type": "HowToTool",
          "name": "PWA Generator",
          "url": "https://example.com/pwa-generator"
        }
      ],
      "step": [
        {
          "@type": "HowToStep",
          "name": "Bild hochladen",
          "text": "Laden Sie Ihr Logo oder Icon als PNG, JPG, SVG oder WebP hoch",
          "image": "https://example.com/step1.jpg",
          "url": "https://example.com/pwa-generator#upload"
        },
        {
          "@type": "HowToStep",
          "name": "Bild zuschneiden",
          "text": "Verwenden Sie den Cropper, um den gewünschten Bereich auszuwählen",
          "image": "https://example.com/step2.jpg",
          "url": "https://example.com/pwa-generator#crop"
        },
        {
          "@type": "HowToStep",
          "name": "Icons generieren",
          "text": "Klicken Sie auf 'Icons generieren' um alle Größen zu erstellen",
          "image": "https://example.com/step3.jpg",
          "url": "https://example.com/pwa-generator#generate"
        },
        {
          "@type": "HowToStep",
          "name": "PWA konfigurieren",
          "text": "Geben Sie App-Name, Beschreibung und Farben ein",
          "image": "https://example.com/step4.jpg",
          "url": "https://example.com/pwa-generator#configure"
        },
        {
          "@type": "HowToStep",
          "name": "Dateien herunterladen",
          "text": "Laden Sie alle generierten Dateien herunter und integrieren Sie sie in Ihre Website",
          "image": "https://example.com/step5.jpg",
          "url": "https://example.com/pwa-generator#download"
        }
      ]
    },
    {
      "@type": "BreadcrumbList",
      "itemListElement": [
        {
          "@type": "ListItem",
          "position": 1,
          "name": "Home",
          "item": "https://example.com/"
        },
        {
          "@type": "ListItem",
          "position": 2,
          "name": "Tools",
          "item": "https://example.com/tools"
        },
        {
          "@type": "ListItem",
          "position": 3,
          "name": "PWA Generator",
          "item": "https://example.com/pwa-generator"
        }
      ]
    },
    {
      "@type": "SoftwareApplication",
      "name": "PWA Generator Pro",
      "operatingSystem": "Web",
      "applicationCategory": "WebApplication",
      "ratingValue": "4.9",
      "ratingCount": "2847",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "EUR"
      }
    }
  ]
}
</script>

<!-- Weitere SEO-Optimierungen -->
<link rel="alternate" hreflang="de" href="https://example.com/de/pwa-generator">
<link rel="alternate" hreflang="en" href="https://example.com/en/pwa-generator">
<link rel="alternate" hreflang="x-default" href="https://example.com/pwa-generator">

<!-- Preconnect für Performance -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdnjs.cloudflare.com">
<link rel="dns-prefetch" href="https://www.google-analytics.com">

<!-- PWA & Performance Tags -->
<meta name="theme-color" content="#2196F3">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="PWA Generator">
<meta name="application-name" content="PWA Generator">
<meta name="msapplication-TileColor" content="#2196F3">
<meta name="msapplication-config" content="/browserconfig.xml">

PHP 8.4 Backend

Serverseitiger Code für Bildverarbeitung und Icon-Generierung mit PHP 8.4

<?php
declare(strict_types=1);

// PHP 8.4 PWA Icon Generator
namespace PWAGenerator;

use GdImage;
use Exception;

#[\Attribute]
class IconSize {
    public function __construct(
        public readonly int $width,
        public readonly int $height,
        public readonly string $purpose = 'any'
    ) {}
}

class PWAIconGenerator {
    private const QUALITY_PNG = 9;
    private const QUALITY_WEBP = 90;
    private const QUALITY_JPEG = 95;

    // Alle benötigten Icon-Größen für maximale Kompatibilität
    private const ICON_SIZES = [
        // Favicon Standards
        ['size' => 16, 'name' => 'favicon-16x16.png'],
        ['size' => 32, 'name' => 'favicon-32x32.png'],
        ['size' => 48, 'name' => 'favicon-48x48.png'],
        ['size' => 64, 'name' => 'favicon-64x64.png'],

        // PWA Icons
        ['size' => 72, 'name' => 'icon-72x72.png'],
        ['size' => 96, 'name' => 'icon-96x96.png'],
        ['size' => 128, 'name' => 'icon-128x128.png'],
        ['size' => 144, 'name' => 'icon-144x144.png'],
        ['size' => 152, 'name' => 'icon-152x152.png'],
        ['size' => 192, 'name' => 'icon-192x192.png'],
        ['size' => 384, 'name' => 'icon-384x384.png'],
        ['size' => 512, 'name' => 'icon-512x512.png'],

        // Apple Touch Icons
        ['size' => 57, 'name' => 'apple-touch-icon-57x57.png'],
        ['size' => 60, 'name' => 'apple-touch-icon-60x60.png'],
        ['size' => 72, 'name' => 'apple-touch-icon-72x72.png'],
        ['size' => 76, 'name' => 'apple-touch-icon-76x76.png'],
        ['size' => 114, 'name' => 'apple-touch-icon-114x114.png'],
        ['size' => 120, 'name' => 'apple-touch-icon-120x120.png'],
        ['size' => 144, 'name' => 'apple-touch-icon-144x144.png'],
        ['size' => 152, 'name' => 'apple-touch-icon-152x152.png'],
        ['size' => 167, 'name' => 'apple-touch-icon-167x167.png'],
        ['size' => 180, 'name' => 'apple-touch-icon-180x180.png'],

        // Windows Tiles
        ['size' => 70, 'name' => 'mstile-70x70.png'],
        ['size' => 150, 'name' => 'mstile-150x150.png'],
        ['size' => 270, 'name' => 'mstile-270x270.png'],
        ['size' => 310, 'name' => 'mstile-310x310.png'],
        ['size' => 310, 'name' => 'mstile-310x150.png', 'width' => 310, 'height' => 150],

        // Android Chrome
        ['size' => 36, 'name' => 'android-chrome-36x36.png'],
        ['size' => 48, 'name' => 'android-chrome-48x48.png'],
        ['size' => 72, 'name' => 'android-chrome-72x72.png'],
        ['size' => 96, 'name' => 'android-chrome-96x96.png'],
        ['size' => 144, 'name' => 'android-chrome-144x144.png'],
        ['size' => 192, 'name' => 'android-chrome-192x192.png'],
        ['size' => 256, 'name' => 'android-chrome-256x256.png'],
        ['size' => 384, 'name' => 'android-chrome-384x384.png'],
        ['size' => 512, 'name' => 'android-chrome-512x512.png'],
    ];

    // iOS Splash Screens
    private const SPLASH_SCREENS = [
        // iPhone
        ['width' => 640, 'height' => 1136, 'name' => 'splash-640x1136.png'],
        ['width' => 750, 'height' => 1334, 'name' => 'splash-750x1334.png'],
        ['width' => 828, 'height' => 1792, 'name' => 'splash-828x1792.png'],
        ['width' => 1125, 'height' => 2436, 'name' => 'splash-1125x2436.png'],
        ['width' => 1170, 'height' => 2532, 'name' => 'splash-1170x2532.png'],
        ['width' => 1242, 'height' => 2208, 'name' => 'splash-1242x2208.png'],
        ['width' => 1242, 'height' => 2688, 'name' => 'splash-1242x2688.png'],
        ['width' => 1284, 'height' => 2778, 'name' => 'splash-1284x2778.png'],

        // iPad
        ['width' => 1536, 'height' => 2048, 'name' => 'splash-1536x2048.png'],
        ['width' => 1668, 'height' => 2224, 'name' => 'splash-1668x2224.png'],
        ['width' => 1668, 'height' => 2388, 'name' => 'splash-1668x2388.png'],
        ['width' => 2048, 'height' => 2732, 'name' => 'splash-2048x2732.png'],
    ];

    public function __construct(
        private readonly string $outputDir = './icons/'
    ) {
        if (!is_dir($this->outputDir)) {
            mkdir($this->outputDir, 0755, true);
        }
    }

    #[IconSize(512, 512, 'any')]
    public function generateFromUpload(array $uploadedFile): array {
        if ($uploadedFile['error'] !== UPLOAD_ERR_OK) {
            throw new Exception('Upload fehlgeschlagen');
        }

        $sourceImage = $this->loadImage($uploadedFile['tmp_name']);
        if (!$sourceImage) {
            throw new Exception('Ungültiges Bildformat');
        }

        $results = [];

        // Generiere alle Icon-Größen
        foreach (self::ICON_SIZES as $iconConfig) {
            $width = $iconConfig['width'] ?? $iconConfig['size'];
            $height = $iconConfig['height'] ?? $iconConfig['size'];

            $resized = $this->resizeImage($sourceImage, $width, $height);
            $path = $this->outputDir . $iconConfig['name'];

            // PNG speichern
            imagepng($resized, $path, self::QUALITY_PNG);

            // WebP-Version erstellen
            $webpPath = str_replace('.png', '.webp', $path);
            imagewebp($resized, $webpPath, self::QUALITY_WEBP);

            $results[] = [
                'name' => $iconConfig['name'],
                'path' => $path,
                'webp' => $webpPath,
                'size' => "{$width}x{$height}"
            ];

            imagedestroy($resized);
        }

        // Generiere ICO-Datei
        $this->generateICO($sourceImage);

        // Generiere optimiertes SVG
        $this->generateSVG($sourceImage);

        // Generiere Splash Screens
        $this->generateSplashScreens($sourceImage);

        imagedestroy($sourceImage);

        return $results;
    }

    private function loadImage(string $path): GdImage|false {
        $info = getimagesize($path);
        if (!$info) return false;

        return match($info['mime']) {
            'image/jpeg' => imagecreatefromjpeg($path),
            'image/png' => imagecreatefrompng($path),
            'image/gif' => imagecreatefromgif($path),
            'image/webp' => imagecreatefromwebp($path),
            default => false
        };
    }

    private function resizeImage(GdImage $source, int $width, int $height): GdImage {
        $sourceWidth = imagesx($source);
        $sourceHeight = imagesy($source);

        $resized = imagecreatetruecolor($width, $height);

        // Transparenz beibehalten
        imagealphablending($resized, false);
        imagesavealpha($resized, true);
        $transparent = imagecolorallocatealpha($resized, 0, 0, 0, 127);
        imagefilledrectangle($resized, 0, 0, $width, $height, $transparent);

        // Bild skalieren
        imagecopyresampled(
            $resized, $source,
            0, 0, 0, 0,
            $width, $height,
            $sourceWidth, $sourceHeight
        );

        return $resized;
    }

    private function generateICO(GdImage $source): void {
        $sizes = [16, 32, 48, 64, 128, 256];
        $icoPath = $this->outputDir . 'favicon.ico';

        $ico = '';
        $images = [];

        foreach ($sizes as $size) {
            $resized = $this->resizeImage($source, $size, $size);
            ob_start();
            imagepng($resized);
            $images[] = ob_get_clean();
            imagedestroy($resized);
        }

        // ICO-Header erstellen
        $ico .= pack('vvv', 0, 1, count($sizes));

        // Directory entries
        $offset = 6 + (count($sizes) * 16);
        foreach ($images as $i => $image) {
            $size = $sizes[$i];
            $ico .= pack('CCCCvvVV', $size, $size, 0, 0, 1, 32, strlen($image), $offset);
            $offset += strlen($image);
        }

        // Bilder anhängen
        foreach ($images as $image) {
            $ico .= $image;
        }

        file_put_contents($icoPath, $ico);
    }

    private function generateSVG(GdImage $source): void {
        $width = imagesx($source);
        $height = imagesy($source);

        // Vereinfachtes SVG aus Bitmap
        $svgPath = $this->outputDir . 'icon.svg';

        // Base64-kodiertes PNG einbetten
        ob_start();
        imagepng($source);
        $pngData = ob_get_clean();
        $base64 = base64_encode($pngData);

        $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' . $width . ' ' . $height . '">' . "\n";
        $svg .= '    <image href="data:image/png;base64,' . $base64 . '" width="' . $width . '" height="' . $height . '"/>' . "\n";
        $svg .= '</svg>';

        file_put_contents($svgPath, $svg);
    }

    private function generateSplashScreens(GdImage $logo): void {
        foreach (self::SPLASH_SCREENS as $screen) {
            $splash = imagecreatetruecolor($screen['width'], $screen['height']);

            // Hintergrund (kann angepasst werden)
            $bg = imagecolorallocate($splash, 33, 150, 243);
            imagefilledrectangle($splash, 0, 0, $screen['width'], $screen['height'], $bg);

            // Logo zentriert platzieren
            $logoSize = min($screen['width'], $screen['height']) * 0.3;
            $resizedLogo = $this->resizeImage($logo, (int)$logoSize, (int)$logoSize);

            $x = ($screen['width'] - $logoSize) / 2;
            $y = ($screen['height'] - $logoSize) / 2;

            imagecopy($splash, $resizedLogo, (int)$x, (int)$y, 0, 0, (int)$logoSize, (int)$logoSize);

            $path = $this->outputDir . 'splash/' . $screen['name'];
            if (!is_dir(dirname($path))) {
                mkdir(dirname($path), 0755, true);
            }

            imagepng($splash, $path, self::QUALITY_PNG);
            imagedestroy($splash);
            imagedestroy($resizedLogo);
        }
    }

    public function generateManifest(array $appConfig): string {
        $manifest = [
            'name' => $appConfig['name'],
            'short_name' => $appConfig['short_name'],
            'description' => $appConfig['description'],
            'start_url' => '/',
            'display' => $appConfig['display'] ?? 'standalone',
            'orientation' => $appConfig['orientation'] ?? 'any',
            'theme_color' => $appConfig['theme_color'] ?? '#2196F3',
            'background_color' => $appConfig['background_color'] ?? '#ffffff',
            'icons' => [],
            'shortcuts' => $appConfig['shortcuts'] ?? [],
            'categories' => $appConfig['categories'] ?? ['utilities'],
            'screenshots' => $appConfig['screenshots'] ?? []
        ];

        // Icons hinzufügen
        foreach (self::ICON_SIZES as $icon) {
            $size = $icon['width'] ?? $icon['size'];
            $sizeStr = "{$size}x{$size}";

            $manifest['icons'][] = [
                'src' => "/icons/{$icon['name']}",
                'sizes' => $sizeStr,
                'type' => 'image/png',
                'purpose' => 'any maskable'
            ];

            // WebP-Version
            $manifest['icons'][] = [
                'src' => "/icons/" . str_replace('.png', '.webp', $icon['name']),
                'sizes' => $sizeStr,
                'type' => 'image/webp',
                'purpose' => 'any maskable'
            ];
        }

        return json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
    }
}

// Verwendung
try {
    $generator = new PWAIconGenerator('./generated-icons/');

    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['icon'])) {
        $results = $generator->generateFromUpload($_FILES['icon']);

        // Manifest generieren
        $manifest = $generator->generateManifest($_POST);
        file_put_contents('./manifest.json', $manifest);

        // ZIP-Archiv erstellen
        $zip = new \ZipArchive();
        $zipName = 'pwa-icons-' . time() . '.zip';

        if ($zip->open($zipName, \ZipArchive::CREATE) === TRUE) {
            foreach ($results as $icon) {
                $zip->addFile($icon['path'], 'icons/' . basename($icon['path']));
                if (file_exists($icon['webp'])) {
                    $zip->addFile($icon['webp'], 'icons/' . basename($icon['webp']));
                }
            }
            $zip->addFromString('manifest.json', $manifest);
            $zip->close();

            // Download senden
            header('Content-Type: application/zip');
            header('Content-Disposition: attachment; filename="' . $zipName . '"');
            header('Content-Length: ' . filesize($zipName));
            readfile($zipName);
            unlink($zipName);
        }
    }
} catch (Exception $e) {
    http_response_code(500);
    echo json_encode(['error' => $e->getMessage()]);
}
?>

PWA-Funktionen

📱
Installierbar
Kann wie eine native App installiert werden
🔌
Offline
Funktioniert ohne Internetverbindung
🔔
Push-Benachrichtigungen
Sendet Push-Benachrichtigungen
🔄
Hintergrund-Sync
Synchronisation im Hintergrund
📍
Geolokalisierung
Zugriff auf Standort des Benutzers
📷
Kamera
Zugriff auf die Gerätekamera
📱
App installieren

Zum Startbildschirm hinzufügen