hwpx-kit-cli 0.2.1__tar.gz → 0.4.0__tar.gz
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.
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/.claude-plugin/plugin.json +8 -8
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/.gitignore +1 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/CLAUDE.md +1 -1
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/PKG-INFO +32 -4
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/README.md +31 -3
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/commands/hwpx.md +4 -1
- hwpx_kit_cli-0.4.0/docs/images/After.png +0 -0
- hwpx_kit_cli-0.4.0/docs/images/Before.png +0 -0
- hwpx_kit_cli-0.4.0/docs/images/Tasking.png +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/pyproject.toml +44 -44
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/skills/gongmun-format/SKILL.md +12 -1
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/skills/hwpx-form/SKILL.md +54 -2
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/adapter/hwpx_engine.py +65 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/cli.py +66 -0
- hwpx_kit_cli-0.4.0/src/hwpx_kit/commands/fill_batch.py +103 -0
- hwpx_kit_cli-0.4.0/src/hwpx_kit/commands/header_footer.py +13 -0
- hwpx_kit_cli-0.4.0/src/hwpx_kit/commands/image_add.py +25 -0
- hwpx_kit_cli-0.4.0/src/hwpx_kit/commands/inspect_doc.py +30 -0
- hwpx_kit_cli-0.4.0/src/hwpx_kit/inspect_rules.py +84 -0
- hwpx_kit_cli-0.4.0/tests/test_fill_batch.py +153 -0
- hwpx_kit_cli-0.4.0/tests/test_image_header.py +152 -0
- hwpx_kit_cli-0.4.0/tests/test_inspect.py +119 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/.claude-plugin/marketplace.json +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/.github/workflows/tests.yml +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/LICENSE +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/install.ps1 +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/install.sh +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/sample-values.json +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/scripts/build_skill_package.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/scripts/check_stdout_purity.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/scripts/make_sample.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/skills/doc-create/SKILL.md +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/skills/format-convert/SKILL.md +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/skills/office-export/SKILL.md +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/skills/table-calc/SKILL.md +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/__init__.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/adapter/__init__.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/adapter/base.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/adapter/kordoc_engine.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/adapter/office_readers.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/__init__.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/analyze.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/convert.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/export.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/fill.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/fmt.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/generate.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/outline.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/page_break.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/read.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/render.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/row_height.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/table_clear.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/table_copy.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/table_map_cmd.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/table_new.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/table_rows.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/table_set.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/commands/validate.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/format.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/src/hwpx_kit/output.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/templates/Default_Template.hwpx +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/__init__.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/conftest.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/fixtures/real/legacy-sample.hwp +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/fixtures/real/seoul-attachment.hwpx +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/fixtures/real/seoul-body.hwpx +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/fixtures/real/seoul-report-brief.hwpx +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/fixtures/real/seoul-report-cover.hwpx +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_adapter.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_analyze.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_cli.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_convert.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_export.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_fill.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_format.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_integration.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_kordoc.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_office_readers.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_output.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_page_break.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_read_validate.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_real_forms.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_row_height.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_skill_package.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_stdout_purity.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_table_clear.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_table_copy.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_table_map_new.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_table_rows.py +0 -0
- {hwpx_kit_cli-0.2.1 → hwpx_kit_cli-0.4.0}/tests/test_table_set.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "hwpx-kit",
|
|
3
|
-
"description": "한글(HWPX/HWP) 문서 자동화 스킬 모음 — 양식 분석·채우기·백지 생성·표기 변환·공문 규약·표 계산·오피스 출력. CLI는 첫 사용 시 스킬이 설치를 안내 (Python 3.10+ 필요).",
|
|
4
|
-
"version": "0.
|
|
5
|
-
"author": {
|
|
6
|
-
"name": "6aneffy",
|
|
7
|
-
"email": "tkdgus990809@gmail.com"
|
|
8
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "hwpx-kit",
|
|
3
|
+
"description": "한글(HWPX/HWP) 문서 자동화 스킬 모음 — 양식 분석·채우기·백지 생성·표기 변환·공문 규약·표 계산·오피스 출력. CLI는 첫 사용 시 스킬이 설치를 안내 (Python 3.10+ 필요).",
|
|
4
|
+
"version": "0.4.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "6aneffy",
|
|
7
|
+
"email": "tkdgus990809@gmail.com"
|
|
8
|
+
}
|
|
9
9
|
}
|
|
@@ -26,7 +26,7 @@ skills/*/SKILL.md → Claude Code 플러그인 스킬들 (.claude-plugin/plugin.
|
|
|
26
26
|
└─ office-export → hwpx→docx(변환)·pptx/xlsx(재구성, officecli 선택 의존)
|
|
27
27
|
|
|
28
28
|
CLI (cli.py) — JSON 봉투, 종료코드 0/1/2
|
|
29
|
-
└─ commands/ (analyze·fill·read·validate·convert·export·render·generate·fmt·row-add·row-del·row-height·table-clear·table-set·table-copy·table-map·table-new·page-break·outline)
|
|
29
|
+
└─ commands/ (analyze·fill·fill-batch·inspect·image-add·header-footer·read·validate·convert·export·render·generate·fmt·row-add·row-del·row-height·table-clear·table-set·table-copy·table-map·table-new·page-break·outline)
|
|
30
30
|
├─ format.py ← 순수함수 (금액한글화·요일·만나이) — 엔진 임포트 금지, fmt는 파일 인수 없음(FILE_NOT_FOUND 가드 우회)
|
|
31
31
|
└─ adapter/ ← 명령 계층은 어댑터 인터페이스만 사용 (엔진 직접 import 금지)
|
|
32
32
|
├─ hwpx_engine.py → python-hwpx (hwpx 분석·채우기·검증·표 조작, 순수 Python)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hwpx-kit-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: 한글(HWPX) 문서 자동화 CLI — Korean HWPX document automation for AI agents: analyze, fill, read, validate, table ops
|
|
5
5
|
Project-URL: Homepage, https://github.com/6aneffy/hwpx-kit
|
|
6
6
|
Project-URL: Repository, https://github.com/6aneffy/hwpx-kit
|
|
@@ -33,7 +33,7 @@ Description-Content-Type: text/markdown
|
|
|
33
33
|
[](https://pypi.org/project/hwpx-kit-cli/)
|
|
34
34
|
[](https://www.python.org/)
|
|
35
35
|
[](https://claude.com/claude-code)
|
|
36
|
-
[](tests/)
|
|
37
37
|
|
|
38
38
|
</div>
|
|
39
39
|
|
|
@@ -66,6 +66,30 @@ hwpx-kit은 **AI 에이전트가 한글 문서를 읽고, 채우고, 고치고,
|
|
|
66
66
|
**이런 분들을 위해 만들었습니다**: 공공기관·학교의 서식 담당자, 사업계획서를 반복 작성하는
|
|
67
67
|
창업자·연구자, 한글 문서를 AI 워크플로에 넣고 싶은 개발자.
|
|
68
68
|
|
|
69
|
+
## 데모 — 한마디로 기획서 한 부
|
|
70
|
+
|
|
71
|
+
> "hwpx로 새로운 사업기획서 작성해줘. 서울광역시에서 진행하는 Claude Code 해커톤"
|
|
72
|
+
|
|
73
|
+
**① 시작점 — 내장 템플릿** (양식 파일이 없을 때 기준. 갖고 있는 양식이 있으면 그 양식을 그대로 씁니다)
|
|
74
|
+
|
|
75
|
+

|
|
76
|
+
|
|
77
|
+
**② 작업 과정** — 필수 정보(일시·대상·주최·예산)를 먼저 묻고, 답을 받아 진행합니다
|
|
78
|
+
|
|
79
|
+

|
|
80
|
+
|
|
81
|
+
**③ 결과물** — 표지·5개 장·일정표·예산표까지 채워진 완성본. 서식은 템플릿 그대로
|
|
82
|
+
|
|
83
|
+

|
|
84
|
+
|
|
85
|
+
이 데모에서 실제로 일어난 일들:
|
|
86
|
+
|
|
87
|
+
- **아는 것만 쓰고, 모르는 건 묻습니다** — 장소가 미정이라고 하자 "서울 시내 행사장(추후 확정)"으로 표기하고 확정되면 수정하겠다고 알림. 말하지 않은 정원·시상금을 지어내지 않음
|
|
88
|
+
- **내용을 주면 그대로, 안 주면 Claude가 초안** — 시상금 구성은 사용자가 준 숫자 그대로, 추진배경·목적은 "필요하다고 생각하는 것들 다 써줘"라는 요청에 Claude가 작성. **상세하게 말할수록 상세하게 반영됩니다**
|
|
89
|
+
- **표 계산은 결정론** — 예산표 합계 7,000 + 30,000 = 총계 37,000천원, 수기 계산 착오 없음
|
|
90
|
+
- **구조도 바꿉니다** — 장 추가/복제, 표 행 추가·삭제(서식 승계), 쪽나눔까지. "5장 뒤에 강사진 장 추가해줘"가 통합니다
|
|
91
|
+
- **기존 양식 재활용** — 작성 완료된 옛 기획서를 주고 "○○사업용으로 바꿔줘"라고 하면 그 문서를 양식 삼아 내용만 전면 교체 (서식 무손상)
|
|
92
|
+
|
|
69
93
|
## 30초 설치
|
|
70
94
|
|
|
71
95
|
Claude Code에서:
|
|
@@ -120,6 +144,8 @@ hwpx-kit validate 문서.hwpx --json # 구조 검증
|
|
|
120
144
|
|
|
121
145
|
# 채우기·편집 (원본 불변 — 항상 --out 사본)
|
|
122
146
|
hwpx-kit fill 양식.hwpx --data 값.json --out 결과.hwpx
|
|
147
|
+
hwpx-kit fill-batch 양식.hwpx --rows 명단.xlsx --template 값틀.json --out-dir 결과/ --name "{성명}_위촉장.hwpx" # 메일머지
|
|
148
|
+
hwpx-kit inspect 결과.hwpx --json # 제출 전 검수 — 잔여 마커·공문 표기·개인정보
|
|
123
149
|
hwpx-kit table-set 문서.hwpx --table 3 --data 셀.json --out 결과.hwpx # 좌표 셀 쓰기
|
|
124
150
|
hwpx-kit table-clear 문서.hwpx --table 3 --rows 1-20 --out 결과.hwpx # 셀 내용 비우기
|
|
125
151
|
hwpx-kit table-copy 문서.hwpx --table 1 --after-table 4 --out 결과.hwpx # 표 통째 복제 (서식·병합 유지)
|
|
@@ -128,6 +154,8 @@ hwpx-kit row-add 문서.hwpx --table 3 --like 2 --count 3 --out 결과.hwpx
|
|
|
128
154
|
hwpx-kit row-del 문서.hwpx --table 3 --rows 5-7 --out 결과.hwpx # 행 삭제
|
|
129
155
|
hwpx-kit row-height 문서.hwpx --table 3 --like 1 --rows 2-5 --out 결과.hwpx # 행 높이 정돈
|
|
130
156
|
hwpx-kit page-break 문서.hwpx --table 5 --out 결과.hwpx # 쪽나눔 (새 장 시작)
|
|
157
|
+
hwpx-kit image-add 문서.hwpx --image 도장.png --at-text "(인)" --width-mm 20 --out 결과.hwpx # 직인·사진
|
|
158
|
+
hwpx-kit header-footer 문서.hwpx --footer "대외비" --page-number center --out 결과.hwpx
|
|
131
159
|
|
|
132
160
|
# 변환·생성
|
|
133
161
|
hwpx-kit fmt --amount 12340 --json # 금12,340원(금일만이천삼백사십원)
|
|
@@ -164,7 +192,7 @@ hwpx-kit render 문서.hwpx --out p.svg # SVG 미리보기 (kordoc)
|
|
|
164
192
|
## 아키텍처
|
|
165
193
|
|
|
166
194
|
```
|
|
167
|
-
Claude Code 스킬 6종 ──→ hwpx-kit CLI (
|
|
195
|
+
Claude Code 스킬 6종 ──→ hwpx-kit CLI (25 명령, JSON 봉투)
|
|
168
196
|
└─ adapter/ ← 엔진 격리 계층
|
|
169
197
|
├─ python-hwpx hwpx 분석·채우기·표 조작
|
|
170
198
|
├─ pypdf·python-docx·openpyxl 타 포맷 읽기
|
|
@@ -180,7 +208,7 @@ templates/ 기본 계획서 템플릿 (표지·장 헤더·표 골격, 마커
|
|
|
180
208
|
|
|
181
209
|
## 검증
|
|
182
210
|
|
|
183
|
-
- 자동 테스트
|
|
211
|
+
- 자동 테스트 186개 (pytest)
|
|
184
212
|
- 실서식 수렴 테스트: 보도자료·신청서·계획서·회의록·공고문 5계열,
|
|
185
213
|
실제 공공기관 문서 30여 종으로 analyze→fill→validate 왕복
|
|
186
214
|
- 실사용 시나리오 테스트 3회 (백지 생성 12분 완주, 장·표 동적 추가, 보도자료 변환)
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[](https://pypi.org/project/hwpx-kit-cli/)
|
|
11
11
|
[](https://www.python.org/)
|
|
12
12
|
[](https://claude.com/claude-code)
|
|
13
|
-
[](tests/)
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
@@ -43,6 +43,30 @@ hwpx-kit은 **AI 에이전트가 한글 문서를 읽고, 채우고, 고치고,
|
|
|
43
43
|
**이런 분들을 위해 만들었습니다**: 공공기관·학교의 서식 담당자, 사업계획서를 반복 작성하는
|
|
44
44
|
창업자·연구자, 한글 문서를 AI 워크플로에 넣고 싶은 개발자.
|
|
45
45
|
|
|
46
|
+
## 데모 — 한마디로 기획서 한 부
|
|
47
|
+
|
|
48
|
+
> "hwpx로 새로운 사업기획서 작성해줘. 서울광역시에서 진행하는 Claude Code 해커톤"
|
|
49
|
+
|
|
50
|
+
**① 시작점 — 내장 템플릿** (양식 파일이 없을 때 기준. 갖고 있는 양식이 있으면 그 양식을 그대로 씁니다)
|
|
51
|
+
|
|
52
|
+

|
|
53
|
+
|
|
54
|
+
**② 작업 과정** — 필수 정보(일시·대상·주최·예산)를 먼저 묻고, 답을 받아 진행합니다
|
|
55
|
+
|
|
56
|
+

|
|
57
|
+
|
|
58
|
+
**③ 결과물** — 표지·5개 장·일정표·예산표까지 채워진 완성본. 서식은 템플릿 그대로
|
|
59
|
+
|
|
60
|
+

|
|
61
|
+
|
|
62
|
+
이 데모에서 실제로 일어난 일들:
|
|
63
|
+
|
|
64
|
+
- **아는 것만 쓰고, 모르는 건 묻습니다** — 장소가 미정이라고 하자 "서울 시내 행사장(추후 확정)"으로 표기하고 확정되면 수정하겠다고 알림. 말하지 않은 정원·시상금을 지어내지 않음
|
|
65
|
+
- **내용을 주면 그대로, 안 주면 Claude가 초안** — 시상금 구성은 사용자가 준 숫자 그대로, 추진배경·목적은 "필요하다고 생각하는 것들 다 써줘"라는 요청에 Claude가 작성. **상세하게 말할수록 상세하게 반영됩니다**
|
|
66
|
+
- **표 계산은 결정론** — 예산표 합계 7,000 + 30,000 = 총계 37,000천원, 수기 계산 착오 없음
|
|
67
|
+
- **구조도 바꿉니다** — 장 추가/복제, 표 행 추가·삭제(서식 승계), 쪽나눔까지. "5장 뒤에 강사진 장 추가해줘"가 통합니다
|
|
68
|
+
- **기존 양식 재활용** — 작성 완료된 옛 기획서를 주고 "○○사업용으로 바꿔줘"라고 하면 그 문서를 양식 삼아 내용만 전면 교체 (서식 무손상)
|
|
69
|
+
|
|
46
70
|
## 30초 설치
|
|
47
71
|
|
|
48
72
|
Claude Code에서:
|
|
@@ -97,6 +121,8 @@ hwpx-kit validate 문서.hwpx --json # 구조 검증
|
|
|
97
121
|
|
|
98
122
|
# 채우기·편집 (원본 불변 — 항상 --out 사본)
|
|
99
123
|
hwpx-kit fill 양식.hwpx --data 값.json --out 결과.hwpx
|
|
124
|
+
hwpx-kit fill-batch 양식.hwpx --rows 명단.xlsx --template 값틀.json --out-dir 결과/ --name "{성명}_위촉장.hwpx" # 메일머지
|
|
125
|
+
hwpx-kit inspect 결과.hwpx --json # 제출 전 검수 — 잔여 마커·공문 표기·개인정보
|
|
100
126
|
hwpx-kit table-set 문서.hwpx --table 3 --data 셀.json --out 결과.hwpx # 좌표 셀 쓰기
|
|
101
127
|
hwpx-kit table-clear 문서.hwpx --table 3 --rows 1-20 --out 결과.hwpx # 셀 내용 비우기
|
|
102
128
|
hwpx-kit table-copy 문서.hwpx --table 1 --after-table 4 --out 결과.hwpx # 표 통째 복제 (서식·병합 유지)
|
|
@@ -105,6 +131,8 @@ hwpx-kit row-add 문서.hwpx --table 3 --like 2 --count 3 --out 결과.hwpx
|
|
|
105
131
|
hwpx-kit row-del 문서.hwpx --table 3 --rows 5-7 --out 결과.hwpx # 행 삭제
|
|
106
132
|
hwpx-kit row-height 문서.hwpx --table 3 --like 1 --rows 2-5 --out 결과.hwpx # 행 높이 정돈
|
|
107
133
|
hwpx-kit page-break 문서.hwpx --table 5 --out 결과.hwpx # 쪽나눔 (새 장 시작)
|
|
134
|
+
hwpx-kit image-add 문서.hwpx --image 도장.png --at-text "(인)" --width-mm 20 --out 결과.hwpx # 직인·사진
|
|
135
|
+
hwpx-kit header-footer 문서.hwpx --footer "대외비" --page-number center --out 결과.hwpx
|
|
108
136
|
|
|
109
137
|
# 변환·생성
|
|
110
138
|
hwpx-kit fmt --amount 12340 --json # 금12,340원(금일만이천삼백사십원)
|
|
@@ -141,7 +169,7 @@ hwpx-kit render 문서.hwpx --out p.svg # SVG 미리보기 (kordoc)
|
|
|
141
169
|
## 아키텍처
|
|
142
170
|
|
|
143
171
|
```
|
|
144
|
-
Claude Code 스킬 6종 ──→ hwpx-kit CLI (
|
|
172
|
+
Claude Code 스킬 6종 ──→ hwpx-kit CLI (25 명령, JSON 봉투)
|
|
145
173
|
└─ adapter/ ← 엔진 격리 계층
|
|
146
174
|
├─ python-hwpx hwpx 분석·채우기·표 조작
|
|
147
175
|
├─ pypdf·python-docx·openpyxl 타 포맷 읽기
|
|
@@ -157,7 +185,7 @@ templates/ 기본 계획서 템플릿 (표지·장 헤더·표 골격, 마커
|
|
|
157
185
|
|
|
158
186
|
## 검증
|
|
159
187
|
|
|
160
|
-
- 자동 테스트
|
|
188
|
+
- 자동 테스트 186개 (pytest)
|
|
161
189
|
- 실서식 수렴 테스트: 보도자료·신청서·계획서·회의록·공고문 5계열,
|
|
162
190
|
실제 공공기관 문서 30여 종으로 analyze→fill→validate 왕복
|
|
163
191
|
- 실사용 시나리오 테스트 3회 (백지 생성 12분 완주, 장·표 동적 추가, 보도자료 변환)
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: hwpx-kit 도움말 — 스킬 6종과 사용 예시 안내
|
|
2
|
+
description: hwpx-kit 도움말 — 스킬 6종과 사용 예시 안내 (사용자가 /hwpx를 직접 입력했을 때 전용)
|
|
3
|
+
disable-model-invocation: true
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
아래 카탈로그를 그대로 사용자에게 출력하라. 다른 도구를 호출하지 말 것.
|
|
7
|
+
(이 카탈로그는 사용자가 /hwpx를 직접 쳤을 때만 보여주는 것 — 문서 작업
|
|
8
|
+
요청이 들어온 상황이라면 카탈로그 출력 없이 해당 스킬로 바로 진행하라.)
|
|
6
9
|
|
|
7
10
|
```
|
|
8
11
|
📄 hwpx-kit — 한글(HWPX) 문서 자동화
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "hwpx-kit-cli"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "한글(HWPX) 문서 자동화 CLI — Korean HWPX document automation for AI agents: analyze, fill, read, validate, table ops"
|
|
5
|
-
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.10"
|
|
7
|
-
license = "Apache-2.0"
|
|
8
|
-
authors = [{ name = "6aneffy", email = "tkdgus990809@gmail.com" }]
|
|
9
|
-
keywords = ["hwpx", "hwp", "hangul", "korean", "document-automation", "claude-code"]
|
|
10
|
-
classifiers = [
|
|
11
|
-
"Programming Language :: Python :: 3",
|
|
12
|
-
"Operating System :: OS Independent",
|
|
13
|
-
"Topic :: Office/Business :: Office Suites",
|
|
14
|
-
]
|
|
15
|
-
dependencies = [
|
|
16
|
-
"openpyxl>=3.1.5",
|
|
17
|
-
"pypdf>=6.14.2",
|
|
18
|
-
"python-docx>=1.2.0",
|
|
19
|
-
"python-hwpx>=2.24,<3",
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
[project.optional-dependencies]
|
|
23
|
-
convert = ["pyhwpx; sys_platform == 'win32'"]
|
|
24
|
-
|
|
25
|
-
[project.urls]
|
|
26
|
-
Homepage = "https://github.com/6aneffy/hwpx-kit"
|
|
27
|
-
Repository = "https://github.com/6aneffy/hwpx-kit"
|
|
28
|
-
Issues = "https://github.com/6aneffy/hwpx-kit/issues"
|
|
29
|
-
|
|
30
|
-
[project.scripts]
|
|
31
|
-
hwpx-kit = "hwpx_kit.cli:main"
|
|
32
|
-
|
|
33
|
-
[dependency-groups]
|
|
34
|
-
dev = ["pytest>=8"]
|
|
35
|
-
|
|
36
|
-
[build-system]
|
|
37
|
-
requires = ["hatchling"]
|
|
38
|
-
build-backend = "hatchling.build"
|
|
39
|
-
|
|
40
|
-
[tool.hatch.build.targets.wheel]
|
|
41
|
-
packages = ["src/hwpx_kit"]
|
|
42
|
-
|
|
43
|
-
[tool.pytest.ini_options]
|
|
44
|
-
testpaths = ["tests"]
|
|
1
|
+
[project]
|
|
2
|
+
name = "hwpx-kit-cli"
|
|
3
|
+
version = "0.4.0"
|
|
4
|
+
description = "한글(HWPX) 문서 자동화 CLI — Korean HWPX document automation for AI agents: analyze, fill, read, validate, table ops"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = "Apache-2.0"
|
|
8
|
+
authors = [{ name = "6aneffy", email = "tkdgus990809@gmail.com" }]
|
|
9
|
+
keywords = ["hwpx", "hwp", "hangul", "korean", "document-automation", "claude-code"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Programming Language :: Python :: 3",
|
|
12
|
+
"Operating System :: OS Independent",
|
|
13
|
+
"Topic :: Office/Business :: Office Suites",
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"openpyxl>=3.1.5",
|
|
17
|
+
"pypdf>=6.14.2",
|
|
18
|
+
"python-docx>=1.2.0",
|
|
19
|
+
"python-hwpx>=2.24,<3",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
convert = ["pyhwpx; sys_platform == 'win32'"]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/6aneffy/hwpx-kit"
|
|
27
|
+
Repository = "https://github.com/6aneffy/hwpx-kit"
|
|
28
|
+
Issues = "https://github.com/6aneffy/hwpx-kit/issues"
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
hwpx-kit = "hwpx_kit.cli:main"
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = ["pytest>=8"]
|
|
35
|
+
|
|
36
|
+
[build-system]
|
|
37
|
+
requires = ["hatchling"]
|
|
38
|
+
build-backend = "hatchling.build"
|
|
39
|
+
|
|
40
|
+
[tool.hatch.build.targets.wheel]
|
|
41
|
+
packages = ["src/hwpx_kit"]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
testpaths = ["tests"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gongmun-format
|
|
3
|
-
description: 행안부 공문서 작성 규약 — 글머리 위계(□○-※*), 날짜·금액 표기, 홑낫표, 어절정리, 복붙 깨짐
|
|
3
|
+
description: 행안부 공문서 작성 규약 — 글머리 위계(□○-※*), 날짜·금액 표기, 홑낫표, 어절정리, 복붙 깨짐 복구, 제출 전 기계 검수(inspect). 공문·보도자료·계획안 초안 작성이나 "공문 형식으로", "공문서 규정대로", "보고서 양식 맞춰줘", "제출 전에 검토해줘" 요청 시 사용. Triggers: Korean government document style, gongmun formatting, review before submission.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# gongmun-format: 행안부 공문 작성 규약
|
|
@@ -34,6 +34,17 @@ description: 행안부 공문서 작성 규약 — 글머리 위계(□○-※*)
|
|
|
34
34
|
- **법령·문서명**: 홑낫표 `「」` 로 감싼다 — `「행정 효율과 협업 촉진에 관한 규정」`
|
|
35
35
|
- **증감·비율**: table-calc 스킬의 표기 관습(△·%p 등) 따름
|
|
36
36
|
|
|
37
|
+
## 제출 전 검수 — "검토해줘" (기계 검사 먼저, 판단은 그 위에)
|
|
38
|
+
|
|
39
|
+
`hwpx-kit inspect <문서.hwpx> --json` — 결정론 검사 3종 (fmt와 같은 "암산 금지" 원칙):
|
|
40
|
+
- **residue**: 채우다 만 {{마커}}·○○○ 자리표시·"(예시)" 안내문 잔존
|
|
41
|
+
- **gongmun**: 날짜 하이픈 표기(2026-07-10 → `2026. 7. 10.`), 오전/오후 시각(→ 24시각제)
|
|
42
|
+
- **pii**: 주민번호·휴대전화·이메일 잔존 (공개 배포 문서면 특히)
|
|
43
|
+
|
|
44
|
+
종료코드 2 = 위반 있음. `issues[]`의 code·context를 **사람 말로 요약**해 보여주고,
|
|
45
|
+
수정할 항목을 확정받아 fill(text:/delete:)로 고친 뒤 재검사. 기계 검사가 못 보는
|
|
46
|
+
것(위계 건너뜀, 어색한 문장, 내용 누락)은 read 출력을 직접 읽고 이 스킬 규약으로 판단.
|
|
47
|
+
|
|
37
48
|
## 텍스트 정리 (재작성 계열 — 우리가 범피스보다 잘하는 것)
|
|
38
49
|
|
|
39
50
|
한글 문서에서 read로 뽑은 텍스트나 사용자가 붙여넣은 텍스트가 깨져 있으면:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hwpx-kit
|
|
3
|
-
description: HWPX 양식 자동화 — 한글(HWPX/HWP) 양식 분석, 내용 채우기, 본문 추출, 구조 검증, .hwp→.hwpx 변환. 사용자가 hwpx/hwp 파일을 주며 "이 양식에 채워줘", "여기에 써줘", "이 문서 읽어줘/요약해줘", "hwp 변환해줘" 할 때 사용. Triggers: fill this Korean form, read this hwp/hwpx file, Hangul document automation.
|
|
3
|
+
description: HWPX 양식 자동화 — 한글(HWPX/HWP) 양식 분석, 내용 채우기, 본문 추출, 구조 검증, .hwp→.hwpx 변환. 사용자가 hwpx/hwp 파일을 주며 "이 양식에 채워줘", "여기에 써줘", "이 명단으로 30명분 만들어줘"(일괄 생성), "이 엑셀 표 넣어줘", "도장 찍어줘/사진 붙여줘", "쪽번호 넣어줘", "이 문서 읽어줘/요약해줘", "hwp 변환해줘" 할 때 사용. Triggers: fill this Korean form, mail merge certificates, read this hwp/hwpx file, Hangul document automation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hwpx-kit: HWPX 양식 자동화
|
|
@@ -21,6 +21,11 @@ CLI `hwpx-kit`으로 hwpx 문서를 다룬다. 항상 `--json`을 붙여 실행
|
|
|
21
21
|
(실패 시 폴백: `pip install --user git+https://github.com/6aneffy/hwpx-kit.git`)
|
|
22
22
|
→ `python -m hwpx_kit.cli --version` 확인 후 원래 작업 계속
|
|
23
23
|
|
|
24
|
+
**구버전 업그레이드도 같은 이름**: 설치돼 있는데 필요한 명령(row-add 등)이
|
|
25
|
+
없으면 `pip install -U hwpx-kit-cli`. 🔴 **PyPI 배포명은 `hwpx-kit-cli`다**
|
|
26
|
+
— `hwpx-kit`은 PyPI에 없는 이름이니 (플러그인·명령어 이름일 뿐) 그걸로
|
|
27
|
+
검색/설치 시도하지 말 것
|
|
28
|
+
|
|
24
29
|
기존 문서(계획안·보고서 작성본)를 양식 삼아 **다른 사업 문서를 새로 쓰는** 요청도
|
|
25
30
|
같은 흐름이다 — 내용(사업명·배경·커리큘럼 등)은 네가 초안을 짜서 사용자와 확정하고,
|
|
26
31
|
문서화는 read → text:/table: 교체로 한다. 서식은 원본 그대로 유지된다.
|
|
@@ -129,9 +134,56 @@ CLI `hwpx-kit`으로 hwpx 문서를 다룬다. 항상 `--json`을 붙여 실행
|
|
|
129
134
|
`hwpx-kit fill <양식.hwpx> --data <값.json> --out <결과.hwpx> --json`
|
|
130
135
|
- 원본은 절대 수정되지 않는다. `--out`은 원본과 다른 경로여야 한다
|
|
131
136
|
- 종료 코드 2면 `data.unmatched[]`의 사유를 사용자에게 보여주고 값이나 키를 수정해 재시도
|
|
132
|
-
4. **검증**: `hwpx-kit validate <결과.hwpx> --json`
|
|
137
|
+
4. **검증**: `hwpx-kit validate <결과.hwpx> --json` (구조) →
|
|
138
|
+
`hwpx-kit inspect <결과.hwpx> --json` (내용 — 잔여 {{마커}}·○○○ 자리표시·
|
|
139
|
+
공문 표기 위반·개인정보 잔존을 기계 검사. 종료코드 2면 issues[]를 사람 말로
|
|
140
|
+
요약해 보여주고 수정 여부 확인)
|
|
133
141
|
5. **안내**: 결과 파일 경로를 알려주고, 최종 서식은 한글(한컴오피스)에서 열어 확인하도록 권한다
|
|
134
142
|
|
|
143
|
+
## 일괄 생성 (메일머지) — "이 명단으로 30명분 만들어줘"
|
|
144
|
+
|
|
145
|
+
위촉장·수료증·통지서처럼 **같은 양식 × 명단 N명**은 fill을 N번 돌리지 말고
|
|
146
|
+
`fill-batch` 하나로:
|
|
147
|
+
|
|
148
|
+
1. 명단 파일 확보 — xlsx(1행이 헤더)·csv·json 배열 모두 됨. 명단이 대화로만
|
|
149
|
+
왔으면 json으로 저장
|
|
150
|
+
2. analyze로 fill_key 파악 → **템플릿 JSON** 작성: 값 속에 `{열이름}` 자리표시
|
|
151
|
+
```json
|
|
152
|
+
{"marker:성명": "{성명}", "marker:과정명": "{과정} 과정"}
|
|
153
|
+
```
|
|
154
|
+
3. **첫 1부를 먼저 뽑아 사용자 승인** (fill로 1건) — 승인 후 전체:
|
|
155
|
+
`hwpx-kit fill-batch <양식.hwpx> --rows <명단.xlsx> --template <템플릿.json> --out-dir <폴더> --name "{성명}_위촉장.hwpx" --json`
|
|
156
|
+
4. 종료코드 2면 `rows[]`에서 실패 행만 추려 보고. 파일명 충돌은 자동 `_2` 접미,
|
|
157
|
+
금지문자는 `_` 치환. 원본 불변은 fill과 동일
|
|
158
|
+
5. 개인정보 든 명단 파일은 사용자 문서와 동일한 기밀 취급 — 커밋·업로드 금지
|
|
159
|
+
|
|
160
|
+
## 사진·직인 넣기 — "여기 도장 찍어줘", "사진 붙여줘"
|
|
161
|
+
|
|
162
|
+
`hwpx-kit image-add <문서.hwpx> --image <파일.png> ... --out <결과.hwpx>`
|
|
163
|
+
- **직인/서명**: 대상 문단 원문으로 — `--at-text "서명: (인)" --width-mm 20`
|
|
164
|
+
(원문은 read 출력에서 복사. 이미지 파일은 사용자에게 받는다 — 도장을 그려주지 않는다)
|
|
165
|
+
- **사진 첨부 칸**(표 안): `--table N --cell R,C --width-mm 35 --height-mm 45`
|
|
166
|
+
(증명사진 규격 예시. 좌표는 table-map으로 확인)
|
|
167
|
+
- 글자처럼취급으로 들어가므로 셀 크기가 이미지에 맞을수록 깔끔 — 크게 넣으면
|
|
168
|
+
행이 부푼다. 삽입 후 validate + 한글 육안 확인 안내
|
|
169
|
+
- 사용자 제공 사진·도장 이미지도 문서와 동일한 기밀 취급
|
|
170
|
+
|
|
171
|
+
## 머리말·꼬리말·쪽번호 — "쪽번호 넣어줘"
|
|
172
|
+
|
|
173
|
+
`hwpx-kit header-footer <문서.hwpx> [--header "텍스트"] [--footer "텍스트"] [--page-number center] --out <결과.hwpx>`
|
|
174
|
+
— 쪽번호 위치는 left/center/right. 문서관리번호·대외비 표기 등에 사용.
|
|
175
|
+
|
|
176
|
+
## 엑셀 표를 한글 문서에 넣기 — "이 엑셀 표 문서에 넣어줘"
|
|
177
|
+
|
|
178
|
+
복붙(서식 깨짐) 대신 도구 조합으로:
|
|
179
|
+
1. `hwpx-kit read <파일.xlsx> --format md` — 표 데이터 확보
|
|
180
|
+
2. `outline`으로 삽입 위치(앵커 문단/표) 확인, 애매하면 사용자에게
|
|
181
|
+
3. `table-new --rows R --cols C --like-table <이웃 표> --after-text "<앵커>"` —
|
|
182
|
+
문서 기존 표 서식을 차용해 새 표 생성 (이웃 표 없으면 --like-table 생략)
|
|
183
|
+
4. `table-set --data <셀.json>`으로 값 기입 — 숫자는 세 자리 콤마 등 표기는
|
|
184
|
+
table-calc 스킬 관습, 헤더 행은 (0,c) 좌표
|
|
185
|
+
5. validate → 경로 안내
|
|
186
|
+
|
|
135
187
|
## 예시 텍스트 교체 (보도자료 등 "예시 문구가 든 양식")
|
|
136
188
|
|
|
137
189
|
누름틀/빈칸이 아니라 예시 문장이 채워진 양식(보도자료 제목·본문 등)은 `text:` 키 사용:
|
|
@@ -671,6 +671,71 @@ class HwpxEngineAdapter:
|
|
|
671
671
|
self._mark_sections_dirty()
|
|
672
672
|
return len(targets)
|
|
673
673
|
|
|
674
|
+
_HWPUNIT_PER_MM = 7200 / 25.4 # 1mm ≈ 283.46 hwpunit
|
|
675
|
+
|
|
676
|
+
def insert_image(self, image_path: str, *, at_text: str | None = None,
|
|
677
|
+
table_index: int | None = None, cell: tuple[int, int] | None = None,
|
|
678
|
+
width_mm: float = 20.0, height_mm: float | None = None) -> dict:
|
|
679
|
+
"""이미지를 문단(원문 앵커) 또는 표 셀에 글자처럼취급으로 삽입.
|
|
680
|
+
|
|
681
|
+
엔진 2단계: add_image(바이너리 등록) → paragraph.add_picture(배치).
|
|
682
|
+
height_mm 생략 시 정사각(width와 동일) — 비율 유지가 필요하면 호출자가 지정.
|
|
683
|
+
"""
|
|
684
|
+
import os as _os
|
|
685
|
+
|
|
686
|
+
ext = _os.path.splitext(image_path)[1].lower().lstrip(".")
|
|
687
|
+
if ext == "jpeg":
|
|
688
|
+
ext = "jpg"
|
|
689
|
+
if ext not in ("png", "jpg", "bmp", "gif"):
|
|
690
|
+
raise ValueError(f"지원하지 않는 이미지 형식: .{ext} (png/jpg/bmp/gif)")
|
|
691
|
+
with open(image_path, "rb") as f:
|
|
692
|
+
image_data = f.read()
|
|
693
|
+
|
|
694
|
+
width = int(width_mm * self._HWPUNIT_PER_MM)
|
|
695
|
+
height = int((height_mm if height_mm is not None else width_mm) * self._HWPUNIT_PER_MM)
|
|
696
|
+
|
|
697
|
+
with quiet_engine():
|
|
698
|
+
if cell is not None:
|
|
699
|
+
if table_index is None:
|
|
700
|
+
raise ValueError("--cell은 --table과 함께 지정하세요.")
|
|
701
|
+
from hwpx.tools import table_navigation as tn
|
|
702
|
+
|
|
703
|
+
tables = tn._collect_document_tables(self._doc)
|
|
704
|
+
if not 0 <= table_index < len(tables):
|
|
705
|
+
raise ValueError(f"표 인덱스 범위 밖: {table_index} (표 {len(tables)}개)")
|
|
706
|
+
r, c = cell
|
|
707
|
+
paragraphs = tables[table_index].table.cell(r, c).paragraphs
|
|
708
|
+
target = list(paragraphs)[0]
|
|
709
|
+
else:
|
|
710
|
+
target = self._find_anchor_paragraph(anchor_text=at_text,
|
|
711
|
+
after_table=table_index)
|
|
712
|
+
item_id = self._doc.add_image(image_data, ext)
|
|
713
|
+
target.add_picture(item_id, width=width, height=height, treat_as_char=True)
|
|
714
|
+
return {"binary_item_id": item_id, "width": width, "height": height}
|
|
715
|
+
|
|
716
|
+
def set_header_footer(self, *, header: str | None = None,
|
|
717
|
+
footer: str | None = None,
|
|
718
|
+
page_number: str | None = None) -> list[str]:
|
|
719
|
+
"""머리말/꼬리말 텍스트·쪽번호 설정. 적용 항목 이름 목록 반환."""
|
|
720
|
+
applied: list[str] = []
|
|
721
|
+
align_map = {"left": "LEFT", "center": "CENTER", "right": "RIGHT"}
|
|
722
|
+
with quiet_engine():
|
|
723
|
+
if header is not None:
|
|
724
|
+
self._doc.set_header_text(header)
|
|
725
|
+
applied.append("header")
|
|
726
|
+
if footer is not None:
|
|
727
|
+
self._doc.set_footer_text(footer)
|
|
728
|
+
applied.append("footer")
|
|
729
|
+
if page_number is not None:
|
|
730
|
+
align = align_map.get(page_number.lower())
|
|
731
|
+
if align is None:
|
|
732
|
+
raise ValueError(f"쪽번호 위치는 left/center/right 중 하나: {page_number}")
|
|
733
|
+
self._doc.set_page_number(align=align)
|
|
734
|
+
applied.append("page_number")
|
|
735
|
+
if not applied:
|
|
736
|
+
raise ValueError("--header/--footer/--page-number 중 하나는 지정하세요.")
|
|
737
|
+
return applied
|
|
738
|
+
|
|
674
739
|
def save_copy(self, out_path: str) -> str:
|
|
675
740
|
out_abs = os.path.abspath(out_path)
|
|
676
741
|
if out_abs == self._source_path:
|
|
@@ -91,6 +91,38 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
91
91
|
ph.add_argument("--out", required=True, help="출력 hwpx 경로 (원본 불변)")
|
|
92
92
|
ph.add_argument("--json", action="store_true")
|
|
93
93
|
|
|
94
|
+
pfb = sub.add_parser("fill-batch", help="명단 × 양식 일괄 생성 (메일머지) — 위촉장·수료증·통지서 N부")
|
|
95
|
+
pfb.add_argument("file", help="양식 hwpx")
|
|
96
|
+
pfb.add_argument("--rows", required=True, help="명단 파일 (xlsx: 1행 헤더 / csv / json 객체 배열)")
|
|
97
|
+
pfb.add_argument("--template", required=True, help="fill 데이터 JSON — 값 속 {열이름}이 행마다 치환됨")
|
|
98
|
+
pfb.add_argument("--out-dir", required=True, help="출력 폴더 (없으면 생성)")
|
|
99
|
+
pfb.add_argument("--name", required=True, help='파일명 패턴, 예: "{성명}_위촉장.hwpx" (충돌 시 _2 접미)')
|
|
100
|
+
pfb.add_argument("--json", action="store_true")
|
|
101
|
+
|
|
102
|
+
pin = sub.add_parser("inspect", help="제출 전 기계 검수 — 잔여물({{마커}}·○○○)·공문 표기(날짜·시각)·개인정보. 위반 시 종료코드 2")
|
|
103
|
+
pin.add_argument("file")
|
|
104
|
+
pin.add_argument("--checks", help="쉼표 구분: residue,gongmun,pii (생략 시 전부)")
|
|
105
|
+
pin.add_argument("--json", action="store_true")
|
|
106
|
+
|
|
107
|
+
pim = sub.add_parser("image-add", help="이미지(사진·직인) 삽입 — 문단 앵커 또는 표 셀에 글자처럼취급")
|
|
108
|
+
pim.add_argument("file")
|
|
109
|
+
pim.add_argument("--image", required=True, help="이미지 파일 (png/jpg/bmp/gif)")
|
|
110
|
+
pim.add_argument("--at-text", help="이 원문 문단에 삽입 (공백 정규화 전체 일치)")
|
|
111
|
+
pim.add_argument("--table", type=int, help="표 인덱스 (--cell과 함께)")
|
|
112
|
+
pim.add_argument("--cell", help="셀 좌표 'R,C' (0-기준)")
|
|
113
|
+
pim.add_argument("--width-mm", type=float, default=20.0, help="가로 크기 mm (기본 20)")
|
|
114
|
+
pim.add_argument("--height-mm", type=float, help="세로 크기 mm (생략 시 가로와 동일)")
|
|
115
|
+
pim.add_argument("--out", required=True, help="출력 hwpx 경로 (원본 불변)")
|
|
116
|
+
pim.add_argument("--json", action="store_true")
|
|
117
|
+
|
|
118
|
+
phf = sub.add_parser("header-footer", help="머리말/꼬리말 텍스트·쪽번호 설정")
|
|
119
|
+
phf.add_argument("file")
|
|
120
|
+
phf.add_argument("--header", help="머리말 텍스트")
|
|
121
|
+
phf.add_argument("--footer", help="꼬리말 텍스트")
|
|
122
|
+
phf.add_argument("--page-number", help="쪽번호 위치: left/center/right")
|
|
123
|
+
phf.add_argument("--out", required=True, help="출력 hwpx 경로 (원본 불변)")
|
|
124
|
+
phf.add_argument("--json", action="store_true")
|
|
125
|
+
|
|
94
126
|
pra = sub.add_parser("row-add", help="표 행 추가 — 기준 행 서식·높이 승계, 내용은 비움 (세로 병합 걸리면 거부)")
|
|
95
127
|
pra.add_argument("file")
|
|
96
128
|
pra.add_argument("--table", type=int, required=True, help="표 인덱스 (0-기준)")
|
|
@@ -214,6 +246,40 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
214
246
|
args.file, table=args.table, like=args.like,
|
|
215
247
|
rows=parse_rows_spec(args.rows), out_path=args.out,
|
|
216
248
|
)
|
|
249
|
+
elif args.command == "fill-batch":
|
|
250
|
+
from hwpx_kit.commands.fill_batch import run_fill_batch
|
|
251
|
+
|
|
252
|
+
data = run_fill_batch(
|
|
253
|
+
args.file, rows_path=args.rows, template_path=args.template,
|
|
254
|
+
out_dir=args.out_dir, name_pattern=args.name,
|
|
255
|
+
)
|
|
256
|
+
if not data["all_ok"]:
|
|
257
|
+
exit_code = 2
|
|
258
|
+
elif args.command == "inspect":
|
|
259
|
+
from hwpx_kit.commands.inspect_doc import run_inspect
|
|
260
|
+
|
|
261
|
+
data = run_inspect(
|
|
262
|
+
args.file,
|
|
263
|
+
checks=[c.strip() for c in args.checks.split(",")] if args.checks else None,
|
|
264
|
+
)
|
|
265
|
+
if not data["clean"]:
|
|
266
|
+
exit_code = 2
|
|
267
|
+
elif args.command == "image-add":
|
|
268
|
+
from hwpx_kit.commands.image_add import run_image_add
|
|
269
|
+
|
|
270
|
+
data = run_image_add(
|
|
271
|
+
args.file, image_path=args.image, at_text=args.at_text,
|
|
272
|
+
table=args.table, cell=args.cell,
|
|
273
|
+
width_mm=args.width_mm, height_mm=args.height_mm,
|
|
274
|
+
out_path=args.out,
|
|
275
|
+
)
|
|
276
|
+
elif args.command == "header-footer":
|
|
277
|
+
from hwpx_kit.commands.header_footer import run_header_footer
|
|
278
|
+
|
|
279
|
+
data = run_header_footer(
|
|
280
|
+
args.file, header=args.header, footer=args.footer,
|
|
281
|
+
page_number=args.page_number, out_path=args.out,
|
|
282
|
+
)
|
|
217
283
|
elif args.command == "row-add":
|
|
218
284
|
from hwpx_kit.commands.table_rows import run_row_add
|
|
219
285
|
|