"""Generates the PWA app icons as flat, dependency-free PNGs. Draws a simple "page with folded corner + text lines" glyph on a solid brand-blue background, using supersampled coverage for anti-aliasing. No image libraries required (stdlib struct/zlib only) so this can run anywhere Python 3 runs. """ import struct import zlib import os BRAND_BLUE = (31, 111, 235) PAGE_WHITE = (255, 255, 255) FOLD_SHADE = (198, 214, 245) LINE_BLUE = (99, 150, 235) SUPERSAMPLE = 4 def mix(c1, c2, t): return tuple(round(a + (b - a) * t) for a, b in zip(c1, c2)) def rounded_rect_contains(x, y, left, top, right, bottom, radius): if left <= x <= right and top <= y <= bottom: cx = min(max(x, left + radius), right - radius) cy = min(max(y, top + radius), bottom - radius) return (x - cx) ** 2 + (y - cy) ** 2 <= radius * radius return False def line_contains(x, y, cx, cy, half_w, half_h): return abs(x - cx) <= half_w and abs(y - cy) <= half_h def pixel_color(x, y, size): # Kept within the maskable-icon "safe zone" (content within a circle of # radius 0.4 * size centered on the canvas) so Android's adaptive-icon # masks (circle, squircle, rounded square, ...) never clip the glyph. left = size * 0.25 right = size * 0.75 top = size * 0.20 bottom = size * 0.80 radius = size * 0.045 fold = size * 0.14 in_page = rounded_rect_contains(x, y, left, top, right, bottom, radius) # Cut the top-right corner off diagonally to form a folded-page look. if in_page and (x - (right - fold)) > 0 and (top - y) < 0 and (x - (right - fold)) + (y - top) > fold: in_page = False if in_page: # Shadow triangle for the folded flap, just inside the cut edge. dx = x - (right - fold) dy = y - top if dx > 0 and dy >= 0 and dx + dy <= fold and dx + dy >= fold * 0.55: return FOLD_SHADE page_w = right - left line_h = size * 0.028 line_specs = [ (0.46, 0.70), (0.60, 0.70), (0.74, 0.46), ] for rel_y, rel_w in line_specs: cy = top + (bottom - top) * rel_y half_h = line_h / 2 half_w = page_w * rel_w / 2 cx = left + page_w / 2 if line_contains(x, y, cx, cy, half_w, half_h): return LINE_BLUE return PAGE_WHITE return BRAND_BLUE def make_png(path, size): raw = bytearray() step = 1.0 / SUPERSAMPLE for y in range(size): raw.append(0) # filter type 0 for each scanline for x in range(size): r_acc = g_acc = b_acc = 0 for sy in range(SUPERSAMPLE): for sx in range(SUPERSAMPLE): sample_x = x + (sx + 0.5) * step sample_y = y + (sy + 0.5) * step r, g, b = pixel_color(sample_x, sample_y, size) r_acc += r g_acc += g b_acc += b n = SUPERSAMPLE * SUPERSAMPLE raw += bytes([round(r_acc / n), round(g_acc / n), round(b_acc / n)]) compressed = zlib.compress(bytes(raw), 9) def chunk(tag, data): return struct.pack(">I", len(data)) + tag + data + struct.pack( ">I", zlib.crc32(tag + data) & 0xFFFFFFFF ) sig = b"\x89PNG\r\n\x1a\n" ihdr = struct.pack(">IIBBBBB", size, size, 8, 2, 0, 0, 0) png = sig + chunk(b"IHDR", ihdr) + chunk(b"IDAT", compressed) + chunk(b"IEND", b"") os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, "wb") as f: f.write(png) if __name__ == "__main__": make_png("public/icons/icon-192.png", 192) make_png("public/icons/icon-512.png", 512) make_png("public/icons/apple-touch-icon.png", 180) print("Icons generated")