@kaydash9999/banker-plugins 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +52 -0
- package/README.md +28 -8
- package/codex/manifest.json +8 -1
- package/codex/transform-matrix.md +2 -2
- package/commands/setup.md +2 -0
- package/package.json +6 -1
- package/skills/3d-intro-build/SKILL.md +214 -0
- package/skills/3d-intro-build/references/LICENSE +21 -0
- package/skills/3d-intro-build/references/NOTICE.md +32 -0
- package/skills/3d-intro-build/references/assemble.mjs +252 -0
- package/skills/3d-intro-build/references/azure-adapter.mjs +495 -0
- package/skills/3d-intro-build/references/curate.mjs +301 -0
- package/skills/3d-intro-build/references/index-template.html +73 -0
- package/skills/3d-intro-build/references/prompts.md +171 -0
- package/skills/3d-intro-build/references/scrub-engine.js +448 -0
- package/skills/3d-intro-build/references/serve.mjs +123 -0
- package/skills/3d-intro-setup/SKILL.md +196 -0
- package/skills/3d-intro-setup/references/azure-adapter.mjs +495 -0
- package/skills/lineage/SKILL.md +126 -134
- package/skills/lineage/lineage.py +1041 -354
- package/skills/motion-graphic-make/SKILL.md +57 -0
- package/skills/motion-graphic-setup/SKILL.md +141 -0
- package/skills/ralph-qa/SKILL.md +253 -77
- package/skills/ralph-qa/references/verifier-probe.mjs +860 -0
- package/skills/refresh-readme/SKILL.md +3 -3
- package/skills/vertical-pptx/SKILL.md +409 -0
- package/skills/vertical-pptx/build_a4p.js +923 -0
- package/skills/vertical-pptx/build_a4p.py +916 -0
- package/skills/vertical-pptx/verify_a4p.mjs +2168 -0
- package/skills/vertical-pptx-setup/SKILL.md +223 -0
|
@@ -127,9 +127,9 @@ node -e "const m=require('./codex/manifest.json');console.log(m.surfaces.filter(
|
|
|
127
127
|
node -e "const p=require('./package.json');console.log(p.version, JSON.stringify(p.dependencies||{}))"
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
**앞의 두 줄이 갈리면 그 차이가 사실이다.** 이 저장소에서 실제로 갈린다: 디렉터리는
|
|
130
|
+
**앞의 두 줄이 갈리면 그 차이가 사실이다.** 이 저장소에서 실제로 갈린다: 디렉터리는 55 인데 `SKILL.md` 보유는 54 이다.
|
|
131
131
|
`skills/ultra-init-workspace/` 는 런타임 산출물 디렉터리라 `SKILL.md` 가 없고, 그래서 배포 매니페스트에도 없고 `package.json` `files[]` 도 negation 으로 뺀다.
|
|
132
|
-
즉 "디렉터리를 세면 스킬 수" 라는 그럴듯한 가정이 **여기서 1 을 더 센다.** 세 곳(매니페스트·`files[]`·테스트의 `SKILL.md` 필터)이 이미
|
|
132
|
+
즉 "디렉터리를 세면 스킬 수" 라는 그럴듯한 가정이 **여기서 1 을 더 센다.** 세 곳(매니페스트·`files[]`·테스트의 `SKILL.md` 필터)이 이미 54 를 말하는데 디렉터리 카운트만 55 라면, 틀린 쪽은 카운트다.
|
|
133
133
|
|
|
134
134
|
**README 는 어떤 경우에도 사실이 아니다. 그리고 사실을 세는 명령도 틀릴 수 있다. 세 소스가 같은 값을 말할 때만 통과다.**
|
|
135
135
|
|
|
@@ -139,7 +139,7 @@ node -e "const p=require('./package.json');console.log(p.version, JSON.stringify
|
|
|
139
139
|
|
|
140
140
|
| README 주장 | 위치 | 코드 사실 | 근거 | 판정 |
|
|
141
141
|
|---|---|---|---|---|
|
|
142
|
-
| "스킬 42개" | 10행·131행 |
|
|
142
|
+
| "스킬 42개" | 10행·131행 | 53 | `ls -d skills/*/SKILL.md` = 54, 매니페스트 skill 표면 = 54 (디렉터리 카운트는 55 라 셋 중 혼자 다르다) | 드리프트: 2곳 다 고침 |
|
|
143
143
|
| "`--fast` 지원" | 60행 | 없음 | 파서에 해당 플래그 없음 | 드리프트: 문장 삭제 |
|
|
144
144
|
| "Node 18+ 필요" | 20행 | `engines.node: ">=18"` | package.json | 통과 |
|
|
145
145
|
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vertical-pptx
|
|
3
|
+
description: "(banker) A4 세로(210x297mm) 규격 PPTX 생성·점검·수리와 16:9 덱의 A4 세로 변환. 'A4 세로 pptx'/'세로형 슬라이드'/'인쇄용 pptx'/'vertical-pptx' 시 사용."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# vertical-pptx - A4 세로(210x297mm) 규격 PPTX
|
|
7
|
+
|
|
8
|
+
인쇄해서 돌리는 문서형 덱을 A4 세로 규격으로 만들고, 그 결과가 정말 A4 세로인지 파일 바이트로 판정한다.
|
|
9
|
+
이 스킬이 소유하는 것은 **규격과 최소 결정론적 레이아웃**이다: 210x297mm·여백·8열 20행 그리드·한글 3슬롯·인쇄 pt·검증·수리.
|
|
10
|
+
색 팔레트·차트·아이콘·디자인 감각은 소유하지 않는다. 그건 아래 §위임 에 맡긴다.
|
|
11
|
+
|
|
12
|
+
self-contained 번들 5파일이고 저장소에 의존성을 하나도 더하지 않는다.
|
|
13
|
+
|
|
14
|
+
| 파일 | 역할 |
|
|
15
|
+
|---|---|
|
|
16
|
+
| `SKILL.md` | 이 문서 |
|
|
17
|
+
| `build_a4p.js` | node 빌더(pptxgenjs) + 이 프로젝트의 **유일한** 마크다운 → deck JSON 변환기 |
|
|
18
|
+
| `build_a4p.py` | python 빌더(python-pptx). 입력은 deck JSON 뿐이고 `--md` 가 없다 |
|
|
19
|
+
| `verify_a4p.mjs` | 검증기·수리기·`--suggest-deck`. node stdlib 만 쓰고 외부 의존성이 0 이다 |
|
|
20
|
+
| `a4p.test.mjs` | 유닛(`node --test`). 외부 의존성 0 으로 상시 실행된다 |
|
|
21
|
+
|
|
22
|
+
## When to use
|
|
23
|
+
|
|
24
|
+
- "A4 세로 pptx", "세로형 슬라이드", "인쇄용 pptx", "문서형 덱", "제안서 pptx" 를 만들 때
|
|
25
|
+
- 이미 있는 pptx 가 진짜 A4 세로인지 판정할 때(치수·`type` 속성·여백·도형 이탈)
|
|
26
|
+
- 16:9 덱을 A4 세로로 바꾸고 무엇이 파손됐는지 슬라이드별로 받아야 할 때
|
|
27
|
+
- 마크다운 원고를 그대로 인쇄 규격 덱으로 떨어뜨릴 때
|
|
28
|
+
|
|
29
|
+
## 표준 레시피 (4단계)
|
|
30
|
+
|
|
31
|
+
`--suggest-deck` 은 고급 플래그가 아니라 **세 번째 단계**다.
|
|
32
|
+
기본 상자에 안 들어가는 본문은 흔하고, 그때마다 왕복하는 것이 정상 워크플로다.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
S=<이 스킬 디렉터리>
|
|
36
|
+
F=<빌더가 보고한 fontFile 경로>
|
|
37
|
+
|
|
38
|
+
# 1) build 마크다운 → deck JSON → pptx
|
|
39
|
+
node $S/build_a4p.js --md 원고.md --emit-json > deck.json
|
|
40
|
+
node $S/build_a4p.js --in deck.json --out out.pptx
|
|
41
|
+
|
|
42
|
+
# 2) verify 규격·여백·이탈·오버플로 판정
|
|
43
|
+
node $S/verify_a4p.mjs out.pptx --font-file $F
|
|
44
|
+
|
|
45
|
+
# 3) suggest 오버플로가 있을 때만. rows 만 고친 새 deck JSON 을 낸다
|
|
46
|
+
node $S/verify_a4p.mjs out.pptx --deck deck.json --font-file $F --suggest-deck fixed.json
|
|
47
|
+
|
|
48
|
+
# 4) rebuild 고친 deck 으로 다시 빌드하고 2) 로 돌아가 확인
|
|
49
|
+
node $S/build_a4p.js --in fixed.json --out out.pptx
|
|
50
|
+
node $S/verify_a4p.mjs out.pptx --font-file $F
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`--emit-json` 에 `--out` 을 안 주면 stdout 은 deck JSON 전용이 되고 사람이 읽는 표는 stderr 로 간다.
|
|
54
|
+
그래서 위처럼 `> deck.json` 리다이렉트가 안전하다.
|
|
55
|
+
|
|
56
|
+
python 빌더는 같은 deck JSON 을 먹는다.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
python build_a4p.py --in deck.json --out out.pptx
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 기본 상자 용량 (rows 를 언제 손대야 하는가)
|
|
63
|
+
|
|
64
|
+
`body` 기본값은 2행이다. 그 상자에 얼마나 들어가는지 모르면 `rows` 를 언제 올려야 하는지 알 수 없으므로 수치로 적는다.
|
|
65
|
+
|
|
66
|
+
- 2행 상자 높이 = `361800 * 2 + 144000` = 867600 EMU = **24.1mm**
|
|
67
|
+
- 10.5pt · 줄간 1.2 → 줄 높이 12.6pt = 4.445mm → **약 5줄**
|
|
68
|
+
- 10.5pt 에서 1em = 3.7042mm → 본문 폭 190mm = **51.3em**
|
|
69
|
+
- Noto Sans CJK KR 의 한글 advance 는 0.92em → **한 줄에 한글 약 55자**
|
|
70
|
+
- 즉 기본 2행 상자는 **한글 약 275자**(5줄 x 55자)
|
|
71
|
+
|
|
72
|
+
가정을 함께 적는다.
|
|
73
|
+
`bodyPt` 10.5 · 줄간 1.2 · `marginMm` 10 · 8열 전폭 · 텍스트 상자 inset 0 · Noto Sans CJK KR 메트릭 기준이다.
|
|
74
|
+
커널링과 줄바꿈 규칙은 반영하지 않았으므로 이 값은 **넉넉한 쪽 추정**이고, 마지막 줄이 꽉 차는 경우는 드물다.
|
|
75
|
+
`bodyPt` 를 11 로 올리면 줄당 글자 수가 약 5% 줄어든다.
|
|
76
|
+
|
|
77
|
+
일반적인 마크다운 문단(100~300자)은 상당수가 들어가고 긴 문단은 넘친다.
|
|
78
|
+
"못 쓴다" 도 "대개 괜찮다" 도 아니라 **왕복이 상시로 필요한 수준**이라 4단계 레시피가 기본이다.
|
|
79
|
+
|
|
80
|
+
## 규격 상수 (EMU)
|
|
81
|
+
|
|
82
|
+
1mm = 36000 EMU 다. A4 세로는 그 정의에서 곧바로 나온다.
|
|
83
|
+
|
|
84
|
+
| 항목 | 식 | m=10 (기본) |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| 슬라이드 | `210 * 36000` x `297 * 36000` | **7560000 x 10692000** |
|
|
87
|
+
| 여백 | `36000m` | 360000 (10mm) |
|
|
88
|
+
| 본문 폭 | `7560000 - 72000m` | 6840000 (190mm) |
|
|
89
|
+
| 본문 높이 | `10692000 - 72000m` | 9972000 (277mm) |
|
|
90
|
+
| 거터 | 144000 (4mm 고정) | 144000 |
|
|
91
|
+
| 컬럼 폭 (8열) | `(182 - 2m) * 4500` | 729000 (20.25mm) |
|
|
92
|
+
| 행 높이 (20행) | `(221 - 2m) * 1800` | 361800 (10.05mm) |
|
|
93
|
+
| 컬럼 피치 | 컬럼 폭 + 144000 | 873000 |
|
|
94
|
+
| 행 피치 | 행 높이 + 144000 | 505800 |
|
|
95
|
+
|
|
96
|
+
컬럼 `c` 에서 `k` 열: `x = 36000m + 컬럼피치 * c` · `cx = 컬럼폭 * k + 144000 * (k - 1)`.
|
|
97
|
+
행 `r` 에서 `n` 행: `y = 36000m + 행피치 * r` · `cy = 행높이 * n + 144000 * (n - 1)`.
|
|
98
|
+
|
|
99
|
+
검산: `k=8` → `cx = 6840000` 이고 `n=20` → `cy = 9972000` 으로 본문 영역과 정확히 일치한다.
|
|
100
|
+
|
|
101
|
+
`meta.marginMm` 은 **10 이상 40 이하의 정수**여야 한다.
|
|
102
|
+
10 은 동결 스펙의 하한이고, 정수 제약은 위 식들의 EMU 정수성을 보장한다.
|
|
103
|
+
범위를 벗어나거나 정수가 아니면 **exit 3**(사용법 오류)이다.
|
|
104
|
+
|
|
105
|
+
## 프리셋 트랩 3종
|
|
106
|
+
|
|
107
|
+
셋 다 이론이 아니라 실측이다. A4 라고 적힌 것이 A4 가 아닌 세 가지 경로다.
|
|
108
|
+
|
|
109
|
+
| # | 트랩 | 실측값 | 옳은 길 |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| 1 | PowerPoint 의 "A4 용지(210x297mm)" 프리셋 | 10.833 x 7.5in = **9906000 x 6858000**. 세로가 아니라 **가로**다 | 페이지 설정에서 **사용자 지정 21 x 29.7cm** 뿐이다 |
|
|
112
|
+
| 2 | 반올림한 인치 | `8.27in` → cx=**7562088** (+2088) · `11.69in` → cy=**10689336** (-2664) | 전정밀도 `210 / 25.4` 와 `297 / 25.4` |
|
|
113
|
+
| 3 | python-pptx 의 잔존 `type` | 치수는 A4 인데 `p:sldSz` 에 `type="screen4x3"` 가 그대로 남는다 | `type` 속성을 명시적으로 제거한다 |
|
|
114
|
+
|
|
115
|
+
**트랩 1 이 왜 생기나.** Microsoft 가 프린터 여백을 미리 빼고 프리셋을 만들어 뒀다.
|
|
116
|
+
그래서 이름은 A4 인데 인쇄 가능 영역 치수이고, 실제 용지 규격이 아니다.
|
|
117
|
+
이 값은 다른 오답과 성격이 달라서 검증기가 `PRESET_TRAP` 이라는 별도 판정 코드로 낸다.
|
|
118
|
+
|
|
119
|
+
**트랩 2 의 차이는 0.06mm 와 0.07mm 다.** 눈에는 안 보이지만 "이 파일이 A4 인가" 는 이진 질문이고 허용오차가 0 이다.
|
|
120
|
+
검증기의 sldSz 비교에는 허용오차가 없다.
|
|
121
|
+
|
|
122
|
+
**트랩 3 이 무서운 이유는 치수만 봐서는 통과하기 때문이다.**
|
|
123
|
+
치수와 선언이 어긋난 파일이고, 선언 쪽을 믿는 뷰어와 변환기가 있다.
|
|
124
|
+
**치수만으로는 파일을 판정할 수 없다**는 것이 이 트랩의 교훈이다.
|
|
125
|
+
pptxgenjs 는 `type` 속성을 아예 쓰지 않는데, OOXML 에서 이 속성의 기본값이 `custom` 이라 의미상 동일하다.
|
|
126
|
+
그래서 판정 규칙은 "속성 부재 또는 `custom` 이면 통과, 그 밖의 값이면 실패" 다.
|
|
127
|
+
|
|
128
|
+
## 여백과 그리드
|
|
129
|
+
|
|
130
|
+
여백은 사방 **10mm 이상**이다.
|
|
131
|
+
가정용·사무용 프린터 대부분이 가장자리 4~5mm 를 물리적으로 인쇄하지 못하기 때문이고, 재단 없이 그대로 배포되는 문서라 잘려도 복구할 방법이 없다.
|
|
132
|
+
10mm 는 그 물리 한계에 여유를 얹은 값이다.
|
|
133
|
+
|
|
134
|
+
그리드는 **8열 x 20행**이고 거터는 **4mm** 다.
|
|
135
|
+
컬럼 폭 729000 EMU, 행 높이 361800 EMU 다(m=10).
|
|
136
|
+
이 그리드는 A4 규격에서 도출되는 값이 아니라 이 스킬이 내린 **레이아웃 결정**이다. 그래서 소유한다고 정직하게 적는다.
|
|
137
|
+
|
|
138
|
+
## 행 배정 규칙과 INV-G
|
|
139
|
+
|
|
140
|
+
### INV-G (기하 불변식)
|
|
141
|
+
|
|
142
|
+
모든 도형의 `x`·`y`·`cx`·`cy` 는 다음 값들**만**의 순수 함수다.
|
|
143
|
+
|
|
144
|
+
> `meta.marginMm` · 페이지 인덱스 · 블록 순서 · 블록 `type` · `bullets.items` 배열 길이 · 명시된 `rows` · 컬럼 스팬
|
|
145
|
+
|
|
146
|
+
**텍스트의 내용·길이·폰트·pt 는 어떤 좌표에도 들어가지 않는다.**
|
|
147
|
+
|
|
148
|
+
이 불변식이 두 빌더를 교체 가능하게 만든다.
|
|
149
|
+
같은 deck JSON 을 node 빌더와 python 빌더에 먹이면 좌표가 EMU 단위로 완전히 같아야 하고, 그 동등성 검사에 폰트 리더가 필요 없다.
|
|
150
|
+
그리고 폭 측정이 빌더 출력을 한 바이트도 바꾸지 않으므로 측정은 검증기 단독 소유가 된다.
|
|
151
|
+
|
|
152
|
+
### 행 수
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
rows(block) = min(18, block.rows ?? default(type))
|
|
156
|
+
|
|
157
|
+
default(head) = 1
|
|
158
|
+
default(body) = 2
|
|
159
|
+
default(bullets) = max(1, items.length)
|
|
160
|
+
페이지 제목(h1) = 2행 고정 (모든 슬라이드 상단에 예약)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
모든 블록 타입이 `rows` 레버를 갖는다.
|
|
164
|
+
`body` 에만 있으면 한 줄에 안 들어가는 h2 도, 두 줄로 감기는 불릿 항목도 고칠 방법이 없다.
|
|
165
|
+
|
|
166
|
+
**18 클램프.** 제목이 2행을 먹으므로 블록이 쓸 수 있는 행은 `20 - 2 = 18` 이다.
|
|
167
|
+
클램프가 없으면 `rows: 19` 짜리 합법 덱 하나가 슬라이드 밖 4.05mm 로 나가는 파일을 만든다.
|
|
168
|
+
`n=18` 이면 `y + cy = 10332000` 으로 본문 영역 하단과 정확히 일치하고, 이 일치는 `marginMm` 값과 무관하게 성립한다.
|
|
169
|
+
|
|
170
|
+
**클램프 발동 판정은 `block.rows` 가 아니라 `block.rows ?? default(type)` 로 한다.**
|
|
171
|
+
항목 19개짜리 불릿 블록에 `rows` 를 안 적어도 기본값이 19 라 클램프가 걸린다.
|
|
172
|
+
조건을 `block.rows` 로만 읽으면 경고 없이 항목 17·18 이 같은 행에 겹친 채 출하된다.
|
|
173
|
+
클램프에 걸린 블록은 `BLOCK_TOO_TALL` 경고와 함께 자기 슬라이드에 혼자 놓인다.
|
|
174
|
+
|
|
175
|
+
**페이지 채우기는 세는 규칙뿐이다.** 블록을 순서대로 훑으며 `used += rows(block)` 하고, 18 을 넘기면 새 슬라이드를 연다.
|
|
176
|
+
**블록은 절대 쪼개지 않는다.** 쪼개려면 측정해야 하고, 측정은 여기서 하지 않는다.
|
|
177
|
+
|
|
178
|
+
**불릿 행 배분.** 항목 `i`(0-based)는 `min(i, n-1)` 행에서 시작하고 남는 행은 전부 마지막 항목이 가져간다.
|
|
179
|
+
`n < items.length` 면 `n-1` 행부터 항목들이 겹쳐 쌓이고, 그 겹침은 검증기가 보고한다.
|
|
180
|
+
|
|
181
|
+
### 상자보다 긴 텍스트는 넘친다
|
|
182
|
+
|
|
183
|
+
상자에 안 들어가는 텍스트는 **넘친다. 조용히 흘려 넣거나 옮기지 않는다.**
|
|
184
|
+
|
|
185
|
+
이것은 결함이 아니라 **인쇄 규격 도구로서 내린 사양 결정**이다.
|
|
186
|
+
자동 리플로우를 넣는 순간 텍스트 길이가 좌표에 들어가고 INV-G 가 깨진다.
|
|
187
|
+
그러면 두 빌더의 동등성 검증이 폰트 리더 두 벌을 요구하게 되고, 원고를 한 글자 고칠 때마다 전체 페이지네이션이 움직여 어제 검토한 배치가 오늘 달라진다.
|
|
188
|
+
인쇄용 문서에서 그건 규격 위반보다 나쁘다.
|
|
189
|
+
|
|
190
|
+
고치는 길은 조용한 이동이 아니라 `--suggest-deck` 이다.
|
|
191
|
+
검증기가 **실제로 재고 나서** `rows` 만 고친 새 deck JSON 을 내놓고, 사람이나 모델이 그 JSON 을 보고 다시 빌드한다.
|
|
192
|
+
측정이 기하에 영향을 주는 통로는 이 하나뿐이고, 그 통로는 사람이 검토할 수 있는 파일을 지난다.
|
|
193
|
+
|
|
194
|
+
`rows: 18` 로도 안 들어가면 `rows: 19` 를 제안하지 않는다.
|
|
195
|
+
`BLOCK_TOO_TALL` 과 함께 "블록을 나눠라" 를 낸다. 어디서 나눌지는 측정이 필요하고 그건 이 스킬의 범위 밖이다.
|
|
196
|
+
|
|
197
|
+
## 한글 3슬롯
|
|
198
|
+
|
|
199
|
+
run 의 `a:latin`·`a:ea`·`a:cs` **세 슬롯에 같은 typeface 를 넣어야 한다.**
|
|
200
|
+
`a:ea` 가 없으면 한글이 대체 폰트로 떨어져 일부 뷰어에서 tofu(빈 네모)가 나고, 줄바꿈 위치가 달라져 오버플로 판정 자체가 무의미해진다.
|
|
201
|
+
|
|
202
|
+
| 라이브러리 | 동작 | 필요한 조치 |
|
|
203
|
+
|---|---|---|
|
|
204
|
+
| pptxgenjs | `fontFace` 하나로 3슬롯을 모두 emit 한다 | 없음 |
|
|
205
|
+
| python-pptx | `run.font.name` 은 `a:latin` **하나만** emit 한다 | `a:ea`·`a:cs` 를 직접 주입한다 |
|
|
206
|
+
|
|
207
|
+
### 폰트 이름을 하드코딩하지 않는 이유
|
|
208
|
+
|
|
209
|
+
**맑은 고딕을 박아 넣지 않는다.** Windows 전용 폰트다.
|
|
210
|
+
macOS 와 Linux 에서는 대체 폰트로 치환되고, 치환되면 글자 폭이 달라져 줄바꿈 위치가 이동한다.
|
|
211
|
+
그러면 그리드에 맞춰 잡은 배치가 무너지고, 오버플로 판정도 함께 무효가 된다.
|
|
212
|
+
검증기는 산출물에 적힌 typeface **이름**을 경로로 해석해 폭을 재기 때문에, 그 머신에 없는 이름이 적혀 있으면 판정 전체가 `estimate` 로 강등된다.
|
|
213
|
+
|
|
214
|
+
빌더는 아래 체인을 위에서부터 훑으며 **실제로 파일이 존재하는 이름**을 고른다.
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
--font <이름> → BANKER_PPTX_FONT → meta.font →
|
|
218
|
+
Noto Sans CJK KR → Malgun Gothic → Apple SD Gothic Neo → NanumGothic → 시스템 한국어 sans
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
명시한 이름(`--font`·환경변수·`meta.font`)이 해석되지 않으면 `FONT_NOT_FOUND` 경고를 남기고 다음으로 내려간다. 조용히 바꾸지 않는다.
|
|
222
|
+
**빌더는 고른 폰트 이름과 절대경로를 항상 보고한다.** 사람이 읽는 표와 stdout 마지막 줄 JSON 양쪽에 나온다.
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{"skill":"vertical-pptx","builder":"node","font":"Noto Sans CJK KR","fontFile":"/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc","slides":1,"warnings":[]}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
그 `fontFile` 을 검증기에 `--font-file` 로 넘기면 폭을 실제 메트릭으로 잰다.
|
|
229
|
+
검증기는 그 경로를 **믿지 않고 대조한다**. 폰트의 `name` 테이블 패밀리가 pptx 의 `a:latin` 과 다르면 경로를 버리고 이름 재해석으로 내려간다.
|
|
230
|
+
경로는 존재하는데 다른 폰트인 경우가 이 설계에서 유일하게 남은 조용한 오답 경로라서 그렇다.
|
|
231
|
+
|
|
232
|
+
## 본문은 10~11pt
|
|
233
|
+
|
|
234
|
+
화면용 덱의 습관대로 18~24pt 를 쓰면 A4 한 장에 문단 몇 개밖에 안 들어간다.
|
|
235
|
+
인쇄해서 손에 들고 읽는 문서의 본문 크기는 **10~11pt** 다. 기본값은 10.5pt 다.
|
|
236
|
+
|
|
237
|
+
**출처 둘.**
|
|
238
|
+
|
|
239
|
+
1. <https://practicaltypography.com/point-size.html> - 인쇄물 본문은 10~12pt 이며, 12pt 가 기본처럼 느껴지는 것은 타자기 시대의 잔재라고 적는다.
|
|
240
|
+
2. 「행정업무의 운영 및 혁신에 관한 규정」 **제8조** - 한국 공문서의 용지 규격을 210x297mm 로 정한다.
|
|
241
|
+
|
|
242
|
+
두 번째 출처는 **조문 번호만 인용하고 조문 텍스트는 인용하지 않는다.**
|
|
243
|
+
law.go.kr 이 이 호스트에서 ECONNREFUSED 라 조문 원문을 열어 보지 못했기 때문이다.
|
|
244
|
+
읽지 않은 문장을 인용문으로 적지 않는다는 뜻이고, 조문 텍스트가 필요하면 직접 확인해야 한다.
|
|
245
|
+
|
|
246
|
+
## 방향은 한 파일에 섞을 수 없다
|
|
247
|
+
|
|
248
|
+
**하나의 pptx 는 하나의 슬라이드 크기만 갖는다.** `p:sldSz` 가 프레젠테이션 전체에 하나뿐이기 때문이고, Microsoft 문서도 한 프레젠테이션의 모든 슬라이드가 같은 방향을 공유한다고 명시한다.
|
|
249
|
+
|
|
250
|
+
"3페이지만 가로로" 같은 요청을 받으면 **먼저 이 제약을 말하고**, 방향별로 파일을 나누어 각각 빌드하라고 안내한다.
|
|
251
|
+
가로 페이지가 섞인 deck JSON 을 넘기면 빌더가 **exit 3** 으로 거부하고 파일을 나누라는 안내를 낸다.
|
|
252
|
+
|
|
253
|
+
## 폭 판정은 하한이다
|
|
254
|
+
|
|
255
|
+
예측 폭은 **하한(lower bound)** 이다.
|
|
256
|
+
커널링 쌍과 줄바꿈 규칙(한국어는 거의 아무 데서나 끊기고 라틴 문자는 아니다)을 재현하지 않기 때문에, 실제 폭은 계산값보다 넓을 수는 있어도 좁을 수는 없다.
|
|
257
|
+
그래서 "넘친다" 는 판정은 건전하고, "안 넘친다" 는 그저 침묵이다.
|
|
258
|
+
|
|
259
|
+
`fontSource` 는 검증기 출력 필드이고 **기본값은 `estimate`** 다.
|
|
260
|
+
`measured` 는 실제 폰트 파일을 열고 그 `name` 테이블 패밀리가 run 의 `a:latin` 과 일치함을 확인한 뒤에야 붙는다.
|
|
261
|
+
`estimate` 모드에서는 검증기가 **"넘친다" 를 단정하지 않는다.** finding 은 `unconfirmed` 상태로 "확인 불가(estimate)" 라고만 적고 그 이유를 함께 낸다.
|
|
262
|
+
|
|
263
|
+
라벨은 fail-safe 로 설계됐다.
|
|
264
|
+
폰트를 못 연 릴리스는 자동으로 보수적이 되고, 틀린 폭에 `measured` 가 붙는 일은 없다.
|
|
265
|
+
|
|
266
|
+
## 종료 코드
|
|
267
|
+
|
|
268
|
+
세 스크립트가 같은 규약을 쓴다. 다른 스킬이 이 스크립트들을 호출할 때 기대할 계약이다.
|
|
269
|
+
|
|
270
|
+
| 코드 | 뜻 | 언제 나오나 |
|
|
271
|
+
|---|---|---|
|
|
272
|
+
| **0** | 통과 | 빌드 성공 · 검증 PASS |
|
|
273
|
+
| **1** | 검증 실패 | 규격 위반·도형 이탈·오버플로 확정 · 재압축 무결성 실패 |
|
|
274
|
+
| **2** | 환경 미비 | pptxgenjs 또는 python-pptx 미도달 · python 3.9 미만 |
|
|
275
|
+
| **3** | 사용법 오류 | deck 스키마 위반 · `marginMm` 범위 밖 · 방향 혼합 · 인자 오류 · 입력 파일 없음 |
|
|
276
|
+
|
|
277
|
+
exit 2 는 사용자가 그대로 따라 할 수 있는 안내라서 stdout 으로 나간다. 나머지 오류는 stderr 다.
|
|
278
|
+
|
|
279
|
+
## node 가 이 스킬의 베이스라인이다
|
|
280
|
+
|
|
281
|
+
**검증 경로는 node 를 요구한다.**
|
|
282
|
+
`build_a4p.py` 는 node 가 없는 환경이 아니라 **pptxgenjs 를 설치할 수 없는 환경**을 위한 것이다.
|
|
283
|
+
python 으로 빌드했더라도 그 결과를 판정하려면 `verify_a4p.mjs` 를 돌려야 하고, 검증 없는 생성은 이 스킬의 가치가 아니다.
|
|
284
|
+
|
|
285
|
+
## 위임
|
|
286
|
+
|
|
287
|
+
> **이번 세션에서 사용 가능한 스킬 목록에 pptx 문서 스킬(`document-skills:pptx`)이 보이면**, 레이아웃·색·차트·pptxgenjs 함정은 그쪽에 맡기고 이 스킬은 규격과 검증만 맡는다.
|
|
288
|
+
> 목록에 없으면 아래 §위임 불가 시 최소 지식 만으로 진행한다. 설치를 요구하며 멈추지 않는다.
|
|
289
|
+
|
|
290
|
+
조건은 **런타임 이름이 아니라 관측 가능한 능력**이다.
|
|
291
|
+
"Codex 면" 으로 분기하면 양방향으로 틀린다.
|
|
292
|
+
`document-skills:pptx` 는 Claude Code 내장이 아니라 별도 플러그인이라 Claude 사용자에게도 없을 수 있고, Codex 쪽은 나중에 같은 능력을 얻을 수 있다.
|
|
293
|
+
모델이 자기 컨텍스트에서 확인할 수 있는 사실("목록에 보이는가")로만 판정한다.
|
|
294
|
+
|
|
295
|
+
위임한 뒤에도 규격 판정은 이 스킬이 한다. 상대가 만든 덱도 `verify_a4p.mjs` 를 통과해야 한다.
|
|
296
|
+
|
|
297
|
+
## 위임 불가 시 최소 지식
|
|
298
|
+
|
|
299
|
+
위임 대상이 목록에 없을 때 이 블록만으로 진행한다. 범위는 아래 여섯 항목이 전부다.
|
|
300
|
+
|
|
301
|
+
**색 팔레트·차트·아이콘·디자인 가이드는 여기 없다. 그건 위임 대상의 영역이다.**
|
|
302
|
+
**이 목록에 항목을 추가하려면 그것이 규격 판정에 필요한 이유를 함께 적는다.**
|
|
303
|
+
|
|
304
|
+
**1. 레이아웃을 슬라이드보다 먼저 확정한다.**
|
|
305
|
+
슬라이드를 하나라도 추가한 뒤에 크기를 바꾸면 이미 만든 슬라이드가 옛 크기를 따른다.
|
|
306
|
+
|
|
307
|
+
```js
|
|
308
|
+
// pptxgenjs
|
|
309
|
+
pptx.defineLayout({ name: 'A4P', width: 210 / 25.4, height: 297 / 25.4 });
|
|
310
|
+
pptx.layout = 'A4P';
|
|
311
|
+
// 그 다음에야 addSlide()
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
# python-pptx
|
|
316
|
+
prs.slide_width = Emu(7560000)
|
|
317
|
+
prs.slide_height = Emu(10692000)
|
|
318
|
+
# p:sldSz 의 type 속성을 지운다 (기본값이 custom 이라 지우는 것이 곧 custom 선언이다)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**2. 여백은 사방 10mm 이상이다.** 프린터가 가장자리 4~5mm 를 인쇄하지 못한다.
|
|
322
|
+
좌표 단위는 EMU 이고 1mm = 36000 EMU 다. 여백 10mm = 360000, 본문 폭 = 6840000, 본문 높이 = 9972000.
|
|
323
|
+
pptxgenjs 의 `x`/`y`/`w`/`h` 는 100 이상이면 EMU 로, 그 미만이면 인치로 해석된다. 위 값들은 전부 100 이상이라 EMU 로 간다.
|
|
324
|
+
|
|
325
|
+
**3. A4 세로 기본 배치 4종** (단위 EMU, 여백 10mm 기준).
|
|
326
|
+
|
|
327
|
+
| 배치 | 도형 | x | y | cx | cy |
|
|
328
|
+
|---|---|---|---|---|---|
|
|
329
|
+
| 제목형 | 제목 | 360000 | 360000 | 6840000 | 867600 |
|
|
330
|
+
| 본문형 | 제목 | 360000 | 360000 | 6840000 | 867600 |
|
|
331
|
+
| | 본문 | 360000 | 1371600 | 6840000 | 867600 |
|
|
332
|
+
| 2단 | 좌 | 360000 | 1371600 | 3348000 | 867600 |
|
|
333
|
+
| | 우 | 3852000 | 1371600 | 3348000 | 867600 |
|
|
334
|
+
| 표 | 표 전체 | 360000 | 1371600 | 6840000 | 행 수 x 361800 |
|
|
335
|
+
|
|
336
|
+
아래로 더 쌓을 때 `y` 는 `360000 + 505800 * r`(r = 0부터 19까지)을 쓰고, `n` 행짜리 상자의 `cy` 는 `361800 * n + 144000 * (n - 1)` 이다.
|
|
337
|
+
`y + cy` 가 **10332000** 을 넘으면 하단 여백을 침범한 것이다.
|
|
338
|
+
|
|
339
|
+
**4. 본문은 10~11pt 다.** 제목은 본문 + 6pt, 소제목은 본문 + 2pt 정도면 충분하다.
|
|
340
|
+
줄간은 1.2 배수를 쓴다.
|
|
341
|
+
|
|
342
|
+
**5. 한글은 3슬롯 전부 채운다.** `a:latin`·`a:ea`·`a:cs` 에 같은 typeface 를 넣는다.
|
|
343
|
+
pptxgenjs 는 `fontFace` 하나면 되고, python-pptx 는 `a:ea`·`a:cs` 를 직접 주입해야 한다.
|
|
344
|
+
폰트 이름은 그 머신에 실제로 있는 것을 쓴다. 맑은 고딕은 Windows 전용이라 하드코딩하지 않는다.
|
|
345
|
+
|
|
346
|
+
**6. 색은 `#` 없는 6자리 hex 다.** `'1A1A1A'` 는 되고 `'#1A1A1A'` 는 안 된다.
|
|
347
|
+
두 라이브러리 모두 이 형식을 요구하고, `#` 를 붙이면 색이 조용히 무시되거나 XML 이 깨진다.
|
|
348
|
+
|
|
349
|
+
## 의존성 (자동 설치하지 않는다)
|
|
350
|
+
|
|
351
|
+
| 도구 | 필요한 곳 | 없으면 |
|
|
352
|
+
|---|---|---|
|
|
353
|
+
| node (>= 16.7) | `verify_a4p.mjs` 전부 · `build_a4p.js` | 검증 자체가 불가능하다 |
|
|
354
|
+
| pptxgenjs (>= 4) | `build_a4p.js` 빌드 경로 | exit 2 + `docs-setup` 안내 |
|
|
355
|
+
| python 3.9+ 와 python-pptx | `build_a4p.py` | exit 2 + `docs-setup` 안내 |
|
|
356
|
+
|
|
357
|
+
`verify_a4p.mjs` 는 node stdlib 만 쓴다. 외부 의존성이 0 이라 어디서든 판정만은 돌릴 수 있다.
|
|
358
|
+
|
|
359
|
+
스크립트는 **의존성을 자동 설치하지 않는다.** exit 2 와 함께 사람이 읽고 따라 할 수 있는 안내를 내고 멈춘다.
|
|
360
|
+
런타임 준비 절차는 `docs-setup` 스킬(`/banker:docs-setup`)에 있다.
|
|
361
|
+
|
|
362
|
+
일회성으로 붙여 볼 때는 프로젝트 트리를 건드리지 않는 스크래치 설치를 쓴다.
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
npm i --no-save --no-audit --no-fund pptxgenjs@4
|
|
366
|
+
python -m pip install --target <dir> python-pptx fonttools
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
`--target` 으로 받았으면 그 경로를 `PYTHONPATH` 에, npm 쪽은 `NODE_PATH` 에 넣어 부른다.
|
|
370
|
+
|
|
371
|
+
## 수리와 변환
|
|
372
|
+
|
|
373
|
+
이미 있는 덱을 고칠 때 쓴다. **도형은 하나도 움직이지 않는다.**
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
node verify_a4p.mjs 남의덱.pptx --repair --out fixed.pptx # sldSz 만 A4 세로로 교체
|
|
377
|
+
node verify_a4p.mjs 16by9.pptx --convert-a4p --out a4p.pptx # 위 + 슬라이드별 파손 리포트
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
기본은 **새 파일**이다. 원본 덮어쓰기는 `--in-place` 로 명시해야 한다.
|
|
381
|
+
`--convert-a4p` 는 좁아진 폭 때문에 슬라이드 밖으로 나간 도형을 슬라이드별로 이탈량(mm)과 함께 열거한다.
|
|
382
|
+
자동 재배치는 하지 않는다. 어디로 옮겨야 하는지는 이 도구가 아는 정보가 아니다.
|
|
383
|
+
|
|
384
|
+
**쓰기 성공과 규격 통과는 다른 주장이다.**
|
|
385
|
+
수리한 뒤 찍히는 VERDICT 는 원본이 아니라 산출물을 다시 검증한 결과이고, sldSz 를 고쳐도 도형이 삐져나와 있으면 여전히 FAIL 이다.
|
|
386
|
+
|
|
387
|
+
## 함정
|
|
388
|
+
|
|
389
|
+
- **`8.27 x 11.69` 를 쓰지 않는다.** cx 가 2088 EMU 어긋난다. `210 / 25.4` 와 `297 / 25.4` 를 그대로 넘긴다.
|
|
390
|
+
- **치수만 보고 판정하지 않는다.** python-pptx 산출물은 A4 치수에 `type="screen4x3"` 를 달고 나온다.
|
|
391
|
+
- **PowerPoint 의 "A4 용지" 프리셋을 고르지 않는다.** 그건 가로 10.833 x 7.5in 다.
|
|
392
|
+
- **`a:ea` 누락.** python-pptx 에서 가장 흔한 한글 깨짐 원인이다.
|
|
393
|
+
- **맑은 고딕 하드코딩.** Windows 전용이고, 치환되면 그리드와 오버플로 판정이 함께 무효가 된다.
|
|
394
|
+
- **`fontSource: estimate` 인데 "안 넘친다" 로 읽기.** 그건 판정이 아니라 침묵이다. `--font-file` 을 넘겨 `measured` 로 올린 뒤 읽는다.
|
|
395
|
+
- **`--suggest-deck` 을 `--deck` 없이 부르기.** pptx 는 (슬라이드, 도형)만 알려 주고 내놓아야 하는 것은 (페이지, 블록)의 `rows` 다. 원본 deck JSON 이 필수 입력이다.
|
|
396
|
+
- **python 빌더에 `--md` 를 찾기.** 없다. 마크다운 파서는 `build_a4p.js` 한 벌뿐이고, 두 벌이 되는 순간 같은 원고가 두 개의 덱이 된다.
|
|
397
|
+
- **넘치는 텍스트를 자동으로 옮겨 주길 기대하기.** 사양상 옮기지 않는다. `--suggest-deck` 이 그 자리다.
|
|
398
|
+
|
|
399
|
+
## verify-before-claim (DoD)
|
|
400
|
+
|
|
401
|
+
"완료" 전 4-field: ① 변경 파일 ② Evidence(`verify_a4p.mjs` exit code + VERDICT 줄 + `fontSource`) ③ 검증 layer ④ Unknown.
|
|
402
|
+
`fontSource` 가 `estimate` 면 오버플로에 대해 "통과" 라고 적지 않는다. "미측정" 이 정직한 값이다.
|
|
403
|
+
픽셀 렌더 검증은 **Unknown** 이다(soffice 부재). 구조·좌표·메트릭 검증으로 대체하고 그렇게 표기한다.
|
|
404
|
+
|
|
405
|
+
## 참고
|
|
406
|
+
|
|
407
|
+
- `build_a4p.js --help` · `build_a4p.py --help` · `verify_a4p.mjs --help` 가 각 파일의 전체 플래그를 낸다.
|
|
408
|
+
- `verify_a4p.mjs --json` 이 기계 판독용 리포트를 낸다. finding 마다 `code`·`slide`·`shape`·`severity`·`status`·`detail` 이 붙는다.
|
|
409
|
+
- 한글 3슬롯 주입 패턴의 다른 사례: `skills/arch-diagram/build_pptx_template.py`
|