Repository navigation
Expand file tree
/
Copy pathrender.py
More file actions
99 lines (87 loc) · 4.11 KB
/
Copy pathrender.py
File metadata and controls
99 lines (87 loc) · 4.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
"""Renders the README visuals from the HTML templates in this folder.
From the repository root:
python docs/features/src/render.py
Needs Python 3 with Pillow and Microsoft Edge (or set EDGE to a Chromium
binary). Each template is screenshotted headless and saved as WebP in
docs/features/, then a contact sheet of all of them is written to
docs/features/src/contact-sheet.webp.
"""
import os
import subprocess
import sys
import tempfile
import time
from pathlib import Path
from PIL import Image
SRC = Path(__file__).resolve().parent
OUT = SRC.parent
EDGE = os.environ.get("EDGE", r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe")
LIMIT = 250 * 1024
# output name -> (template + query, theme, height[, width, format])
VISUALS = {
"hero-dark": ("hero.html", "dark", 800),
"hero-light": ("hero.html", "light", 800),
"dictation": ("dictation.html", "dark", 800),
"formatting": ("formatting.html", "dark", 800),
"prompts": ("prompts.html", "dark", 800),
"meetings": ("meetings.html", "dark", 800),
"assistant": ("assistant.html", "dark", 800),
"investigations": ("investigations.html", "dark", 1100),
"messages": ("messages.html", "dark", 800),
"more": ("more.html", "dark", 800),
# Repository social preview (Settings > General > Social preview) and a
# 1200x630 variant for phorminx.net. PNG, because GitHub needs PNG/JPG/GIF.
"social-preview": ("social.html", "light", 640, 1280, "png"),
"social-preview-web": ("social.html?size=web", "light", 630, 1200, "png"),
}
def render(name: str, template: str, theme: str, height: int, scratch: Path,
width: int = 1600, fmt: str = "webp") -> Path:
png = scratch / f"{name}.png"
page, _, query = template.partition("?")
url = (SRC / page).as_uri() + f"?theme={theme}" + (f"&{query}" if query else "")
subprocess.run(
[EDGE, "--headless=new", "--disable-gpu", "--hide-scrollbars",
"--force-device-scale-factor=1", "--allow-file-access-from-files",
f"--user-data-dir={scratch / 'profile'}", f"--window-size={width},{height}",
"--virtual-time-budget=3000", f"--screenshot={png}", url],
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
)
# msedge.exe can return before its headless child has written the file.
for _ in range(100):
if png.exists() and png.stat().st_size:
break
time.sleep(0.2)
time.sleep(0.3)
image = Image.open(png).convert("RGB").crop((0, 0, width, height))
if fmt == "png":
out = OUT / f"{name}.png"
image.save(out, "PNG", optimize=True)
print(f"{out.name}: {image.width}x{image.height}, {out.stat().st_size // 1024} KB")
return out
out = OUT / f"{name}.webp"
for quality in (90, 84, 78, 70, 62):
image.save(out, "WEBP", quality=quality, method=6)
if out.stat().st_size <= LIMIT:
break
print(f"{out.name}: {image.width}x{image.height}, {out.stat().st_size // 1024} KB")
return out
def contact_sheet(paths: list[Path]) -> None:
thumbs = [Image.open(p).convert("RGB") for p in paths]
width = 760
rows = [(t.resize((width, round(t.height * width / t.width))), p.stem) for t, p in zip(thumbs, paths)]
columns, gap = 2, 24
heights = [max(r[0].height for r in rows[i:i + columns]) for i in range(0, len(rows), columns)]
sheet = Image.new("RGB", (columns * width + (columns + 1) * gap, sum(heights) + (len(heights) + 1) * gap), "#2a2f32")
y = gap
for row_index, row_height in enumerate(heights):
for column, (thumb, _) in enumerate(rows[row_index * columns:(row_index + 1) * columns]):
sheet.paste(thumb, (gap + column * (width + gap), y))
y += row_height + gap
sheet.save(SRC / "contact-sheet.webp", "WEBP", quality=80, method=6)
print("contact-sheet.webp written")
if __name__ == "__main__":
only = set(sys.argv[1:])
with tempfile.TemporaryDirectory() as scratch:
outputs = [render(name, *spec[:3], Path(scratch), *spec[3:]) for name, spec in VISUALS.items() if not only or name in only]
if not only:
contact_sheet([o for o in outputs if o.suffix == ".webp"])