"""Step 1~4를 같은 슬라이드 레이아웃 위에 레이어로 쌓아 PNG 생성.""" import json, urllib.parse, time, sys from pathlib import Path sys.path.insert(0, ".") run_dir = Path("data/runs/20260402_091318") ctx_1a = json.loads((run_dir / "stage_1a_context.json").read_text(encoding="utf-8")) ctx_1b = json.loads((run_dir / "stage_1b_context.json").read_text(encoding="utf-8")) ctx_15a = json.loads((run_dir / "stage_1_5a_context.json").read_text(encoding="utf-8")) ctx_17 = json.loads((run_dir / "stage_1_7_context.json").read_text(encoding="utf-8")) ctx_15b = json.loads((run_dir / "stage_1_5b_context.json").read_text(encoding="utf-8")) topics = ctx_1b.get("topics", []) containers = ctx_15a.get("containers", {}) fh = ctx_15a.get("font_hierarchy", {}) ratio = ctx_15a.get("container_ratio", [72, 28]) refs = ctx_17.get("references", {}) ps = ctx_1a.get("page_structure", {}) if "roles" in ps: ps = ps["roles"] containers_b = ctx_15b.get("containers", {}) topic_map = {t["id"]: t for t in topics} slide_w, slide_h = 1280, 720 pad = 40 header_h = 66 gap = 20 footer_h = containers.get("결론", {}).get("height_px", 60) inner_w = slide_w - pad * 2 body_pct = ratio[0] if ratio else 72 sidebar_pct = ratio[1] if len(ratio) > 1 else 28 body_w = int(inner_w * body_pct / 100) sidebar_w = inner_w - body_w - gap body_zone_h = slide_h - pad * 2 - header_h - footer_h - gap * 2 bg_h = containers.get("배경", {}).get("height_px", 117) core_h = body_zone_h - bg_h - 12 L = { "배경": {"x": pad, "y": pad+header_h+gap, "w": body_w, "h": bg_h}, "본심": {"x": pad, "y": pad+header_h+gap+bg_h+12, "w": body_w, "h": core_h}, "첨부": {"x": pad+body_w+gap, "y": pad+header_h+gap, "w": sidebar_w, "h": body_zone_h}, "결론": {"x": pad, "y": slide_h-pad-footer_h, "w": inner_w, "h": footer_h}, } C = {"배경": "#dc2626", "본심": "#2563eb", "첨부": "#16a34a", "결론": "#7c3aed"} def area(role, inner): p = L[role]; c = C[role] return (f'
{inner}
') def header(title): return (f'
건설산업 DX의 올바른 이해
') def slide(step_title, areas_html): return (f'' f'' f'
{step_title}
' f'
' f'{header(step_title)}{areas_html}
') # Step 1 a1 = "" for role in L: c = C[role]; p = L[role] fk = {"배경":"bg","본심":"core","첨부":"sidebar","결론":"key_msg"}.get(role,"core") fv = fh.get(fk, 12) a1 += area(role, f'
' f'{role}
' f'{p["w"]}x{p["h"]}px / font:{fv}px
') # Step 2 a2 = "" for role in L: c = C[role]; info = ps.get(role, {}); tids = info.get("topic_ids", []) w = info.get("weight", 0) inner = f'
{role} (w:{w})
' for tid in tids: t = topic_map.get(tid, {}) inner += (f'
' f'T{tid}: {t.get("title","")[:25]}
' f'' f'{t.get("purpose","")} / {t.get("relation_type","")}
') a2 += area(role, inner) # Step 3 a3 = "" for role in L: c = C[role]; ref = refs.get(role, {}); p = L[role] bid = ref.get("block_id", "?"); vtype = ref.get("visual_type", "?") info = ps.get(role, {}); tids = info.get("topic_ids", []) tnames = ", ".join(f"T{tid}" for tid in tids) mt = max(0, p["h"]//2-25) a3 += area(role, f'
' f'
{role} ({tnames})
' f'
📦
' f'
{bid}
' f'
type: {vtype}
') # Step 4 a4 = "" for role in L: c = C[role]; ref = refs.get(role, {}); p = L[role] bid = ref.get("block_id", "?") cb = containers_b.get(role, {}); db = cb.get("design_budget") or {} text_h = db.get("text_height_px", 0) avail_h = db.get("available_height_px", 0) fits = db.get("fits", False) total = max(text_h + avail_h, 1) tp = int(text_h / total * 100) bw = p["w"] - 20 fc = "green" if fits else "red" a4 += area(role, f'
' f'
{role}: {bid}
' f'
' f'
텍스트{text_h}px
' f'
여유{avail_h}px
' f'
' f'
fits:{fits} / {p["w"]}x{p["h"]}px
') htmls = { "viz_1_containers": slide(f"Step 1: 컨테이너 포션과 위치 (비율 {body_pct}:{sidebar_pct})", a1), "viz_2_content": slide("Step 2: 각 영역별 내용 배치", a2), "viz_3_blocks": slide("Step 3: 블록 선택 결과", a3), "viz_4_budget": slide("Step 4: 블록별 디자인 예산", a4), } for name, html in htmls.items(): (run_dir / f"{name}.html").write_text(html, encoding="utf-8") # PNG from selenium import webdriver from selenium.webdriver.chrome.options import Options opts = Options() opts.add_argument("--headless") opts.add_argument("--no-sandbox") opts.add_argument("--force-device-scale-factor=2") driver = webdriver.Chrome(options=opts) driver.set_window_size(1380, 820) for name in htmls: html = (run_dir / f"{name}.html").read_text(encoding="utf-8") encoded = urllib.parse.quote(html, safe="") driver.get(f"data:text/html;charset=utf-8,{encoded}") time.sleep(2) driver.save_screenshot(str(run_dir / f"{name}.png")) print(f"{name}.png") driver.quit() print("완료")