📱 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