"""VerIQ — OG image generation (1200x630 PNG cards for social scrapers).
Scrapers (X/Twitter, Telegram, Facebook) don't render SVG and need a real
PNG. We draw the card with Pillow: dark brutalist background, huge IQ number,
percentile line, bell-curve strip with a marker, signature badge, verify URL.
Fonts are bundled in backend/fonts/ so the card renders identically anywhere.
Cached on disk per result_id (design version prefixed).
"""
import os
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
CACHE_DIR = Path(os.environ.get("VERIQ_OG_CACHE", "/var/lib/veriq/og_cache"))
W, H = 1200, 630
BG = (13, 13, 15)
LINE = (42, 42, 51)
CURVE_FILL = (38, 47, 66)
TEXT = (232, 232, 234)
MUTED = (138, 138, 148)
ACCENT = (122, 162, 247)
GOOD = (158, 206, 106)
BAD = (247, 118, 142)
WARN = (224, 175, 104)
DESIGN = "r2" # bump to invalidate the on-disk cache after card redesign
_FONT_DIRS = [
str(Path(__file__).resolve().parent / "fonts"), # bundled (repo)
"/usr/share/fonts/truetype/dejavu/",
"/usr/share/fonts/dejavu/",
]
def _font(name, size):
for d in _FONT_DIRS:
p = Path(d) / name
if p.exists():
return ImageFont.truetype(str(p), size)
return ImageFont.load_default()
def _fonts():
return {
"logo": _font("DejaVuSans-Bold.ttf", 40),
"domain": _font("DejaVuSans.ttf", 26),
"big": _font("DejaVuSans-Bold.ttf", 250),
"sub": _font("DejaVuSans-Bold.ttf", 42),
"small": _font("DejaVuSans.ttf", 26),
"badge": _font("DejaVuSans-Bold.ttf", 28),
"url": _font("DejaVuSans-Bold.ttf", 32),
}
def _center(draw, text, y, font, fill):
bbox = draw.textbbox((0, 0), text, font=font)
w = bbox[2] - bbox[0]
draw.text(((W - w) / 2, y), text, font=font, fill=fill)
def _bell_strip(d, iq, y, h):
"""Bell-curve strip: subtle filled gaussian + accent marker at iq."""
x0, x1 = 200, W - 200
lo, hi = 55.0, 145.0
mu, sd = 100.0, 15.0
def fx(iqv):
t = (iqv - lo) / (hi - lo)
return x0 + t * (x1 - x0)
def gauss(iqv):
return ((iqv - mu) / sd) ** 2
g_edge = gauss(lo) # 9.0 — density at strip edges
pts = [(x0, y + h)]
steps = 120
for i in range(steps + 1):
iqv = lo + (hi - lo) * i / steps
t = 1.0 - gauss(iqv) / g_edge # 1 at center, 0 at edges
pts.append((fx(iqv), y + h - t * (h - 6)))
pts.append((x1, y + h))
d.polygon(pts, fill=CURVE_FILL)
# baseline
d.line([x0, y + h, x1, y + h], fill=LINE, width=2)
# marker at user's iq
mx = fx(max(lo, min(hi, iq)))
d.line([mx, y - 6, mx, y + h], fill=ACCENT, width=5)
d.polygon([(mx - 9, y - 16), (mx + 9, y - 16), (mx, y - 4)], fill=ACCENT)
def generate_card(iq, percentile, signature_valid, flagged, result_id):
"""Render the OG card, return path to cached PNG."""
try:
CACHE_DIR.mkdir(parents=True, exist_ok=True)
cache = CACHE_DIR
except PermissionError:
import tempfile
cache = Path(tempfile.gettempdir()) / "veriq_og"
cache.mkdir(parents=True, exist_ok=True)
out = cache / (DESIGN + "-" + result_id + ".png")
if out.exists():
return out
img = Image.new("RGB", (W, H), BG)
d = ImageDraw.Draw(img)
f = _fonts()
# frame
d.rectangle([8, 8, W - 9, H - 9], outline=LINE, width=2)
# accent corner blocks (brutalist)
d.rectangle([8, 8, 8 + 56, 8 + 10], fill=ACCENT)
d.rectangle([8, 8, 8 + 10, 8 + 56], fill=ACCENT)
d.rectangle([W - 9 - 56, H - 9 - 10, W - 9, H - 9], fill=ACCENT)
d.rectangle([W - 9 - 10, H - 9 - 56, W - 9, H - 9], fill=ACCENT)
# logo top-left, domain top-right
d.text((48, 40), "Ver", font=f["logo"], fill=TEXT)
logo_w = d.textbbox((0, 0), "Ver", font=f["logo"])[2]
d.text((48 + logo_w, 40), "IQ", font=f["logo"], fill=ACCENT)
dom = "veriq.gnexus.space"
dbbox = d.textbbox((0, 0), dom, font=f["domain"])
d.text((W - 48 - (dbbox[2] - dbbox[0]), 48), dom, font=f["domain"], fill=MUTED)
# huge score — the hero
_center(d, str(iq), 88, f["big"], TEXT)
# percentile line
_center(d, "higher than " + str(percentile) + "% of test takers", 352,
f["sub"], ACCENT)
# bell-curve strip with marker
_bell_strip(d, iq, 418, 74)
# badges row
y = 516
if signature_valid:
badge_text = "SIGNATURE VALID"
badge_color = GOOD
else:
badge_text = "SIGNATURE INVALID"
badge_color = BAD
bbox = d.textbbox((0, 0), badge_text, font=f["badge"])
bw = bbox[2] - bbox[0] + 48
bx = (W - bw) / 2
d.rectangle([bx, y, bx + bw, y + 52], outline=badge_color, width=3)
d.text((bx + 24, y + 9), badge_text, font=f["badge"], fill=badge_color)
if flagged:
fx = bx + bw + 16
fb = d.textbbox((0, 0), "FLAGGED", font=f["badge"])
fw = fb[2] - fb[0] + 40
d.rectangle([fx, y, fx + fw, y + 52], outline=WARN, width=3)
d.text((fx + 20, y + 9), "FLAGGED", font=f["badge"], fill=WARN)
# verify URL — the call to action
_center(d, "veriq.gnexus.space/v/" + result_id, 582, f["url"], TEXT)
img.save(out, "PNG", optimize=True)
return out