PraneshJs commited on
Commit
bbd60a9
·
verified ·
1 Parent(s): 0edf50d

added app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -0
app.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ import zipfile
3
+ import json
4
+ import time
5
+ from PIL import Image
6
+ import gradio as gr
7
+
8
+ def center_crop_square(img: Image.Image) -> Image.Image:
9
+ w, h = img.size
10
+ min_side = min(w, h)
11
+ left = (w - min_side) // 2
12
+ top = (h - min_side) // 2
13
+ right = left + min_side
14
+ bottom = top + min_side
15
+ return img.crop((left, top, right, bottom))
16
+
17
+ def generate_favicons(image, name, short_name, theme_color, background_color, tile_color):
18
+ if image is None:
19
+ return None
20
+
21
+ img = Image.open(image).convert("RGBA")
22
+ img = center_crop_square(img)
23
+
24
+ if not name:
25
+ name = "My App"
26
+ if not short_name:
27
+ short_name = "App"
28
+ if not theme_color:
29
+ theme_color = "#ffffff"
30
+ if not background_color:
31
+ background_color = "#ffffff"
32
+ if not tile_color:
33
+ tile_color = theme_color
34
+
35
+ icon_specs = [
36
+ (16, "favicon-16x16.png"),
37
+ (32, "favicon-32x32.png"),
38
+ (48, "favicon-48x48.png"),
39
+ (57, "apple-icon-57x57.png"),
40
+ (60, "apple-icon-60x60.png"),
41
+ (72, "apple-icon-72x72.png"),
42
+ (76, "apple-icon-76x76.png"),
43
+ (96, "android-icon-96x96.png"),
44
+ (114, "apple-icon-114x114.png"),
45
+ (120, "apple-icon-120x120.png"),
46
+ (128, "android-icon-128x128.png"),
47
+ (144, "android-icon-144x144.png"),
48
+ (152, "apple-icon-152x152.png"),
49
+ (167, "apple-icon-167x167.png"),
50
+ (180, "apple-icon-180x180.png"),
51
+ (192, "android-icon-192x192.png"),
52
+ (256, "android-icon-256x256.png"),
53
+ (384, "android-icon-384x384.png"),
54
+ (512, "android-icon-512x512.png"),
55
+ (150, "ms-icon-150x150.png"),
56
+ (32, "ms-icon-32x32.png"),
57
+ (70, "ms-icon-70x70.png"),
58
+ (310, "ms-icon-310x310.png"),
59
+ ]
60
+
61
+ files = {}
62
+
63
+ # Generate PNGs
64
+ for size, filename in icon_specs:
65
+ resized = img.resize((size, size), Image.LANCZOS)
66
+ buf = io.BytesIO()
67
+ resized.save(buf, format="PNG")
68
+ files[filename] = buf.getvalue()
69
+
70
+ # Apple touch icon
71
+ if "apple-icon-180x180.png" in files:
72
+ files["apple-touch-icon.png"] = files["apple-icon-180x180.png"]
73
+
74
+ # favicon.ico (multi-size)
75
+ ico_sizes = [(16,16), (32,32), (48,48), (64,64), (128,128), (256,256)]
76
+ ico_buf = io.BytesIO()
77
+ img.save(ico_buf, format="ICO", sizes=ico_sizes)
78
+ files["favicon.ico"] = ico_buf.getvalue()
79
+
80
+ # manifest.json
81
+ icons = [
82
+ {
83
+ "src": fn,
84
+ "sizes": f"{sz}x{sz}",
85
+ "type": "image/png"
86
+ }
87
+ for sz, fn in icon_specs if fn.endswith(".png")
88
+ ]
89
+ icons.append({
90
+ "src": "android-icon-512x512.png",
91
+ "sizes": "512x512",
92
+ "type": "image/png",
93
+ "purpose": "any maskable"
94
+ })
95
+
96
+ manifest = {
97
+ "name": name,
98
+ "short_name": short_name,
99
+ "start_url": "/",
100
+ "display": "standalone",
101
+ "background_color": background_color,
102
+ "theme_color": theme_color,
103
+ "icons": icons
104
+ }
105
+ files["site.webmanifest"] = json.dumps(manifest, indent=2).encode()
106
+ files["manifest.json"] = json.dumps(manifest, indent=2).encode()
107
+
108
+ # browserconfig.xml
109
+ bc = f"""<?xml version="1.0" encoding="utf-8"?>
110
+ <browserconfig><msapplication><tile>
111
+ <square70x70logo src="ms-icon-70x70.png"/>
112
+ <square150x150logo src="ms-icon-150x150.png"/>
113
+ <square310x310logo src="ms-icon-310x310.png"/>
114
+ <TileColor>{tile_color}</TileColor>
115
+ </tile></msapplication></browserconfig>
116
+ """
117
+ files["browserconfig.xml"] = bc.encode()
118
+
119
+ # README.txt
120
+ readme = f"Generated favicons for {name}\n\nAll icons are in the root of the ZIP.\nTheme color: {theme_color}\n"
121
+ files["README.txt"] = readme.encode()
122
+
123
+ # ZIP
124
+ zip_buf = io.BytesIO()
125
+ with zipfile.ZipFile(zip_buf, "w") as zf:
126
+ for path, data in files.items():
127
+ zf.writestr(path, data)
128
+ zip_buf.seek(0)
129
+
130
+ ts = int(time.time())
131
+ filename = f"favicons-{ts}.zip"
132
+ return filename, zip_buf
133
+
134
+ def gradio_ui(image, name, short_name, theme_color, background_color, tile_color):
135
+ result = generate_favicons(image, name, short_name, theme_color, background_color, tile_color)
136
+ if result is None:
137
+ return None
138
+ filename, zip_buf = result
139
+ return (filename, zip_buf)
140
+
141
+ demo = gr.Interface(
142
+ fn=gradio_ui,
143
+ inputs=[
144
+ gr.Image(type="filepath", label="Upload Image"),
145
+ gr.Textbox(label="App Name", placeholder="My App"),
146
+ gr.Textbox(label="Short Name", placeholder="App"),
147
+ gr.ColorPicker(label="Theme Color", value="#ffffff"),
148
+ gr.ColorPicker(label="Background Color", value="#ffffff"),
149
+ gr.ColorPicker(label="Tile Color", value="#ffffff"),
150
+ ],
151
+ outputs=gr.File(label="Download Favicons ZIP"),
152
+ title="Favicon Generator",
153
+ description="Upload an image and generate favicons + manifest + favicon.ico for PWA"
154
+ )
155
+
156
+ if __name__ == "__main__":
157
+ demo.launch(debug=True, pwa=True)