- harness·zio-harness·proposal-builder·zioinfo → plugins/zioinfo (git mv 히스토리 보존) - 스킬 4·커맨드 3(/zioinfo:pmo·proposal·wiki)·에이전트 15·graphify 훅·knowledge 통합 - 신규: /zioinfo:wiki (graphify LLM wiki — graphify-out/wiki/ 커뮤니티별 아티클) - 신규: ZIO WISE 테마 (themes/zioinfo.json, experimental) - manifest 최신화: $schema·displayName(ZIO INFOTECH Suite)·experimental.themes - marketplace.json 단일 엔트리, 루트 plugin.json 제거 - CLAUDE.md·PROJECT_MAP·docs/plugins.md·README 3종·CHANGELOG·설치가이드 pptx 재구성 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
92 lines
4.2 KiB
Python
92 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
build_deck.py — 템플릿 스타일 + 제안서 콘텐츠(JSON) → proposal.pptx 생성.
|
|
deck-designer가 프로젝트별 콘텐츠 JSON만 주입해 재생성 가능한 골격 스크립트.
|
|
디자인 시스템(색/폰트)·선 아이콘(SVG는 사전 PNG/EMF 변환 또는 도형)·도식은 deck-designer가 채운다.
|
|
|
|
사용: python build_deck.py --content content.json [--template template.pptx] --out proposal.pptx
|
|
content.json 예:
|
|
{
|
|
"design": {"primary":"1F4E79","accent":"2E9BD6","text":"222222","font_ko":"맑은 고딕"},
|
|
"slides": [
|
|
{"layout":"title", "title":"○○시스템 구축 제안서", "subtitle":"제안사 / 2026"},
|
|
{"layout":"section", "title":"1. 사업 이해"},
|
|
{"layout":"content", "title":"추진 전략", "bullets":["...","..."]},
|
|
{"layout":"diagram", "title":"추진 체계도", "note":"도형은 deck-designer가 보강"}
|
|
]
|
|
}
|
|
의존: pip install python-pptx · 외부 호출 없음.
|
|
"""
|
|
import sys, json, argparse
|
|
|
|
def hex_color(s):
|
|
from pptx.dml.color import RGBColor
|
|
return RGBColor.from_string(s)
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--content", required=True)
|
|
ap.add_argument("--template")
|
|
ap.add_argument("--out", default="proposal.pptx")
|
|
a = ap.parse_args()
|
|
try:
|
|
from pptx import Presentation
|
|
from pptx.util import Inches, Pt
|
|
from pptx.enum.text import PP_ALIGN
|
|
except ImportError:
|
|
print("python-pptx 미설치 → pip install python-pptx", file=sys.stderr); sys.exit(2)
|
|
|
|
with open(a.content, encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
design = data.get("design", {})
|
|
primary = design.get("primary", "1F4E79")
|
|
accent = design.get("accent", "2E9BD6")
|
|
text_c = design.get("text", "222222")
|
|
font_ko = design.get("font_ko", "맑은 고딕")
|
|
|
|
prs = Presentation(a.template) if a.template else Presentation()
|
|
blank = prs.slide_layouts[6] if len(prs.slide_layouts) > 6 else prs.slide_layouts[-1]
|
|
W, H = prs.slide_width, prs.slide_height
|
|
|
|
def add_text(slide, txt, left, top, width, height, size, color, bold=False, align=PP_ALIGN.LEFT):
|
|
tb = slide.shapes.add_textbox(left, top, width, height)
|
|
tf = tb.text_frame; tf.word_wrap = True
|
|
p = tf.paragraphs[0]; p.alignment = align
|
|
r = p.add_run(); r.text = txt
|
|
r.font.size = Pt(size); r.font.bold = bold
|
|
r.font.name = font_ko; r.font.color.rgb = hex_color(color)
|
|
return tb
|
|
|
|
for s in data.get("slides", []):
|
|
slide = prs.slides.add_slide(blank)
|
|
layout = s.get("layout", "content")
|
|
title = s.get("title", "")
|
|
if layout == "title":
|
|
# 상단 컬러 바
|
|
bar = slide.shapes.add_shape(1, 0, 0, W, Inches(2.2))
|
|
bar.fill.solid(); bar.fill.fore_color.rgb = hex_color(primary); bar.line.fill.background()
|
|
add_text(slide, title, Inches(0.8), Inches(0.7), W - Inches(1.6), Inches(1.2), 36, "FFFFFF", True)
|
|
if s.get("subtitle"):
|
|
add_text(slide, s["subtitle"], Inches(0.8), Inches(2.5), W - Inches(1.6), Inches(0.8), 18, text_c)
|
|
elif layout == "section":
|
|
bar = slide.shapes.add_shape(1, 0, Inches(2.6), W, Inches(1.6))
|
|
bar.fill.solid(); bar.fill.fore_color.rgb = hex_color(accent); bar.line.fill.background()
|
|
add_text(slide, title, Inches(0.8), Inches(2.9), W - Inches(1.6), Inches(1.0), 30, "FFFFFF", True)
|
|
else:
|
|
# 제목 + 강조 underline
|
|
add_text(slide, title, Inches(0.7), Inches(0.5), W - Inches(1.4), Inches(0.9), 26, primary, True)
|
|
ul = slide.shapes.add_shape(1, Inches(0.7), Inches(1.35), Inches(2.2), Pt(3))
|
|
ul.fill.solid(); ul.fill.fore_color.rgb = hex_color(accent); ul.line.fill.background()
|
|
top = Inches(1.8)
|
|
for b in s.get("bullets", []):
|
|
add_text(slide, "• " + b, Inches(0.9), top, W - Inches(1.8), Inches(0.6), 16, text_c)
|
|
top += Inches(0.55)
|
|
if s.get("note"):
|
|
add_text(slide, s["note"], Inches(0.9), H - Inches(0.8), W - Inches(1.8), Inches(0.5), 11, "888888")
|
|
|
|
prs.save(a.out)
|
|
print(f"생성: {a.out} (slides={len(data.get('slides', []))})")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|