File size: 5,548 Bytes
bbd60a9
 
 
 
64cf80c
 
bbd60a9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cc12929
bbd60a9
 
 
cc12929
 
 
 
bbd60a9
 
 
 
 
 
 
 
 
 
 
cc12929
bbd60a9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cc12929
bbd60a9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cc12929
bbd60a9
 
cc12929
 
bbd60a9
cc12929
bbd60a9
 
 
 
 
 
 
 
 
cc12929
bbd60a9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64cf80c
 
 
 
 
 
 
 
bbd60a9
 
 
 
 
 
 
 
 
 
 
 
cc12929
 
bbd60a9
 
 
cc12929
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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()