ObsiGate/generate_pwa_icons.py

191 lines
7.6 KiB
Python

#!/usr/bin/env python3
"""
Generate PWA icons for ObsiGate
Creates PNG icons in various sizes from an SVG template
"""
import os
from pathlib import Path
def create_svg_icon(size, output_path):
"""Create a simple SVG icon for ObsiGate"""
svg_content = f'''<?xml version="1.0" encoding="UTF-8"?>
<svg width="{size}" height="{size}" viewBox="0 0 {size} {size}" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#2563eb;stop-opacity:1" />
<stop offset="100%" style="stop-color:#1d4ed8;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Background -->
<rect width="{size}" height="{size}" rx="{size * 0.15}" fill="url(#grad1)"/>
<!-- Book icon -->
<g transform="translate({size * 0.25}, {size * 0.2})">
<!-- Book cover -->
<rect x="0" y="0" width="{size * 0.5}" height="{size * 0.6}" rx="{size * 0.02}" fill="white" opacity="0.95"/>
<!-- Book spine -->
<rect x="{size * 0.05}" y="0" width="{size * 0.02}" height="{size * 0.6}" fill="#1a1a1a" opacity="0.2"/>
<!-- Book pages lines -->
<line x1="{size * 0.1}" y1="{size * 0.15}" x2="{size * 0.45}" y2="{size * 0.15}" stroke="#1a1a1a" stroke-width="{size * 0.01}" opacity="0.3"/>
<line x1="{size * 0.1}" y1="{size * 0.25}" x2="{size * 0.45}" y2="{size * 0.25}" stroke="#1a1a1a" stroke-width="{size * 0.01}" opacity="0.3"/>
<line x1="{size * 0.1}" y1="{size * 0.35}" x2="{size * 0.45}" y2="{size * 0.35}" stroke="#1a1a1a" stroke-width="{size * 0.01}" opacity="0.3"/>
<line x1="{size * 0.1}" y1="{size * 0.45}" x2="{size * 0.35}" y2="{size * 0.45}" stroke="#1a1a1a" stroke-width="{size * 0.01}" opacity="0.3"/>
</g>
</svg>'''
with open(output_path, 'w', encoding='utf-8') as f:
f.write(svg_content)
print(f"Created SVG: {output_path}")
def create_maskable_svg_icon(size, output_path):
"""Create a maskable SVG icon (with safe zone padding)"""
# Maskable icons need 10% safe zone padding
inner_size = int(size * 0.8)
padding = int(size * 0.1)
svg_content = f'''<?xml version="1.0" encoding="UTF-8"?>
<svg width="{size}" height="{size}" viewBox="0 0 {size} {size}" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#2563eb;stop-opacity:1" />
<stop offset="100%" style="stop-color:#1d4ed8;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Full background for maskable -->
<rect width="{size}" height="{size}" fill="url(#grad1)"/>
<!-- Book icon (centered with padding) -->
<g transform="translate({padding + inner_size * 0.25}, {padding + inner_size * 0.2})">
<!-- Book cover -->
<rect x="0" y="0" width="{inner_size * 0.5}" height="{inner_size * 0.6}" rx="{inner_size * 0.02}" fill="white" opacity="0.95"/>
<!-- Book spine -->
<rect x="{inner_size * 0.05}" y="0" width="{inner_size * 0.02}" height="{inner_size * 0.6}" fill="#1a1a1a" opacity="0.2"/>
<!-- Book pages lines -->
<line x1="{inner_size * 0.1}" y1="{inner_size * 0.15}" x2="{inner_size * 0.45}" y2="{inner_size * 0.15}" stroke="#1a1a1a" stroke-width="{inner_size * 0.01}" opacity="0.3"/>
<line x1="{inner_size * 0.1}" y1="{inner_size * 0.25}" x2="{inner_size * 0.45}" y2="{inner_size * 0.25}" stroke="#1a1a1a" stroke-width="{inner_size * 0.01}" opacity="0.3"/>
<line x1="{inner_size * 0.1}" y1="{inner_size * 0.35}" x2="{inner_size * 0.45}" y2="{inner_size * 0.35}" stroke="#1a1a1a" stroke-width="{inner_size * 0.01}" opacity="0.3"/>
<line x1="{inner_size * 0.1}" y1="{inner_size * 0.45}" x2="{inner_size * 0.35}" y2="{inner_size * 0.45}" stroke="#1a1a1a" stroke-width="{inner_size * 0.01}" opacity="0.3"/>
</g>
</svg>'''
with open(output_path, 'w', encoding='utf-8') as f:
f.write(svg_content)
print(f"Created maskable SVG: {output_path}")
def create_search_icon_svg(size, output_path):
"""Create a search icon for shortcuts"""
svg_content = f'''<?xml version="1.0" encoding="UTF-8"?>
<svg width="{size}" height="{size}" viewBox="0 0 {size} {size}" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#2563eb;stop-opacity:1" />
<stop offset="100%" style="stop-color:#1d4ed8;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Background -->
<rect width="{size}" height="{size}" rx="{size * 0.15}" fill="url(#grad1)"/>
<!-- Search icon -->
<g transform="translate({size * 0.2}, {size * 0.2})">
<circle cx="{size * 0.25}" cy="{size * 0.25}" r="{size * 0.18}" fill="none" stroke="white" stroke-width="{size * 0.05}" opacity="0.95"/>
<line x1="{size * 0.38}" y1="{size * 0.38}" x2="{size * 0.5}" y2="{size * 0.5}" stroke="white" stroke-width="{size * 0.05}" stroke-linecap="round" opacity="0.95"/>
</g>
</svg>'''
with open(output_path, 'w', encoding='utf-8') as f:
f.write(svg_content)
print(f"Created search icon SVG: {output_path}")
def main():
"""Generate all PWA icons"""
# Create icons directory
icons_dir = Path(__file__).parent / 'frontend' / 'icons'
icons_dir.mkdir(exist_ok=True)
# Icon sizes for PWA
sizes = [72, 96, 128, 144, 152, 192, 384, 512]
print("Generating PWA icons as SVG files...")
print("Note: For production, convert these SVG files to PNG using a tool like Inkscape or ImageMagick")
print("Example: inkscape icon.svg --export-filename=icon.png --export-width=512\n")
# Generate regular icons
for size in sizes:
svg_path = icons_dir / f'icon-{size}x{size}.svg'
create_svg_icon(size, svg_path)
# Generate maskable icons
for size in [192, 512]:
svg_path = icons_dir / f'icon-{size}x{size}-maskable.svg'
create_maskable_svg_icon(size, svg_path)
# Generate search icon
svg_path = icons_dir / 'search-96x96.svg'
create_search_icon_svg(96, svg_path)
print("\n✅ SVG icons generated successfully!")
print(f"📁 Location: {icons_dir}")
print("\n⚠️ Important: Convert SVG to PNG for production:")
print(" - Install ImageMagick or Inkscape")
print(" - Run conversion script or use online tools")
print(" - Alternative: Use the SVG files directly (modern browsers support it)")
# Create a simple README
readme_path = icons_dir / 'README.md'
with open(readme_path, 'w', encoding='utf-8') as f:
f.write("""# ObsiGate PWA Icons
## Generated Icons
This directory contains PWA icons in SVG format.
### Converting to PNG
For production, convert these SVG files to PNG:
**Using ImageMagick:**
```bash
for file in *.svg; do
size=$(echo $file | grep -oP '\\d+x\\d+' | head -1 | cut -d'x' -f1)
convert -background none -resize ${size}x${size} "$file" "${file%.svg}.png"
done
```
**Using Inkscape:**
```bash
for file in *.svg; do
size=$(echo $file | grep -oP '\\d+x\\d+' | head -1 | cut -d'x' -f1)
inkscape "$file" --export-filename="${file%.svg}.png" --export-width=$size
done
```
**Online tools:**
- https://cloudconvert.com/svg-to-png
- https://convertio.co/svg-png/
### Icon Types
- **Regular icons**: Standard app icons with rounded corners
- **Maskable icons**: Icons with safe zone padding for adaptive icons
- **Search icon**: Icon for the search shortcut
### Sizes
- 72x72, 96x96, 128x128, 144x144, 152x152: Mobile devices
- 192x192: Android home screen
- 384x384: High-res displays
- 512x512: Splash screens and high-DPI displays
""")
print(f"\n📝 Created README: {readme_path}")
if __name__ == '__main__':
main()