File size: 4,073 Bytes
27aa296
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
"""The SimWorld emblem as the site's logo (user, 2026-10-17: "网页的logo换成SimWorld的logo").

- assets/simworld-mark.png: the emblem the user supplied (2000 px, transparent), cropped to its alpha bounds, centred on
  a square, scaled to 192 px and stored with a 256-colour palette (10 kB; no visible difference at this size).
- Every page header: the emblem replaces the wireframe cube in front of "Code4Scene"; the name and "by SimWorld" stay.
- Every page's favicon, plus an apple-touch icon: the emblem instead of assets/code4scene-mark.svg (kept in the repo).
- assets/buildingbench.css: the mark's size, 30 px (26 px on phones), where the cube was 26 x 29 px.
Pages are edited as text, line for line, so nothing else in them changes (paper.html is hand-written for Scholar).
scripts/t2s_gallery.py writes the same header and icons into the gallery pages it builds. Re-runnable.
    python3 scripts/simworld_logo.py [--src /path/to/simworld-emblem.png]
"""
from __future__ import annotations

import argparse
import re
from pathlib import Path

from PIL import Image

ROOT = Path(__file__).resolve().parent.parent
MARK = "assets/simworld-mark.png"
V = "20261017logo"
CUBE = re.compile(r'<svg aria-hidden="true" fill="none" viewbox="0 0 30 32">.*?</svg>', re.S)
OLD_ICON = re.compile(r'<link[^>]*assets/code4scene-mark\.svg[^>]*>')
CSS_START, CSS_END = "/* SimWorld emblem in the header (scripts/simworld_logo.py) */", "/* end SimWorld emblem */"
CSS = """
.brand-home .brand-mark{display:block;flex:none;width:30px;height:30px}
@media(max-width:650px){.brand-home .brand-mark{width:26px;height:26px}}
"""


def mark(src: Path) -> None:
    im = Image.open(src).convert("RGBA")
    im = im.crop(im.getbbox())
    side = max(im.size)
    sq = Image.new("RGBA", (side, side), (0, 0, 0, 0))
    sq.alpha_composite(im, ((side - im.width) // 2, (side - im.height) // 2))
    sq = sq.resize((192, 192), Image.LANCZOS)
    sq.quantize(colors=256, method=Image.Quantize.FASTOCTREE, dither=Image.Dither.FLOYDSTEINBERG).save(ROOT / MARK, optimize=True)


def page(p: Path) -> bool:
    s0 = s = p.read_text()
    prefix = "../" * (len(p.relative_to(ROOT).parts) - 1)
    src = f"{prefix}{MARK}?v={V}"
    img = f'<img alt="" class="brand-mark" decoding="async" height="30" src="{src}" width="30"/>'
    s = CUBE.sub(img, s, count=1)
    s = re.sub(r'<img alt="" class="brand-mark"[^>]*/>', img, s, count=1)          # a re-run: the current version
    icons = (f'<link href="{src}" rel="icon" sizes="192x192" type="image/png"/>'
             f'<link href="{src}" rel="apple-touch-icon"/>')
    if OLD_ICON.search(s):
        s = OLD_ICON.sub(icons, s, count=1)
    else:
        s = re.sub(r'<link href="[^"]*simworld-mark\.png[^"]*" rel="icon"[^>]*/><link href="[^"]*" rel="apple-touch-icon"/>',
                   icons, s, count=1)
    s = re.sub(r'(assets/buildingbench\.css)\?v=[^"]*', rf"\1?v={V}", s)
    if s != s0:
        p.write_text(s)
    return s != s0


def css() -> None:
    p = ROOT / "assets/buildingbench.css"
    s = p.read_text()
    block = CSS_START + CSS + CSS_END
    if CSS_START in s:
        s = s[:s.index(CSS_START)] + block + s[s.index(CSS_END) + len(CSS_END):]
    else:
        s = s.rstrip("\n") + "\n" + block + "\n"
    p.write_text(s)


def main() -> None:
    ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
    ap.add_argument("--src", type=Path, help="the emblem image (transparent); omit to keep assets/simworld-mark.png")
    a = ap.parse_args()
    if a.src:
        mark(a.src)
    assert (ROOT / MARK).exists(), "run once with --src"
    pages = [ROOT / "index.html", ROOT / "cases.html", ROOT / "paper.html"] + sorted((ROOT / "gallery").rglob("*.html"))
    n = sum(page(p) for p in pages if p.exists())
    css()
    left = [str(p.relative_to(ROOT)) for p in pages if "code4scene-mark.svg" in p.read_text() or "M15 2 27 9v14" in p.read_text()]
    assert not left, left
    print(f"{n} pages updated; mark {(ROOT / MARK).stat().st_size} bytes")


if __name__ == "__main__":
    main()