sites/randogoth.com/plugins/crtimages.py

57 lines
1.9 KiB
Python
Raw Permalink Normal View History

"""Wrap every image in an article body so it can carry a CRT overlay.
The scanlines belong on the pictures, not on the whole screen. A board showing
a photograph was a monitor displaying it; the text around it is just text, and
a gradient over the entire page shimmers at non-integer zoom.
An <img> cannot host a ::after of its own, so each one gets a wrapper. Done here
rather than with CSS :has() because images are not reliably alone in their
paragraph, and :has() would miss the ones that are not.
"""
import re
from pelican import signals
IMG = re.compile(r'<img\b[^>]*>')
IFRAME = re.compile(r'<iframe\b[^>]*>')
DIM = re.compile(r'\b(width|height)="(\d+)"')
def _wrap(content):
body = getattr(content, "_content", None)
if not body or "<img" not in body:
return
# skip images already wrapped, e.g. if the signal fires twice
if 'class="crt"' in body:
return
content._content = IMG.sub(lambda m: f'<span class="crt">{m.group(0)}</span>', body)
def _ratio(content):
"""Give every embed its own aspect ratio so it can fill the column.
The embeds carry fixed pixel sizes from whenever they were pasted in, and
they are not all the same shape: four are 16:9 but one is 500x402. Forcing a
single ratio would letterbox or crop that one, so each gets the ratio its own
attributes describe, and anything without attributes falls back to 16:9.
"""
body = getattr(content, "_content", None)
if not body or "<iframe" not in body:
return
def sub(m):
tag = m.group(0)
if "aspect-ratio" in tag:
return tag
d = dict(DIM.findall(tag))
w, h = d.get("width"), d.get("height")
ratio = f"{w}/{h}" if w and h and int(h) else "16/9"
return tag[:-1].rstrip() + f' style="aspect-ratio:{ratio}">'
content._content = IFRAME.sub(sub, body)
def register():
signals.content_object_init.connect(_wrap)
signals.content_object_init.connect(_ratio)