PPTX quality linter with Korean-typography awareness: catches silent font fallback, tracking damage, text collisions, off-canvas bleed, and AI-generated deck tells in built .pptx files
Project description
빌드된 .pptx를 배포 직전에 기계로 검사합니다. 파워포인트 없이 파일만으로 동작하고, 한글
타이포그래피를 일급으로 다룹니다. 이름은 덱의 구조와 한글 타이포를 배포 전에 벼려 다듬는
대장간이라는 뜻입니다.
왜
한글 덱이 깨지는 지점은 대부분 조용합니다.
- 라틴 전용 폰트에 실린 한글은 에러 없이 Malgun으로 폴백됩니다.
- 자간(tracking)은 한글 낱자 사이를 소리 없이 벌려 놓습니다.
- autofit은 글자를 판독 불가 크기까지 줄여 놓습니다.
코드 리뷰로는 안 보이고 렌더를 눈으로 봐야만 잡히던 이 결함들을, Archforge는 pptx 내부 (XML, 폰트 슬롯, 좌표, 이미지 알파)를 직접 읽어 빌드 직후에 잡습니다.
설치
pip install archforge # PyPI
pip install -e . # 리포에서 직접(개발용)
파이썬 3.9 이상이면 되고, python-pptx와 Pillow만 딸려 옵니다. 파워포인트는 필요 없습니다.
에이전트 스킬팩도 wheel에 동봉돼 있어 설치 후 바로 받을 수 있습니다.
archforge skill # 스킬팩(SKILL.md) 출력
archforge skill --install # ./.claude/skills/archforge-pptx-lint/ 에 설치
archforge skill --install DIR # 원하는 스킬 폴더에 설치
사용
archforge deck.pptx # 사람용 리포트, ERROR 있으면 exit 1
archforge deck.pptx --json # 기계 판독용 JSON (에이전트·CI)
archforge deck.pptx --strict # WARN도 실패 + E2 숫자 맥락 예외 해제
archforge deck.pptx --ghost # 페이지별 타이틀 나열 (수평 논리 검토)
archforge deck.pptx --render pages/ # p01.png·p02.png 형식 렌더로 이미지 위 대비(W7)까지
archforge deck.pptx --skip W14,W6 # 장르에 안 맞는 경고 억제(에디토리얼·템플릿 덱)
archforge deck.pptx --hard-min 5 --body-min 9 --small-min 7.5 # 크기 게이트 임계 조정
archforge deck.pptx --w6-sim 0.95 --w6-cluster 5 # W6 골격 반복 임계 조정
--json 출력:
{
"file": "deck.pptx",
"errors": [{ "page": 3, "code": "E1", "message": "...", "detail": "..." }],
"warnings": [{ "page": 5, "code": "W15", "message": "...", "detail": "..." }],
"ghost": [{ "page": 1, "title": "..." }],
"summary": { "error_count": 1, "warn_count": 2, "pass": false }
}
무엇을 잡나
ERROR (배포 차단, exit 1)
| 코드 | 내용 | 고치는 법 |
|---|---|---|
E1 |
한글을 실제로 렌더할 폰트가 라틴 전용(한글 글리프 없음): 조용한 Malgun 폴백. 실효 폰트는 실측 렌더 모델로 해석 (run a:ea > 비어있지 않은 테마 a:ea > 빈 테마일 때만 run a:latin > OS 폴백) |
한글 런에 CJK 폰트를. a:ea 명시가 가장 견고, 모노·라틴 디스플레이 폰트는 영문·숫자에만 |
E2 |
대시류 문자 (em·en·figure dash, 수평 바, 수학 마이너스 U+2212, 전각 하이픈 U+FF0D 등). 정당 타이포 두 형태는 기본 통과: 숫자 사이 en dash(연도·수치 범위), 숫자 앞 마이너스(음수). --strict는 전부 차단 |
산문 대시는 콜론·쉼표·괄호로. 범위·음수는 기본 모드에선 그대로 두면 됨 |
E3 |
실효 크기(autofit·문단·placeholder 상속 체인 반영) 5pt 미만: 판독 불가 | 요소를 줄이고 대표 하나를 크게 |
E4 |
연속 한글에 양수 자간: 낱자가 벌어짐 | CJK 런은 자간 0, ASCII 라벨만 트래킹 |
WARN (권고)
| 코드 | 내용 |
|---|---|
W1 |
본문급 프레임이 9pt 미만 |
W5 |
상속 체인(run·문단·placeholder·마스터·defaultTextStyle) 어디에도 크기 없음 |
W6 |
같은 레이아웃 골격이 4장 이상 반복 (--w6-sim/--w6-cluster로 조정) |
W7 |
이미지 위 텍스트 대비 낮음 (--render 필요) |
W8 |
좁은 프레임(≤4in)의 소형 한글 (목업·카드 내부) |
W9 |
색 세로바를 리스트 마커로 반복 |
W10 |
직접 그린 도식이 여러 페이지에서 반복 |
W11 |
AI 티 카피 (버즈워드·뻔한 오프닝) |
W12 |
푸터 baseline 어긋남 |
W13 |
PPT 자체 그림자·글로·3D 효과 |
W14 |
서술형 명사구 타이틀 (숫자+단위 타이틀은 주장으로 인정, 에디토리얼 덱은 --skip W14) |
W15 |
텍스트끼리 겹침 |
W16 |
화면 밖 넘침 |
W17 |
텍스트가 이미지 잉크 경계에 걸침 |
W18 |
손상·비정형 속성으로 일부 구간 검사 불능 (결과 불완전 가능, --strict면 실패) |
작동 방식
E1의 폰트 해석은 규격 추정이 아니라 실측입니다. PowerPoint COM으로 프로브 덱을 렌더해
슬롯 우선순위(run a:ea > 비어있지 않은 테마 a:ea > 빈 테마일 때만 run a:latin)를
확정하고 회귀 픽스처로 고정했습니다. 테마 a:ea는 슬라이드가 실제로 쓰는 마스터의 관계로
해석하므로 멀티마스터 덱에서도 엉뚱한 테마로 판정하지 않습니다.
실효 크기는 상속 체인 전체를 해석합니다: run > 문단 > 도형 lstStyle > 레이아웃 placeholder
마스터 placeholder·txStyles > 프레젠테이션 defaultTextStyle. 그래서 명시 크기 없는 템플릿·placeholder 덱에서도
E3/W1/W8이 실제로 돕니다.
W15~W17은 프레임 박스가 아니라 실효 글리프·잉크 영역을 근사해서 봅니다. run별 크기,
실제 행간, autofit(퍼센트 문자열 포함), wrap 모드, 그룹 좌표 변환, 정렬, 이미지 알파
트림·크롭·flip까지 반영하고, 드롭캡·잔상 타이포·장식 블리드·카드 위 캡션 같은 의도적
연출은 제외합니다.
임계값은 취향이 아니라 캘리브레이션 결과입니다. 실덱 코퍼스 50여 개를 전수 스캔해 플래그된 페이지를 실제 렌더와 대조하고, 진짜 결함과 오탐이 갈리는 지점에 임계를 놓았습니다. 이후 적대적 검증(재현 pptx로 오탐을 공격)으로 그룹 이동, wrap=none, 팔레트 투명 PNG, 크롭·회전·flip 같은 외부 pptx의 다양성까지 회귀 픽스처로 고정했습니다. 게이트별 임계와 근거, 실측 기록은 docs/CALIBRATION.md에 있습니다.
임의 pptx가 들어와도 리포트는 살아남습니다. run·슬라이드 단위 가드가 외부 생성기의
쓰레기 속성(스키마 유효한 universal measure 포함)을 흡수하고 나머지 검사를 계속하며,
가드가 삼킨 구간은 stderr만이 아니라 W18로 JSON·텍스트 출력에 표면화됩니다. exit code나
summary.pass만 보는 CI가 불완전 검사를 완전 통과로 오독하지 않게 하기 위한 계약입니다.
E2의 숫자 맥락 예외는 run이 아니라 문단 컨텍스트로 판정합니다. PowerPoint가 철자검사나
서식 경계로 연도 범위를 "2020"과 "(U+2013)2024" 두 run으로 쪼개 놓아도 오탐하지
않습니다.
알려진 한계 1:
E1은 블록리스트 방식이라 목록에 없는 라틴 전용 폰트는 놓칠 수 있습니다. 흔한 라틴 패밀리 60여 종을 등재해 뒀고, 빠진 폰트는 이슈로 알려주세요. 알려진 한계 2: 레이아웃 lstStyle로 정렬을 상속하는 플레이스홀더는 좌정렬로 후퇴 판정합니다. 템플릿 덱에서W15/W16이 이상해 보이면 렌더로 확정하세요.
에이전트 연동
LLM 에이전트가 python-pptx 류로 덱을 만드는 워크플로를 일차 사용자로 설계했습니다.
빌드 → archforge --json → error_count 0 될 때까지 수정 → WARN은 렌더 보고 판단
Agent Skills 스킬팩(SKILL.md + YAML frontmatter 표준)이 이 루프와 코드별 수정 가이드를
에이전트에게 가르칩니다. wheel에 동봉되므로 pip install archforge 후
archforge skill --install이면 끝이고, 리포를 클론했다면 skills/archforge-pptx-lint/를
그대로 써도 됩니다. 지원하는 어느 에이전트(Claude Code, Codex 등)든 설치됩니다.
린트 통과가 완성이라는 뜻은 아닙니다. 이 린터는 기계로 잡히는 결함군을 담당하고, 페이지 구성과 서사의 품질은 여전히 렌더를 보는 눈의 몫입니다.
English
Archforge is a CLI quality linter for built .pptx files with first-class Korean
typography awareness. It catches the silent failure modes of Korean decks (Latin-only
font fallback, CJK tracking damage, sub-5pt autofit shrinkage) plus the general mechanical
defect class (text collisions, off-canvas bleed, text straddling image edges, AI-generated
deck tells) by reading the pptx internals directly, no PowerPoint required.
The E1 font-resolution model was measured by rendering probe decks in PowerPoint via COM
(not guessed from the spec), themes resolve per slide master, and effective sizes walk the
full placeholder inheritance chain down to defaultTextStyle. Thresholds are calibrated
against rendered output of a real deck corpus and hardened with adversarial reproduction
fixtures (group transforms, wrap="none", palette-transparency PNGs, crop/flip/rotation);
see docs/CALIBRATION.md. Built for LLM-agent build-lint-fix loops: stable codes, --json
output, and an Agent Skills pack that ships inside the wheel (archforge skill --install).
License
MIT © Minjae Kwon (Ash)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file archforge-0.2.0.tar.gz.
File metadata
- Download URL: archforge-0.2.0.tar.gz
- Upload date:
- Size: 67.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c51e7810dc5bd22692c17e6a5fa01f8033acab19a717d7a5323d76f6add7d5b
|
|
| MD5 |
d97ef6cd926d6d5bee20abd9baf6e380
|
|
| BLAKE2b-256 |
25a9332a3d050cde8f74b37e117021e5353b3ae6955bdd31b3e5aeb199f435fc
|
File details
Details for the file archforge-0.2.0-py3-none-any.whl.
File metadata
- Download URL: archforge-0.2.0-py3-none-any.whl
- Upload date:
- Size: 41.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
154b45f0859e8ffab9f59e1cb3d2ae56414e13c660a34ae68cb6cd82b8918362
|
|
| MD5 |
67541ab46c5bf4944c917255f7d31dee
|
|
| BLAKE2b-256 |
d8edea9bf32a9f06bb003d075397f579437fcbceda12c57a00414b421fa6efdf
|