Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 40b609a7d4 | |||
| 3a68c705c6 |
@@ -1,3 +1,10 @@
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="assets/logo/gosentry-logo-dark.svg">
|
||||
<img src="assets/logo/gosentry-logo.svg" alt="GoSentry" width="420">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
# GoSentry
|
||||
|
||||
GoSentry is a cross-platform desktop scheduler. It provides a native GUI for
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# GoSentry logo
|
||||
|
||||
Recommended wordmark variant from [`../gosentry-logo.html`](../gosentry-logo.html):
|
||||
**Space Grotesk SemiBold** with an amber **G**, a **clock-dial "o"** (ring + hands),
|
||||
and a petrol **"Sentry"**. The dial sits exactly in the `o` slot, so the schedule /
|
||||
"watch" theme lives inside the letters instead of a bolt-on icon.
|
||||
|
||||
## Colors
|
||||
|
||||
| token | hex | use |
|
||||
|--------|-----------|-----------------------------|
|
||||
| amber | `#F7A80C` | `G`, dial ring + hands |
|
||||
| petrol | `#0A4A58` | `Sentry` (light background) |
|
||||
| white | `#FFFFFF` | `Sentry` (dark background) |
|
||||
|
||||
## Files
|
||||
|
||||
Vector (self-contained — glyphs are outlined to paths, no font required):
|
||||
|
||||
- `gosentry-logo.svg` — transparent, petrol `Sentry` (for light backgrounds)
|
||||
- `gosentry-logo-dark.svg` — transparent, white `Sentry` (for dark backgrounds)
|
||||
- `gosentry-logo-mono.svg` — single-color petrol
|
||||
|
||||
Raster (transparent PNG, aspect ≈ 4326×1034 ≈ 4.18:1):
|
||||
|
||||
- `gosentry-logo-{256,512,1024,2048}.png` — petrol `Sentry`
|
||||
- `gosentry-logo-dark-{256,512,1024,2048}.png` — white `Sentry`
|
||||
|
||||
## Regenerating
|
||||
|
||||
Requires `fonttools` and `matplotlib`, plus the Space Grotesk variable font
|
||||
(SIL OFL) instanced to weight 600 as `SpaceGrotesk-600.ttf`:
|
||||
|
||||
```sh
|
||||
python gen_logo.py # writes SVGs into ./out
|
||||
python raster.py # writes PNGs into ./out
|
||||
```
|
||||
|
||||
`gen_logo.py` (SVG) and `raster.py` (PNG) share the same layout + dial geometry,
|
||||
so both outputs stay identical. Space Grotesk is licensed under the SIL Open Font
|
||||
License; outlining its glyphs into a logo is permitted.
|
||||
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate GoSentry wordmark logo assets (recommended variant:
|
||||
Space Grotesk SemiBold + amber 'G' + clock-dial 'o' with hands + 'Sentry')."""
|
||||
import os
|
||||
from fontTools.ttLib import TTFont
|
||||
from fontTools.pens.svgPathPen import SVGPathPen
|
||||
from fontTools.pens.transformPen import TransformPen
|
||||
from fontTools.pens.boundsPen import BoundsPen
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
OUT = os.path.join(HERE, "out")
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
|
||||
AMBER = "#F7A80C"
|
||||
PETROL = "#0A4A58"
|
||||
WHITE = "#FFFFFF"
|
||||
|
||||
LS = -30 # letter-spacing -0.03em at 1000 upm
|
||||
|
||||
f = TTFont(os.path.join(HERE, "SpaceGrotesk-600.ttf"))
|
||||
cmap = f.getBestCmap()
|
||||
hmtx = f["hmtx"]
|
||||
gs = f.getGlyphSet()
|
||||
|
||||
def glyph_path(ch, dx):
|
||||
"""Return SVG path 'd' for ch, shifted by dx in font units (y still up)."""
|
||||
g = cmap[ord(ch)]
|
||||
pen = SVGPathPen(gs)
|
||||
tpen = TransformPen(pen, (1, 0, 0, 1, dx, 0))
|
||||
gs[g].draw(tpen)
|
||||
return pen.getCommands(), hmtx[g][0]
|
||||
|
||||
def o_metrics():
|
||||
g = cmap[ord("o")]
|
||||
bp = BoundsPen(gs); gs[g].draw(bp)
|
||||
xmin,ymin,xmax,ymax = bp.bounds
|
||||
return hmtx[g][0], xmin, ymin, xmax, ymax
|
||||
|
||||
# ---- layout the wordmark ---------------------------------------------------
|
||||
x = 0.0
|
||||
G_d, adv = glyph_path("G", x); x += adv + LS
|
||||
|
||||
# dial occupies the 'o' advance slot
|
||||
o_adv, oxmin, oymin, oxmax, oymax = o_metrics()
|
||||
o_left = x
|
||||
cx = o_left + (oxmin + oxmax) / 2.0
|
||||
cy = (oymin + oymax) / 2.0
|
||||
R = ((oxmax - oxmin) + (oymax - oymin)) / 4.0 # avg radius, matches the 'o'
|
||||
x += o_adv + LS
|
||||
|
||||
sentry_d = []
|
||||
for ch in "Sentry":
|
||||
d, adv = glyph_path(ch, x)
|
||||
sentry_d.append(d)
|
||||
x += adv + LS
|
||||
x -= LS # no trailing letter-spacing
|
||||
SENTRY_D = " ".join(sentry_d)
|
||||
|
||||
# ---- dial geometry (matched to the 'o', proportions from the HTML mock) ----
|
||||
SW = R * 0.30 # ring stroke width
|
||||
Rmid = R - SW / 2.0 # centreline radius of ring
|
||||
HW = R * 0.21 # hand width
|
||||
hour_len = R * 0.54 # 12 o'clock hand
|
||||
min_len = R * 0.46 # ~4 o'clock hand
|
||||
min_ang = 62 # degrees clockwise from 12
|
||||
import math
|
||||
mx = cx + min_len * math.sin(math.radians(min_ang))
|
||||
my = cy + min_len * math.cos(math.radians(min_ang)) # font-up: +y is up
|
||||
cap = R * 0.14
|
||||
|
||||
dial = f'''<circle cx="{cx:.1f}" cy="{cy:.1f}" r="{Rmid:.1f}" fill="none" stroke="{AMBER}" stroke-width="{SW:.1f}"/>
|
||||
<path d="M{cx:.1f} {cy:.1f} L{cx:.1f} {cy+hour_len:.1f}" stroke="{AMBER}" stroke-width="{HW:.1f}" stroke-linecap="round"/>
|
||||
<path d="M{cx:.1f} {cy:.1f} L{mx:.1f} {my:.1f}" stroke="{AMBER}" stroke-width="{HW:.1f}" stroke-linecap="round"/>
|
||||
<circle cx="{cx:.1f}" cy="{cy:.1f}" r="{cap:.1f}" fill="{AMBER}"/>'''
|
||||
|
||||
# ---- overall bounds (font units, y up) -------------------------------------
|
||||
bp = BoundsPen(gs)
|
||||
xall = 0.0
|
||||
gG = cmap[ord("G")]; gs[gG].draw(TransformPen(bp,(1,0,0,1,0,0)))
|
||||
xall += hmtx[gG][0] + LS + o_adv + LS
|
||||
for ch in "Sentry":
|
||||
g = cmap[ord(ch)]
|
||||
gs[g].draw(TransformPen(bp,(1,0,0,1,xall,0)))
|
||||
xall += hmtx[g][0] + LS
|
||||
bx0,by0,bx1,by1 = bp.bounds
|
||||
# include the dial extents
|
||||
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
|
||||
W = (bx1 - bx0) + 2*PAD
|
||||
H = (by1 - by0) + 2*PAD
|
||||
# transform: font(x,y up) -> screen: translate then flip y
|
||||
tx = PAD - bx0
|
||||
ty = PAD + by1
|
||||
transform = f"matrix(1 0 0 -1 {tx:.2f} {ty:.2f})"
|
||||
|
||||
def svg(sentry_color, bg=None, name=""):
|
||||
bgrect = f'<rect width="{W:.1f}" height="{H:.1f}" fill="{bg}"/>\n' if bg else ""
|
||||
return f'''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W:.1f} {H:.1f}" role="img" aria-label="GoSentry">
|
||||
{bgrect}<g transform="{transform}">
|
||||
<path d="{G_d}" fill="{AMBER}"/>
|
||||
<path d="{SENTRY_D}" fill="{sentry_color}"/>
|
||||
{dial}
|
||||
</g>
|
||||
</svg>
|
||||
'''
|
||||
|
||||
variants = {
|
||||
"gosentry-logo.svg": svg(PETROL), # light bg, transparent
|
||||
"gosentry-logo-dark.svg": svg(WHITE), # dark bg, transparent
|
||||
"gosentry-logo-onlight.svg": svg(PETROL, bg="#FFFFFF"),
|
||||
"gosentry-logo-ondark.svg": svg(WHITE, bg="#04262E"),
|
||||
"gosentry-logo-mono.svg": svg(PETROL).replace(AMBER, PETROL), # single-colour petrol
|
||||
}
|
||||
for fn, data in variants.items():
|
||||
with open(os.path.join(OUT, fn), "w", encoding="utf-8") as fh:
|
||||
fh.write(data)
|
||||
print("wrote", fn)
|
||||
print("viewBox %.1f x %.1f" % (W, H))
|
||||
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 4326.0 1034.0" role="img" aria-label="GoSentry">
|
||||
<g transform="matrix(1 0 0 -1 8.00 774.00)">
|
||||
<path d="M312.0 -14Q238.0 -14 179.5 18.5Q121.0 51 86.5 113.5Q52.0 176 52.0 267V433Q52.0 569 128.0 641.5Q204.0 714 332.0 714Q460.0 714 529.0 645.5Q598.0 577 598.0 461V457H480.0V465Q480.0 505 464.0 537.0Q448.0 569 415.0 587.5Q382.0 606 332.0 606Q258.0 606 215.5 560.5Q173.0 515 173.0 435V265Q173.0 186 215.5 139.0Q258.0 92 334.0 92Q410.0 92 445.0 133.0Q480.0 174 480.0 238V250H303.0V352H598.0V0H488.0V69H471.0Q463.0 51 446.0 31.5Q429.0 12 397.5 -1.0Q366.0 -14 312.0 -14Z" fill="#F7A80C"/>
|
||||
<path d="M1524.0 -14Q1444.0 -14 1382.5 14.5Q1321.0 43 1286.0 97.0Q1251.0 151 1251.0 229V255H1370.0V229Q1370.0 160 1412.0 126.0Q1454.0 92 1524.0 92Q1595.0 92 1631.0 121.0Q1667.0 150 1667.0 196Q1667.0 227 1650.0 246.5Q1633.0 266 1600.5 278.0Q1568.0 290 1522.0 301L1492.0 307Q1423.0 323 1372.5 347.5Q1322.0 372 1295.0 411.0Q1268.0 450 1268.0 513Q1268.0 576 1298.0 621.0Q1328.0 666 1383.0 690.0Q1438.0 714 1512.0 714Q1586.0 714 1644.0 689.0Q1702.0 664 1735.5 614.0Q1769.0 564 1769.0 489V456H1650.0V489Q1650.0 532 1633.0 558.0Q1616.0 584 1585.0 596.0Q1554.0 608 1512.0 608Q1450.0 608 1418.0 584.0Q1386.0 560 1386.0 516Q1386.0 488 1400.5 468.5Q1415.0 449 1443.5 436.5Q1472.0 424 1515.0 415L1545.0 408Q1617.0 392 1671.0 367.5Q1725.0 343 1755.5 303.0Q1786.0 263 1786.0 199Q1786.0 136 1753.5 88.0Q1721.0 40 1662.5 13.0Q1604.0 -14 1524.0 -14Z M2091.0 -14Q2017.0 -14 1960.5 17.5Q1904.0 49 1872.5 106.5Q1841.0 164 1841.0 241V253Q1841.0 331 1872.0 388.0Q1903.0 445 1959.0 476.5Q2015.0 508 2088.0 508Q2160.0 508 2214.0 476.5Q2268.0 445 2298.0 388.0Q2328.0 331 2328.0 255V214H1957.0Q1959.0 156 1998.0 121.0Q2037.0 86 2094.0 86Q2150.0 86 2177.0 110.5Q2204.0 135 2218.0 166L2313.0 117Q2299.0 90 2272.5 59.5Q2246.0 29 2202.0 7.5Q2158.0 -14 2091.0 -14ZM1958.0 301H2211.0Q2207.0 350 2173.5 379.0Q2140.0 408 2087.0 408Q2032.0 408 1999.0 379.0Q1966.0 350 1958.0 301Z M2416.0 0V494H2529.0V425H2546.0Q2559.0 453 2593.0 478.0Q2627.0 503 2696.0 503Q2753.0 503 2797.0 477.0Q2841.0 451 2865.5 405.0Q2890.0 359 2890.0 296V0H2775.0V287Q2775.0 347 2745.5 376.5Q2716.0 406 2662.0 406Q2601.0 406 2566.0 365.5Q2531.0 325 2531.0 250V0Z M3189.0 0Q3141.0 0 3112.5 28.5Q3084.0 57 3084.0 106V399H2955.0V494H3084.0V653H3199.0V494H3341.0V399H3199.0V125Q3199.0 95 3227.0 95H3326.0V0Z M3428.0 0V494H3541.0V437H3558.0Q3569.0 468 3595.0 482.0Q3621.0 496 3657.0 496H3717.0V394H3655.0Q3605.0 394 3574.0 367.5Q3543.0 341 3543.0 286V0Z M3844.0 -200V-100H4117.0Q4145.0 -100 4145.0 -70V68H4128.0Q4120.0 50 4102.0 32.5Q4084.0 15 4054.0 3.5Q4024.0 -8 3978.0 -8Q3921.0 -8 3877.0 17.5Q3833.0 43 3809.0 89.5Q3785.0 136 3785.0 198V494H3899.0V207Q3899.0 147 3928.5 118.0Q3958.0 89 4012.0 89Q4073.0 89 4108.5 129.0Q4144.0 169 4144.0 244V494H4258.0V-94Q4258.0 -143 4230.0 -171.5Q4202.0 -200 4154.0 -200Z" fill="#FFFFFF"/>
|
||||
<circle cx="937.5" cy="247.0" r="220.4" fill="none" stroke="#F7A80C" stroke-width="77.8"/>
|
||||
<path d="M937.5 247.0 L937.5 387.0" stroke="#F7A80C" stroke-width="54.4" stroke-linecap="round"/>
|
||||
<path d="M937.5 247.0 L1042.8 303.0" stroke="#F7A80C" stroke-width="54.4" stroke-linecap="round"/>
|
||||
<circle cx="937.5" cy="247.0" r="36.3" fill="#F7A80C"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 4326.0 1034.0" role="img" aria-label="GoSentry">
|
||||
<g transform="matrix(1 0 0 -1 8.00 774.00)">
|
||||
<path d="M312.0 -14Q238.0 -14 179.5 18.5Q121.0 51 86.5 113.5Q52.0 176 52.0 267V433Q52.0 569 128.0 641.5Q204.0 714 332.0 714Q460.0 714 529.0 645.5Q598.0 577 598.0 461V457H480.0V465Q480.0 505 464.0 537.0Q448.0 569 415.0 587.5Q382.0 606 332.0 606Q258.0 606 215.5 560.5Q173.0 515 173.0 435V265Q173.0 186 215.5 139.0Q258.0 92 334.0 92Q410.0 92 445.0 133.0Q480.0 174 480.0 238V250H303.0V352H598.0V0H488.0V69H471.0Q463.0 51 446.0 31.5Q429.0 12 397.5 -1.0Q366.0 -14 312.0 -14Z" fill="#0A4A58"/>
|
||||
<path d="M1524.0 -14Q1444.0 -14 1382.5 14.5Q1321.0 43 1286.0 97.0Q1251.0 151 1251.0 229V255H1370.0V229Q1370.0 160 1412.0 126.0Q1454.0 92 1524.0 92Q1595.0 92 1631.0 121.0Q1667.0 150 1667.0 196Q1667.0 227 1650.0 246.5Q1633.0 266 1600.5 278.0Q1568.0 290 1522.0 301L1492.0 307Q1423.0 323 1372.5 347.5Q1322.0 372 1295.0 411.0Q1268.0 450 1268.0 513Q1268.0 576 1298.0 621.0Q1328.0 666 1383.0 690.0Q1438.0 714 1512.0 714Q1586.0 714 1644.0 689.0Q1702.0 664 1735.5 614.0Q1769.0 564 1769.0 489V456H1650.0V489Q1650.0 532 1633.0 558.0Q1616.0 584 1585.0 596.0Q1554.0 608 1512.0 608Q1450.0 608 1418.0 584.0Q1386.0 560 1386.0 516Q1386.0 488 1400.5 468.5Q1415.0 449 1443.5 436.5Q1472.0 424 1515.0 415L1545.0 408Q1617.0 392 1671.0 367.5Q1725.0 343 1755.5 303.0Q1786.0 263 1786.0 199Q1786.0 136 1753.5 88.0Q1721.0 40 1662.5 13.0Q1604.0 -14 1524.0 -14Z M2091.0 -14Q2017.0 -14 1960.5 17.5Q1904.0 49 1872.5 106.5Q1841.0 164 1841.0 241V253Q1841.0 331 1872.0 388.0Q1903.0 445 1959.0 476.5Q2015.0 508 2088.0 508Q2160.0 508 2214.0 476.5Q2268.0 445 2298.0 388.0Q2328.0 331 2328.0 255V214H1957.0Q1959.0 156 1998.0 121.0Q2037.0 86 2094.0 86Q2150.0 86 2177.0 110.5Q2204.0 135 2218.0 166L2313.0 117Q2299.0 90 2272.5 59.5Q2246.0 29 2202.0 7.5Q2158.0 -14 2091.0 -14ZM1958.0 301H2211.0Q2207.0 350 2173.5 379.0Q2140.0 408 2087.0 408Q2032.0 408 1999.0 379.0Q1966.0 350 1958.0 301Z M2416.0 0V494H2529.0V425H2546.0Q2559.0 453 2593.0 478.0Q2627.0 503 2696.0 503Q2753.0 503 2797.0 477.0Q2841.0 451 2865.5 405.0Q2890.0 359 2890.0 296V0H2775.0V287Q2775.0 347 2745.5 376.5Q2716.0 406 2662.0 406Q2601.0 406 2566.0 365.5Q2531.0 325 2531.0 250V0Z M3189.0 0Q3141.0 0 3112.5 28.5Q3084.0 57 3084.0 106V399H2955.0V494H3084.0V653H3199.0V494H3341.0V399H3199.0V125Q3199.0 95 3227.0 95H3326.0V0Z M3428.0 0V494H3541.0V437H3558.0Q3569.0 468 3595.0 482.0Q3621.0 496 3657.0 496H3717.0V394H3655.0Q3605.0 394 3574.0 367.5Q3543.0 341 3543.0 286V0Z M3844.0 -200V-100H4117.0Q4145.0 -100 4145.0 -70V68H4128.0Q4120.0 50 4102.0 32.5Q4084.0 15 4054.0 3.5Q4024.0 -8 3978.0 -8Q3921.0 -8 3877.0 17.5Q3833.0 43 3809.0 89.5Q3785.0 136 3785.0 198V494H3899.0V207Q3899.0 147 3928.5 118.0Q3958.0 89 4012.0 89Q4073.0 89 4108.5 129.0Q4144.0 169 4144.0 244V494H4258.0V-94Q4258.0 -143 4230.0 -171.5Q4202.0 -200 4154.0 -200Z" fill="#0A4A58"/>
|
||||
<circle cx="937.5" cy="247.0" r="220.4" fill="none" stroke="#0A4A58" stroke-width="77.8"/>
|
||||
<path d="M937.5 247.0 L937.5 387.0" stroke="#0A4A58" stroke-width="54.4" stroke-linecap="round"/>
|
||||
<path d="M937.5 247.0 L1042.8 303.0" stroke="#0A4A58" stroke-width="54.4" stroke-linecap="round"/>
|
||||
<circle cx="937.5" cy="247.0" r="36.3" fill="#0A4A58"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 4326.0 1034.0" role="img" aria-label="GoSentry">
|
||||
<g transform="matrix(1 0 0 -1 8.00 774.00)">
|
||||
<path d="M312.0 -14Q238.0 -14 179.5 18.5Q121.0 51 86.5 113.5Q52.0 176 52.0 267V433Q52.0 569 128.0 641.5Q204.0 714 332.0 714Q460.0 714 529.0 645.5Q598.0 577 598.0 461V457H480.0V465Q480.0 505 464.0 537.0Q448.0 569 415.0 587.5Q382.0 606 332.0 606Q258.0 606 215.5 560.5Q173.0 515 173.0 435V265Q173.0 186 215.5 139.0Q258.0 92 334.0 92Q410.0 92 445.0 133.0Q480.0 174 480.0 238V250H303.0V352H598.0V0H488.0V69H471.0Q463.0 51 446.0 31.5Q429.0 12 397.5 -1.0Q366.0 -14 312.0 -14Z" fill="#F7A80C"/>
|
||||
<path d="M1524.0 -14Q1444.0 -14 1382.5 14.5Q1321.0 43 1286.0 97.0Q1251.0 151 1251.0 229V255H1370.0V229Q1370.0 160 1412.0 126.0Q1454.0 92 1524.0 92Q1595.0 92 1631.0 121.0Q1667.0 150 1667.0 196Q1667.0 227 1650.0 246.5Q1633.0 266 1600.5 278.0Q1568.0 290 1522.0 301L1492.0 307Q1423.0 323 1372.5 347.5Q1322.0 372 1295.0 411.0Q1268.0 450 1268.0 513Q1268.0 576 1298.0 621.0Q1328.0 666 1383.0 690.0Q1438.0 714 1512.0 714Q1586.0 714 1644.0 689.0Q1702.0 664 1735.5 614.0Q1769.0 564 1769.0 489V456H1650.0V489Q1650.0 532 1633.0 558.0Q1616.0 584 1585.0 596.0Q1554.0 608 1512.0 608Q1450.0 608 1418.0 584.0Q1386.0 560 1386.0 516Q1386.0 488 1400.5 468.5Q1415.0 449 1443.5 436.5Q1472.0 424 1515.0 415L1545.0 408Q1617.0 392 1671.0 367.5Q1725.0 343 1755.5 303.0Q1786.0 263 1786.0 199Q1786.0 136 1753.5 88.0Q1721.0 40 1662.5 13.0Q1604.0 -14 1524.0 -14Z M2091.0 -14Q2017.0 -14 1960.5 17.5Q1904.0 49 1872.5 106.5Q1841.0 164 1841.0 241V253Q1841.0 331 1872.0 388.0Q1903.0 445 1959.0 476.5Q2015.0 508 2088.0 508Q2160.0 508 2214.0 476.5Q2268.0 445 2298.0 388.0Q2328.0 331 2328.0 255V214H1957.0Q1959.0 156 1998.0 121.0Q2037.0 86 2094.0 86Q2150.0 86 2177.0 110.5Q2204.0 135 2218.0 166L2313.0 117Q2299.0 90 2272.5 59.5Q2246.0 29 2202.0 7.5Q2158.0 -14 2091.0 -14ZM1958.0 301H2211.0Q2207.0 350 2173.5 379.0Q2140.0 408 2087.0 408Q2032.0 408 1999.0 379.0Q1966.0 350 1958.0 301Z M2416.0 0V494H2529.0V425H2546.0Q2559.0 453 2593.0 478.0Q2627.0 503 2696.0 503Q2753.0 503 2797.0 477.0Q2841.0 451 2865.5 405.0Q2890.0 359 2890.0 296V0H2775.0V287Q2775.0 347 2745.5 376.5Q2716.0 406 2662.0 406Q2601.0 406 2566.0 365.5Q2531.0 325 2531.0 250V0Z M3189.0 0Q3141.0 0 3112.5 28.5Q3084.0 57 3084.0 106V399H2955.0V494H3084.0V653H3199.0V494H3341.0V399H3199.0V125Q3199.0 95 3227.0 95H3326.0V0Z M3428.0 0V494H3541.0V437H3558.0Q3569.0 468 3595.0 482.0Q3621.0 496 3657.0 496H3717.0V394H3655.0Q3605.0 394 3574.0 367.5Q3543.0 341 3543.0 286V0Z M3844.0 -200V-100H4117.0Q4145.0 -100 4145.0 -70V68H4128.0Q4120.0 50 4102.0 32.5Q4084.0 15 4054.0 3.5Q4024.0 -8 3978.0 -8Q3921.0 -8 3877.0 17.5Q3833.0 43 3809.0 89.5Q3785.0 136 3785.0 198V494H3899.0V207Q3899.0 147 3928.5 118.0Q3958.0 89 4012.0 89Q4073.0 89 4108.5 129.0Q4144.0 169 4144.0 244V494H4258.0V-94Q4258.0 -143 4230.0 -171.5Q4202.0 -200 4154.0 -200Z" fill="#0A4A58"/>
|
||||
<circle cx="937.5" cy="247.0" r="220.4" fill="none" stroke="#F7A80C" stroke-width="77.8"/>
|
||||
<path d="M937.5 247.0 L937.5 387.0" stroke="#F7A80C" stroke-width="54.4" stroke-linecap="round"/>
|
||||
<path d="M937.5 247.0 L1042.8 303.0" stroke="#F7A80C" stroke-width="54.4" stroke-linecap="round"/>
|
||||
<circle cx="937.5" cy="247.0" r="36.3" fill="#F7A80C"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,97 @@
|
||||
#!/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}")
|
||||