V'-2/V'-4 수정: 표 행 수 계산 + footer 최소 높이 + 이미지 비율
- V'-2: 표 공간 계산에 V'-4(결론 위까지 채움) 높이 반영 → Kei에게 정확한 행 수 전달 (1행 → 5행) - V'-2: 이미지 높이를 실제 비율로 계산 (sub_layout 고정값 대신) → 200/2.73 = 73px (기존 172px → 공간 100px 확보) - footer 최소 높이: design tokens 기반 동적 계산 → weight 0.05일 때 26px → 53px 보장 - assemble_stage2: 이미지 높이도 실제 비율 반영 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -386,7 +386,10 @@ def assemble(run_dir: str):
|
|||||||
break
|
break
|
||||||
|
|
||||||
svg_w = int(svg_sc["width_px"]) if svg_sc else 200
|
svg_w = int(svg_sc["width_px"]) if svg_sc else 200
|
||||||
svg_h = int(svg_sc["height_px"]) if svg_sc else 265
|
# 이미지 높이: 실제 비율로 계산 (sub_layout 고정값 대신)
|
||||||
|
slide_images = ctx.get("slide_images", [])
|
||||||
|
img_ratio = next((img.get("ratio", 1) for img in slide_images if img.get("b64")), 1)
|
||||||
|
svg_h = int(svg_w / img_ratio) if img_ratio > 0 else int(svg_sc["height_px"]) if svg_sc else 265
|
||||||
# 본심의 모든 topic 텍스트를 합침
|
# 본심의 모든 topic 텍스트를 합침
|
||||||
all_core_text = "\n".join(get_text(topic_map.get(tid, {})) for tid in tids if topic_map.get(tid))
|
all_core_text = "\n".join(get_text(topic_map.get(tid, {})) for tid in tids if topic_map.get(tid))
|
||||||
|
|
||||||
|
|||||||
@@ -716,19 +716,39 @@ async def generate_slide(
|
|||||||
popup = next((p for p in popups if pr in p.get("title", "")), None)
|
popup = next((p for p in popups if pr in p.get("title", "")), None)
|
||||||
if not popup:
|
if not popup:
|
||||||
continue
|
continue
|
||||||
# 공란 계산: after(updated_containers) 기준
|
# 공란 계산: V'-4 적용 후 높이 (결론 바로 위까지 채움)
|
||||||
after_ci = updated_containers.get(role)
|
from src.fit_verifier import _load_design_tokens as _ldt_v2
|
||||||
after_h = after_ci.height_px if after_ci else float(text_sc["height_px"])
|
_v2_tokens = _ldt_v2()
|
||||||
# after 높이에서 제목+keymsg+padding 제외 → 텍스트 영역
|
_v2_slide_h = _v2_tokens.get("slide_height", 720)
|
||||||
|
_v2_pad = _v2_tokens["spacing_page"]
|
||||||
|
_v2_header_h = _v2_tokens.get("header_height", 66)
|
||||||
|
_v2_gap = _v2_tokens["spacing_block"]
|
||||||
|
_v2_gap_small = _v2_tokens["spacing_small"]
|
||||||
|
_v2_concl_ci = updated_containers.get("결론") or next((ci for r, ci in updated_containers.items() if ci.zone == "footer"), None)
|
||||||
|
_v2_concl_h = _v2_concl_ci.height_px if _v2_concl_ci else 53
|
||||||
|
_v2_ft_top = _v2_slide_h - _v2_pad - _v2_concl_h - _v2_gap
|
||||||
|
_v2_column_bottom = _v2_ft_top - _v2_gap
|
||||||
|
_v2_bg_ci = next((ci for r, ci in updated_containers.items() if ci.zone == "body" and r != role), None)
|
||||||
|
_v2_bg_h = _v2_bg_ci.height_px if _v2_bg_ci else 0
|
||||||
|
_v2_core_top = _v2_pad + _v2_header_h + _v2_gap + _v2_bg_h + _v2_gap_small
|
||||||
|
after_h = _v2_column_bottom - _v2_core_top # 결론 위까지의 실제 본심 높이
|
||||||
keymsg_h = 0
|
keymsg_h = 0
|
||||||
for sc in role_scs:
|
for sc in role_scs:
|
||||||
if sc.get("name") == "keymsg":
|
if sc.get("name") == "keymsg":
|
||||||
keymsg_h = float(sc.get("height_px", 0))
|
keymsg_h = float(sc.get("height_px", 0))
|
||||||
title_h = (fs + 1) * 1.5 + 4
|
title_h = (fs + 1) * 1.5 + 4
|
||||||
content_area_h = after_h - 16 - title_h - keymsg_h - 8 # pad*2 + gap
|
# 이미지 높이: 실제 비율로 계산
|
||||||
|
svg_sc = next((sc for sc in role_scs if sc.get("name") == "svg"), None)
|
||||||
|
img_h = 0
|
||||||
|
if svg_sc:
|
||||||
|
svg_w = float(svg_sc.get("width_px", 200))
|
||||||
|
img_ratio = next((img.get("ratio", 1) for img in (context.slide_images or []) if img.get("b64")), 1)
|
||||||
|
img_h = svg_w / img_ratio if img_ratio > 0 else float(svg_sc.get("height_px", 0))
|
||||||
text_lines = len([l for l in st_text.split("\n") if l.strip() and not l.strip().startswith("[팝업:") and not l.strip().startswith("[이미지:") and not l.strip().lstrip("• ").startswith("출처:")])
|
text_lines = len([l for l in st_text.split("\n") if l.strip() and not l.strip().startswith("[팝업:") and not l.strip().startswith("[이미지:") and not l.strip().lstrip("• ").startswith("출처:")])
|
||||||
text_h_used = text_lines * fs * 1.55
|
text_h_used = text_lines * fs * 1.55
|
||||||
available_h = content_area_h - text_h_used
|
# 표 공간 = 전체 - 제목 - max(이미지,텍스트) - keymsg - padding
|
||||||
|
upper_h = max(img_h, text_h_used)
|
||||||
|
available_h = after_h - 16 - title_h - upper_h - keymsg_h - 8
|
||||||
available_w = float(text_sc["width_px"])
|
available_w = float(text_sc["width_px"])
|
||||||
if available_h < fs * 3:
|
if available_h < fs * 3:
|
||||||
continue # 공간 부족하면 건너뜀
|
continue # 공간 부족하면 건너뜀
|
||||||
|
|||||||
@@ -400,6 +400,12 @@ def calculate_container_specs(
|
|||||||
# 비중 비율로 높이 할당
|
# 비중 비율로 높이 할당
|
||||||
ratio = weight / total_weight
|
ratio = weight / total_weight
|
||||||
height_px = max(min_block_h, int(available * ratio))
|
height_px = max(min_block_h, int(available * ratio))
|
||||||
|
# footer는 최소 높이 보장 (font_size * line_height + padding)
|
||||||
|
if zone_name == "footer":
|
||||||
|
from src.fit_verifier import _load_design_tokens as _ldt_footer
|
||||||
|
_ft = _ldt_footer()
|
||||||
|
_footer_min = int(14 * _ft.get("line_height_ko", 1.7) + _ft["spacing_page"])
|
||||||
|
height_px = max(_footer_min, height_px)
|
||||||
|
|
||||||
# 블록 내부 제약 계산 — topic당 높이로 판단
|
# 블록 내부 제약 계산 — topic당 높이로 판단
|
||||||
topic_count = max(1, len(topic_ids))
|
topic_count = max(1, len(topic_ids))
|
||||||
|
|||||||
Reference in New Issue
Block a user