#!/usr/bin/env python3 """PPTX 덱을 균등 배율로 축소/확대한다 (좌표·폰트·선굵기·여백 전부). INFINITH 덱(20x11.25in)은 표준 캔버스(13.33x7.5in)의 1.5배라 폰트 pt 숫자가 1.5배 부풀려져 있다. 2/3로 균등 축소하면 화면 모습은 동일하고 pt는 표준이 된다. 원본은 덮어쓰지 않는다. 반드시 새 파일로 저장한다. python3 scripts/rescale_deck.py [배율=0.6667] """ import sys from fractions import Fraction from pptx import Presentation NS = "{http://schemas.openxmlformats.org/drawingml/2006/main}" # EMU 좌표 속성 (요소 태그 → 속성들) GEOM = { f"{NS}off": ("x", "y"), f"{NS}ext": ("cx", "cy"), f"{NS}chOff": ("x", "y"), f"{NS}chExt": ("cx", "cy"), } # 폰트 크기(1/100pt) 속성을 갖는 요소 SZ_TAGS = {f"{NS}rPr", f"{NS}defRPr", f"{NS}endParaRPr"} # 텍스트 프레임 안쪽 여백(EMU) BODYPR_ATTRS = ("lIns", "tIns", "rIns", "bIns") def scale_int(v, f): return str(int(round(int(v) * f))) def scale_tree(root, f): for el in root.iter(): tag = el.tag if tag in GEOM: for a in GEOM[tag]: if el.get(a) is not None: el.set(a, scale_int(el.get(a), f)) elif tag in SZ_TAGS: if el.get("sz") is not None: el.set("sz", scale_int(el.get("sz"), f)) elif tag == f"{NS}ln": if el.get("w") is not None: el.set("w", scale_int(el.get("w"), f)) elif tag == f"{NS}bodyPr": for a in BODYPR_ATTRS: if el.get(a) is not None: el.set(a, scale_int(el.get(a), f)) elif tag == f"{NS}spcPts": if el.get("val") is not None: el.set("val", scale_int(el.get("val"), f)) def main(): src, dst = sys.argv[1], sys.argv[2] f = float(sys.argv[3]) if len(sys.argv) > 3 else float(Fraction(2, 3)) if src == dst: sys.exit("원본 덮어쓰기 금지. 출력 파일명을 다르게 지정하세요.") prs = Presentation(src) prs.slide_width = int(round(prs.slide_width * f)) prs.slide_height = int(round(prs.slide_height * f)) for slide in prs.slides: scale_tree(slide._element, f) for layout in prs.slide_layouts: scale_tree(layout._element, f) for master in prs.slide_masters: scale_tree(master._element, f) prs.save(dst) print(f"저장: {dst} ({prs.slide_width/914400:.3f} x {prs.slide_height/914400:.3f} in, 배율 {f:.4f})") if __name__ == "__main__": main()