OG Image Module
On-the-fly Open Graph image generation for django-cfg. Renders PNG images using PicTex, caches them in MEDIA_ROOT/ogimage/.
No database, no models, no migrations — pure library.
How it works
GET /og/<b64params>/
→ decode base64 → OGImageParams
→ check MEDIA_ROOT/ogimage/<key[:2]>/<key[2:4]>/<key>.png
→ HIT → FileResponse (instant)
→ MISS → render(params) → save → FileResponseThe cache key is a SHA256[:40] of the stable params (everything except page_url). Same params always produce the same file — no redundant renders. Files are stored in a two-level sharded layout (like Git objects) to avoid inode limits on large deployments.
Setup
URLs
URLs are registered automatically via add_django_cfg_urls():
# project/urls.py
from django_cfg import add_django_cfg_urls
urlpatterns = add_django_cfg_urls(urlpatterns)
# exposes: GET /og/<b64params>/ (name: og-render)Media serving is also handled automatically — no extra configuration needed.
Renderer
Uses PicTex (pictex>=2.1.0) — available in djangocfg[full]. PicTex supports gradients, RTL text shaping (Arabic, Hebrew), and CJK fonts.
Quick start
With a preset
from django_cfg.modules.django_ogimage import get_or_create_og_url, DARK_BLUE
# Returns /media/ogimage/.../....png — renders on first call, instant on subsequent
url = get_or_create_og_url(DARK_BLUE.to_params(title="My Page", description="..."))
# Absolute URL
url = get_or_create_og_url(DARK_BLUE.to_params(title="My Page"), request=request)By preset name
from django_cfg.modules.django_ogimage import get_or_create_og_url, get_preset
url = get_or_create_og_url(get_preset("dark_blue").to_params(title="My Page"))With branding — logo / site name / layout
from django_cfg.modules.django_ogimage import get_branded_og_url, DARK_BLUE, HERO
params = DARK_BLUE.to_params(title="My Page", description="Subtitle")
# Site name only
url = get_branded_og_url(params, site_name="Acme Corp")
# Built-in Material icon + site name
url = get_branded_og_url(params, icon="dashboard", site_name="Acme Corp")
# External logo file + custom layout + absolute URL
url = get_branded_og_url(
params,
logo_url="/path/to/logo.png",
site_name="Acme Corp",
layout=HERO,
request=request,
)get_branded_og_url has its own combined cache key — identical inputs never re-render.
In a DRF serializer
from django_cfg.modules.django_ogimage import get_branded_og_url, get_preset
class ArticleSerializer(serializers.ModelSerializer):
og_image_url = serializers.SerializerMethodField()
def get_og_image_url(self, obj):
params = get_preset("dark_blue").to_params(
title=obj.title,
description=obj.excerpt,
)
return get_branded_og_url(
params,
site_name="My Site",
request=self.context.get("request"),
)Typed utility function
from django_cfg.modules.django_ogimage import get_or_create_og_url, get_preset, OGImagePreset
def render_og(title: str, preset: str | OGImagePreset = "dark_blue") -> str:
if isinstance(preset, str):
preset = get_preset(preset) # raises ValueError for unknown names
return get_or_create_og_url(preset.to_params(title=title))
render_og("Hello") # uses dark_blue
render_og("Hello", "light_warm") # by name
render_og("Hello", DARK_BLUE) # by objectBuild a shareable render URL
Instead of returning the media file path directly, you can build a URL pointing to the render endpoint. The browser will hit /og/<b64params>/ and get the PNG:
from django_cfg.modules.django_ogimage import OGImageParams, build_og_url
params = OGImageParams(title="My Page", description="...", style="dark")
url = build_og_url(params, request=request)
# → https://example.com/og/eyJ0aXRsZSI6...Render PNG directly (no HTTP)
from django_cfg.modules.django_ogimage import render, OGImageParams
png_bytes = render(OGImageParams(title="Hello", style="light"))OGImageParams fields
| Field | Default | Description |
|---|---|---|
title | required | Main heading text (max 300 chars) |
description | "" | Subtitle / body text (max 600 chars) |
locale | "en" | BCP 47 locale — selects font |
style | "dark" | dark or light |
bg_color | "#1a1a2e" | Background color (hex) |
bg_color2 | "#16213e" | Gradient / secondary background (hex) |
text_color | "#ffffff" | Text color (hex) |
accent_color | "#3b82f6" | Accent / highlight color (hex) |
size | "1200x630" | Output dimensions |
page_url | "" | Source URL — excluded from cache key |
Supported sizes
1200x630 (default) · 1200x600 · 800x418 · 1200x1200
Supported locales
Fonts are not bundled with the package — they are downloaded on first use and cached in ~/.cache/django_cfg/fonts/. At Django startup all fonts begin downloading in background threads so they’re ready before the first request.
| Locale | Font | Download size |
|---|---|---|
en, * | Inter (variable TTF) | ~350 KB |
ko | Noto Sans KR | ~3.5 MB |
ja | Noto Sans JP | ~4.2 MB |
zh | Noto Sans SC | ~6.5 MB |
ar | Noto Sans Arabic (RTL) | ~300 KB |
he | Noto Sans Hebrew (RTL) | ~200 KB |
If a font download fails the module falls back to a system font silently — no exception is raised. Override the cache directory with DJANGO_CFG_FONTS_DIR.
Related
- Presets — built-in color schemes and
build_preset() - Layouts — layout presets (DEFAULT, HERO, ARTICLE, MINIMAL)
- Branding & Icons —
get_branded_og_url, logo, built-in icons - Configuration — environment variables