PraneshJs's picture
improved quality
cc12929 verified
import io
import zipfile
import json
import time
import tempfile
import os
from PIL import Image
import gradio as gr
def center_crop_square(img: Image.Image) -> Image.Image:
w, h = img.size
min_side = min(w, h)
left = (w - min_side) // 2
top = (h - min_side) // 2
right = left + min_side
bottom = top + min_side
return img.crop((left, top, right, bottom))
def generate_favicons(image, name, short_name, theme_color, background_color, tile_color):
if image is None:
return None
img = Image.open(image).convert("RGBA")
img = center_crop_square(img)
# Ensure a very high-res base image
base_size = max(1024, img.width, img.height)
img = img.resize((base_size, base_size), Image.LANCZOS)
if not name:
name = "My App"
if not short_name:
short_name = "App"
if not theme_color:
theme_color = "#ffffff"
if not background_color:
background_color = "#ffffff"
if not tile_color:
tile_color = theme_color
# Icon specs: regular + ultra-high for Apple/Android
icon_specs = [
(16, "favicon-16x16.png"),
(32, "favicon-32x32.png"),
(48, "favicon-48x48.png"),
(57, "apple-icon-57x57.png"),
(60, "apple-icon-60x60.png"),
(72, "apple-icon-72x72.png"),
(76, "apple-icon-76x76.png"),
(96, "android-icon-96x96.png"),
(114, "apple-icon-114x114.png"),
(120, "apple-icon-120x120.png"),
(128, "android-icon-128x128.png"),
(144, "android-icon-144x144.png"),
(152, "apple-icon-152x152.png"),
(167, "apple-icon-167x167.png"),
(180, "apple-icon-180x180.png"),
(192, "android-icon-192x192.png"),
(256, "android-icon-256x256.png"),
(384, "android-icon-384x384.png"),
(512, "android-icon-512x512.png"),
(1024, "apple-icon-1024x1024.png"), # Retina / App Store
(150, "ms-icon-150x150.png"),
(32, "ms-icon-32x32.png"),
(70, "ms-icon-70x70.png"),
(310, "ms-icon-310x310.png"),
]
files = {}
# Generate PNGs
for size, filename in icon_specs:
resized = img.resize((size, size), Image.LANCZOS)
buf = io.BytesIO()
resized.save(buf, format="PNG")
files[filename] = buf.getvalue()
# Apple touch icon
files["apple-touch-icon.png"] = files.get("apple-icon-180x180.png", img.resize((180,180), Image.LANCZOS).tobytes())
# favicon.ico (multi-size)
ico_sizes = [16, 32, 48, 64, 128, 256]
ico_images = [img.resize((s, s), Image.LANCZOS) for s in ico_sizes]
ico_buf = io.BytesIO()
ico_images[0].save(ico_buf, format='ICO', sizes=[(s, s) for s in ico_sizes])
files["favicon.ico"] = ico_buf.getvalue()
# manifest.json
icons = [
{
"src": fn,
"sizes": f"{sz}x{sz}",
"type": "image/png"
}
for sz, fn in icon_specs if fn.endswith(".png") and sz <= 512
]
icons.append({
"src": "android-icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
})
manifest = {
"name": name,
"short_name": short_name,
"start_url": "/",
"display": "standalone",
"background_color": background_color,
"theme_color": theme_color,
"icons": icons
}
files["site.webmanifest"] = json.dumps(manifest, indent=2).encode()
files["manifest.json"] = json.dumps(manifest, indent=2).encode()
# browserconfig.xml
bc = f"""<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile>
<square70x70logo src="ms-icon-70x70.png"/>
<square150x150logo src="ms-icon-150x150.png"/>
<square310x310logo src="ms-icon-310x310.png"/>
<TileColor>{tile_color}</TileColor>
</tile></msapplication></browserconfig>
"""
files["browserconfig.xml"] = bc.encode()
# README.txt
readme = f"Generated favicons for {name}\n\nAll icons are in the root of the ZIP.\nTheme color: {theme_color}\n"
files["README.txt"] = readme.encode()
# ZIP
zip_buf = io.BytesIO()
with zipfile.ZipFile(zip_buf, "w") as zf:
for path, data in files.items():
zf.writestr(path, data)
zip_buf.seek(0)
ts = int(time.time())
filename = f"favicons-{ts}.zip"
return filename, zip_buf
def gradio_ui(image, name, short_name, theme_color, background_color, tile_color):
result = generate_favicons(image, name, short_name, theme_color, background_color, tile_color)
if result is None:
return None
filename, zip_buf = result
# Save ZIP to temporary file for Gradio download
tmp_dir = tempfile.mkdtemp()
filepath = os.path.join(tmp_dir, filename)
with open(filepath, "wb") as f:
f.write(zip_buf.read())
return filepath
demo = gr.Interface(
fn=gradio_ui,
inputs=[
gr.Image(type="filepath", label="Upload Image"),
gr.Textbox(label="App Name", placeholder="My App"),
gr.Textbox(label="Short Name", placeholder="App"),
gr.ColorPicker(label="Theme Color", value="#ffffff"),
gr.ColorPicker(label="Background Color", value="#ffffff"),
gr.ColorPicker(label="Tile Color", value="#ffffff"),
],
outputs=gr.File(label="Download Favicons ZIP"),
title="Ultra High-Quality Favicon Generator",
description="Upload a high-res image (≥1024px) and generate ultra-high quality favicons for Retina and modern devices."
)
if __name__ == "__main__":
demo.launch()