# modules/exporters.py from pathlib import Path from typing import Optional from docx import Document from pptx import Presentation from pptx.util import Pt # utils 側で書き込み可能な場所を自動選択 from .utils import ensure_dirs, export_dir def _resolve_outpath(out_path: Optional[str], default_name: str) -> Path: ensure_dirs() base = export_dir() if out_path: p = Path(out_path) if not p.is_absolute(): p = base / p else: p = base / default_name p.parent.mkdir(parents=True, exist_ok=True) return p def export_docx(markdown_text: str, out_path: Optional[str] = None) -> str: """ 非依存最小の DOCX エクスポート(Markdown は簡易に段落分割のみ) """ p = _resolve_outpath(out_path, "proposal.docx") doc = Document() # シンプルに行単位で追加(見出しの # は太字化のみ) for line in (markdown_text or "").splitlines(): if line.strip().startswith("#"): run = doc.add_paragraph().add_run(line.lstrip("# ").strip()) run.bold = True else: doc.add_paragraph(line) doc.save(p) return str(p) def export_pptx(markdown_text: str, out_path: Optional[str] = None) -> str: """ 非依存最小の PPTX エクスポート(1行=1スライド、最初の行はタイトル扱い) """ p = _resolve_outpath(out_path, "proposal.pptx") prs = Presentation() # 16:9 既定レイアウト(タイトル&本文)を使う layout = prs.slide_layouts[1] lines = [ln for ln in (markdown_text or "").splitlines()] # 1枚目: タイトルスライド if lines: slide0 = prs.slides.add_slide(prs.slide_layouts[0]) # タイトル slide0.shapes.title.text = lines[0].lstrip("# ").strip() or "提案ドラフト" if len(lines) > 1 and lines[1].strip(): slide0.placeholders[1].text = lines[1].strip() # 2枚目以降: 残りの行を1行=1スライドで for ln in lines[2:] if len(lines) > 2 else []: slide = prs.slides.add_slide(layout) slide.shapes.title.text = "概要" tf = slide.placeholders[1].text_frame tf.clear() p_para = tf.paragraphs[0] p_para.text = ln p_para.font.size = Pt(18) prs.save(p) return str(p)