191 lines
7.6 KiB
Python
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()
|