485d105bcd
New scripts/inline_images.py (stdlib only: base64, re, mimetypes) — base64-embeds all relative-path <img src='assets/...'> images into the rendered HTML so it's redistributable without the assets/ folder. MIME-sniffs by extension (.png->image/png, .svg->image/svg+xml, etc). render_slides.sh Step 3 invokes it after the MARP HTML render, before staging. Verified: 2 images inlined, 0 file-path refs remaining. ---ci--- project: acdl phase: 3 milestone: v1.23 status: execute phase_role: execution ---/ci---
72 lines
2.0 KiB
Python
72 lines
2.0 KiB
Python
#!/usr/bin/env python3
|
|
"""scripts/inline_images.py — base64-embed all relative-path images in an
|
|
HTML file so it becomes self-contained (redistributable without the
|
|
assets/ folder).
|
|
|
|
Usage: python scripts/inline_images.py <html-path>
|
|
|
|
Stdlib only (base64, re, mimetypes, sys, pathlib).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
import mimetypes
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
IMG_SRC_RE = re.compile(
|
|
r'(<img\b[^>]*\bsrc=")(assets/[^"]+)("[^>]*>)',
|
|
re.IGNORECASE,
|
|
)
|
|
|
|
|
|
def _mime_for(path: Path) -> str:
|
|
ext = path.suffix.lower()
|
|
if ext == ".svg":
|
|
return "image/svg+xml"
|
|
guessed, _ = mimetypes.guess_type(str(path))
|
|
return guessed or "application/octet-stream"
|
|
|
|
|
|
def inline(html_path: Path) -> int:
|
|
html = html_path.read_text(encoding="utf-8")
|
|
repo_root = html_path.parent.parent.parent
|
|
|
|
count = 0
|
|
|
|
def replacer(match: re.Match[str]) -> str:
|
|
nonlocal count
|
|
prefix, rel_src, suffix = match.group(1), match.group(2), match.group(3)
|
|
img_path = html_path.parent / rel_src
|
|
if not img_path.exists():
|
|
print(f" WARNING: image not found: {rel_src}", file=sys.stderr)
|
|
return match.group(0)
|
|
mime = _mime_for(img_path)
|
|
data = base64.b64encode(img_path.read_bytes()).decode("ascii")
|
|
count += 1
|
|
return f'{prefix}data:{mime};base64,{data}{suffix}'
|
|
|
|
new_html = IMG_SRC_RE.sub(replacer, html)
|
|
|
|
if count > 0:
|
|
html_path.write_text(new_html, encoding="utf-8")
|
|
|
|
return count
|
|
|
|
|
|
def main() -> int:
|
|
if len(sys.argv) != 2:
|
|
print("Usage: python scripts/inline_images.py <html-path>", file=sys.stderr)
|
|
return 1
|
|
html_path = Path(sys.argv[1])
|
|
if not html_path.exists():
|
|
print(f"ERROR: {html_path} not found", file=sys.stderr)
|
|
return 1
|
|
count = inline(html_path)
|
|
print(f"Inlined {count} image(s) into {html_path}", file=sys.stderr)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main()) |