diff options
| author | user <user@clank> | 2026-09-02 20:18:10 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-09-02 20:18:10 +0200 |
| commit | dc72a22da1b24ffb835681a1a8e771365020041b (patch) | |
| tree | 81efe0b89b2ea60c9595bc6626af07ca68396e54 /web/gen-card | |
| parent | README.md: chore fix (diff) | |
| download | slosh-dc72a22da1b24ffb835681a1a8e771365020041b.tar.gz slosh-dc72a22da1b24ffb835681a1a8e771365020041b.tar.xz slosh-dc72a22da1b24ffb835681a1a8e771365020041b.zip | |
web: card
Diffstat (limited to 'web/gen-card')
| -rwxr-xr-x | web/gen-card | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/web/gen-card b/web/gen-card new file mode 100755 index 0000000..b303c29 --- /dev/null +++ b/web/gen-card @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +"""Render the social-embed card, web/card.png, from logo.txt. + +The card is what Discord/X/Slack show when someone pastes slosh.foo: a +1200x630 PNG of the logo in the site's palette (contrib/themes/sl0p.kdl -- +hot pink on near-black) over the tagline. The logo is drawn as geometry, +exactly like web/gen-logo-mask does for the page (half-block cells, 6x10 +units, horizontal runs merged, EPS overlap against seam pinstripes), so the +card can never disagree with the mask. + +Like gen-logo-mask this is run by hand when logo.txt or the palette moves, +and the result is committed; build-site only copies it. Needs Pillow. +""" + +import os +import sys + +from PIL import Image, ImageDraw, ImageFont + +HERE = os.path.dirname(os.path.abspath(__file__)) +LOGO = os.path.join(os.path.dirname(HERE), "logo.txt") +OUT = os.path.join(HERE, "card.png") + +# site.css :root, verbatim. +BG = "#0b0b0e" +FG = "#e8e8ea" +DIM = "#8a8a95" +PINK = "#ff5fd7" + +W, H = 1200, 630 +SS = 2 # supersample, for crisp downscaled edges and text + +# One logo cell in device pixels (at 1x). 6x10 units in the mask; the same +# ratio here, just bigger. +CW, CH, EPS = 19, 32, 1.0 + +TAGLINE = "a modern terminal multiplexer" +SUBLINE = "bringing the bling back to your shells" +SITE = "https://slosh.foo" + + +def runs(row, want): + out, start = [], None + for i, ch in enumerate(row + " "): + hit = ch in want + if hit and start is None: + start = i + if not hit and start is not None: + out.append((start, i)) + start = None + return out + + +def font(size, *names): + for name in names: + for d in ("/usr/share/fonts/TTF", "/usr/share/fonts/truetype/dejavu"): + p = os.path.join(d, name) + if os.path.exists(p): + return ImageFont.truetype(p, size) + sys.exit(f"gen-card: none of {names} found -- install DejaVu fonts") + + +lines = open(LOGO).read().rstrip("\n").splitlines() +cols = max(len(l) for l in lines) + +img = Image.new("RGB", (W * SS, H * SS), BG) +draw = ImageDraw.Draw(img) + +cw, ch, eps = CW * SS, CH * SS, EPS * SS +lw, lh = cols * cw, len(lines) * ch +lx, ly = (W * SS - lw) // 2, 150 * SS + +for j, row in enumerate(lines): + for a, b in runs(row, "\u2580\u2588"): # top halves + draw.rectangle( + [lx + a * cw, ly + j * ch - eps, lx + b * cw, ly + j * ch + ch / 2 + eps], + fill=PINK, + ) + for a, b in runs(row, "\u2584\u2588"): # bottom halves + draw.rectangle( + [ + lx + a * cw, + ly + j * ch + ch / 2 - eps, + lx + b * cw, + ly + (j + 1) * ch + eps, + ], + fill=PINK, + ) + +mono = font(36 * SS, "DejaVuSansMono.ttf") +mono_sm = font(27 * SS, "DejaVuSansMono.ttf") + + +def centred(text, y, fnt, fill): + tw = draw.textlength(text, font=fnt) + draw.text(((W * SS - tw) / 2, y * SS), text, font=fnt, fill=fill) + + +centred(TAGLINE, 442, mono, FG) +centred(SUBLINE, 502, mono_sm, DIM) +centred(SITE, 560, mono_sm, PINK) + +img = img.resize((W, H), Image.LANCZOS) +img.save(OUT, optimize=True) +print( + f"gen-card: {W}x{H}, logo {lw // SS}x{lh // SS} at ({lx // SS},{ly // SS}) -> {os.path.relpath(OUT, HERE)}" +) |
