"""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, big IQ number,
percentile line, signature badge. Cached on disk per result_id.
"""
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)
TEXT = (232, 232, 234)
MUTED = (138, 138, 148)
ACCENT = (122, 162, 247)
GOOD = (158, 206, 106)
BAD = (247, 118, 142)
WARN = (224, 175, 104)
_FONT_DIRS = [
"/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", 44),
"big": _font("DejaVuSans-Bold.ttf", 240),
"sub": _font("DejaVuSans.ttf", 40),
"small": _font("DejaVuSans.ttf", 28),
"badge": _font("DejaVuSans-Bold.ttf", 30),
}
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 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 / (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
d.text((48, 44), "Ver", font=f["logo"], fill=TEXT)
logo_w = d.textbbox((0, 0), "Ver", font=f["logo"])[2]
d.text((48 + logo_w, 44), "IQ", font=f["logo"], fill=ACCENT)
# big score
_center(d, str(iq), 130, f["big"], ACCENT)
# percentile line
_center(d, "higher than " + str(percentile) + "% of test takers", 400,
f["sub"], TEXT)
# badges row
y = 480
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 + 56], outline=badge_color, width=3)
d.text((bx + 24, y + 10), 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 + 56], outline=WARN, width=3)
d.text((fx + 20, y + 10), "FLAGGED", font=f["badge"], fill=WARN)
# footer
_center(d, "veriq · verify at /v/" + result_id + " · signed with Ed25519",
560, f["small"], MUTED)
img.save(out, "PNG", optimize=True)
return out