Files
twislaandClaude Sonnet 5.5 492d8bb187
Site / build (pull_request) Successful in 9s
Site: security.txt, robots.txt, and favicon fallbacks (ICO, touch icon)
security.txt (RFC 9116) under .well-known with the mailbox as contact and an
expiry in September 2027. robots.txt allows everything and points to the
sitemap. The pixel 9 gets a favicon.ico (32 and 48 px) and an
apple-touch-icon.png, drawn by tools/make_favicons.py from the same
rectangles as favicon.svg.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-06 20:12:54 +02:00

62 lines
2.0 KiB
Python

#!/usr/bin/env python3
"""Draw the pixel "9" of static/img/favicon.svg as PNGs and an ICO, with the standard library.
python3 site/tools/make_favicons.py
Writes static/favicon.ico (32 and 48 px, PNG inside) and static/apple-touch-icon.png (180 px).
The 9 is a 7x7 grid, scaled by a whole number and centred on the background colour."""
import struct
import zlib
from pathlib import Path
BG = (0x00, 0x00, 0x55)
FG = (0x00, 0xDB, 0xFF)
# x, y, w, h: the same rectangles as favicon.svg
RECTS = [(1, 0, 3, 1), (0, 1, 1, 2), (4, 1, 1, 5), (1, 3, 3, 1), (0, 6, 3, 1)]
GRID = 7
STATIC = Path(__file__).resolve().parent.parent / "static"
def cells():
on = set()
for x, y, w, h in RECTS:
on.update((x + i, y + j) for i in range(w) for j in range(h))
return on
def png(size):
scale = size // GRID
pad = (size - scale * GRID) // 2
on = cells()
rows = bytearray()
for py in range(size):
rows.append(0)
for px in range(size):
cx, cy = (px - pad) // scale, (py - pad) // scale
inside = 0 <= px - pad < scale * GRID and 0 <= py - pad < scale * GRID
rows.extend(FG if inside and (cx, cy) in on else BG)
def chunk(kind, data):
body = kind + data
return struct.pack(">I", len(data)) + body + struct.pack(">I", zlib.crc32(body))
return (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", size, size, 8, 2, 0, 0, 0)) +
chunk(b"IDAT", zlib.compress(bytes(rows), 9)) + chunk(b"IEND", b""))
def ico(sizes):
images = [png(s) for s in sizes]
out = struct.pack("<HHH", 0, 1, len(images))
offset = 6 + 16 * len(images)
for s, data in zip(sizes, images):
out += struct.pack("<BBBBHHII", s, s, 0, 0, 1, 32, len(data), offset)
offset += len(data)
return out + b"".join(images)
if __name__ == "__main__":
(STATIC / "favicon.ico").write_bytes(ico([32, 48]))
(STATIC / "apple-touch-icon.png").write_bytes(png(180))
print("wrote favicon.ico, apple-touch-icon.png")