Files
gosentry/assets/logo/raster.py
T
mixeme 3a68c705c6 assets: add GoSentry wordmark logo (clock-dial variant)
Recommended variant from gosentry-logo.html: Space Grotesk SemiBold with
an amber "G", a clock-dial "o" (ring + hands), and petrol "Sentry". Ships
self-contained SVGs (glyphs outlined to paths, no font dependency) in
light/dark/mono plus transparent PNGs at 256-2048px, with a README and the
gen_logo.py/raster.py generators for reproducibility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 23:51:38 +03:00

98 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""Rasterize the GoSentry wordmark to PNG at several widths, reusing the
same font outlines + dial geometry as gen_logo.py (no SVG rasterizer needed)."""
import os, math
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch, Circle
from matplotlib.lines import Line2D
from fontTools.ttLib import TTFont
from fontTools.pens.basePen import BasePen
from fontTools.pens.boundsPen import BoundsPen
from fontTools.pens.transformPen import TransformPen
HERE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.join(HERE, "out"); os.makedirs(OUT, exist_ok=True)
AMBER, PETROL, WHITE = "#F7A80C", "#0A4A58", "#FFFFFF"
LS = -30
f = TTFont(os.path.join(HERE, "SpaceGrotesk-600.ttf"))
cmap, hmtx, gs = f.getBestCmap(), f["hmtx"], f.getGlyphSet()
class MplPen(BasePen):
def __init__(self, glyphSet):
super().__init__(glyphSet); self.v=[]; self.c=[]
def _moveTo(self,p): self.v.append(p); self.c.append(Path.MOVETO)
def _lineTo(self,p): self.v.append(p); self.c.append(Path.LINETO)
def _curveToOne(self,p1,p2,p3):
self.v += [p1,p2,p3]; self.c += [Path.CURVE4]*3
def _qCurveToOne(self,p1,p2):
self.v += [p1,p2]; self.c += [Path.CURVE3]*2
def _closePath(self):
self.v.append((0,0)); self.c.append(Path.CLOSEPOLY)
def glyph_mplpath(ch, dx):
pen = MplPen(gs)
gs[cmap[ord(ch)]].draw(TransformPen(pen,(1,0,0,1,dx,0)))
return Path(pen.v, pen.c), hmtx[cmap[ord(ch)]][0]
# layout
x=0.0
paths_amber=[]; paths_petrol=[]
p,adv = glyph_mplpath("G",x); paths_amber.append(p); x+=adv+LS
og=cmap[ord("o")]; bp=BoundsPen(gs); gs[og].draw(bp)
oxmin,oymin,oxmax,oymax=bp.bounds; o_adv=hmtx[og][0]
cx=x+(oxmin+oxmax)/2; cy=(oymin+oymax)/2
R=((oxmax-oxmin)+(oymax-oymin))/4
x+=o_adv+LS
for ch in "Sentry":
p,adv=glyph_mplpath(ch,x); paths_petrol.append(p); x+=adv+LS
x-=LS
SW=R*0.30; Rmid=R-SW/2; HW=R*0.21
hour_len=R*0.54; min_len=R*0.46; ang=math.radians(62)
mx=cx+min_len*math.sin(ang); my=cy+min_len*math.cos(ang); cap=R*0.14
# bounds
allb=BoundsPen(gs); gs[cmap[ord('G')]].draw(allb)
xx=hmtx[cmap[ord('G')]][0]+LS+o_adv+LS
for ch in "Sentry":
gs[cmap[ord(ch)]].draw(TransformPen(allb,(1,0,0,1,xx,0))); xx+=hmtx[cmap[ord(ch)]][0]+LS
bx0,by0,bx1,by1=allb.bounds
bx0=min(bx0,cx-R-SW/2); bx1=max(bx1,cx+R+SW/2)
by0=min(by0,cy-R-SW/2); by1=max(by1,cy+R+SW/2)
PAD=60
X0,X1=bx0-PAD,bx1+PAD; Y0,Y1=by0-PAD,by1+PAD
W=X1-X0; H=Y1-Y0
def render(path_png, width_px, sentry_color, bg=None, mono=False):
dpi=100
fw=width_px/dpi; fh=fw*H/W
fig=plt.figure(figsize=(fw,fh),dpi=dpi)
ax=fig.add_axes([0,0,1,1]); ax.set_xlim(X0,X1); ax.set_ylim(Y0,Y1)
ax.set_aspect('equal'); ax.axis('off')
if bg: fig.patch.set_facecolor(bg); ax.set_facecolor(bg)
else: fig.patch.set_alpha(0)
amberc = sentry_color if mono else AMBER
for p in paths_amber: ax.add_patch(PathPatch(p,facecolor=amberc,edgecolor='none',antialiased=True))
for p in paths_petrol: ax.add_patch(PathPatch(p,facecolor=sentry_color,edgecolor='none',antialiased=True))
pt_per_unit = fw/W*72
ax.add_patch(Circle((cx,cy),Rmid,fill=False,edgecolor=amberc,linewidth=SW*pt_per_unit))
for (ex,ey) in [(cx,cy+hour_len),(mx,my)]:
ax.add_line(Line2D([cx,ex],[cy,ey],color=amberc,linewidth=HW*pt_per_unit,
solid_capstyle='round'))
ax.add_patch(Circle((cx,cy),cap,facecolor=amberc,edgecolor='none'))
fig.savefig(path_png,dpi=dpi,transparent=(bg is None))
plt.close(fig)
print("wrote",os.path.basename(path_png))
for w in (256,512,1024,2048):
render(os.path.join(OUT,f"gosentry-logo-{w}.png"),w,PETROL)
render(os.path.join(OUT,f"gosentry-logo-dark-{w}.png"),w,WHITE)
render(os.path.join(OUT,"gosentry-logo-onlight-1024.png"),1024,PETROL,bg="#FFFFFF")
render(os.path.join(OUT,"gosentry-logo-ondark-1024.png"),1024,WHITE,bg="#04262E")
render(os.path.join(OUT,"gosentry-logo-mono-1024.png"),1024,PETROL,mono=True)
print(f"aspect {W:.0f}x{H:.0f}")