Noto Sans wordmark on brand green (#2F7D34), white sprout, and the official
value tagline ('keep and share traditional seeds' / 'guarda y comparte...').
Generated by tool/gen_feature_graphic.py for en-US and es-ES.
101 lines
3.1 KiB
Python
101 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate Play Store feature graphics (1024x500) + store icon (512) for Tane.
|
|
|
|
Brand green #2F7D34, white sprout from the adaptive foreground, 'Tane' wordmark
|
|
in Noto Sans (the site's brand typeface), and the official value-proposition
|
|
tagline. On-brand and replaceable with a designed asset later.
|
|
|
|
Run from apps/app_seeds/: python3 tool/gen_feature_graphic.py
|
|
"""
|
|
import os
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
REPO_APP = "/home/vjrj/proyectos/dev/tane/.claude/worktrees/interesting-cray-6261fd/apps/app_seeds"
|
|
NOTO = "/usr/share/fonts/truetype/noto"
|
|
F_BOLD = f"{NOTO}/NotoSans-Bold.ttf"
|
|
F_MED = f"{NOTO}/NotoSans-Medium.ttf"
|
|
F_REG = f"{NOTO}/NotoSans-Regular.ttf"
|
|
FG = f"{REPO_APP}/assets/icon_foreground.png"
|
|
ICON = f"{REPO_APP}/assets/icon.png"
|
|
|
|
GREEN = (47, 125, 52) # #2F7D34
|
|
GREEN_DARK = (34, 82, 30) # #22521E
|
|
WHITE = (255, 255, 255)
|
|
FAINT = (208, 228, 210)
|
|
|
|
W, H = 1024, 500
|
|
|
|
# value line = what the app is (explains it); attrs = the differentiators.
|
|
COPY = {
|
|
"en-US": ("Keep and share traditional seeds", "Offline · Encrypted · No account"),
|
|
"es-ES": ("Guarda y comparte semillas tradicionales", "Sin conexión · Cifrado · Sin cuenta"),
|
|
}
|
|
|
|
|
|
def sprout(height):
|
|
im = Image.open(FG).convert("RGBA")
|
|
bb = im.getbbox()
|
|
if bb:
|
|
im = im.crop(bb)
|
|
s = height / im.height
|
|
return im.resize((max(1, int(im.width * s)), height), Image.LANCZOS)
|
|
|
|
|
|
def bg():
|
|
top = Image.new("RGB", (W, H), GREEN)
|
|
bot = Image.new("RGB", (W, H), GREEN_DARK)
|
|
mask = Image.new("L", (W, H))
|
|
md = ImageDraw.Draw(mask)
|
|
for y in range(H):
|
|
md.line([(0, y), (W, y)], fill=int(60 * (y / H)))
|
|
return Image.composite(bot, top, mask)
|
|
|
|
|
|
def center_line(d, text, font, top, fill):
|
|
b = d.textbbox((0, 0), text, font=font)
|
|
w = b[2] - b[0]
|
|
d.text(((W - w) / 2 - b[0], top - b[1]), text, font=font, fill=fill)
|
|
return b[3] - b[1]
|
|
|
|
|
|
def feature_graphic(locale, out):
|
|
img = bg()
|
|
d = ImageDraw.Draw(img)
|
|
value, attrs = COPY[locale]
|
|
|
|
# Row 1: sprout + 'Tane', centered as a group in the upper half.
|
|
spr = sprout(150)
|
|
wf = ImageFont.truetype(F_BOLD, 120)
|
|
wb = d.textbbox((0, 0), "Tane", font=wf)
|
|
ww, wh = wb[2] - wb[0], wb[3] - wb[1]
|
|
gap = 26
|
|
gw = spr.width + gap + ww
|
|
gx = (W - gw) // 2
|
|
row_top = 92
|
|
cy = row_top + spr.height / 2
|
|
img.paste(spr, (gx, row_top), spr)
|
|
d.text((gx + spr.width + gap - wb[0], cy - wh / 2 - wb[1]), "Tane", font=wf, fill=WHITE)
|
|
y = row_top + spr.height + 34
|
|
|
|
# Row 2: value proposition (explains the app).
|
|
vf = ImageFont.truetype(F_MED, 42)
|
|
y += center_line(d, value, vf, y, WHITE) + 18
|
|
|
|
# Row 3: differentiators, quieter.
|
|
af = ImageFont.truetype(F_REG, 30)
|
|
center_line(d, attrs, af, y, FAINT)
|
|
|
|
img.save(out, "PNG")
|
|
print("wrote", out, img.size)
|
|
|
|
|
|
def store_icon(out):
|
|
Image.open(ICON).convert("RGB").resize((512, 512), Image.LANCZOS).save(out, "PNG")
|
|
print("wrote", out)
|
|
|
|
|
|
for loc in ("en-US", "es-ES"):
|
|
d = f"{REPO_APP}/fastlane/metadata/android/{loc}/images"
|
|
os.makedirs(d, exist_ok=True)
|
|
feature_graphic(loc, f"{d}/featureGraphic.png")
|
|
store_icon(f"{d}/icon.png")
|