import html
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
OUT = ROOT / "assets" / "banner.svg"
BG, CARD = "#171717", "#3a3320"
INK = "#ddd6c6" GOLD = "#d4b24a" GOLDLF = "#f4cb4b" RED = "#e5675a" BLUE = "#6a93d4"
FS, CW, LH, PAD = 15.5, 9.3, 18.5, 18.0
FONT = "ui-monospace, SFMono-Regular, Menlo, Consolas, "DejaVu Sans Mono", monospace"
def ansi() -> str:
exe = ROOT / "target" / "release" / "lindisfarner"
cmd = [str(exe)] if exe.exists() else ["cargo", "run", "-q", "--release", "--"]
out = subprocess.run(
cmd
+ [
"sample.txt",
"-c",
"always",
"--columns",
"2",
"--justify",
"--incipit",
"--fillers",
"--drolleries",
"--pilcrows",
"-r",
"kidneys,bloom,cat,tea",
"-w",
"96",
],
cwd=ROOT,
capture_output=True,
text=True,
check=True,
)
return out.stdout
def style(codes, bold):
if 33 in codes:
return (GOLDLF if bold else GOLD, bold)
if 31 in codes:
return (RED, bold) if 34 in codes:
return (BLUE, bold) return (INK, bold)
def runs(line):
out, cur, codes, bold, i = [], "", set(), False, 0
while i < len(line):
c = line[i]
if c == "\x1b" and line[i + 1 : i + 2] == "[":
j = line.index("m", i)
if cur:
out.append((cur, *style(codes, bold)))
cur = ""
for n in [int(x) for x in line[i + 2 : j].split(";") if x] or [0]:
if n == 0:
codes.clear()
bold = False
elif n == 1:
bold = True
else:
codes.add(n)
i = j + 1
else:
cur += c
i += 1
if cur:
out.append((cur, *style(codes, bold)))
return out
def main():
lines = ansi().split("\n")
while lines and lines[-1] == "":
lines.pop()
cols = max(sum(len(t) for t, _, _ in runs(l)) for l in lines)
w, h = PAD * 2 + cols * CW, PAD * 2 + len(lines) * LH
s = [
"<!-- Generated by scripts/render-banner.py — the most elaborate lindisfarner page. Do not edit by hand. -->",
f'<svg xmlns="http://www.w3.org/2000/svg" width="{w:.0f}" height="{h:.0f}" '
f'viewBox="0 0 {w:.0f} {h:.0f}" font-family="{FONT}" font-size="{FS}">',
f'<rect x="0.5" y="0.5" width="{w - 1:.0f}" height="{h - 1:.0f}" rx="11" fill="{BG}" stroke="{CARD}"/>',
]
for row, line in enumerate(lines):
y = PAD + row * LH + FS * 0.78
col = 0
for text, color, bold in runs(line):
seg, segx = "", None
def flush(buf, bx):
weight = ' font-weight="bold"' if bold else ""
xs = " ".join(f"{PAD + (bx + k) * CW:.2f}" for k in range(len(buf)))
s.append(
f'<text xml:space="preserve" y="{y:.2f}" x="{xs}" '
f'fill="{color}"{weight}>{html.escape(buf)}</text>'
)
for ch in text:
if ch == " ":
if seg:
flush(seg, segx)
seg, segx = "", None
col += 1
else:
if not seg:
segx = col
seg += ch
col += 1
if seg:
flush(seg, segx)
s.append("</svg>")
OUT.write_text("\n".join(s) + "\n")
print(f"wrote {OUT.relative_to(ROOT)} ({w:.0f}x{h:.0f}, {cols} cols, {len(lines)} rows)")
if __name__ == "__main__":
sys.exit(main())