#!/usr/bin/env bash # # Generate the Open Graph / Twitter link-preview card. # # Produces a 1200x630 branded card (the summary_large_image spec) from the # headshot + site theme, written to static/og-preview.jpg. Re-run this whenever # the headshot or branding changes, then redeploy and re-scrape the social # crawlers (see the OG meta tags in src/routes/+layout.svelte). # # Requires ImageMagick 7 (`magick`). Run from anywhere; paths are repo-relative. # # Usage: ./scripts/generate-og-image.sh set -euo pipefail # --- locate repo + inputs --------------------------------------------------- REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$REPO_ROOT" SRC_HEADSHOT="static/headshot.jpg" OUT="static/og-preview.jpg" if ! command -v magick >/dev/null 2>&1; then echo "error: ImageMagick 7 (magick) not found on PATH" >&2 exit 1 fi if [[ ! -f "$SRC_HEADSHOT" ]]; then echo "error: $SRC_HEADSHOT not found" >&2 exit 1 fi # --- pick a bold + regular serif font (with fallbacks) ---------------------- pick_font() { for f in "$@"; do [[ -f "$f" ]] && { echo "$f"; return 0; } done # Last resort: ask fontconfig for any serif. fc-match -f '%{file}' serif 2>/dev/null || true } SERIF_B="$(pick_font \ /usr/share/fonts/TTF/DejaVuSerif-Bold.ttf \ /usr/share/fonts/truetype/dejavu/DejaVuSerif-Bold.ttf \ /usr/share/fonts/liberation/LiberationSerif-Bold.ttf)" SERIF="$(pick_font \ /usr/share/fonts/TTF/DejaVuSerif.ttf \ /usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf \ /usr/share/fonts/liberation/LiberationSerif-Regular.ttf)" : "${SERIF_B:?no bold serif font found}" : "${SERIF:?no serif font found}" # --- theme colours (mirror src/lib/theme.ts) -------------------------------- GOLD='#ffd700' GOLD_SOFT='#e8c95a' MUTED='#a8a8b8' TMP="$(mktemp -d)" trap 'rm -rf "$TMP"' EXIT # 1) Dark radial-gradient base (matches the #05050a space theme) magick -size 1200x630 radial-gradient:'#16162c'-'#05050a' "$TMP/base.png" # 2) Circular-crop the headshot to 300x300 magick "$SRC_HEADSHOT" -resize 300x300^ -gravity center -extent 300x300 \ \( -size 300x300 xc:none -fill white -draw "circle 150,150 150,0" \) \ -alpha set -compose CopyOpacity -composite "$TMP/headshot_circle.png" # 3) Composite headshot (centre 330,315) + gold ring + faint outer ring magick "$TMP/base.png" "$TMP/headshot_circle.png" -geometry +180+165 -compose over -composite \ -fill none -stroke "$GOLD" -strokewidth 5 -draw "circle 330,315 330,165" \ -stroke "$GOLD" -strokewidth 2 -draw "fill-opacity 0.4 circle 330,315 330,157" \ "$TMP/with_headshot.png" # 4) Text block (right side) + divider rule magick "$TMP/with_headshot.png" \ -font "$SERIF_B" -fill "$GOLD" -pointsize 92 -gravity NorthWest -annotate +540+205 'Kevin Riehl' \ -stroke "$GOLD" -strokewidth 2 -draw "line 545,330 980,330" -stroke none \ -font "$SERIF" -fill "$GOLD_SOFT" -pointsize 34 -kerning 6 -annotate +547+360 'THE ATLAS OF SKILLS' \ -font "$SERIF" -fill "$MUTED" -pointsize 26 -kerning 0 -annotate +547+420 'Interactive skill-tree portfolio' \ -fill "$MUTED" -pointsize 26 -annotate +547+455 'Experience · Projects · Skills' \ "$TMP/card.png" # 5) Flatten to an optimised, progressive JPG magick "$TMP/card.png" -strip -interlace Plane -quality 90 "$OUT" echo "wrote $OUT ($(magick identify -format '%wx%h, %B bytes' "$OUT"))"